Skip to content

Commit

Permalink
[Feat] no-render-return-undefined: initital commit
Browse files Browse the repository at this point in the history
  • Loading branch information
akulsr0 committed May 7, 2024
1 parent 03cd4b5 commit 499e261
Show file tree
Hide file tree
Showing 3 changed files with 258 additions and 1 deletion.
99 changes: 99 additions & 0 deletions lib/rules/no-render-return-undefined.js
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);
// },
};
},
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"test": "npm run unit-test",
"posttest": "aud --production",
"type-check": "tsc",
"unit-test": "istanbul cover node_modules/mocha/bin/_mocha tests/lib/**/*.js tests/util/**/*.js tests/index.js",
"unit-test": "istanbul cover node_modules/mocha/bin/_mocha tests/lib/rules/no-render-return-undefined.js",
"update:eslint-docs": "eslint-doc-generator"
},
"repository": {
Expand Down
158 changes: 158 additions & 0 deletions tests/lib/rules/no-render-return-undefined.js
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' }],
},
]),
});

0 comments on commit 499e261

Please sign in to comment.