-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Feat] no-render-return-undefined: initital commit
- Loading branch information
Showing
3 changed files
with
258 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/** | ||
* @fileoverview Prevent returning undefined from react components | ||
* @author Akul Srivastava | ||
*/ | ||
|
||
'use strict'; | ||
|
||
const astUtil = require('../util/ast'); | ||
const docsUrl = require('../util/docsUrl'); | ||
const report = require('../util/report'); | ||
const variableUtil = require('../util/variable'); | ||
|
||
const messages = { | ||
returnsUndefined: "Don't return undefined from react components", | ||
}; | ||
|
||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
meta: { | ||
docs: { | ||
description: 'Disallow returning undefined from react components', | ||
category: 'Best Practices', | ||
recommended: false, | ||
url: docsUrl('no-render-return-undefined'), | ||
}, | ||
messages, | ||
schema: [], | ||
}, | ||
|
||
create(context) { | ||
return { | ||
FunctionDeclaration(node) { | ||
const fnName = node.id.name; | ||
const isReactComponent = fnName[0] === fnName[0].toUpperCase(); | ||
const returnStatement = astUtil.findReturnStatement(node); | ||
|
||
if (!isReactComponent) return; | ||
|
||
const variables = variableUtil.variablesInScope(context); | ||
const returnNode = returnStatement && returnStatement.argument; | ||
const returnIdentifierName = returnNode && returnNode.name; | ||
const returnIdentifierVar = variableUtil.getVariable( | ||
variables, | ||
returnIdentifierName | ||
); | ||
const returnIdentifierValue = (() => { | ||
if (!returnNode) return undefined; | ||
if ( | ||
returnIdentifierVar | ||
&& returnIdentifierVar.defs | ||
&& returnIdentifierVar.defs[0] | ||
) { | ||
const value = returnIdentifierVar.defs[0].node.init; | ||
if ( | ||
returnIdentifierVar.defs[0].node.type === 'VariableDeclarator' | ||
&& value === null | ||
) { | ||
return undefined; | ||
} | ||
return value; | ||
} | ||
|
||
if (returnNode.type === 'ArrayExpression') { | ||
return returnNode.elements; | ||
} | ||
|
||
if (returnNode.type === 'JSXElement') { | ||
return returnNode; | ||
} | ||
|
||
return returnNode.value; | ||
})(); | ||
|
||
// console.log('DEBUG', returnIdentifierValue); | ||
|
||
const returnsArrayHavingUndefined = Array.isArray(returnIdentifierValue) | ||
&& returnIdentifierValue.some((el) => el.type === 'Identifier' && el.name === 'undefined'); | ||
|
||
if ( | ||
!returnStatement | ||
|| returnIdentifierName === 'undefined' | ||
|| returnIdentifierValue === undefined | ||
|| (returnIdentifierValue && returnIdentifierValue.name === 'undefined') | ||
|| returnsArrayHavingUndefined | ||
) { | ||
report(context, messages.returnsUndefined, 'returnsUndefined', { | ||
node, | ||
}); | ||
} | ||
}, | ||
// FunctionExpression(node) { | ||
// // console.log('DEBUG fn expression', node); | ||
// }, | ||
// ArrowFunctionExpression(node) { | ||
// // console.log('DEBUG fn arrow function', node); | ||
// }, | ||
}; | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
/** | ||
* @fileoverview Tests for no-danger | ||
* @author Scott Andrews | ||
*/ | ||
|
||
'use strict'; | ||
|
||
// ----------------------------------------------------------------------------- | ||
// Requirements | ||
// ----------------------------------------------------------------------------- | ||
|
||
const RuleTester = require('eslint').RuleTester; | ||
const rule = require('../../../lib/rules/no-render-return-undefined'); | ||
|
||
const parsers = require('../../helpers/parsers'); | ||
|
||
const parserOptions = { | ||
ecmaVersion: 2018, | ||
sourceType: 'module', | ||
ecmaFeatures: { | ||
jsx: true, | ||
}, | ||
}; | ||
|
||
// ----------------------------------------------------------------------------- | ||
// Tests | ||
// ----------------------------------------------------------------------------- | ||
|
||
const ruleTester = new RuleTester({ parserOptions }); | ||
ruleTester.run('no-render-return-undefined', rule, { | ||
valid: parsers.all([ | ||
{ | ||
code: ` | ||
function App() { | ||
return 123; | ||
} | ||
`, | ||
}, | ||
{ | ||
code: ` | ||
function App() { | ||
return 'Hello World'; | ||
} | ||
`, | ||
}, | ||
{ | ||
code: ` | ||
function App() { | ||
return null; | ||
} | ||
`, | ||
}, | ||
{ | ||
code: ` | ||
function App() { | ||
return []; | ||
} | ||
`, | ||
}, | ||
{ | ||
code: ` | ||
function App() { | ||
return <div />; | ||
} | ||
`, | ||
}, | ||
{ | ||
code: ` | ||
function App() { | ||
return <div></div>; | ||
} | ||
`, | ||
}, | ||
{ | ||
code: ` | ||
function App() { | ||
return <div>Hello World</div>; | ||
} | ||
`, | ||
}, | ||
{ | ||
code: ` | ||
function App() { | ||
return <Component />; | ||
} | ||
`, | ||
}, | ||
]), | ||
invalid: parsers.all([ | ||
{ | ||
code: ` | ||
function App() {} | ||
`, | ||
errors: [{ messageId: 'returnsUndefined' }], | ||
}, | ||
{ | ||
code: ` | ||
function App() { | ||
return undefined; | ||
} | ||
`, | ||
errors: [{ messageId: 'returnsUndefined' }], | ||
}, | ||
{ | ||
code: ` | ||
function App() { | ||
const toReturn = undefined; | ||
return toReturn; | ||
} | ||
`, | ||
errors: [{ messageId: 'returnsUndefined' }], | ||
}, | ||
{ | ||
code: ` | ||
function App() { | ||
var toReturn; | ||
return toReturn; | ||
} | ||
`, | ||
errors: [{ messageId: 'returnsUndefined' }], | ||
}, | ||
{ | ||
code: ` | ||
function App() { | ||
let toReturn; | ||
return toReturn; | ||
} | ||
`, | ||
errors: [{ messageId: 'returnsUndefined' }], | ||
}, | ||
{ | ||
code: ` | ||
var foo; | ||
function App() { | ||
return foo; | ||
} | ||
`, | ||
errors: [{ messageId: 'returnsUndefined' }], | ||
}, | ||
{ | ||
code: ` | ||
let foo; | ||
function App() { | ||
return foo; | ||
} | ||
`, | ||
errors: [{ messageId: 'returnsUndefined' }], | ||
}, | ||
{ | ||
code: ` | ||
function App() { | ||
return [undefined]; | ||
} | ||
`, | ||
errors: [{ messageId: 'returnsUndefined' }], | ||
}, | ||
]), | ||
}); |