Skip to content

Commit f7792b3

Browse files
committed
Add jsx-no-undef rule
1 parent 0c24de4 commit f7792b3

File tree

5 files changed

+137
-2
lines changed

5 files changed

+137
-2
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Finally, enable all of the rules that you would like to use.
4343
{
4444
"rules": {
4545
"react/display-name": 1,
46+
"react/jsx-no-undef": 1,
4647
"react/jsx-uses-react": 1,
4748
"react/jsx-uses-vars": 1,
4849
"react/no-did-mount-set-state": 1,
@@ -59,6 +60,7 @@ Finally, enable all of the rules that you would like to use.
5960
# List of supported rules
6061

6162
* [display-name](docs/rules/display-name.md): Prevent missing displayName in a React component definition
63+
* [jsx-no-undef](docs/rules/jsx-no-undef.md): Disallow undeclared variables in JSX
6264
* [jsx-uses-react](docs/rules/jsx-uses-react.md): Prevent React to be incorrectly marked as unused
6365
* [jsx-uses-vars](docs/rules/jsx-uses-vars.md): Prevent variables used in JSX to be incorrectly marked as unused
6466
* [no-did-mount-set-state](docs/rules/no-did-mount-set-state.md): Prevent usage of setState in componentDidMount

docs/rules/jsx-no-undef.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Disallow undeclared variables in JSX (jsx-no-undef)
2+
3+
This rules can help you locate potential ReferenceErrors resulting from misspellings or missing components.
4+
5+
## Rule Details
6+
7+
The following patterns are considered warnings:
8+
9+
```js
10+
<Hello name="John" />;
11+
```
12+
13+
The following patterns are not considered warnings:
14+
15+
```js
16+
var Hello = require('./Hello');
17+
18+
<Hello name="John" />;
19+
```
20+
21+
## When Not To Use It
22+
23+
If you are not using JSX then you can disable this rule.

index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ module.exports = {
1111
'no-did-mount-set-state': require('./lib/rules/no-did-mount-set-state'),
1212
'no-did-update-set-state': require('./lib/rules/no-did-update-set-state'),
1313
'react-in-jsx-scope': require('./lib/rules/react-in-jsx-scope'),
14-
'jsx-uses-vars': require('./lib/rules/jsx-uses-vars')
14+
'jsx-uses-vars': require('./lib/rules/jsx-uses-vars'),
15+
'jsx-no-undef': require('./lib/rules/jsx-no-undef')
1516
},
1617
rulesConfig: {
1718
'jsx-uses-react': 0,
@@ -23,6 +24,7 @@ module.exports = {
2324
'no-did-mount-set-state': 0,
2425
'no-did-update-set-state': 0,
2526
'react-in-jsx-scope': 0,
26-
'jsx-uses-vars': 0
27+
'jsx-uses-vars': 0,
28+
'jsx-no-undef': 0
2729
}
2830
};

lib/rules/jsx-no-undef.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* @fileoverview Disallow undeclared variables in JSX
3+
* @author Yannick Croissant
4+
*/
5+
6+
'use strict';
7+
8+
/**
9+
* Checks if a node name match the JSX tag convention.
10+
* @param {String} name - Name of the node to check.
11+
* @returns {boolean} Whether or not the node name match the JSX tag convention.
12+
*/
13+
var tagConvention = /^[a-z]|\-/;
14+
function isTagName(name) {
15+
return tagConvention.test(name);
16+
}
17+
18+
// ------------------------------------------------------------------------------
19+
// Rule Definition
20+
// ------------------------------------------------------------------------------
21+
22+
module.exports = function(context) {
23+
24+
/**
25+
* Compare an identifier with the variables declared in the scope
26+
* @param {ASTNode} node - Identifier or JSXIdentifier node
27+
* @returns {void}
28+
*/
29+
function checkIdentifierInJSX(node) {
30+
var scope = context.getScope(),
31+
variables = scope.variables,
32+
i,
33+
len;
34+
35+
while (scope.type !== 'global') {
36+
scope = scope.upper;
37+
variables = scope.variables.concat(variables);
38+
}
39+
if (scope.childScopes.length) {
40+
variables = scope.childScopes[0].variables.concat(variables);
41+
}
42+
43+
for (i = 0, len = variables.length; i < len; i++) {
44+
if (variables[i].name === node.name) {
45+
return;
46+
}
47+
}
48+
49+
context.report(node, '\'' + node.name + '\' is not defined.');
50+
}
51+
52+
return {
53+
'JSXOpeningElement': function(node) {
54+
if (isTagName(node.name.name)) {
55+
return;
56+
}
57+
checkIdentifierInJSX(node.name);
58+
}
59+
};
60+
61+
};

tests/lib/rules/jsx-no-undef.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* @fileoverview Tests for jsx-no-undef
3+
* @author Yannick Croissant
4+
*/
5+
6+
'use strict';
7+
8+
// -----------------------------------------------------------------------------
9+
// Requirements
10+
// -----------------------------------------------------------------------------
11+
12+
var eslint = require('eslint').linter;
13+
var ESLintTester = require('eslint-tester');
14+
15+
// -----------------------------------------------------------------------------
16+
// Tests
17+
// -----------------------------------------------------------------------------
18+
19+
var eslintTester = new ESLintTester(eslint);
20+
eslint.defineRule('no-undef', require('eslint/lib/rules/no-undef'));
21+
eslintTester.addRuleTest('lib/rules/jsx-no-undef', {
22+
valid: [{
23+
code: '/*eslint no-undef:1*/ var React, App; React.render(<App />);',
24+
args: [1, {vars: 'all'}],
25+
ecmaFeatures: {modules: true, jsx: true}
26+
}, {
27+
code: '/*eslint no-undef:1*/ var React, App; React.render(<App />);',
28+
args: [1, {vars: 'all'}],
29+
ecmaFeatures: {jsx: true}
30+
}, {
31+
code: '/*eslint no-undef:1*/ var React; React.render(<img />);',
32+
args: [1, {vars: 'all'}],
33+
ecmaFeatures: {jsx: true}
34+
}, {
35+
code: '/*eslint no-undef:1*/ var React; React.render(<x-gif />);',
36+
args: [1, {vars: 'all'}],
37+
ecmaFeatures: {jsx: true}
38+
}],
39+
invalid: [{
40+
code: '/*eslint no-undef:1*/ var React; React.render(<App />);',
41+
args: [1, {vars: 'all'}],
42+
errors: [{
43+
message: '\'App\' is not defined.'
44+
}],
45+
ecmaFeatures: {jsx: true}
46+
}]
47+
});

0 commit comments

Comments
 (0)