diff --git a/.eslintrc b/.eslintrc index 4991f200f2..d10247d296 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,82 +1,82 @@ { - "root": true, - "extends": ["airbnb-base", "plugin:eslint-plugin/recommended"], - "plugins": ["eslint-plugin"], - "env": { - "es6": true, - "node": true - }, - "parserOptions": { - "ecmaVersion": 6, - "ecmaFeatures": { - "jsx": true - }, - "sourceType": "script", - }, - "ignorePatterns": [ - "coverage/", - ".nyc_output/", - ], - "rules": { - "comma-dangle": [2, "always-multiline"], - "object-shorthand": [2, "always", { - "ignoreConstructors": false, - "avoidQuotes": false, // this is the override vs airbnb - }], - "max-len": [2, 120, { - "ignoreStrings": true, - "ignoreTemplateLiterals": true, - "ignoreComments": true, - }], - "consistent-return": 0, + "root": true, + "extends": ["airbnb-base", "plugin:eslint-plugin/recommended"], + "plugins": ["eslint-plugin"], + "env": { + "es6": true, + "node": true + }, + "parserOptions": { + "ecmaVersion": 6, + "ecmaFeatures": { + "jsx": true + }, + "sourceType": "script", + }, + "ignorePatterns": [ + "coverage/", + ".nyc_output/", + ], + "rules": { + "comma-dangle": [2, "always-multiline"], + "object-shorthand": [2, "always", { + "ignoreConstructors": false, + "avoidQuotes": false, // this is the override vs airbnb + }], + "max-len": [2, 120, { + "ignoreStrings": true, + "ignoreTemplateLiterals": true, + "ignoreComments": true, + }], + "consistent-return": 0, - "prefer-destructuring": [2, { "array": false, "object": false }, { "enforceForRenamedProperties": false }], - "prefer-object-spread": 0, // until node 8 is required - "prefer-rest-params": 0, // until node 6 is required - "prefer-spread": 0, // until node 6 is required - "function-call-argument-newline": 1, // TODO: enable - "function-paren-newline": 0, - "no-plusplus": [2, {"allowForLoopAfterthoughts": true}], - "no-param-reassign": 1, - "no-restricted-syntax": [2, { - "selector": "ObjectPattern", - "message": "Object destructuring is not compatible with Node v4" - }], - "strict": [2, "safe"], - "valid-jsdoc": [2, { - "requireReturn": false, - "requireParamDescription": false, - "requireReturnDescription": false, - }], + "prefer-destructuring": [2, { "array": false, "object": false }, { "enforceForRenamedProperties": false }], + "prefer-object-spread": 0, // until node 8 is required + "prefer-rest-params": 0, // until node 6 is required + "prefer-spread": 0, // until node 6 is required + "function-call-argument-newline": 1, // TODO: enable + "function-paren-newline": 0, + "no-plusplus": [2, {"allowForLoopAfterthoughts": true}], + "no-param-reassign": 1, + "no-restricted-syntax": [2, { + "selector": "ObjectPattern", + "message": "Object destructuring is not compatible with Node v4" + }], + "strict": [2, "safe"], + "valid-jsdoc": [2, { + "requireReturn": false, + "requireParamDescription": false, + "requireReturnDescription": false, + }], - "eslint-plugin/consistent-output": 0, - "eslint-plugin/require-meta-docs-description": [2, { "pattern": "^(Enforce|Require|Disallow)" }], - "eslint-plugin/require-meta-schema": 0, - "eslint-plugin/require-meta-type": 0 - }, - "overrides": [ - { - "files": "tests/**", - "rules": { - "no-template-curly-in-string": 1, - }, - }, - { - "files": "markdown.config.js", - "rules": { - "no-console": 0, - }, - }, - { - "files": ".github/workflows/*.js", - "parserOptions": { - "ecmaVersion": 2019, - }, - "rules": { - "camelcase": 0, - "no-console": 0, - "no-restricted-syntax": 0, - }, - }, - ], -} + "eslint-plugin/consistent-output": 0, + "eslint-plugin/require-meta-docs-description": [2, { "pattern": "^(Enforce|Require|Disallow)" }], + "eslint-plugin/require-meta-schema": 0, + "eslint-plugin/require-meta-type": 0 + }, + "overrides": [ + { + "files": "tests/**", + "rules": { + "no-template-curly-in-string": 1, + }, + }, + { + "files": "markdown.config.js", + "rules": { + "no-console": 0, + }, + }, + { + "files": ".github/workflows/*.js", + "parserOptions": { + "ecmaVersion": 2019, + }, + "rules": { + "camelcase": 0, + "no-console": 0, + "no-restricted-syntax": 0, + }, + }, + ], + } diff --git a/CHANGELOG.md b/CHANGELOG.md index a358c59def..a2c437523f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,12 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ### Fixed * [`boolean-prop-naming`]: avoid a crash with a non-TSTypeReference type ([#3718][] @developer-bandi) +* [`jsx-no-leaked-render`]: invalid report if left side is boolean ([#3746][] @akulsr0) +* [`jsx-closing-bracket-location`]: message shows `{{details}}` when there are no details ([#3759][] @mdjermanovic) +* [`no-invalid-html-attribute`]: ensure error messages are correct ([#3759][] @mdjermanovic, @ljharb) +[#3759]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3759 +[#3746]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3746 [#3718]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3718 ## [7.34.1] - 2024.03.15 diff --git a/lib/rules/boolean-prop-naming.js b/lib/rules/boolean-prop-naming.js index 173b154354..f64da93242 100644 --- a/lib/rules/boolean-prop-naming.js +++ b/lib/rules/boolean-prop-naming.js @@ -13,6 +13,10 @@ const propsUtil = require('../util/props'); const docsUrl = require('../util/docsUrl'); const propWrapperUtil = require('../util/propWrapper'); const report = require('../util/report'); +const eslintUtil = require('../util/eslint'); + +const getSourceCode = eslintUtil.getSourceCode; +const getText = eslintUtil.getText; // ------------------------------------------------------------------------------ // Rule Definition @@ -115,7 +119,7 @@ module.exports = { // we can't get the name of the Flow object key name. So we have // to hack around it for now. if (node.type === 'ObjectTypeProperty') { - return context.getSourceCode().getFirstToken(node).value; + return getSourceCode(context).getFirstToken(node).value; } return node.key.name; @@ -308,7 +312,7 @@ module.exports = { && node.value.type === 'CallExpression' && propWrapperUtil.isPropWrapperFunction( context, - context.getSourceCode().getText(node.value.callee) + getText(context, node.value.callee) ) ) { checkPropWrapperArguments(node, node.value.arguments); @@ -334,7 +338,7 @@ module.exports = { right.type === 'CallExpression' && propWrapperUtil.isPropWrapperFunction( context, - context.getSourceCode().getText(right.callee) + getText(context, right.callee) ) ) { checkPropWrapperArguments(component.node, right.arguments); diff --git a/lib/rules/button-has-type.js b/lib/rules/button-has-type.js index 204a33c43e..d40596067d 100644 --- a/lib/rules/button-has-type.js +++ b/lib/rules/button-has-type.js @@ -135,7 +135,7 @@ module.exports = { checkValue(node, propValue); }, CallExpression(node) { - if (!isCreateElement(node, context) || node.arguments.length < 1) { + if (!isCreateElement(context, node) || node.arguments.length < 1) { return; } diff --git a/lib/rules/checked-requires-onchange-or-readonly.js b/lib/rules/checked-requires-onchange-or-readonly.js index 420611fee5..c719644e1c 100644 --- a/lib/rules/checked-requires-onchange-or-readonly.js +++ b/lib/rules/checked-requires-onchange-or-readonly.js @@ -115,7 +115,7 @@ module.exports = { checkAttributesAndReport(node, propSet); }, CallExpression(node) { - if (!isCreateElement(node, context)) { + if (!isCreateElement(context, node)) { return; } diff --git a/lib/rules/destructuring-assignment.js b/lib/rules/destructuring-assignment.js index e1a98d30a5..b4aa3da159 100644 --- a/lib/rules/destructuring-assignment.js +++ b/lib/rules/destructuring-assignment.js @@ -6,9 +6,13 @@ const Components = require('../util/Components'); const docsUrl = require('../util/docsUrl'); +const eslintUtil = require('../util/eslint'); const isAssignmentLHS = require('../util/ast').isAssignmentLHS; const report = require('../util/report'); +const getScope = eslintUtil.getScope; +const getText = eslintUtil.getText; + const DEFAULT_OPTION = 'always'; function createSFCParams() { @@ -102,7 +106,7 @@ module.exports = { function handleStatelessComponent(node) { const params = evalParams(node.params); - const SFCComponent = components.get(context.getScope(node).block); + const SFCComponent = components.get(getScope(context, node).block); if (!SFCComponent) { return; } @@ -120,7 +124,7 @@ module.exports = { } function handleStatelessComponentExit(node) { - const SFCComponent = components.get(context.getScope(node).block); + const SFCComponent = components.get(getScope(context, node).block); if (SFCComponent) { sfcParams.pop(); } @@ -192,7 +196,7 @@ module.exports = { 'FunctionExpression:exit': handleStatelessComponentExit, MemberExpression(node) { - let scope = context.getScope(node); + let scope = getScope(context, node); let SFCComponent = components.get(scope.block); while (!SFCComponent && scope.upper && scope.upper !== scope) { SFCComponent = components.get(scope.upper.block); @@ -210,7 +214,7 @@ module.exports = { VariableDeclarator(node) { const classComponent = utils.getParentComponent(node); - const SFCComponent = components.get(context.getScope(node).block); + const SFCComponent = components.get(getScope(context, node).block); const destructuring = (node.init && node.id && node.id.type === 'ObjectPattern'); // let {foo} = props; @@ -248,7 +252,7 @@ module.exports = { && destructureInSignature === 'always' && node.init.name === 'props' ) { - const scopeSetProps = context.getScope().set.get('props'); + const scopeSetProps = getScope(context, node).set.get('props'); const propsRefs = scopeSetProps && scopeSetProps.references; if (!propsRefs) { return; @@ -269,7 +273,7 @@ module.exports = { param.typeAnnotation ? param.typeAnnotation.range[0] : param.range[1], ]; return [ - fixer.replaceTextRange(replaceRange, context.getSourceCode().getText(node.id)), + fixer.replaceTextRange(replaceRange, getText(context, node.id)), fixer.remove(node.parent), ]; }, diff --git a/lib/rules/forbid-elements.js b/lib/rules/forbid-elements.js index c7f978a164..dd3ffa8f5d 100644 --- a/lib/rules/forbid-elements.js +++ b/lib/rules/forbid-elements.js @@ -7,6 +7,7 @@ const has = require('object.hasown/polyfill')(); const docsUrl = require('../util/docsUrl'); +const getText = require('../util/eslint').getText; const isCreateElement = require('../util/isCreateElement'); const report = require('../util/report'); @@ -90,11 +91,11 @@ module.exports = { return { JSXOpeningElement(node) { - reportIfForbidden(context.getSourceCode().getText(node.name), node.name); + reportIfForbidden(getText(context, node.name), node.name); }, CallExpression(node) { - if (!isCreateElement(node, context)) { + if (!isCreateElement(context, node)) { return; } @@ -110,7 +111,7 @@ module.exports = { } else if (argType === 'Literal' && /^[a-z][^.]*$/.test(argument.value)) { reportIfForbidden(argument.value, argument); } else if (argType === 'MemberExpression') { - reportIfForbidden(context.getSourceCode().getText(argument), argument); + reportIfForbidden(getText(context, argument), argument); } }, }; diff --git a/lib/rules/forbid-prop-types.js b/lib/rules/forbid-prop-types.js index 92f0f0f51f..b561b26da1 100644 --- a/lib/rules/forbid-prop-types.js +++ b/lib/rules/forbid-prop-types.js @@ -10,6 +10,7 @@ const astUtil = require('../util/ast'); const docsUrl = require('../util/docsUrl'); const propWrapperUtil = require('../util/propWrapper'); const report = require('../util/report'); +const getText = require('../util/eslint').getText; // ------------------------------------------------------------------------------ // Constants @@ -162,7 +163,7 @@ module.exports = { checkProperties(node.properties); break; case 'Identifier': { - const propTypesObject = variableUtil.findVariableByName(context, node.name); + const propTypesObject = variableUtil.findVariableByName(context, node, node.name); if (propTypesObject && propTypesObject.properties) { checkProperties(propTypesObject.properties); } @@ -171,7 +172,7 @@ module.exports = { case 'CallExpression': { const innerNode = node.arguments && node.arguments[0]; if ( - propWrapperUtil.isPropWrapperFunction(context, context.getSourceCode().getText(node.callee)) + propWrapperUtil.isPropWrapperFunction(context, getText(context, node.callee)) && innerNode ) { checkNode(innerNode); diff --git a/lib/rules/function-component-definition.js b/lib/rules/function-component-definition.js index 43e3154873..24ad3f48d7 100644 --- a/lib/rules/function-component-definition.js +++ b/lib/rules/function-component-definition.js @@ -9,6 +9,7 @@ const arrayIncludes = require('array-includes'); const Components = require('../util/Components'); const docsUrl = require('../util/docsUrl'); const reportC = require('../util/report'); +const getText = require('../util/eslint').getText; // ------------------------------------------------------------------------------ // Rule Definition @@ -181,8 +182,7 @@ module.exports = { ); function getFixer(node, options) { - const sourceCode = context.getSourceCode(); - const source = sourceCode.getText(); + const source = getText(context); const typeAnnotation = getTypeAnnotation(node, source); diff --git a/lib/rules/hook-use-state.js b/lib/rules/hook-use-state.js index a9deed4fd6..938802d82b 100644 --- a/lib/rules/hook-use-state.js +++ b/lib/rules/hook-use-state.js @@ -9,6 +9,7 @@ const Components = require('../util/Components'); const docsUrl = require('../util/docsUrl'); const report = require('../util/report'); const getMessageData = require('../util/message'); +const getText = require('../util/eslint').getText; // ------------------------------------------------------------------------------ // Rule Definition @@ -160,14 +161,14 @@ module.exports = { fix: (fixer) => [ // Add useMemo import, if necessary useStateReactImportSpecifier - && (!useMemoReactImportSpecifier || defaultReactImportName) - && fixer.insertTextAfter(useStateReactImportSpecifier, ', useMemo'), + && (!useMemoReactImportSpecifier || defaultReactImportName) + && fixer.insertTextAfter(useStateReactImportSpecifier, ', useMemo'), // Convert single-value destructure to simple assignment fixer.replaceTextRange(node.parent.id.range, valueVariableName), // Convert useState call to useMemo + arrow function + dependency array fixer.replaceTextRange( node.range, - `${useMemoCode}(() => ${context.getSourceCode().getText(node.arguments[0])}, [])` + `${useMemoCode}(() => ${getText(context, node.arguments[0])}, [])` ), ].filter(Boolean), } diff --git a/lib/rules/iframe-missing-sandbox.js b/lib/rules/iframe-missing-sandbox.js index 810e30b52b..7d8f234d97 100644 --- a/lib/rules/iframe-missing-sandbox.js +++ b/lib/rules/iframe-missing-sandbox.js @@ -131,7 +131,7 @@ module.exports = { }, CallExpression(node) { - if (isCreateElement(node, context) && node.arguments && node.arguments.length > 0) { + if (isCreateElement(context, node) && node.arguments && node.arguments.length > 0) { const tag = node.arguments[0]; if (tag.type === 'Literal' && tag.value === 'iframe') { checkProps(context, node); diff --git a/lib/rules/jsx-closing-bracket-location.js b/lib/rules/jsx-closing-bracket-location.js index 4816fd0068..26556d6117 100644 --- a/lib/rules/jsx-closing-bracket-location.js +++ b/lib/rules/jsx-closing-bracket-location.js @@ -7,6 +7,7 @@ const has = require('object.hasown/polyfill')(); const docsUrl = require('../util/docsUrl'); +const getSourceCode = require('../util/eslint').getSourceCode; const report = require('../util/report'); // ------------------------------------------------------------------------------ @@ -170,11 +171,11 @@ module.exports = { let spaces = []; switch (expectedLocation) { case 'props-aligned': - indentation = /^\s*/.exec(context.getSourceCode().lines[tokens.lastProp.firstLine - 1])[0]; + indentation = /^\s*/.exec(getSourceCode(context).lines[tokens.lastProp.firstLine - 1])[0]; break; case 'tag-aligned': case 'line-aligned': - indentation = /^\s*/.exec(context.getSourceCode().lines[tokens.opening.line - 1])[0]; + indentation = /^\s*/.exec(getSourceCode(context).lines[tokens.opening.line - 1])[0]; break; default: indentation = ''; @@ -194,7 +195,7 @@ module.exports = { * prop and start of opening line. */ function getTokensLocations(node) { - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); const opening = sourceCode.getFirstToken(node).loc.start; const closing = sourceCode.getLastTokens(node, node.selfClosing ? 2 : 1)[0].loc.start; const tag = sourceCode.getFirstToken(node.name).loc.start; @@ -266,7 +267,10 @@ module.exports = { return; } - const data = { location: MESSAGE_LOCATION[expectedLocation] }; + const data = { + location: MESSAGE_LOCATION[expectedLocation], + details: '', + }; const correctColumn = getCorrectColumn(tokens, expectedLocation); if (correctColumn !== null) { diff --git a/lib/rules/jsx-curly-brace-presence.js b/lib/rules/jsx-curly-brace-presence.js index 3eac3b47ea..083a60a0e3 100755 --- a/lib/rules/jsx-curly-brace-presence.js +++ b/lib/rules/jsx-curly-brace-presence.js @@ -11,6 +11,10 @@ const arrayIncludes = require('array-includes'); const docsUrl = require('../util/docsUrl'); const jsxUtil = require('../util/jsx'); const report = require('../util/report'); +const eslintUtil = require('../util/eslint'); + +const getSourceCode = eslintUtil.getSourceCode; +const getText = eslintUtil.getText; // ------------------------------------------------------------------------------ // Constants @@ -176,8 +180,7 @@ module.exports = { let textToReplace; if (jsxUtil.isJSX(expression)) { - const sourceCode = context.getSourceCode(); - textToReplace = sourceCode.getText(expression); + textToReplace = getText(context, expression); } else { const expressionType = expression && expression.type; const parentType = JSXExpressionNode.parent.type; @@ -188,9 +191,7 @@ module.exports = { : expression.raw.slice(1, -1) }"`; } else if (jsxUtil.isJSX(expression)) { - const sourceCode = context.getSourceCode(); - - textToReplace = sourceCode.getText(expression); + textToReplace = getText(context, expression); } else { textToReplace = expressionType === 'TemplateLiteral' ? expression.quasis[0].value.cooked : expression.value; @@ -207,7 +208,7 @@ module.exports = { node: literalNode, fix(fixer) { if (jsxUtil.isJSX(literalNode)) { - return fixer.replaceText(literalNode, `{${context.getSourceCode().getText(literalNode)}}`); + return fixer.replaceText(literalNode, `{${getText(context, literalNode)}}`); } // If a HTML entity name is found, bail out because it can be fixed @@ -251,7 +252,7 @@ module.exports = { const expression = JSXExpressionNode.expression; const expressionType = expression.type; - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); // Curly braces containing comments are necessary if (sourceCode.getCommentsInside && sourceCode.getCommentsInside(JSXExpressionNode).length > 0) { return; diff --git a/lib/rules/jsx-curly-newline.js b/lib/rules/jsx-curly-newline.js index 068a7103da..046ff79582 100644 --- a/lib/rules/jsx-curly-newline.js +++ b/lib/rules/jsx-curly-newline.js @@ -5,8 +5,12 @@ 'use strict'; const docsUrl = require('../util/docsUrl'); +const eslintUtil = require('../util/eslint'); const report = require('../util/report'); +const getSourceCode = eslintUtil.getSourceCode; +const getText = eslintUtil.getText; + // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ @@ -77,7 +81,7 @@ module.exports = { }, create(context) { - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); const option = getNormalizedOption(context); // ---------------------------------------------------------------------- @@ -130,8 +134,7 @@ module.exports = { report(context, messages.unexpectedAfter, 'unexpectedAfter', { node: leftCurly, fix(fixer) { - return sourceCode - .getText() + return getText(context) .slice(leftCurly.range[1], tokenAfterLeftCurly.range[0]) .trim() ? null // If there is a comment between the { and the first element, don't do a fix. @@ -149,8 +152,7 @@ module.exports = { report(context, messages.unexpectedBefore, 'unexpectedBefore', { node: rightCurly, fix(fixer) { - return sourceCode - .getText() + return getText(context) .slice(tokenBeforeRightCurly.range[1], rightCurly.range[0]) .trim() ? null // If there is a comment between the last element and the }, don't do a fix. diff --git a/lib/rules/jsx-curly-spacing.js b/lib/rules/jsx-curly-spacing.js index 094e612c7d..8d504f1d20 100644 --- a/lib/rules/jsx-curly-spacing.js +++ b/lib/rules/jsx-curly-spacing.js @@ -13,6 +13,7 @@ const has = require('object.hasown/polyfill')(); const docsUrl = require('../util/docsUrl'); +const getSourceCode = require('../util/eslint').getSourceCode; const report = require('../util/report'); // ------------------------------------------------------------------------------ @@ -175,7 +176,7 @@ module.exports = { * @returns {Object|*|{range, text}} */ function fixByTrimmingWhitespace(fixer, fromLoc, toLoc, mode, spacing) { - let replacementText = context.getSourceCode().text.slice(fromLoc, toLoc); + let replacementText = getSourceCode(context).text.slice(fromLoc, toLoc); if (mode === 'start') { replacementText = replacementText.replace(/^\s+/gm, ''); } else { @@ -206,7 +207,7 @@ module.exports = { token: token.value, }, fix(fixer) { - const nextToken = context.getSourceCode().getTokenAfter(token); + const nextToken = getSourceCode(context).getTokenAfter(token); return fixByTrimmingWhitespace(fixer, token.range[1], nextToken.range[0], 'start', spacing); }, }); @@ -227,7 +228,7 @@ module.exports = { token: token.value, }, fix(fixer) { - const previousToken = context.getSourceCode().getTokenBefore(token); + const previousToken = getSourceCode(context).getTokenBefore(token); return fixByTrimmingWhitespace(fixer, previousToken.range[1], token.range[0], 'end', spacing); }, }); @@ -247,7 +248,7 @@ module.exports = { token: token.value, }, fix(fixer) { - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); const nextToken = sourceCode.getTokenAfter(token); let nextComment; @@ -284,7 +285,7 @@ module.exports = { token: token.value, }, fix(fixer) { - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); const previousToken = sourceCode.getTokenBefore(token); let previousComment; @@ -370,7 +371,7 @@ module.exports = { return; } - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); const first = sourceCode.getFirstToken(node); const last = sourceCode.getLastToken(node); let second = sourceCode.getTokenAfter(first, { includeComments: true }); diff --git a/lib/rules/jsx-equals-spacing.js b/lib/rules/jsx-equals-spacing.js index e5eefd2bb5..3424961a9d 100644 --- a/lib/rules/jsx-equals-spacing.js +++ b/lib/rules/jsx-equals-spacing.js @@ -6,6 +6,7 @@ 'use strict'; const docsUrl = require('../util/docsUrl'); +const getSourceCode = require('../util/eslint').getSourceCode; const report = require('../util/report'); // ------------------------------------------------------------------------------ @@ -59,7 +60,7 @@ module.exports = { return; } - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); const equalToken = sourceCode.getTokenAfter(attrNode.name); const spacedBefore = sourceCode.isSpaceBetweenTokens(attrNode.name, equalToken); const spacedAfter = sourceCode.isSpaceBetweenTokens(equalToken, attrNode.value); diff --git a/lib/rules/jsx-fragments.js b/lib/rules/jsx-fragments.js index 38b4dd8b4b..4dadb076d7 100644 --- a/lib/rules/jsx-fragments.js +++ b/lib/rules/jsx-fragments.js @@ -11,6 +11,7 @@ const variableUtil = require('../util/variable'); const testReactVersion = require('../util/version').testReactVersion; const docsUrl = require('../util/docsUrl'); const report = require('../util/report'); +const getText = require('../util/eslint').getText; // ------------------------------------------------------------------------------ // Rule Definition @@ -65,36 +66,34 @@ module.exports = { } function getFixerToLong(jsxFragment) { - const sourceCode = context.getSourceCode(); if (!jsxFragment.closingFragment || !jsxFragment.openingFragment) { // the old TS parser crashes here // TODO: FIXME: can we fake these two descriptors? return null; } return function fix(fixer) { - let source = sourceCode.getText(); + let source = getText(context); source = replaceNode(source, jsxFragment.closingFragment, closeFragLong); source = replaceNode(source, jsxFragment.openingFragment, openFragLong); - const lengthDiff = openFragLong.length - sourceCode.getText(jsxFragment.openingFragment).length - + closeFragLong.length - sourceCode.getText(jsxFragment.closingFragment).length; + const lengthDiff = openFragLong.length - getText(context, jsxFragment.openingFragment).length + + closeFragLong.length - getText(context, jsxFragment.closingFragment).length; const range = jsxFragment.range; return fixer.replaceTextRange(range, source.slice(range[0], range[1] + lengthDiff)); }; } function getFixerToShort(jsxElement) { - const sourceCode = context.getSourceCode(); return function fix(fixer) { - let source = sourceCode.getText(); + let source = getText(context); let lengthDiff; if (jsxElement.closingElement) { source = replaceNode(source, jsxElement.closingElement, closeFragShort); source = replaceNode(source, jsxElement.openingElement, openFragShort); - lengthDiff = sourceCode.getText(jsxElement.openingElement).length - openFragShort.length - + sourceCode.getText(jsxElement.closingElement).length - closeFragShort.length; + lengthDiff = getText(context, jsxElement.openingElement).length - openFragShort.length + + getText(context, jsxElement.closingElement).length - closeFragShort.length; } else { source = replaceNode(source, jsxElement.openingElement, `${openFragShort}${closeFragShort}`); - lengthDiff = sourceCode.getText(jsxElement.openingElement).length - openFragShort.length + lengthDiff = getText(context, jsxElement.openingElement).length - openFragShort.length - closeFragShort.length; } @@ -103,8 +102,8 @@ module.exports = { }; } - function refersToReactFragment(name) { - const variableInit = variableUtil.findVariableByName(context, name); + function refersToReactFragment(node, name) { + const variableInit = variableUtil.findVariableByName(context, node, name); if (!variableInit) { return false; } @@ -185,7 +184,7 @@ module.exports = { const openingEl = node.openingElement; const elName = elementType(openingEl); - if (fragmentNames.has(elName) || refersToReactFragment(elName)) { + if (fragmentNames.has(elName) || refersToReactFragment(node, elName)) { if (reportOnReactVersion(node)) { return; } diff --git a/lib/rules/jsx-handler-names.js b/lib/rules/jsx-handler-names.js index 8ff56fb47a..d839073a52 100644 --- a/lib/rules/jsx-handler-names.js +++ b/lib/rules/jsx-handler-names.js @@ -6,6 +6,7 @@ 'use strict'; const docsUrl = require('../util/docsUrl'); +const getText = require('../util/eslint').getText; const report = require('../util/report'); // ------------------------------------------------------------------------------ @@ -129,10 +130,10 @@ module.exports = { const propKey = typeof node.name === 'object' ? node.name.name : node.name; const expression = node.value.expression; - const propValue = context.getSourceCode() - .getText(checkInlineFunction && isInlineHandler(node) ? expression.body.callee : expression) - .replace(/\s*/g, '') - .replace(/^this\.|.*::/, ''); + const propValue = getText( + context, + checkInlineFunction && isInlineHandler(node) ? expression.body.callee : expression + ).replace(/\s*/g, '').replace(/^this\.|.*::/, ''); if (propKey === 'ref') { return; diff --git a/lib/rules/jsx-indent-props.js b/lib/rules/jsx-indent-props.js index 0d971ac7ac..050e721df3 100644 --- a/lib/rules/jsx-indent-props.js +++ b/lib/rules/jsx-indent-props.js @@ -32,6 +32,7 @@ const astUtil = require('../util/ast'); const docsUrl = require('../util/docsUrl'); +const getText = require('../util/eslint').getText; const reportC = require('../util/report'); // ------------------------------------------------------------------------------ @@ -140,7 +141,7 @@ module.exports = { * @return {Number} Indent */ function getNodeIndent(node) { - let src = context.getSourceCode().getText(node, node.loc.start.column + extraColumnStart); + let src = getText(context, node, node.loc.start.column + extraColumnStart); const lines = src.split('\n'); src = lines[0]; diff --git a/lib/rules/jsx-indent.js b/lib/rules/jsx-indent.js index 9a86e6c03a..27397172c8 100644 --- a/lib/rules/jsx-indent.js +++ b/lib/rules/jsx-indent.js @@ -36,6 +36,10 @@ const astUtil = require('../util/ast'); const docsUrl = require('../util/docsUrl'); const reportC = require('../util/report'); const jsxUtil = require('../util/jsx'); +const eslintUtil = require('../util/eslint'); + +const getSourceCode = eslintUtil.getSourceCode; +const getText = eslintUtil.getText; // ------------------------------------------------------------------------------ // Rule Definition @@ -116,7 +120,7 @@ module.exports = { } if (node.type === 'ReturnStatement') { - const raw = context.getSourceCode().getText(node); + const raw = getText(context, node); const lines = raw.split('\n'); if (lines.length > 1) { return function fix(fixer) { @@ -168,7 +172,7 @@ module.exports = { * @return {Number} Indent */ function getNodeIndent(node, byLastLine, excludeCommas) { - let src = context.getSourceCode().getText(node, node.loc.start.column + extraColumnStart); + let src = getText(context, node, node.loc.start.column + extraColumnStart); const lines = src.split('\n'); if (byLastLine) { src = lines[lines.length - 1]; @@ -215,7 +219,7 @@ module.exports = { && node.parent.parent && node.parent.parent.type === 'ConditionalExpression' && node.parent.parent.alternate === node.parent - && context.getSourceCode().getTokenBefore(node).value !== '(' + && getSourceCode(context).getTokenBefore(node).value !== '(' ); } @@ -334,7 +338,7 @@ module.exports = { } function handleOpeningElement(node) { - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); let prevToken = sourceCode.getTokenBefore(node); if (!prevToken) { return; @@ -377,7 +381,7 @@ module.exports = { return; } const nameIndent = getNodeIndent(node.name); - const lastToken = context.getSourceCode().getLastToken(node.value); + const lastToken = getSourceCode(context).getLastToken(node.value); const firstInLine = astUtil.getFirstNodeInLine(context, lastToken); const indent = node.name.loc.start.line === firstInLine.loc.start.line ? 0 : nameIndent; checkNodesIndent(firstInLine, indent); @@ -424,7 +428,7 @@ module.exports = { } if ( !fn - || !jsxUtil.isReturningJSX(node, context, true) + || !jsxUtil.isReturningJSX(context, node, true) ) { return; } diff --git a/lib/rules/jsx-key.js b/lib/rules/jsx-key.js index 7ea874d0ae..feee7ad5a0 100644 --- a/lib/rules/jsx-key.js +++ b/lib/rules/jsx-key.js @@ -12,6 +12,7 @@ const docsUrl = require('../util/docsUrl'); const pragmaUtil = require('../util/pragma'); const report = require('../util/report'); const astUtil = require('../util/ast'); +const getText = require('../util/eslint').getText; // ------------------------------------------------------------------------------ // Rule Definition @@ -213,7 +214,7 @@ module.exports = { } } else { keys.forEach((attr) => { - const value = context.getSourceCode().getText(attr.value); + const value = getText(context, attr.value); if (!map[value]) { map[value] = []; } map[value].push(attr); diff --git a/lib/rules/jsx-max-depth.js b/lib/rules/jsx-max-depth.js index 01698264c7..6b1db78189 100644 --- a/lib/rules/jsx-max-depth.js +++ b/lib/rules/jsx-max-depth.js @@ -150,7 +150,7 @@ module.exports = { return; } - const variables = variableUtil.variablesInScope(context); + const variables = variableUtil.variablesInScope(context, node); const element = findJSXElementOrFragment(variables, node.expression.name, []); if (element) { diff --git a/lib/rules/jsx-max-props-per-line.js b/lib/rules/jsx-max-props-per-line.js index 95d7942f9a..eebf7f0f33 100644 --- a/lib/rules/jsx-max-props-per-line.js +++ b/lib/rules/jsx-max-props-per-line.js @@ -6,11 +6,12 @@ 'use strict'; const docsUrl = require('../util/docsUrl'); +const getText = require('../util/eslint').getText; const report = require('../util/report'); function getPropName(context, propNode) { if (propNode.type === 'JSXSpreadAttribute') { - return context.getSourceCode().getText(propNode.argument); + return getText(context, propNode.argument); } return propNode.name.name; } @@ -87,7 +88,6 @@ module.exports = { }; function generateFixFunction(line, max) { - const sourceCode = context.getSourceCode(); const output = []; const front = line[0].range[0]; const back = line[line.length - 1].range[1]; @@ -96,9 +96,9 @@ module.exports = { const nodes = line.slice(i, i + max); output.push(nodes.reduce((prev, curr) => { if (prev === '') { - return sourceCode.getText(curr); + return getText(context, curr); } - return `${prev} ${sourceCode.getText(curr)}`; + return `${prev} ${getText(context, curr)}`; }, '')); } diff --git a/lib/rules/jsx-newline.js b/lib/rules/jsx-newline.js index 966bf6a34b..7fd395c72f 100644 --- a/lib/rules/jsx-newline.js +++ b/lib/rules/jsx-newline.js @@ -7,6 +7,7 @@ 'use strict'; const docsUrl = require('../util/docsUrl'); +const getText = require('../util/eslint').getText; const report = require('../util/report'); // ------------------------------------------------------------------------------ @@ -71,10 +72,9 @@ module.exports = { }, create(context) { const jsxElementParents = new Set(); - const sourceCode = context.getSourceCode(); function isBlockCommentInCurlyBraces(element) { - const elementRawValue = sourceCode.getText(element); + const elementRawValue = getText(context, element); return /^\s*{\/\*/.test(elementRawValue); } @@ -122,8 +122,7 @@ module.exports = { fix(fixer) { return fixer.replaceText( firstAdjacentSibling, - sourceCode.getText(firstAdjacentSibling) - .replace(regex, replacement) + getText(context, firstAdjacentSibling).replace(regex, replacement) ); }, }); @@ -151,8 +150,7 @@ module.exports = { return fixer.replaceText( firstAdjacentSibling, // double or remove the last newline - sourceCode.getText(firstAdjacentSibling) - .replace(regex, replacement) + getText(context, firstAdjacentSibling).replace(regex, replacement) ); }, }); diff --git a/lib/rules/jsx-no-bind.js b/lib/rules/jsx-no-bind.js index 17e56e2e04..4d6e349d25 100644 --- a/lib/rules/jsx-no-bind.js +++ b/lib/rules/jsx-no-bind.js @@ -11,6 +11,7 @@ const propName = require('jsx-ast-utils/propName'); const docsUrl = require('../util/docsUrl'); const jsxUtil = require('../util/jsx'); const report = require('../util/report'); +const getAncestors = require('../util/eslint').getAncestors; // ----------------------------------------------------------------------------- // Rule Definition @@ -123,7 +124,7 @@ module.exports = { } function getBlockStatementAncestors(node) { - return context.getAncestors(node).filter( + return getAncestors(context, node).filter( (ancestor) => ancestor.type === 'BlockStatement' ).reverse(); } diff --git a/lib/rules/jsx-no-comment-textnodes.js b/lib/rules/jsx-no-comment-textnodes.js index 2a90467d3f..de4d288c37 100644 --- a/lib/rules/jsx-no-comment-textnodes.js +++ b/lib/rules/jsx-no-comment-textnodes.js @@ -6,6 +6,7 @@ 'use strict'; const docsUrl = require('../util/docsUrl'); +const getText = require('../util/eslint').getText; const report = require('../util/report'); // ------------------------------------------------------------------------------ @@ -16,9 +17,14 @@ const messages = { putCommentInBraces: 'Comments inside children section of tag should be placed inside braces', }; -function checkText(node, context) { +/** + * @param {Context} context + * @param {ASTNode} node + * @returns {void} + */ +function checkText(context, node) { // since babel-eslint has the wrong node.raw, we'll get the source text - const rawValue = context.getSourceCode().getText(node); + const rawValue = getText(context, node); if (/^\s*\/(\/|\*)/m.test(rawValue)) { // inside component, e.g.
literal
if ( @@ -55,10 +61,10 @@ module.exports = { return { Literal(node) { - checkText(node, context); + checkText(context, node); }, JSXText(node) { - checkText(node, context); + checkText(context, node); }, }; }, diff --git a/lib/rules/jsx-no-constructed-context-values.js b/lib/rules/jsx-no-constructed-context-values.js index f28c51fd4a..2e9ef93422 100644 --- a/lib/rules/jsx-no-constructed-context-values.js +++ b/lib/rules/jsx-no-constructed-context-values.js @@ -8,6 +8,7 @@ const Components = require('../util/Components'); const docsUrl = require('../util/docsUrl'); +const getScope = require('../util/eslint').getScope; const report = require('../util/report'); // ------------------------------------------------------------------------------ @@ -139,7 +140,7 @@ module.exports = { url: docsUrl('jsx-no-constructed-context-values'), }, messages, - schema: {}, + schema: false, }, // eslint-disable-next-line arrow-body-style @@ -180,7 +181,7 @@ module.exports = { } const valueExpression = valueNode.expression; - const invocationScope = context.getScope(); + const invocationScope = getScope(context, node); // Check if the value prop is a construction const constructInfo = isConstruction(valueExpression, invocationScope); diff --git a/lib/rules/jsx-no-leaked-render.js b/lib/rules/jsx-no-leaked-render.js index a6bf54b72a..f7eea1ad52 100644 --- a/lib/rules/jsx-no-leaked-render.js +++ b/lib/rules/jsx-no-leaked-render.js @@ -8,8 +8,10 @@ const find = require('es-iterator-helpers/Iterator.prototype.find'); const from = require('es-iterator-helpers/Iterator.from'); +const getText = require('../util/eslint').getText; const docsUrl = require('../util/docsUrl'); const report = require('../util/report'); +const variableUtil = require('../util/variable'); const testReactVersion = require('../util/version').testReactVersion; const isParenthesized = require('../util/ast').isParenthesized; @@ -54,13 +56,12 @@ function extractExpressionBetweenLogicalAnds(node) { } function ruleFixer(context, fixStrategy, fixer, reportedNode, leftNode, rightNode) { - const sourceCode = context.getSourceCode(); - const rightSideText = sourceCode.getText(rightNode); + const rightSideText = getText(context, rightNode); if (fixStrategy === COERCE_STRATEGY) { const expressions = extractExpressionBetweenLogicalAnds(leftNode); const newText = expressions.map((node) => { - let nodeText = sourceCode.getText(node); + let nodeText = getText(context, node); if (isParenthesized(context, node)) { nodeText = `(${nodeText})`; } @@ -98,7 +99,7 @@ function ruleFixer(context, fixStrategy, fixer, reportedNode, leftNode, rightNod } if (fixStrategy === TERNARY_STRATEGY) { - let leftSideText = sourceCode.getText(trimLeftNode(leftNode)); + let leftSideText = getText(context, trimLeftNode(leftNode)); if (isParenthesized(context, leftNode)) { leftSideText = `(${leftSideText})`; } @@ -160,6 +161,17 @@ module.exports = { if (isCoerceValidLeftSide || getIsCoerceValidNestedLogicalExpression(leftSide)) { return; } + const variables = variableUtil.variablesInScope(context, node); + const leftSideVar = variableUtil.getVariable(variables, leftSide.name); + if (leftSideVar) { + const leftSideValue = leftSideVar.defs + && leftSideVar.defs.length + && leftSideVar.defs[0].node.init + && leftSideVar.defs[0].node.init.value; + if (typeof leftSideValue === 'boolean') { + return; + } + } } if (testReactVersion(context, '>= 18') && leftSide.type === 'Literal' && leftSide.value === '') { diff --git a/lib/rules/jsx-no-literals.js b/lib/rules/jsx-no-literals.js index ca4f3a8d46..d7f43ab05c 100644 --- a/lib/rules/jsx-no-literals.js +++ b/lib/rules/jsx-no-literals.js @@ -11,6 +11,7 @@ const map = require('es-iterator-helpers/Iterator.prototype.map'); const docsUrl = require('../util/docsUrl'); const report = require('../util/report'); +const getText = require('../util/eslint').getText; // ------------------------------------------------------------------------------ // Rule Definition @@ -149,7 +150,7 @@ module.exports = { report(context, messages[messageId], messageId, { node, data: { - text: context.getSourceCode().getText(node).trim(), + text: getText(context, node).trim(), }, }); } diff --git a/lib/rules/jsx-no-undef.js b/lib/rules/jsx-no-undef.js index 3a1f4dbc5f..a15de8e854 100644 --- a/lib/rules/jsx-no-undef.js +++ b/lib/rules/jsx-no-undef.js @@ -6,6 +6,7 @@ 'use strict'; const docsUrl = require('../util/docsUrl'); +const eslintUtil = require('../util/eslint'); const jsxUtil = require('../util/jsx'); const report = require('../util/report'); @@ -50,8 +51,8 @@ module.exports = { * @returns {void} */ function checkIdentifierInJSX(node) { - let scope = context.getScope(); - const sourceCode = context.getSourceCode(); + let scope = eslintUtil.getScope(context, node); + const sourceCode = eslintUtil.getSourceCode(context); const sourceType = sourceCode.ast.sourceType; const scopeUpperBound = !allowGlobals && sourceType === 'module' ? 'module' : 'global'; let variables = scope.variables; diff --git a/lib/rules/jsx-no-useless-fragment.js b/lib/rules/jsx-no-useless-fragment.js index 303b9719e5..4c2886469f 100644 --- a/lib/rules/jsx-no-useless-fragment.js +++ b/lib/rules/jsx-no-useless-fragment.js @@ -10,6 +10,7 @@ const pragmaUtil = require('../util/pragma'); const jsxUtil = require('../util/jsx'); const docsUrl = require('../util/docsUrl'); const report = require('../util/report'); +const getText = require('../util/eslint').getText; function isJSXText(node) { return !!node && (node.type === 'JSXText' || node.type === 'Literal'); @@ -216,7 +217,7 @@ module.exports = { const opener = node.type === 'JSXFragment' ? node.openingFragment : node.openingElement; const closer = node.type === 'JSXFragment' ? node.closingFragment : node.closingElement; - const childrenText = opener.selfClosing ? '' : context.getSourceCode().getText().slice(opener.range[1], closer.range[0]); + const childrenText = opener.selfClosing ? '' : getText(context).slice(opener.range[1], closer.range[0]); return fixer.replaceText(node, trimLikeReact(childrenText)); }; diff --git a/lib/rules/jsx-one-expression-per-line.js b/lib/rules/jsx-one-expression-per-line.js index f18fa919e1..3ae61e3d96 100644 --- a/lib/rules/jsx-one-expression-per-line.js +++ b/lib/rules/jsx-one-expression-per-line.js @@ -6,9 +6,13 @@ 'use strict'; const docsUrl = require('../util/docsUrl'); +const eslintUtil = require('../util/eslint'); const jsxUtil = require('../util/jsx'); const report = require('../util/report'); +const getSourceCode = eslintUtil.getSourceCode; +const getText = eslintUtil.getText; + // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ @@ -55,8 +59,12 @@ module.exports = { return `${node.loc.start.line},${node.loc.start.column}`; } + /** + * @param {ASTNode} n + * @returns {string} + */ function nodeDescriptor(n) { - return n.openingElement ? n.openingElement.name.name : context.getSourceCode().getText(n).replace(/\n/g, ''); + return n.openingElement ? n.openingElement.name.name : getText(context, n).replace(/\n/g, ''); } function handleJSX(node) { @@ -163,20 +171,20 @@ module.exports = { function spaceBetweenPrev() { return ((prevChild.type === 'Literal' || prevChild.type === 'JSXText') && / $/.test(prevChild.raw)) || ((child.type === 'Literal' || child.type === 'JSXText') && /^ /.test(child.raw)) - || context.getSourceCode().isSpaceBetweenTokens(prevChild, child); + || getSourceCode(context).isSpaceBetweenTokens(prevChild, child); } function spaceBetweenNext() { return ((nextChild.type === 'Literal' || nextChild.type === 'JSXText') && /^ /.test(nextChild.raw)) || ((child.type === 'Literal' || child.type === 'JSXText') && / $/.test(child.raw)) - || context.getSourceCode().isSpaceBetweenTokens(child, nextChild); + || getSourceCode(context).isSpaceBetweenTokens(child, nextChild); } if (!prevChild && !nextChild) { return; } - const source = context.getSourceCode().getText(child); + const source = getText(context, child); const leadingSpace = !!(prevChild && spaceBetweenPrev()); const trailingSpace = !!(nextChild && spaceBetweenNext()); const leadingNewLine = !!prevChild; diff --git a/lib/rules/jsx-props-no-multi-spaces.js b/lib/rules/jsx-props-no-multi-spaces.js index edd122c413..ce80338cd8 100644 --- a/lib/rules/jsx-props-no-multi-spaces.js +++ b/lib/rules/jsx-props-no-multi-spaces.js @@ -6,8 +6,12 @@ 'use strict'; const docsUrl = require('../util/docsUrl'); +const eslintUtil = require('../util/eslint'); const report = require('../util/report'); +const getSourceCode = eslintUtil.getSourceCode; +const getText = eslintUtil.getText; + // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ @@ -34,12 +38,12 @@ module.exports = { }, create(context) { - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); function getPropName(propNode) { switch (propNode.type) { case 'JSXSpreadAttribute': - return context.getSourceCode().getText(propNode.argument); + return getText(context, propNode.argument); case 'JSXIdentifier': return propNode.name; case 'JSXMemberExpression': @@ -47,7 +51,7 @@ module.exports = { default: return propNode.name ? propNode.name.name - : `${context.getSourceCode().getText(propNode.object)}.${propNode.property.name}`; // needed for typescript-eslint parser + : `${getText(context, propNode.object)}.${propNode.property.name}`; // needed for typescript-eslint parser } } @@ -82,7 +86,7 @@ module.exports = { return; } - const between = context.getSourceCode().text.slice(prev.range[1], node.range[0]); + const between = getSourceCode(context).text.slice(prev.range[1], node.range[0]); if (between !== ' ') { report(context, messages.onlyOneSpace, 'onlyOneSpace', { diff --git a/lib/rules/jsx-sort-default-props.js b/lib/rules/jsx-sort-default-props.js index f35e40dc5f..89b7bb64cc 100644 --- a/lib/rules/jsx-sort-default-props.js +++ b/lib/rules/jsx-sort-default-props.js @@ -10,6 +10,10 @@ const variableUtil = require('../util/variable'); const docsUrl = require('../util/docsUrl'); const report = require('../util/report'); const log = require('../util/log'); +const eslintUtil = require('../util/eslint'); + +const getFirstTokens = eslintUtil.getFirstTokens; +const getText = eslintUtil.getText; let isWarnedForDeprecation = false; @@ -65,7 +69,7 @@ module.exports = { // (babel-eslint@5 does not expose property name so we have to rely on tokens) } if (node.type === 'ClassProperty') { - const tokens = context.getSourceCode().getFirstTokens(node, 2); + const tokens = getFirstTokens(context, node, 2); return tokens[1] && tokens[1].type === 'Identifier' ? tokens[1].value : tokens[0].value; } return ''; @@ -82,16 +86,19 @@ module.exports = { } function getKey(node) { - return context.getSourceCode().getText(node.key || node.argument); + return getText(context, node.key || node.argument); } /** * Find a variable by name in the current scope. + * @param {ASTNode} node The node to look for. * @param {string} name Name of the variable to look for. * @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise. */ - function findVariableByName(name) { - const variable = variableUtil.variablesInScope(context).find((item) => item.name === name); + function findVariableByName(node, name) { + const variable = variableUtil + .variablesInScope(context, node) + .find((item) => item.name === name); if (!variable || !variable.defs[0] || !variable.defs[0].node) { return null; @@ -111,7 +118,7 @@ module.exports = { */ function checkSorted(declarations) { // function fix(fixer) { - // return propTypesSortUtil.fixPropTypesSort(fixer, context, declarations, ignoreCase); + // return propTypesSortUtil.fixPropTypesSort(context, fixer, declarations, ignoreCase); // } declarations.reduce((prev, curr, idx, decls) => { @@ -147,7 +154,7 @@ module.exports = { if (node.type === 'ObjectExpression') { checkSorted(node.properties); } else if (node.type === 'Identifier') { - const propTypesObject = findVariableByName(node.name); + const propTypesObject = findVariableByName(node, node.name); if (propTypesObject && propTypesObject.properties) { checkSorted(propTypesObject.properties); } diff --git a/lib/rules/jsx-sort-props.js b/lib/rules/jsx-sort-props.js index 3ca1724ebe..f811e16cf7 100644 --- a/lib/rules/jsx-sort-props.js +++ b/lib/rules/jsx-sort-props.js @@ -12,6 +12,10 @@ const toSorted = require('array.prototype.tosorted'); const docsUrl = require('../util/docsUrl'); const jsxUtil = require('../util/jsx'); const report = require('../util/report'); +const eslintUtil = require('../util/eslint'); + +const getText = eslintUtil.getText; +const getSourceCode = eslintUtil.getSourceCode; // ------------------------------------------------------------------------------ // Rule Definition @@ -141,7 +145,7 @@ function contextCompare(a, b, options) { * @return {Array>} */ function getGroupsOfSortableAttributes(attributes, context) { - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); const sortableAttributeGroups = []; let groupCount = 0; @@ -212,7 +216,6 @@ function getGroupsOfSortableAttributes(attributes, context) { } function generateFixerFunction(node, context, reservedList) { - const sourceCode = context.getSourceCode(); const attributes = node.attributes.slice(0); const configuration = context.options[0] || {}; const ignoreCase = configuration.ignoreCase || false; @@ -245,7 +248,7 @@ function generateFixerFunction(node, context, reservedList) { return function fixFunction(fixer) { const fixers = []; - let source = sourceCode.getText(); + let source = getText(context); sortableAttributeGroups.forEach((sortableGroup, ii) => { sortableGroup.forEach((attr, jj) => { diff --git a/lib/rules/jsx-space-before-closing.js b/lib/rules/jsx-space-before-closing.js index f1e1e61d96..e923adfbd3 100644 --- a/lib/rules/jsx-space-before-closing.js +++ b/lib/rules/jsx-space-before-closing.js @@ -10,6 +10,7 @@ const getTokenBeforeClosingBracket = require('../util/getTokenBeforeClosingBrack const docsUrl = require('../util/docsUrl'); const log = require('../util/log'); const report = require('../util/report'); +const getSourceCode = require('../util/eslint').getSourceCode; let isWarnedForDeprecation = false; @@ -54,7 +55,7 @@ module.exports = { return; } - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); const leftToken = getTokenBeforeClosingBracket(node); const closingSlash = sourceCode.getTokenAfter(leftToken); diff --git a/lib/rules/jsx-tag-spacing.js b/lib/rules/jsx-tag-spacing.js index 9cc18e8c37..ac7c693278 100644 --- a/lib/rules/jsx-tag-spacing.js +++ b/lib/rules/jsx-tag-spacing.js @@ -8,6 +8,10 @@ const getTokenBeforeClosingBracket = require('../util/getTokenBeforeClosingBracket'); const docsUrl = require('../util/docsUrl'); const report = require('../util/report'); +const eslintUtil = require('../util/eslint'); + +const getFirstTokens = eslintUtil.getFirstTokens; +const getSourceCode = eslintUtil.getSourceCode; const messages = { selfCloseSlashNoSpace: 'Whitespace is forbidden between `/` and `>`; write `/>`', @@ -29,7 +33,7 @@ const messages = { // ------------------------------------------------------------------------------ function validateClosingSlash(context, node, option) { - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); let adjacent; @@ -64,7 +68,7 @@ function validateClosingSlash(context, node, option) { }); } } else { - const firstTokens = sourceCode.getFirstTokens(node, 2); + const firstTokens = getFirstTokens(context, node, 2); adjacent = !sourceCode.isSpaceBetweenTokens(firstTokens[0], firstTokens[1]); @@ -97,7 +101,7 @@ function validateClosingSlash(context, node, option) { } function validateBeforeSelfClosing(context, node, option) { - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); const leftToken = getTokenBeforeClosingBracket(node); const closingSlash = sourceCode.getTokenAfter(leftToken); @@ -141,7 +145,7 @@ function validateBeforeSelfClosing(context, node, option) { } function validateAfterOpening(context, node, option) { - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); const openingToken = sourceCode.getTokenBefore(node.name); if (option === 'allow-multiline') { @@ -182,7 +186,7 @@ function validateAfterOpening(context, node, option) { function validateBeforeClosing(context, node, option) { // Don't enforce this rule for self closing tags if (!node.selfClosing) { - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); const leftToken = option === 'proportional-always' ? getTokenBeforeClosingBracket(node) : sourceCode.getLastTokens(node, 2)[0]; diff --git a/lib/rules/jsx-uses-react.js b/lib/rules/jsx-uses-react.js index bf06f508f8..5d64f76cd8 100644 --- a/lib/rules/jsx-uses-react.js +++ b/lib/rules/jsx-uses-react.js @@ -7,6 +7,7 @@ const pragmaUtil = require('../util/pragma'); const docsUrl = require('../util/docsUrl'); +const markVariableAsUsed = require('../util/eslint').markVariableAsUsed; // ------------------------------------------------------------------------------ // Rule Definition @@ -29,8 +30,12 @@ module.exports = { const pragma = pragmaUtil.getFromContext(context); const fragment = pragmaUtil.getFragmentFromContext(context); - function handleOpeningElement() { - context.markVariableAsUsed(pragma); + /** + * @param {ASTNode} node + * @returns {void} + */ + function handleOpeningElement(node) { + markVariableAsUsed(pragma, node, context); } // -------------------------------------------------------------------------- // Public @@ -39,8 +44,8 @@ module.exports = { return { JSXOpeningElement: handleOpeningElement, JSXOpeningFragment: handleOpeningElement, - JSXFragment() { - context.markVariableAsUsed(fragment); + JSXFragment(node) { + markVariableAsUsed(fragment, node, context); }, }; }, diff --git a/lib/rules/jsx-uses-vars.js b/lib/rules/jsx-uses-vars.js index 9ea9ab001e..6f5f4f6cc0 100644 --- a/lib/rules/jsx-uses-vars.js +++ b/lib/rules/jsx-uses-vars.js @@ -6,6 +6,7 @@ 'use strict'; const docsUrl = require('../util/docsUrl'); +const markVariableAsUsed = require('../util/eslint').markVariableAsUsed; // ------------------------------------------------------------------------------ // Rule Definition @@ -53,7 +54,7 @@ module.exports = { return; } - context.markVariableAsUsed(name); + markVariableAsUsed(name, node, context); }, }; diff --git a/lib/rules/jsx-wrap-multilines.js b/lib/rules/jsx-wrap-multilines.js index 59fa5f294b..17a9812465 100644 --- a/lib/rules/jsx-wrap-multilines.js +++ b/lib/rules/jsx-wrap-multilines.js @@ -7,10 +7,14 @@ const has = require('object.hasown/polyfill')(); const docsUrl = require('../util/docsUrl'); +const eslintUtil = require('../util/eslint'); const jsxUtil = require('../util/jsx'); const reportC = require('../util/report'); const isParenthesized = require('../util/ast').isParenthesized; +const getSourceCode = eslintUtil.getSourceCode; +const getText = eslintUtil.getText; + // ------------------------------------------------------------------------------ // Constants // ------------------------------------------------------------------------------ @@ -93,7 +97,7 @@ module.exports = { } function needsOpeningNewLine(node) { - const previousToken = context.getSourceCode().getTokenBefore(node); + const previousToken = getSourceCode(context).getTokenBefore(node); if (!isParenthesized(context, node)) { return false; @@ -107,7 +111,7 @@ module.exports = { } function needsClosingNewLine(node) { - const nextToken = context.getSourceCode().getTokenAfter(node); + const nextToken = getSourceCode(context).getTokenAfter(node); if (!isParenthesized(context, node)) { return false; @@ -143,11 +147,11 @@ module.exports = { return; } - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); const option = getOption(type); if ((option === true || option === 'parens') && !isParenthesized(context, node) && isMultilines(node)) { - report(node, 'missingParens', (fixer) => fixer.replaceText(node, `(${sourceCode.getText(node)})`)); + report(node, 'missingParens', (fixer) => fixer.replaceText(node, `(${getText(context, node)})`)); } if (option === 'parens-new-line' && isMultilines(node)) { @@ -162,18 +166,18 @@ module.exports = { 'missingParens', (fixer) => fixer.replaceTextRange( [tokenBefore.range[0], tokenAfter && (tokenAfter.value === ';' || tokenAfter.value === '}') ? tokenAfter.range[0] : node.range[1]], - `${trimTokenBeforeNewline(node, tokenBefore)}(\n${start.column > 0 ? ' '.repeat(start.column) : ''}${sourceCode.getText(node)}\n${start.column > 0 ? ' '.repeat(start.column - 2) : ''})` + `${trimTokenBeforeNewline(node, tokenBefore)}(\n${start.column > 0 ? ' '.repeat(start.column) : ''}${getText(context, node)}\n${start.column > 0 ? ' '.repeat(start.column - 2) : ''})` ) ); } else { - report(node, 'missingParens', (fixer) => fixer.replaceText(node, `(\n${sourceCode.getText(node)}\n)`)); + report(node, 'missingParens', (fixer) => fixer.replaceText(node, `(\n${getText(context, node)}\n)`)); } } else { const needsOpening = needsOpeningNewLine(node); const needsClosing = needsClosingNewLine(node); if (needsOpening || needsClosing) { report(node, 'parensOnNewLines', (fixer) => { - const text = sourceCode.getText(node); + const text = getText(context, node); let fixed = text; if (needsOpening) { fixed = `\n${fixed}`; @@ -192,7 +196,7 @@ module.exports = { const tokenAfter = sourceCode.getTokenAfter(node); report(node, 'extraParens', (fixer) => fixer.replaceTextRange( [tokenBefore.range[0], tokenAfter.range[1]], - sourceCode.getText(node) + getText(context, node) )); } } diff --git a/lib/rules/no-access-state-in-setstate.js b/lib/rules/no-access-state-in-setstate.js index 89d4976077..be72ea42e4 100644 --- a/lib/rules/no-access-state-in-setstate.js +++ b/lib/rules/no-access-state-in-setstate.js @@ -8,6 +8,7 @@ const docsUrl = require('../util/docsUrl'); const componentUtil = require('../util/componentUtil'); const report = require('../util/report'); +const getScope = require('../util/eslint').getScope; // ------------------------------------------------------------------------------ // Rule Definition @@ -47,8 +48,15 @@ module.exports = { return current.arguments[0] === node; } - function isClassComponent() { - return !!(componentUtil.getParentES6Component(context) || componentUtil.getParentES5Component(context)); + /** + * @param {ASTNode} node + * @returns {boolean} + */ + function isClassComponent(node) { + return !!( + componentUtil.getParentES6Component(context, node) + || componentUtil.getParentES5Component(context, node) + ); } // The methods array contains all methods or functions that are using this.state @@ -58,7 +66,7 @@ module.exports = { const vars = []; return { CallExpression(node) { - if (!isClassComponent()) { + if (!isClassComponent(node)) { return; } // Appends all the methods that are calling another @@ -103,7 +111,7 @@ module.exports = { if ( node.property.name === 'state' && node.object.type === 'ThisExpression' - && isClassComponent() + && isClassComponent(node) ) { let current = node; while (current.type !== 'Program') { @@ -134,7 +142,7 @@ module.exports = { if (current.type === 'VariableDeclarator') { vars.push({ node, - scope: context.getScope(), + scope: getScope(context, node), variableName: current.id.name, }); break; @@ -158,7 +166,7 @@ module.exports = { while (current.type !== 'Program') { if (isFirstArgumentInSetStateCall(current, node)) { vars - .filter((v) => v.scope === context.getScope() && v.variableName === node.name) + .filter((v) => v.scope === getScope(context, node) && v.variableName === node.name) .forEach((v) => { report(context, messages.useCallback, 'useCallback', { node: v.node, @@ -176,7 +184,7 @@ module.exports = { if (property && property.key && property.key.name === 'state' && isDerivedFromThis) { vars.push({ node: property.key, - scope: context.getScope(), + scope: getScope(context, node), variableName: property.key.name, }); } diff --git a/lib/rules/no-adjacent-inline-elements.js b/lib/rules/no-adjacent-inline-elements.js index 60e404ba22..5d7fdb8d6e 100644 --- a/lib/rules/no-adjacent-inline-elements.js +++ b/lib/rules/no-adjacent-inline-elements.js @@ -111,7 +111,7 @@ module.exports = { validate(node, node.children); }, CallExpression(node) { - if (!isCreateElement(node, context)) { + if (!isCreateElement(context, node)) { return; } if (node.arguments.length < 2 || !node.arguments[2]) { diff --git a/lib/rules/no-array-index-key.js b/lib/rules/no-array-index-key.js index bc8c91f9ef..c79fd56e2c 100644 --- a/lib/rules/no-array-index-key.js +++ b/lib/rules/no-array-index-key.js @@ -28,7 +28,7 @@ function isCreateCloneElement(node, context) { } if (node.type === 'Identifier') { - const variable = variableUtil.findVariableByName(context, node.name); + const variable = variableUtil.findVariableByName(context, node, node.name); if (variable && variable.type === 'ImportSpecifier') { return variable.parent.source.value === 'react'; } diff --git a/lib/rules/no-arrow-function-lifecycle.js b/lib/rules/no-arrow-function-lifecycle.js index a1de789b81..e56cb78601 100644 --- a/lib/rules/no-arrow-function-lifecycle.js +++ b/lib/rules/no-arrow-function-lifecycle.js @@ -13,8 +13,12 @@ const componentUtil = require('../util/componentUtil'); const docsUrl = require('../util/docsUrl'); const lifecycleMethods = require('../util/lifecycleMethods'); const report = require('../util/report'); +const eslintUtil = require('../util/eslint'); -function getText(node) { +const getSourceCode = eslintUtil.getSourceCode; +const getText = eslintUtil.getText; + +function getRuleText(node) { const params = node.value.params.map((p) => p.name); if (node.type === 'Property') { @@ -67,7 +71,7 @@ module.exports = { if (nodeType === 'ArrowFunctionExpression' && isLifecycleMethod) { const body = node.value.body; const isBlockBody = body.type === 'BlockStatement'; - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); let nextComment = []; let previousComment = []; @@ -103,7 +107,7 @@ module.exports = { node.key.range[1], (previousComment.length > 0 ? previousComment[0] : body).range[0], ]; - const hasSemi = node.value.expression && sourceCode.getText(node).slice(node.value.range[1] - node.range[0]) === ';'; + const hasSemi = node.value.expression && getText(context, node).slice(node.value.range[1] - node.range[0]) === ';'; report( context, @@ -117,13 +121,13 @@ module.exports = { fix(fixer) { if (!sourceCode.getCommentsAfter) { // eslint 3.x - return isBlockBody && fixer.replaceTextRange(headRange, getText(node)); + return isBlockBody && fixer.replaceTextRange(headRange, getRuleText(node)); } return [].concat( - fixer.replaceTextRange(headRange, getText(node)), + fixer.replaceTextRange(headRange, getRuleText(node)), isBlockBody ? [] : fixer.replaceTextRange( [bodyRange[0], bodyRange[1] + (hasSemi ? 1 : 0)], - `{ return ${previousComment.map((x) => sourceCode.getText(x)).join('')}${sourceCode.getText(body)}${nextComment.map((x) => sourceCode.getText(x)).join('')}; }` + `{ return ${previousComment.map((x) => getText(context, x)).join('')}${getText(context, body)}${nextComment.map((x) => getText(context, x)).join('')}; }` ) ); }, diff --git a/lib/rules/no-children-prop.js b/lib/rules/no-children-prop.js index 440e61e945..508488e4d9 100644 --- a/lib/rules/no-children-prop.js +++ b/lib/rules/no-children-prop.js @@ -21,7 +21,7 @@ const report = require('../util/report'); * object literal, False if not. */ function isCreateElementWithProps(node, context) { - return isCreateElement(node, context) + return isCreateElement(context, node) && node.arguments.length > 1 && node.arguments[1].type === 'ObjectExpression'; } diff --git a/lib/rules/no-danger-with-children.js b/lib/rules/no-danger-with-children.js index 17d55930ff..d3508721fe 100644 --- a/lib/rules/no-danger-with-children.js +++ b/lib/rules/no-danger-with-children.js @@ -31,8 +31,9 @@ module.exports = { schema: [], // no options }, create(context) { - function findSpreadVariable(name) { - return variableUtil.variablesInScope(context).find((item) => item.name === name); + function findSpreadVariable(node, name) { + return variableUtil.variablesInScope(context, node) + .find((item) => item.name === name); } /** * Takes a ObjectExpression and returns the value of the prop if it has it @@ -50,7 +51,7 @@ module.exports = { return prop.key.name === propName; } if (prop.type === 'ExperimentalSpreadProperty' || prop.type === 'SpreadElement') { - const variable = findSpreadVariable(prop.argument.name); + const variable = findSpreadVariable(node, prop.argument.name); if (variable && variable.defs.length && variable.defs[0].node.init) { if (seenProps.indexOf(prop.argument.name) > -1) { return false; @@ -73,7 +74,7 @@ module.exports = { const attributes = node.openingElement.attributes; return attributes.find((attribute) => { if (attribute.type === 'JSXSpreadAttribute') { - const variable = findSpreadVariable(attribute.argument.name); + const variable = findSpreadVariable(node, attribute.argument.name); if (variable && variable.defs.length && variable.defs[0].node.init) { return findObjectProp(variable.defs[0].node.init, propName, []); } @@ -127,7 +128,8 @@ module.exports = { let props = node.arguments[1]; if (props.type === 'Identifier') { - const variable = variableUtil.variablesInScope(context).find((item) => item.name === props.name); + const variable = variableUtil.variablesInScope(context, node) + .find((item) => item.name === props.name); if (variable && variable.defs.length && variable.defs[0].node.init) { props = variable.defs[0].node.init; } diff --git a/lib/rules/no-deprecated.js b/lib/rules/no-deprecated.js index d37b55bbfd..462bd4c756 100644 --- a/lib/rules/no-deprecated.js +++ b/lib/rules/no-deprecated.js @@ -14,6 +14,7 @@ const docsUrl = require('../util/docsUrl'); const pragmaUtil = require('../util/pragma'); const testReactVersion = require('../util/version').testReactVersion; const report = require('../util/report'); +const getText = require('../util/eslint').getText; // ------------------------------------------------------------------------------ // Constants @@ -217,7 +218,7 @@ module.exports = { return { MemberExpression(node) { - checkDeprecation(node, context.getSourceCode().getText(node)); + checkDeprecation(node, getText(context, node)); }, ImportDeclaration(node) { diff --git a/lib/rules/no-direct-mutation-state.js b/lib/rules/no-direct-mutation-state.js index 9349a85fe5..3df0998c6e 100644 --- a/lib/rules/no-direct-mutation-state.js +++ b/lib/rules/no-direct-mutation-state.js @@ -98,7 +98,7 @@ module.exports = { }, AssignmentExpression(node) { - const component = components.get(utils.getParentComponent()); + const component = components.get(utils.getParentComponent(node)); if (shouldIgnoreComponent(component) || !node.left || !node.left.object) { return; } @@ -114,7 +114,7 @@ module.exports = { }, UpdateExpression(node) { - const component = components.get(utils.getParentComponent()); + const component = components.get(utils.getParentComponent(node)); if (shouldIgnoreComponent(component) || node.argument.type !== 'MemberExpression') { return; } diff --git a/lib/rules/no-invalid-html-attribute.js b/lib/rules/no-invalid-html-attribute.js index 1dd4d0f44b..98c33fbc3f 100644 --- a/lib/rules/no-invalid-html-attribute.js +++ b/lib/rules/no-invalid-html-attribute.js @@ -238,8 +238,8 @@ const messages = { suggestRemoveDefault: '"remove {{attributeName}}"', suggestRemoveEmpty: '"remove empty attribute {{attributeName}}"', suggestRemoveInvalid: '“remove invalid attribute {{reportingValue}}”', - suggestRemoveWhitespaces: 'remove whitespaces in “{{reportingValue}}”', - suggestRemoveNonString: 'remove non-string value in “{{reportingValue}}”', + suggestRemoveWhitespaces: 'remove whitespaces in “{{attributeName}}”', + suggestRemoveNonString: 'remove non-string value in “{{attributeName}}”', }; function splitIntoRangedParts(node, regex) { @@ -259,13 +259,18 @@ function splitIntoRangedParts(node, regex) { function checkLiteralValueNode(context, attributeName, node, parentNode, parentNodeName) { if (typeof node.value !== 'string') { + const data = { attributeName, reportingValue: node.value }; + report(context, messages.onlyStrings, 'onlyStrings', { node, - data: { attributeName }, + data, suggest: [ Object.assign( getMessageData('suggestRemoveNonString', messages.suggestRemoveNonString), - { fix(fixer) { return fixer.remove(parentNode); } } + { + data, + fix(fixer) { return fixer.remove(parentNode); }, + } ), ], }); @@ -273,13 +278,18 @@ function checkLiteralValueNode(context, attributeName, node, parentNode, parentN } if (!node.value.trim()) { + const data = { attributeName, reportingValue: node.value }; + report(context, messages.noEmpty, 'noEmpty', { node, - data: { attributeName }, + data, suggest: [ Object.assign( getMessageData('suggestRemoveEmpty', messages.suggestRemoveEmpty), - { fix(fixer) { return fixer.remove(node.parent); } } + { + data, + fix(fixer) { return fixer.remove(node.parent); }, + } ), ], }); @@ -291,32 +301,44 @@ function checkLiteralValueNode(context, attributeName, node, parentNode, parentN const allowedTags = VALID_VALUES.get(attributeName).get(singlePart.value); const reportingValue = singlePart.reportingValue; - const suggest = [ - Object.assign( - getMessageData('suggestRemoveInvalid', messages.suggestRemoveInvalid), - { fix(fixer) { return fixer.removeRange(singlePart.range); } } - ), - ]; - if (!allowedTags) { const data = { attributeName, reportingValue, }; + report(context, messages.neverValid, 'neverValid', { node, data, - suggest, + suggest: [ + Object.assign( + getMessageData('suggestRemoveInvalid', messages.suggestRemoveInvalid), + { + data, + fix(fixer) { return fixer.removeRange(singlePart.range); }, + } + ), + ], }); } else if (!allowedTags.has(parentNodeName)) { + const data = { + attributeName, + reportingValue, + elementName: parentNodeName, + }; + report(context, messages.notValidFor, 'notValidFor', { node, - data: { - attributeName, - reportingValue, - elementName: parentNodeName, - }, - suggest, + data, + suggest: [ + Object.assign( + getMessageData('suggestRemoveInvalid', messages.suggestRemoveInvalid), + { + data, + fix(fixer) { return fixer.removeRange(singlePart.range); }, + } + ), + ], }); } } @@ -360,7 +382,10 @@ function checkLiteralValueNode(context, attributeName, node, parentNode, parentN suggest: [ Object.assign( getMessageData('suggestRemoveWhitespaces', messages.suggestRemoveWhitespaces), - { fix(fixer) { return fixer.removeRange(whitespacePart.range); } } + { + data: { attributeName }, + fix(fixer) { return fixer.removeRange(whitespacePart.range); }, + } ), ], }); @@ -371,7 +396,10 @@ function checkLiteralValueNode(context, attributeName, node, parentNode, parentN suggest: [ Object.assign( getMessageData('suggestRemoveWhitespaces', messages.suggestRemoveWhitespaces), - { fix(fixer) { return fixer.replaceTextRange(whitespacePart.range, '\u0020'); } } + { + data: { attributeName }, + fix(fixer) { return fixer.replaceTextRange(whitespacePart.range, '\u0020'); }, + } ), ], }); @@ -390,16 +418,21 @@ function checkAttribute(context, node) { COMPONENT_ATTRIBUTE_MAP.get(attribute).values(), (tagName) => `"<${tagName}>"` ).join(', '); + const data = { + attributeName: attribute, + tagNames, + }; + report(context, messages.onlyMeaningfulFor, 'onlyMeaningfulFor', { node: node.name, - data: { - attributeName: attribute, - tagNames, - }, + data, suggest: [ Object.assign( getMessageData('suggestRemoveDefault', messages.suggestRemoveDefault), - { fix(fixer) { return fixer.remove(node); } } + { + data, + fix(fixer) { return fixer.remove(node); }, + } ), ], }); @@ -409,13 +442,15 @@ function checkAttribute(context, node) { function fix(fixer) { return fixer.remove(node); } if (!node.value) { + const data = { attributeName: attribute }; + report(context, messages.emptyIsMeaningless, 'emptyIsMeaningless', { node: node.name, - data: { attributeName: attribute }, + data, suggest: [ Object.assign( getMessageData('suggestRemoveEmpty', messages.suggestRemoveEmpty), - { fix } + { data, fix } ), ], }); @@ -435,24 +470,28 @@ function checkAttribute(context, node) { } if (node.value.expression.type === 'ObjectExpression') { + const data = { attributeName: attribute }; + report(context, messages.onlyStrings, 'onlyStrings', { node: node.value, - data: { attributeName: attribute }, + data, suggest: [ Object.assign( getMessageData('suggestRemoveDefault', messages.suggestRemoveDefault), - { fix } + { data, fix } ), ], }); } else if (node.value.expression.type === 'Identifier' && node.value.expression.name === 'undefined') { + const data = { attributeName: attribute }; + report(context, messages.onlyStrings, 'onlyStrings', { node: node.value, - data: { attributeName: attribute }, + data, suggest: [ Object.assign( getMessageData('suggestRemoveDefault', messages.suggestRemoveDefault), - { fix } + { data, fix } ), ], }); @@ -476,16 +515,21 @@ function checkPropValidValue(context, node, value, attribute) { const validTagSet = validTags.get(value.value); if (!validTagSet) { + const data = { + attributeName: attribute, + reportingValue: value.value, + }; + report(context, messages.neverValid, 'neverValid', { node: value, - data: { - attributeName: attribute, - reportingValue: value.value, - }, + data, suggest: [ Object.assign( getMessageData('suggestRemoveInvalid', messages.suggestRemoveInvalid), - { fix(fixer) { return fixer.replaceText(value, value.raw.replace(value.value, '')); } } + { + data, + fix(fixer) { return fixer.replaceText(value, value.raw.replace(value.value, '')); }, + } ), ], }); diff --git a/lib/rules/no-is-mounted.js b/lib/rules/no-is-mounted.js index c15b6e81a4..fd99d5b683 100644 --- a/lib/rules/no-is-mounted.js +++ b/lib/rules/no-is-mounted.js @@ -6,6 +6,7 @@ 'use strict'; const docsUrl = require('../util/docsUrl'); +const getAncestors = require('../util/eslint').getAncestors; const report = require('../util/report'); // ------------------------------------------------------------------------------ @@ -40,7 +41,7 @@ module.exports = { if (callee.object.type !== 'ThisExpression' || callee.property.name !== 'isMounted') { return; } - const ancestors = context.getAncestors(callee); + const ancestors = getAncestors(context, node); for (let i = 0, j = ancestors.length; i < j; i++) { if (ancestors[i].type === 'Property' || ancestors[i].type === 'MethodDefinition') { report(context, messages.noIsMounted, 'noIsMounted', { diff --git a/lib/rules/no-namespace.js b/lib/rules/no-namespace.js index d7559f5ebd..20ca5d9324 100644 --- a/lib/rules/no-namespace.js +++ b/lib/rules/no-namespace.js @@ -36,7 +36,7 @@ module.exports = { create(context) { return { CallExpression(node) { - if (isCreateElement(node, context) && node.arguments.length > 0 && node.arguments[0].type === 'Literal') { + if (isCreateElement(context, node) && node.arguments.length > 0 && node.arguments[0].type === 'Literal') { const name = node.arguments[0].value; if (typeof name !== 'string' || name.indexOf(':') === -1) return undefined; report(context, messages.noNamespace, 'noNamespace', { diff --git a/lib/rules/no-set-state.js b/lib/rules/no-set-state.js index c88db30d7e..199e922b75 100644 --- a/lib/rules/no-set-state.js +++ b/lib/rules/no-set-state.js @@ -68,7 +68,7 @@ module.exports = { ) { return; } - const component = components.get(utils.getParentComponent()); + const component = components.get(utils.getParentComponent(node)); const setStateUsages = (component && component.setStateUsages) || []; setStateUsages.push(callee); components.set(node, { diff --git a/lib/rules/no-string-refs.js b/lib/rules/no-string-refs.js index 466a214579..09ce286685 100644 --- a/lib/rules/no-string-refs.js +++ b/lib/rules/no-string-refs.js @@ -50,7 +50,7 @@ module.exports = { */ function isRefsUsage(node) { return !!( - (componentUtil.getParentES6Component(context) || componentUtil.getParentES5Component(context)) + (componentUtil.getParentES6Component(context, node) || componentUtil.getParentES5Component(context, node)) && node.object.type === 'ThisExpression' && node.property.name === 'refs' ); diff --git a/lib/rules/no-this-in-sfc.js b/lib/rules/no-this-in-sfc.js index cf9eb99bd4..c520abd31c 100644 --- a/lib/rules/no-this-in-sfc.js +++ b/lib/rules/no-this-in-sfc.js @@ -34,7 +34,7 @@ module.exports = { create: Components.detect((context, components, utils) => ({ MemberExpression(node) { if (node.object.type === 'ThisExpression') { - const component = components.get(utils.getParentStatelessComponent()); + const component = components.get(utils.getParentStatelessComponent(node)); if (!component || (component.node && component.node.parent && component.node.parent.type === 'Property')) { return; } diff --git a/lib/rules/no-unescaped-entities.js b/lib/rules/no-unescaped-entities.js index a449ad5206..ecf1abc5e1 100644 --- a/lib/rules/no-unescaped-entities.js +++ b/lib/rules/no-unescaped-entities.js @@ -6,6 +6,7 @@ 'use strict'; const docsUrl = require('../util/docsUrl'); +const getSourceCode = require('../util/eslint').getSourceCode; const jsxUtil = require('../util/jsx'); const report = require('../util/report'); @@ -83,9 +84,9 @@ module.exports = { const entities = configuration.forbid || DEFAULTS; // HTML entities are already escaped in node.value (as well as node.raw), - // so pull the raw text from context.getSourceCode() + // so pull the raw text from getSourceCode(context) for (let i = node.loc.start.line; i <= node.loc.end.line; i++) { - let rawLine = context.getSourceCode().lines[i - 1]; + let rawLine = getSourceCode(context).lines[i - 1]; let start = 0; let end = rawLine.length; if (i === node.loc.start.line) { diff --git a/lib/rules/no-unknown-property.js b/lib/rules/no-unknown-property.js index 9491f9c658..3e8c6de111 100644 --- a/lib/rules/no-unknown-property.js +++ b/lib/rules/no-unknown-property.js @@ -7,6 +7,7 @@ const has = require('object.hasown/polyfill')(); const docsUrl = require('../util/docsUrl'); +const getText = require('../util/eslint').getText; const testReactVersion = require('../util/version').testReactVersion; const report = require('../util/report'); @@ -555,7 +556,7 @@ module.exports = { return { JSXAttribute(node) { const ignoreNames = getIgnoreConfig(); - const actualName = context.getSourceCode().getText(node.name); + const actualName = getText(context, node.name); if (ignoreNames.indexOf(actualName) >= 0) { return; } diff --git a/lib/rules/no-unstable-nested-components.js b/lib/rules/no-unstable-nested-components.js index 2ef635efab..c1dd606b6a 100644 --- a/lib/rules/no-unstable-nested-components.js +++ b/lib/rules/no-unstable-nested-components.js @@ -68,7 +68,7 @@ function isCreateElementMatcher(node, context) { return ( node && node.type === 'CallExpression' - && isCreateElement(node, context) + && isCreateElement(context, node) ); } @@ -306,7 +306,7 @@ module.exports = { * @returns {Boolean} True if node is inside class component's render block, false if not */ function isInsideRenderMethod(node) { - const parentComponent = utils.getParentComponent(); + const parentComponent = utils.getParentComponent(node); if (!parentComponent || parentComponent.type !== 'ClassDeclaration') { return false; @@ -334,8 +334,8 @@ module.exports = { * @returns {Boolean} True if given node a function component declared inside class component, false if not */ function isFunctionComponentInsideClassComponent(node) { - const parentComponent = utils.getParentComponent(); - const parentStatelessComponent = utils.getParentStatelessComponent(); + const parentComponent = utils.getParentComponent(node); + const parentStatelessComponent = utils.getParentStatelessComponent(node); return ( parentComponent diff --git a/lib/rules/no-unused-state.js b/lib/rules/no-unused-state.js index 73a0e52163..0ed480694d 100644 --- a/lib/rules/no-unused-state.js +++ b/lib/rules/no-unused-state.js @@ -13,6 +13,7 @@ const docsUrl = require('../util/docsUrl'); const ast = require('../util/ast'); const componentUtil = require('../util/componentUtil'); const report = require('../util/report'); +const getScope = require('../util/eslint').getScope; // Descend through all wrapping TypeCastExpressions and return the expression // that was cast. @@ -107,7 +108,7 @@ module.exports = { 'componentDidUpdate', ]; - let scope = context.getScope(); + let scope = getScope(context, node); while (scope) { const parent = scope.block && scope.block.parent; if ( @@ -368,7 +369,7 @@ module.exports = { return; } - const childScope = context.getScope().childScopes.find((x) => x.block === node.value); + const childScope = getScope(context, node).childScopes.find((x) => x.block === node.value); if (!childScope) { return; } diff --git a/lib/rules/prefer-exact-props.js b/lib/rules/prefer-exact-props.js index 4ad088e9ee..6d7db27d0f 100644 --- a/lib/rules/prefer-exact-props.js +++ b/lib/rules/prefer-exact-props.js @@ -10,6 +10,7 @@ const propsUtil = require('../util/props'); const propWrapperUtil = require('../util/propWrapper'); const variableUtil = require('../util/variable'); const report = require('../util/report'); +const getText = require('../util/eslint').getText; // ----------------------------------------------------------------------------- // Rule Definition @@ -36,7 +37,6 @@ module.exports = { create: Components.detect((context, components, utils) => { const typeAliases = {}; const exactWrappers = propWrapperUtil.getExactPropWrapperFunctions(context); - const sourceCode = context.getSourceCode(); function getPropTypesErrorMessage() { const formattedWrappers = propWrapperUtil.formatPropWrapperFunctions(exactWrappers); @@ -83,7 +83,7 @@ module.exports = { return ( node && node.type === 'CallExpression' - && !propWrapperUtil.isExactPropWrapperFunction(context, sourceCode.getText(node.callee)) + && !propWrapperUtil.isExactPropWrapperFunction(context, getText(context, node.callee)) ); } @@ -149,7 +149,7 @@ module.exports = { reportPropTypesError(node); } else if (right.type === 'Identifier') { const identifier = right.name; - const propsDefinition = variableUtil.findVariableByName(context, identifier); + const propsDefinition = variableUtil.findVariableByName(context, node, identifier); if (isNonEmptyObjectExpression(propsDefinition)) { reportPropTypesError(node); } else if (isNonExactPropWrapperFunction(propsDefinition)) { diff --git a/lib/rules/prefer-stateless-function.js b/lib/rules/prefer-stateless-function.js index 07c435747f..bfbabe76be 100644 --- a/lib/rules/prefer-stateless-function.js +++ b/lib/rules/prefer-stateless-function.js @@ -15,6 +15,10 @@ const astUtil = require('../util/ast'); const componentUtil = require('../util/componentUtil'); const docsUrl = require('../util/docsUrl'); const report = require('../util/report'); +const eslintUtil = require('../util/eslint'); + +const getScope = eslintUtil.getScope; +const getText = eslintUtil.getText; // ------------------------------------------------------------------------------ // Rule Definition @@ -332,7 +336,7 @@ module.exports = { // Mark `ref` usage JSXAttribute(node) { - const name = context.getSourceCode().getText(node.name); + const name = getText(context, node.name); if (name !== 'ref') { return; } @@ -342,7 +346,7 @@ module.exports = { // Mark `render` that do not return some JSX ReturnStatement(node) { let blockNode; - let scope = context.getScope(); + let scope = getScope(context, node); while (scope) { blockNode = scope.block && scope.block.parent; if (blockNode && (blockNode.type === 'MethodDefinition' || blockNode.type === 'Property')) { diff --git a/lib/rules/prop-types.js b/lib/rules/prop-types.js index 07adca104a..eb6bab1c09 100644 --- a/lib/rules/prop-types.js +++ b/lib/rules/prop-types.js @@ -74,11 +74,11 @@ module.exports = { /** * Checks if the component must be validated * @param {Object} component The component to process - * @returns {Boolean} True if the component must be validated, false if not. + * @returns {boolean} True if the component must be validated, false if not. */ function mustBeValidated(component) { const isSkippedByConfig = skipUndeclared && typeof component.declaredPropTypes === 'undefined'; - return Boolean( + return !!( component && component.usedPropTypes && !component.ignorePropsValidation @@ -90,7 +90,7 @@ module.exports = { * Internal: Checks if the prop is declared * @param {Object} declaredPropTypes Description of propTypes declared in the current component * @param {String[]} keyList Dot separated name of the prop to check. - * @returns {Boolean} True if the prop is declared, false if not. + * @returns {boolean} True if the prop is declared, false if not. */ function internalIsDeclaredInComponent(declaredPropTypes, keyList) { for (let i = 0, j = keyList.length; i < j; i++) { diff --git a/lib/rules/react-in-jsx-scope.js b/lib/rules/react-in-jsx-scope.js index 1af398deae..f2d8bc4837 100644 --- a/lib/rules/react-in-jsx-scope.js +++ b/lib/rules/react-in-jsx-scope.js @@ -37,7 +37,7 @@ module.exports = { const pragma = pragmaUtil.getFromContext(context); function checkIfReactIsInScope(node) { - const variables = variableUtil.variablesInScope(context); + const variables = variableUtil.variablesInScope(context, node); if (variableUtil.findVariable(variables, pragma)) { return; } diff --git a/lib/rules/require-optimization.js b/lib/rules/require-optimization.js index 9d440b19ee..dc0b60f73a 100644 --- a/lib/rules/require-optimization.js +++ b/lib/rules/require-optimization.js @@ -11,6 +11,7 @@ const Components = require('../util/Components'); const componentUtil = require('../util/componentUtil'); const docsUrl = require('../util/docsUrl'); const report = require('../util/report'); +const getScope = require('../util/eslint').getScope; const messages = { noShouldComponentUpdate: 'Component is not optimized. Please add a shouldComponentUpdate method.', @@ -154,11 +155,12 @@ module.exports = { /** * Checks if we are declaring function in class - * @returns {Boolean} True if we are declaring function in class, false if not. + * @param {ASTNode} node + * @returns {boolean} True if we are declaring function in class, false if not. */ - function isFunctionInClass() { + function isFunctionInClass(node) { let blockNode; - let scope = context.getScope(); + let scope = getScope(context, node); while (scope) { blockNode = scope.block; if (blockNode && blockNode.type === 'ClassDeclaration') { @@ -173,7 +175,7 @@ module.exports = { return { ArrowFunctionExpression(node) { // Skip if the function is declared in the class - if (isFunctionInClass()) { + if (isFunctionInClass(node)) { return; } // Stateless Functional Components cannot be optimized (yet) @@ -193,7 +195,7 @@ module.exports = { FunctionDeclaration(node) { // Skip if the function is declared in the class - if (isFunctionInClass()) { + if (isFunctionInClass(node)) { return; } // Stateless Functional Components cannot be optimized (yet) @@ -202,7 +204,7 @@ module.exports = { FunctionExpression(node) { // Skip if the function is declared in the class - if (isFunctionInClass()) { + if (isFunctionInClass(node)) { return; } // Stateless Functional Components cannot be optimized (yet) diff --git a/lib/rules/require-render-return.js b/lib/rules/require-render-return.js index d79c9406b7..c46613e420 100644 --- a/lib/rules/require-render-return.js +++ b/lib/rules/require-render-return.js @@ -12,6 +12,7 @@ const astUtil = require('../util/ast'); const componentUtil = require('../util/componentUtil'); const docsUrl = require('../util/docsUrl'); const report = require('../util/report'); +const getAncestors = require('../util/eslint').getAncestors; // ------------------------------------------------------------------------------ // Rule Definition @@ -61,7 +62,7 @@ module.exports = { return { ReturnStatement(node) { - const ancestors = context.getAncestors(node).reverse(); + const ancestors = getAncestors(context, node).reverse(); let depth = 0; ancestors.forEach((ancestor) => { if (/Function(Expression|Declaration)$/.test(ancestor.type)) { diff --git a/lib/rules/sort-default-props.js b/lib/rules/sort-default-props.js index aae419ba18..aca868f95d 100644 --- a/lib/rules/sort-default-props.js +++ b/lib/rules/sort-default-props.js @@ -9,6 +9,10 @@ const variableUtil = require('../util/variable'); const docsUrl = require('../util/docsUrl'); const report = require('../util/report'); +const eslintUtil = require('../util/eslint'); + +const getFirstTokens = eslintUtil.getFirstTokens; +const getText = eslintUtil.getText; // ------------------------------------------------------------------------------ // Rule Definition @@ -60,7 +64,7 @@ module.exports = { // (babel-eslint@5 does not expose property name so we have to rely on tokens) } if (node.type === 'ClassProperty') { - const tokens = context.getSourceCode().getFirstTokens(node, 2); + const tokens = getFirstTokens(context, node, 2); return tokens[1] && tokens[1].type === 'Identifier' ? tokens[1].value : tokens[0].value; } return ''; @@ -77,16 +81,18 @@ module.exports = { } function getKey(node) { - return context.getSourceCode().getText(node.key || node.argument); + return getText(context, node.key || node.argument); } /** * Find a variable by name in the current scope. + * @param {ASTNode} node The node to look for. * @param {string} name Name of the variable to look for. * @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise. */ - function findVariableByName(name) { - const variable = variableUtil.variablesInScope(context).find((item) => item.name === name); + function findVariableByName(node, name) { + const variable = variableUtil.variablesInScope(context, node) + .find((item) => item.name === name); if (!variable || !variable.defs[0] || !variable.defs[0].node) { return null; @@ -106,7 +112,7 @@ module.exports = { */ function checkSorted(declarations) { // function fix(fixer) { - // return propTypesSortUtil.fixPropTypesSort(fixer, context, declarations, ignoreCase); + // return propTypesSortUtil.fixPropTypesSort(context, fixer, declarations, ignoreCase); // } declarations.reduce((prev, curr, idx, decls) => { @@ -142,7 +148,7 @@ module.exports = { if (node.type === 'ObjectExpression') { checkSorted(node.properties); } else if (node.type === 'Identifier') { - const propTypesObject = findVariableByName(node.name); + const propTypesObject = findVariableByName(node, node.name); if (propTypesObject && propTypesObject.properties) { checkSorted(propTypesObject.properties); } diff --git a/lib/rules/sort-prop-types.js b/lib/rules/sort-prop-types.js index 8c3574af4a..808dd1df1f 100644 --- a/lib/rules/sort-prop-types.js +++ b/lib/rules/sort-prop-types.js @@ -10,6 +10,10 @@ const docsUrl = require('../util/docsUrl'); const propWrapperUtil = require('../util/propWrapper'); const propTypesSortUtil = require('../util/propTypesSort'); const report = require('../util/report'); +const eslintUtil = require('../util/eslint'); + +const getSourceCode = eslintUtil.getSourceCode; +const getText = eslintUtil.getText; // ------------------------------------------------------------------------------ // Rule Definition @@ -23,12 +27,12 @@ const messages = { function getKey(context, node) { if (node.type === 'ObjectTypeProperty') { - return context.getSourceCode().getFirstToken(node).value; + return getSourceCode(context).getFirstToken(node).value; } if (node.key && node.key.value) { return node.key.value; } - return context.getSourceCode().getText(node.key || node.argument); + return getText(context, node.key || node.argument); } function getValueName(node) { @@ -118,8 +122,8 @@ module.exports = { function fix(fixer) { return propTypesSortUtil.fixPropTypesSort( - fixer, context, + fixer, declarations, ignoreCase, requiredFirst, @@ -207,7 +211,7 @@ module.exports = { checkSorted(node.properties); break; case 'Identifier': { - const propTypesObject = variableUtil.findVariableByName(context, node.name); + const propTypesObject = variableUtil.findVariableByName(context, node, node.name); if (propTypesObject && propTypesObject.properties) { checkSorted(propTypesObject.properties); } @@ -260,7 +264,7 @@ module.exports = { if (firstArg.properties) { checkSorted(firstArg.properties); } else if (firstArg.type === 'Identifier') { - const variable = variableUtil.findVariableByName(context, firstArg.name); + const variable = variableUtil.findVariableByName(context, node, firstArg.name); if (variable && variable.properties) { checkSorted(variable.properties); } diff --git a/lib/rules/state-in-constructor.js b/lib/rules/state-in-constructor.js index c518b1713a..9dd756b4d2 100644 --- a/lib/rules/state-in-constructor.js +++ b/lib/rules/state-in-constructor.js @@ -44,7 +44,7 @@ module.exports = { option === 'always' && !node.static && node.key.name === 'state' - && componentUtil.getParentES6Component(context) + && componentUtil.getParentES6Component(context, node) ) { report(context, messages.stateInitConstructor, 'stateInitConstructor', { node, @@ -55,8 +55,8 @@ module.exports = { if ( option === 'never' && componentUtil.isStateMemberExpression(node.left) - && astUtil.inConstructor(context) - && componentUtil.getParentES6Component(context) + && astUtil.inConstructor(context, node) + && componentUtil.getParentES6Component(context, node) ) { report(context, messages.stateInitClassProp, 'stateInitClassProp', { node, diff --git a/lib/rules/static-property-placement.js b/lib/rules/static-property-placement.js index efc50da3bd..7baf2faeb4 100644 --- a/lib/rules/static-property-placement.js +++ b/lib/rules/static-property-placement.js @@ -12,6 +12,7 @@ const astUtil = require('../util/ast'); const componentUtil = require('../util/componentUtil'); const propsUtil = require('../util/props'); const report = require('../util/report'); +const getScope = require('../util/eslint').getScope; // ------------------------------------------------------------------------------ // Positioning Options @@ -97,11 +98,12 @@ module.exports = { /** * Checks if we are declaring context in class + * @param {ASTNode} node * @returns {Boolean} True if we are declaring context in class, false if not. */ - function isContextInClass() { + function isContextInClass(node) { let blockNode; - let scope = context.getScope(); + let scope = getScope(context, node); while (scope) { blockNode = scope.block; if (blockNode && blockNode.type === 'ClassDeclaration') { @@ -149,7 +151,7 @@ module.exports = { // ---------------------------------------------------------------------- return { 'ClassProperty, PropertyDefinition'(node) { - if (!componentUtil.getParentES6Component(context)) { + if (!componentUtil.getParentES6Component(context, node)) { return; } @@ -160,7 +162,7 @@ module.exports = { // If definition type is undefined then it must not be a defining expression or if the definition is inside a // class body then skip this node. const right = node.parent.right; - if (!right || right.type === 'undefined' || isContextInClass()) { + if (!right || right.type === 'undefined' || isContextInClass(node)) { return; } @@ -178,7 +180,11 @@ module.exports = { MethodDefinition(node) { // If the function is inside a class and is static getter then check if correctly positioned - if (componentUtil.getParentES6Component(context) && node.static && node.kind === 'get') { + if ( + componentUtil.getParentES6Component(context, node) + && node.static + && node.kind === 'get' + ) { // Report error if needed reportNodeIncorrectlyPositioned(node, STATIC_GETTER); } diff --git a/lib/rules/style-prop-object.js b/lib/rules/style-prop-object.js index 4d6684a6f1..4f77beccb2 100644 --- a/lib/rules/style-prop-object.js +++ b/lib/rules/style-prop-object.js @@ -61,7 +61,8 @@ module.exports = { * @param {object} node A Identifier node */ function checkIdentifiers(node) { - const variable = variableUtil.variablesInScope(context).find((item) => item.name === node.name); + const variable = variableUtil.variablesInScope(context, node) + .find((item) => item.name === node.name); if (!variable || !variable.defs[0] || !variable.defs[0].node.init) { return; @@ -77,7 +78,7 @@ module.exports = { return { CallExpression(node) { if ( - isCreateElement(node, context) + isCreateElement(context, node) && node.arguments.length > 1 ) { if (node.arguments[0].name) { diff --git a/lib/rules/void-dom-elements-no-children.js b/lib/rules/void-dom-elements-no-children.js index 721ffae872..a8a6ba0149 100644 --- a/lib/rules/void-dom-elements-no-children.js +++ b/lib/rules/void-dom-elements-no-children.js @@ -108,7 +108,7 @@ module.exports = { return; } - if (!isCreateElement(node, context)) { + if (!isCreateElement(context, node)) { return; } diff --git a/lib/util/Components.js b/lib/util/Components.js index a31989702f..67fac5afc5 100644 --- a/lib/util/Components.js +++ b/lib/util/Components.js @@ -21,6 +21,10 @@ const usedPropTypesUtil = require('./usedPropTypes'); const defaultPropsUtil = require('./defaultProps'); const isFirstLetterCapitalized = require('./isFirstLetterCapitalized'); const isDestructuredFromPragmaImport = require('./isDestructuredFromPragmaImport'); +const eslintUtil = require('./eslint'); + +const getScope = eslintUtil.getScope; +const getText = eslintUtil.getText; function getId(node) { return node ? `${node.range[0]}:${node.range[1]}` : ''; @@ -282,7 +286,6 @@ function mergeRules(rules) { function componentRule(rule, context) { const pragma = pragmaUtil.getFromContext(context); - const sourceCode = context.getSourceCode(); const components = new Components(); const wrapperFunctions = getWrapperFunctions(context, pragma); @@ -291,19 +294,25 @@ function componentRule(rule, context) { /** * Check if variable is destructured from pragma import * + * @param {ASTNode} node The AST node to check * @param {string} variable The variable name to check - * @returns {Boolean} True if createElement is destructured from the pragma + * @returns {boolean} True if createElement is destructured from the pragma */ - isDestructuredFromPragmaImport(variable) { - return isDestructuredFromPragmaImport(variable, context); + isDestructuredFromPragmaImport(node, variable) { + return isDestructuredFromPragmaImport(context, node, variable); }, + /** + * @param {ASTNode} ASTNode + * @param {boolean=} strict + * @returns {boolean} + */ isReturningJSX(ASTNode, strict) { - return jsxUtil.isReturningJSX(ASTNode, context, strict, true); + return jsxUtil.isReturningJSX(context, ASTNode, strict, true); }, isReturningJSXOrNull(ASTNode, strict) { - return jsxUtil.isReturningJSX(ASTNode, context, strict); + return jsxUtil.isReturningJSX(context, ASTNode, strict); }, isReturningOnlyNull(ASTNode) { @@ -412,7 +421,7 @@ function componentRule(rule, context) { return wrapperFunction.property === node.callee.name && (!wrapperFunction.object // Functions coming from the current pragma need special handling - || (wrapperFunction.object === pragma && this.isDestructuredFromPragmaImport(node.callee.name)) + || (wrapperFunction.object === pragma && this.isDestructuredFromPragmaImport(node, node.callee.name)) ); }); }, @@ -426,14 +435,15 @@ function componentRule(rule, context) { /** * Get the parent component node from the current scope + * @param {ASTNode} node * * @returns {ASTNode} component node, null if we are not in a component */ - getParentComponent() { + getParentComponent(node) { return ( - componentUtil.getParentES6Component(context) - || componentUtil.getParentES5Component(context) - || utils.getParentStatelessComponent() + componentUtil.getParentES6Component(context, node) + || componentUtil.getParentES5Component(context, node) + || utils.getParentStatelessComponent(node) ); }, @@ -612,13 +622,13 @@ function componentRule(rule, context) { /** * Get the parent stateless component node from the current scope * + * @param {ASTNode} node The AST node being checked * @returns {ASTNode} component node, null if we are not in a component */ - getParentStatelessComponent() { - let scope = context.getScope(); + getParentStatelessComponent(node) { + let scope = getScope(context, node); while (scope) { - const node = scope.block; - const statelessComponent = utils.getStatelessComponent(node); + const statelessComponent = utils.getStatelessComponent(scope.block); if (statelessComponent) { return statelessComponent; } @@ -641,14 +651,15 @@ function componentRule(rule, context) { let componentNode; // Get the component path const componentPath = []; - while (node) { - if (node.property && node.property.type === 'Identifier') { - componentPath.push(node.property.name); + let nodeTemp = node; + while (nodeTemp) { + if (nodeTemp.property && nodeTemp.property.type === 'Identifier') { + componentPath.push(nodeTemp.property.name); } - if (node.object && node.object.type === 'Identifier') { - componentPath.push(node.object.name); + if (nodeTemp.object && nodeTemp.object.type === 'Identifier') { + componentPath.push(nodeTemp.object.name); } - node = node.object; + nodeTemp = nodeTemp.object; } componentPath.reverse(); const componentName = componentPath.slice(0, componentPath.length - 1).join('.'); @@ -659,7 +670,7 @@ function componentRule(rule, context) { return null; } let variableInScope; - const variables = variableUtil.variablesInScope(context); + const variables = variableUtil.variablesInScope(context, node); for (i = 0, j = variables.length; i < j; i++) { if (variables[i].name === variableName) { variableInScope = variables[i]; @@ -676,7 +687,7 @@ function componentRule(rule, context) { if (refId.parent && refId.parent.type === 'MemberExpression') { refId = refId.parent; } - if (sourceCode.getText(refId) !== componentName) { + if (getText(context, refId) !== componentName) { return false; } if (refId.type === 'MemberExpression') { @@ -776,7 +787,7 @@ function componentRule(rule, context) { && node.callee.type === 'Identifier' && node.callee.name.match(USE_HOOK_PREFIX_REGEX); - const scope = (isPotentialReactHookCall || isPotentialHookCall) && context.getScope(); + const scope = (isPotentialReactHookCall || isPotentialHookCall) && getScope(context, node); const reactResolvedDefs = isPotentialReactHookCall && scope.references @@ -888,7 +899,7 @@ function componentRule(rule, context) { }, ThisExpression(node) { - const component = utils.getParentStatelessComponent(); + const component = utils.getParentStatelessComponent(node); if (!component || !/Function/.test(component.type) || !node.parent.property) { return; } diff --git a/lib/util/annotations.js b/lib/util/annotations.js index 60aaef8cd0..24b18074c0 100644 --- a/lib/util/annotations.js +++ b/lib/util/annotations.js @@ -6,6 +6,8 @@ 'use strict'; +const getFirstTokens = require('./eslint').getFirstTokens; + /** * Checks if we are declaring a `props` argument with a flow type annotation. * @param {ASTNode} node The AST node being checked. @@ -19,7 +21,7 @@ function isAnnotatedFunctionPropsDeclaration(node, context) { const typeNode = node.params[0].type === 'AssignmentPattern' ? node.params[0].left : node.params[0]; - const tokens = context.getFirstTokens(typeNode, 2); + const tokens = getFirstTokens(context, typeNode, 2); const isAnnotated = typeNode.typeAnnotation; const isDestructuredProps = typeNode.type === 'ObjectPattern'; const isProps = tokens[0].value === 'props' || (tokens[1] && tokens[1].value === 'props'); diff --git a/lib/util/ast.js b/lib/util/ast.js index fd6019a3f2..5664dcb512 100644 --- a/lib/util/ast.js +++ b/lib/util/ast.js @@ -5,6 +5,11 @@ 'use strict'; const estraverse = require('estraverse'); +const eslintUtil = require('./eslint'); + +const getFirstTokens = eslintUtil.getFirstTokens; +const getScope = eslintUtil.getScope; +const getSourceCode = eslintUtil.getSourceCode; // const pragmaUtil = require('./pragma'); /** @@ -186,7 +191,7 @@ function getComponentProperties(node) { * @return {ASTNode} the first node in the line */ function getFirstNodeInLine(context, node) { - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); let token = node; let lines; do { @@ -253,10 +258,11 @@ function isClass(node) { /** * Check if we are in a class constructor * @param {Context} context + * @param {ASTNode} node The AST node being checked. * @return {boolean} */ -function inConstructor(context) { - let scope = context.getScope(); +function inConstructor(context, node) { + let scope = getScope(context, node); while (scope) { // @ts-ignore if (scope.block && scope.block.parent && scope.block.parent.kind === 'constructor') { @@ -284,7 +290,7 @@ function stripQuotes(string) { */ function getKeyValue(context, node) { if (node.type === 'ObjectTypeProperty') { - const tokens = context.getSourceCode().getFirstTokens(node, 2); + const tokens = getFirstTokens(context, node, 2); return (tokens[0].value === '+' || tokens[0].value === '-' ? tokens[1].value : stripQuotes(tokens[0].value) @@ -311,7 +317,7 @@ function getKeyValue(context, node) { * @returns {boolean} */ function isParenthesized(context, node) { - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); const previousToken = sourceCode.getTokenBefore(node); const nextToken = sourceCode.getTokenAfter(node); diff --git a/lib/util/componentUtil.js b/lib/util/componentUtil.js index 35d54edcc5..f98762a49a 100644 --- a/lib/util/componentUtil.js +++ b/lib/util/componentUtil.js @@ -2,6 +2,11 @@ const doctrine = require('doctrine'); const pragmaUtil = require('./pragma'); +const eslintUtil = require('./eslint'); + +const getScope = eslintUtil.getScope; +const getSourceCode = eslintUtil.getSourceCode; +const getText = eslintUtil.getText; // eslint-disable-next-line valid-jsdoc /** @@ -57,7 +62,7 @@ function isES5Component(node, context) { * @returns {boolean} */ function isExplicitComponent(node, context) { - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); let comment; // Sometimes the passed node may not have been parsed yet by eslint, and this function call crashes. // Can be removed when eslint sets "parent" property for all nodes on initial AST traversal: https://github.com/eslint/eslint-scope/issues/27 @@ -116,13 +121,14 @@ function isES6Component(node, context) { /** * Get the parent ES5 component node from the current scope * @param {Context} context + * @param {ASTNode} node * @returns {ASTNode|null} */ -function getParentES5Component(context) { - let scope = context.getScope(); +function getParentES5Component(context, node) { + let scope = getScope(context, node); while (scope) { // @ts-ignore - const node = scope.block && scope.block.parent && scope.block.parent.parent; + node = scope.block && scope.block.parent && scope.block.parent.parent; if (node && isES5Component(node, context)) { return node; } @@ -134,14 +140,15 @@ function getParentES5Component(context) { /** * Get the parent ES6 component node from the current scope * @param {Context} context + * @param {ASTNode} node * @returns {ASTNode | null} */ -function getParentES6Component(context) { - let scope = context.getScope(); +function getParentES6Component(context, node) { + let scope = getScope(context, node); while (scope && scope.type !== 'class') { scope = scope.upper; } - const node = scope && scope.block; + node = scope && scope.block; if (!node || !isES6Component(node, context)) { return null; } @@ -156,9 +163,8 @@ function getParentES6Component(context) { */ function isPureComponent(node, context) { const pragma = getPragma(context); - const sourceCode = context.getSourceCode(); if (node.superClass) { - return new RegExp(`^(${pragma}\\.)?PureComponent$`).test(sourceCode.getText(node.superClass)); + return new RegExp(`^(${pragma}\\.)?PureComponent$`).test(getText(context, node.superClass)); } return false; } diff --git a/lib/util/defaultProps.js b/lib/util/defaultProps.js index 44b42454ce..cd14c7fa41 100644 --- a/lib/util/defaultProps.js +++ b/lib/util/defaultProps.js @@ -10,12 +10,11 @@ const componentUtil = require('./componentUtil'); const propsUtil = require('./props'); const variableUtil = require('./variable'); const propWrapperUtil = require('./propWrapper'); +const getText = require('./eslint').getText; const QUOTES_REGEX = /^["']|["']$/g; module.exports = function defaultPropsInstructions(context, components, utils) { - const sourceCode = context.getSourceCode(); - /** * Try to resolve the node passed in to a variable in the current scope. If the node passed in is not * an Identifier, then the node is simply returned. @@ -24,7 +23,7 @@ module.exports = function defaultPropsInstructions(context, components, utils) { */ function resolveNodeValue(node) { if (node.type === 'Identifier') { - return variableUtil.findVariableByName(context, node.name); + return variableUtil.findVariableByName(context, node, node.name); } if ( node.type === 'CallExpression' @@ -51,7 +50,7 @@ module.exports = function defaultPropsInstructions(context, components, utils) { } return objectExpression.properties.map((defaultProp) => ({ - name: sourceCode.getText(defaultProp.key).replace(QUOTES_REGEX, ''), + name: getText(context, defaultProp.key).replace(QUOTES_REGEX, ''), node: defaultProp, })); } @@ -172,7 +171,7 @@ module.exports = function defaultPropsInstructions(context, components, utils) { } // find component this propTypes/defaultProps belongs to - const component = components.get(componentUtil.getParentES6Component(context)); + const component = components.get(componentUtil.getParentES6Component(context, node)); if (!component) { return; } @@ -215,7 +214,7 @@ module.exports = function defaultPropsInstructions(context, components, utils) { } // find component this propTypes/defaultProps belongs to - const component = components.get(componentUtil.getParentES6Component(context)); + const component = components.get(componentUtil.getParentES6Component(context, node)); if (!component) { return; } diff --git a/lib/util/eslint.js b/lib/util/eslint.js new file mode 100644 index 0000000000..79a0537f3b --- /dev/null +++ b/lib/util/eslint.js @@ -0,0 +1,46 @@ +'use strict'; + +function getSourceCode(context) { + return context.getSourceCode ? context.getSourceCode() : context.sourceCode; +} + +function getAncestors(context, node) { + const sourceCode = getSourceCode(context); + return sourceCode.getAncestors ? sourceCode.getAncestors(node) : context.getAncestors(); +} + +function getScope(context, node) { + const sourceCode = getSourceCode(context); + if (sourceCode.getScope) { + return sourceCode.getScope(node); + } + + return context.getScope(); +} + +function markVariableAsUsed(name, node, context) { + const sourceCode = getSourceCode(context); + return sourceCode.markVariableAsUsed + ? sourceCode.markVariableAsUsed(name, node) + : context.markVariableAsUsed(name); +} + +function getFirstTokens(context, node, count) { + const sourceCode = getSourceCode(context); + return sourceCode.getFirstTokens ? sourceCode.getFirstTokens(node, count) : context.getFirstTokens(node, count); +} + +function getText(context) { + const sourceCode = getSourceCode(context); + const args = Array.prototype.slice.call(arguments, 1); + return sourceCode.getText ? sourceCode.getText.apply(sourceCode, args) : context.getSource.apply(context, args); +} + +module.exports = { + getAncestors, + getFirstTokens, + getScope, + getSourceCode, + getText, + markVariableAsUsed, +}; diff --git a/lib/util/isCreateElement.js b/lib/util/isCreateElement.js index c28dc563fb..9e531964fa 100644 --- a/lib/util/isCreateElement.js +++ b/lib/util/isCreateElement.js @@ -5,11 +5,11 @@ const isDestructuredFromPragmaImport = require('./isDestructuredFromPragmaImport /** * Checks if the node is a createElement call - * @param {ASTNode} node - The AST node being checked. * @param {Context} context - The AST node being checked. - * @returns {Boolean} - True if node is a createElement call object literal, False if not. + * @param {ASTNode} node - The AST node being checked. + * @returns {boolean} - True if node is a createElement call object literal, False if not. */ -module.exports = function isCreateElement(node, context) { +module.exports = function isCreateElement(context, node) { if ( node.callee && node.callee.type === 'MemberExpression' @@ -24,7 +24,7 @@ module.exports = function isCreateElement(node, context) { node && node.callee && node.callee.name === 'createElement' - && isDestructuredFromPragmaImport('createElement', context) + && isDestructuredFromPragmaImport(context, node, 'createElement') ) { return true; } diff --git a/lib/util/isDestructuredFromPragmaImport.js b/lib/util/isDestructuredFromPragmaImport.js index 6f8deb08bc..4d64596274 100644 --- a/lib/util/isDestructuredFromPragmaImport.js +++ b/lib/util/isDestructuredFromPragmaImport.js @@ -6,13 +6,14 @@ const variableUtil = require('./variable'); /** * Check if variable is destructured from pragma import * - * @param {string} variable The variable name to check * @param {Context} context eslint context - * @returns {Boolean} True if createElement is destructured from the pragma + * @param {ASTNode} node The AST node to check + * @param {string} variable The variable name to check + * @returns {boolean} True if createElement is destructured from the pragma */ -module.exports = function isDestructuredFromPragmaImport(variable, context) { +module.exports = function isDestructuredFromPragmaImport(context, node, variable) { const pragma = pragmaUtil.getFromContext(context); - const variables = variableUtil.variablesInScope(context); + const variables = variableUtil.variablesInScope(context, node); const variableInScope = variableUtil.getVariable(variables, variable); if (variableInScope) { const latestDef = variableUtil.getLatestVariableDefinition(variableInScope); diff --git a/lib/util/jsx.js b/lib/util/jsx.js index 55073bfe1e..07a09a8022 100644 --- a/lib/util/jsx.js +++ b/lib/util/jsx.js @@ -86,13 +86,13 @@ function isWhiteSpaces(value) { /** * Check if the node is returning JSX or null * - * @param {ASTNode} ASTnode The AST node being checked * @param {Context} context The context of `ASTNode`. + * @param {ASTNode} ASTnode The AST node being checked * @param {Boolean} [strict] If true, in a ternary condition the node must return JSX in both cases * @param {Boolean} [ignoreNull] If true, null return values will be ignored * @returns {Boolean} True if the node is returning JSX or null, false if not */ -function isReturningJSX(ASTnode, context, strict, ignoreNull) { +function isReturningJSX(context, ASTnode, strict, ignoreNull) { const isJSXValue = (node) => { if (!node) { return false; @@ -114,14 +114,14 @@ function isReturningJSX(ASTnode, context, strict, ignoreNull) { case 'JSXFragment': return true; case 'CallExpression': - return isCreateElement(node, context); + return isCreateElement(context, node); case 'Literal': if (!ignoreNull && node.value === null) { return true; } return false; case 'Identifier': { - const variable = variableUtil.findVariableByName(context, node.name); + const variable = variableUtil.findVariableByName(context, node, node.name); return isJSX(variable); } default: diff --git a/lib/util/makeNoMethodSetStateRule.js b/lib/util/makeNoMethodSetStateRule.js index 1225092802..faee6de163 100644 --- a/lib/util/makeNoMethodSetStateRule.js +++ b/lib/util/makeNoMethodSetStateRule.js @@ -9,6 +9,7 @@ const findLast = require('array.prototype.findlast'); const docsUrl = require('./docsUrl'); const report = require('./report'); +const getAncestors = require('./eslint').getAncestors; const testReactVersion = require('./version').testReactVersion; // ------------------------------------------------------------------------------ @@ -93,7 +94,7 @@ module.exports = function makeNoMethodSetStateRule(methodName, shouldCheckUnsafe ) { return; } - const ancestors = context.getAncestors(callee); + const ancestors = getAncestors(context, node); let depth = 0; findLast(ancestors, (ancestor) => { // ancestors.some((ancestor) => { diff --git a/lib/util/pragma.js b/lib/util/pragma.js index 2bde47fb37..4114442d05 100644 --- a/lib/util/pragma.js +++ b/lib/util/pragma.js @@ -5,6 +5,8 @@ 'use strict'; +const getSourceCode = require('./eslint').getSourceCode; + const JSX_ANNOTATION_REGEX = /@jsx\s+([^\s]+)/; // Does not check for reserved keywords or unicode characters const JS_IDENTIFIER_REGEX = /^[_$a-zA-Z][_$a-zA-Z0-9]*$/; @@ -48,7 +50,7 @@ function getFragmentFromContext(context) { function getFromContext(context) { let pragma = 'React'; - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); const pragmaNode = sourceCode.getAllComments().find((node) => JSX_ANNOTATION_REGEX.test(node.value)); if (pragmaNode) { diff --git a/lib/util/propTypes.js b/lib/util/propTypes.js index a189f871ca..6f879e2347 100644 --- a/lib/util/propTypes.js +++ b/lib/util/propTypes.js @@ -13,6 +13,12 @@ const testFlowVersion = require('./version').testFlowVersion; const propWrapperUtil = require('./propWrapper'); const astUtil = require('./ast'); const isFirstLetterCapitalized = require('./isFirstLetterCapitalized'); +const eslintUtil = require('./eslint'); + +const getFirstTokens = eslintUtil.getFirstTokens; +const getScope = eslintUtil.getScope; +const getSourceCode = eslintUtil.getSourceCode; +const getText = eslintUtil.getText; /** * Check if node is function type. @@ -361,14 +367,15 @@ module.exports = function propTypesInstructions(context, components, utils) { /** * Resolve node of type Identifier when building declaration types. * @param {ASTNode} node + * @param {ASTNode} rootNode * @param {Function} callback called with the resolved value only if resolved. */ - function resolveValueForIdentifierNode(node, callback) { + function resolveValueForIdentifierNode(node, rootNode, callback) { if ( node && node.type === 'Identifier' ) { - const scope = context.getScope(); + const scope = getScope(context, rootNode); const identVariable = scope.variableScope.variables.find( (variable) => variable.name === node.name ); @@ -384,10 +391,11 @@ module.exports = function propTypesInstructions(context, components, utils) { * The representation is used to verify nested used properties. * @param {ASTNode} value Node of the PropTypes for the desired property * @param {string} parentName + * @param {ASTNode} rootNode * @return {Object} The representation of the declaration, empty object means * the property is declared without the need for further analysis. */ - function buildReactDeclarationTypes(value, parentName) { + function buildReactDeclarationTypes(value, parentName, rootNode) { if ( value && value.callee @@ -409,7 +417,7 @@ module.exports = function propTypesInstructions(context, components, utils) { // propTypes = { // example: variableType // } - resolveValueForIdentifierNode(value, (newValue) => { + resolveValueForIdentifierNode(value, rootNode, (newValue) => { identNodeResolved = true; value = newValue; }); @@ -431,7 +439,7 @@ module.exports = function propTypesInstructions(context, components, utils) { // example: variableType.isRequired // } if (!identNodeResolved) { - resolveValueForIdentifierNode(value, (newValue) => { + resolveValueForIdentifierNode(value, rootNode, (newValue) => { value = newValue; }); } @@ -462,7 +470,7 @@ module.exports = function propTypesInstructions(context, components, utils) { iterateProperties(context, argument.properties, (childKey, childValue, propNode) => { if (childValue) { // skip spread propTypes const fullName = [parentName, childKey].join('.'); - const types = buildReactDeclarationTypes(childValue, fullName); + const types = buildReactDeclarationTypes(childValue, fullName, rootNode); types.fullName = fullName; types.name = childKey; types.node = propNode; @@ -474,7 +482,7 @@ module.exports = function propTypesInstructions(context, components, utils) { case 'arrayOf': case 'objectOf': { const fullName = [parentName, '*'].join('.'); - const child = buildReactDeclarationTypes(argument, fullName); + const child = buildReactDeclarationTypes(argument, fullName, rootNode); child.fullName = fullName; child.name = '__ANY_KEY__'; child.node = argument; @@ -497,7 +505,7 @@ module.exports = function propTypesInstructions(context, components, utils) { /** @type {UnionTypeDefinition} */ const unionTypeDefinition = { type: 'union', - children: argument.elements.map((element) => buildReactDeclarationTypes(element, parentName)), + children: argument.elements.map((element) => buildReactDeclarationTypes(element, parentName, rootNode)), }; if (unionTypeDefinition.children.length === 0) { // no complex type found, simply accept everything @@ -573,13 +581,14 @@ module.exports = function propTypesInstructions(context, components, utils) { } class DeclarePropTypesForTSTypeAnnotation { - constructor(propTypes, declaredPropTypes) { + constructor(propTypes, declaredPropTypes, rootNode) { this.propTypes = propTypes; this.declaredPropTypes = declaredPropTypes; this.foundDeclaredPropertiesList = []; this.referenceNameMap = new Set(); - this.sourceCode = context.getSourceCode(); + this.sourceCode = getSourceCode(context); this.shouldIgnorePropTypes = false; + this.rootNode = rootNode; this.visitTSNode(this.propTypes); this.endAndStructDeclaredPropTypes(); } @@ -609,7 +618,7 @@ module.exports = function propTypesInstructions(context, components, utils) { this.visitTSNode(typeAnnotation); } else if (astUtil.isTSTypeParameterInstantiation(node)) { if (Array.isArray(node.params)) { - node.params.forEach(this.visitTSNode, this); + node.params.forEach((x) => this.visitTSNode(x)); } } else { this.shouldIgnorePropTypes = true; @@ -655,7 +664,7 @@ module.exports = function propTypesInstructions(context, components, utils) { return; } if (typeName === 'ReturnType') { - this.convertReturnTypeToPropTypes(node); + this.convertReturnTypeToPropTypes(node, this.rootNode); return; } // Prevent recursive inheritance will cause maximum callstack. @@ -709,24 +718,24 @@ module.exports = function propTypesInstructions(context, components, utils) { this.visitTSNode(typeAnnotation); } if (Array.isArray(node.extends)) { - node.extends.forEach(this.visitTSNode, this); + node.extends.forEach((x) => this.visitTSNode(x)); // This line is trying to handle typescript-eslint-parser // typescript-eslint-parser extension is name as heritage } else if (Array.isArray(node.heritage)) { - node.heritage.forEach(this.visitTSNode, this); + node.heritage.forEach((x) => this.visitTSNode(x)); } } convertIntersectionTypeToPropTypes(node) { if (!node) return; if (Array.isArray(node.types)) { - node.types.forEach(this.visitTSNode, this); + node.types.forEach((x) => this.visitTSNode(x)); } else { this.shouldIgnorePropTypes = true; } } - convertReturnTypeToPropTypes(node) { + convertReturnTypeToPropTypes(node, rootNode) { // ReturnType should always have one parameter const nodeTypeParams = node.typeParameters; if (nodeTypeParams) { @@ -776,7 +785,7 @@ module.exports = function propTypesInstructions(context, components, utils) { this.shouldIgnorePropTypes = true; return; } - const types = buildReactDeclarationTypes(value, key); + const types = buildReactDeclarationTypes(value, key, rootNode); types.fullName = key; types.name = key; types.node = propNode; @@ -850,8 +859,9 @@ module.exports = function propTypesInstructions(context, components, utils) { * Mark a prop type as declared * @param {ASTNode} node The AST node being checked. * @param {ASTNode} propTypes The AST node containing the proptypes + * @param {ASTNode} rootNode */ - function markPropTypesAsDeclared(node, propTypes) { + function markPropTypesAsDeclared(node, propTypes, rootNode) { let componentNode = node; while (componentNode && !components.get(componentNode)) { componentNode = componentNode.parent; @@ -869,7 +879,7 @@ module.exports = function propTypesInstructions(context, components, utils) { ignorePropsValidation = true; return; } - const types = buildReactDeclarationTypes(value, key); + const types = buildReactDeclarationTypes(value, key, rootNode); types.fullName = key; types.name = key; types.node = propNode; @@ -903,10 +913,11 @@ module.exports = function propTypesInstructions(context, components, utils) { ignorePropsValidation = true; break; } - const parentProp = context.getSource(propTypes.parent.left.object).replace(/^.*\.propTypes\./, ''); + const parentProp = getText(context, propTypes.parent.left.object).replace(/^.*\.propTypes\./, ''); const types = buildReactDeclarationTypes( propTypes.parent.right, - parentProp + parentProp, + rootNode ); types.name = propTypes.property.name; @@ -934,12 +945,11 @@ module.exports = function propTypesInstructions(context, components, utils) { break; } case 'Identifier': { - const variablesInScope = variableUtil.variablesInScope(context); - const firstMatchingVariable = variablesInScope + const firstMatchingVariable = variableUtil.variablesInScope(context, node) .find((variableInScope) => variableInScope.name === propTypes.name); if (firstMatchingVariable) { const defInScope = firstMatchingVariable.defs[firstMatchingVariable.defs.length - 1]; - markPropTypesAsDeclared(node, defInScope.node && defInScope.node.init); + markPropTypesAsDeclared(node, defInScope.node && defInScope.node.init, rootNode); return; } ignorePropsValidation = true; @@ -949,11 +959,11 @@ module.exports = function propTypesInstructions(context, components, utils) { if ( propWrapperUtil.isPropWrapperFunction( context, - context.getSourceCode().getText(propTypes.callee) + getText(context, propTypes.callee) ) && propTypes.arguments && propTypes.arguments[0] ) { - markPropTypesAsDeclared(node, propTypes.arguments[0]); + markPropTypesAsDeclared(node, propTypes.arguments[0], rootNode); return; } break; @@ -974,7 +984,7 @@ module.exports = function propTypesInstructions(context, components, utils) { break; case 'TSTypeReference': case 'TSTypeAnnotation': { - const tsTypeAnnotation = new DeclarePropTypesForTSTypeAnnotation(propTypes, declaredPropTypes); + const tsTypeAnnotation = new DeclarePropTypesForTSTypeAnnotation(propTypes, declaredPropTypes, rootNode); ignorePropsValidation = tsTypeAnnotation.shouldIgnorePropTypes; declaredPropTypes = tsTypeAnnotation.declaredPropTypes; } @@ -995,8 +1005,9 @@ module.exports = function propTypesInstructions(context, components, utils) { /** * @param {ASTNode} node We expect either an ArrowFunctionExpression, * FunctionDeclaration, or FunctionExpression + * @param {ASTNode} rootNode */ - function markAnnotatedFunctionArgumentsAsDeclared(node) { + function markAnnotatedFunctionArgumentsAsDeclared(node, rootNode) { if (!node.params || !node.params.length) { return; } @@ -1017,7 +1028,7 @@ module.exports = function propTypesInstructions(context, components, utils) { ) { const propTypesParams = node.parent.typeParameters; const declaredPropTypes = {}; - const obj = new DeclarePropTypesForTSTypeAnnotation(propTypesParams.params[1], declaredPropTypes); + const obj = new DeclarePropTypesForTSTypeAnnotation(propTypesParams.params[1], declaredPropTypes, rootNode); components.set(node, { declaredPropTypes: obj.declaredPropTypes, ignorePropsValidation: obj.shouldIgnorePropTypes, @@ -1048,13 +1059,13 @@ module.exports = function propTypesInstructions(context, components, utils) { if (param.typeAnnotation && param.typeAnnotation.typeAnnotation && param.typeAnnotation.typeAnnotation.type === 'UnionTypeAnnotation') { param.typeAnnotation.typeAnnotation.types.forEach((annotation) => { if (annotation.type === 'GenericTypeAnnotation') { - markPropTypesAsDeclared(node, resolveTypeAnnotation(annotation)); + markPropTypesAsDeclared(node, resolveTypeAnnotation(annotation), rootNode); } else { - markPropTypesAsDeclared(node, annotation); + markPropTypesAsDeclared(node, annotation, rootNode); } }); } else { - markPropTypesAsDeclared(node, resolveTypeAnnotation(param)); + markPropTypesAsDeclared(node, resolveTypeAnnotation(param), rootNode); } } else { // implements what's discussed here: https://github.com/jsx-eslint/eslint-plugin-react/issues/2777#issuecomment-683944481 @@ -1070,7 +1081,7 @@ module.exports = function propTypesInstructions(context, components, utils) { if (!isValidReactGenericTypeAnnotation(annotation)) return; - markPropTypesAsDeclared(node, resolveTypeAnnotation(siblingIdentifier)); + markPropTypesAsDeclared(node, resolveTypeAnnotation(siblingIdentifier), rootNode); } } @@ -1109,7 +1120,7 @@ module.exports = function propTypesInstructions(context, components, utils) { */ function isAnnotatedClassPropsDeclaration(node) { if (node && (node.type === 'ClassProperty' || node.type === 'PropertyDefinition')) { - const tokens = context.getFirstTokens(node, 2); + const tokens = getFirstTokens(context, node, 2); if ( node.typeAnnotation && ( tokens[0].value === 'props' @@ -1132,15 +1143,15 @@ module.exports = function propTypesInstructions(context, components, utils) { ClassDeclaration(node) { if (isSuperTypeParameterPropsDeclaration(node)) { - markPropTypesAsDeclared(node, resolveSuperParameterPropsType(node)); + markPropTypesAsDeclared(node, resolveSuperParameterPropsType(node), node); } }, 'ClassProperty, PropertyDefinition'(node) { if (isAnnotatedClassPropsDeclaration(node)) { - markPropTypesAsDeclared(node, resolveTypeAnnotation(node)); + markPropTypesAsDeclared(node, resolveTypeAnnotation(node), node); } else if (propsUtil.isPropTypesDeclaration(node)) { - markPropTypesAsDeclared(node, node.value); + markPropTypesAsDeclared(node, node.value, node); } }, @@ -1150,13 +1161,13 @@ module.exports = function propTypesInstructions(context, components, utils) { if (!propsUtil.isPropTypesDeclaration(property)) { return; } - markPropTypesAsDeclared(node, property.value); + markPropTypesAsDeclared(node, property.value, node); }); }, FunctionExpression(node) { if (node.parent.type !== 'MethodDefinition') { - markAnnotatedFunctionArgumentsAsDeclared(node); + markAnnotatedFunctionArgumentsAsDeclared(node, node); } }, @@ -1193,7 +1204,7 @@ module.exports = function propTypesInstructions(context, components, utils) { return; } try { - markPropTypesAsDeclared(component.node, node.parent.right || node.parent); + markPropTypesAsDeclared(component.node, node.parent.right || node.parent, node); } catch (e) { if (e.constructor !== RangeError) { throw e; } } @@ -1213,7 +1224,7 @@ module.exports = function propTypesInstructions(context, components, utils) { } if (i >= 0) { - markPropTypesAsDeclared(node, node.value.body.body[i].argument); + markPropTypesAsDeclared(node, node.value.body.body[i].argument, node); } }, @@ -1244,7 +1255,7 @@ module.exports = function propTypesInstructions(context, components, utils) { 'Program:exit'() { classExpressions.forEach((node) => { if (isSuperTypeParameterPropsDeclaration(node)) { - markPropTypesAsDeclared(node, resolveSuperParameterPropsType(node)); + markPropTypesAsDeclared(node, resolveSuperParameterPropsType(node), node); } }); }, diff --git a/lib/util/propTypesSort.js b/lib/util/propTypesSort.js index 505f346a3b..12982430b3 100644 --- a/lib/util/propTypesSort.js +++ b/lib/util/propTypesSort.js @@ -7,6 +7,10 @@ const toSorted = require('array.prototype.tosorted'); const astUtil = require('./ast'); +const eslintUtil = require('./eslint'); + +const getSourceCode = eslintUtil.getSourceCode; +const getText = eslintUtil.getText; /** * Returns the value name of a node. @@ -114,8 +118,8 @@ const commentnodeMap = new WeakMap(); // all nodes reference WeakMap for start a /** * Fixes sort order of prop types. * + * @param {Context} context the second element to compare. * @param {Fixer} fixer the first element to compare. - * @param {Object} context the second element to compare. * @param {Array} declarations The context of the two nodes. * @param {Boolean=} ignoreCase whether or not to ignore case when comparing the two elements. * @param {Boolean=} requiredFirst whether or not to sort required elements first. @@ -125,8 +129,8 @@ const commentnodeMap = new WeakMap(); // all nodes reference WeakMap for start a * @returns {Object|*|{range, text}} the sort order of the two elements. */ function fixPropTypesSort( - fixer, context, + fixer, declarations, ignoreCase, requiredFirst, @@ -136,7 +140,7 @@ function fixPropTypesSort( ) { function sortInSource(allNodes, source) { const originalSource = source; - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); for (let i = 0; i < allNodes.length; i++) { const node = allNodes[i]; let commentAfter = []; @@ -178,9 +182,9 @@ function fixPropTypesSort( (a, b) => sorter(a, b, context, ignoreCase, requiredFirst, callbacksLast, noSortAlphabetically) ); + const sourceCodeText = getText(context); source = nodes.reduceRight((acc, attr, index) => { const sortedAttr = sortedAttributes[index]; - const sourceCodeText = sourceCode.getText(); const commentNode = commentnodeMap.get(sortedAttr); let sortedAttrText = sourceCodeText.slice(commentNode.start, commentNode.end); if (sortShapeProp && isShapeProp(sortedAttr.value)) { @@ -199,7 +203,7 @@ function fixPropTypesSort( return source; } - const source = sortInSource(declarations, context.getSourceCode().getText()); + const source = sortInSource(declarations, getText(context)); const rangeStart = commentnodeMap.get(declarations[0]).start; const rangeEnd = commentnodeMap.get(declarations[declarations.length - 1]).end; diff --git a/lib/util/usedPropTypes.js b/lib/util/usedPropTypes.js index 6a0a650333..9ecef0af37 100644 --- a/lib/util/usedPropTypes.js +++ b/lib/util/usedPropTypes.js @@ -10,6 +10,10 @@ const astUtil = require('./ast'); const componentUtil = require('./componentUtil'); const testReactVersion = require('./version').testReactVersion; const ast = require('./ast'); +const eslintUtil = require('./eslint'); + +const getScope = eslintUtil.getScope; +const getSourceCode = eslintUtil.getSourceCode; // ------------------------------------------------------------------------------ // Constants @@ -80,11 +84,12 @@ function mustBeValidated(component) { /** * Check if we are in a lifecycle method * @param {object} context + * @param {ASTNode} node The AST node being checked. * @param {boolean} checkAsyncSafeLifeCycles * @return {boolean} true if we are in a class constructor, false if not */ -function inLifeCycleMethod(context, checkAsyncSafeLifeCycles) { - let scope = context.getScope(); +function inLifeCycleMethod(context, node, checkAsyncSafeLifeCycles) { + let scope = getScope(context, node); while (scope) { if (scope.block && scope.block.parent && scope.block.parent.key) { const name = scope.block.parent.key.name; @@ -158,11 +163,11 @@ function isSetStateUpdater(node) { && node.parent.arguments[0] === node; } -function isPropArgumentInSetStateUpdater(context, name) { +function isPropArgumentInSetStateUpdater(context, node, name) { if (typeof name !== 'string') { return; } - let scope = context.getScope(); + let scope = getScope(context, node); while (scope) { const unwrappedParentCalleeNode = scope.block && scope.block.parent @@ -186,10 +191,11 @@ function isPropArgumentInSetStateUpdater(context, name) { /** * @param {Context} context + * @param {ASTNode} node * @returns {boolean} */ -function isInClassComponent(context) { - return !!(componentUtil.getParentES6Component(context) || componentUtil.getParentES5Component(context)); +function isInClassComponent(context, node) { + return !!(componentUtil.getParentES6Component(context, node) || componentUtil.getParentES5Component(context, node)); } /** @@ -211,22 +217,22 @@ function isThisDotProps(node) { * @returns {Boolean} True if the prop has spread operator, false if not. */ function hasSpreadOperator(context, node) { - const tokens = context.getSourceCode().getTokens(node); + const tokens = getSourceCode(context).getTokens(node); return tokens.length && tokens[0].value === '...'; } /** * Checks if the node is a propTypes usage of the form `this.props.*`, `props.*`, `prevProps.*`, or `nextProps.*`. - * @param {ASTNode} node * @param {Context} context + * @param {ASTNode} node * @param {Object} utils * @param {boolean} checkAsyncSafeLifeCycles * @returns {boolean} */ -function isPropTypesUsageByMemberExpression(node, context, utils, checkAsyncSafeLifeCycles) { +function isPropTypesUsageByMemberExpression(context, node, utils, checkAsyncSafeLifeCycles) { const unwrappedObjectNode = ast.unwrapTSAsExpression(node.object); - if (isInClassComponent(context)) { + if (isInClassComponent(context, node)) { // this.props.* if (isThisDotProps(unwrappedObjectNode)) { return true; @@ -234,12 +240,12 @@ function isPropTypesUsageByMemberExpression(node, context, utils, checkAsyncSafe // props.* or prevProps.* or nextProps.* if ( isCommonVariableNameForProps(unwrappedObjectNode.name) - && (inLifeCycleMethod(context, checkAsyncSafeLifeCycles) || astUtil.inConstructor(context)) + && (inLifeCycleMethod(context, node, checkAsyncSafeLifeCycles) || astUtil.inConstructor(context, node)) ) { return true; } // this.setState((_, props) => props.*)) - if (isPropArgumentInSetStateUpdater(context, unwrappedObjectNode.name)) { + if (isPropArgumentInSetStateUpdater(context, node, unwrappedObjectNode.name)) { return true; } return false; @@ -250,13 +256,13 @@ function isPropTypesUsageByMemberExpression(node, context, utils, checkAsyncSafe /** * Retrieve the name of a property node - * @param {ASTNode} node The AST node with the property. * @param {Context} context + * @param {ASTNode} node The AST node with the property. * @param {Object} utils * @param {boolean} checkAsyncSafeLifeCycles * @return {string|undefined} the name of the property or undefined if not found */ -function getPropertyName(node, context, utils, checkAsyncSafeLifeCycles) { +function getPropertyName(context, node, utils, checkAsyncSafeLifeCycles) { const property = node.property; if (property) { switch (property.type) { @@ -274,7 +280,7 @@ function getPropertyName(node, context, utils, checkAsyncSafeLifeCycles) { } // Accept number as well but only accept props[123] if (typeof property.value === 'number') { - if (isPropTypesUsageByMemberExpression(node, context, utils, checkAsyncSafeLifeCycles)) { + if (isPropTypesUsageByMemberExpression(context, node, utils, checkAsyncSafeLifeCycles)) { return property.raw; } } @@ -309,7 +315,7 @@ module.exports = function usedPropTypesInstructions(context, components, utils) switch (node.type) { case 'OptionalMemberExpression': case 'MemberExpression': - name = getPropertyName(node, context, utils, checkAsyncSafeLifeCycles); + name = getPropertyName(context, node, utils, checkAsyncSafeLifeCycles); if (name) { allNames = parentNames.concat(name); if ( @@ -362,7 +368,7 @@ module.exports = function usedPropTypesInstructions(context, components, utils) throw new Error(`${node.type} ASTNodes are not handled by markPropTypesAsUsed`); } - const component = components.get(utils.getParentComponent()); + const component = components.get(utils.getParentComponent(node)); const usedPropTypes = (component && component.usedPropTypes) || []; let ignoreUnusedPropTypesValidation = (component && component.ignoreUnusedPropTypesValidation) || false; @@ -472,7 +478,7 @@ module.exports = function usedPropTypesInstructions(context, components, utils) const unwrappedInitNode = ast.unwrapTSAsExpression(node.init); // let props = this.props - if (isThisDotProps(unwrappedInitNode) && isInClassComponent(context) && node.id.type === 'Identifier') { + if (isThisDotProps(unwrappedInitNode) && isInClassComponent(context, node) && node.id.type === 'Identifier') { propVariables.set(node.id.name, []); } @@ -501,14 +507,14 @@ module.exports = function usedPropTypesInstructions(context, components, utils) // let {firstname} = props if ( isCommonVariableNameForProps(unwrappedInitNode.name) - && (utils.getParentStatelessComponent() || isInLifeCycleMethod(node, checkAsyncSafeLifeCycles)) + && (utils.getParentStatelessComponent(node) || isInLifeCycleMethod(node, checkAsyncSafeLifeCycles)) ) { markPropTypesAsUsed(node.id); return; } // let {firstname} = this.props - if (isThisDotProps(unwrappedInitNode) && isInClassComponent(context)) { + if (isThisDotProps(unwrappedInitNode) && isInClassComponent(context, node)) { markPropTypesAsUsed(node.id); return; } @@ -532,14 +538,14 @@ module.exports = function usedPropTypesInstructions(context, components, utils) 'FunctionExpression:exit': popScope, JSXSpreadAttribute(node) { - const component = components.get(utils.getParentComponent()); + const component = components.get(utils.getParentComponent(node)); components.set(component ? component.node : node, { ignoreUnusedPropTypesValidation: node.argument.type !== 'ObjectExpression', }); }, 'MemberExpression, OptionalMemberExpression'(node) { - if (isPropTypesUsageByMemberExpression(node, context, utils, checkAsyncSafeLifeCycles)) { + if (isPropTypesUsageByMemberExpression(context, node, utils, checkAsyncSafeLifeCycles)) { markPropTypesAsUsed(node); return; } diff --git a/lib/util/variable.js b/lib/util/variable.js index a93cf18b5e..2903c67344 100644 --- a/lib/util/variable.js +++ b/lib/util/variable.js @@ -6,6 +6,7 @@ 'use strict'; const toReversed = require('array.prototype.toreversed'); +const getScope = require('./eslint').getScope; /** * Search a particular variable in a list @@ -33,10 +34,11 @@ function getVariable(variables, name) { * Contain a patch for babel-eslint to avoid https://github.com/babel/babel-eslint/issues/21 * * @param {Object} context The current rule context. + * @param {ASTNode} node The node to start looking from. * @returns {Array} The variables list */ -function variablesInScope(context) { - let scope = context.getScope(); +function variablesInScope(context, node) { + let scope = getScope(context, node); let variables = scope.variables; while (scope.type !== 'global') { @@ -56,11 +58,12 @@ function variablesInScope(context) { /** * Find a variable by name in the current scope. * @param {Object} context The current rule context. + * @param {ASTNode} node The node to check. Must be an Identifier node. * @param {string} name Name of the variable to look for. * @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise. */ -function findVariableByName(context, name) { - const variable = getVariable(variablesInScope(context), name); +function findVariableByName(context, node, name) { + const variable = getVariable(variablesInScope(context, node), name); if (!variable || !variable.defs[0] || !variable.defs[0].node) { return null; diff --git a/tests/lib/rules/function-component-definition.js b/tests/lib/rules/function-component-definition.js index 8f0e96ee17..f5282bf6f7 100644 --- a/tests/lib/rules/function-component-definition.js +++ b/tests/lib/rules/function-component-definition.js @@ -807,11 +807,7 @@ ruleTester.run('function-component-definition', rule, { return
; } `, - output: ` - var Hello: React.FC = function(props) { - return
; - } - `, + output: null, options: [{ namedComponents: 'function-declaration' }], errors: [{ messageId: 'function-declaration' }], features: ['types'], @@ -822,11 +818,7 @@ ruleTester.run('function-component-definition', rule, { return
; }; `, - output: ` - var Hello: React.FC = (props) => { - return
; - }; - `, + output: null, options: [{ namedComponents: 'function-declaration' }], errors: [{ messageId: 'function-declaration' }], features: ['types'], @@ -852,11 +844,7 @@ ruleTester.run('function-component-definition', rule, { return
; } `, - output: ` - function Hello(props: Test) { - return
; - } - `, + output: null, options: [{ namedComponents: 'arrow-function' }], errors: [{ messageId: 'arrow-function' }], features: ['types'], @@ -963,13 +951,7 @@ ruleTester.run('function-component-definition', rule, { } } `, - output: ` - function wrap(Component) { - return function(props) { - return
- } - } - `, + output: null, errors: [{ messageId: 'arrow-function' }], options: [{ unnamedComponents: 'arrow-function' }], features: ['types'], diff --git a/tests/lib/rules/jsx-closing-bracket-location.js b/tests/lib/rules/jsx-closing-bracket-location.js index c23ec1943a..120f1e3638 100644 --- a/tests/lib/rules/jsx-closing-bracket-location.js +++ b/tests/lib/rules/jsx-closing-bracket-location.js @@ -419,7 +419,10 @@ ruleTester.run('jsx-closing-bracket-location', rule, { errors: [ { messageId: 'bracketLocation', - data: { location: MESSAGE_AFTER_TAG }, + data: { + location: MESSAGE_AFTER_TAG, + details: '', + }, }, ], }, @@ -434,7 +437,10 @@ ruleTester.run('jsx-closing-bracket-location', rule, { errors: [ { messageId: 'bracketLocation', - data: { location: MESSAGE_AFTER_PROPS }, + data: { + location: MESSAGE_AFTER_PROPS, + details: '', + }, }, ], }, @@ -449,7 +455,10 @@ ruleTester.run('jsx-closing-bracket-location', rule, { errors: [ { messageId: 'bracketLocation', - data: { location: MESSAGE_AFTER_PROPS }, + data: { + location: MESSAGE_AFTER_PROPS, + details: '', + }, }, ], }, @@ -536,7 +545,10 @@ ruleTester.run('jsx-closing-bracket-location', rule, { errors: [ { messageId: 'bracketLocation', - data: { location: MESSAGE_AFTER_PROPS }, + data: { + location: MESSAGE_AFTER_PROPS, + details: '', + }, }, ], }, @@ -578,7 +590,10 @@ ruleTester.run('jsx-closing-bracket-location', rule, { errors: [ { messageId: 'bracketLocation', - data: { location: MESSAGE_AFTER_PROPS }, + data: { + location: MESSAGE_AFTER_PROPS, + details: '', + }, }, ], }, @@ -644,7 +659,10 @@ ruleTester.run('jsx-closing-bracket-location', rule, { errors: [ { messageId: 'bracketLocation', - data: { location: MESSAGE_AFTER_PROPS }, + data: { + location: MESSAGE_AFTER_PROPS, + details: '', + }, }, ], }, @@ -686,7 +704,10 @@ ruleTester.run('jsx-closing-bracket-location', rule, { errors: [ { messageId: 'bracketLocation', - data: { location: MESSAGE_AFTER_PROPS }, + data: { + location: MESSAGE_AFTER_PROPS, + details: '', + }, }, ], }, @@ -1098,7 +1119,10 @@ ruleTester.run('jsx-closing-bracket-location', rule, { errors: [ { messageId: 'bracketLocation', - data: { location: MESSAGE_AFTER_TAG }, + data: { + location: MESSAGE_AFTER_TAG, + details: '', + }, }, ], }, @@ -1216,7 +1240,10 @@ ruleTester.run('jsx-closing-bracket-location', rule, { errors: [ { messageId: 'bracketLocation', - data: { location: MESSAGE_AFTER_PROPS }, + data: { + location: MESSAGE_AFTER_PROPS, + details: '', + }, }, ], }, @@ -1258,7 +1285,10 @@ ruleTester.run('jsx-closing-bracket-location', rule, { errors: [ { messageId: 'bracketLocation', - data: { location: MESSAGE_AFTER_PROPS }, + data: { + location: MESSAGE_AFTER_PROPS, + details: '', + }, }, ], }, @@ -1324,7 +1354,10 @@ ruleTester.run('jsx-closing-bracket-location', rule, { errors: [ { messageId: 'bracketLocation', - data: { location: MESSAGE_AFTER_PROPS }, + data: { + location: MESSAGE_AFTER_PROPS, + details: '', + }, }, ], }, @@ -1366,7 +1399,10 @@ ruleTester.run('jsx-closing-bracket-location', rule, { errors: [ { messageId: 'bracketLocation', - data: { location: MESSAGE_AFTER_PROPS }, + data: { + location: MESSAGE_AFTER_PROPS, + details: '', + }, }, ], }, @@ -1778,7 +1814,10 @@ ruleTester.run('jsx-closing-bracket-location', rule, { errors: [ { messageId: 'bracketLocation', - data: { location: MESSAGE_AFTER_TAG }, + data: { + location: MESSAGE_AFTER_TAG, + details: '', + }, }, ], }, diff --git a/tests/lib/rules/jsx-fragments.js b/tests/lib/rules/jsx-fragments.js index 1ee6fb3fa2..2807a15f75 100644 --- a/tests/lib/rules/jsx-fragments.js +++ b/tests/lib/rules/jsx-fragments.js @@ -142,7 +142,7 @@ ruleTester.run('jsx-fragments', rule, { }, { code: '<>', - output: '<>', // should get '', but the old TS parser lacks opening/closing Fragment info + output: null, // should get '', but the old TS parser lacks opening/closing Fragment info features: ['fragment', 'no-babel', 'ts', 'no-ts-new'], options: ['element'], settings, diff --git a/tests/lib/rules/jsx-no-leaked-render.js b/tests/lib/rules/jsx-no-leaked-render.js index f5729bf059..a6742a29cd 100644 --- a/tests/lib/rules/jsx-no-leaked-render.js +++ b/tests/lib/rules/jsx-no-leaked-render.js @@ -195,6 +195,24 @@ ruleTester.run('jsx-no-leaked-render', rule, { `, options: [{ validStrategies: ['coerce'] }], }, + { + code: ` + const isOpen = true; + const Component = () => { + return 0} /> + } + `, + options: [{ validStrategies: ['coerce'] }], + }, + { + code: ` + const isOpen = false; + const Component = () => { + return 0} /> + } + `, + options: [{ validStrategies: ['coerce'] }], + }, ]) || [], invalid: parsers.all([].concat( @@ -972,6 +990,26 @@ ruleTester.run('jsx-no-leaked-render', rule, { line: 5, column: 16, }], - } : [] + } : [], + { + code: ` + const isOpen = 0; + const Component = () => { + return 0} /> + } + `, + output: ` + const isOpen = 0; + const Component = () => { + return 0} /> + } + `, + options: [{ validStrategies: ['coerce'] }], + errors: [{ + message: 'Potential leaked value that might cause unintentionally rendered values or rendering crashes', + line: 4, + column: 33, + }], + } )), }); diff --git a/tests/lib/rules/jsx-no-useless-fragment.js b/tests/lib/rules/jsx-no-useless-fragment.js index 22594aa6bf..d1b599a8b2 100644 --- a/tests/lib/rules/jsx-no-useless-fragment.js +++ b/tests/lib/rules/jsx-no-useless-fragment.js @@ -194,7 +194,7 @@ ruleTester.run('jsx-no-useless-fragment', rule, { }, { code: '
<>{"a"}{"b"}
', - output: '
<>{"a"}{"b"}
', + output: null, errors: [{ messageId: 'ChildOfHtmlElement', type: 'JSXFragment' }], features: ['fragment', 'ts-old', 'no-ts-new', 'no-babel', 'no-default'], }, diff --git a/tests/lib/rules/no-invalid-html-attribute.js b/tests/lib/rules/no-invalid-html-attribute.js index f1e20aa9a6..b171d823a4 100644 --- a/tests/lib/rules/no-invalid-html-attribute.js +++ b/tests/lib/rules/no-invalid-html-attribute.js @@ -247,12 +247,13 @@ ruleTester.run('no-invalid-html-attribute', rule, { { messageId: 'neverValid', data: { - reportingValue: 'alternatex', attributeName: 'rel', + reportingValue: 'alternatex', }, suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'alternatex' }, output: '', }, ], @@ -266,12 +267,13 @@ ruleTester.run('no-invalid-html-attribute', rule, { { messageId: 'neverValid', data: { - reportingValue: 'alternatex', attributeName: 'rel', + reportingValue: 'alternatex', }, suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'alternatex' }, output: 'React.createElement("a", { rel: "" })', }, ], @@ -291,6 +293,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'alternatex' }, output: 'React.createElement("a", { rel: [""] })', }, ], @@ -304,12 +307,13 @@ ruleTester.run('no-invalid-html-attribute', rule, { { messageId: 'neverValid', data: { - reportingValue: 'alternatex', attributeName: 'rel', + reportingValue: 'alternatex', }, suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'alternatex' }, output: '', }, ], @@ -323,12 +327,13 @@ ruleTester.run('no-invalid-html-attribute', rule, { { messageId: 'neverValid', data: { - reportingValue: 'alternatex alternate', attributeName: 'rel', + reportingValue: 'alternatex alternate', }, suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'alternatex alternate' }, output: 'React.createElement("a", { rel: "" })', }, ], @@ -348,6 +353,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'alternatex alternate' }, output: 'React.createElement("a", { rel: [""] })', }, ], @@ -367,6 +373,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'alternatex' }, output: '', }, ], @@ -386,6 +393,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'alternate alternatex' }, output: 'React.createElement("a", { rel: "" })', }, ], @@ -405,6 +413,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'alternate alternatex' }, output: 'React.createElement("a", { rel: [""] })', }, ], @@ -424,6 +433,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveDefault', + data: { attributeName: 'rel' }, output: '', }, ], @@ -443,6 +453,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { // suggestions: [ // { // messageId: 'suggestRemoveDefault', + // data: { attributeName: 'rel' }, // output: 'React.createElement("html", { })', // }, // ], @@ -460,6 +471,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveEmpty', + data: { attributeName: 'rel' }, output: '', }, ], @@ -508,6 +520,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveDefault', + data: { attributeName: 'rel' }, output: '', }, ], @@ -524,6 +537,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveNonString', + data: { attributeName: 'rel' }, output: '', }, ], @@ -540,6 +554,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveNonString', + data: { attributeName: 'rel' }, output: '', }, ], @@ -552,10 +567,11 @@ ruleTester.run('no-invalid-html-attribute', rule, { errors: [ { messageId: 'onlyStrings', - data: { attributeName: 'rel' }, + data: { attributeName: 'rel', reportingValue: 'true' }, suggestions: [ { messageId: 'suggestRemoveNonString', + data: { attributeName: 'rel', reportingValue: 'true' }, output: '', }, ], @@ -572,6 +588,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveDefault', + data: { attributeName: 'rel' }, output: '', }, ], @@ -588,6 +605,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveDefault', + data: { attributeName: 'rel' }, output: '', }, ], @@ -607,6 +625,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'foobar' }, output: '', }, ], @@ -623,6 +642,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveWhitespaces', + data: { attributeName: 'rel' }, output: '', }, ], @@ -639,6 +659,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveWhitespaces', + data: { attributeName: 'rel' }, output: '', }, ], @@ -655,6 +676,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveWhitespaces', + data: { attributeName: 'rel' }, output: '', }, ], @@ -674,6 +696,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'foobar' }, output: '', }, ], @@ -693,6 +716,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'foobar' }, output: 'React.createElement("a", { rel: ["noreferrer", "noopener", "" ] })', }, ], @@ -712,6 +736,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'foobar' }, output: '', }, ], @@ -731,6 +756,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'foobar' }, output: '', }, ], @@ -745,6 +771,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'batgo' }, output: '', }, ], @@ -753,6 +780,13 @@ ruleTester.run('no-invalid-html-attribute', rule, { { messageId: 'spaceDelimited', data: { attributeName: 'rel' }, + suggestions: [ + { + messageId: 'suggestRemoveWhitespaces', + data: { attributeName: 'rel' }, + output: '', + }, + ], }, ], }, @@ -765,6 +799,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveWhitespaces', + data: { attributeName: 'rel' }, output: '', }, ], @@ -781,6 +816,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveWhitespaces', + data: { attributeName: 'rel' }, output: '', }, ], @@ -800,6 +836,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'batgo' }, output: '', }, ], @@ -808,6 +845,13 @@ ruleTester.run('no-invalid-html-attribute', rule, { { messageId: 'spaceDelimited', data: { attributeName: 'rel' }, + suggestions: [ + { + messageId: 'suggestRemoveWhitespaces', + data: { attributeName: 'rel' }, + output: '', + }, + ], }, ], }, @@ -823,6 +867,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'batgo' }, output: '', }, ], @@ -839,6 +884,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveWhitespaces', + data: { attributeName: 'rel' }, output: '', }, ], @@ -859,6 +905,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'canonical' }, output: '', }, ], @@ -879,6 +926,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'dns-prefetch' }, output: '', }, ], @@ -899,6 +947,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'icon' }, output: '', }, ], @@ -925,12 +974,16 @@ ruleTester.run('no-invalid-html-attribute', rule, { { messageId: 'neverValid', data: { - reportingValue: 'foo', attributeName: 'rel', + reportingValue: 'foo', }, suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { + attributeName: 'rel', + reportingValue: 'foo', + }, output: '', }, ], @@ -956,6 +1009,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveWhitespaces', + data: { attributeName: 'rel' }, output: '', }, ], @@ -969,12 +1023,16 @@ ruleTester.run('no-invalid-html-attribute', rule, { { messageId: 'neverValid', data: { - reportingValue: 'foo', attributeName: 'rel', + reportingValue: 'foo', }, suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { + attributeName: 'rel', + reportingValue: 'foo', + }, output: '', }, ], @@ -983,6 +1041,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { { messageId: 'notAlone', data: { + attributeName: 'rel', reportingValue: 'shortcut', missingValue: 'icon', }, @@ -993,6 +1052,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveWhitespaces', + data: { attributeName: 'rel' }, output: '', }, ], @@ -1013,6 +1073,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'manifest' }, output: '', }, ], @@ -1033,6 +1094,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'modulepreload' }, output: '', }, ], @@ -1053,6 +1115,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'pingback' }, output: '', }, ], @@ -1073,6 +1136,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'preconnect' }, output: '', }, ], @@ -1093,6 +1157,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'prefetch' }, output: '', }, ], @@ -1113,6 +1178,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'preload' }, output: '', }, ], @@ -1133,6 +1199,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'prerender' }, output: '', }, ], @@ -1153,6 +1220,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'stylesheet' }, output: '', }, ], @@ -1173,6 +1241,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'canonical' }, output: '', }, ], @@ -1193,6 +1262,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'dns-prefetch' }, output: '', }, ], @@ -1213,6 +1283,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'icon' }, output: '', }, ], @@ -1233,6 +1304,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'manifest' }, output: '', }, ], @@ -1253,6 +1325,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'modulepreload' }, output: '', }, ], @@ -1273,6 +1346,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'pingback' }, output: '', }, ], @@ -1293,6 +1367,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'preconnect' }, output: '', }, ], @@ -1313,6 +1388,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'prefetch' }, output: '', }, ], @@ -1333,6 +1409,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'preload' }, output: '', }, ], @@ -1353,6 +1430,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'prerender' }, output: '', }, ], @@ -1373,6 +1451,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'stylesheet' }, output: '', }, ], @@ -1393,6 +1472,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'bookmark' }, output: '', }, ], @@ -1413,6 +1493,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'external' }, output: '', }, ], @@ -1433,6 +1514,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'nofollow' }, output: '', }, ], @@ -1453,6 +1535,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'noopener' }, output: '', }, ], @@ -1472,6 +1555,8 @@ ruleTester.run('no-invalid-html-attribute', rule, { }, suggestions: [ { + messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'noreferrer' }, output: '', }, ], @@ -1492,6 +1577,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'opener' }, output: '', }, ], @@ -1512,6 +1598,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'tag' }, output: '', }, ], @@ -1532,6 +1619,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'alternate' }, output: '
', }, ], @@ -1552,6 +1640,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'author' }, output: '
', }, ], @@ -1572,6 +1661,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'bookmark' }, output: '
', }, ], @@ -1592,6 +1682,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'canonical' }, output: '
', }, ], @@ -1612,6 +1703,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'dns-prefetch' }, output: '
', }, ], @@ -1632,6 +1724,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'icon' }, output: '
', }, ], @@ -1652,6 +1745,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'manifest' }, output: '
', }, ], @@ -1672,6 +1766,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'modulepreload' }, output: '
', }, ], @@ -1692,6 +1787,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'pingback' }, output: '
', }, ], @@ -1712,6 +1808,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'preconnect' }, output: '
', }, ], @@ -1732,6 +1829,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'prefetch' }, output: '
', }, ], @@ -1752,6 +1850,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'preload' }, output: '
', }, ], @@ -1772,6 +1871,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'prerender' }, output: '
', }, ], @@ -1792,6 +1892,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'stylesheet' }, output: '
', }, ], @@ -1812,6 +1913,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveInvalid', + data: { reportingValue: 'tag' }, output: '
', }, ], @@ -1832,6 +1934,7 @@ ruleTester.run('no-invalid-html-attribute', rule, { suggestions: [ { messageId: 'suggestRemoveEmpty', + data: { attributeName: 'rel' }, output: '
', }, ], diff --git a/tests/util/jsx.js b/tests/util/jsx.js index c8009fc005..7be16d5410 100644 --- a/tests/util/jsx.js +++ b/tests/util/jsx.js @@ -21,6 +21,7 @@ const parseCode = (code) => { }; const mockContext = { + getSourceCode() { return { getScope: mockContext.getScope }; }, getScope() { return { type: 'global', @@ -34,7 +35,7 @@ const mockContext = { describe('jsxUtil', () => { describe('isReturningJSX', () => { const assertValid = (codeStr) => assert( - isReturningJSX(parseCode(codeStr), mockContext) + isReturningJSX(mockContext, parseCode(codeStr)) ); it('Works when returning JSX', () => {