diff --git a/lib/rules/destructuring-assignment.js b/lib/rules/destructuring-assignment.js index dc3589ad77..343de7406c 100644 --- a/lib/rules/destructuring-assignment.js +++ b/lib/rules/destructuring-assignment.js @@ -39,6 +39,8 @@ module.exports = { const configuration = context.options[0] || DEFAULT_OPTION; const ignoreClassFields = (context.options[1] && (context.options[1].ignoreClassFields === true)) || false; + // set to save renamed var of useContext + const contextSet = new Set(); /** * @param {ASTNode} node We expect either an ArrowFunctionExpression, * FunctionDeclaration, or FunctionExpression @@ -61,14 +63,24 @@ module.exports = { } function handleSFCUsage(node) { - // props.aProp || context.aProp - const isPropUsed = (node.object.name === 'props' || node.object.name === 'context') && !isAssignmentLHS(node); + // props.aProp + const isPropUsed = node.object.name === 'props' && !isAssignmentLHS(node); if (isPropUsed && configuration === 'always') { context.report({ node, message: `Must use destructuring ${node.object.name} assignment` }); } + + // const foo = useContext(aContext); + // foo.aProp + const isContextUsed = contextSet.has(node.object.name) && !isAssignmentLHS(node); + if (isContextUsed && configuration === 'always') { + context.report({ + node, + message: `Must use destructuring ${node.object.name} assignment` + }); + } } function isInClassProperty(node) { @@ -125,13 +137,29 @@ module.exports = { const SFCComponent = components.get(context.getScope(node).block); const destructuring = (node.init && node.id && node.id.type === 'ObjectPattern'); + const identifier = (node.init && node.id && node.id.type === 'Identifier'); // let {foo} = props; - const destructuringSFC = destructuring && (node.init.name === 'props' || node.init.name === 'context'); + const destructuringSFC = destructuring && node.init.name === 'props'; + // let {foo} = useContext(aContext); + const destructuringUseContext = destructuring && node.init.callee && node.init.callee.name === 'useContext'; + // let foo = useContext(aContext); + const assignUseContext = identifier && node.init.callee && node.init.callee.name === 'useContext'; // let {foo} = this.props; const destructuringClass = destructuring && node.init.object && node.init.object.type === 'ThisExpression' && ( node.init.property.name === 'props' || node.init.property.name === 'context' || node.init.property.name === 'state' ); + if (SFCComponent && assignUseContext) { + contextSet.add(node.id.name); + } + + if (SFCComponent && destructuringUseContext && configuration === 'never') { + context.report({ + node, + message: `Must never use destructuring ${node.init.callee.name} assignment` + }); + } + if (SFCComponent && destructuringSFC && configuration === 'never') { context.report({ node, diff --git a/tests/lib/rules/destructuring-assignment.js b/tests/lib/rules/destructuring-assignment.js index 40d9e63ca3..dd05a43db4 100644 --- a/tests/lib/rules/destructuring-assignment.js +++ b/tests/lib/rules/destructuring-assignment.js @@ -176,6 +176,18 @@ ruleTester.run('destructuring-assignment', rule, { ].join('\n'), options: ['always', {ignoreClassFields: true}], parser: parsers.BABEL_ESLINT + }, { + code: `const MyComponent = (props) => { + const {foo} = useContext(aContext); + return
{foo}
+ };`, + options: ['always'] + }, { + code: `const MyComponent = (props) => { + const foo = useContext(aContext); + return
{foo.test}
+ };`, + options: ['never'] }], invalid: [{ @@ -314,5 +326,23 @@ ruleTester.run('destructuring-assignment', rule, { errors: [ {message: 'Must never use destructuring state assignment'} ] + }, { + code: `const MyComponent = (props) => { + const foo = useContext(aContext); + return
{foo.test}
+ };`, + options: ['always'], + errors: [ + {message: 'Must use destructuring foo assignment'} + ] + }, { + code: `const MyComponent = (props) => { + const {foo} = useContext(aContext); + return
{foo}
+ };`, + options: ['never'], + errors: [ + {message: 'Must never use destructuring useContext assignment'} + ] }] });