Skip to content

Commit 499e261

Browse files
committed
[Feat] no-render-return-undefined: initital commit
1 parent 03cd4b5 commit 499e261

File tree

3 files changed

+258
-1
lines changed

3 files changed

+258
-1
lines changed
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/**
2+
* @fileoverview Prevent returning undefined from react components
3+
* @author Akul Srivastava
4+
*/
5+
6+
'use strict';
7+
8+
const astUtil = require('../util/ast');
9+
const docsUrl = require('../util/docsUrl');
10+
const report = require('../util/report');
11+
const variableUtil = require('../util/variable');
12+
13+
const messages = {
14+
returnsUndefined: "Don't return undefined from react components",
15+
};
16+
17+
/** @type {import('eslint').Rule.RuleModule} */
18+
module.exports = {
19+
meta: {
20+
docs: {
21+
description: 'Disallow returning undefined from react components',
22+
category: 'Best Practices',
23+
recommended: false,
24+
url: docsUrl('no-render-return-undefined'),
25+
},
26+
messages,
27+
schema: [],
28+
},
29+
30+
create(context) {
31+
return {
32+
FunctionDeclaration(node) {
33+
const fnName = node.id.name;
34+
const isReactComponent = fnName[0] === fnName[0].toUpperCase();
35+
const returnStatement = astUtil.findReturnStatement(node);
36+
37+
if (!isReactComponent) return;
38+
39+
const variables = variableUtil.variablesInScope(context);
40+
const returnNode = returnStatement && returnStatement.argument;
41+
const returnIdentifierName = returnNode && returnNode.name;
42+
const returnIdentifierVar = variableUtil.getVariable(
43+
variables,
44+
returnIdentifierName
45+
);
46+
const returnIdentifierValue = (() => {
47+
if (!returnNode) return undefined;
48+
if (
49+
returnIdentifierVar
50+
&& returnIdentifierVar.defs
51+
&& returnIdentifierVar.defs[0]
52+
) {
53+
const value = returnIdentifierVar.defs[0].node.init;
54+
if (
55+
returnIdentifierVar.defs[0].node.type === 'VariableDeclarator'
56+
&& value === null
57+
) {
58+
return undefined;
59+
}
60+
return value;
61+
}
62+
63+
if (returnNode.type === 'ArrayExpression') {
64+
return returnNode.elements;
65+
}
66+
67+
if (returnNode.type === 'JSXElement') {
68+
return returnNode;
69+
}
70+
71+
return returnNode.value;
72+
})();
73+
74+
// console.log('DEBUG', returnIdentifierValue);
75+
76+
const returnsArrayHavingUndefined = Array.isArray(returnIdentifierValue)
77+
&& returnIdentifierValue.some((el) => el.type === 'Identifier' && el.name === 'undefined');
78+
79+
if (
80+
!returnStatement
81+
|| returnIdentifierName === 'undefined'
82+
|| returnIdentifierValue === undefined
83+
|| (returnIdentifierValue && returnIdentifierValue.name === 'undefined')
84+
|| returnsArrayHavingUndefined
85+
) {
86+
report(context, messages.returnsUndefined, 'returnsUndefined', {
87+
node,
88+
});
89+
}
90+
},
91+
// FunctionExpression(node) {
92+
// // console.log('DEBUG fn expression', node);
93+
// },
94+
// ArrowFunctionExpression(node) {
95+
// // console.log('DEBUG fn arrow function', node);
96+
// },
97+
};
98+
},
99+
};

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"test": "npm run unit-test",
1616
"posttest": "aud --production",
1717
"type-check": "tsc",
18-
"unit-test": "istanbul cover node_modules/mocha/bin/_mocha tests/lib/**/*.js tests/util/**/*.js tests/index.js",
18+
"unit-test": "istanbul cover node_modules/mocha/bin/_mocha tests/lib/rules/no-render-return-undefined.js",
1919
"update:eslint-docs": "eslint-doc-generator"
2020
},
2121
"repository": {
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/**
2+
* @fileoverview Tests for no-danger
3+
* @author Scott Andrews
4+
*/
5+
6+
'use strict';
7+
8+
// -----------------------------------------------------------------------------
9+
// Requirements
10+
// -----------------------------------------------------------------------------
11+
12+
const RuleTester = require('eslint').RuleTester;
13+
const rule = require('../../../lib/rules/no-render-return-undefined');
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+
ruleTester.run('no-render-return-undefined', rule, {
31+
valid: parsers.all([
32+
{
33+
code: `
34+
function App() {
35+
return 123;
36+
}
37+
`,
38+
},
39+
{
40+
code: `
41+
function App() {
42+
return 'Hello World';
43+
}
44+
`,
45+
},
46+
{
47+
code: `
48+
function App() {
49+
return null;
50+
}
51+
`,
52+
},
53+
{
54+
code: `
55+
function App() {
56+
return [];
57+
}
58+
`,
59+
},
60+
{
61+
code: `
62+
function App() {
63+
return <div />;
64+
}
65+
`,
66+
},
67+
{
68+
code: `
69+
function App() {
70+
return <div></div>;
71+
}
72+
`,
73+
},
74+
{
75+
code: `
76+
function App() {
77+
return <div>Hello World</div>;
78+
}
79+
`,
80+
},
81+
{
82+
code: `
83+
function App() {
84+
return <Component />;
85+
}
86+
`,
87+
},
88+
]),
89+
invalid: parsers.all([
90+
{
91+
code: `
92+
function App() {}
93+
`,
94+
errors: [{ messageId: 'returnsUndefined' }],
95+
},
96+
{
97+
code: `
98+
function App() {
99+
return undefined;
100+
}
101+
`,
102+
errors: [{ messageId: 'returnsUndefined' }],
103+
},
104+
{
105+
code: `
106+
function App() {
107+
const toReturn = undefined;
108+
return toReturn;
109+
}
110+
`,
111+
errors: [{ messageId: 'returnsUndefined' }],
112+
},
113+
{
114+
code: `
115+
function App() {
116+
var toReturn;
117+
return toReturn;
118+
}
119+
`,
120+
errors: [{ messageId: 'returnsUndefined' }],
121+
},
122+
{
123+
code: `
124+
function App() {
125+
let toReturn;
126+
return toReturn;
127+
}
128+
`,
129+
errors: [{ messageId: 'returnsUndefined' }],
130+
},
131+
{
132+
code: `
133+
var foo;
134+
function App() {
135+
return foo;
136+
}
137+
`,
138+
errors: [{ messageId: 'returnsUndefined' }],
139+
},
140+
{
141+
code: `
142+
let foo;
143+
function App() {
144+
return foo;
145+
}
146+
`,
147+
errors: [{ messageId: 'returnsUndefined' }],
148+
},
149+
{
150+
code: `
151+
function App() {
152+
return [undefined];
153+
}
154+
`,
155+
errors: [{ messageId: 'returnsUndefined' }],
156+
},
157+
]),
158+
});

0 commit comments

Comments
 (0)