├── .editorconfig ├── .eslint-doc-generatorrc.js ├── .eslintrc ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ └── bug_report.yml └── workflows │ ├── node-18+.yml │ ├── node-minors.yml │ ├── node-pretest.yml │ ├── npm-publish.yml │ ├── rebase.yml │ ├── release.yml │ ├── require-allow-edits.yml │ ├── smoke-test.yml │ └── type-check.yml ├── .gitignore ├── .markdownlint.json ├── .markdownlintignore ├── .npmrc ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── build.tsconfig.json ├── configs ├── all.js ├── jsx-runtime.js └── recommended.js ├── docs └── rules │ ├── boolean-prop-naming.md │ ├── button-has-type.md │ ├── checked-requires-onchange-or-readonly.md │ ├── default-props-match-prop-types.md │ ├── destructuring-assignment.md │ ├── display-name.md │ ├── forbid-component-props.md │ ├── forbid-dom-props.md │ ├── forbid-elements.md │ ├── forbid-foreign-prop-types.md │ ├── forbid-prop-types.md │ ├── forward-ref-uses-ref.md │ ├── function-component-definition.md │ ├── hook-use-state.md │ ├── iframe-missing-sandbox.md │ ├── jsx-boolean-value.md │ ├── jsx-child-element-spacing.md │ ├── jsx-closing-bracket-location.md │ ├── jsx-closing-tag-location.md │ ├── jsx-curly-brace-presence.md │ ├── jsx-curly-newline.md │ ├── jsx-curly-spacing.md │ ├── jsx-equals-spacing.md │ ├── jsx-filename-extension.md │ ├── jsx-first-prop-new-line.md │ ├── jsx-fragments.md │ ├── jsx-handler-names.md │ ├── jsx-indent-props.md │ ├── jsx-indent.md │ ├── jsx-key.md │ ├── jsx-max-depth.md │ ├── jsx-max-props-per-line.md │ ├── jsx-newline.md │ ├── jsx-no-bind.md │ ├── jsx-no-comment-textnodes.md │ ├── jsx-no-constructed-context-values.md │ ├── jsx-no-duplicate-props.md │ ├── jsx-no-leaked-render.md │ ├── jsx-no-literals.md │ ├── jsx-no-script-url.md │ ├── jsx-no-target-blank.md │ ├── jsx-no-undef.md │ ├── jsx-no-useless-fragment.md │ ├── jsx-one-expression-per-line.md │ ├── jsx-pascal-case.md │ ├── jsx-props-no-multi-spaces.md │ ├── jsx-props-no-spread-multi.md │ ├── jsx-props-no-spreading.md │ ├── jsx-sort-default-props.md │ ├── jsx-sort-props.md │ ├── jsx-space-before-closing.md │ ├── jsx-tag-spacing.md │ ├── jsx-uses-react.md │ ├── jsx-uses-vars.md │ ├── jsx-wrap-multilines.md │ ├── no-access-state-in-setstate.md │ ├── no-adjacent-inline-elements.md │ ├── no-array-index-key.md │ ├── no-arrow-function-lifecycle.md │ ├── no-children-prop.md │ ├── no-danger-with-children.md │ ├── no-danger.md │ ├── no-deprecated.md │ ├── no-did-mount-set-state.md │ ├── no-did-update-set-state.md │ ├── no-direct-mutation-state.md │ ├── no-find-dom-node.md │ ├── no-invalid-html-attribute.md │ ├── no-is-mounted.md │ ├── no-multi-comp.md │ ├── no-namespace.md │ ├── no-object-type-as-default-prop.md │ ├── no-redundant-should-component-update.md │ ├── no-render-return-value.md │ ├── no-set-state.md │ ├── no-string-refs.md │ ├── no-this-in-sfc.md │ ├── no-typos.md │ ├── no-unescaped-entities.md │ ├── no-unknown-property.md │ ├── no-unsafe.md │ ├── no-unstable-nested-components.md │ ├── no-unused-class-component-methods.md │ ├── no-unused-prop-types.md │ ├── no-unused-state.md │ ├── no-will-update-set-state.md │ ├── prefer-es6-class.md │ ├── prefer-exact-props.md │ ├── prefer-read-only-props.md │ ├── prefer-stateless-function.md │ ├── prop-types.md │ ├── react-in-jsx-scope.md │ ├── require-default-props.md │ ├── require-optimization.md │ ├── require-render-return.md │ ├── self-closing-comp.md │ ├── sort-comp.md │ ├── sort-default-props.md │ ├── sort-prop-types.md │ ├── state-in-constructor.md │ ├── static-property-placement.md │ ├── style-prop-object.md │ └── void-dom-elements-no-children.md ├── index.js ├── lib ├── rules │ ├── boolean-prop-naming.js │ ├── button-has-type.js │ ├── checked-requires-onchange-or-readonly.js │ ├── default-props-match-prop-types.js │ ├── destructuring-assignment.js │ ├── display-name.js │ ├── forbid-component-props.js │ ├── forbid-dom-props.js │ ├── forbid-elements.js │ ├── forbid-foreign-prop-types.js │ ├── forbid-prop-types.js │ ├── forward-ref-uses-ref.js │ ├── function-component-definition.js │ ├── hook-use-state.js │ ├── iframe-missing-sandbox.js │ ├── index.js │ ├── jsx-boolean-value.js │ ├── jsx-child-element-spacing.js │ ├── jsx-closing-bracket-location.js │ ├── jsx-closing-tag-location.js │ ├── jsx-curly-brace-presence.js │ ├── jsx-curly-newline.js │ ├── jsx-curly-spacing.js │ ├── jsx-equals-spacing.js │ ├── jsx-filename-extension.js │ ├── jsx-first-prop-new-line.js │ ├── jsx-fragments.js │ ├── jsx-handler-names.js │ ├── jsx-indent-props.js │ ├── jsx-indent.js │ ├── jsx-key.js │ ├── jsx-max-depth.js │ ├── jsx-max-props-per-line.js │ ├── jsx-newline.js │ ├── jsx-no-bind.js │ ├── jsx-no-comment-textnodes.js │ ├── jsx-no-constructed-context-values.js │ ├── jsx-no-duplicate-props.js │ ├── jsx-no-leaked-render.js │ ├── jsx-no-literals.js │ ├── jsx-no-script-url.js │ ├── jsx-no-target-blank.js │ ├── jsx-no-undef.js │ ├── jsx-no-useless-fragment.js │ ├── jsx-one-expression-per-line.js │ ├── jsx-pascal-case.js │ ├── jsx-props-no-multi-spaces.js │ ├── jsx-props-no-spread-multi.js │ ├── jsx-props-no-spreading.js │ ├── jsx-sort-default-props.js │ ├── jsx-sort-props.js │ ├── jsx-space-before-closing.js │ ├── jsx-tag-spacing.js │ ├── jsx-uses-react.js │ ├── jsx-uses-vars.js │ ├── jsx-wrap-multilines.js │ ├── no-access-state-in-setstate.js │ ├── no-adjacent-inline-elements.js │ ├── no-array-index-key.js │ ├── no-arrow-function-lifecycle.js │ ├── no-children-prop.js │ ├── no-danger-with-children.js │ ├── no-danger.js │ ├── no-deprecated.js │ ├── no-did-mount-set-state.js │ ├── no-did-update-set-state.js │ ├── no-direct-mutation-state.js │ ├── no-find-dom-node.js │ ├── no-invalid-html-attribute.js │ ├── no-is-mounted.js │ ├── no-multi-comp.js │ ├── no-namespace.js │ ├── no-object-type-as-default-prop.js │ ├── no-redundant-should-component-update.js │ ├── no-render-return-value.js │ ├── no-set-state.js │ ├── no-string-refs.js │ ├── no-this-in-sfc.js │ ├── no-typos.js │ ├── no-unescaped-entities.js │ ├── no-unknown-property.js │ ├── no-unsafe.js │ ├── no-unstable-nested-components.js │ ├── no-unused-class-component-methods.js │ ├── no-unused-prop-types.js │ ├── no-unused-state.js │ ├── no-will-update-set-state.js │ ├── prefer-es6-class.js │ ├── prefer-exact-props.js │ ├── prefer-read-only-props.js │ ├── prefer-stateless-function.js │ ├── prop-types.js │ ├── react-in-jsx-scope.js │ ├── require-default-props.js │ ├── require-optimization.js │ ├── require-render-return.js │ ├── self-closing-comp.js │ ├── sort-comp.js │ ├── sort-default-props.js │ ├── sort-prop-types.js │ ├── state-in-constructor.js │ ├── static-property-placement.js │ ├── style-prop-object.js │ └── void-dom-elements-no-children.js ├── types.d.ts └── util │ ├── Components.js │ ├── annotations.js │ ├── ast.js │ ├── componentUtil.js │ ├── defaultProps.js │ ├── docsUrl.js │ ├── error.js │ ├── eslint.js │ ├── getTokenBeforeClosingBracket.js │ ├── isCreateContext.js │ ├── isCreateElement.js │ ├── isDestructuredFromPragmaImport.js │ ├── isFirstLetterCapitalized.js │ ├── jsx.js │ ├── lifecycleMethods.js │ ├── linkComponents.js │ ├── log.js │ ├── makeNoMethodSetStateRule.js │ ├── message.js │ ├── pragma.js │ ├── propTypes.js │ ├── propTypesSort.js │ ├── propWrapper.js │ ├── props.js │ ├── report.js │ ├── usedPropTypes.js │ ├── variable.js │ └── version.js ├── package.json ├── test-published-types ├── .npmrc ├── index.js ├── package.json └── tsconfig.json ├── test ├── eslint-remote-tester.config.js └── mocha.opts ├── tests ├── fixtures │ ├── flat-config │ │ ├── config-all │ │ │ ├── eslint.config-deep.js │ │ │ ├── eslint.config-root.js │ │ │ └── test.jsx │ │ ├── config-jsx-runtime │ │ │ ├── eslint.config-deep.js │ │ │ ├── eslint.config-root.js │ │ │ └── test.jsx │ │ ├── config-recommended │ │ │ ├── eslint.config-deep.js │ │ │ ├── eslint.config-root.js │ │ │ └── test.jsx │ │ ├── plugin-and-config │ │ │ ├── eslint.config-deep.js │ │ │ ├── eslint.config-root.js │ │ │ └── test.jsx │ │ └── plugin │ │ │ ├── eslint.config.js │ │ │ └── test.jsx │ └── version │ │ ├── detect-version-missing │ │ ├── node_modules │ │ │ └── react │ │ │ │ ├── index.js │ │ │ │ └── package.json │ │ └── test.js │ │ ├── detect-version-sibling │ │ ├── node_modules │ │ │ ├── flow-bin │ │ │ │ └── package.json │ │ │ └── react │ │ │ │ ├── index.js │ │ │ │ └── package.json │ │ └── test.js │ │ └── detect-version │ │ ├── detect-version-child │ │ ├── node_modules │ │ │ ├── flow-bin │ │ │ │ └── package.json │ │ │ └── react │ │ │ │ ├── index.js │ │ │ │ └── package.json │ │ └── test.js │ │ ├── node_modules │ │ ├── flow-bin │ │ │ └── package.json │ │ └── react │ │ │ ├── index.js │ │ │ └── package.json │ │ └── test.js ├── flat-config.js ├── helpers │ ├── getESLintCoreRule.js │ ├── getRuleDefiner.js │ ├── parsers.js │ └── ruleTester.js ├── index.js ├── lib │ └── rules │ │ ├── boolean-prop-naming.js │ │ ├── button-has-type.js │ │ ├── checked-requires-onchange-or-readonly.js │ │ ├── default-props-match-prop-types.js │ │ ├── destructuring-assignment.js │ │ ├── display-name.js │ │ ├── forbid-component-props.js │ │ ├── forbid-dom-props.js │ │ ├── forbid-elements.js │ │ ├── forbid-foreign-prop-types.js │ │ ├── forbid-prop-types.js │ │ ├── forward-ref-uses-ref.js │ │ ├── function-component-definition.js │ │ ├── hook-use-state.js │ │ ├── iframe-missing-sandbox.js │ │ ├── jsx-boolean-value.js │ │ ├── jsx-child-element-spacing.js │ │ ├── jsx-closing-bracket-location.js │ │ ├── jsx-closing-tag-location.js │ │ ├── jsx-curly-brace-presence.js │ │ ├── jsx-curly-newline.js │ │ ├── jsx-curly-spacing.js │ │ ├── jsx-equals-spacing.js │ │ ├── jsx-filename-extension.js │ │ ├── jsx-first-prop-new-line.js │ │ ├── jsx-fragments.js │ │ ├── jsx-handler-names.js │ │ ├── jsx-indent-props.js │ │ ├── jsx-indent.js │ │ ├── jsx-key.js │ │ ├── jsx-max-depth.js │ │ ├── jsx-max-props-per-line.js │ │ ├── jsx-newline.js │ │ ├── jsx-no-bind.js │ │ ├── jsx-no-comment-textnodes.js │ │ ├── jsx-no-constructed-context-values.js │ │ ├── jsx-no-duplicate-props.js │ │ ├── jsx-no-leaked-render.js │ │ ├── jsx-no-literals.js │ │ ├── jsx-no-script-url.js │ │ ├── jsx-no-target-blank.js │ │ ├── jsx-no-undef.js │ │ ├── jsx-no-useless-fragment.js │ │ ├── jsx-one-expression-per-line.js │ │ ├── jsx-pascal-case.js │ │ ├── jsx-props-no-multi-spaces.js │ │ ├── jsx-props-no-spread-multi.js │ │ ├── jsx-props-no-spreading.js │ │ ├── jsx-sort-default-props.js │ │ ├── jsx-sort-props.js │ │ ├── jsx-space-before-closing.js │ │ ├── jsx-tag-spacing.js │ │ ├── jsx-uses-react.js │ │ ├── jsx-uses-vars.js │ │ ├── jsx-wrap-multilines.js │ │ ├── no-access-state-in-setstate.js │ │ ├── no-adjacent-inline-elements.js │ │ ├── no-array-index-key.js │ │ ├── no-arrow-function-lifecycle.js │ │ ├── no-children-prop.js │ │ ├── no-danger-with-children.js │ │ ├── no-danger.js │ │ ├── no-deprecated.js │ │ ├── no-did-mount-set-state.js │ │ ├── no-did-update-set-state.js │ │ ├── no-direct-mutation-state.js │ │ ├── no-find-dom-node.js │ │ ├── no-invalid-html-attribute.js │ │ ├── no-is-mounted.js │ │ ├── no-multi-comp.js │ │ ├── no-namespace.js │ │ ├── no-object-type-as-default-prop.js │ │ ├── no-redundant-should-component-update.js │ │ ├── no-render-return-value.js │ │ ├── no-set-state.js │ │ ├── no-string-refs.js │ │ ├── no-this-in-sfc.js │ │ ├── no-typos.js │ │ ├── no-unescaped-entities.js │ │ ├── no-unknown-property.js │ │ ├── no-unsafe.js │ │ ├── no-unstable-nested-components.js │ │ ├── no-unused-class-component-methods.js │ │ ├── no-unused-prop-types.js │ │ ├── no-unused-state.js │ │ ├── no-will-update-set-state.js │ │ ├── prefer-es6-class.js │ │ ├── prefer-exact-props.js │ │ ├── prefer-read-only-props.js │ │ ├── prefer-stateless-function.js │ │ ├── prop-types.js │ │ ├── react-in-jsx-scope.js │ │ ├── require-default-props.js │ │ ├── require-optimization.js │ │ ├── require-render-return.js │ │ ├── self-closing-comp.js │ │ ├── sort-comp.js │ │ ├── sort-default-props.js │ │ ├── sort-prop-types.js │ │ ├── state-in-constructor.js │ │ ├── static-property-placement.js │ │ ├── style-prop-object.js │ │ └── void-dom-elements-no-children.js └── util │ ├── .eslintrc │ ├── Components.js │ ├── ast.js │ ├── isFirstLetterCapitalized.js │ ├── jsx.js │ ├── linkComponents.js │ ├── pragma.js │ ├── propWrapper.js │ ├── variable.js │ └── version.js ├── tsconfig.json └── types ├── rules └── jsx-no-literals.d.ts └── string.prototype.repeat └── index.d.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | quote_type = single 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslint-doc-generatorrc.js: -------------------------------------------------------------------------------- 1 | /** @type {import('eslint-doc-generator').GenerateOptions} */ 2 | const config = { 3 | configEmoji: [ 4 | ['jsx-runtime', '🏃'], 5 | ['recommended', '☑️'], 6 | ], 7 | ignoreConfig: ['all', 'flat'], 8 | urlConfigs: 'https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs', 9 | }; 10 | 11 | module.exports = config; 12 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": ["airbnb-base", "plugin:eslint-plugin/recommended"], 4 | "plugins": ["eslint-plugin"], 5 | "env": { 6 | "es6": true, 7 | "node": true 8 | }, 9 | "parserOptions": { 10 | "ecmaVersion": 6, 11 | "ecmaFeatures": { 12 | "jsx": true 13 | }, 14 | "sourceType": "script", 15 | }, 16 | "ignorePatterns": [ 17 | "coverage/", 18 | ".nyc_output/", 19 | "test-published-types/", 20 | "tests/fixtures/flat-config/", 21 | "**/*/*.d.ts", 22 | ], 23 | "rules": { 24 | "comma-dangle": [2, "always-multiline"], 25 | "object-shorthand": [2, "always", { 26 | "ignoreConstructors": false, 27 | "avoidQuotes": false, // this is the override vs airbnb 28 | }], 29 | "max-len": [2, 140, { 30 | "ignoreStrings": true, 31 | "ignoreTemplateLiterals": true, 32 | "ignoreComments": true, 33 | }], 34 | "consistent-return": 0, 35 | 36 | "prefer-destructuring": [2, { "array": false, "object": false }, { "enforceForRenamedProperties": false }], 37 | "prefer-object-spread": 0, // until node 8 is required 38 | "prefer-rest-params": 0, // until node 6 is required 39 | "prefer-spread": 0, // until node 6 is required 40 | "function-call-argument-newline": 1, // TODO: enable 41 | "function-paren-newline": 0, 42 | "no-plusplus": [2, {"allowForLoopAfterthoughts": true}], 43 | "no-param-reassign": 1, 44 | "no-restricted-syntax": [2, { 45 | "selector": "ObjectPattern", 46 | "message": "Object destructuring is not compatible with Node v4" 47 | }], 48 | "strict": [2, "safe"], 49 | "valid-jsdoc": [2, { 50 | "requireReturn": false, 51 | "requireParamDescription": false, 52 | "requireReturnDescription": false, 53 | }], 54 | 55 | "eslint-plugin/consistent-output": 0, 56 | "eslint-plugin/require-meta-docs-description": [2, { "pattern": "^(Enforce|Require|Disallow)" }], 57 | "eslint-plugin/require-meta-schema": 0, 58 | "eslint-plugin/require-meta-type": 0 59 | }, 60 | "overrides": [ 61 | { 62 | "files": "tests/**", 63 | "rules": { 64 | "no-template-curly-in-string": 1, 65 | }, 66 | }, 67 | { 68 | "files": "markdown.config.js", 69 | "rules": { 70 | "no-console": 0, 71 | }, 72 | }, 73 | { 74 | "files": ".github/workflows/*.js", 75 | "parserOptions": { 76 | "ecmaVersion": 2019, 77 | }, 78 | "rules": { 79 | "camelcase": 0, 80 | "no-console": 0, 81 | "no-restricted-syntax": 0, 82 | }, 83 | }, 84 | ], 85 | } 86 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [jsx-eslint, ljharb] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: jsx-eslint # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: npm/eslint-plugin-react 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report 3 | title: "[Bug]: " 4 | labels: ["bug", "triage"] 5 | assignees: [] 6 | body: 7 | - type: checkboxes 8 | attributes: 9 | label: Is there an existing issue for this? 10 | description: Please search to see if an issue already exists for the bug you encountered. 11 | options: 12 | - label: I have searched the existing issues and my issue is unique 13 | required: true 14 | - label: My issue appears in the command-line and not only in the text editor 15 | required: true 16 | - type: textarea 17 | id: Code 18 | attributes: 19 | label: Description Overview 20 | description: A clear and concise description of bug w/ examples 21 | placeholder: | 22 | Brief description here 23 | 24 | Show example of your code (as text format), add images/videos/gifs to help explain example 25 | and/or Link of repo to where issue is occurring 26 | 27 | What is happening? / What is the error? 28 | 29 | What command(s) did you run to reproduce issue? 30 | value: | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | validations: 44 | required: true 45 | - type: textarea 46 | id: expected_behavior 47 | attributes: 48 | label: Expected Behavior 49 | description: A clear and concise description of what you expected to happen. 50 | placeholder: | 51 | Brief description here 52 | 53 | Show example of code (as text format), add images/videos/gifs to help explain expected behavior 54 | value: | 55 | 56 | 57 | 58 | 59 | validations: 60 | required: true 61 | - type: input 62 | id: eslint-plugin-react-version 63 | attributes: 64 | label: eslint-plugin-react version 65 | placeholder: v7.31.11 66 | validations: 67 | required: true 68 | - type: input 69 | id: eslint-version 70 | attributes: 71 | label: eslint version 72 | placeholder: v8.28.0 73 | validations: 74 | required: true 75 | - type: input 76 | id: node-version 77 | attributes: 78 | label: node version 79 | placeholder: v8.19.2 80 | validations: 81 | required: true 82 | -------------------------------------------------------------------------------- /.github/workflows/node-pretest.yml: -------------------------------------------------------------------------------- 1 | name: 'Tests: pretest/posttest' 2 | 3 | on: [pull_request, push] 4 | 5 | jobs: 6 | pretest: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v4 11 | - uses: ljharb/actions/node/install@main 12 | name: 'nvm install lts/* && npm install' 13 | with: 14 | node-version: 'lts/*' 15 | - run: npm run pretest 16 | 17 | posttest: 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v4 22 | - uses: ljharb/actions/node/install@main 23 | name: 'nvm install lts/* && npm install' 24 | with: 25 | node-version: 'lts/*' 26 | env: 27 | NPM_CONFIG_LEGACY_PEER_DEPS: true 28 | - run: npx ls-engines 29 | - run: echo 'legacy-peer-deps=true' >> .npmrc 30 | - run: npm run posttest 31 | -------------------------------------------------------------------------------- /.github/workflows/rebase.yml: -------------------------------------------------------------------------------- 1 | name: Automatic Rebase 2 | 3 | on: [pull_request_target] 4 | 5 | jobs: 6 | _: 7 | uses: ljharb/actions/.github/workflows/rebase.yml@main 8 | secrets: 9 | token: ${{ secrets.GITHUB_TOKEN }} 10 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*.*.*" 7 | 8 | jobs: 9 | release: 10 | if: github.event.repository.fork == false 11 | 12 | runs-on: ubuntu-latest 13 | 14 | permissions: 15 | contents: write 16 | 17 | steps: 18 | - uses: step-security/harden-runner@v2 19 | with: 20 | allowed-endpoints: 21 | api.github.com:443 22 | github.com:443 23 | raw.githubusercontent.com:443 24 | nodejs.org:443 25 | registry.npmjs.org:443 26 | 27 | - name: Get version from tag 28 | id: tag_name 29 | run: echo "current_version=${GITHUB_REF#refs/tags/v}" >> "$GITHUB_OUTPUT" 30 | shell: bash 31 | 32 | - uses: actions/checkout@v4 33 | 34 | - uses: ljharb/actions/node/install@main 35 | with: 36 | node-version: node 37 | skip-install: true 38 | skip-ls-check: true 39 | 40 | - uses: mindsers/changelog-reader-action@v2 41 | id: changelog_reader 42 | with: 43 | version: ${{ steps.tag_name.outputs.current_version }} 44 | 45 | - name: Get common links from changelog 46 | id: changelog 47 | run: | 48 | # Parse the changelog for common links 49 | _links="$(egrep '^\[.*]:.+' ${GITHUB_WORKSPACE:-.}/CHANGELOG.md | sort -u)" 50 | _links="${_links//'%'/'%25'}" 51 | # _links="${_links//$'\n'/'%0A'}" 52 | _links="${_links//$'\r'/'%0D'}" 53 | # Set output 'links' to $_links 54 | DELIMITER=$(uuidgen) 55 | echo "links<<${DELIMITER}" >> "${GITHUB_OUTPUT}" 56 | echo "$_links" >> "${GITHUB_OUTPUT}" 57 | echo "${DELIMITER}" >> "${GITHUB_OUTPUT}" 58 | 59 | - name: 'concat data > tmp.md' 60 | run: | 61 | cat << 'EOF' > tmp.md 62 | ${{ steps.changelog_reader.outputs.changes }} 63 | ${{ steps.changelog.outputs.links }} 64 | EOF 65 | 66 | - run: cat tmp.md 67 | 68 | - id: prune-footnotes 69 | run: | 70 | DELIMITER=$(uuidgen) 71 | echo "body<<${DELIMITER}" >> "${GITHUB_OUTPUT}" 72 | npx gfm-footnotes -i tmp.md >> "${GITHUB_OUTPUT}" 73 | echo "${DELIMITER}" >> "${GITHUB_OUTPUT}" 74 | 75 | - uses: softprops/action-gh-release@v2 76 | with: 77 | body: | 78 | ${{ steps.prune-footnotes.outputs.body }} 79 | -------------------------------------------------------------------------------- /.github/workflows/require-allow-edits.yml: -------------------------------------------------------------------------------- 1 | name: Require “Allow Edits” 2 | 3 | on: [pull_request_target] 4 | 5 | jobs: 6 | _: 7 | name: "Require “Allow Edits”" 8 | 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: ljharb/require-allow-edits@main 13 | -------------------------------------------------------------------------------- /.github/workflows/smoke-test.yml: -------------------------------------------------------------------------------- 1 | name: Smoke test 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * SUN' 6 | workflow_dispatch: 7 | 8 | jobs: 9 | smoke-test: 10 | if: ${{ github.repository == 'jsx-eslint/eslint-plugin-react' || github.event_name == 'workflow_dispatch' }} 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: ljharb/actions/node/install@main 15 | name: 'nvm install lts/* && npm install' 16 | with: 17 | node-version: 'lts/*' 18 | skip-ls-check: true 19 | - run: | 20 | npm link 21 | npm link eslint-plugin-react 22 | - uses: AriPerkkio/eslint-remote-tester-run-action@v4 23 | with: 24 | issue-title: 'Results of weekly scheduled smoke test' 25 | issue-label: 'smoke-test' 26 | eslint-remote-tester-config: test/eslint-remote-tester.config.js 27 | -------------------------------------------------------------------------------- /.github/workflows/type-check.yml: -------------------------------------------------------------------------------- 1 | name: "Types: check published types" 2 | 3 | on: [pull_request, push] 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | test: 10 | name: TS ${{ matrix.ts_version }}, "${{ matrix.ts_lib }}" 11 | runs-on: ubuntu-latest 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | ts_version: 16 | # The official ESLint types are not compatible with TS 3.9 17 | # - 3.9 18 | - '4.0' 19 | - 4.1 20 | - 4.2 21 | - 4.3 22 | - 4.4 23 | - 4.5 24 | - '5.0' 25 | - 5.5 26 | - 5.6 27 | ts_lib: 28 | - es2015 29 | - es2015,dom 30 | - es2020 31 | - esnext 32 | steps: 33 | - uses: actions/checkout@v4 34 | with: 35 | persist-credentials: false 36 | show-progress: false 37 | 38 | - uses: ljharb/actions/node/install@main 39 | name: 'nvm install lts/* && npm install' 40 | with: 41 | node-version: 'lts/*' 42 | skip-ls-check: true 43 | 44 | - name: build types 45 | run: npm run build-types 46 | 47 | # Pack the lib into a tarball so that when we install the lib later in the 48 | # test-published-types directory, it's only install `dependencies` of the 49 | # lib. 50 | - name: pack the lib 51 | run: npm pack --pack-destination /tmp/ 52 | 53 | - name: find the packed lib 54 | run: echo "ESLINT_PLUGIN_REACT_PATH=$(ls /tmp/eslint-plugin-react*.tgz | tail -n 1)" >> $GITHUB_ENV 55 | 56 | - name: show the path to the packed lib 57 | run: echo "$ESLINT_PLUGIN_REACT_PATH" 58 | 59 | - name: npm install working directory 60 | run: npm install 61 | working-directory: test-published-types 62 | 63 | - name: install eslint-plugin-react and typescript version ${{ matrix.ts_version }} 64 | run: npm install --no-save "$ESLINT_PLUGIN_REACT_PATH" typescript@${{ matrix.ts_version }} 65 | working-directory: test-published-types 66 | 67 | - name: show installed typescript version 68 | run: npm list typescript --depth=0 69 | working-directory: test-published-types 70 | 71 | - name: show installed eslint-plugin-react version 72 | run: npm list eslint-plugin-react --depth=0 73 | working-directory: test-published-types 74 | 75 | - name: check types with lib "${{ matrix.ts_lib }}" 76 | run: npx tsc --lib ${{ matrix.ts_lib }} 77 | working-directory: test-published-types 78 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # gitignore 2 | 3 | lib-cov 4 | *.seed 5 | *.log 6 | *.csv 7 | *.dat 8 | *.out 9 | *.pid 10 | *.gz 11 | *.sublime-project 12 | *.sublime-workspace 13 | pids 14 | logs 15 | reports 16 | coverage 17 | .nyc_output/ 18 | build 19 | node_modules 20 | !tests/**/node_modules 21 | npm-debug.log 22 | sftp-config.json 23 | eslint-remote-tester-results 24 | 25 | # Only apps should have lockfiles 26 | npm-shrinkwrap≥json 27 | package-lock.json 28 | yarn.lock 29 | 30 | .npmignore 31 | 32 | /lib/**/*.d.ts 33 | /lib/**/*.d.ts.map 34 | !/lib/types.d.ts 35 | /index.d.ts 36 | /index.d.ts.map 37 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "line-length": false, 3 | "no-inline-html": false, 4 | "no-hard-tabs": { "spaces_per_tab": 2 }, 5 | "ul-style": { "style": "dash" } 6 | } 7 | -------------------------------------------------------------------------------- /.markdownlintignore: -------------------------------------------------------------------------------- 1 | CHANGELOG.md 2 | LICENSE 3 | node_modules 4 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to `eslint-plugin-react` 2 | 3 | ## `eslint` Rules 4 | 5 | ### Markup 6 | 7 | Files documenting individual rules live in docs/rules folder. They're annotated 8 | using Markdown, which adds extra layer of legibility and better visual orientation 9 | to the documents. Consider this when writing supporting text for the rules. 10 | 11 | See the list below for some guidelines that need to be followed when adding to the 12 | collection. 13 | 14 | - Negative adverbs preceding snippets in 'Rule Details' sections must be **bold**. 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Yannick Croissant 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | This is the list of versions of `eslint-plugin-react` which are currently being supported with security updates. 6 | 7 | | Version | Supported | 8 | | -------- | ------------------ | 9 | | 7.x | :white_check_mark: | 10 | | < 7.x | :x: | 11 | 12 | ## Reporting a Vulnerability 13 | 14 | To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. 15 | -------------------------------------------------------------------------------- /build.tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig", 3 | "files": [ 4 | "index.js" 5 | ], 6 | "compilerOptions": { 7 | "declaration": true, 8 | "declarationMap": true, 9 | "noEmit": false, 10 | "emitDeclarationOnly": true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /configs/all.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const plugin = require('..'); 4 | 5 | const legacyConfig = plugin.configs.all; 6 | 7 | module.exports = { 8 | plugins: { react: plugin }, 9 | rules: legacyConfig.rules, 10 | languageOptions: { parserOptions: legacyConfig.parserOptions }, 11 | }; 12 | 13 | Object.defineProperty(module.exports, 'languageOptions', { enumerable: false }); 14 | -------------------------------------------------------------------------------- /configs/jsx-runtime.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const plugin = require('..'); 4 | 5 | const legacyConfig = plugin.configs['jsx-runtime']; 6 | 7 | module.exports = { 8 | plugins: { react: plugin }, 9 | rules: legacyConfig.rules, 10 | languageOptions: { parserOptions: legacyConfig.parserOptions }, 11 | }; 12 | 13 | Object.defineProperty(module.exports, 'languageOptions', { enumerable: false }); 14 | -------------------------------------------------------------------------------- /configs/recommended.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const plugin = require('..'); 4 | 5 | const legacyConfig = plugin.configs.recommended; 6 | 7 | module.exports = { 8 | plugins: { react: plugin }, 9 | rules: legacyConfig.rules, 10 | languageOptions: { parserOptions: legacyConfig.parserOptions }, 11 | }; 12 | 13 | Object.defineProperty(module.exports, 'languageOptions', { enumerable: false }); 14 | -------------------------------------------------------------------------------- /docs/rules/button-has-type.md: -------------------------------------------------------------------------------- 1 | # Disallow usage of `button` elements without an explicit `type` attribute (`react/button-has-type`) 2 | 3 | 4 | 5 | The default value of the `type` attribute for HTML `button` elements is `"submit"`. This is often not the desired behavior and may lead to unexpected page reloads. 6 | This rules enforces an explicit `type` attribute for all `button` elements and checks that its value is valid per the spec (i.e., is one of `"button"`, `"submit"`, and `"reset"`). 7 | 8 | ## Rule Details 9 | 10 | Examples of **incorrect** code for this rule: 11 | 12 | ```jsx 13 | var Hello = 14 | var Hello = 15 | var Hello = 16 | 17 | var Hello = React.createElement('button', {}, 'Hello') 18 | var Hello = React.createElement('button', {type: 'foo'}, 'Hello') 19 | ``` 20 | 21 | Examples of **correct** code for this rule: 22 | 23 | ```jsx 24 | var Hello = Hello 25 | var Hello = Hello 26 | var Hello = 27 | var Hello = 28 | var Hello = 29 | var Hello = 30 | 31 | var Hello = React.createElement('span', {}, 'Hello') 32 | var Hello = React.createElement('span', {type: 'foo'}, 'Hello') 33 | var Hello = React.createElement('button', {type: 'button'}, 'Hello') 34 | var Hello = React.createElement('button', {type: 'submit'}, 'Hello') 35 | var Hello = React.createElement('button', {type: 'reset'}, 'Hello') 36 | var Hello = React.createElement('button', {type: condition ? 'button' : 'submit'}, 'Hello') 37 | ``` 38 | 39 | ## Rule Options 40 | 41 | ```js 42 | ... 43 | "react/button-has-type": [, { 44 | "button": , 45 | "submit": , 46 | "reset": 47 | }] 48 | ... 49 | ``` 50 | 51 | You can forbid particular type attribute values by passing `false` as corresponding option (by default all of them are `true`). 52 | 53 | Examples of **incorrect** code for this rule, when configured with `{ "reset": false }`: 54 | 55 | ```jsx 56 | var Hello = 57 | var Hello = 58 | 59 | var Hello = React.createElement('button', {type: 'reset'}, 'Hello') 60 | var Hello = React.createElement('button', {type: condition ? "button" : "reset"}, 'Hello') 61 | ``` 62 | 63 | ## When Not To Use It 64 | 65 | If you use only `"submit"` buttons, you can disable this rule 66 | -------------------------------------------------------------------------------- /docs/rules/checked-requires-onchange-or-readonly.md: -------------------------------------------------------------------------------- 1 | # Enforce using `onChange` or `readonly` attribute when `checked` is used (`react/checked-requires-onchange-or-readonly`) 2 | 3 | 4 | 5 | This rule enforces `onChange` or `readonly` attribute for `checked` property of input elements. 6 | 7 | It also warns when `checked` and `defaultChecked` properties are used together. 8 | 9 | ## Rule Details 10 | 11 | Example of **incorrect** code for this rule: 12 | 13 | ```jsx 14 | 15 | 16 | 17 | 18 | React.createElement('input', { checked: false }); 19 | React.createElement('input', { type: 'checkbox', checked: true }); 20 | React.createElement('input', { type: 'checkbox', checked: true, defaultChecked: true }); 21 | ``` 22 | 23 | Example of **correct** code for this rule: 24 | 25 | ```jsx 26 | {}} /> 27 | 28 | 29 | 30 | 31 | React.createElement('input', { type: 'checkbox', checked: true, onChange() {} }); 32 | React.createElement('input', { type: 'checkbox', checked: true, readOnly: true }); 33 | React.createElement('input', { type: 'checkbox', checked: true, onChange() {}, readOnly: true }); 34 | React.createElement('input', { type: 'checkbox', defaultChecked: true }); 35 | ``` 36 | 37 | ## Rule Options 38 | 39 | ```js 40 | "react/checked-requires-onchange-or-readonly": [, { 41 | "ignoreMissingProperties": , 42 | "ignoreExclusiveCheckedAttribute": 43 | }] 44 | ``` 45 | -------------------------------------------------------------------------------- /docs/rules/forbid-dom-props.md: -------------------------------------------------------------------------------- 1 | # Disallow certain props on DOM Nodes (`react/forbid-dom-props`) 2 | 3 | 4 | 5 | This rule prevents passing of props to elements. This rule only applies to DOM Nodes (e.g. `
`) and not Components (e.g. ``). 6 | The list of forbidden props can be customized with the `forbid` option. 7 | 8 | ## Rule Details 9 | 10 | This rule checks all JSX elements and verifies that no forbidden props are used 11 | on DOM Nodes. This rule is off by default. 12 | 13 | Examples of **incorrect** code for this rule: 14 | 15 | ```jsx 16 | // [1, { "forbid": ["id"] }] 17 |
18 | ``` 19 | 20 | ```jsx 21 | // [1, { "forbid": ["style"] }] 22 |
23 | ``` 24 | 25 | Examples of **correct** code for this rule: 26 | 27 | ```jsx 28 | // [1, { "forbid": ["id"] }] 29 | 30 | ``` 31 | 32 | ```jsx 33 | // [1, { "forbid": ["id"] }] 34 | 35 | ``` 36 | 37 | ## Rule Options 38 | 39 | ```js 40 | ... 41 | "react/forbid-dom-props": [, { "forbid": [|] }] 42 | ... 43 | ``` 44 | 45 | ### `forbid` 46 | 47 | An array of strings, with the names of props that are forbidden. The default value of this option `[]`. 48 | Each array element can either be a string with the property name or object specifying the property name, an optional 49 | custom message, and a DOM nodes disallowed list (e.g. `
`): 50 | 51 | ```js 52 | { 53 | "propName": "someProp", 54 | "disallowedFor": ["DOMNode", "AnotherDOMNode"], 55 | "message": "Avoid using someProp" 56 | } 57 | ``` 58 | 59 | ### Related rules 60 | 61 | - [forbid-component-props](./forbid-component-props.md) 62 | -------------------------------------------------------------------------------- /docs/rules/forbid-elements.md: -------------------------------------------------------------------------------- 1 | # Disallow certain elements (`react/forbid-elements`) 2 | 3 | 4 | 5 | You may want to forbid usage of certain elements in favor of others, (e.g. forbid all `
` and use `` instead). This rule allows you to configure a list of forbidden elements and to specify their desired replacements. 6 | 7 | ## Rule Details 8 | 9 | This rule checks all JSX elements and `React.createElement` calls and verifies that no forbidden elements are used. This rule is off by default. If on, no elements are forbidden by default. 10 | 11 | ## Rule Options 12 | 13 | ```js 14 | ... 15 | "react/forbid-elements": [, { "forbid": [] }] 16 | ... 17 | ``` 18 | 19 | ### `forbid` 20 | 21 | An array of strings and/or objects. An object in this array may have the following properties: 22 | 23 | - `element` (required): the name of the forbidden element (e.g. `'button'`, `'Modal'`) 24 | - `message`: additional message that gets reported 25 | 26 | A string item in the array is a shorthand for `{ element: string }`. 27 | 28 | Examples of **correct** code for this rule: 29 | 30 | ```jsx 31 | // [1, { "forbid": ["button"] }] 32 |
55 | React.createElement('div', {}, React.createElement('button', {}, React.createElement('input'))); 56 | ``` 57 | 58 | ## When Not To Use It 59 | 60 | If you don't want to forbid any elements. 61 | -------------------------------------------------------------------------------- /docs/rules/forbid-foreign-prop-types.md: -------------------------------------------------------------------------------- 1 | # Disallow using another component's propTypes (`react/forbid-foreign-prop-types`) 2 | 3 | 4 | 5 | This rule forbids using another component's prop types unless they are explicitly imported/exported. This allows people who want to use [babel-plugin-transform-react-remove-prop-types](https://github.com/oliviertassinari/babel-plugin-transform-react-remove-prop-types) to remove propTypes from their components in production builds, to do so safely. 6 | 7 | In order to ensure that imports are explicitly exported it is recommended to use the ["named" rule in eslint-plugin-import](https://github.com/benmosher/eslint-plugin-import/blob/HEAD/docs/rules/named.md) in conjunction with this rule. 8 | 9 | ## Rule Details 10 | 11 | This rule checks all objects and ensures that the `propTypes` property is not used. 12 | 13 | Examples of **incorrect** code for this rule: 14 | 15 | ```js 16 | import SomeComponent from './SomeComponent'; 17 | SomeComponent.propTypes; 18 | 19 | var { propTypes } = SomeComponent; 20 | 21 | SomeComponent['propTypes']; 22 | ``` 23 | 24 | Examples of **correct** code for this rule: 25 | 26 | ```js 27 | import SomeComponent, {propTypes as someComponentPropTypes} from './SomeComponent'; 28 | ``` 29 | 30 | ## Rule Options 31 | 32 | ```js 33 | ... 34 | "react/forbid-foreign-prop-types": [, { "allowInPropTypes": [] }] 35 | ... 36 | ``` 37 | 38 | ### `allowInPropTypes` 39 | 40 | If `true`, the rule will not warn on foreign propTypes usage inside a propTypes declaration. 41 | 42 | ## When Not To Use It 43 | 44 | This rule aims to make a certain production optimization, removing prop types, less prone to error. This rule may not be relevant to you if you do not wish to make use of this optimization. 45 | 46 | If you are writing a higher-order component that hoists the wrapped component's propTypes, you might want to disable this rule. 47 | -------------------------------------------------------------------------------- /docs/rules/forbid-prop-types.md: -------------------------------------------------------------------------------- 1 | # Disallow certain propTypes (`react/forbid-prop-types`) 2 | 3 | 4 | 5 | By default this rule prevents vague prop types with more specific alternatives available (`any`, `array`, `object`), but any prop type can be disabled if desired. The defaults are chosen because they have obvious replacements. `any` should be replaced with, well, anything. `array` and `object` can be replaced with `arrayOf` and `shape`, respectively. 6 | 7 | ## Rule Details 8 | 9 | This rule checks all JSX components and verifies that no forbidden propsTypes are used. 10 | This rule is off by default. 11 | 12 | Examples of **incorrect** code for this rule: 13 | 14 | ```jsx 15 | var Component = createReactClass({ 16 | propTypes: { 17 | a: PropTypes.any, 18 | r: PropTypes.array, 19 | o: PropTypes.object 20 | }, 21 | ... 22 | }); 23 | 24 | class Component extends React.Component { 25 | ... 26 | } 27 | Component.propTypes = { 28 | a: PropTypes.any, 29 | r: PropTypes.array, 30 | o: PropTypes.object 31 | }; 32 | 33 | class Component extends React.Component { 34 | static propTypes = { 35 | a: PropTypes.any, 36 | r: PropTypes.array, 37 | o: PropTypes.object 38 | } 39 | render() { 40 | return
; 41 | } 42 | } 43 | ``` 44 | 45 | ## Rule Options 46 | 47 | ```js 48 | ... 49 | "react/forbid-prop-types": [, { "forbid": [], "checkContextTypes": , "checkChildContextTypes": }] 50 | ... 51 | ``` 52 | 53 | ### `forbid` 54 | 55 | An array of strings, with the names of `PropTypes` keys that are forbidden. The default value for this option is `['any', 'array', 'object']`. 56 | 57 | ### `checkContextTypes` 58 | 59 | Whether or not to check `contextTypes` for forbidden prop types. The default value is `false`. 60 | 61 | ### `checkChildContextTypes` 62 | 63 | Whether or not to check `childContextTypes` for forbidden prop types. The default value is `false`. 64 | 65 | ## When Not To Use It 66 | 67 | This rule is a formatting/documenting preference and not following it won't negatively affect the quality of your code. This rule encourages prop types that more specifically document their usage. 68 | -------------------------------------------------------------------------------- /docs/rules/forward-ref-uses-ref.md: -------------------------------------------------------------------------------- 1 | # Require all forwardRef components include a ref parameter (`react/forward-ref-uses-ref`) 2 | 3 | 💡 This rule is manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions). 4 | 5 | 6 | 7 | Requires that components wrapped with `forwardRef` must have a `ref` parameter. Omitting the `ref` argument is usually a bug, and components not using `ref` don't need to be wrapped by `forwardRef`. 8 | 9 | See 10 | 11 | ## Rule Details 12 | 13 | This rule checks all React components using `forwardRef` and verifies that there is a second parameter. 14 | 15 | The following patterns are considered warnings: 16 | 17 | ```jsx 18 | var React = require('react'); 19 | 20 | var Component = React.forwardRef((props) => ( 21 |
22 | )); 23 | ``` 24 | 25 | The following patterns are **not** considered warnings: 26 | 27 | ```jsx 28 | var React = require('react'); 29 | 30 | var Component = React.forwardRef((props, ref) => ( 31 |
32 | )); 33 | 34 | var Component = React.forwardRef((props, ref) => ( 35 |
36 | )); 37 | 38 | function Component(props) { 39 | return
; 40 | }; 41 | ``` 42 | 43 | ## When not to use 44 | 45 | If you don't want to enforce that components using `forwardRef` utilize the forwarded ref. 46 | -------------------------------------------------------------------------------- /docs/rules/hook-use-state.md: -------------------------------------------------------------------------------- 1 | # Ensure destructuring and symmetric naming of useState hook value and setter variables (`react/hook-use-state`) 2 | 3 | 💡 This rule is manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions). 4 | 5 | 6 | 7 | 💡 This rule provides editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions). 8 | 9 | ## Rule Details 10 | 11 | This rule checks whether the value and setter variables destructured from a `React.useState()` call are named symmetrically. 12 | 13 | Examples of **incorrect** code for this rule: 14 | 15 | ```js 16 | import React from 'react'; 17 | export default function useColor() { 18 | // useState call is not destructured into value + setter pair 19 | const useStateResult = React.useState(); 20 | return useStateResult; 21 | } 22 | ``` 23 | 24 | ```js 25 | import React from 'react'; 26 | export default function useColor() { 27 | // useState call is destructured into value + setter pair, but identifier 28 | // names do not follow the [thing, setThing] naming convention 29 | const [color, updateColor] = React.useState(); 30 | return [color, updateColor]; 31 | } 32 | ``` 33 | 34 | Examples of **correct** code for this rule: 35 | 36 | ```js 37 | import React from 'react'; 38 | export default function useColor() { 39 | // useState call is destructured into value + setter pair whose identifiers 40 | // follow the [thing, setThing] naming convention 41 | const [color, setColor] = React.useState(); 42 | return [color, setColor]; 43 | } 44 | ``` 45 | 46 | ```js 47 | import React from 'react'; 48 | export default function useColor() { 49 | // useState result is directly returned 50 | return React.useState(); 51 | } 52 | ``` 53 | 54 | ## Rule Options 55 | 56 | ```js 57 | ... 58 | "react/hook-use-state": [, { "allowDestructuredState": }] 59 | ... 60 | ``` 61 | 62 | ### `allowDestructuredState` 63 | 64 | When `true` the rule will ignore the name of the destructured value. 65 | 66 | Examples of **correct** code for this rule, when configured with `{ "allowDestructuredState": true }`: 67 | 68 | ```jsx 69 | import React from 'react'; 70 | const [{foo, bar, baz}, setFooBarBaz] = React.useState({foo: "bbb", bar: "aaa", baz: "qqq"}) 71 | ``` 72 | -------------------------------------------------------------------------------- /docs/rules/iframe-missing-sandbox.md: -------------------------------------------------------------------------------- 1 | # Enforce sandbox attribute on iframe elements (`react/iframe-missing-sandbox`) 2 | 3 | 4 | 5 | The sandbox attribute enables an extra set of restrictions for the content in the iframe. Using sandbox attribute is considered a good security practice. 6 | 7 | See 8 | 9 | ## Rule Details 10 | 11 | This rule checks all React iframe elements and verifies that there is sandbox attribute and that it's value is valid. In addition to that it also reports cases where attribute contains `allow-scripts` and `allow-same-origin` at the same time as this combination allows the embedded document to remove the sandbox attribute and bypass the restrictions. 12 | 13 | The following patterns are considered warnings: 14 | 15 | ```jsx 16 | var React = require('react'); 17 | 18 | var Frame = () => ( 19 |
20 | 21 | {React.createElement('iframe')} 22 |
23 | ); 24 | ``` 25 | 26 | The following patterns are **not** considered warnings: 27 | 28 | ```jsx 29 | var React = require('react'); 30 | 31 | var Frame = 35 | {React.createElement('iframe', { sandbox: "allow-popups" })} 36 |
37 | ); 38 | ``` 39 | 40 | ## When not to use 41 | 42 | If you don't want to enforce sandbox attribute on iframe elements. 43 | -------------------------------------------------------------------------------- /docs/rules/jsx-boolean-value.md: -------------------------------------------------------------------------------- 1 | # Enforce boolean attributes notation in JSX (`react/jsx-boolean-value`) 2 | 3 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 4 | 5 | 6 | 7 | When using a [boolean attribute in JSX](https://web.archive.org/web/20160607204033/http://facebook.github.io/react/docs/jsx-in-depth.html#boolean-attributes), you can set the attribute value to `true` or omit the value. 8 | 9 | ## Rule Details 10 | 11 | This rule will enforce one or the other to keep consistency in your code. 12 | 13 | ## Rule Options 14 | 15 | This rule takes two arguments. If the first argument is `"always"` then it warns whenever an attribute is missing its value. If `"never"` then it warns if an attribute has a `true` value. The default value of this option is `"never"`. 16 | 17 | The second argument is optional. If provided, it must be an object. These properties are supported: 18 | 19 | First, the `"never"` and `"always"` properties are one set. The two properties cannot be set together. `"never"` must be used when the first argument is `"always"` and `"always"` must be used when the first argument is `"never"`. This property’s value must be an array of strings representing prop names. 20 | 21 | When the first argument is `"never"`, a boolean `"assumeUndefinedIsFalse"` may be provided, which defaults to `false`. When `true`, an absent boolean prop will be treated as if it were explicitly set to `false`. 22 | 23 | Examples of **incorrect** code for this rule, when configured with `"never"`, or with `"always", { "never": ["personal"] }`: 24 | 25 | ```jsx 26 | var Hello = ; 27 | ``` 28 | 29 | Examples of **correct** code for this rule, when configured with `"never"`, or with `"always", { "never": ["personal"] }`: 30 | 31 | ```jsx 32 | var Hello = ; 33 | ``` 34 | 35 | Examples of **incorrect** code for this rule, when configured with `"always"`, or with `"never", { "always": ["personal"] }`: 36 | 37 | ```jsx 38 | var Hello = ; 39 | ``` 40 | 41 | Examples of **correct** code for this rule, when configured with `"always"`, or with `"never", { "always": ["personal"] }`: 42 | 43 | ```jsx 44 | var Hello = ; 45 | ``` 46 | 47 | Examples of **incorrect** code for this rule, when configured with `"never", { "assumeUndefinedIsFalse": true }`, or with `"always", { "never": ["personal"], "assumeUndefinedIsFalse": true }`: 48 | 49 | ```jsx 50 | var Hello = ; 51 | ``` 52 | 53 | Examples of **correct** code for this rule, when configured with `"never", { "assumeUndefinedIsFalse": true }`, or with `"always", { "never": ["personal"], "assumeUndefinedIsFalse": true }`: 54 | 55 | ```jsx 56 | var Hello = ; 57 | ``` 58 | 59 | ## When Not To Use It 60 | 61 | If you do not want to enforce any style for boolean attributes, then you can disable this rule. 62 | -------------------------------------------------------------------------------- /docs/rules/jsx-child-element-spacing.md: -------------------------------------------------------------------------------- 1 | # Enforce or disallow spaces inside of curly braces in JSX attributes and expressions (`react/jsx-child-element-spacing`) 2 | 3 | 4 | 5 | ## Rule Details 6 | 7 | Since React removes extraneous new lines between elements when possible, it is possible to end up with inline elements that are not rendered with spaces between them and adjacent text. This is often indicative of an error, so this rule attempts to detect JSX markup with ambiguous spacing. 8 | 9 | Examples of **incorrect** code for this rule: 10 | 11 | ```jsx 12 |
13 | Here is a 14 | link 15 |
16 | ``` 17 | 18 | ```jsx 19 |
20 | This text 21 | is bold 22 |
23 | ``` 24 | 25 | Examples of **correct** code for this rule: 26 | 27 | ```jsx 28 |
29 | Spacing is 30 | {' '} 31 | explicit 32 |
33 | ``` 34 | 35 | ```jsx 36 |
37 | Lack of spacing is{/* 38 | */}explicit 39 |
40 | ``` 41 | 42 | ## When Not To Use It 43 | 44 | You can turn this rule off if you are not concerned with inline elements appearing adjacent to text, 45 | or if you always explicitly include `{' '}` between elements to denote spacing. 46 | -------------------------------------------------------------------------------- /docs/rules/jsx-equals-spacing.md: -------------------------------------------------------------------------------- 1 | # Enforce or disallow spaces around equal signs in JSX attributes (`react/jsx-equals-spacing`) 2 | 3 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 4 | 5 | 6 | 7 | Some style guides require or disallow spaces around equal signs. 8 | 9 | ## Rule Details 10 | 11 | This rule will enforce consistency of spacing around equal signs in JSX attributes, by requiring or disallowing one or more spaces before and after `=`. 12 | 13 | ## Rule Options 14 | 15 | There are two options for the rule: 16 | 17 | - `"always"` enforces spaces around the equal sign 18 | - `"never"` disallows spaces around the equal sign (default) 19 | 20 | Depending on your coding conventions, you can choose either option by specifying it in your configuration: 21 | 22 | ```json 23 | "react/jsx-equals-spacing": [2, "always"] 24 | ``` 25 | 26 | ### never 27 | 28 | Examples of **incorrect** code for this rule, when configured with `"never"`: 29 | 30 | ```jsx 31 | ; 32 | ; 33 | ; 34 | ``` 35 | 36 | Examples of **correct** code for this rule, when configured with `"never"`: 37 | 38 | ```jsx 39 | ; 40 | ; 41 | ; 42 | ``` 43 | 44 | ### always 45 | 46 | Examples of **incorrect** code for this rule, when configured with `"always"`: 47 | 48 | ```jsx 49 | ; 50 | ; 51 | ; 52 | ``` 53 | 54 | Examples of **correct** code for this rule, when configured with `"always"`: 55 | 56 | ```jsx 57 | ; 58 | ; 59 | ; 60 | ``` 61 | 62 | ## When Not To Use It 63 | 64 | You can turn this rule off if you are not concerned with the consistency of spacing around equal signs in JSX attributes. 65 | -------------------------------------------------------------------------------- /docs/rules/jsx-filename-extension.md: -------------------------------------------------------------------------------- 1 | # Disallow file extensions that may contain JSX (`react/jsx-filename-extension`) 2 | 3 | 4 | 5 | ## Rule Details 6 | 7 | Examples of **incorrect** code for this rule: 8 | 9 | ```jsx 10 | // filename: MyComponent.js 11 | function MyComponent() { 12 | return
; 13 | } 14 | ``` 15 | 16 | Examples of **correct** code for this rule: 17 | 18 | ```jsx 19 | // filename: MyComponent.jsx 20 | function MyComponent() { 21 | return
; 22 | } 23 | ``` 24 | 25 | Beware this rule **only** reports JSX syntax, **not** other non-standard syntax such as experimental features or type annotations. 26 | 27 | ## Rule Options 28 | 29 | ### `allow` (default: `"always"`) 30 | 31 | When to allow a JSX filename extension. By default all files may have a JSX extension. Set this to `as-needed` to only allow JSX file extensions in files that contain JSX syntax. 32 | 33 | ```js 34 | "rules": { 35 | "react/jsx-filename-extension": [1, { "allow": "as-needed" }] 36 | } 37 | ``` 38 | 39 | ### `extensions` (default: `[".jsx"]`) 40 | 41 | The set of allowed extensions is configurable. By default '.jsx' is allowed. If you wanted to allow both '.jsx' and '.js', the configuration would be: 42 | 43 | ```js 44 | "rules": { 45 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }] 46 | } 47 | ``` 48 | 49 | ### `ignoreFilesWithoutCode` (default: `false`) 50 | 51 | If enabled, files that do not contain code (i.e. are empty, contain only whitespaces or comments) will not be rejected. 52 | 53 | ```js 54 | "rules": { 55 | "react/jsx-filename-extension": [1, { "ignoreFilesWithoutCode": true }] 56 | } 57 | ``` 58 | 59 | ## When Not To Use It 60 | 61 | If you don't care about restricting the file extensions that may contain JSX. 62 | -------------------------------------------------------------------------------- /docs/rules/jsx-fragments.md: -------------------------------------------------------------------------------- 1 | # Enforce shorthand or standard form for React fragments (`react/jsx-fragments`) 2 | 3 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 4 | 5 | 6 | 7 | In JSX, a React [fragment] is created either with `...`, or, using the shorthand syntax, `<>...`. 8 | 9 | ## Rule Details 10 | 11 | This rule allows you to enforce one way or the other. 12 | 13 | Support for fragments was added in React v16.2, so the rule will warn on either of these forms if an older React version is specified in [shared settings][shared_settings]. 14 | 15 | ## Rule Options 16 | 17 | ```js 18 | ... 19 | "react/jsx-fragments": [, ] 20 | ... 21 | ``` 22 | 23 | ### `syntax` mode 24 | 25 | This is the default mode. It will enforce the shorthand syntax for React fragments, with one exception. [Keys or attributes are not supported by the shorthand syntax][short_syntax], so the rule will not warn on standard-form fragments that use those. 26 | 27 | Examples of **incorrect** code for this rule: 28 | 29 | ```jsx 30 | 31 | ``` 32 | 33 | Examples of **correct** code for this rule: 34 | 35 | ```jsx 36 | <> 37 | ``` 38 | 39 | ```jsx 40 | 41 | ``` 42 | 43 | ### `element` mode 44 | 45 | This mode enforces the standard form for React fragments. 46 | 47 | Examples of **incorrect** code for this rule: 48 | 49 | ```jsx 50 | <> 51 | ``` 52 | 53 | Examples of **correct** code for this rule: 54 | 55 | ```jsx 56 | 57 | ``` 58 | 59 | ```jsx 60 | 61 | ``` 62 | 63 | [fragment]: https://reactjs.org/docs/fragments.html 64 | [shared_settings]: /README.md#configuration 65 | [short_syntax]: https://reactjs.org/docs/fragments.html#short-syntax 66 | -------------------------------------------------------------------------------- /docs/rules/jsx-handler-names.md: -------------------------------------------------------------------------------- 1 | # Enforce event handler naming conventions in JSX (`react/jsx-handler-names`) 2 | 3 | 4 | 5 | Ensures that any component or prop methods used to handle events are correctly prefixed. 6 | 7 | ## Rule Details 8 | 9 | Examples of **incorrect** code for this rule: 10 | 11 | ```jsx 12 | 13 | ``` 14 | 15 | ```jsx 16 | 17 | ``` 18 | 19 | Examples of **correct** code for this rule: 20 | 21 | ```jsx 22 | 23 | ``` 24 | 25 | ```jsx 26 | 27 | ``` 28 | 29 | ## Rule Options 30 | 31 | ```js 32 | ... 33 | "react/jsx-handler-names": [, { 34 | "eventHandlerPrefix": , 35 | "eventHandlerPropPrefix": , 36 | "checkLocalVariables": , 37 | "checkInlineFunction": , 38 | "ignoreComponentNames": Array 39 | }] 40 | ... 41 | ``` 42 | 43 | - `eventHandlerPrefix`: Prefix for component methods used as event handlers. Defaults to `handle` 44 | - `eventHandlerPropPrefix`: Prefix for props that are used as event handlers. Defaults to `on` 45 | - `checkLocalVariables`: Determines whether event handlers stored as local variables are checked. Defaults to `false` 46 | - `checkInlineFunction`: Determines whether event handlers set as inline functions are checked. Defaults to `false` 47 | - `ignoreComponentNames`: Array of glob strings, when matched with component name, ignores the rule on that component. Defaults to `[]` 48 | 49 | ## When Not To Use It 50 | 51 | If you are not using JSX, or if you don't want to enforce specific naming conventions for event handlers. 52 | -------------------------------------------------------------------------------- /docs/rules/jsx-indent.md: -------------------------------------------------------------------------------- 1 | # Enforce JSX indentation (`react/jsx-indent`) 2 | 3 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 4 | 5 | 6 | 7 | This option validates a specific indentation style for JSX. 8 | 9 | Note: The fixer will fix whitespace and tabs indentation. 10 | 11 | ## Rule Details 12 | 13 | This rule is aimed to enforce consistent indentation style. The default style is `4 spaces`. 14 | 15 | Examples of **incorrect** code for this rule: 16 | 17 | ```jsx 18 | // 2 spaces indentation 19 | 20 | 21 | 22 | 23 | // no indentation 24 | 25 | 26 | 27 | 28 | // 1 tab indentation 29 | 30 | 31 | 32 | ``` 33 | 34 | ## Rule Options 35 | 36 | It takes an option as the second parameter which can be `"tab"` for tab-based indentation or a positive number for space indentations. 37 | To enable checking the indentation of attributes or add indentation to logical expressions, use the third parameter to turn on the `checkAttributes` (default is false) and `indentLogicalExpressions` (default is false) respectively. 38 | 39 | ```js 40 | ... 41 | "react/jsx-indent": [, 'tab'|, {checkAttributes: , indentLogicalExpressions: }] 42 | ... 43 | ``` 44 | 45 | Examples of **incorrect** code for this rule: 46 | 47 | ```jsx 48 | // 2 spaces indentation 49 | // [2, 2] 50 | 51 | 52 | 53 | 54 | // tab indentation 55 | // [2, 'tab'] 56 | 57 | 58 | 59 | 60 | // [2, 2, {checkAttributes: true}] 61 |
hi
64 | } 65 | /> 66 | }> 67 |
68 | 69 | // [2, 2, {indentLogicalExpressions: true}] 70 | 71 | {condition && ( 72 | 73 | )} 74 | 75 | ``` 76 | 77 | Examples of **correct** code for this rule: 78 | 79 | ```jsx 80 | 81 | // 2 spaces indentation 82 | // [2, 2] 83 | 84 | 85 | 86 | 87 | // tab indentation 88 | // [2, 'tab'] 89 | 90 | 91 | 92 | 93 | // no indentation 94 | // [2, 0] 95 | 96 | 97 | 98 | 99 | // [2, 2, {checkAttributes: false}] 100 |
hi
103 | } 104 | /> 105 | }> 106 |
107 | 108 | // [2, 2, {indentLogicalExpressions: true}] 109 | 110 | {condition && ( 111 | 112 | )} 113 | 114 | ``` 115 | 116 | ## When Not To Use It 117 | 118 | If you are not using JSX then you can disable this rule. 119 | -------------------------------------------------------------------------------- /docs/rules/jsx-key.md: -------------------------------------------------------------------------------- 1 | # Disallow missing `key` props in iterators/collection literals (`react/jsx-key`) 2 | 3 | 💼 This rule is enabled in the ☑️ `recommended` [config](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs). 4 | 5 | 6 | 7 | Warn if an element that likely requires a `key` prop--namely, one present in an 8 | array literal or an arrow function expression. 9 | 10 | ## Rule Details 11 | 12 | Examples of **incorrect** code for this rule: 13 | 14 | ```jsx 15 | [, , ]; 16 | ``` 17 | 18 | ```jsx 19 | data.map(x => {x}); 20 | ``` 21 | 22 | ```jsx 23 | Array.from([1, 2, 3], (x) => {x}); 24 | ``` 25 | 26 | ```jsx 27 | 28 | ``` 29 | 30 | In the last example the key is being spread, which is currently possible, but discouraged in favor of the statically provided key. 31 | 32 | Examples of **correct** code for this rule: 33 | 34 | ```jsx 35 | [, , ]; 36 | ``` 37 | 38 | ```jsx 39 | data.map((x) => {x}); 40 | ``` 41 | 42 | ```jsx 43 | Array.from([1, 2, 3], (x) => {x}); 44 | ``` 45 | 46 | ```jsx 47 | 48 | ``` 49 | 50 | ## Rule Options 51 | 52 | ```js 53 | ... 54 | "react/jsx-key": [, { "checkFragmentShorthand": }] 55 | ... 56 | ``` 57 | 58 | ### `checkFragmentShorthand` (default: `false`) 59 | 60 | When `true` the rule will check if usage of the [shorthand fragment syntax][short_syntax] requires a key. This option was added to avoid a breaking change and will be the default in the next major version. 61 | 62 | Examples of **incorrect** code for this rule: 63 | 64 | ```jsx 65 | [<>, <>, <>]; 66 | ``` 67 | 68 | ```jsx 69 | data.map(x => <>{x}); 70 | ``` 71 | 72 | ### `checkKeyMustBeforeSpread` (default: `false`) 73 | 74 | When `true` the rule will check if key prop after spread to avoid [createElement fallback](https://github.com/facebook/react/issues/20031#issuecomment-710346866). 75 | 76 | Examples of **incorrect** code for this rule: 77 | 78 | ```jsx 79 | ; 80 | ``` 81 | 82 | ### `warnOnDuplicates` (default: `false`) 83 | 84 | When `true` the rule will check for any duplicate key prop values. 85 | 86 | Examples of **incorrect** code for this rule: 87 | 88 | ```jsx 89 | const spans = [ 90 | , 91 | , 92 | ]; 93 | ``` 94 | 95 | ## When Not To Use It 96 | 97 | If you are not using JSX then you can disable this rule. 98 | 99 | Also, if you have some prevalent situation where you use arrow functions to 100 | return JSX that will not be held in an iterable, you may want to disable this 101 | rule. 102 | 103 | [short_syntax]: https://reactjs.org/docs/fragments.html#short-syntax 104 | -------------------------------------------------------------------------------- /docs/rules/jsx-max-depth.md: -------------------------------------------------------------------------------- 1 | # Enforce JSX maximum depth (`react/jsx-max-depth`) 2 | 3 | 4 | 5 | This option validates a specific depth for JSX. 6 | 7 | ## Rule Details 8 | 9 | Examples of **incorrect** code for this rule: 10 | 11 | ```jsx 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ``` 21 | 22 | ## Rule Options 23 | 24 | It takes an option as the second parameter which can be a positive number for depth count. 25 | 26 | ```js 27 | ... 28 | "react/jsx-max-depth": [, { "max": }] 29 | ... 30 | ``` 31 | 32 | Examples of **incorrect** code for this rule: 33 | 34 | ```jsx 35 | // [2, { "max": 1 }] 36 | 37 | 38 | 39 | 40 | 41 | 42 | // [2, { "max": 1 }] 43 | const foobar = ; 44 | 45 | {foobar} 46 | 47 | 48 | // [2, { "max": 2 }] 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | ``` 57 | 58 | Examples of **correct** code for this rule: 59 | 60 | ```jsx 61 | 62 | // [2, { "max": 1 }] 63 | 64 | 65 | 66 | 67 | // [2,{ "max": 2 }] 68 | 69 | 70 | 71 | 72 | 73 | 74 | // [2, { "max": 3 }] 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | ``` 83 | 84 | ## When Not To Use It 85 | 86 | If you are not using JSX then you can disable this rule. 87 | -------------------------------------------------------------------------------- /docs/rules/jsx-no-comment-textnodes.md: -------------------------------------------------------------------------------- 1 | # Disallow comments from being inserted as text nodes (`react/jsx-no-comment-textnodes`) 2 | 3 | 💼 This rule is enabled in the ☑️ `recommended` [config](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs). 4 | 5 | 6 | 7 | This rule prevents comment strings (e.g. beginning with `//` or `/*`) from being accidentally 8 | injected as a text node in JSX statements. 9 | 10 | ## Rule Details 11 | 12 | Examples of **incorrect** code for this rule: 13 | 14 | ```jsx 15 | var Hello = createReactClass({ 16 | render: function() { 17 | return ( 18 |
// empty div
19 | ); 20 | } 21 | }); 22 | 23 | var Hello = createReactClass({ 24 | render: function() { 25 | return ( 26 |
27 | /* empty div */ 28 |
29 | ); 30 | } 31 | }); 32 | ``` 33 | 34 | Examples of **correct** code for this rule: 35 | 36 | ```jsx 37 | var Hello = createReactClass({ 38 | displayName: 'Hello', 39 | render: function() { 40 | return
{/* empty div */}
; 41 | } 42 | }); 43 | 44 | var Hello = createReactClass({ 45 | displayName: 'Hello', 46 | render: function() { 47 | return
; 48 | } 49 | }); 50 | 51 | var Hello = createReactClass({ 52 | displayName: 'Hello', 53 | render: function() { 54 | return
; 55 | } 56 | }); 57 | ``` 58 | 59 | ## Legitimate uses 60 | 61 | It's possible you may want to legitimately output comment start characters (`//` or `/*`) in a JSX text node. In which case, you can do the following: 62 | 63 | ```jsx 64 | var Hello = createReactClass({ 65 | render: function() { 66 | return ( 67 |
{'/* This will be output as a text node */'}
68 | ); 69 | } 70 | }); 71 | ``` 72 | -------------------------------------------------------------------------------- /docs/rules/jsx-no-constructed-context-values.md: -------------------------------------------------------------------------------- 1 | # Disallows JSX context provider values from taking values that will cause needless rerenders (`react/jsx-no-constructed-context-values`) 2 | 3 | 4 | 5 | This rule prevents non-stable values (i.e. object identities) from being used as a value for `Context.Provider`. 6 | 7 | ## Rule Details 8 | 9 | One way to resolve this issue may be to wrap the value in a `useMemo()`. If it's a function then `useCallback()` can be used as well. 10 | 11 | If you _expect_ the context to be rerun on each render, then consider adding a comment/lint suppression explaining why. 12 | 13 | ## Examples 14 | 15 | Examples of **incorrect** code for this rule: 16 | 17 | ```jsx 18 | return ( 19 | 20 | ... 21 | 22 | ) 23 | ``` 24 | 25 | ```jsx 26 | import React from 'react'; 27 | 28 | const MyContext = React.createContext(); 29 | function Component() { 30 | function foo() {} 31 | return (); 32 | } 33 | ``` 34 | 35 | Examples of **correct** code for this rule: 36 | 37 | ```jsx 38 | const foo = useMemo(() => ({foo: 'bar'}), []); 39 | return ( 40 | 41 | ... 42 | 43 | ) 44 | ``` 45 | 46 | ```jsx 47 | const SomeContext = createContext(); 48 | const Component = () => ; 49 | ``` 50 | 51 | ## Legitimate Uses 52 | 53 | React Context, and all its child nodes and Consumers are rerendered whenever the value prop changes. Because each Javascript object carries its own _identity_, things like object expressions (`{foo: 'bar'}`) or function expressions get a new identity on every run through the component. This makes the context think it has gotten a new object and can cause needless rerenders and unintended consequences. 54 | 55 | This can be a pretty large performance hit because not only will it cause the context providers and consumers to rerender with all the elements in its subtree, the processing for the tree scan react does to render the provider and find consumers is also wasted. 56 | -------------------------------------------------------------------------------- /docs/rules/jsx-no-duplicate-props.md: -------------------------------------------------------------------------------- 1 | # Disallow duplicate properties in JSX (`react/jsx-no-duplicate-props`) 2 | 3 | 💼 This rule is enabled in the ☑️ `recommended` [config](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs). 4 | 5 | 6 | 7 | Creating JSX elements with duplicate props can cause unexpected behavior in your application. 8 | 9 | ## Rule Details 10 | 11 | Examples of **incorrect** code for this rule: 12 | 13 | ```jsx 14 | ; 15 | ``` 16 | 17 | Examples of **correct** code for this rule: 18 | 19 | ```jsx 20 | ; 21 | ``` 22 | 23 | ## Rule Options 24 | 25 | ```js 26 | ... 27 | "react/jsx-no-duplicate-props": [, { "ignoreCase": }] 28 | ... 29 | ``` 30 | 31 | ### `ignoreCase` 32 | 33 | When `true` the rule ignores the case of the props. Default to `false`. 34 | 35 | ## When Not To Use It 36 | 37 | If you are not using JSX then you can disable this rule. 38 | -------------------------------------------------------------------------------- /docs/rules/jsx-no-script-url.md: -------------------------------------------------------------------------------- 1 | # Disallow usage of `javascript:` URLs (`react/jsx-no-script-url`) 2 | 3 | 4 | 5 | **In React 16.9** any URLs starting with `javascript:` [scheme](https://wiki.whatwg.org/wiki/URL_schemes#javascript:_URLs) log a warning. 6 | React considers the pattern as a dangerous attack surface, see [details](https://reactjs.org/blog/2019/08/08/react-v16.9.0.html#deprecating-javascript-urls). 7 | **In a future major release**, React will throw an error if it encounters a `javascript:` URL. 8 | 9 | ## Rule Details 10 | 11 | Examples of **incorrect** code for this rule: 12 | 13 | ```jsx 14 | 15 | 16 | 17 | ``` 18 | 19 | Examples of **correct** code for this rule: 20 | 21 | ```jsx 22 | 23 | 24 | ``` 25 | 26 | This rule takes the `linkComponents` setting into account. 27 | 28 | ## Rule Options 29 | 30 | This rule accepts array option (optional) and object option (optional). 31 | 32 | ### Array option (default `[]`) 33 | 34 | ```json 35 | { 36 | "react/jsx-no-script-url": [ 37 | "error", 38 | [ 39 | { 40 | "name": "Link", 41 | "props": ["to"] 42 | }, 43 | { 44 | "name": "Foo", 45 | "props": ["href", "to"] 46 | } 47 | ] 48 | ] 49 | } 50 | ``` 51 | 52 | Allows you to indicate a specific list of properties used by a custom component to be checked. 53 | 54 | #### name 55 | 56 | Component name. 57 | 58 | #### props 59 | 60 | List of properties that should be validated. 61 | 62 | Examples of **incorrect** code for this rule, when configured with the above options: 63 | 64 | ```jsx 65 | 66 | 67 | 68 | ``` 69 | 70 | ### Object option 71 | 72 | #### includeFromSettings (default `false`) 73 | 74 | Indicates if the `linkComponents` config in [global shared settings](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/README.md#configuration) should also be taken into account. If enabled, components and properties defined in settings will be added to the list provided in first option (if provided): 75 | 76 | ```json 77 | { 78 | "react/jsx-no-script-url": [ 79 | "error", 80 | [ 81 | { 82 | "name": "Link", 83 | "props": ["to"] 84 | }, 85 | { 86 | "name": "Foo", 87 | "props": ["href", "to"] 88 | } 89 | ], 90 | { "includeFromSettings": true } 91 | ] 92 | } 93 | ``` 94 | 95 | If only global settings should be used for this rule, the array option can be omitted: 96 | 97 | ```jsonc 98 | { 99 | // same as ["error", [], { "includeFromSettings": true }] 100 | "react/jsx-no-script-url": ["error", { "includeFromSettings": true }] 101 | } 102 | ``` 103 | -------------------------------------------------------------------------------- /docs/rules/jsx-no-undef.md: -------------------------------------------------------------------------------- 1 | # Disallow undeclared variables in JSX (`react/jsx-no-undef`) 2 | 3 | 💼 This rule is enabled in the ☑️ `recommended` [config](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs). 4 | 5 | 6 | 7 | This rule helps locate potential ReferenceErrors resulting from misspellings or missing components. 8 | 9 | ## Rule Details 10 | 11 | Examples of **incorrect** code for this rule: 12 | 13 | ```jsx 14 | ; 15 | ``` 16 | 17 | ```jsx 18 | // will ignore Text in the global scope and warn 19 | var Hello = React.createClass({ 20 | render: function() { 21 | return Hello; 22 | } 23 | }); 24 | module.exports = Hello; 25 | ``` 26 | 27 | Examples of **correct** code for this rule: 28 | 29 | ```jsx 30 | var Hello = require('./Hello'); 31 | 32 | ; 33 | ``` 34 | 35 | ## Rule Options 36 | 37 | ```js 38 | ... 39 | "react/jsx-no-undef": [, { "allowGlobals": }] 40 | ... 41 | ``` 42 | 43 | ### `allowGlobals` 44 | 45 | When `true` the rule will consider the global scope when checking for defined Components. 46 | 47 | Examples of **correct** code for this rule, when `"allowGlobals"` is `true`: 48 | 49 | ```jsx 50 | var Text = require('./Text'); 51 | var Hello = React.createClass({ 52 | render: function() { 53 | return Hello; 54 | } 55 | }); 56 | module.exports = Hello; 57 | ``` 58 | 59 | ## When Not To Use It 60 | 61 | If you are not using JSX then you can disable this rule. 62 | -------------------------------------------------------------------------------- /docs/rules/jsx-no-useless-fragment.md: -------------------------------------------------------------------------------- 1 | # Disallow unnecessary fragments (`react/jsx-no-useless-fragment`) 2 | 3 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 4 | 5 | 6 | 7 | A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a [keyed fragment](https://reactjs.org/docs/fragments.html#keyed-fragments). 8 | 9 | ## Rule Details 10 | 11 | Examples of **incorrect** code for this rule: 12 | 13 | ```jsx 14 | <>{foo} 15 | 16 | <> 17 | 18 |

<>foo

19 | 20 | <> 21 | 22 | foo 23 | 24 | foo 25 | 26 |
27 | <> 28 |
29 |
30 | 31 |
32 | 33 | {showFullName ? <>{fullName} : <>{firstName}} 34 | ``` 35 | 36 | Examples of **correct** code for this rule: 37 | 38 | ```jsx 39 | {foo} 40 | 41 | 42 | 43 | <> 44 | 45 | 46 | 47 | 48 | <>foo {bar} 49 | 50 | <> {foo} 51 | 52 | const cat = <>meow 53 | 54 | 55 | <> 56 |
57 |
58 | 59 | 60 | 61 | {item.value} 62 | 63 | {showFullName ? fullName : firstName} 64 | ``` 65 | 66 | ## Rule Options 67 | 68 | ### `allowExpressions` 69 | 70 | When `true` single expressions in a fragment will be allowed. This is useful in 71 | places like Typescript where `string` does not satisfy the expected return type 72 | of `JSX.Element`. A common workaround is to wrap the variable holding a string 73 | in a fragment and expression. 74 | 75 | Examples of **correct** code for the rule, when `"allowExpressions"` is `true`: 76 | 77 | ```jsx 78 | <>{foo} 79 | 80 | <> 81 | {foo} 82 | 83 | ``` 84 | -------------------------------------------------------------------------------- /docs/rules/jsx-one-expression-per-line.md: -------------------------------------------------------------------------------- 1 | # Require one JSX element per line (`react/jsx-one-expression-per-line`) 2 | 3 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 4 | 5 | 6 | 7 | This option limits every line in JSX to one expression each. 8 | 9 | Note: The fixer will insert line breaks between any expression that are on the same line. 10 | 11 | ## Rule Details 12 | 13 | Examples of **incorrect** code for this rule: 14 | 15 | ```jsx 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | World 27 | 28 | 29 | 30 | { 'World' } 31 | 32 | 33 | 34 | { this.world() } 35 | 36 | 37 | 38 | { 'Hello' }{ ' ' }{ 'World' } 39 | 40 | 41 | 44 | 45 | 46 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | ``` 56 | 57 | Examples of **correct** code for this rule: 58 | 59 | ```jsx 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | World 72 | 73 | 74 | 75 | 76 | { 'World' } 77 | 78 | 79 | 80 | 81 | { this.world() } 82 | 83 | 84 | 85 | { 'Hello' } 86 | { ' ' } 87 | { 'World' } 88 | 89 | 90 | 93 | 94 | 95 | 96 | 97 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | ``` 108 | 109 | ## Rule Options 110 | 111 | ```js 112 | ... 113 | "react/jsx-one-expression-per-line": [, { "allow": "none"|"literal"|"single-child" }] 114 | ... 115 | ``` 116 | 117 | ### `allow` 118 | 119 | Defaults to `none`. 120 | 121 | Examples of **correct** code for this rule, when configured as `"literal"`: 122 | 123 | ```jsx 124 | Hello 125 | ``` 126 | 127 | Examples of **correct** code for this rule, when configured as `"single-child"`: 128 | 129 | ```jsx 130 | Hello 131 | 132 | {"Hello"} 133 | 134 | 135 | ``` 136 | 137 | Examples of **correct** code for this rule, when configured as `"non-jsx"`: 138 | 139 | ```jsx 140 | Hello {someVariable} 141 | 142 | Hello {} there! 143 | ``` 144 | -------------------------------------------------------------------------------- /docs/rules/jsx-pascal-case.md: -------------------------------------------------------------------------------- 1 | # Enforce PascalCase for user-defined JSX components (`react/jsx-pascal-case`) 2 | 3 | 4 | 5 | Enforces coding style that user-defined JSX components are defined and referenced in PascalCase. 6 | 7 | Note that since React's JSX uses the upper vs. lower case convention to distinguish between local component classes and HTML tags this rule will not warn on components that start with a lower case letter. 8 | 9 | ## Rule Details 10 | 11 | Examples of **incorrect** code for this rule: 12 | 13 | ```jsx 14 | 15 | ``` 16 | 17 | ```jsx 18 | 19 | ``` 20 | 21 | Examples of **correct** code for this rule: 22 | 23 | ```jsx 24 |
25 | ``` 26 | 27 | ```jsx 28 | 29 | ``` 30 | 31 | ```jsx 32 | 33 |
34 | 35 | ``` 36 | 37 | ```jsx 38 | 39 | ``` 40 | 41 | ## Rule Options 42 | 43 | ```js 44 | ... 45 | "react/jsx-pascal-case": [, { allowAllCaps: , allowNamespace: , allowLeadingUnderscore: , ignore: }] 46 | ... 47 | ``` 48 | 49 | - `enabled`: for enabling the rule. 0=off, 1=warn, 2=error. Defaults to 0. 50 | - `allowAllCaps`: optional boolean set to `true` to allow components name in all caps (default to `false`). 51 | - `allowLeadingUnderscore`: optional boolean set to `true` to allow components name with that starts with an underscore (default to `false`). 52 | - `allowNamespace`: optional boolean set to `true` to ignore namespaced components (default to `false`). 53 | - `ignore`: optional string-array of component names to ignore during validation (supports [minimatch](https://github.com/isaacs/minimatch)-style globs). 54 | 55 | ### `allowAllCaps` 56 | 57 | Examples of **correct** code for this rule, when `allowAllCaps` is `true`: 58 | 59 | ```jsx 60 | 61 | 62 | ``` 63 | 64 | ### `allowNamespace` 65 | 66 | Examples of **correct** code for this rule, when `allowNamespace` is `true`: 67 | 68 | ```jsx 69 | 70 | 71 | ``` 72 | 73 | ### `allowLeadingUnderscore` 74 | 75 | Examples of **correct** code for this rule, when `allowLeadingUnderscore` is `true`: 76 | 77 | ```jsx 78 | <_AllowedComponent /> 79 | <_AllowedComponent> 80 |
81 | 82 | ``` 83 | 84 | **WARNING:** Adding a leading underscore to the name of a component does **NOT** affect the visibility or accessibility of that component. Attempting to use leading underscores to enforce privacy of your components is an error. 85 | 86 | ## When Not To Use It 87 | 88 | If you are not using JSX. 89 | -------------------------------------------------------------------------------- /docs/rules/jsx-props-no-multi-spaces.md: -------------------------------------------------------------------------------- 1 | # Disallow multiple spaces between inline JSX props (`react/jsx-props-no-multi-spaces`) 2 | 3 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 4 | 5 | 6 | 7 | Enforces that there is exactly one space between all attributes and after tag name and the first attribute in the same line. 8 | 9 | ## Rule Details 10 | 11 | Examples of **incorrect** code for this rule: 12 | 13 | ```jsx 14 | 15 | ``` 16 | 17 | ```jsx 18 | 19 | ``` 20 | 21 | ```jsx 22 | 26 | ``` 27 | 28 | Examples of **correct** code for this rule: 29 | 30 | ```jsx 31 | 32 | ``` 33 | 34 | ```jsx 35 | 36 | ``` 37 | 38 | ```jsx 39 | 42 | ``` 43 | 44 | ## When Not To Use It 45 | 46 | If you are not using JSX or don't care about the space between two props in the same line. 47 | 48 | If you have enabled the core rule `no-multi-spaces` with eslint >= 3, you don't need this rule. 49 | -------------------------------------------------------------------------------- /docs/rules/jsx-props-no-spread-multi.md: -------------------------------------------------------------------------------- 1 | # Disallow JSX prop spreading the same identifier multiple times (`react/jsx-props-no-spread-multi`) 2 | 3 | 4 | 5 | Enforces that any unique expression is only spread once. 6 | Generally spreading the same expression twice is an indicator of a mistake since any attribute between the spreads may be overridden when the intent was not to. 7 | Even when that is not the case this will lead to unnecessary computations being performed. 8 | 9 | ## Rule Details 10 | 11 | Examples of **incorrect** code for this rule: 12 | 13 | ```jsx 14 | 15 | ``` 16 | 17 | Examples of **correct** code for this rule: 18 | 19 | ```jsx 20 | 21 | 22 | ``` 23 | 24 | ## When Not To Use It 25 | 26 | When spreading the same expression multiple times yields different results. 27 | -------------------------------------------------------------------------------- /docs/rules/jsx-space-before-closing.md: -------------------------------------------------------------------------------- 1 | # Enforce spacing before closing bracket in JSX (`react/jsx-space-before-closing`) 2 | 3 | ❌ This rule is deprecated. It was replaced by [`react/jsx-tag-spacing`](jsx-tag-spacing.md). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | Please use the `"beforeSelfClosing"` option of the [jsx-tag-spacing](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-tag-spacing.md) rule instead. 10 | 11 | Enforce or forbid spaces before the closing bracket of self-closing JSX elements. 12 | 13 | ## Rule Details 14 | 15 | This rule checks if there is one or more spaces before the closing bracket of self-closing JSX elements. 16 | 17 | ## Rule Options 18 | 19 | This rule takes one argument. If it is `"always"` then it warns whenever a space is missing before the closing bracket. If `"never"` then it warns if a space is present before the closing bracket. The default value of this option is `"always"`. 20 | 21 | Examples of **incorrect** code for this rule, when configured with `"always"`: 22 | 23 | ```jsx 24 | 25 | 26 | ``` 27 | 28 | Examples of **correct** code for this rule, when configured with `"always"`: 29 | 30 | ```jsx 31 | 32 | 33 | 37 | ``` 38 | 39 | Examples of **incorrect** code for this rule, when configured with `"never"`: 40 | 41 | ```jsx 42 | 43 | 44 | ``` 45 | 46 | Examples of **correct** code for this rule, when configured with `"never"`: 47 | 48 | ```jsx 49 | 50 | 51 | 55 | ``` 56 | 57 | ## When Not To Use It 58 | 59 | You can turn this rule off if you are not concerned with the consistency of spacing before closing brackets. 60 | -------------------------------------------------------------------------------- /docs/rules/jsx-uses-react.md: -------------------------------------------------------------------------------- 1 | # Disallow React to be incorrectly marked as unused (`react/jsx-uses-react`) 2 | 3 | 💼🚫 This rule is enabled in the ☑️ `recommended` [config](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs). This rule is _disabled_ in the 🏃 `jsx-runtime` [config](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs). 4 | 5 | 6 | 7 | JSX expands to a call to `React.createElement`, a file which includes `React` 8 | but only uses JSX should consider the `React` variable as used. 9 | 10 | If you are using the @jsx pragma this rule will mark the designated variable and not the `React` one. 11 | 12 | This rule has no effect if the `no-unused-vars` rule is not enabled. 13 | 14 | You can use the [shared settings](/README.md#configuration) to specify a custom pragma. 15 | 16 | ## Rule Details 17 | 18 | Examples of **incorrect** code for this rule: 19 | 20 | ```js 21 | var React = require('react'); 22 | 23 | // nothing to do with React 24 | ``` 25 | 26 | ```jsx 27 | /** @jsx Foo */ 28 | var React = require('react'); 29 | 30 | var Hello =
Hello {this.props.name}
; 31 | ``` 32 | 33 | Examples of **correct** code for this rule: 34 | 35 | ```jsx 36 | var React = require('react'); 37 | 38 | var Hello =
Hello {this.props.name}
; 39 | ``` 40 | 41 | ```jsx 42 | /** @jsx Foo */ 43 | var Foo = require('foo'); 44 | 45 | var Hello =
Hello {this.props.name}
; 46 | ``` 47 | 48 | ## When Not To Use It 49 | 50 | If you are not using JSX, if React is declared as global variable, or if you do not use the `no-unused-vars` rule. 51 | 52 | If you are using the [new JSX transform from React 17](https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html#removing-unused-react-imports), you should disable this rule by extending [`react/jsx-runtime`](https://github.com/jsx-eslint/eslint-plugin-react/blob/HEAD/index.js#L163-L176) in your eslint config (add `"plugin:react/jsx-runtime"` to `"extends"`). 53 | -------------------------------------------------------------------------------- /docs/rules/jsx-uses-vars.md: -------------------------------------------------------------------------------- 1 | # Disallow variables used in JSX to be incorrectly marked as unused (`react/jsx-uses-vars`) 2 | 3 | 💼 This rule is enabled in the ☑️ `recommended` [config](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs). 4 | 5 | 6 | 7 | Since 0.17.0 the `eslint` `no-unused-vars` rule does not detect variables used in JSX ([see details](https://eslint.org/blog/2015/03/eslint-0.17.0-released#changes-to-jsxreact-handling)). This rule will find variables used in JSX and mark them as used. 8 | 9 | This rule only has an effect when the `no-unused-vars` rule is enabled. 10 | 11 | ## Rule Details 12 | 13 | Examples of **incorrect** code for this rule: 14 | 15 | ```js 16 | var Hello = require('./Hello'); 17 | ``` 18 | 19 | Examples of **correct** code for this rule: 20 | 21 | ```jsx 22 | var Hello = require('./Hello'); 23 | 24 | ; 25 | ``` 26 | 27 | ## When Not To Use It 28 | 29 | If you are not using JSX or if you do not use the `no-unused-vars` rule then you can disable this rule. 30 | -------------------------------------------------------------------------------- /docs/rules/no-access-state-in-setstate.md: -------------------------------------------------------------------------------- 1 | # Disallow when this.state is accessed within setState (`react/no-access-state-in-setstate`) 2 | 3 | 4 | 5 | Usage of `this.state` inside `setState` calls might result in errors when two state calls are called in batch and thus referencing old state and not the current state. 6 | 7 | ## Rule Details 8 | 9 | This rule should prevent usage of `this.state` inside `setState` calls. 10 | 11 | ## Examples 12 | 13 | An example can be an increment function: 14 | 15 | ```javascript 16 | function increment() { 17 | this.setState({value: this.state.value + 1}); 18 | } 19 | ``` 20 | 21 | If two `setState` operations are grouped together in a batch, they both evaluate the old state. Given that `state.value` is 1: 22 | 23 | ```javascript 24 | this.setState({value: this.state.value + 1}) // 2 25 | this.setState({value: this.state.value + 1}) // 2, not 3 26 | ``` 27 | 28 | This can be avoided with using callbacks which takes the previous state as first argument: 29 | 30 | ```javascript 31 | function increment() { 32 | this.setState(prevState => ({value: prevState.value + 1})); 33 | } 34 | ``` 35 | 36 | Then react will call the argument with the correct and updated state, even when things happen in batches. And the example above will be something like: 37 | 38 | ```javascript 39 | setState({value: 1 + 1}) 40 | setState({value: 2 + 1}) 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/rules/no-adjacent-inline-elements.md: -------------------------------------------------------------------------------- 1 | # Disallow adjacent inline elements not separated by whitespace (`react/no-adjacent-inline-elements`) 2 | 3 | 4 | 5 | Adjacent inline elements not separated by whitespace will bump up against each 6 | other when viewed in an unstyled manner, which usually isn't desirable. 7 | 8 | ## Rule Details 9 | 10 | Examples of **incorrect** code for this rule: 11 | 12 | ```jsx 13 |
14 |
15 | 16 | React.createElement("div", undefined, [React.createElement("a"), React.createElement("span")]); 17 | ``` 18 | 19 | Examples of **correct** code for this rule: 20 | 21 | ```jsx 22 |
23 |
24 | 25 | React.createElement("div", undefined, [React.createElement("a"), " ", React.createElement("a")]); 26 | ``` 27 | -------------------------------------------------------------------------------- /docs/rules/no-arrow-function-lifecycle.md: -------------------------------------------------------------------------------- 1 | # Lifecycle methods should be methods on the prototype, not class fields (`react/no-arrow-function-lifecycle`) 2 | 3 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 4 | 5 | 6 | 7 | It is not necessary to use arrow function for lifecycle methods. This makes things harder to test, conceptually less performant (although in practice, performance will not be affected, since most engines will optimize efficiently), and can break hot reloading patterns. 8 | 9 | ## Rule Details 10 | 11 | The following patterns are considered warnings: 12 | 13 | ```jsx 14 | class Hello extends React.Component { 15 | render = () => { 16 | return
; 17 | } 18 | } 19 | 20 | var AnotherHello = createReactClass({ 21 | render: () => { 22 | return
; 23 | }, 24 | }); 25 | ``` 26 | 27 | The following patterns are **not** considered warnings: 28 | 29 | ```jsx 30 | class Hello extends React.Component { 31 | render() { 32 | return
; 33 | } 34 | } 35 | 36 | var AnotherHello = createReactClass({ 37 | render() { 38 | return
; 39 | }, 40 | }); 41 | 42 | ``` 43 | 44 | ## When Not To Use It 45 | 46 | If you don't care about performance of your application or conceptual correctness of class property placement, you can disable this rule. 47 | -------------------------------------------------------------------------------- /docs/rules/no-children-prop.md: -------------------------------------------------------------------------------- 1 | # Disallow passing of children as props (`react/no-children-prop`) 2 | 3 | 💼 This rule is enabled in the ☑️ `recommended` [config](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs). 4 | 5 | 6 | 7 | Children should always be actual children, not passed in as a prop. 8 | 9 | When using JSX, the children should be nested between the opening and closing 10 | tags. When not using JSX, the children should be passed as additional 11 | arguments to `React.createElement`. 12 | 13 | ## Rule Details 14 | 15 | Examples of **incorrect** code for this rule: 16 | 17 | ```jsx 18 |
19 | 20 | } /> 21 | 22 | 23 | React.createElement("div", { children: 'Children' }) 24 | ``` 25 | 26 | Examples of **correct** code for this rule: 27 | 28 | ```jsx 29 |
Children
30 | 31 | Children 32 | 33 | 34 | Child 1 35 | Child 2 36 | 37 | 38 | React.createElement("div", {}, 'Children') 39 | React.createElement("div", 'Child 1', 'Child 2') 40 | ``` 41 | 42 | ## Rule Options 43 | 44 | ```js 45 | "react/no-children-prop": [, { 46 | "allowFunctions": || false 47 | }] 48 | ``` 49 | 50 | ### `allowFunctions` 51 | 52 | When `true`, and passing a function as `children`, it must be in prop position and not child position. 53 | 54 | The following patterns are considered warnings: 55 | 56 | ```jsx 57 | {data => data.value} 58 | React.createElement(MyComponent, {}, data => data.value) 59 | ``` 60 | 61 | The following are **not** considered warnings: 62 | 63 | ```jsx 64 | data.value} /> 65 | React.createElement(MyComponent, { children: data => data.value }) 66 | ``` 67 | -------------------------------------------------------------------------------- /docs/rules/no-danger-with-children.md: -------------------------------------------------------------------------------- 1 | # Disallow when a DOM element is using both children and dangerouslySetInnerHTML (`react/no-danger-with-children`) 2 | 3 | 💼 This rule is enabled in the ☑️ `recommended` [config](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs). 4 | 5 | 6 | 7 | This rule helps prevent problems caused by using children and the dangerouslySetInnerHTML prop at the same time. 8 | React will throw a warning if this rule is ignored. 9 | 10 | ## Rule Details 11 | 12 | Examples of **incorrect** code for this rule: 13 | 14 | ```jsx 15 |
16 | Children 17 |
18 | 19 | 20 | Children 21 | 22 | 23 | ``` 24 | 25 | ```js 26 | React.createElement("div", { dangerouslySetInnerHTML: { __html: "HTML" } }, "Children"); 27 | 28 | React.createElement("Hello", { dangerouslySetInnerHTML: { __html: "HTML" } }, "Children"); 29 | ``` 30 | 31 | Examples of **correct** code for this rule: 32 | 33 | ```jsx 34 |
35 | 36 | 37 | 38 |
39 | Children 40 |
41 | 42 | 43 | Children 44 | 45 | 46 | ``` 47 | 48 | ```js 49 | React.createElement("div", { dangerouslySetInnerHTML: { __html: "HTML" } }); 50 | 51 | React.createElement("Hello", { dangerouslySetInnerHTML: { __html: "HTML" } }); 52 | 53 | React.createElement("div", {}, "Children"); 54 | 55 | React.createElement("Hello", {}, "Children"); 56 | ``` 57 | -------------------------------------------------------------------------------- /docs/rules/no-danger.md: -------------------------------------------------------------------------------- 1 | # Disallow usage of dangerous JSX properties (`react/no-danger`) 2 | 3 | 4 | 5 | Dangerous properties in React are those whose behavior is known to be a common source of application vulnerabilities. The properties' names clearly indicate they are dangerous and should be avoided unless great care is taken. 6 | 7 | See 8 | 9 | ## Rule Details 10 | 11 | Examples of **incorrect** code for this rule: 12 | 13 | ```jsx 14 | var React = require('react'); 15 | 16 | var Hello =
; 17 | ``` 18 | 19 | Examples of **correct** code for this rule: 20 | 21 | ```jsx 22 | var React = require('react'); 23 | 24 | var Hello =
Hello World
; 25 | ``` 26 | 27 | ## Rule Options 28 | 29 | ```js 30 | ... 31 | "react/no-danger": [, { 32 | "customComponentNames": Array, 33 | }] 34 | ... 35 | ``` 36 | 37 | ### customComponentNames 38 | 39 | Defaults to `[]`, if you want to enable this rule for all custom components you can pass `customComponentNames` as `['*']`, or else you can pass specific components name to the array. 40 | 41 | ## When Not To Use It 42 | 43 | If you are certain the content passed to dangerouslySetInnerHTML is sanitized HTML you can disable this rule. 44 | -------------------------------------------------------------------------------- /docs/rules/no-deprecated.md: -------------------------------------------------------------------------------- 1 | # Disallow usage of deprecated methods (`react/no-deprecated`) 2 | 3 | 💼 This rule is enabled in the ☑️ `recommended` [config](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs). 4 | 5 | 6 | 7 | Several methods are deprecated between React versions. This rule will warn you if you try to use a deprecated method. Use the [shared settings](/README.md#configuration) to specify the React version. 8 | 9 | ## Rule Details 10 | 11 | Examples of **incorrect** code for this rule: 12 | 13 | ```jsx 14 | React.render(, root); 15 | 16 | React.unmountComponentAtNode(root); 17 | 18 | React.findDOMNode(this.refs.foo); 19 | 20 | React.renderToString(); 21 | 22 | React.renderToStaticMarkup(); 23 | 24 | React.createClass({ /* Class object */ }); 25 | 26 | const propTypes = { 27 | foo: PropTypes.bar, 28 | }; 29 | 30 | //Any factories under React.DOM 31 | React.DOM.div(); 32 | 33 | import React, { PropTypes } from 'react'; 34 | 35 | // old lifecycles (since React 16.9) 36 | componentWillMount() { } 37 | componentWillReceiveProps() { } 38 | componentWillUpdate() { } 39 | 40 | // React 18 deprecations 41 | import { render } from 'react-dom'; 42 | ReactDOM.render(
, container); 43 | 44 | import { hydrate } from 'react-dom'; 45 | ReactDOM.hydrate(
, container); 46 | 47 | import {unmountComponentAtNode} from 'react-dom'; 48 | ReactDOM.unmountComponentAtNode(container); 49 | 50 | import { renderToNodeStream } from 'react-dom/server'; 51 | ReactDOMServer.renderToNodeStream(element); 52 | ``` 53 | 54 | Examples of **correct** code for this rule: 55 | 56 | ```jsx 57 | // when React < 18 58 | ReactDOM.render(, root); 59 | 60 | // when React is < 0.14 61 | ReactDOM.findDOMNode(this.refs.foo); 62 | 63 | import { PropTypes } from 'prop-types'; 64 | 65 | UNSAFE_componentWillMount() { } 66 | UNSAFE_componentWillReceiveProps() { } 67 | UNSAFE_componentWillUpdate() { } 68 | 69 | ReactDOM.createPortal(child, container); 70 | 71 | import { createRoot } from 'react-dom/client'; 72 | const root = createRoot(container); 73 | root.unmount(); 74 | 75 | import { hydrateRoot } from 'react-dom/client'; 76 | const root = hydrateRoot(container, ); 77 | ``` 78 | -------------------------------------------------------------------------------- /docs/rules/no-did-mount-set-state.md: -------------------------------------------------------------------------------- 1 | # Disallow usage of setState in componentDidMount (`react/no-did-mount-set-state`) 2 | 3 | 4 | 5 | Updating the state after a component mount will trigger a second `render()` call and can lead to property/layout thrashing. 6 | 7 | ## Rule Details 8 | 9 | This rule is aimed to forbid the use of `this.setState` in `componentDidMount` outside of functions, such as callbacks. 10 | 11 | Examples of **incorrect** code for this rule: 12 | 13 | ```jsx 14 | var Hello = createReactClass({ 15 | componentDidMount: function() { 16 | this.setState({ 17 | name: this.props.name.toUpperCase() 18 | }); 19 | }, 20 | render: function() { 21 | return
Hello {this.state.name}
; 22 | } 23 | }); 24 | ``` 25 | 26 | Examples of **correct** code for this rule: 27 | 28 | ```jsx 29 | var Hello = createReactClass({ 30 | componentDidMount: function() { 31 | this.onMount(function callback(newName) { 32 | this.setState({ 33 | name: newName 34 | }); 35 | }); 36 | }, 37 | render: function() { 38 | return
Hello {this.state.name}
; 39 | } 40 | }); 41 | ``` 42 | 43 | ```jsx 44 | var Hello = createReactClass({ 45 | componentDidMount: function() { 46 | this.props.onMount(); 47 | }, 48 | render: function() { 49 | return
Hello {this.props.name}
; 50 | } 51 | }); 52 | ``` 53 | 54 | ## Rule Options 55 | 56 | ```js 57 | ... 58 | "react/no-did-mount-set-state": [, ] 59 | ... 60 | ``` 61 | 62 | ### `disallow-in-func` mode 63 | 64 | By default this rule forbids any call to `this.setState` in `componentDidMount` outside of functions. The `disallow-in-func` mode makes this rule more strict by disallowing calls to `this.setState` even within functions. 65 | 66 | Examples of **incorrect** code for this rule: 67 | 68 | ```jsx 69 | var Hello = createReactClass({ 70 | componentDidMount: function() { 71 | this.setState({ 72 | name: this.props.name.toUpperCase() 73 | }); 74 | }, 75 | render: function() { 76 | return
Hello {this.state.name}
; 77 | } 78 | }); 79 | ``` 80 | 81 | ```jsx 82 | var Hello = createReactClass({ 83 | componentDidMount: function() { 84 | this.onMount(function callback(newName) { 85 | this.setState({ 86 | name: newName 87 | }); 88 | }); 89 | }, 90 | render: function() { 91 | return
Hello {this.state.name}
; 92 | } 93 | }); 94 | ``` 95 | -------------------------------------------------------------------------------- /docs/rules/no-did-update-set-state.md: -------------------------------------------------------------------------------- 1 | # Disallow usage of setState in componentDidUpdate (`react/no-did-update-set-state`) 2 | 3 | 4 | 5 | Updating the state after a component update will trigger a second `render()` call and can lead to property/layout thrashing. 6 | 7 | ## Rule Details 8 | 9 | Examples of **incorrect** code for this rule: 10 | 11 | ```jsx 12 | var Hello = createReactClass({ 13 | componentDidUpdate: function() { 14 | this.setState({ 15 | name: this.props.name.toUpperCase() 16 | }); 17 | }, 18 | render: function() { 19 | return
Hello {this.state.name}
; 20 | } 21 | }); 22 | ``` 23 | 24 | Examples of **correct** code for this rule: 25 | 26 | ```jsx 27 | var Hello = createReactClass({ 28 | componentDidUpdate: function() { 29 | this.props.onUpdate(); 30 | }, 31 | render: function() { 32 | return
Hello {this.props.name}
; 33 | } 34 | }); 35 | ``` 36 | 37 | ```jsx 38 | var Hello = createReactClass({ 39 | componentDidUpdate: function() { 40 | this.onUpdate(function callback(newName) { 41 | this.setState({ 42 | name: newName 43 | }); 44 | }); 45 | }, 46 | render: function() { 47 | return
Hello {this.props.name}
; 48 | } 49 | }); 50 | ``` 51 | 52 | ## Rule Options 53 | 54 | ```js 55 | ... 56 | "react/no-did-update-set-state": [, ] 57 | ... 58 | ``` 59 | 60 | ### `disallow-in-func` mode 61 | 62 | By default this rule forbids any call to `this.setState` in `componentDidUpdate` outside of functions. The `disallow-in-func` mode makes this rule more strict by disallowing calls to `this.setState` even within functions. 63 | 64 | Examples of **incorrect** code for this rule: 65 | 66 | ```jsx 67 | var Hello = createReactClass({ 68 | componentDidUpdate: function() { 69 | this.setState({ 70 | name: this.props.name.toUpperCase() 71 | }); 72 | }, 73 | render: function() { 74 | return
Hello {this.state.name}
; 75 | } 76 | }); 77 | ``` 78 | 79 | ```jsx 80 | var Hello = createReactClass({ 81 | componentDidUpdate: function() { 82 | this.onUpdate(function callback(newName) { 83 | this.setState({ 84 | name: newName 85 | }); 86 | }); 87 | }, 88 | render: function() { 89 | return
Hello {this.state.name}
; 90 | } 91 | }); 92 | ``` 93 | -------------------------------------------------------------------------------- /docs/rules/no-direct-mutation-state.md: -------------------------------------------------------------------------------- 1 | # Disallow direct mutation of this.state (`react/no-direct-mutation-state`) 2 | 3 | 💼 This rule is enabled in the ☑️ `recommended` [config](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs). 4 | 5 | 6 | 7 | NEVER mutate `this.state` directly, as calling `setState()` afterwards may replace 8 | the mutation you made. Treat `this.state` as if it were immutable. 9 | 10 | The only place that's acceptable to assign this.state is in a ES6 `class` component constructor. 11 | 12 | ## Rule Details 13 | 14 | This rule is aimed to forbid the use of mutating `this.state` directly. 15 | 16 | Examples of **incorrect** code for this rule: 17 | 18 | ```jsx 19 | var Hello = createReactClass({ 20 | componentDidMount: function() { 21 | this.state.name = this.props.name.toUpperCase(); 22 | }, 23 | render: function() { 24 | return
Hello {this.state.name}
; 25 | } 26 | }); 27 | 28 | class Hello extends React.Component { 29 | constructor(props) { 30 | super(props) 31 | 32 | // Assign at instance creation time, not on a callback 33 | doSomethingAsync(() => { 34 | this.state = 'bad'; 35 | }); 36 | } 37 | } 38 | ``` 39 | 40 | Examples of **correct** code for this rule: 41 | 42 | ```jsx 43 | var Hello = createReactClass({ 44 | componentDidMount: function() { 45 | this.setState({ 46 | name: this.props.name.toUpperCase(); 47 | }); 48 | }, 49 | render: function() { 50 | return
Hello {this.state.name}
; 51 | } 52 | }); 53 | 54 | class Hello extends React.Component { 55 | constructor(props) { 56 | super(props) 57 | 58 | this.state = { 59 | foo: 'bar', 60 | } 61 | } 62 | } 63 | ``` 64 | -------------------------------------------------------------------------------- /docs/rules/no-find-dom-node.md: -------------------------------------------------------------------------------- 1 | # Disallow usage of findDOMNode (`react/no-find-dom-node`) 2 | 3 | 💼 This rule is enabled in the ☑️ `recommended` [config](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs). 4 | 5 | 6 | 7 | Facebook will eventually deprecate `findDOMNode` as it blocks certain improvements in React in the future. 8 | 9 | It is recommended to use callback refs instead. See [Dan Abramov comments and examples](https://github.com/jsx-eslint/eslint-plugin-react/issues/678#issue-165177220). 10 | 11 | ## Rule Details 12 | 13 | Examples of **incorrect** code for this rule: 14 | 15 | ```jsx 16 | class MyComponent extends Component { 17 | componentDidMount() { 18 | findDOMNode(this).scrollIntoView(); 19 | } 20 | render() { 21 | return
22 | } 23 | } 24 | ``` 25 | 26 | Examples of **correct** code for this rule: 27 | 28 | ```jsx 29 | class MyComponent extends Component { 30 | componentDidMount() { 31 | this.node.scrollIntoView(); 32 | } 33 | render() { 34 | return
this.node = node} /> 35 | } 36 | } 37 | ``` 38 | -------------------------------------------------------------------------------- /docs/rules/no-invalid-html-attribute.md: -------------------------------------------------------------------------------- 1 | # Disallow usage of invalid attributes (`react/no-invalid-html-attribute`) 2 | 3 | 💡 This rule is manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions). 4 | 5 | 6 | 7 | Some HTML elements have a specific set of valid values for some attributes. 8 | For instance the elements: `a`, `area`, `link`, or `form` all have an attribute called `rel`. 9 | There is a fixed list of values that have any meaning for this attribute on these tags (see [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel)). 10 | To help with minimizing confusion while reading code, only the appropriate values should be on each attribute. 11 | 12 | ## Rule Details 13 | 14 | This rule aims to remove invalid attribute values. 15 | 16 | ## Rule Options 17 | 18 | The options is a list of attributes to check. Defaults to `["rel"]`. 19 | 20 | ## When Not To Use It 21 | 22 | When you don't want to enforce attribute value correctness. 23 | -------------------------------------------------------------------------------- /docs/rules/no-is-mounted.md: -------------------------------------------------------------------------------- 1 | # Disallow usage of isMounted (`react/no-is-mounted`) 2 | 3 | 💼 This rule is enabled in the ☑️ `recommended` [config](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs). 4 | 5 | 6 | 7 | [`isMounted` is an anti-pattern][anti-pattern], is not available when using ES6 classes, and it is on its way to being officially deprecated. 8 | 9 | [anti-pattern]: https://legacy.reactjs.org/blog/2015/12/16/ismounted-antipattern.html 10 | 11 | ## Rule Details 12 | 13 | Examples of **incorrect** code for this rule: 14 | 15 | ```jsx 16 | var Hello = createReactClass({ 17 | handleClick: function() { 18 | setTimeout(function() { 19 | if (this.isMounted()) { 20 | return; 21 | } 22 | }); 23 | }, 24 | render: function() { 25 | return
Hello
; 26 | } 27 | }); 28 | ``` 29 | 30 | Examples of **correct** code for this rule: 31 | 32 | ```jsx 33 | var Hello = createReactClass({ 34 | render: function() { 35 | return
Hello
; 36 | } 37 | }); 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/rules/no-multi-comp.md: -------------------------------------------------------------------------------- 1 | # Disallow multiple component definition per file (`react/no-multi-comp`) 2 | 3 | 4 | 5 | Declaring only one component per file improves readability and reusability of components. 6 | 7 | ## Rule Details 8 | 9 | Examples of **incorrect** code for this rule: 10 | 11 | ```jsx 12 | var Hello = createReactClass({ 13 | render: function() { 14 | return
Hello {this.props.name}
; 15 | } 16 | }); 17 | 18 | var HelloJohn = createReactClass({ 19 | render: function() { 20 | return ; 21 | } 22 | }); 23 | ``` 24 | 25 | Examples of **correct** code for this rule: 26 | 27 | ```jsx 28 | var Hello = require('./components/Hello'); 29 | 30 | var HelloJohn = createReactClass({ 31 | render: function() { 32 | return ; 33 | } 34 | }); 35 | ``` 36 | 37 | ## Rule Options 38 | 39 | ```js 40 | ... 41 | "react/no-multi-comp": [, { "ignoreStateless": }] 42 | ... 43 | ``` 44 | 45 | ### `ignoreStateless` 46 | 47 | When `true` the rule will ignore stateless components and will allow you to have multiple stateless components, or one stateful component and some stateless components in the same file. 48 | 49 | Examples of **correct** code for this rule: 50 | 51 | ```jsx 52 | function Hello(props) { 53 | return
Hello {props.name}
; 54 | } 55 | function HelloAgain(props) { 56 | return
Hello again {props.name}
; 57 | } 58 | ``` 59 | 60 | ```jsx 61 | function Hello(props) { 62 | return
Hello {props.name}
; 63 | } 64 | class HelloJohn extends React.Component { 65 | render() { 66 | return ; 67 | } 68 | } 69 | module.exports = HelloJohn; 70 | ``` 71 | 72 | ## When Not To Use It 73 | 74 | If you prefer to declare multiple components per file you can disable this rule. 75 | -------------------------------------------------------------------------------- /docs/rules/no-namespace.md: -------------------------------------------------------------------------------- 1 | # Enforce that namespaces are not used in React elements (`react/no-namespace`) 2 | 3 | 4 | 5 | Enforces the absence of a namespace in React elements, such as with `svg:circle`, as they are not supported in React. 6 | 7 | ## Rule Details 8 | 9 | The following patterns are considered warnings: 10 | 11 | ```jsx 12 | 13 | ``` 14 | 15 | ```jsx 16 | 17 | ``` 18 | 19 | The following patterns are **not** considered warnings: 20 | 21 | ```jsx 22 | 23 | ``` 24 | 25 | ```jsx 26 | 27 | ``` 28 | 29 | ## When Not To Use It 30 | 31 | If you are not using React. 32 | -------------------------------------------------------------------------------- /docs/rules/no-object-type-as-default-prop.md: -------------------------------------------------------------------------------- 1 | # Disallow usage of referential-type variables as default param in functional component (`react/no-object-type-as-default-prop`) 2 | 3 | 4 | 5 | Warns if in a functional component, an object type value (such as array/object literal/function/etc) is used as default prop, to prevent potential unnecessary rerenders, and performance regressions. 6 | 7 | ## Rule Details 8 | 9 | Certain values (like arrays, objects, functions, etc) are compared by identity instead of by value. This means that, for example, whilst two empty arrays conceptually represent the same value - JavaScript semantics dictate that they are distinct and unequal as they represent two distinct values. 10 | 11 | When using object destructuring syntax you can set the default value for a given property if it does not exist. If you set the default value to one of the values that is compared by identity, it will mean that each time the destructure is evaluated the JS engine will create a new, distinct value in the destructured variable. 12 | 13 | In the context of a React functional component's props argument this means for each render, the property has a new, distinct value. When this value is passed to a hook as a dependency or passed into a child component as a property React will see this as a new value - meaning that a hook will be re-evaluated, or a memoized component will rerender. 14 | 15 | This obviously destroys any performance benefits you get from memoization. Additionally, in certain circumstances this can cause infinite rerender loops, which can often be hard to debug. 16 | 17 | It's worth noting that primitive literal values (`string`, `number`, `boolean`, `null`, and `undefined`) can be considered to be compared "by value", or alternatively, as always having the same identity (every `3` is the same exact `3`). Thus, it's safe for those to be inlined as a default value. 18 | 19 | To fix the violations, the easiest way is to use a referencing variable in module scope instead of using the literal values, e.g: 20 | 21 | ```jsx 22 | const emptyArray = []; 23 | 24 | function Component({ 25 | items = emptyArray, 26 | }) {} 27 | ``` 28 | 29 | Examples of ***invalid*** code for this rule: 30 | 31 | ```jsx 32 | function Component({ 33 | items = [], 34 | }) {} 35 | 36 | const Component = ({ 37 | items = {}, 38 | }) => {} 39 | 40 | const Component = ({ 41 | items = () => {}, 42 | }) => {} 43 | ``` 44 | 45 | Examples of ***valid*** code for this rule: 46 | 47 | ```jsx 48 | const emptyArray = []; 49 | 50 | function Component({ 51 | items = emptyArray, 52 | }) {} 53 | 54 | const emptyObject = {}; 55 | const Component = ({ 56 | items = emptyObject, 57 | }) => {} 58 | 59 | const noopFunc = () => {}; 60 | const Component = ({ 61 | items = noopFunc, 62 | }) => {} 63 | 64 | // primitives are all compared by value, so are safe to be inlined 65 | function Component({ 66 | num = 3, 67 | str = 'foo', 68 | bool = true, 69 | }) {} 70 | ``` 71 | -------------------------------------------------------------------------------- /docs/rules/no-redundant-should-component-update.md: -------------------------------------------------------------------------------- 1 | # Disallow usage of shouldComponentUpdate when extending React.PureComponent (`react/no-redundant-should-component-update`) 2 | 3 | 4 | 5 | Warns if you have `shouldComponentUpdate` defined when defining a component that extends React.PureComponent. 6 | While having `shouldComponentUpdate` will still work, it becomes pointless to extend PureComponent. 7 | 8 | ## Rule Details 9 | 10 | Examples of **incorrect** code for this rule: 11 | 12 | ```jsx 13 | class Foo extends React.PureComponent { 14 | shouldComponentUpdate() { 15 | // do check 16 | } 17 | 18 | render() { 19 | return
Radical!
20 | } 21 | } 22 | 23 | function Bar() { 24 | return class Baz extends React.PureComponent { 25 | shouldComponentUpdate() { 26 | // do check 27 | } 28 | 29 | render() { 30 | return
Groovy!
31 | } 32 | } 33 | } 34 | ``` 35 | 36 | Examples of **correct** code for this rule: 37 | 38 | ```jsx 39 | class Foo extends React.Component { 40 | shouldComponentUpdate() { 41 | // do check 42 | } 43 | 44 | render() { 45 | return
Radical!
46 | } 47 | } 48 | 49 | function Bar() { 50 | return class Baz extends React.Component { 51 | shouldComponentUpdate() { 52 | // do check 53 | } 54 | 55 | render() { 56 | return
Groovy!
57 | } 58 | } 59 | } 60 | 61 | class Qux extends React.PureComponent { 62 | render() { 63 | return
Tubular!
64 | } 65 | } 66 | ``` 67 | -------------------------------------------------------------------------------- /docs/rules/no-render-return-value.md: -------------------------------------------------------------------------------- 1 | # Disallow usage of the return value of ReactDOM.render (`react/no-render-return-value`) 2 | 3 | 💼 This rule is enabled in the ☑️ `recommended` [config](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs). 4 | 5 | 6 | 7 | > `ReactDOM.render()` currently returns a reference to the root `ReactComponent` instance. However, using this return value is legacy and should be avoided because future versions of React may render components asynchronously in some cases. If you need a reference to the root `ReactComponent` instance, the preferred solution is to attach a [callback ref](https://legacy.reactjs.org/docs/refs-and-the-dom.html#callback-refs) to the root element. 8 | 9 | Source: [ReactDOM documentation](https://legacy.reactjs.org/docs/react-dom.html#render) 10 | 11 | ## Rule Details 12 | 13 | This rule will warn you if you try to use the `ReactDOM.render()` return value. 14 | 15 | Examples of **incorrect** code for this rule: 16 | 17 | ```jsx 18 | const inst = ReactDOM.render(, document.body); 19 | doSomethingWithInst(inst); 20 | ``` 21 | 22 | Examples of **correct** code for this rule: 23 | 24 | ```jsx 25 | ReactDOM.render(, document.body); 26 | 27 | ReactDOM.render(, document.body, doSomethingWithInst); 28 | ``` 29 | -------------------------------------------------------------------------------- /docs/rules/no-set-state.md: -------------------------------------------------------------------------------- 1 | # Disallow usage of setState (`react/no-set-state`) 2 | 3 | 4 | 5 | When using an architecture that separates your application state from your UI components (e.g. Flux), it may be desirable to forbid the use of local component state. This rule is especially helpful in read-only applications (that don't use forms), since local component state should rarely be necessary in such cases. 6 | 7 | ## Rule Details 8 | 9 | Examples of **incorrect** code for this rule: 10 | 11 | ```jsx 12 | var Hello = createReactClass({ 13 | getInitialState: function() { 14 | return { 15 | name: this.props.name 16 | }; 17 | }, 18 | handleClick: function() { 19 | this.setState({ 20 | name: this.props.name.toUpperCase() 21 | }); 22 | }, 23 | render: function() { 24 | return
Hello {this.state.name}
; 25 | } 26 | }); 27 | ``` 28 | 29 | Examples of **correct** code for this rule: 30 | 31 | ```jsx 32 | var Hello = createReactClass({ 33 | render: function() { 34 | return
Hello {this.props.name}
; 35 | } 36 | }); 37 | ``` 38 | -------------------------------------------------------------------------------- /docs/rules/no-string-refs.md: -------------------------------------------------------------------------------- 1 | # Disallow using string references (`react/no-string-refs`) 2 | 3 | 💼 This rule is enabled in the ☑️ `recommended` [config](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs). 4 | 5 | 6 | 7 | Currently, two ways are supported by React to refer to components. The first way, providing a string identifier, is now considered legacy in the official documentation. The documentation now prefers a second method -- referring to components by setting a property on the `this` object in the reference callback. 8 | 9 | ## Rule Details 10 | 11 | Examples of **incorrect** code for this rule: 12 | 13 | ```jsx 14 | var Hello = createReactClass({ 15 | render: function() { 16 | return
Hello, world.
; 17 | } 18 | }); 19 | ``` 20 | 21 | ```jsx 22 | var Hello = createReactClass({ 23 | componentDidMount: function() { 24 | var component = this.refs.hello; 25 | // ...do something with component 26 | }, 27 | render: function() { 28 | return
Hello, world.
; 29 | } 30 | }); 31 | ``` 32 | 33 | Examples of **correct** code for this rule: 34 | 35 | ```jsx 36 | var Hello = createReactClass({ 37 | componentDidMount: function() { 38 | var component = this.hello; 39 | // ...do something with component 40 | }, 41 | render() { 42 | return
{ this.hello = c; }}>Hello, world.
; 43 | } 44 | }); 45 | ``` 46 | 47 | ## Rule Options 48 | 49 | ```js 50 | "react/no-string-refs": [, {"noTemplateLiterals": }] 51 | ``` 52 | 53 | ### `noTemplateLiterals` 54 | 55 | When set to `true`, it will give warning when using template literals for refs. 56 | Examples of **incorrect** code for this rule: 57 | 58 | ```jsx 59 | var Hello = createReactClass({ 60 | render: function() { 61 | return
Hello, world.
; 62 | } 63 | }); 64 | ``` 65 | 66 | ```jsx 67 | var Hello = createReactClass({ 68 | render: function() { 69 | return
Hello, world.
; 70 | } 71 | }); 72 | ``` 73 | -------------------------------------------------------------------------------- /docs/rules/no-unused-class-component-methods.md: -------------------------------------------------------------------------------- 1 | # Disallow declaring unused methods of component class (`react/no-unused-class-component-methods`) 2 | 3 | 4 | 5 | Warns you if you have defined a method or property but it is never being used anywhere. 6 | 7 | ## Rule Details 8 | 9 | The following patterns are considered warnings: 10 | 11 | ```jsx 12 | class Foo extends React.Component { 13 | handleClick() {} 14 | render() { 15 | return null; 16 | } 17 | } 18 | ``` 19 | 20 | The following patterns are **not** considered warnings: 21 | 22 | ```jsx 23 | class Foo extends React.Component { 24 | static getDerivedStateFromError(error) { 25 | return { hasError: true }; 26 | } 27 | action() {} 28 | componentDidMount() { 29 | this.action(); 30 | } 31 | render() { 32 | return null; 33 | } 34 | } 35 | }); 36 | ``` 37 | -------------------------------------------------------------------------------- /docs/rules/no-unused-state.md: -------------------------------------------------------------------------------- 1 | # Disallow definitions of unused state (`react/no-unused-state`) 2 | 3 | 4 | 5 | Warns you if you have defined a property on the state, but it is not being used anywhere. 6 | 7 | ## Rule Details 8 | 9 | Examples of **incorrect** code for this rule: 10 | 11 | ```jsx 12 | class MyComponent extends React.Component { 13 | state = { foo: 0 }; 14 | render() { 15 | return ; 16 | } 17 | } 18 | 19 | var UnusedGetInitialStateTest = createReactClass({ 20 | getInitialState: function() { 21 | return { foo: 0 }; 22 | }, 23 | render: function() { 24 | return ; 25 | } 26 | }) 27 | ``` 28 | 29 | Examples of **correct** code for this rule: 30 | 31 | ```jsx 32 | class MyComponent extends React.Component { 33 | state = { foo: 0 }; 34 | render() { 35 | return ; 36 | } 37 | } 38 | 39 | var UnusedGetInitialStateTest = createReactClass({ 40 | getInitialState: function() { 41 | return { foo: 0 }; 42 | }, 43 | render: function() { 44 | return ; 45 | } 46 | }) 47 | ``` 48 | -------------------------------------------------------------------------------- /docs/rules/no-will-update-set-state.md: -------------------------------------------------------------------------------- 1 | # Disallow usage of setState in componentWillUpdate (`react/no-will-update-set-state`) 2 | 3 | 4 | 5 | Updating the state during the componentWillUpdate step can lead to indeterminate component state and is not allowed. 6 | 7 | ## Rule Details 8 | 9 | Examples of **incorrect** code for this rule: 10 | 11 | ```jsx 12 | var Hello = createReactClass({ 13 | componentWillUpdate: function() { 14 | this.setState({ 15 | name: this.props.name.toUpperCase() 16 | }); 17 | }, 18 | render: function() { 19 | return
Hello {this.state.name}
; 20 | } 21 | }); 22 | ``` 23 | 24 | Examples of **correct** code for this rule: 25 | 26 | ```jsx 27 | var Hello = createReactClass({ 28 | componentWillUpdate: function() { 29 | this.props.prepareHandler(); 30 | }, 31 | render: function() { 32 | return
Hello {this.props.name}
; 33 | } 34 | }); 35 | ``` 36 | 37 | ```jsx 38 | var Hello = createReactClass({ 39 | componentWillUpdate: function() { 40 | this.prepareHandler(function callback(newName) { 41 | this.setState({ 42 | name: newName 43 | }); 44 | }); 45 | }, 46 | render: function() { 47 | return
Hello {this.props.name}
; 48 | } 49 | }); 50 | ``` 51 | 52 | ## Rule Options 53 | 54 | ```js 55 | ... 56 | "react/no-will-update-set-state": [, ] 57 | ... 58 | ``` 59 | 60 | ### `disallow-in-func` mode 61 | 62 | By default this rule forbids any call to `this.setState` in `componentWillUpdate` outside of functions. The `disallow-in-func` mode makes this rule more strict by disallowing calls to `this.setState` even within functions. 63 | 64 | Examples of **incorrect** code for this rule: 65 | 66 | ```jsx 67 | var Hello = createReactClass({ 68 | componentWillUpdate: function() { 69 | this.setState({ 70 | name: this.props.name.toUpperCase() 71 | }); 72 | }, 73 | render: function() { 74 | return
Hello {this.state.name}
; 75 | } 76 | }); 77 | ``` 78 | 79 | ```jsx 80 | var Hello = createReactClass({ 81 | componentWillUpdate: function() { 82 | this.prepareHandler(function callback(newName) { 83 | this.setState({ 84 | name: newName 85 | }); 86 | }); 87 | }, 88 | render: function() { 89 | return
Hello {this.state.name}
; 90 | } 91 | }); 92 | ``` 93 | -------------------------------------------------------------------------------- /docs/rules/prefer-es6-class.md: -------------------------------------------------------------------------------- 1 | # Enforce ES5 or ES6 class for React Components (`react/prefer-es6-class`) 2 | 3 | 4 | 5 | React offers you two ways to create traditional components: using the ES5 `create-react-class` module or the new ES6 class system. 6 | 7 | ## Rule Details 8 | 9 | This rule allows you to enforce one way or another. 10 | 11 | ## Rule Options 12 | 13 | ```js 14 | ... 15 | "react/prefer-es6-class": [, ] 16 | ... 17 | ``` 18 | 19 | ### `always` mode 20 | 21 | Will enforce ES6 classes for React Components. This is the default mode. 22 | 23 | Examples of **incorrect** code for this rule: 24 | 25 | ```jsx 26 | var Hello = createReactClass({ 27 | render: function() { 28 | return
Hello {this.props.name}
; 29 | } 30 | }); 31 | ``` 32 | 33 | Examples of **correct** code for this rule: 34 | 35 | ```jsx 36 | class Hello extends React.Component { 37 | render() { 38 | return
Hello {this.props.name}
; 39 | } 40 | } 41 | ``` 42 | 43 | ### `never` mode 44 | 45 | Will enforce ES5 classes for React Components. 46 | 47 | Examples of **incorrect** code for this rule: 48 | 49 | ```jsx 50 | class Hello extends React.Component { 51 | render() { 52 | return
Hello {this.props.name}
; 53 | } 54 | } 55 | ``` 56 | 57 | Examples of **correct** code for this rule: 58 | 59 | ```jsx 60 | var Hello = createReactClass({ 61 | render: function() { 62 | return
Hello {this.props.name}
; 63 | } 64 | }); 65 | ``` 66 | -------------------------------------------------------------------------------- /docs/rules/prefer-read-only-props.md: -------------------------------------------------------------------------------- 1 | # Enforce that props are read-only (`react/prefer-read-only-props`) 2 | 3 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 4 | 5 | 6 | 7 | Using Flow, one can define types for props. This rule enforces that prop types are read-only (covariant). 8 | 9 | ## Rule Details 10 | 11 | Examples of **incorrect** code for this rule: 12 | 13 | In Flow: 14 | 15 | ```jsx 16 | type Props = { 17 | name: string, 18 | } 19 | class Hello extends React.Component { 20 | render () { 21 | return
Hello {this.props.name}
; 22 | } 23 | } 24 | 25 | function Hello(props: {-name: string}) { 26 | return
Hello {props.name}
; 27 | } 28 | 29 | const Hello = (props: {|name: string|}) => ( 30 |
Hello {props.name}
31 | ); 32 | ``` 33 | 34 | In TypeScript: 35 | 36 | ```tsx 37 | type Props = { 38 | name: string; 39 | } 40 | class Hello extends React.Component { 41 | render () { 42 | return
Hello {this.props.name}
; 43 | } 44 | } 45 | 46 | interface Props { 47 | name: string; 48 | } 49 | class Hello extends React.Component { 50 | render () { 51 | return
Hello {this.props.name}
; 52 | } 53 | } 54 | ``` 55 | 56 | Examples of **correct** code for this rule: 57 | 58 | In Flow: 59 | 60 | ```jsx 61 | type Props = { 62 | +name: string, 63 | } 64 | class Hello extends React.Component { 65 | render () { 66 | return
Hello {this.props.name}
; 67 | } 68 | } 69 | 70 | function Hello(props: {+name: string}) { 71 | return
Hello {props.name}
; 72 | } 73 | 74 | const Hello = (props: {|+name: string|}) => ( 75 |
Hello {props.name}
76 | ); 77 | ``` 78 | 79 | In TypeScript: 80 | 81 | ```tsx 82 | type Props = { 83 | readonly name: string; 84 | } 85 | class Hello extends React.Component { 86 | render () { 87 | return
Hello {this.props.name}
; 88 | } 89 | } 90 | 91 | interface Props { 92 | readonly name: string; 93 | } 94 | class Hello extends React.Component { 95 | render () { 96 | return
Hello {this.props.name}
; 97 | } 98 | } 99 | ``` 100 | -------------------------------------------------------------------------------- /docs/rules/prefer-stateless-function.md: -------------------------------------------------------------------------------- 1 | # Enforce stateless components to be written as a pure function (`react/prefer-stateless-function`) 2 | 3 | 4 | 5 | Stateless functional components are simpler than class based components and will benefit from future React performance optimizations specific to these components. 6 | 7 | ## Rule Details 8 | 9 | This rule will check your class based React components for 10 | 11 | - methods/properties other than `displayName`, `propTypes`, `contextTypes`, `defaultProps`, `render` and useless constructor (same detection as `eslint` [no-useless-constructor rule](https://eslint.org/docs/rules/no-useless-constructor)) 12 | - instance property other than `this.props` and `this.context` 13 | - extension of `React.PureComponent` (if the `ignorePureComponents` flag is true) 14 | - presence of `ref` attribute in JSX 15 | - the use of decorators 16 | - `render` method that return anything but JSX: `undefined`, `null`, etc. (only in React <15.0.0, see [shared settings](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/README.md#configuration) for React version configuration) 17 | 18 | If none of these elements are found, the rule will warn you to write this component as a pure function. 19 | 20 | Examples of **incorrect** code for this rule: 21 | 22 | ```jsx 23 | var Hello = createReactClass({ 24 | render: function() { 25 | return
Hello {this.props.name}
; 26 | } 27 | }); 28 | ``` 29 | 30 | Examples of **correct** code for this rule: 31 | 32 | ```jsx 33 | const Foo = function(props, context) { 34 | const { 35 | location 36 | } = context.router; 37 | 38 | return
{props.foo}
; 39 | }; 40 | ``` 41 | 42 | Examples of **correct** code for this rule, in React <15.0.0: 43 | 44 | ```jsx 45 | class Foo extends React.Component { 46 | render() { 47 | if (!this.props.foo) { 48 | return null 49 | } 50 | return
{this.props.foo}
; 51 | } 52 | } 53 | ``` 54 | 55 | ## Rule Options 56 | 57 | ```js 58 | ... 59 | "react/prefer-stateless-function": [, { "ignorePureComponents": }] 60 | ... 61 | ``` 62 | 63 | - `enabled`: for enabling the rule. 0=off, 1=warn, 2=error. Defaults to 0. 64 | - `ignorePureComponents`: optional boolean set to `true` to ignore components extending from `React.PureComponent` (default to `false`). 65 | 66 | ### `ignorePureComponents` 67 | 68 | When `true` the rule will ignore Components extending from `React.PureComponent` that use `this.props` or `this.context`. 69 | 70 | Examples of **correct** code for this rule: 71 | 72 | ```jsx 73 | class Foo extends React.PureComponent { 74 | render() { 75 | return
{this.props.foo}
; 76 | } 77 | } 78 | 79 | class Bar extends React.PureComponent { 80 | render() { 81 | return
Baz
; 82 | } 83 | } 84 | ``` 85 | -------------------------------------------------------------------------------- /docs/rules/react-in-jsx-scope.md: -------------------------------------------------------------------------------- 1 | # Disallow missing React when using JSX (`react/react-in-jsx-scope`) 2 | 3 | 💼🚫 This rule is enabled in the ☑️ `recommended` [config](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs). This rule is _disabled_ in the 🏃 `jsx-runtime` [config](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs). 4 | 5 | 6 | 7 | When using JSX, `` expands to `React.createElement("a")`. Therefore the `React` variable must be in scope. 8 | 9 | If you are using the @jsx pragma this rule will check the designated variable and not the `React` one. 10 | 11 | ## Rule Details 12 | 13 | Examples of **incorrect** code for this rule: 14 | 15 | ```jsx 16 | var Hello =
Hello {this.props.name}
; 17 | ``` 18 | 19 | ```jsx 20 | /** @jsx Foo.bar */ 21 | var React = require('react'); 22 | 23 | var Hello =
Hello {this.props.name}
; 24 | ``` 25 | 26 | Examples of **correct** code for this rule: 27 | 28 | ```jsx 29 | import React from 'react'; 30 | 31 | var Hello =
Hello {this.props.name}
; 32 | ``` 33 | 34 | ```jsx 35 | var React = require('react'); 36 | 37 | var Hello =
Hello {this.props.name}
; 38 | ``` 39 | 40 | ```jsx 41 | /** @jsx Foo.bar */ 42 | var Foo = require('foo'); 43 | 44 | var Hello =
Hello {this.props.name}
; 45 | ``` 46 | 47 | ## When Not To Use It 48 | 49 | If you are not using JSX, or if you are setting `React` as a global variable. 50 | 51 | If you are using the [new JSX transform from React 17](https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html#removing-unused-react-imports), you should disable this rule by extending [`react/jsx-runtime`](https://github.com/jsx-eslint/eslint-plugin-react/blob/8cf47a8ac2242ee00ea36eac4b6ae51956ba4411/index.js#L165-L179) in your eslint config (add `"plugin:react/jsx-runtime"` to `"extends"`). 52 | -------------------------------------------------------------------------------- /docs/rules/require-optimization.md: -------------------------------------------------------------------------------- 1 | # Enforce React components to have a shouldComponentUpdate method (`react/require-optimization`) 2 | 3 | 4 | 5 | This rule prevents you from creating React components without declaring a `shouldComponentUpdate` method. 6 | 7 | ## Rule Details 8 | 9 | Examples of **incorrect** code for this rule: 10 | 11 | ```js 12 | class YourComponent extends React.Component { 13 | 14 | } 15 | ``` 16 | 17 | ```js 18 | createReactClass({ 19 | }); 20 | ``` 21 | 22 | Examples of **correct** code for this rule: 23 | 24 | ```js 25 | class YourComponent extends React.Component { 26 | shouldComponentUpdate () { 27 | return false; 28 | } 29 | } 30 | ``` 31 | 32 | ```js 33 | createReactClass({ 34 | shouldComponentUpdate: function () { 35 | return false; 36 | } 37 | }); 38 | ``` 39 | 40 | ```js 41 | createReactClass({ 42 | mixins: [PureRenderMixin] 43 | }); 44 | ``` 45 | 46 | ```js 47 | @reactMixin.decorate(PureRenderMixin) 48 | createReactClass({ 49 | 50 | }); 51 | ``` 52 | 53 | ## Rule Options 54 | 55 | ```js 56 | ... 57 | "react/require-optimization": [, { allowDecorators: [] }] 58 | ... 59 | ``` 60 | 61 | - `enabled`: for enabling the rule. 0=off, 1=warn, 2=error. Defaults to 0. 62 | - `allowDecorators`: optional array of decorators names to allow validation. 63 | 64 | ### `allowDecorators` 65 | 66 | Sets the allowed names of decorators. If the variable is present in the chain of decorators, it validates 67 | 68 | Examples of **correct** code for this rule: 69 | 70 | ```js 71 | // ['pureRender'] 72 | @pureRender 73 | class Hello extends React.Component {} 74 | ``` 75 | 76 | ### Example 77 | 78 | ```js 79 | ... 80 | "react/require-optimization": [2, {allowDecorators: ['customDecorators']}] 81 | ... 82 | ``` 83 | -------------------------------------------------------------------------------- /docs/rules/require-render-return.md: -------------------------------------------------------------------------------- 1 | # Enforce ES5 or ES6 class for returning value in render function (`react/require-render-return`) 2 | 3 | 💼 This rule is enabled in the ☑️ `recommended` [config](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs). 4 | 5 | 6 | 7 | When writing the `render` method in a component it is easy to forget to return the JSX content. This rule will warn if the `return` statement is missing. 8 | 9 | ## Rule Details 10 | 11 | Examples of **incorrect** code for this rule: 12 | 13 | ```jsx 14 | var Hello = createReactClass({ 15 | render() { 16 |
Hello
; 17 | } 18 | }); 19 | 20 | class Hello extends React.Component { 21 | render() { 22 |
Hello
; 23 | } 24 | } 25 | ``` 26 | 27 | Examples of **correct** code for this rule: 28 | 29 | ```jsx 30 | var Hello = createReactClass({ 31 | render() { 32 | return
Hello
; 33 | } 34 | }); 35 | 36 | class Hello extends React.Component { 37 | render() { 38 | return
Hello
; 39 | } 40 | } 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/rules/self-closing-comp.md: -------------------------------------------------------------------------------- 1 | # Disallow extra closing tags for components without children (`react/self-closing-comp`) 2 | 3 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 4 | 5 | 6 | 7 | Components without children can be self-closed to avoid unnecessary extra closing tag. 8 | 9 | ## Rule Details 10 | 11 | Examples of **incorrect** code for this rule: 12 | 13 | ```jsx 14 | var HelloJohn = ; 15 | 16 | var HelloJohnCompound = ; 17 | ``` 18 | 19 | Examples of **correct** code for this rule: 20 | 21 | ```jsx 22 | var contentContainer =
; 23 | 24 | var intentionalSpace =
{' '}
; 25 | 26 | var HelloJohn = ; 27 | 28 | var HelloJohnCompound = ; 29 | 30 | var Profile = ; 31 | 32 | var ProfileCompound = ; 33 | 34 | var HelloSpace = {' '}; 35 | ``` 36 | 37 | ## Rule Options 38 | 39 | The rule can take one argument to select types of tags, which should be self-closed when this is possible. By default custom components tags and html tags should be self-closed. 40 | 41 | ```js 42 | ... 43 | "react/self-closing-comp": ["error", { 44 | "component": true, 45 | "html": true 46 | }] 47 | ... 48 | ``` 49 | 50 | ### `component` 51 | 52 | When `true`, custom components tags should be self-closed. 53 | 54 | Examples of **incorrect** code for this rule: 55 | 56 | ```jsx 57 | var HelloJohn = ; 58 | ``` 59 | 60 | Examples of **correct** code for this rule: 61 | 62 | ```jsx 63 | var contentContainer =
; 64 | 65 | var intentionalSpace =
{' '}
; 66 | 67 | var HelloJohn = ; 68 | 69 | var HelloJohnCompound = ; 70 | 71 | var Profile = ; 72 | 73 | var ProfileCompound = ; 74 | ``` 75 | 76 | ### `html` 77 | 78 | When `true`, html components tags should be self-closed. 79 | 80 | Examples of **incorrect** code for this rule: 81 | 82 | ```jsx 83 | var contentContainer =
; 84 | ``` 85 | 86 | Examples of **correct** code for this rule: 87 | 88 | ```jsx 89 | var contentContainer =
; 90 | 91 | var contentContainer =
; 92 | 93 | var intentionalSpace =
{' '}
; 94 | ``` 95 | -------------------------------------------------------------------------------- /docs/rules/state-in-constructor.md: -------------------------------------------------------------------------------- 1 | # Enforce class component state initialization style (`react/state-in-constructor`) 2 | 3 | 4 | 5 | ## Rule Details 6 | 7 | This rule will enforce the state initialization style to be either in a constructor or with a class property. 8 | 9 | ## Rule Options 10 | 11 | ```js 12 | ... 13 | "react/state-in-constructor": [, ] 14 | ... 15 | ``` 16 | 17 | ### `always` mode 18 | 19 | Will enforce the state initialization style to be in a constructor. This is the default mode. 20 | 21 | Examples of **incorrect** code for this rule: 22 | 23 | ```jsx 24 | class Foo extends React.Component { 25 | state = { bar: 0 } 26 | render() { 27 | return
Foo
28 | } 29 | } 30 | ``` 31 | 32 | Examples of **correct** code for this rule: 33 | 34 | ```jsx 35 | class Foo extends React.Component { 36 | constructor(props) { 37 | super(props) 38 | this.state = { bar: 0 } 39 | } 40 | render() { 41 | return
Foo
42 | } 43 | } 44 | ``` 45 | 46 | ### `never` mode 47 | 48 | Will enforce the state initialization style to be with a class property. 49 | 50 | Examples of **incorrect** code for this rule: 51 | 52 | ```jsx 53 | class Foo extends React.Component { 54 | constructor(props) { 55 | super(props) 56 | this.state = { bar: 0 } 57 | } 58 | render() { 59 | return
Foo
60 | } 61 | } 62 | ``` 63 | 64 | Examples of **correct** code for this rule: 65 | 66 | ```jsx 67 | class Foo extends React.Component { 68 | state = { bar: 0 } 69 | render() { 70 | return
Foo
71 | } 72 | } 73 | ``` 74 | 75 | ## When Not To Use It 76 | 77 | When the way a component state is being initialized doesn't matter. 78 | -------------------------------------------------------------------------------- /docs/rules/style-prop-object.md: -------------------------------------------------------------------------------- 1 | # Enforce style prop value is an object (`react/style-prop-object`) 2 | 3 | 4 | 5 | Require that the value of the prop `style` be an object or a variable that is 6 | an object. 7 | 8 | ## Rule Details 9 | 10 | Examples of **incorrect** code for this rule: 11 | 12 | ```jsx 13 |
14 | 15 |
16 | 17 | 18 | 19 | const styles = true; 20 |
21 | ``` 22 | 23 | ```js 24 | React.createElement("div", { style: "color: 'red'" }); 25 | 26 | React.createElement("div", { style: true }); 27 | 28 | React.createElement("Hello", { style: true }); 29 | 30 | const styles = true; 31 | React.createElement("div", { style: styles }); 32 | ``` 33 | 34 | Examples of **correct** code for this rule: 35 | 36 | ```jsx 37 |
38 | 39 | 40 | 41 | const styles = { color: "red" }; 42 |
43 | ``` 44 | 45 | ```js 46 | React.createElement("div", { style: { color: 'red' }}); 47 | 48 | React.createElement("Hello", { style: { color: 'red' }}); 49 | 50 | const styles = { height: '100px' }; 51 | React.createElement("div", { style: styles }); 52 | ``` 53 | 54 | ## Rule Options 55 | 56 | ```js 57 | ... 58 | "react/style-prop-object": [, { 59 | "allow": [] 60 | }] 61 | ... 62 | ``` 63 | 64 | ### `allow` 65 | 66 | A list of elements that are allowed to have a non-object value in their style attribute. The default value is `[]`. 67 | 68 | #### Example 69 | 70 | ```js 71 | { 72 | "allow": ["MyComponent"] 73 | } 74 | ``` 75 | 76 | Examples of **incorrect** code for this rule: 77 | 78 | ```js 79 | 80 | React.createElement(Hello, { style: "some styling" }); 81 | ``` 82 | 83 | Examples of **correct** code for this rule: 84 | 85 | ```js 86 | 87 | React.createElement(MyComponent, { style: "some styling" }); 88 | ``` 89 | -------------------------------------------------------------------------------- /docs/rules/void-dom-elements-no-children.md: -------------------------------------------------------------------------------- 1 | # Disallow void DOM elements (e.g. ``, `
`) from receiving children (`react/void-dom-elements-no-children`) 2 | 3 | 4 | 5 | There are some HTML elements that are only self-closing (e.g. `img`, `br`, `hr`). These are collectively known as void DOM elements. If you try to give these children, React will give you a warning like: 6 | 7 | > Invariant Violation: img is a void element tag and must neither have `children` nor use `dangerouslySetInnerHTML`. 8 | 9 | ## Rule Details 10 | 11 | Examples of **incorrect** code for this rule: 12 | 13 | ```jsx 14 |
Children
15 |
16 |
17 | React.createElement('br', undefined, 'Children') 18 | React.createElement('br', { children: 'Children' }) 19 | React.createElement('br', { dangerouslySetInnerHTML: { __html: 'HTML' } }) 20 | ``` 21 | 22 | Examples of **correct** code for this rule: 23 | 24 | ```jsx 25 |
Children
26 |
27 |
28 | React.createElement('div', undefined, 'Children') 29 | React.createElement('div', { children: 'Children' }) 30 | React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }) 31 | ``` 32 | -------------------------------------------------------------------------------- /lib/rules/jsx-no-comment-textnodes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Comments inside children section of tag should be placed inside braces. 3 | * @author Ben Vinegar 4 | */ 5 | 6 | 'use strict'; 7 | 8 | const docsUrl = require('../util/docsUrl'); 9 | const getText = require('../util/eslint').getText; 10 | const report = require('../util/report'); 11 | 12 | // ------------------------------------------------------------------------------ 13 | // Rule Definition 14 | // ------------------------------------------------------------------------------ 15 | 16 | const messages = { 17 | putCommentInBraces: 'Comments inside children section of tag should be placed inside braces', 18 | }; 19 | 20 | /** 21 | * @param {Context} context 22 | * @param {ASTNode} node 23 | * @returns {void} 24 | */ 25 | function checkText(context, node) { 26 | // since babel-eslint has the wrong node.raw, we'll get the source text 27 | const rawValue = getText(context, node); 28 | if (/^\s*\/(\/|\*)/m.test(rawValue)) { 29 | // inside component, e.g.
literal
30 | if ( 31 | node.parent.type !== 'JSXAttribute' 32 | && node.parent.type !== 'JSXExpressionContainer' 33 | && node.parent.type.indexOf('JSX') !== -1 34 | ) { 35 | report(context, messages.putCommentInBraces, 'putCommentInBraces', { 36 | node, 37 | }); 38 | } 39 | } 40 | } 41 | 42 | /** @type {import('eslint').Rule.RuleModule} */ 43 | module.exports = { 44 | meta: { 45 | docs: { 46 | description: 'Disallow comments from being inserted as text nodes', 47 | category: 'Possible Errors', 48 | recommended: true, 49 | url: docsUrl('jsx-no-comment-textnodes'), 50 | }, 51 | 52 | messages, 53 | 54 | schema: [], 55 | }, 56 | 57 | create(context) { 58 | // -------------------------------------------------------------------------- 59 | // Public 60 | // -------------------------------------------------------------------------- 61 | 62 | return { 63 | Literal(node) { 64 | checkText(context, node); 65 | }, 66 | JSXText(node) { 67 | checkText(context, node); 68 | }, 69 | }; 70 | }, 71 | }; 72 | -------------------------------------------------------------------------------- /lib/rules/jsx-no-duplicate-props.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Enforce no duplicate props 3 | * @author Markus Ånöstam 4 | */ 5 | 6 | 'use strict'; 7 | 8 | const has = require('hasown'); 9 | const docsUrl = require('../util/docsUrl'); 10 | const report = require('../util/report'); 11 | 12 | // ------------------------------------------------------------------------------ 13 | // Rule Definition 14 | // ------------------------------------------------------------------------------ 15 | 16 | const messages = { 17 | noDuplicateProps: 'No duplicate props allowed', 18 | }; 19 | 20 | /** @type {import('eslint').Rule.RuleModule} */ 21 | module.exports = { 22 | meta: { 23 | docs: { 24 | description: 'Disallow duplicate properties in JSX', 25 | category: 'Possible Errors', 26 | recommended: true, 27 | url: docsUrl('jsx-no-duplicate-props'), 28 | }, 29 | 30 | messages, 31 | 32 | schema: [{ 33 | type: 'object', 34 | properties: { 35 | ignoreCase: { 36 | type: 'boolean', 37 | }, 38 | }, 39 | additionalProperties: false, 40 | }], 41 | }, 42 | 43 | create(context) { 44 | const configuration = context.options[0] || {}; 45 | const ignoreCase = configuration.ignoreCase || false; 46 | 47 | return { 48 | JSXOpeningElement(node) { 49 | const props = {}; 50 | 51 | node.attributes.forEach((decl) => { 52 | if (decl.type === 'JSXSpreadAttribute') { 53 | return; 54 | } 55 | 56 | let name = decl.name.name; 57 | 58 | if (typeof name !== 'string') { 59 | return; 60 | } 61 | 62 | if (ignoreCase) { 63 | name = name.toLowerCase(); 64 | } 65 | 66 | if (has(props, name)) { 67 | report(context, messages.noDuplicateProps, 'noDuplicateProps', { 68 | node: decl, 69 | }); 70 | } else { 71 | props[name] = 1; 72 | } 73 | }); 74 | }, 75 | }; 76 | }, 77 | }; 78 | -------------------------------------------------------------------------------- /lib/rules/jsx-props-no-spread-multi.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Prevent JSX prop spreading the same expression multiple times 3 | * @author Simon Schick 4 | */ 5 | 6 | 'use strict'; 7 | 8 | const docsUrl = require('../util/docsUrl'); 9 | const report = require('../util/report'); 10 | 11 | // ------------------------------------------------------------------------------ 12 | // Rule Definition 13 | // ------------------------------------------------------------------------------ 14 | 15 | const messages = { 16 | noMultiSpreading: 'Spreading the same expression multiple times is forbidden', 17 | }; 18 | 19 | /** @type {import('eslint').Rule.RuleModule} */ 20 | module.exports = { 21 | meta: { 22 | docs: { 23 | description: 'Disallow JSX prop spreading the same identifier multiple times', 24 | category: 'Best Practices', 25 | recommended: false, 26 | url: docsUrl('jsx-props-no-spread-multi'), 27 | }, 28 | messages, 29 | }, 30 | 31 | create(context) { 32 | return { 33 | JSXOpeningElement(node) { 34 | const spreads = node.attributes.filter( 35 | (attr) => attr.type === 'JSXSpreadAttribute' 36 | && attr.argument.type === 'Identifier' 37 | ); 38 | if (spreads.length < 2) { 39 | return; 40 | } 41 | // We detect duplicate expressions by their identifier 42 | const identifierNames = new Set(); 43 | spreads.forEach((spread) => { 44 | if (identifierNames.has(spread.argument.name)) { 45 | report(context, messages.noMultiSpreading, 'noMultiSpreading', { 46 | node: spread, 47 | }); 48 | } 49 | identifierNames.add(spread.argument.name); 50 | }); 51 | }, 52 | }; 53 | }, 54 | }; 55 | -------------------------------------------------------------------------------- /lib/rules/jsx-uses-react.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Prevent React to be marked as unused 3 | * @author Glen Mailer 4 | */ 5 | 6 | 'use strict'; 7 | 8 | const pragmaUtil = require('../util/pragma'); 9 | const docsUrl = require('../util/docsUrl'); 10 | const markVariableAsUsed = require('../util/eslint').markVariableAsUsed; 11 | 12 | // ------------------------------------------------------------------------------ 13 | // Rule Definition 14 | // ------------------------------------------------------------------------------ 15 | 16 | /** @type {import('eslint').Rule.RuleModule} */ 17 | module.exports = { 18 | // eslint-disable-next-line eslint-plugin/prefer-message-ids -- https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/issues/292 19 | meta: { 20 | docs: { 21 | description: 'Disallow React to be incorrectly marked as unused', 22 | category: 'Best Practices', 23 | recommended: true, 24 | url: docsUrl('jsx-uses-react'), 25 | }, 26 | schema: [], 27 | }, 28 | 29 | create(context) { 30 | const pragma = pragmaUtil.getFromContext(context); 31 | const fragment = pragmaUtil.getFragmentFromContext(context); 32 | 33 | /** 34 | * @param {ASTNode} node 35 | * @returns {void} 36 | */ 37 | function handleOpeningElement(node) { 38 | markVariableAsUsed(pragma, node, context); 39 | } 40 | // -------------------------------------------------------------------------- 41 | // Public 42 | // -------------------------------------------------------------------------- 43 | 44 | return { 45 | JSXOpeningElement: handleOpeningElement, 46 | JSXOpeningFragment: handleOpeningElement, 47 | JSXFragment(node) { 48 | markVariableAsUsed(fragment, node, context); 49 | }, 50 | }; 51 | }, 52 | }; 53 | -------------------------------------------------------------------------------- /lib/rules/jsx-uses-vars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Prevent variables used in JSX to be marked as unused 3 | * @author Yannick Croissant 4 | */ 5 | 6 | 'use strict'; 7 | 8 | const docsUrl = require('../util/docsUrl'); 9 | const markVariableAsUsed = require('../util/eslint').markVariableAsUsed; 10 | 11 | // ------------------------------------------------------------------------------ 12 | // Rule Definition 13 | // ------------------------------------------------------------------------------ 14 | 15 | const isTagNameRe = /^[a-z]/; 16 | const isTagName = (name) => isTagNameRe.test(name); 17 | 18 | /** @type {import('eslint').Rule.RuleModule} */ 19 | module.exports = { 20 | // eslint-disable-next-line eslint-plugin/prefer-message-ids -- https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/issues/292 21 | meta: { 22 | docs: { 23 | description: 'Disallow variables used in JSX to be incorrectly marked as unused', 24 | category: 'Best Practices', 25 | recommended: true, 26 | url: docsUrl('jsx-uses-vars'), 27 | }, 28 | schema: [], 29 | }, 30 | 31 | create(context) { 32 | return { 33 | JSXOpeningElement(node) { 34 | let name; 35 | if (node.name.namespace) { 36 | // 37 | return; 38 | } 39 | if (node.name.name) { 40 | // 41 | name = node.name.name; 42 | // Exclude lowercase tag names like
43 | if (isTagName(name)) { 44 | return; 45 | } 46 | } else if (node.name.object) { 47 | // 48 | let parent = node.name.object; 49 | while (parent.object) { 50 | parent = parent.object; 51 | } 52 | name = parent.name; 53 | } else { 54 | return; 55 | } 56 | 57 | markVariableAsUsed(name, node, context); 58 | }, 59 | 60 | }; 61 | }, 62 | }; 63 | -------------------------------------------------------------------------------- /lib/rules/no-did-mount-set-state.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Prevent usage of setState in componentDidMount 3 | * @author Yannick Croissant 4 | */ 5 | 6 | 'use strict'; 7 | 8 | const makeNoMethodSetStateRule = require('../util/makeNoMethodSetStateRule'); 9 | 10 | /** @type {import('eslint').Rule.RuleModule} */ 11 | module.exports = makeNoMethodSetStateRule('componentDidMount'); 12 | -------------------------------------------------------------------------------- /lib/rules/no-did-update-set-state.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Prevent usage of setState in componentDidUpdate 3 | * @author Yannick Croissant 4 | */ 5 | 6 | 'use strict'; 7 | 8 | const makeNoMethodSetStateRule = require('../util/makeNoMethodSetStateRule'); 9 | 10 | /** @type {import('eslint').Rule.RuleModule} */ 11 | module.exports = makeNoMethodSetStateRule('componentDidUpdate'); 12 | -------------------------------------------------------------------------------- /lib/rules/no-find-dom-node.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Prevent usage of findDOMNode 3 | * @author Yannick Croissant 4 | */ 5 | 6 | 'use strict'; 7 | 8 | const docsUrl = require('../util/docsUrl'); 9 | const report = require('../util/report'); 10 | 11 | // ------------------------------------------------------------------------------ 12 | // Rule Definition 13 | // ------------------------------------------------------------------------------ 14 | 15 | const messages = { 16 | noFindDOMNode: 'Do not use findDOMNode. It doesn’t work with function components and is deprecated in StrictMode. See https://reactjs.org/docs/react-dom.html#finddomnode', 17 | }; 18 | 19 | /** @type {import('eslint').Rule.RuleModule} */ 20 | module.exports = { 21 | meta: { 22 | docs: { 23 | description: 'Disallow usage of findDOMNode', 24 | category: 'Best Practices', 25 | recommended: true, 26 | url: docsUrl('no-find-dom-node'), 27 | }, 28 | 29 | messages, 30 | 31 | schema: [], 32 | }, 33 | 34 | create(context) { 35 | return { 36 | CallExpression(node) { 37 | const callee = node.callee; 38 | 39 | const isFindDOMNode = ('name' in callee && callee.name === 'findDOMNode') || ( 40 | 'property' in callee 41 | && callee.property 42 | && 'name' in callee.property 43 | && callee.property.name === 'findDOMNode' 44 | ); 45 | 46 | if (!isFindDOMNode) { 47 | return; 48 | } 49 | 50 | report(context, messages.noFindDOMNode, 'noFindDOMNode', { 51 | node: callee, 52 | }); 53 | }, 54 | }; 55 | }, 56 | }; 57 | -------------------------------------------------------------------------------- /lib/rules/no-is-mounted.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Prevent usage of isMounted 3 | * @author Joe Lencioni 4 | */ 5 | 6 | 'use strict'; 7 | 8 | const docsUrl = require('../util/docsUrl'); 9 | const getAncestors = require('../util/eslint').getAncestors; 10 | const report = require('../util/report'); 11 | 12 | // ------------------------------------------------------------------------------ 13 | // Rule Definition 14 | // ------------------------------------------------------------------------------ 15 | 16 | const messages = { 17 | noIsMounted: 'Do not use isMounted', 18 | }; 19 | 20 | /** @type {import('eslint').Rule.RuleModule} */ 21 | module.exports = { 22 | meta: { 23 | docs: { 24 | description: 'Disallow usage of isMounted', 25 | category: 'Best Practices', 26 | recommended: true, 27 | url: docsUrl('no-is-mounted'), 28 | }, 29 | 30 | messages, 31 | 32 | schema: [], 33 | }, 34 | 35 | create(context) { 36 | return { 37 | CallExpression(node) { 38 | const callee = node.callee; 39 | if (callee.type !== 'MemberExpression') { 40 | return; 41 | } 42 | if ( 43 | callee.object.type !== 'ThisExpression' 44 | || !('name' in callee.property) 45 | || callee.property.name !== 'isMounted' 46 | ) { 47 | return; 48 | } 49 | const ancestors = getAncestors(context, node); 50 | for (let i = 0, j = ancestors.length; i < j; i++) { 51 | if (ancestors[i].type === 'Property' || ancestors[i].type === 'MethodDefinition') { 52 | report(context, messages.noIsMounted, 'noIsMounted', { 53 | node: callee, 54 | }); 55 | break; 56 | } 57 | } 58 | }, 59 | }; 60 | }, 61 | }; 62 | -------------------------------------------------------------------------------- /lib/rules/no-multi-comp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Prevent multiple component definition per file 3 | * @author Yannick Croissant 4 | */ 5 | 6 | 'use strict'; 7 | 8 | const values = require('object.values'); 9 | 10 | const Components = require('../util/Components'); 11 | const docsUrl = require('../util/docsUrl'); 12 | const report = require('../util/report'); 13 | 14 | // ------------------------------------------------------------------------------ 15 | // Rule Definition 16 | // ------------------------------------------------------------------------------ 17 | 18 | const messages = { 19 | onlyOneComponent: 'Declare only one React component per file', 20 | }; 21 | 22 | /** @type {import('eslint').Rule.RuleModule} */ 23 | module.exports = { 24 | meta: { 25 | docs: { 26 | description: 'Disallow multiple component definition per file', 27 | category: 'Stylistic Issues', 28 | recommended: false, 29 | url: docsUrl('no-multi-comp'), 30 | }, 31 | 32 | messages, 33 | 34 | schema: [{ 35 | type: 'object', 36 | properties: { 37 | ignoreStateless: { 38 | default: false, 39 | type: 'boolean', 40 | }, 41 | }, 42 | additionalProperties: false, 43 | }], 44 | }, 45 | 46 | create: Components.detect((context, components, utils) => { 47 | const configuration = context.options[0] || {}; 48 | const ignoreStateless = configuration.ignoreStateless || false; 49 | 50 | /** 51 | * Checks if the component is ignored 52 | * @param {Object} component The component being checked. 53 | * @returns {boolean} True if the component is ignored, false if not. 54 | */ 55 | function isIgnored(component) { 56 | return ( 57 | ignoreStateless && ( 58 | /Function/.test(component.node.type) 59 | || utils.isPragmaComponentWrapper(component.node) 60 | ) 61 | ); 62 | } 63 | 64 | return { 65 | 'Program:exit'() { 66 | if (components.length() <= 1) { 67 | return; 68 | } 69 | 70 | values(components.list()) 71 | .filter((component) => !isIgnored(component)) 72 | .slice(1) 73 | .forEach((component) => { 74 | report(context, messages.onlyOneComponent, 'onlyOneComponent', { 75 | node: component.node, 76 | }); 77 | }); 78 | }, 79 | }; 80 | }), 81 | }; 82 | -------------------------------------------------------------------------------- /lib/rules/no-namespace.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Enforce that namespaces are not used in React elements 3 | * @author Yacine Hmito 4 | */ 5 | 6 | 'use strict'; 7 | 8 | const elementType = require('jsx-ast-utils/elementType'); 9 | const docsUrl = require('../util/docsUrl'); 10 | const isCreateElement = require('../util/isCreateElement'); 11 | const report = require('../util/report'); 12 | 13 | // ------------------------------------------------------------------------------ 14 | // Rule Definition 15 | // ------------------------------------------------------------------------------ 16 | 17 | const messages = { 18 | noNamespace: 'React component {{name}} must not be in a namespace, as React does not support them', 19 | }; 20 | 21 | /** @type {import('eslint').Rule.RuleModule} */ 22 | module.exports = { 23 | meta: { 24 | docs: { 25 | description: 'Enforce that namespaces are not used in React elements', 26 | category: 'Possible Errors', 27 | recommended: false, 28 | url: docsUrl('no-namespace'), 29 | }, 30 | 31 | messages, 32 | 33 | schema: [], 34 | }, 35 | 36 | create(context) { 37 | return { 38 | CallExpression(node) { 39 | if (isCreateElement(context, node) && node.arguments.length > 0 && node.arguments[0].type === 'Literal') { 40 | const name = node.arguments[0].value; 41 | if (typeof name !== 'string' || name.indexOf(':') === -1) return undefined; 42 | report(context, messages.noNamespace, 'noNamespace', { 43 | node, 44 | data: { 45 | name, 46 | }, 47 | }); 48 | } 49 | }, 50 | JSXOpeningElement(node) { 51 | const name = elementType(node); 52 | if (typeof name !== 'string' || name.indexOf(':') === -1) return undefined; 53 | report(context, messages.noNamespace, 'noNamespace', { 54 | node, 55 | data: { 56 | name, 57 | }, 58 | }); 59 | }, 60 | }; 61 | }, 62 | }; 63 | -------------------------------------------------------------------------------- /lib/rules/no-redundant-should-component-update.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Flag shouldComponentUpdate when extending PureComponent 3 | */ 4 | 5 | 'use strict'; 6 | 7 | const astUtil = require('../util/ast'); 8 | const componentUtil = require('../util/componentUtil'); 9 | const docsUrl = require('../util/docsUrl'); 10 | const report = require('../util/report'); 11 | 12 | // ------------------------------------------------------------------------------ 13 | // Rule Definition 14 | // ------------------------------------------------------------------------------ 15 | 16 | const messages = { 17 | noShouldCompUpdate: '{{component}} does not need shouldComponentUpdate when extending React.PureComponent.', 18 | }; 19 | 20 | /** @type {import('eslint').Rule.RuleModule} */ 21 | module.exports = { 22 | meta: { 23 | docs: { 24 | description: 'Disallow usage of shouldComponentUpdate when extending React.PureComponent', 25 | category: 'Possible Errors', 26 | recommended: false, 27 | url: docsUrl('no-redundant-should-component-update'), 28 | }, 29 | 30 | messages, 31 | 32 | schema: [], 33 | }, 34 | 35 | create(context) { 36 | /** 37 | * Checks for shouldComponentUpdate property 38 | * @param {ASTNode} node The AST node being checked. 39 | * @returns {boolean} Whether or not the property exists. 40 | */ 41 | function hasShouldComponentUpdate(node) { 42 | const properties = astUtil.getComponentProperties(node); 43 | return properties.some((property) => { 44 | const name = astUtil.getPropertyName(property); 45 | return name === 'shouldComponentUpdate'; 46 | }); 47 | } 48 | 49 | /** 50 | * Get name of node if available 51 | * @param {ASTNode} node The AST node being checked. 52 | * @return {string} The name of the node 53 | */ 54 | function getNodeName(node) { 55 | if (node.id) { 56 | return node.id.name; 57 | } 58 | if (node.parent && node.parent.id) { 59 | return node.parent.id.name; 60 | } 61 | return ''; 62 | } 63 | 64 | /** 65 | * Checks for violation of rule 66 | * @param {ASTNode} node The AST node being checked. 67 | */ 68 | function checkForViolation(node) { 69 | if (componentUtil.isPureComponent(node, context)) { 70 | const hasScu = hasShouldComponentUpdate(node); 71 | if (hasScu) { 72 | const className = getNodeName(node); 73 | report(context, messages.noShouldCompUpdate, 'noShouldCompUpdate', { 74 | node, 75 | data: { 76 | component: className, 77 | }, 78 | }); 79 | } 80 | } 81 | } 82 | 83 | return { 84 | ClassDeclaration: checkForViolation, 85 | ClassExpression: checkForViolation, 86 | }; 87 | }, 88 | }; 89 | -------------------------------------------------------------------------------- /lib/rules/no-render-return-value.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Prevent usage of the return value of React.render 3 | * @author Dustan Kasten 4 | */ 5 | 6 | 'use strict'; 7 | 8 | const testReactVersion = require('../util/version').testReactVersion; 9 | const docsUrl = require('../util/docsUrl'); 10 | const report = require('../util/report'); 11 | 12 | // ------------------------------------------------------------------------------ 13 | // Rule Definition 14 | // ------------------------------------------------------------------------------ 15 | 16 | const messages = { 17 | noReturnValue: 'Do not depend on the return value from {{node}}.render', 18 | }; 19 | 20 | /** @type {import('eslint').Rule.RuleModule} */ 21 | module.exports = { 22 | meta: { 23 | docs: { 24 | description: 'Disallow usage of the return value of ReactDOM.render', 25 | category: 'Best Practices', 26 | recommended: true, 27 | url: docsUrl('no-render-return-value'), 28 | }, 29 | 30 | messages, 31 | 32 | schema: [], 33 | }, 34 | 35 | create(context) { 36 | // -------------------------------------------------------------------------- 37 | // Public 38 | // -------------------------------------------------------------------------- 39 | 40 | let calleeObjectName = /^ReactDOM$/; 41 | if (testReactVersion(context, '>= 15.0.0')) { 42 | calleeObjectName = /^ReactDOM$/; 43 | } else if (testReactVersion(context, '^0.14.0')) { 44 | calleeObjectName = /^React(DOM)?$/; 45 | } else if (testReactVersion(context, '^0.13.0')) { 46 | calleeObjectName = /^React$/; 47 | } 48 | 49 | return { 50 | CallExpression(node) { 51 | const callee = node.callee; 52 | const parent = node.parent; 53 | if (callee.type !== 'MemberExpression') { 54 | return; 55 | } 56 | 57 | if ( 58 | callee.object.type !== 'Identifier' 59 | || !calleeObjectName.test(callee.object.name) 60 | || (!('name' in callee.property) || callee.property.name !== 'render') 61 | ) { 62 | return; 63 | } 64 | 65 | if ( 66 | parent.type === 'VariableDeclarator' 67 | || parent.type === 'Property' 68 | || parent.type === 'ReturnStatement' 69 | || parent.type === 'ArrowFunctionExpression' 70 | || parent.type === 'AssignmentExpression' 71 | ) { 72 | report(context, messages.noReturnValue, 'noReturnValue', { 73 | node: callee, 74 | data: { 75 | node: callee.object.name, 76 | }, 77 | }); 78 | } 79 | }, 80 | }; 81 | }, 82 | }; 83 | -------------------------------------------------------------------------------- /lib/rules/no-set-state.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Prevent usage of setState 3 | * @author Mark Dalgleish 4 | */ 5 | 6 | 'use strict'; 7 | 8 | const values = require('object.values'); 9 | 10 | const Components = require('../util/Components'); 11 | const docsUrl = require('../util/docsUrl'); 12 | const report = require('../util/report'); 13 | 14 | // ------------------------------------------------------------------------------ 15 | // Rule Definition 16 | // ------------------------------------------------------------------------------ 17 | 18 | const messages = { 19 | noSetState: 'Do not use setState', 20 | }; 21 | 22 | /** @type {import('eslint').Rule.RuleModule} */ 23 | module.exports = { 24 | meta: { 25 | docs: { 26 | description: 'Disallow usage of setState', 27 | category: 'Stylistic Issues', 28 | recommended: false, 29 | url: docsUrl('no-set-state'), 30 | }, 31 | 32 | messages, 33 | 34 | schema: [], 35 | }, 36 | 37 | create: Components.detect((context, components, utils) => { 38 | /** 39 | * Checks if the component is valid 40 | * @param {Object} component The component to process 41 | * @returns {boolean} True if the component is valid, false if not. 42 | */ 43 | function isValid(component) { 44 | return !!component && !component.useSetState; 45 | } 46 | 47 | /** 48 | * Reports usages of setState for a given component 49 | * @param {Object} component The component to process 50 | */ 51 | function reportSetStateUsages(component) { 52 | for (let i = 0, j = component.setStateUsages.length; i < j; i++) { 53 | const setStateUsage = component.setStateUsages[i]; 54 | report(context, messages.noSetState, 'noSetState', { 55 | node: setStateUsage, 56 | }); 57 | } 58 | } 59 | 60 | return { 61 | CallExpression(node) { 62 | const callee = node.callee; 63 | if ( 64 | callee.type !== 'MemberExpression' 65 | || callee.object.type !== 'ThisExpression' 66 | || callee.property.name !== 'setState' 67 | ) { 68 | return; 69 | } 70 | const component = components.get(utils.getParentComponent(node)); 71 | const setStateUsages = (component && component.setStateUsages) || []; 72 | setStateUsages.push(callee); 73 | components.set(node, { 74 | useSetState: true, 75 | setStateUsages, 76 | }); 77 | }, 78 | 79 | 'Program:exit'() { 80 | values(components.list()) 81 | .filter((component) => !isValid(component)) 82 | .forEach((component) => { 83 | reportSetStateUsages(component); 84 | }); 85 | }, 86 | }; 87 | }), 88 | }; 89 | -------------------------------------------------------------------------------- /lib/rules/no-this-in-sfc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Report "this" being used in stateless functional components. 3 | */ 4 | 5 | 'use strict'; 6 | 7 | const Components = require('../util/Components'); 8 | const docsUrl = require('../util/docsUrl'); 9 | const report = require('../util/report'); 10 | 11 | // ------------------------------------------------------------------------------ 12 | // Rule Definition 13 | // ------------------------------------------------------------------------------ 14 | 15 | const messages = { 16 | noThisInSFC: 'Stateless functional components should not use `this`', 17 | }; 18 | 19 | /** @type {import('eslint').Rule.RuleModule} */ 20 | module.exports = { 21 | meta: { 22 | docs: { 23 | description: 'Disallow `this` from being used in stateless functional components', 24 | category: 'Possible Errors', 25 | recommended: false, 26 | url: docsUrl('no-this-in-sfc'), 27 | }, 28 | 29 | messages, 30 | 31 | schema: [], 32 | }, 33 | 34 | create: Components.detect((context, components, utils) => ({ 35 | MemberExpression(node) { 36 | if (node.object.type === 'ThisExpression') { 37 | const component = components.get(utils.getParentStatelessComponent(node)); 38 | if (!component || (component.node && component.node.parent && component.node.parent.type === 'Property')) { 39 | return; 40 | } 41 | report(context, messages.noThisInSFC, 'noThisInSFC', { 42 | node, 43 | }); 44 | } 45 | }, 46 | })), 47 | }; 48 | -------------------------------------------------------------------------------- /lib/rules/no-will-update-set-state.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Prevent usage of setState in componentWillUpdate 3 | * @author Yannick Croissant 4 | */ 5 | 6 | 'use strict'; 7 | 8 | const makeNoMethodSetStateRule = require('../util/makeNoMethodSetStateRule'); 9 | const testReactVersion = require('../util/version').testReactVersion; 10 | 11 | /** @type {import('eslint').Rule.RuleModule} */ 12 | module.exports = makeNoMethodSetStateRule( 13 | 'componentWillUpdate', 14 | (context) => testReactVersion(context, '>= 16.3.0') 15 | ); 16 | -------------------------------------------------------------------------------- /lib/rules/prefer-es6-class.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Enforce ES5 or ES6 class for React Components 3 | * @author Dan Hamilton 4 | */ 5 | 6 | 'use strict'; 7 | 8 | const componentUtil = require('../util/componentUtil'); 9 | const docsUrl = require('../util/docsUrl'); 10 | const report = require('../util/report'); 11 | 12 | // ------------------------------------------------------------------------------ 13 | // Rule Definition 14 | // ------------------------------------------------------------------------------ 15 | 16 | const messages = { 17 | shouldUseES6Class: 'Component should use es6 class instead of createClass', 18 | shouldUseCreateClass: 'Component should use createClass instead of es6 class', 19 | }; 20 | 21 | /** @type {import('eslint').Rule.RuleModule} */ 22 | module.exports = { 23 | meta: { 24 | docs: { 25 | description: 'Enforce ES5 or ES6 class for React Components', 26 | category: 'Stylistic Issues', 27 | recommended: false, 28 | url: docsUrl('prefer-es6-class'), 29 | }, 30 | 31 | messages, 32 | 33 | schema: [{ 34 | enum: ['always', 'never'], 35 | }], 36 | }, 37 | 38 | create(context) { 39 | const configuration = context.options[0] || 'always'; 40 | 41 | return { 42 | ObjectExpression(node) { 43 | if (componentUtil.isES5Component(node, context) && configuration === 'always') { 44 | report(context, messages.shouldUseES6Class, 'shouldUseES6Class', { 45 | node, 46 | }); 47 | } 48 | }, 49 | ClassDeclaration(node) { 50 | if (componentUtil.isES6Component(node, context) && configuration === 'never') { 51 | report(context, messages.shouldUseCreateClass, 'shouldUseCreateClass', { 52 | node, 53 | }); 54 | } 55 | }, 56 | }; 57 | }, 58 | }; 59 | -------------------------------------------------------------------------------- /lib/rules/react-in-jsx-scope.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Prevent missing React when using JSX 3 | * @author Glen Mailer 4 | */ 5 | 6 | 'use strict'; 7 | 8 | const variableUtil = require('../util/variable'); 9 | const pragmaUtil = require('../util/pragma'); 10 | const docsUrl = require('../util/docsUrl'); 11 | const report = require('../util/report'); 12 | 13 | // ----------------------------------------------------------------------------- 14 | // Rule Definition 15 | // ----------------------------------------------------------------------------- 16 | 17 | const messages = { 18 | notInScope: '\'{{name}}\' must be in scope when using JSX', 19 | }; 20 | 21 | /** @type {import('eslint').Rule.RuleModule} */ 22 | module.exports = { 23 | meta: { 24 | docs: { 25 | description: 'Disallow missing React when using JSX', 26 | category: 'Possible Errors', 27 | recommended: true, 28 | url: docsUrl('react-in-jsx-scope'), 29 | }, 30 | 31 | messages, 32 | 33 | schema: [], 34 | }, 35 | 36 | create(context) { 37 | const pragma = pragmaUtil.getFromContext(context); 38 | 39 | function checkIfReactIsInScope(node) { 40 | if (variableUtil.getVariableFromContext(context, node, pragma)) { 41 | return; 42 | } 43 | report(context, messages.notInScope, 'notInScope', { 44 | node, 45 | data: { 46 | name: pragma, 47 | }, 48 | }); 49 | } 50 | 51 | return { 52 | JSXOpeningElement: checkIfReactIsInScope, 53 | JSXOpeningFragment: checkIfReactIsInScope, 54 | }; 55 | }, 56 | }; 57 | -------------------------------------------------------------------------------- /lib/rules/state-in-constructor.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Enforce the state initialization style to be either in a constructor or with a class property 3 | * @author Kanitkorn Sujautra 4 | */ 5 | 6 | 'use strict'; 7 | 8 | const astUtil = require('../util/ast'); 9 | const componentUtil = require('../util/componentUtil'); 10 | const docsUrl = require('../util/docsUrl'); 11 | const report = require('../util/report'); 12 | 13 | // ------------------------------------------------------------------------------ 14 | // Rule Definition 15 | // ------------------------------------------------------------------------------ 16 | 17 | const messages = { 18 | stateInitConstructor: 'State initialization should be in a constructor', 19 | stateInitClassProp: 'State initialization should be in a class property', 20 | }; 21 | 22 | /** @type {import('eslint').Rule.RuleModule} */ 23 | module.exports = { 24 | meta: { 25 | docs: { 26 | description: 'Enforce class component state initialization style', 27 | category: 'Stylistic Issues', 28 | recommended: false, 29 | url: docsUrl('state-in-constructor'), 30 | }, 31 | 32 | messages, 33 | 34 | schema: [{ 35 | enum: ['always', 'never'], 36 | }], 37 | }, 38 | 39 | create(context) { 40 | const option = context.options[0] || 'always'; 41 | return { 42 | 'ClassProperty, PropertyDefinition'(node) { 43 | if ( 44 | option === 'always' 45 | && !node.static 46 | && node.key.name === 'state' 47 | && componentUtil.getParentES6Component(context, node) 48 | ) { 49 | report(context, messages.stateInitConstructor, 'stateInitConstructor', { 50 | node, 51 | }); 52 | } 53 | }, 54 | AssignmentExpression(node) { 55 | if ( 56 | option === 'never' 57 | && componentUtil.isStateMemberExpression(node.left) 58 | && astUtil.inConstructor(context, node) 59 | && componentUtil.getParentES6Component(context, node) 60 | ) { 61 | report(context, messages.stateInitClassProp, 'stateInitClassProp', { 62 | node, 63 | }); 64 | } 65 | }, 66 | }; 67 | }, 68 | }; 69 | -------------------------------------------------------------------------------- /lib/types.d.ts: -------------------------------------------------------------------------------- 1 | import eslint from 'eslint'; 2 | import estree from 'estree'; 3 | 4 | declare global { 5 | interface ASTNode extends estree.BaseNode { 6 | [_: string]: any; // TODO: fixme 7 | } 8 | type Scope = eslint.Scope.Scope; 9 | type Token = eslint.AST.Token; 10 | type Fixer = eslint.Rule.RuleFixer; 11 | type JSXAttribute = ASTNode; 12 | type JSXElement = ASTNode; 13 | type JSXFragment = ASTNode; 14 | type JSXOpeningElement = ASTNode; 15 | type JSXSpreadAttribute = ASTNode; 16 | 17 | type Context = eslint.Rule.RuleContext; 18 | 19 | type TypeDeclarationBuilder = (annotation: ASTNode, parentName: string, seen: Set) => object; 20 | 21 | type TypeDeclarationBuilders = { 22 | [k in string]: TypeDeclarationBuilder; 23 | }; 24 | 25 | type UnionTypeDefinition = { 26 | type: 'union' | 'shape'; 27 | children: unknown[]; 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /lib/util/annotations.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Utility functions for type annotation detection. 3 | * @author Yannick Croissant 4 | * @author Vitor Balocco 5 | */ 6 | 7 | 'use strict'; 8 | 9 | const getFirstTokens = require('./eslint').getFirstTokens; 10 | 11 | /** 12 | * Checks if we are declaring a `props` argument with a flow type annotation. 13 | * @param {ASTNode} node The AST node being checked. 14 | * @param {Object} context 15 | * @returns {boolean} True if the node is a type annotated props declaration, false if not. 16 | */ 17 | function isAnnotatedFunctionPropsDeclaration(node, context) { 18 | if (!node || !node.params || !node.params.length) { 19 | return false; 20 | } 21 | 22 | const typeNode = node.params[0].type === 'AssignmentPattern' ? node.params[0].left : node.params[0]; 23 | 24 | const tokens = getFirstTokens(context, typeNode, 2); 25 | const isAnnotated = typeNode.typeAnnotation; 26 | const isDestructuredProps = typeNode.type === 'ObjectPattern'; 27 | const isProps = tokens[0].value === 'props' || (tokens[1] && tokens[1].value === 'props'); 28 | 29 | return (isAnnotated && (isDestructuredProps || isProps)); 30 | } 31 | 32 | module.exports = { 33 | isAnnotatedFunctionPropsDeclaration, 34 | }; 35 | -------------------------------------------------------------------------------- /lib/util/docsUrl.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function docsUrl(ruleName) { 4 | return `https://github.com/jsx-eslint/eslint-plugin-react/tree/master/docs/rules/${ruleName}.md`; 5 | } 6 | 7 | module.exports = docsUrl; 8 | -------------------------------------------------------------------------------- /lib/util/error.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Logs out a message if there is no format option set. 5 | * @param {string} message - Message to log. 6 | */ 7 | function error(message) { 8 | if (!/=-(f|-format)=/.test(process.argv.join('='))) { 9 | // eslint-disable-next-line no-console 10 | console.error(message); 11 | } 12 | } 13 | 14 | module.exports = error; 15 | -------------------------------------------------------------------------------- /lib/util/eslint.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function getSourceCode(context) { 4 | return context.getSourceCode ? context.getSourceCode() : context.sourceCode; 5 | } 6 | 7 | function getAncestors(context, node) { 8 | const sourceCode = getSourceCode(context); 9 | return sourceCode.getAncestors ? sourceCode.getAncestors(node) : context.getAncestors(); 10 | } 11 | 12 | function getScope(context, node) { 13 | const sourceCode = getSourceCode(context); 14 | if (sourceCode.getScope) { 15 | return sourceCode.getScope(node); 16 | } 17 | 18 | return context.getScope(); 19 | } 20 | 21 | function markVariableAsUsed(name, node, context) { 22 | const sourceCode = getSourceCode(context); 23 | return sourceCode.markVariableAsUsed 24 | ? sourceCode.markVariableAsUsed(name, node) 25 | : context.markVariableAsUsed(name); 26 | } 27 | 28 | function getFirstTokens(context, node, count) { 29 | const sourceCode = getSourceCode(context); 30 | return sourceCode.getFirstTokens ? sourceCode.getFirstTokens(node, count) : context.getFirstTokens(node, count); 31 | } 32 | 33 | function getText(context) { 34 | const sourceCode = getSourceCode(context); 35 | const args = Array.prototype.slice.call(arguments, 1); 36 | return sourceCode.getText ? sourceCode.getText.apply(sourceCode, args) : context.getSource.apply(context, args); 37 | } 38 | 39 | module.exports = { 40 | getAncestors, 41 | getFirstTokens, 42 | getScope, 43 | getSourceCode, 44 | getText, 45 | markVariableAsUsed, 46 | }; 47 | -------------------------------------------------------------------------------- /lib/util/getTokenBeforeClosingBracket.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Find the token before the closing bracket. 5 | * @param {ASTNode} node - The JSX element node. 6 | * @returns {Token} The token before the closing bracket. 7 | */ 8 | function getTokenBeforeClosingBracket(node) { 9 | const attributes = node.attributes; 10 | if (!attributes || attributes.length === 0) { 11 | return node.name; 12 | } 13 | return attributes[attributes.length - 1]; 14 | } 15 | 16 | module.exports = getTokenBeforeClosingBracket; 17 | -------------------------------------------------------------------------------- /lib/util/isCreateContext.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const astUtil = require('./ast'); 4 | 5 | /** 6 | * Checks if the node is a React.createContext call 7 | * @param {ASTNode} node - The AST node being checked. 8 | * @returns {boolean} - True if node is a React.createContext call, false if not. 9 | */ 10 | module.exports = function isCreateContext(node) { 11 | if ( 12 | node.init 13 | && node.init.callee 14 | ) { 15 | if ( 16 | astUtil.isCallExpression(node.init) 17 | && node.init.callee.name === 'createContext' 18 | ) { 19 | return true; 20 | } 21 | 22 | if ( 23 | node.init.callee.type === 'MemberExpression' 24 | && node.init.callee.property 25 | && node.init.callee.property.name === 'createContext' 26 | ) { 27 | return true; 28 | } 29 | } 30 | 31 | if ( 32 | node.expression 33 | && node.expression.type === 'AssignmentExpression' 34 | && node.expression.operator === '=' 35 | && astUtil.isCallExpression(node.expression.right) 36 | && node.expression.right.callee 37 | ) { 38 | const right = node.expression.right; 39 | 40 | if (right.callee.name === 'createContext') { 41 | return true; 42 | } 43 | 44 | if ( 45 | right.callee.type === 'MemberExpression' 46 | && right.callee.property 47 | && right.callee.property.name === 'createContext' 48 | ) { 49 | return true; 50 | } 51 | } 52 | 53 | return false; 54 | }; 55 | -------------------------------------------------------------------------------- /lib/util/isCreateElement.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const pragmaUtil = require('./pragma'); 4 | const isDestructuredFromPragmaImport = require('./isDestructuredFromPragmaImport'); 5 | 6 | /** 7 | * Checks if the node is a createElement call 8 | * @param {Context} context - The AST node being checked. 9 | * @param {ASTNode} node - The AST node being checked. 10 | * @returns {boolean} - True if node is a createElement call object literal, False if not. 11 | */ 12 | module.exports = function isCreateElement(context, node) { 13 | if (!node.callee) { 14 | return false; 15 | } 16 | 17 | if ( 18 | node.callee.type === 'MemberExpression' 19 | && node.callee.property.name === 'createElement' 20 | && node.callee.object 21 | && node.callee.object.name === pragmaUtil.getFromContext(context) 22 | ) { 23 | return true; 24 | } 25 | 26 | if ( 27 | node.callee.name === 'createElement' 28 | && isDestructuredFromPragmaImport(context, node, 'createElement') 29 | ) { 30 | return true; 31 | } 32 | 33 | return false; 34 | }; 35 | -------------------------------------------------------------------------------- /lib/util/isFirstLetterCapitalized.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Check if the first letter of a string is capitalized. 5 | * @param {string} word String to check 6 | * @returns {boolean} True if first letter is capitalized. 7 | */ 8 | module.exports = function isFirstLetterCapitalized(word) { 9 | if (!word) { 10 | return false; 11 | } 12 | const firstLetter = word.replace(/^_+/, '').charAt(0); 13 | return firstLetter.toUpperCase() === firstLetter; 14 | }; 15 | -------------------------------------------------------------------------------- /lib/util/lifecycleMethods.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview lifecycle methods 3 | * @author Tan Nguyen 4 | */ 5 | 6 | 'use strict'; 7 | 8 | module.exports = { 9 | instance: [ 10 | 'getDefaultProps', 11 | 'getInitialState', 12 | 'getChildContext', 13 | 'componentWillMount', 14 | 'UNSAFE_componentWillMount', 15 | 'componentDidMount', 16 | 'componentWillReceiveProps', 17 | 'UNSAFE_componentWillReceiveProps', 18 | 'shouldComponentUpdate', 19 | 'componentWillUpdate', 20 | 'UNSAFE_componentWillUpdate', 21 | 'getSnapshotBeforeUpdate', 22 | 'componentDidUpdate', 23 | 'componentDidCatch', 24 | 'componentWillUnmount', 25 | 'render', 26 | ], 27 | static: [ 28 | 'getDerivedStateFromProps', 29 | ], 30 | }; 31 | -------------------------------------------------------------------------------- /lib/util/linkComponents.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Utility functions for propWrapperFunctions setting 3 | */ 4 | 5 | 'use strict'; 6 | 7 | const iterFrom = require('es-iterator-helpers/Iterator.from'); 8 | const map = require('es-iterator-helpers/Iterator.prototype.map'); 9 | 10 | /** TODO: type {(string | { name: string, linkAttribute: string })[]} */ 11 | /** @type {any} */ 12 | const DEFAULT_LINK_COMPONENTS = ['a']; 13 | const DEFAULT_LINK_ATTRIBUTE = 'href'; 14 | 15 | /** TODO: type {(string | { name: string, formAttribute: string })[]} */ 16 | /** @type {any} */ 17 | const DEFAULT_FORM_COMPONENTS = ['form']; 18 | const DEFAULT_FORM_ATTRIBUTE = 'action'; 19 | 20 | function getFormComponents(context) { 21 | const settings = context.settings || {}; 22 | const formComponents = /** @type {typeof DEFAULT_FORM_COMPONENTS} */ ( 23 | DEFAULT_FORM_COMPONENTS.concat(settings.formComponents || []) 24 | ); 25 | return new Map(map(iterFrom(formComponents), (value) => { 26 | if (typeof value === 'string') { 27 | return [value, [DEFAULT_FORM_ATTRIBUTE]]; 28 | } 29 | return [value.name, [].concat(value.formAttribute)]; 30 | })); 31 | } 32 | 33 | function getLinkComponents(context) { 34 | const settings = context.settings || {}; 35 | const linkComponents = /** @type {typeof DEFAULT_LINK_COMPONENTS} */ ( 36 | DEFAULT_LINK_COMPONENTS.concat(settings.linkComponents || []) 37 | ); 38 | return new Map(map(iterFrom(linkComponents), (value) => { 39 | if (typeof value === 'string') { 40 | return [value, [DEFAULT_LINK_ATTRIBUTE]]; 41 | } 42 | return [value.name, [].concat(value.linkAttribute)]; 43 | })); 44 | } 45 | 46 | module.exports = { 47 | getFormComponents, 48 | getLinkComponents, 49 | }; 50 | -------------------------------------------------------------------------------- /lib/util/log.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Logs out a message if there is no format option set. 5 | * @param {string} message - Message to log. 6 | */ 7 | function log(message) { 8 | if (!/=-(f|-format)=/.test(process.argv.join('='))) { 9 | // eslint-disable-next-line no-console 10 | console.log(message); 11 | } 12 | } 13 | 14 | module.exports = log; 15 | -------------------------------------------------------------------------------- /lib/util/message.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const semver = require('semver'); 4 | const eslintPkg = require('eslint/package.json'); 5 | 6 | module.exports = function getMessageData(messageId, message) { 7 | return messageId && semver.satisfies(eslintPkg.version, '>= 4.15') ? { messageId } : { message }; 8 | }; 9 | -------------------------------------------------------------------------------- /lib/util/pragma.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Utility functions for React pragma configuration 3 | * @author Yannick Croissant 4 | */ 5 | 6 | 'use strict'; 7 | 8 | const getSourceCode = require('./eslint').getSourceCode; 9 | 10 | const JSX_ANNOTATION_REGEX = /@jsx\s+([^\s]+)/; 11 | // Does not check for reserved keywords or unicode characters 12 | const JS_IDENTIFIER_REGEX = /^[_$a-zA-Z][_$a-zA-Z0-9]*$/; 13 | 14 | /** 15 | * @param {Context} context 16 | * @returns {string} 17 | */ 18 | function getCreateClassFromContext(context) { 19 | let pragma = 'createReactClass'; 20 | // .eslintrc shared settings (https://eslint.org/docs/user-guide/configuring#adding-shared-settings) 21 | if (context.settings.react && context.settings.react.createClass) { 22 | pragma = context.settings.react.createClass; 23 | } 24 | if (!JS_IDENTIFIER_REGEX.test(pragma)) { 25 | throw new Error(`createClass pragma ${pragma} is not a valid function name`); 26 | } 27 | return pragma; 28 | } 29 | 30 | /** 31 | * @param {Context} context 32 | * @returns {string} 33 | */ 34 | function getFragmentFromContext(context) { 35 | let pragma = 'Fragment'; 36 | // .eslintrc shared settings (https://eslint.org/docs/user-guide/configuring#adding-shared-settings) 37 | if (context.settings.react && context.settings.react.fragment) { 38 | pragma = context.settings.react.fragment; 39 | } 40 | if (!JS_IDENTIFIER_REGEX.test(pragma)) { 41 | throw new Error(`Fragment pragma ${pragma} is not a valid identifier`); 42 | } 43 | return pragma; 44 | } 45 | 46 | /** 47 | * @param {Context} context 48 | * @returns {string} 49 | */ 50 | function getFromContext(context) { 51 | let pragma = 'React'; 52 | 53 | const sourceCode = getSourceCode(context); 54 | const pragmaNode = sourceCode.getAllComments().find((node) => JSX_ANNOTATION_REGEX.test(node.value)); 55 | 56 | if (pragmaNode) { 57 | const matches = JSX_ANNOTATION_REGEX.exec(pragmaNode.value); 58 | pragma = matches[1].split('.')[0]; 59 | // .eslintrc shared settings (https://eslint.org/docs/user-guide/configuring#adding-shared-settings) 60 | } else if (context.settings.react && context.settings.react.pragma) { 61 | pragma = context.settings.react.pragma; 62 | } 63 | 64 | if (!JS_IDENTIFIER_REGEX.test(pragma)) { 65 | console.warn(`React pragma ${pragma} is not a valid identifier`); 66 | return 'React'; 67 | } 68 | return pragma; 69 | } 70 | 71 | module.exports = { 72 | getCreateClassFromContext, 73 | getFragmentFromContext, 74 | getFromContext, 75 | }; 76 | -------------------------------------------------------------------------------- /lib/util/propWrapper.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Utility functions for propWrapperFunctions setting 3 | */ 4 | 5 | 'use strict'; 6 | 7 | const filter = require('es-iterator-helpers/Iterator.prototype.filter'); 8 | const some = require('es-iterator-helpers/Iterator.prototype.some'); 9 | 10 | function searchPropWrapperFunctions(name, propWrapperFunctions) { 11 | const splitName = name.split('.'); 12 | return some(propWrapperFunctions.values(), (func) => { 13 | if (splitName.length === 2 && func.object === splitName[0] && func.property === splitName[1]) { 14 | return true; 15 | } 16 | return name === func || func.property === name; 17 | }); 18 | } 19 | 20 | function getPropWrapperFunctions(context) { 21 | return new Set(context.settings.propWrapperFunctions || []); 22 | } 23 | 24 | function isPropWrapperFunction(context, name) { 25 | if (typeof name !== 'string') { 26 | return false; 27 | } 28 | const propWrapperFunctions = getPropWrapperFunctions(context); 29 | return searchPropWrapperFunctions(name, propWrapperFunctions); 30 | } 31 | 32 | function getExactPropWrapperFunctions(context) { 33 | const propWrapperFunctions = getPropWrapperFunctions(context); 34 | const exactPropWrappers = filter(propWrapperFunctions.values(), (func) => func.exact === true); 35 | return new Set(exactPropWrappers); 36 | } 37 | 38 | function isExactPropWrapperFunction(context, name) { 39 | const exactPropWrappers = getExactPropWrapperFunctions(context); 40 | return searchPropWrapperFunctions(name, exactPropWrappers); 41 | } 42 | 43 | function formatPropWrapperFunctions(propWrapperFunctions) { 44 | return Array.from(propWrapperFunctions, (func) => { 45 | if (func.object && func.property) { 46 | return `'${func.object}.${func.property}'`; 47 | } 48 | if (func.property) { 49 | return `'${func.property}'`; 50 | } 51 | return `'${func}'`; 52 | }).join(', '); 53 | } 54 | 55 | module.exports = { 56 | formatPropWrapperFunctions, 57 | getExactPropWrapperFunctions, 58 | getPropWrapperFunctions, 59 | isExactPropWrapperFunction, 60 | isPropWrapperFunction, 61 | }; 62 | -------------------------------------------------------------------------------- /lib/util/report.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getMessageData = require('./message'); 4 | 5 | module.exports = function report(context, message, messageId, data) { 6 | context.report( 7 | Object.assign( 8 | getMessageData(messageId, message), 9 | data 10 | ) 11 | ); 12 | }; 13 | -------------------------------------------------------------------------------- /test-published-types/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /test-published-types/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const react = require('eslint-plugin-react'); 4 | 5 | /** @type {import('eslint').Linter.Config[]} */ 6 | const config = [ 7 | { 8 | plugins: { 9 | react, 10 | }, 11 | }, 12 | ]; 13 | 14 | module.exports = config; 15 | -------------------------------------------------------------------------------- /test-published-types/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-plugin-react-test-published-types", 3 | "private": true, 4 | "version": "0.0.0", 5 | "dependencies": { 6 | "eslint": "^9.11.1" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test-published-types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | 4 | "files": [ 5 | "index.js" 6 | ], 7 | 8 | "compilerOptions": { 9 | "lib": ["esnext"], 10 | "types": ["node"], 11 | "skipLibCheck": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/eslint-remote-tester.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const eslintRemoteTesterRepositories = require('eslint-remote-tester-repositories'); 4 | 5 | module.exports = { 6 | repositories: eslintRemoteTesterRepositories.getRepositories({ randomize: true }), 7 | 8 | pathIgnorePattern: eslintRemoteTesterRepositories.getPathIgnorePattern(), 9 | 10 | extensions: ['js', 'jsx', 'ts', 'tsx'], 11 | 12 | concurrentTasks: 3, 13 | 14 | logLevel: 'info', 15 | 16 | /** Optional boolean flag used to enable caching of cloned repositories. For CIs it's ideal to disable caching. Defauls to true. */ 17 | cache: false, 18 | 19 | eslintrc: { 20 | root: true, 21 | env: { 22 | es6: true, 23 | }, 24 | overrides: [ 25 | { 26 | files: ['*.ts', '*.tsx', '*.mts', '*.cts'], 27 | parser: '@typescript-eslint/parser', 28 | }, 29 | ], 30 | parserOptions: { 31 | ecmaVersion: 2020, 32 | sourceType: 'module', 33 | ecmaFeatures: { 34 | jsx: true, 35 | }, 36 | }, 37 | settings: { 38 | react: { 39 | version: '16.13.1', 40 | }, 41 | }, 42 | extends: ['plugin:react/all'], 43 | }, 44 | }; 45 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --reporter=min 2 | -------------------------------------------------------------------------------- /tests/fixtures/flat-config/config-all/eslint.config-deep.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const reactAll = require('../../../../configs/all'); 4 | 5 | module.exports = [{ 6 | files: ['**/*.jsx'], 7 | ...reactAll, 8 | languageOptions: { 9 | ...reactAll.languageOptions 10 | } 11 | }]; 12 | -------------------------------------------------------------------------------- /tests/fixtures/flat-config/config-all/eslint.config-root.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const reactPlugin = require('../../../..'); 4 | 5 | module.exports = [{ 6 | files: ['**/*.jsx'], 7 | ...reactPlugin.configs.flat.all 8 | }]; 9 | -------------------------------------------------------------------------------- /tests/fixtures/flat-config/config-all/test.jsx: -------------------------------------------------------------------------------- 1 |
2 | test 3 |
4 | -------------------------------------------------------------------------------- /tests/fixtures/flat-config/config-jsx-runtime/eslint.config-deep.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const reactRecommended = require('../../../../configs/recommended'); 4 | const reactJSXRuntime = require('../../../../configs/jsx-runtime'); 5 | 6 | module.exports = [ 7 | { 8 | files: ['**/*.jsx'], 9 | ...reactRecommended, 10 | languageOptions: { 11 | ...reactRecommended.languageOptions 12 | } 13 | }, 14 | reactJSXRuntime 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/fixtures/flat-config/config-jsx-runtime/eslint.config-root.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const reactPlugin = require('../../../..'); 4 | 5 | module.exports = [ 6 | { 7 | files: ['**/*.jsx'], 8 | ...reactPlugin.configs.flat.recommended 9 | }, 10 | reactPlugin.configs.flat['jsx-runtime'] 11 | ]; 12 | -------------------------------------------------------------------------------- /tests/fixtures/flat-config/config-jsx-runtime/test.jsx: -------------------------------------------------------------------------------- 1 |
2 | test 3 |
4 | -------------------------------------------------------------------------------- /tests/fixtures/flat-config/config-recommended/eslint.config-deep.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const reactRecommended = require('../../../../configs/recommended'); 4 | 5 | module.exports = [{ 6 | files: ['**/*.jsx'], 7 | ...reactRecommended, 8 | languageOptions: { 9 | ...reactRecommended.languageOptions 10 | } 11 | }]; 12 | -------------------------------------------------------------------------------- /tests/fixtures/flat-config/config-recommended/eslint.config-root.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const reactPlugin = require('../../../..'); 4 | 5 | module.exports = [{ 6 | files: ['**/*.jsx'], 7 | ...reactPlugin.configs.flat.recommended 8 | }]; 9 | -------------------------------------------------------------------------------- /tests/fixtures/flat-config/config-recommended/test.jsx: -------------------------------------------------------------------------------- 1 |
2 | test 3 |
4 | -------------------------------------------------------------------------------- /tests/fixtures/flat-config/plugin-and-config/eslint.config-deep.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const react = require('../../../..'); 4 | const reactRecommended = require('../../../../configs/recommended'); 5 | 6 | module.exports = [ 7 | { 8 | files: ['**/*.jsx'], 9 | plugins: { react } 10 | }, 11 | { 12 | files: ['**/*.jsx'], 13 | ...reactRecommended, 14 | languageOptions: { 15 | ...reactRecommended.languageOptions 16 | } 17 | } 18 | ]; 19 | -------------------------------------------------------------------------------- /tests/fixtures/flat-config/plugin-and-config/eslint.config-root.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const react = require('../../../..'); 4 | const reactRecommended = require('../../../../configs/recommended'); 5 | 6 | module.exports = [ 7 | { 8 | files: ['**/*.jsx'], 9 | plugins: { react } 10 | }, 11 | { 12 | files: ['**/*.jsx'], 13 | ...react.configs.flat.recommended 14 | } 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/fixtures/flat-config/plugin-and-config/test.jsx: -------------------------------------------------------------------------------- 1 |
2 | test 3 |
4 | -------------------------------------------------------------------------------- /tests/fixtures/flat-config/plugin/eslint.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const react = require('../../../..'); 4 | 5 | module.exports = [{ 6 | files: ['**/*.jsx'], 7 | languageOptions: { 8 | parserOptions: { 9 | ecmaFeatures: { 10 | jsx: true, 11 | }, 12 | }, 13 | }, 14 | plugins: { 15 | react, 16 | }, 17 | rules: { 18 | 'react/jsx-no-literals': 1, 19 | }, 20 | }]; 21 | -------------------------------------------------------------------------------- /tests/fixtures/flat-config/plugin/test.jsx: -------------------------------------------------------------------------------- 1 |
2 | test 3 |
4 | -------------------------------------------------------------------------------- /tests/fixtures/version/detect-version-missing/node_modules/react/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const error = new Error(); 4 | error.code = 'MODULE_NOT_FOUND'; 5 | throw error; 6 | -------------------------------------------------------------------------------- /tests/fixtures/version/detect-version-missing/node_modules/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react", 3 | "main": "index.js" 4 | } 5 | -------------------------------------------------------------------------------- /tests/fixtures/version/detect-version-missing/test.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsx-eslint/eslint-plugin-react/f2869fd6dc76ceb863c5e2aeea8bf4d392508775/tests/fixtures/version/detect-version-missing/test.js -------------------------------------------------------------------------------- /tests/fixtures/version/detect-version-sibling/node_modules/flow-bin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flow-bin", 3 | "version": "2.92.0" 4 | } 5 | -------------------------------------------------------------------------------- /tests/fixtures/version/detect-version-sibling/node_modules/react/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | version: '2.3.4', 5 | }; 6 | -------------------------------------------------------------------------------- /tests/fixtures/version/detect-version-sibling/node_modules/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react", 3 | "version": "2.3.4", 4 | "main": "index.js" 5 | } 6 | -------------------------------------------------------------------------------- /tests/fixtures/version/detect-version-sibling/test.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsx-eslint/eslint-plugin-react/f2869fd6dc76ceb863c5e2aeea8bf4d392508775/tests/fixtures/version/detect-version-sibling/test.js -------------------------------------------------------------------------------- /tests/fixtures/version/detect-version/detect-version-child/node_modules/flow-bin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flow-bin", 3 | "version": "3.92.0" 4 | } 5 | -------------------------------------------------------------------------------- /tests/fixtures/version/detect-version/detect-version-child/node_modules/react/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | version: '3.4.5', 5 | }; 6 | -------------------------------------------------------------------------------- /tests/fixtures/version/detect-version/detect-version-child/node_modules/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react", 3 | "version": "3.4.5", 4 | "main": "index.js" 5 | } 6 | -------------------------------------------------------------------------------- /tests/fixtures/version/detect-version/detect-version-child/test.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsx-eslint/eslint-plugin-react/f2869fd6dc76ceb863c5e2aeea8bf4d392508775/tests/fixtures/version/detect-version/detect-version-child/test.js -------------------------------------------------------------------------------- /tests/fixtures/version/detect-version/node_modules/flow-bin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flow-bin", 3 | "version": "0.92.0" 4 | } 5 | -------------------------------------------------------------------------------- /tests/fixtures/version/detect-version/node_modules/react/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | version: '1.2.3', 5 | }; 6 | -------------------------------------------------------------------------------- /tests/fixtures/version/detect-version/node_modules/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react", 3 | "version": "1.2.3", 4 | "main": "index.js" 5 | } 6 | -------------------------------------------------------------------------------- /tests/fixtures/version/detect-version/test.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsx-eslint/eslint-plugin-react/f2869fd6dc76ceb863c5e2aeea8bf4d392508775/tests/fixtures/version/detect-version/test.js -------------------------------------------------------------------------------- /tests/helpers/getESLintCoreRule.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const version = require('eslint/package.json').version; 4 | const semver = require('semver'); 5 | 6 | const isESLintV8 = semver.major(version) >= 8; 7 | 8 | // eslint-disable-next-line global-require, import/no-dynamic-require, import/no-unresolved 9 | const getESLintCoreRule = (ruleId) => (isESLintV8 ? require('eslint/use-at-your-own-risk').builtinRules.get(ruleId) : require(`eslint/lib/rules/${ruleId}`)); 10 | 11 | module.exports = getESLintCoreRule; 12 | -------------------------------------------------------------------------------- /tests/helpers/getRuleDefiner.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const eslint = require('eslint'); 4 | 5 | // `ruleTester` is a RuleTester instance 6 | const getRuleDefiner = (ruleTester) => (typeof Symbol !== 'undefined' && Symbol.for && ruleTester[Symbol.for('react.RuleTester.RuleDefiner')]) 7 | || ruleTester.linter 8 | || eslint.linter 9 | || eslint.Linter; 10 | 11 | module.exports = getRuleDefiner; 12 | -------------------------------------------------------------------------------- /tests/lib/rules/jsx-no-duplicate-props.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Enforce no duplicate props 3 | * @author Markus Ånöstam 4 | */ 5 | 6 | 'use strict'; 7 | 8 | // ----------------------------------------------------------------------------- 9 | // Requirements 10 | // ----------------------------------------------------------------------------- 11 | 12 | const RuleTester = require('../../helpers/ruleTester'); 13 | const rule = require('../../../lib/rules/jsx-no-duplicate-props'); 14 | 15 | const parsers = require('../../helpers/parsers'); 16 | 17 | const parserOptions = { 18 | ecmaVersion: 2018, 19 | sourceType: 'module', 20 | ecmaFeatures: { 21 | jsx: true, 22 | }, 23 | }; 24 | 25 | // ----------------------------------------------------------------------------- 26 | // Tests 27 | // ----------------------------------------------------------------------------- 28 | 29 | const ruleTester = new RuleTester({ parserOptions }); 30 | 31 | const expectedError = { 32 | messageId: 'noDuplicateProps', 33 | type: 'JSXAttribute', 34 | }; 35 | 36 | const ignoreCaseArgs = [{ 37 | ignoreCase: true, 38 | }]; 39 | 40 | ruleTester.run('jsx-no-duplicate-props', rule, { 41 | valid: parsers.all([ 42 | { code: ';' }, 43 | { code: ';' }, 44 | { code: ';' }, 45 | { code: ';' }, 46 | { code: ';' }, 47 | { code: ';' }, 48 | { code: ';' }, 49 | { code: ';' }, 50 | { code: ';' }, 51 | { code: ';' }, 52 | { code: ';' }, 53 | { code: ';' }, 54 | { 55 | code: ';', 56 | options: ignoreCaseArgs, 57 | features: ['jsx namespace'], 58 | }, 59 | ]), 60 | invalid: parsers.all([ 61 | { 62 | code: ';', 63 | errors: [expectedError], 64 | }, 65 | { 66 | code: ';', 67 | errors: [expectedError], 68 | }, 69 | { 70 | code: ';', 71 | errors: [expectedError], 72 | }, 73 | { 74 | code: ';', 75 | options: ignoreCaseArgs, 76 | errors: [expectedError], 77 | }, 78 | { 79 | code: ';', 80 | options: ignoreCaseArgs, 81 | errors: [expectedError], 82 | }, 83 | { 84 | code: ';', 85 | options: ignoreCaseArgs, 86 | errors: [expectedError], 87 | }, 88 | ]), 89 | }); 90 | -------------------------------------------------------------------------------- /tests/lib/rules/jsx-props-no-spread-multi.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Tests for jsx-props-no-spread-multi 3 | */ 4 | 5 | 'use strict'; 6 | 7 | // ----------------------------------------------------------------------------- 8 | // Requirements 9 | // ----------------------------------------------------------------------------- 10 | 11 | const RuleTester = require('../../helpers/ruleTester'); 12 | const rule = require('../../../lib/rules/jsx-props-no-spread-multi'); 13 | 14 | const parsers = require('../../helpers/parsers'); 15 | 16 | const parserOptions = { 17 | ecmaVersion: 2018, 18 | sourceType: 'module', 19 | ecmaFeatures: { 20 | jsx: true, 21 | }, 22 | }; 23 | 24 | // ----------------------------------------------------------------------------- 25 | // Tests 26 | // ----------------------------------------------------------------------------- 27 | 28 | const ruleTester = new RuleTester({ parserOptions }); 29 | const expectedError = { messageId: 'noMultiSpreading' }; 30 | 31 | ruleTester.run('jsx-props-no-spread-multi', rule, { 32 | valid: parsers.all([ 33 | { 34 | code: ` 35 | const a = {}; 36 | 37 | `, 38 | }, 39 | { 40 | code: ` 41 | const a = {}; 42 | const b = {}; 43 | 44 | `, 45 | }, 46 | ]), 47 | 48 | invalid: parsers.all([ 49 | { 50 | code: ` 51 | const props = {}; 52 | 53 | `, 54 | errors: [expectedError], 55 | }, 56 | { 57 | code: ` 58 | const props = {}; 59 |
60 | `, 61 | errors: [expectedError], 62 | }, 63 | { 64 | code: ` 65 | const props = {}; 66 |
67 | `, 68 | errors: [expectedError, expectedError], 69 | }, 70 | ]), 71 | }); 72 | -------------------------------------------------------------------------------- /tests/util/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/util/isFirstLetterCapitalized.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | 5 | const isFirstLetterCapitalized = require('../../lib/util/isFirstLetterCapitalized'); 6 | 7 | describe('isFirstLetterCapitalized', () => { 8 | it('should return false for invalid input', () => { 9 | assert.equal(isFirstLetterCapitalized(), false); 10 | assert.equal(isFirstLetterCapitalized(null), false); 11 | assert.equal(isFirstLetterCapitalized(''), false); 12 | }); 13 | 14 | it('should return false for uncapitalized string', () => { 15 | assert.equal(isFirstLetterCapitalized('isCapitalized'), false); 16 | assert.equal(isFirstLetterCapitalized('lowercase'), false); 17 | assert.equal(isFirstLetterCapitalized('_startsWithUnderscore'), false); 18 | assert.equal(isFirstLetterCapitalized('__startsWithUnderscore'), false); 19 | }); 20 | 21 | it('should return true for capitalized string, with or without leading underscores', () => { 22 | assert.equal(isFirstLetterCapitalized('IsCapitalized'), true); 23 | assert.equal(isFirstLetterCapitalized('_IsCapitalized'), true); 24 | assert.equal(isFirstLetterCapitalized('__IsCapitalized'), true); 25 | assert.equal(isFirstLetterCapitalized('UPPERCASE'), true); 26 | assert.equal(isFirstLetterCapitalized('_UPPERCASE'), true); 27 | assert.equal(isFirstLetterCapitalized('__UPPERCASE'), true); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /tests/util/jsx.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const espree = require('espree'); 5 | 6 | const jsxUtil = require('../../lib/util/jsx'); 7 | 8 | const isReturningJSX = jsxUtil.isReturningJSX; 9 | 10 | const DEFAULT_CONFIG = { 11 | ecmaVersion: 6, 12 | ecmaFeatures: { 13 | jsx: true, 14 | }, 15 | }; 16 | 17 | const parseCode = (code) => { 18 | const ASTnode = espree.parse(code, DEFAULT_CONFIG); 19 | // Return only first statement 20 | return ASTnode.body[0]; 21 | }; 22 | 23 | const mockContext = { 24 | getSourceCode() { return { getScope: mockContext.getScope }; }, 25 | getScope() { 26 | return { 27 | type: 'global', 28 | upper: null, 29 | childScopes: [], 30 | variables: [], 31 | }; 32 | }, 33 | }; 34 | 35 | describe('jsxUtil', () => { 36 | describe('isReturningJSX', () => { 37 | const assertValid = (codeStr) => assert( 38 | isReturningJSX(mockContext, parseCode(codeStr)) 39 | ); 40 | 41 | it('Works when returning JSX', () => { 42 | assertValid(` 43 | function Test() { 44 | return ( 45 | something 46 | ) 47 | } 48 | `); 49 | 50 | assertValid(` 51 | function Test() { 52 | return something; 53 | } 54 | `); 55 | }); 56 | 57 | it('Works when returning null', () => { 58 | assertValid(` 59 | function Test() { 60 | return null; 61 | } 62 | `); 63 | 64 | assertValid(` 65 | function Test({prop}) { 66 | return prop || null; 67 | } 68 | `); 69 | }); 70 | 71 | it('Works with nested return', () => { 72 | assertValid(` 73 | function Test({prop}) { 74 | if (prop) { 75 | return something 76 | } 77 | } 78 | `); 79 | }); 80 | 81 | it('Can ignore null', () => { 82 | assertValid(` 83 | function Test() { 84 | return null; 85 | } 86 | `); 87 | }); 88 | 89 | it('Ignores JSX arguments to function calls used as return value of arrow functions', () => { 90 | let astNode = parseCode(`const obj = { 91 | prop: () => test(something) 92 | }`); 93 | let arrowFunctionExpression = astNode.declarations[0].init.properties[0].value; 94 | 95 | assert(!isReturningJSX(() => false, arrowFunctionExpression, mockContext)); 96 | 97 | astNode = parseCode(`const obj = { 98 | prop: () => { return test(something); } 99 | }`); 100 | arrowFunctionExpression = astNode.declarations[0].init.properties[0].value; 101 | 102 | assert(!isReturningJSX(() => false, arrowFunctionExpression, mockContext)); 103 | }); 104 | }); 105 | }); 106 | -------------------------------------------------------------------------------- /tests/util/linkComponents.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const linkComponentsUtil = require('../../lib/util/linkComponents'); 5 | 6 | describe('linkComponentsFunctions', () => { 7 | describe('getLinkComponents', () => { 8 | it('returns a default map of components', () => { 9 | const context = {}; 10 | assert.deepStrictEqual(linkComponentsUtil.getLinkComponents(context), new Map([ 11 | ['a', ['href']], 12 | ])); 13 | }); 14 | 15 | it('returns a map of components', () => { 16 | const linkComponents = [ 17 | 'Hyperlink', 18 | { 19 | name: 'Link', 20 | linkAttribute: 'to', 21 | }, 22 | { 23 | name: 'Link2', 24 | linkAttribute: ['to1', 'to2'], 25 | }, 26 | ]; 27 | const context = { 28 | settings: { 29 | linkComponents, 30 | }, 31 | }; 32 | assert.deepStrictEqual(linkComponentsUtil.getLinkComponents(context), new Map([ 33 | ['a', ['href']], 34 | ['Hyperlink', ['href']], 35 | ['Link', ['to']], 36 | ['Link2', ['to1', 'to2']], 37 | ])); 38 | }); 39 | }); 40 | 41 | describe('getFormComponents', () => { 42 | it('returns a default map of components', () => { 43 | const context = {}; 44 | assert.deepStrictEqual(linkComponentsUtil.getFormComponents(context), new Map([ 45 | ['form', ['action']], 46 | ])); 47 | }); 48 | 49 | it('returns a map of components', () => { 50 | const formComponents = [ 51 | 'Form', 52 | { 53 | name: 'MyForm', 54 | formAttribute: 'endpoint', 55 | }, 56 | { 57 | name: 'MyForm2', 58 | formAttribute: ['endpoint1', 'endpoint2'], 59 | }, 60 | ]; 61 | const context = { 62 | settings: { 63 | formComponents, 64 | }, 65 | }; 66 | assert.deepStrictEqual(linkComponentsUtil.getFormComponents(context), new Map([ 67 | ['form', ['action']], 68 | ['Form', ['action']], 69 | ['MyForm', ['endpoint']], 70 | ['MyForm2', ['endpoint1', 'endpoint2']], 71 | ])); 72 | }); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /tests/util/pragma.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const SourceCode = require('eslint').SourceCode; 5 | const espree = require('espree'); 6 | 7 | const getFromContext = require('../../lib/util/pragma').getFromContext; 8 | 9 | const DEFAULT_CONFIG = { 10 | ecmaVersion: 6, 11 | comment: true, 12 | tokens: true, 13 | range: true, 14 | loc: true, 15 | }; 16 | 17 | const DEFAULT_SETTINGS = { 18 | react: { 19 | pragma: 'React', 20 | }, 21 | }; 22 | 23 | const fakeContext = (code) => { 24 | const ast = espree.parse(code, DEFAULT_CONFIG); 25 | return { 26 | getSourceCode: () => new SourceCode(code, ast), 27 | settings: DEFAULT_SETTINGS, 28 | }; 29 | }; 30 | 31 | describe('pragma', () => { 32 | describe('getFromContext', () => { 33 | it('finds the pragma in a block comment', () => { 34 | const code = '/* @jsx jsx */'; 35 | assert.strictEqual(getFromContext(fakeContext(code)), 'jsx'); 36 | }); 37 | 38 | it('finds the pragma in a docstring comment', () => { 39 | const code = '/** @jsx jsx */'; 40 | assert.strictEqual(getFromContext(fakeContext(code)), 'jsx'); 41 | }); 42 | 43 | it('finds the pragma in a line comment', () => { 44 | const code = '// @jsx jsx'; 45 | assert.strictEqual( 46 | getFromContext(fakeContext(code)), 47 | 'jsx' 48 | ); 49 | }); 50 | 51 | it('defaults to the value of settings.react.pragma', () => { 52 | const code = ''; 53 | assert.strictEqual( 54 | getFromContext(fakeContext(code)), 55 | DEFAULT_SETTINGS.react.pragma 56 | ); 57 | }); 58 | 59 | it('returns React if the pragma is invalid', () => { 60 | const code = '/* @jsx invalid-jsx-pragma */'; 61 | assert.equal(getFromContext(fakeContext(code)), 'React'); 62 | }); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /tests/util/variable.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | 5 | const getLatestVariableDefinition = require('../../lib/util/variable').getLatestVariableDefinition; 6 | 7 | describe('variable', () => { 8 | describe('getLatestVariableDefinition', () => { 9 | it('should return undefined for empty definitions', () => { 10 | const variable = { 11 | defs: [], 12 | }; 13 | assert.equal(getLatestVariableDefinition(variable), undefined); 14 | }); 15 | 16 | it('should return the latest definition', () => { 17 | const variable = { 18 | defs: [ 19 | 'one', 20 | 'two', 21 | 'latest', 22 | ], 23 | }; 24 | assert.equal(getLatestVariableDefinition(variable), 'latest'); 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 4 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 5 | // "lib": ["es2015"], /* Specify library files to be included in the compilation. */ 6 | "allowJs": true, /* Allow javascript files to be compiled. */ 7 | "checkJs": true, /* Report errors in .js files. */ 8 | "noEmit": true, /* Do not emit outputs. */ 9 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 10 | 11 | /* Strict Type-Checking Options */ 12 | // "strict": true, /* Enable all strict type-checking options. */ 13 | "noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */ 14 | // "strictNullChecks": true, /* Enable strict null checks. */ 15 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 16 | "strictFunctionTypes": true, /* Enable strict checking of function types. */ 17 | "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 18 | "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 19 | "alwaysStrict": false, /* Parse in strict mode and emit "use strict" for each source file. */ 20 | "resolveJsonModule": true 21 | }, 22 | "include": ["lib", "types"], 23 | } 24 | -------------------------------------------------------------------------------- /types/rules/jsx-no-literals.d.ts: -------------------------------------------------------------------------------- 1 | type RawElementConfig = { 2 | noStrings?: boolean; 3 | allowedStrings?: string[]; 4 | ignoreProps?: boolean; 5 | noAttributeStrings?: boolean; 6 | }; 7 | 8 | type RawOverrideConfig = { 9 | allowElement?: boolean; 10 | applyToNestedElements?: boolean; 11 | }; 12 | 13 | interface RawElementOverrides { 14 | elementOverrides?: Record; 15 | } 16 | 17 | export type RawConfig = RawElementConfig & RawElementOverrides; 18 | 19 | interface ElementConfigType { 20 | type: 'element'; 21 | } 22 | 23 | interface ElementConfigProperties { 24 | noStrings: boolean; 25 | allowedStrings: Set; 26 | ignoreProps: boolean; 27 | noAttributeStrings: boolean; 28 | } 29 | 30 | interface OverrideConfigProperties { 31 | type: 'override'; 32 | name: string; 33 | allowElement: boolean; 34 | applyToNestedElements: boolean; 35 | } 36 | 37 | export type ElementConfig = { 38 | type: 'element'; 39 | } & ElementConfigProperties; 40 | 41 | export type OverrideConfig = OverrideConfigProperties & ElementConfigProperties; 42 | 43 | interface ElementOverrides { 44 | elementOverrides: Record; 45 | } 46 | 47 | export type Config = ElementConfig & ElementOverrides; 48 | 49 | export type ResolvedConfig = Config | OverrideConfig; 50 | 51 | -------------------------------------------------------------------------------- /types/string.prototype.repeat/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'string.prototype.repeat' { 2 | function repeat(text: string, count: number): string; 3 | export = repeat; 4 | } 5 | --------------------------------------------------------------------------------