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 |
--------------------------------------------------------------------------------