diff --git a/.eslintrc b/.eslintrc index 998aebcf59..ca692feb48 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,4 +1,6 @@ { + "root": true, + "extends": "airbnb-base", "env": { "es6": true, "node": true @@ -6,168 +8,38 @@ "parserOptions": { "ecmaVersion": 6, "ecmaFeatures": { - "jsx": true - } + "jsx": true + }, + "sourceType": "script", }, "rules": { - // Possible Errors "comma-dangle": [2, "never"], - "computed-property-spacing": [2, "never"], - "no-cond-assign": 2, - "no-console": 2, - "no-constant-condition": 2, - "no-control-regex": 2, - "no-debugger": 2, - "no-dupe-keys": 2, - "no-empty": 2, - "no-empty-character-class": 2, - "no-ex-assign": 2, - "no-extra-boolean-cast": 2, - "no-extra-parens": 0, - "no-extra-semi": 2, - "no-func-assign": 2, - "no-inner-declarations": 2, - "no-invalid-regexp": 2, - "no-irregular-whitespace": 2, - "no-negated-in-lhs": 2, - "no-obj-calls": 2, - "no-regex-spaces": 2, - "no-reserved-keys": 0, - "no-sparse-arrays": 2, - "no-unreachable": 2, - "use-isnan": 2, - "valid-jsdoc": 0, - "valid-typeof": 2, - // Best Practices - "block-scoped-var": 2, - "complexity": 0, - "consistent-return": 2, - "curly": 2, - "default-case": 2, - "dot-notation": 2, - "eqeqeq": 2, - "guard-for-in": 2, - "no-alert": 2, - "no-caller": 2, - "no-confusing-arrow": 2, - "no-div-regex": 2, - "no-else-return": 2, - "no-eq-null": 2, - "no-eval": 2, - "no-extend-native": 2, - "no-extra-bind": 2, - "no-fallthrough": 2, - "no-floating-decimal": 2, - "no-implied-eval": 2, - "no-iterator": 2, - "no-labels": 2, - "no-lone-blocks": 2, - "no-loop-func": 2, - "no-multi-spaces": 2, - "no-multi-str": 2, - "no-native-reassign": 2, - "no-new": 2, - "no-new-func": 2, - "no-new-wrappers": 2, - "no-octal": 2, - "no-octal-escape": 2, - "no-process-env": 2, - "no-proto": 2, - "no-redeclare": 2, - "no-return-assign": 2, - "no-script-url": 2, - "no-self-compare": 2, - "no-sequences": 2, - "no-unused-expressions": 2, - "no-void": 0, - "no-warning-comments": 2, - "no-with": 2, - "prefer-arrow-callback": 2, - "radix": 2, - "vars-on-top": 0, - "wrap-iife": 2, - "yoda": 2, - // Strict Mode - "strict": [2, "global"], - // Variables - "prefer-const": 2, - "no-catch-shadow": 2, - "no-const-assign": 2, - "no-delete-var": 2, - "no-label-var": 2, - "no-shadow": 2, - "no-shadow-restricted-names": 2, - "no-undef": 2, - "no-undef-init": 2, - "no-undefined": 2, - "no-unused-vars": 2, - "no-use-before-define": 2, - "no-var": 2, - // Stylistic Issues - "indent": [2, 2, { - "SwitchCase": 1 - }], - "arrow-body-style": [2, "as-needed"], - "arrow-parens": [2, "as-needed"], - "arrow-spacing": 2, - "brace-style": 2, - "camelcase": 0, - "comma-spacing": 2, - "comma-style": 2, - "consistent-this": 0, - "eol-last": 2, - "func-names": 0, - "func-style": 0, - "key-spacing": [2, { - "beforeColon": false, - "afterColon": true - }], - "max-nested-callbacks": 0, - "new-cap": 2, - "new-parens": 2, - "no-array-constructor": 2, - "no-inline-comments": 0, - "no-lonely-if": 2, - "no-mixed-spaces-and-tabs": 2, - "no-nested-ternary": 2, - "no-new-object": 2, - "semi-spacing": [2, { - "before": false, - "after": true - }], - "no-spaced-func": 2, - "no-ternary": 0, - "no-trailing-spaces": 2, - "no-multiple-empty-lines": 2, - "no-underscore-dangle": 0, - "one-var": 0, - "operator-assignment": [2, "always"], - "padded-blocks": [2, { "blocks": "never", "classes": "never", "switches": "never" }], - "quotes": [2, "single"], - "quote-props": [2, "as-needed"], - "semi": [2, "always"], - "sort-vars": [2, {"ignoreCase": true}], - "keyword-spacing": 2, - "space-before-blocks": 2, "object-curly-spacing": [2, "never"], "array-bracket-spacing": [2, "never"], - "space-in-parens": 2, - "space-infix-ops": 2, - "space-unary-ops": 2, - "spaced-comment": 2, - "wrap-regex": 0, - // Legacy - "max-depth": 0, "max-len": [2, 120, { "ignoreStrings": true, "ignoreTemplateLiterals": true, "ignoreComments": true, }], - "max-params": 0, - "max-statements": 0, - "no-plusplus": 0, - "no-prototype-builtins": 2, - "prefer-template": 2, - "template-curly-spacing": [2, "never"] - } + "operator-linebreak": [2, "after"], + "consistent-return": 0, + + "prefer-destructuring": [2, { "array": false, "object": false }, { "enforceForRenamedProperties": false }], + + "function-paren-newline": 0, + "no-plusplus": 1, + "no-param-reassign": 1, + "no-mixed-operators": 1, + "global-require": 1, + "no-restricted-syntax": 1, + "valid-jsdoc": 1, + }, + "overrides": [ + { + "files": "tests/**", + "rules": { + "no-template-curly-in-string": 1, + }, + }, + ], } diff --git a/index.js b/index.js index b2d4ccdb90..82a89f97fd 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ 'use strict'; -const has = require('has'); +const fromEntries = require('object.fromentries'); +const entries = require('object.entries'); const allRules = { 'boolean-prop-naming': require('./lib/rules/boolean-prop-naming'), @@ -88,24 +89,11 @@ const allRules = { }; function filterRules(rules, predicate) { - const result = {}; - for (const key in rules) { - if (has(rules, key) && predicate(rules[key])) { - result[key] = rules[key]; - } - } - return result; + return fromEntries(entries(rules).filter(entry => predicate(entry[1]))); } function configureAsError(rules) { - const result = {}; - for (const key in rules) { - if (!has(rules, key)) { - continue; - } - result[`react/${key}`] = 2; - } - return result; + return fromEntries(Object.keys(rules).map(key => [`react/${key}`, 2])); } const activeRules = filterRules(allRules, rule => !rule.meta.deprecated); @@ -114,7 +102,7 @@ const activeRulesConfig = configureAsError(activeRules); const deprecatedRules = filterRules(allRules, rule => rule.meta.deprecated); module.exports = { - deprecatedRules: deprecatedRules, + deprecatedRules, rules: allRules, configs: { recommended: { diff --git a/lib/rules/boolean-prop-naming.js b/lib/rules/boolean-prop-naming.js index bc7394d327..ee8007443d 100644 --- a/lib/rules/boolean-prop-naming.js +++ b/lib/rules/boolean-prop-naming.js @@ -2,6 +2,7 @@ * @fileoverview Enforces consistent naming for boolean props * @author Ev Haus */ + 'use strict'; const Components = require('../util/Components'); @@ -52,7 +53,6 @@ module.exports = { }, create: Components.detect((context, components, utils) => { - const sourceCode = context.getSourceCode(); const config = context.options[0] || {}; const rule = config.rule ? new RegExp(config.rule) : null; const propTypeNames = config.propTypeNames || ['bool']; @@ -94,15 +94,15 @@ module.exports = { } /** - * Returns the name of the given node (prop) - * @param {Object} node The node we're getting the name of - */ + * Returns the name of the given node (prop) + * @param {Object} node The node we're getting the name of + */ function getPropName(node) { // Due to this bug https://github.com/babel/babel-eslint/issues/307 // 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 sourceCode.getFirstToken(node).value; + return context.getSourceCode().getFirstToken(node).value; } return node.key.name; @@ -155,7 +155,7 @@ module.exports = { function runCheck(proptypes, addInvalidProp) { proptypes = proptypes || []; - proptypes.forEach(prop => { + proptypes.forEach((prop) => { if (config.validateNested && nestedPropTypes(prop)) { runCheck(prop.value.arguments[0].properties, addInvalidProp); return; @@ -175,12 +175,12 @@ module.exports = { const component = components.get(node) || node; const invalidProps = component.invalidProps || []; - runCheck(proptypes, prop => { + runCheck(proptypes, (prop) => { invalidProps.push(prop); }); components.set(node, { - invalidProps: invalidProps + invalidProps }); } @@ -189,14 +189,14 @@ module.exports = { * @param {Object} component The component to process */ function reportInvalidNaming(component) { - component.invalidProps.forEach(propNode => { + component.invalidProps.forEach((propNode) => { const propName = getPropName(propNode); context.report({ node: propNode, message: config.message || 'Prop name ({{ propName }}) doesn\'t match rule ({{ pattern }})', data: { component: propName, - propName: propName, + propName, pattern: config.rule } }); @@ -215,11 +215,18 @@ module.exports = { // -------------------------------------------------------------------------- return { - ClassProperty: function(node) { + ClassProperty(node) { if (!rule || !propsUtil.isPropTypesDeclaration(node)) { return; } - if (node.value && node.value.type === 'CallExpression' && propWrapperUtil.isPropWrapperFunction(context, sourceCode.getText(node.value.callee))) { + if ( + node.value && + node.value.type === 'CallExpression' && + propWrapperUtil.isPropWrapperFunction( + context, + context.getSourceCode().getText(node.value.callee) + ) + ) { checkPropWrapperArguments(node, node.value.arguments); } if (node.value && node.value.properties) { @@ -230,7 +237,7 @@ module.exports = { } }, - MemberExpression: function(node) { + MemberExpression(node) { if (!rule || !propsUtil.isPropTypesDeclaration(node)) { return; } @@ -239,20 +246,26 @@ module.exports = { return; } const right = node.parent.right; - if (right.type === 'CallExpression' && propWrapperUtil.isPropWrapperFunction(context, sourceCode.getText(right.callee))) { + if ( + right.type === 'CallExpression' && + propWrapperUtil.isPropWrapperFunction( + context, + context.getSourceCode().getText(right.callee) + ) + ) { checkPropWrapperArguments(component.node, right.arguments); return; } validatePropNaming(component.node, node.parent.right.properties); }, - ObjectExpression: function(node) { + ObjectExpression(node) { if (!rule) { return; } // Search for the proptypes declaration - node.properties.forEach(property => { + node.properties.forEach((property) => { if (!propsUtil.isPropTypesDeclaration(property)) { return; } @@ -260,20 +273,20 @@ module.exports = { }); }, - TypeAlias: function(node) { + TypeAlias(node) { // Cache all ObjectType annotations, we will check them at the end if (node.right.type === 'ObjectTypeAnnotation') { objectTypeAnnotations.set(node.id.name, node.right); } }, - 'Program:exit': function() { + 'Program:exit': function () { if (!rule) { return; } const list = components.list(); - Object.keys(list).forEach(component => { + Object.keys(list).forEach((component) => { // If this is a functional component that uses a global type, check it if ( list[component].node.type === 'FunctionDeclaration' && diff --git a/lib/rules/button-has-type.js b/lib/rules/button-has-type.js index 2322a092a2..a7d4694b77 100644 --- a/lib/rules/button-has-type.js +++ b/lib/rules/button-has-type.js @@ -2,6 +2,7 @@ * @fileoverview Forbid "button" element without an explicit "type" attribute * @author Filipp Riabchun */ + 'use strict'; const getProp = require('jsx-ast-utils/getProp'); @@ -15,12 +16,12 @@ const pragmaUtil = require('../util/pragma'); function isCreateElement(node, context) { const pragma = pragmaUtil.getFromContext(context); - return node.callee - && node.callee.type === 'MemberExpression' - && node.callee.property.name === 'createElement' - && node.callee.object - && node.callee.object.name === pragma - && node.arguments.length > 0; + return node.callee && + node.callee.type === 'MemberExpression' && + node.callee.property.name === 'createElement' && + node.callee.object && + node.callee.object.name === pragma && + node.arguments.length > 0; } // ------------------------------------------------------------------------------ @@ -61,12 +62,12 @@ module.exports = { }] }, - create: function(context) { + create(context) { const configuration = Object.assign({}, optionDefaults, context.options[0]); function reportMissing(node) { context.report({ - node: node, + node, message: 'Missing an explicit type attribute for button' }); } @@ -75,19 +76,19 @@ module.exports = { const q = quoteFn || (x => `"${x}"`); if (!(value in configuration)) { context.report({ - node: node, + node, message: `${q(value)} is an invalid value for button type attribute` }); } else if (!configuration[value]) { context.report({ - node: node, + node, message: `${q(value)} is a forbidden value for button type attribute` }); } } return { - JSXElement: function(node) { + JSXElement(node) { if (node.openingElement.name.name !== 'button') { return; } @@ -106,7 +107,7 @@ module.exports = { checkValue(node, propValue); } }, - CallExpression: function(node) { + CallExpression(node) { if (!isCreateElement(node, context)) { return; } diff --git a/lib/rules/default-props-match-prop-types.js b/lib/rules/default-props-match-prop-types.js index cf1597a311..fca3c0fae6 100644 --- a/lib/rules/default-props-match-prop-types.js +++ b/lib/rules/default-props-match-prop-types.js @@ -3,6 +3,7 @@ * @author Vitor Balocco * @author Roy Sutton */ + 'use strict'; const Components = require('../util/Components'); @@ -50,7 +51,7 @@ module.exports = { return; } - Object.keys(defaultProps).forEach(defaultPropName => { + Object.keys(defaultProps).forEach((defaultPropName) => { const defaultProp = defaultProps[defaultPropName]; const prop = propTypes[defaultPropName]; @@ -79,11 +80,11 @@ module.exports = { // -------------------------------------------------------------------------- return { - 'Program:exit': function() { + 'Program:exit': function () { const list = components.list(); // If no defaultProps could be found, we don't report anything. - Object.keys(list).filter(component => list[component].defaultProps).forEach(component => { + Object.keys(list).filter(component => list[component].defaultProps).forEach((component) => { reportInvalidDefaultProps( list[component].declaredPropTypes, list[component].defaultProps || {} diff --git a/lib/rules/destructuring-assignment.js b/lib/rules/destructuring-assignment.js index db1f675d5f..02999116a2 100644 --- a/lib/rules/destructuring-assignment.js +++ b/lib/rules/destructuring-assignment.js @@ -1,10 +1,12 @@ /** * @fileoverview Enforce consistent usage of destructuring assignment of props, state, and context. - **/ + */ + 'use strict'; const Components = require('../util/Components'); const docsUrl = require('../util/docsUrl'); +const isAssignmentLHS = require('../util/ast').isAssignmentLHS; const DEFAULT_OPTION = 'always'; @@ -37,19 +39,6 @@ module.exports = { const configuration = context.options[0] || DEFAULT_OPTION; const ignoreClassFields = context.options[1] && context.options[1].ignoreClassFields === true || false; - /** - * Checks if a prop is being assigned a value props.bar = 'bar' - * @param {ASTNode} node The AST node being checked. - * @returns {Boolean} - */ - - function isAssignmentToProp(node) { - return ( - node.parent && - node.parent.type === 'AssignmentExpression' && - node.parent.left === node - ); - } /** * @param {ASTNode} node We expect either an ArrowFunctionExpression, * FunctionDeclaration, or FunctionExpression @@ -60,12 +49,12 @@ module.exports = { if (destructuringProps && components.get(node) && configuration === 'never') { context.report({ - node: node, + node, message: 'Must never use destructuring props assignment in SFC argument' }); } else if (destructuringContext && components.get(node) && configuration === 'never') { context.report({ - node: node, + node, message: 'Must never use destructuring context assignment in SFC argument' }); } @@ -73,10 +62,10 @@ module.exports = { function handleSFCUsage(node) { // props.aProp || context.aProp - const isPropUsed = (node.object.name === 'props' || node.object.name === 'context') && !isAssignmentToProp(node); + const isPropUsed = (node.object.name === 'props' || node.object.name === 'context') && !isAssignmentLHS(node); if (isPropUsed && configuration === 'always') { context.report({ - node: node, + node, message: `Must use destructuring ${node.object.name} assignment` }); } @@ -98,7 +87,7 @@ module.exports = { const isPropUsed = ( node.object.type === 'MemberExpression' && node.object.object.type === 'ThisExpression' && (node.object.property.name === 'props' || node.object.property.name === 'context' || node.object.property.name === 'state') && - !isAssignmentToProp(node) + !isAssignmentLHS(node) ); if ( @@ -106,7 +95,7 @@ module.exports = { !(ignoreClassFields && isInClassProperty(node)) ) { context.report({ - node: node, + node, message: `Must use destructuring ${node.object.property.name} assignment` }); } @@ -120,7 +109,7 @@ module.exports = { FunctionExpression: handleStatelessComponent, - MemberExpression: function(node) { + MemberExpression(node) { const SFCComponent = components.get(context.getScope(node).block); const classComponent = utils.getParentComponent(node); if (SFCComponent) { @@ -131,7 +120,7 @@ module.exports = { } }, - VariableDeclarator: function(node) { + VariableDeclarator(node) { const classComponent = utils.getParentComponent(node); const SFCComponent = components.get(context.getScope(node).block); @@ -145,7 +134,7 @@ module.exports = { if (SFCComponent && destructuringSFC && configuration === 'never') { context.report({ - node: node, + node, message: `Must never use destructuring ${node.init.name} assignment` }); } @@ -155,7 +144,7 @@ module.exports = { !(ignoreClassFields && node.parent.type === 'ClassProperty') ) { context.report({ - node: node, + node, message: `Must never use destructuring ${node.init.property.name} assignment` }); } diff --git a/lib/rules/display-name.js b/lib/rules/display-name.js index 97da4890a6..ecfeaa7b4f 100644 --- a/lib/rules/display-name.js +++ b/lib/rules/display-name.js @@ -2,6 +2,7 @@ * @fileoverview Prevent missing displayName in a React component definition * @author Yannick Croissant */ + 'use strict'; const Components = require('../util/Components'); @@ -121,14 +122,14 @@ module.exports = { return { - ClassProperty: function(node) { + ClassProperty(node) { if (!propsUtil.isDisplayNameDeclaration(node)) { return; } markDisplayNameAsDeclared(node); }, - MemberExpression: function(node) { + MemberExpression(node) { if (!propsUtil.isDisplayNameDeclaration(node.property)) { return; } @@ -139,7 +140,7 @@ module.exports = { markDisplayNameAsDeclared(component.node); }, - FunctionExpression: function(node) { + FunctionExpression(node) { if (ignoreTranspilerName || !hasTranspilerName(node)) { return; } @@ -148,7 +149,7 @@ module.exports = { } }, - FunctionDeclaration: function(node) { + FunctionDeclaration(node) { if (ignoreTranspilerName || !hasTranspilerName(node)) { return; } @@ -157,7 +158,7 @@ module.exports = { } }, - ArrowFunctionExpression: function(node) { + ArrowFunctionExpression(node) { if (ignoreTranspilerName || !hasTranspilerName(node)) { return; } @@ -166,31 +167,31 @@ module.exports = { } }, - MethodDefinition: function(node) { + MethodDefinition(node) { if (!propsUtil.isDisplayNameDeclaration(node.key)) { return; } markDisplayNameAsDeclared(node); }, - ClassExpression: function(node) { + ClassExpression(node) { if (ignoreTranspilerName || !hasTranspilerName(node)) { return; } markDisplayNameAsDeclared(node); }, - ClassDeclaration: function(node) { + ClassDeclaration(node) { if (ignoreTranspilerName || !hasTranspilerName(node)) { return; } markDisplayNameAsDeclared(node); }, - ObjectExpression: function(node) { + ObjectExpression(node) { if (ignoreTranspilerName || !hasTranspilerName(node)) { // Search for the displayName declaration - node.properties.forEach(property => { + node.properties.forEach((property) => { if (!property.key || !propsUtil.isDisplayNameDeclaration(property.key)) { return; } @@ -201,10 +202,10 @@ module.exports = { markDisplayNameAsDeclared(node); }, - 'Program:exit': function() { + 'Program:exit': function () { const list = components.list(); // Report missing display name for all components - Object.keys(list).filter(component => !list[component].hasDisplayName).forEach(component => { + Object.keys(list).filter(component => !list[component].hasDisplayName).forEach((component) => { reportMissingDisplayName(list[component]); }); } diff --git a/lib/rules/forbid-component-props.js b/lib/rules/forbid-component-props.js index 7bf1431d83..6c11687315 100644 --- a/lib/rules/forbid-component-props.js +++ b/lib/rules/forbid-component-props.js @@ -2,6 +2,7 @@ * @fileoverview Forbid certain props on components * @author Joe Lencioni */ + 'use strict'; const docsUrl = require('../util/docsUrl'); @@ -54,9 +55,9 @@ module.exports = { }] }, - create: function(context) { + create(context) { const configuration = context.options[0] || {}; - const forbid = new Map((configuration.forbid || DEFAULTS).map(value => { + const forbid = new Map((configuration.forbid || DEFAULTS).map((value) => { const propName = typeof value === 'string' ? value : value.propName; const whitelist = typeof value === 'string' ? [] : (value.allowedFor || []); return [propName, whitelist]; @@ -69,7 +70,7 @@ module.exports = { } return { - JSXAttribute: function(node) { + JSXAttribute(node) { const tag = node.parent.name.name; if (tag && tag[0] !== tag[0].toUpperCase()) { // This is a DOM node, not a Component, so exit. @@ -83,7 +84,7 @@ module.exports = { } context.report({ - node: node, + node, message: `Prop \`${prop}\` is forbidden on Components` }); } diff --git a/lib/rules/forbid-dom-props.js b/lib/rules/forbid-dom-props.js index 1e3ac8668e..08853a412d 100644 --- a/lib/rules/forbid-dom-props.js +++ b/lib/rules/forbid-dom-props.js @@ -2,6 +2,7 @@ * @fileoverview Forbid certain props on DOM Nodes * @author David Vázquez */ + 'use strict'; const docsUrl = require('../util/docsUrl'); @@ -41,7 +42,7 @@ module.exports = { }] }, - create: function(context) { + create(context) { function isForbidden(prop) { const configuration = context.options[0] || {}; @@ -50,7 +51,7 @@ module.exports = { } return { - JSXAttribute: function(node) { + JSXAttribute(node) { const tag = node.parent.name.name; if (!(tag && tag[0] !== tag[0].toUpperCase())) { // This is a Component, not a DOM node, so exit. @@ -64,7 +65,7 @@ module.exports = { } context.report({ - node: node, + node, message: `Prop \`${prop}\` is forbidden on DOM Nodes` }); } diff --git a/lib/rules/forbid-elements.js b/lib/rules/forbid-elements.js index 482b27e864..bb005172f9 100644 --- a/lib/rules/forbid-elements.js +++ b/lib/rules/forbid-elements.js @@ -2,6 +2,7 @@ * @fileoverview Forbid certain elements * @author Kenneth Chung */ + 'use strict'; const has = require('has'); @@ -45,14 +46,13 @@ module.exports = { }] }, - create: function(context) { - const sourceCode = context.getSourceCode(); + create(context) { const configuration = context.options[0] || {}; const forbidConfiguration = configuration.forbid || []; const indexedForbidConfigs = {}; - forbidConfiguration.forEach(item => { + forbidConfiguration.forEach((item) => { if (typeof item === 'string') { indexedForbidConfigs[item] = {element: item}; } else { @@ -72,28 +72,28 @@ module.exports = { } function isValidCreateElement(node) { - return node.callee - && node.callee.type === 'MemberExpression' - && node.callee.object.name === 'React' - && node.callee.property.name === 'createElement' - && node.arguments.length > 0; + return node.callee && + node.callee.type === 'MemberExpression' && + node.callee.object.name === 'React' && + node.callee.property.name === 'createElement' && + node.arguments.length > 0; } function reportIfForbidden(element, node) { if (has(indexedForbidConfigs, element)) { context.report({ - node: node, + node, message: errorMessageForElement(element) }); } } return { - JSXOpeningElement: function(node) { - reportIfForbidden(sourceCode.getText(node.name), node.name); + JSXOpeningElement(node) { + reportIfForbidden(context.getSourceCode().getText(node.name), node.name); }, - CallExpression: function(node) { + CallExpression(node) { if (!isValidCreateElement(node)) { return; } @@ -103,10 +103,10 @@ module.exports = { if (argType === 'Identifier' && /^[A-Z_]/.test(argument.name)) { reportIfForbidden(argument.name, argument); - } else if (argType === 'Literal' && /^[a-z][^\.]*$/.test(argument.value)) { + } else if (argType === 'Literal' && /^[a-z][^.]*$/.test(argument.value)) { reportIfForbidden(argument.value, argument); } else if (argType === 'MemberExpression') { - reportIfForbidden(sourceCode.getText(argument), argument); + reportIfForbidden(context.getSourceCode().getText(argument), argument); } } }; diff --git a/lib/rules/forbid-foreign-prop-types.js b/lib/rules/forbid-foreign-prop-types.js index b5a829f631..ad23d508ef 100644 --- a/lib/rules/forbid-foreign-prop-types.js +++ b/lib/rules/forbid-foreign-prop-types.js @@ -2,9 +2,11 @@ * @fileoverview Forbid using another component's propTypes * @author Ian Christian Myers */ + 'use strict'; const docsUrl = require('../util/docsUrl'); +const ast = require('../util/ast'); module.exports = { meta: { @@ -28,7 +30,7 @@ module.exports = { ] }, - create: function(context) { + create(context) { const config = context.options[0] || {}; const allowInPropTypes = config.allowInPropTypes || false; @@ -36,10 +38,6 @@ module.exports = { // Helpers // -------------------------------------------------------------------------- - function isLeftSideOfAssignment(node) { - return node.parent.type === 'AssignmentExpression' && node.parent.left === node; - } - function findParentAssignmentExpression(node) { let parent = node.parent; @@ -93,19 +91,19 @@ module.exports = { } return { - MemberExpression: function(node) { + MemberExpression(node) { if ( node.property && ( !node.computed && node.property.type === 'Identifier' && node.property.name === 'propTypes' && - !isLeftSideOfAssignment(node) && + !ast.isAssignmentLHS(node) && !isAllowedAssignment(node) ) || ( (node.property.type === 'Literal' || node.property.type === 'JSXText') && node.property.value === 'propTypes' && - !isLeftSideOfAssignment(node) && + !ast.isAssignmentLHS(node) && !isAllowedAssignment(node) ) ) { @@ -116,7 +114,7 @@ module.exports = { } }, - ObjectPattern: function(node) { + ObjectPattern(node) { const propTypesNode = node.properties.find(property => property.type === 'Property' && property.key.name === 'propTypes'); if (propTypesNode) { diff --git a/lib/rules/forbid-prop-types.js b/lib/rules/forbid-prop-types.js index de60c84ea7..04b61bf734 100644 --- a/lib/rules/forbid-prop-types.js +++ b/lib/rules/forbid-prop-types.js @@ -1,6 +1,7 @@ /** * @fileoverview Forbid certain propTypes */ + 'use strict'; const variableUtil = require('../util/variable'); @@ -48,7 +49,7 @@ module.exports = { }] }, - create: function(context) { + create(context) { const configuration = context.options[0] || {}; const checkContextTypes = configuration.checkContextTypes || false; const checkChildContextTypes = configuration.checkChildContextTypes || false; @@ -78,7 +79,7 @@ module.exports = { * @returns {void} */ function checkProperties(declarations) { - declarations.forEach(declaration => { + declarations.forEach((declaration) => { if (declaration.type !== 'Property') { return; } @@ -117,25 +118,27 @@ module.exports = { case 'ObjectExpression': checkProperties(node.properties); break; - case 'Identifier': + case 'Identifier': { const propTypesObject = variableUtil.findVariableByName(context, node.name); if (propTypesObject && propTypesObject.properties) { checkProperties(propTypesObject.properties); } break; - case 'CallExpression': + } + case 'CallExpression': { const innerNode = node.arguments && node.arguments[0]; if (propWrapperUtil.isPropWrapperFunction(context, context.getSource(node.callee)) && innerNode) { checkNode(innerNode); } break; + } default: break; } } return { - ClassProperty: function(node) { + ClassProperty(node) { if ( !propsUtil.isPropTypesDeclaration(node) && !shouldCheckContextTypes(node) && @@ -146,7 +149,7 @@ module.exports = { checkNode(node.value); }, - MemberExpression: function(node) { + MemberExpression(node) { if ( !propsUtil.isPropTypesDeclaration(node) && !shouldCheckContextTypes(node) && @@ -158,7 +161,7 @@ module.exports = { checkNode(node.parent.right); }, - MethodDefinition: function(node) { + MethodDefinition(node) { if ( !propsUtil.isPropTypesDeclaration(node) && !shouldCheckContextTypes(node) && @@ -174,8 +177,8 @@ module.exports = { } }, - ObjectExpression: function(node) { - node.properties.forEach(property => { + ObjectExpression(node) { + node.properties.forEach((property) => { if (!property.key) { return; } diff --git a/lib/rules/jsx-boolean-value.js b/lib/rules/jsx-boolean-value.js index e56cac289f..50768b5875 100644 --- a/lib/rules/jsx-boolean-value.js +++ b/lib/rules/jsx-boolean-value.js @@ -2,6 +2,7 @@ * @fileoverview Enforce boolean attributes notation in JSX * @author Yannick Croissant */ + 'use strict'; const docsUrl = require('../util/docsUrl'); @@ -24,7 +25,7 @@ function getErrorData(exceptions) { if (!errorData.has(exceptions)) { const exceptionProps = Array.from(exceptions, name => `\`${name}\``).join(', '); const exceptionsMessage = exceptions.size > 0 ? ` for the following props: ${exceptionProps}` : ''; - errorData.set(exceptions, {exceptionsMessage: exceptionsMessage}); + errorData.set(exceptions, {exceptionsMessage}); } return errorData.get(exceptions); } @@ -104,9 +105,9 @@ module.exports = { if (isAlways(configuration, exceptions, propName) && value === null) { const data = getErrorData(exceptions); context.report({ - node: node, + node, message: ALWAYS_MESSAGE, - data: data, + data, fix(fixer) { return fixer.insertTextAfter(node, '={true}'); } @@ -115,9 +116,9 @@ module.exports = { if (isNever(configuration, exceptions, propName) && value && value.type === 'JSXExpressionContainer' && value.expression.value === true) { const data = getErrorData(exceptions); context.report({ - node: node, + node, message: NEVER_MESSAGE, - data: data, + data, fix(fixer) { return fixer.removeRange([node.name.range[1], value.range[1]]); } diff --git a/lib/rules/jsx-child-element-spacing.js b/lib/rules/jsx-child-element-spacing.js index 271423d970..0d0756fef0 100644 --- a/lib/rules/jsx-child-element-spacing.js +++ b/lib/rules/jsx-child-element-spacing.js @@ -55,7 +55,7 @@ module.exports = { } ] }, - create: function (context) { + create(context) { const TEXT_FOLLOWING_ELEMENT_PATTERN = /^\s*\n\s*\S/; const TEXT_PRECEDING_ELEMENT_PATTERN = /\S\s*\n\s*$/; @@ -71,10 +71,10 @@ module.exports = { INLINE_ELEMENTS.has(elementName(node)) ); - const handleJSX = node => { + const handleJSX = (node) => { let lastChild = null; let child = null; - (node.children.concat([null])).forEach(nextChild => { + (node.children.concat([null])).forEach((nextChild) => { if ( (lastChild || nextChild) && (!lastChild || isInlineElement(lastChild)) && diff --git a/lib/rules/jsx-closing-bracket-location.js b/lib/rules/jsx-closing-bracket-location.js index 860f6f3871..a84bdfd699 100644 --- a/lib/rules/jsx-closing-bracket-location.js +++ b/lib/rules/jsx-closing-bracket-location.js @@ -2,6 +2,7 @@ * @fileoverview Validate closing bracket location in JSX * @author Yannick Croissant */ + 'use strict'; const has = require('has'); @@ -49,7 +50,7 @@ module.exports = { }] }, - create: function(context) { + create(context) { const MESSAGE = 'The closing bracket must be {{location}}{{details}}'; const MESSAGE_LOCATION = { 'after-props': 'placed after the last prop', @@ -60,7 +61,6 @@ module.exports = { }; const DEFAULT_LOCATION = 'tag-aligned'; - const sourceCode = context.getSourceCode(); const config = context.options[0]; const options = { nonEmpty: DEFAULT_LOCATION, @@ -141,9 +141,10 @@ module.exports = { return tokens.lastProp.lastLine === tokens.closing.line; case 'props-aligned': case 'tag-aligned': - case 'line-aligned': + case 'line-aligned': { const correctColumn = getCorrectColumn(tokens, expectedLocation); return correctColumn === tokens.closing.column; + } default: return true; } @@ -158,14 +159,15 @@ module.exports = { */ function getIndentation(tokens, expectedLocation, correctColumn) { correctColumn = correctColumn || 0; - let indentation, spaces = []; + let indentation; + let spaces = []; switch (expectedLocation) { case 'props-aligned': - indentation = /^\s*/.exec(sourceCode.lines[tokens.lastProp.firstLine - 1])[0]; + indentation = /^\s*/.exec(context.getSourceCode().lines[tokens.lastProp.firstLine - 1])[0]; break; case 'tag-aligned': case 'line-aligned': - indentation = /^\s*/.exec(sourceCode.lines[tokens.opening.line - 1])[0]; + indentation = /^\s*/.exec(context.getSourceCode().lines[tokens.opening.line - 1])[0]; break; default: indentation = ''; @@ -185,6 +187,7 @@ module.exports = { * prop and start of opening line. */ function getTokensLocations(node) { + const sourceCode = context.getSourceCode(); 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; @@ -203,12 +206,12 @@ module.exports = { line: opening.line }; return { - tag: tag, - opening: opening, - closing: closing, - lastProp: lastProp, + tag, + opening, + closing, + lastProp, selfClosing: node.selfClosing, - openingStartOfLine: openingStartOfLine + openingStartOfLine }; } @@ -225,15 +228,15 @@ module.exports = { const lastAttributeNode = {}; return { - JSXAttribute: function(node) { + JSXAttribute(node) { lastAttributeNode[getOpeningElementId(node.parent)] = node; }, - JSXSpreadAttribute: function(node) { + JSXSpreadAttribute(node) { lastAttributeNode[getOpeningElementId(node.parent)] = node; }, - 'JSXOpeningElement:exit': function(node) { + 'JSXOpeningElement:exit': function (node) { const attributeNode = lastAttributeNode[getOpeningElementId(node)]; const cachedLastAttributeEndPos = attributeNode ? attributeNode.range[1] : null; let expectedNextLine; @@ -254,11 +257,11 @@ module.exports = { } context.report({ - node: node, + node, loc: tokens.closing, message: MESSAGE, - data: data, - fix: function(fixer) { + data, + fix(fixer) { const closingTag = tokens.selfClosing ? '/>' : '>'; switch (expectedLocation) { case 'after-tag': diff --git a/lib/rules/jsx-closing-tag-location.js b/lib/rules/jsx-closing-tag-location.js index 244d3072f5..d4bf10e1a7 100644 --- a/lib/rules/jsx-closing-tag-location.js +++ b/lib/rules/jsx-closing-tag-location.js @@ -2,6 +2,7 @@ * @fileoverview Validate closing tag location in JSX * @author Ross Solomon */ + 'use strict'; const astUtil = require('../util/ast'); @@ -21,7 +22,7 @@ module.exports = { fixable: 'whitespace' }, - create: function(context) { + create(context) { function handleClosingElement(node) { if (!node.parent) { return; @@ -44,10 +45,10 @@ module.exports = { } context.report({ - node: node, + node, loc: node.loc, message, - fix: function(fixer) { + fix(fixer) { const indent = Array(opening.loc.start.column + 1).join(' '); if (astUtil.isNodeFirstInLine(context, node)) { return fixer.replaceTextRange( diff --git a/lib/rules/jsx-curly-brace-presence.js b/lib/rules/jsx-curly-brace-presence.js index 0020934b1a..5bddc5b98f 100755 --- a/lib/rules/jsx-curly-brace-presence.js +++ b/lib/rules/jsx-curly-brace-presence.js @@ -3,6 +3,7 @@ * @author Jacky Ho * @author Simon Lydell */ + 'use strict'; const docsUrl = require('../util/docsUrl'); @@ -58,7 +59,7 @@ module.exports = { ] }, - create: function(context) { + create(context) { const ruleOptions = context.options[0]; const userConfig = typeof ruleOptions === 'string' ? {props: ruleOptions, children: ruleOptions} : @@ -116,7 +117,7 @@ module.exports = { context.report({ node: JSXExpressionNode, message: 'Curly braces are unnecessary here.', - fix: function(fixer) { + fix(fixer) { const expression = JSXExpressionNode.expression; const expressionType = expression.type; const parentType = JSXExpressionNode.parent.type; @@ -141,7 +142,7 @@ module.exports = { context.report({ node: literalNode, message: 'Need to wrap this literal in a JSX expression.', - fix: function(fixer) { + fix(fixer) { // If a HTML entity name is found, bail out because it can be fixed // by either using the real character or the unicode equivalent. // If it contains any line terminator character, bail out as well. @@ -212,9 +213,9 @@ module.exports = { } if ( - parent.children - && parent.children.length === 1 - && containsWhitespaceExpression(parent.children[0]) + parent.children && + parent.children.length === 1 && + containsWhitespaceExpression(parent.children[0]) ) { return false; } @@ -224,9 +225,9 @@ module.exports = { function shouldCheckForMissingCurly(parent, config) { if ( - parent.children - && parent.children.length === 1 - && containsWhitespaceExpression(parent.children[0]) + parent.children && + parent.children.length === 1 && + containsWhitespaceExpression(parent.children[0]) ) { return false; } @@ -239,13 +240,13 @@ module.exports = { // -------------------------------------------------------------------------- return { - JSXExpressionContainer: node => { + JSXExpressionContainer: (node) => { if (shouldCheckForUnnecessaryCurly(node.parent, userConfig)) { lintUnnecessaryCurly(node); } }, - 'Literal, JSXText': node => { + 'Literal, JSXText': (node) => { if (shouldCheckForMissingCurly(node.parent, userConfig)) { reportMissingCurly(node); } diff --git a/lib/rules/jsx-curly-spacing.js b/lib/rules/jsx-curly-spacing.js index c684ed6465..a53f24a740 100644 --- a/lib/rules/jsx-curly-spacing.js +++ b/lib/rules/jsx-curly-spacing.js @@ -8,6 +8,7 @@ * @author Yannick Croissant * @author Erik Wendel */ + 'use strict'; const has = require('has'); @@ -101,7 +102,7 @@ module.exports = { } }, - create: function(context) { + create(context) { function normalizeConfig(configOrTrue, defaults, lastPass) { const config = configOrTrue === true ? {} : configOrTrue; const when = config.when || defaults.when; @@ -125,7 +126,6 @@ module.exports = { const DEFAULT_ATTRIBUTES = true; const DEFAULT_CHILDREN = false; - const sourceCode = context.getSourceCode(); let originalConfig = context.options[0] || {}; if (SPACING_VALUES.indexOf(originalConfig) !== -1) { originalConfig = Object.assign({when: context.options[0]}, context.options[1]); @@ -163,7 +163,7 @@ module.exports = { * @returns {Object|*|{range, text}} */ function fixByTrimmingWhitespace(fixer, fromLoc, toLoc, mode, spacing) { - let replacementText = sourceCode.text.slice(fromLoc, toLoc); + let replacementText = context.getSourceCode().text.slice(fromLoc, toLoc); if (mode === 'start') { replacementText = replacementText.replace(/^\s+/gm, ''); } else { @@ -188,11 +188,11 @@ module.exports = { */ function reportNoBeginningNewline(node, token, spacing) { context.report({ - node: node, + node, loc: token.loc.start, message: `There should be no newline after '${token.value}'`, - fix: function(fixer) { - const nextToken = sourceCode.getTokenAfter(token); + fix(fixer) { + const nextToken = context.getSourceCode().getTokenAfter(token); return fixByTrimmingWhitespace(fixer, token.range[1], nextToken.range[0], 'start', spacing); } }); @@ -207,11 +207,11 @@ module.exports = { */ function reportNoEndingNewline(node, token, spacing) { context.report({ - node: node, + node, loc: token.loc.start, message: `There should be no newline before '${token.value}'`, - fix: function(fixer) { - const previousToken = sourceCode.getTokenBefore(token); + fix(fixer) { + const previousToken = context.getSourceCode().getTokenBefore(token); return fixByTrimmingWhitespace(fixer, previousToken.range[1], token.range[0], 'end', spacing); } }); @@ -225,10 +225,11 @@ module.exports = { */ function reportNoBeginningSpace(node, token) { context.report({ - node: node, + node, loc: token.loc.start, message: `There should be no space after '${token.value}'`, - fix: function(fixer) { + fix(fixer) { + const sourceCode = context.getSourceCode(); const nextToken = sourceCode.getTokenAfter(token); let nextComment; @@ -259,10 +260,11 @@ module.exports = { */ function reportNoEndingSpace(node, token) { context.report({ - node: node, + node, loc: token.loc.start, message: `There should be no space before '${token.value}'`, - fix: function(fixer) { + fix(fixer) { + const sourceCode = context.getSourceCode(); const previousToken = sourceCode.getTokenBefore(token); let previousComment; @@ -293,10 +295,10 @@ module.exports = { */ function reportRequiredBeginningSpace(node, token) { context.report({ - node: node, + node, loc: token.loc.start, message: `A space is required after '${token.value}'`, - fix: function(fixer) { + fix(fixer) { return fixer.insertTextAfter(token, ' '); } }); @@ -310,10 +312,10 @@ module.exports = { */ function reportRequiredEndingSpace(node, token) { context.report({ - node: node, + node, loc: token.loc.start, message: `A space is required before '${token.value}'`, - fix: function(fixer) { + fix(fixer) { return fixer.insertTextBefore(token, ' '); } }); @@ -344,6 +346,7 @@ module.exports = { return; } + const sourceCode = context.getSourceCode(); const first = context.getFirstToken(node); const last = sourceCode.getLastToken(node); let second = context.getTokenAfter(first, {includeComments: true}); diff --git a/lib/rules/jsx-equals-spacing.js b/lib/rules/jsx-equals-spacing.js index 2c927496e1..27f0bec9a5 100644 --- a/lib/rules/jsx-equals-spacing.js +++ b/lib/rules/jsx-equals-spacing.js @@ -2,6 +2,7 @@ * @fileoverview Disallow or enforce spaces around equal signs in JSX attributes. * @author ryym */ + 'use strict'; const docsUrl = require('../util/docsUrl'); @@ -25,9 +26,8 @@ module.exports = { }] }, - create: function(context) { + create(context) { const config = context.options[0]; - const sourceCode = context.getSourceCode(); /** * Determines a given attribute node has an equal sign. @@ -43,12 +43,13 @@ module.exports = { // -------------------------------------------------------------------------- return { - JSXOpeningElement: function(node) { - node.attributes.forEach(attrNode => { + JSXOpeningElement(node) { + node.attributes.forEach((attrNode) => { if (!hasEqual(attrNode)) { return; } + const sourceCode = context.getSourceCode(); const equalToken = sourceCode.getTokenAfter(attrNode.name); const spacedBefore = sourceCode.isSpaceBetweenTokens(attrNode.name, equalToken); const spacedAfter = sourceCode.isSpaceBetweenTokens(equalToken, attrNode.value); @@ -61,7 +62,7 @@ module.exports = { node: attrNode, loc: equalToken.loc.start, message: 'There should be no space before \'=\'', - fix: function(fixer) { + fix(fixer) { return fixer.removeRange([attrNode.name.range[1], equalToken.range[0]]); } }); @@ -71,7 +72,7 @@ module.exports = { node: attrNode, loc: equalToken.loc.start, message: 'There should be no space after \'=\'', - fix: function(fixer) { + fix(fixer) { return fixer.removeRange([equalToken.range[1], attrNode.value.range[0]]); } }); @@ -83,7 +84,7 @@ module.exports = { node: attrNode, loc: equalToken.loc.start, message: 'A space is required before \'=\'', - fix: function(fixer) { + fix(fixer) { return fixer.insertTextBefore(equalToken, ' '); } }); @@ -93,7 +94,7 @@ module.exports = { node: attrNode, loc: equalToken.loc.start, message: 'A space is required after \'=\'', - fix: function(fixer) { + fix(fixer) { return fixer.insertTextAfter(equalToken, ' '); } }); diff --git a/lib/rules/jsx-filename-extension.js b/lib/rules/jsx-filename-extension.js index 7e7a7c1439..d636b068ae 100644 --- a/lib/rules/jsx-filename-extension.js +++ b/lib/rules/jsx-filename-extension.js @@ -2,6 +2,7 @@ * @fileoverview Restrict file extensions that may contain JSX * @author Joe Lencioni */ + 'use strict'; const path = require('path'); @@ -42,7 +43,7 @@ module.exports = { }] }, - create: function(context) { + create(context) { let invalidExtension; let invalidNode; @@ -79,7 +80,7 @@ module.exports = { JSXElement: handleJSX, JSXFragment: handleJSX, - 'Program:exit': function() { + 'Program:exit': function () { if (!invalidNode) { return; } diff --git a/lib/rules/jsx-first-prop-new-line.js b/lib/rules/jsx-first-prop-new-line.js index dddc0cd993..30b4a574be 100644 --- a/lib/rules/jsx-first-prop-new-line.js +++ b/lib/rules/jsx-first-prop-new-line.js @@ -2,6 +2,7 @@ * @fileoverview Ensure proper position of the first property in JSX * @author Joachim Seminck */ + 'use strict'; const docsUrl = require('../util/docsUrl'); @@ -25,7 +26,7 @@ module.exports = { }] }, - create: function (context) { + create(context) { const configuration = context.options[0] || 'multiline-multiprop'; function isMultilineJSX(jsxNode) { @@ -33,18 +34,18 @@ module.exports = { } return { - JSXOpeningElement: function (node) { + JSXOpeningElement(node) { if ( (configuration === 'multiline' && isMultilineJSX(node)) || (configuration === 'multiline-multiprop' && isMultilineJSX(node) && node.attributes.length > 1) || (configuration === 'always') ) { - node.attributes.some(decl => { + node.attributes.some((decl) => { if (decl.loc.start.line === node.loc.start.line) { context.report({ node: decl, message: 'Property should be placed on a new line', - fix: function(fixer) { + fix(fixer) { return fixer.replaceTextRange([node.name.range[1], decl.range[0]], '\n'); } }); @@ -57,14 +58,12 @@ module.exports = { context.report({ node: firstNode, message: 'Property should be placed on the same line as the component declaration', - fix: function(fixer) { + fix(fixer) { return fixer.replaceTextRange([node.name.range[1], firstNode.range[0]], ' '); } }); - return; } } - return; } }; } diff --git a/lib/rules/jsx-fragments.js b/lib/rules/jsx-fragments.js index 10060036e4..353446c37d 100644 --- a/lib/rules/jsx-fragments.js +++ b/lib/rules/jsx-fragments.js @@ -2,6 +2,7 @@ * @fileoverview Enforce shorthand or standard form for React fragments. * @author Alex Zherdev */ + 'use strict'; const elementType = require('jsx-ast-utils/elementType'); @@ -33,9 +34,8 @@ module.exports = { }] }, - create: function(context) { + create(context) { const configuration = context.options[0] || 'syntax'; - const sourceCode = context.getSourceCode(); const reactPragma = pragmaUtil.getFromContext(context); const fragmentPragma = pragmaUtil.getFragmentFromContext(context); const openFragShort = '<>'; @@ -47,8 +47,8 @@ module.exports = { if (!versionUtil.testReactVersion(context, '16.2.0')) { context.report({ node, - message: 'Fragments are only supported starting from React v16.2. ' - + 'Please disable the `react/jsx-fragments` rule in ESLint settings or upgrade your version of React.' + message: 'Fragments are only supported starting from React v16.2. ' + + 'Please disable the `react/jsx-fragments` rule in ESLint settings or upgrade your version of React.' }); return true; } @@ -57,30 +57,32 @@ module.exports = { } function getFixerToLong(jsxFragment) { - return function(fixer) { + const sourceCode = context.getSourceCode(); + return function (fixer) { let source = sourceCode.getText(); 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 - sourceCode.getText(jsxFragment.openingFragment).length + + closeFragLong.length - sourceCode.getText(jsxFragment.closingFragment).length; const range = jsxFragment.range; return fixer.replaceTextRange(range, source.slice(range[0], range[1] + lengthDiff)); }; } function getFixerToShort(jsxElement) { - return function(fixer) { + const sourceCode = context.getSourceCode(); + return function (fixer) { let source = sourceCode.getText(); 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 = sourceCode.getText(jsxElement.openingElement).length - openFragShort.length + + sourceCode.getText(jsxElement.closingElement).length - closeFragShort.length; } else { source = replaceNode(source, jsxElement.openingElement, `${openFragShort}${closeFragShort}`); - lengthDiff = sourceCode.getText(jsxElement.openingElement).length - openFragShort.length - - closeFragShort.length; + lengthDiff = sourceCode.getText(jsxElement.openingElement).length - openFragShort.length - + closeFragShort.length; } const range = jsxElement.range; @@ -101,22 +103,22 @@ module.exports = { // const Fragment = React.Fragment; if ( - variableInit.type === 'MemberExpression' - && variableInit.object.type === 'Identifier' - && variableInit.object.name === reactPragma - && variableInit.property.type === 'Identifier' - && variableInit.property.name === fragmentPragma + variableInit.type === 'MemberExpression' && + variableInit.object.type === 'Identifier' && + variableInit.object.name === reactPragma && + variableInit.property.type === 'Identifier' && + variableInit.property.name === fragmentPragma ) { return true; } // const { Fragment } = require('react'); if ( - variableInit.callee - && variableInit.callee.name === 'require' - && variableInit.arguments - && variableInit.arguments[0] - && variableInit.arguments[0].value === 'react' + variableInit.callee && + variableInit.callee.name === 'require' && + variableInit.arguments && + variableInit.arguments[0] && + variableInit.arguments[0].value === 'react' ) { return true; } @@ -152,7 +154,7 @@ module.exports = { ImportDeclaration(node) { if (node.source && node.source.value === 'react') { - node.specifiers.forEach(spec => { + node.specifiers.forEach((spec) => { if (spec.imported && spec.imported.name === fragmentPragma) { if (spec.local) { fragmentNames.add(spec.local.name); @@ -162,8 +164,8 @@ module.exports = { } }, - 'Program:exit'() { - jsxElements.forEach(node => { + 'Program:exit': function () { + jsxElements.forEach((node) => { const openingEl = node.openingElement; const elName = elementType(openingEl); diff --git a/lib/rules/jsx-handler-names.js b/lib/rules/jsx-handler-names.js index 29a2db3d3b..42ef5c02c5 100644 --- a/lib/rules/jsx-handler-names.js +++ b/lib/rules/jsx-handler-names.js @@ -2,6 +2,7 @@ * @fileoverview Enforce event handler naming conventions in JSX * @author Jake Marsh */ + 'use strict'; const docsUrl = require('../util/docsUrl'); @@ -33,8 +34,7 @@ module.exports = { }] }, - create: function(context) { - const sourceCode = context.getSourceCode(); + create(context) { const configuration = context.options[0] || {}; const eventHandlerPrefix = configuration.eventHandlerPrefix || 'handle'; const eventHandlerPropPrefix = configuration.eventHandlerPropPrefix || 'on'; @@ -43,13 +43,13 @@ module.exports = { const PROP_EVENT_HANDLER_REGEX = new RegExp(`^(${eventHandlerPropPrefix}[A-Z].*|ref)$`); return { - JSXAttribute: function(node) { + JSXAttribute(node) { if (!node.value || !node.value.expression || !node.value.expression.object) { return; } const propKey = typeof node.name === 'object' ? node.name.name : node.name; - const propValue = sourceCode.getText(node.value.expression).replace(/^this\.|.*::/, ''); + const propValue = context.getSourceCode().getText(node.value.expression).replace(/^this\.|.*::/, ''); if (propKey === 'ref') { return; @@ -60,12 +60,12 @@ module.exports = { if (propIsEventHandler && !propFnIsNamedCorrectly) { context.report({ - node: node, + node, message: `Handler function for ${propKey} prop key must begin with '${eventHandlerPrefix}'` }); } else if (propFnIsNamedCorrectly && !propIsEventHandler) { context.report({ - node: node, + node, message: `Prop key for ${propValue} must begin with '${eventHandlerPropPrefix}'` }); } diff --git a/lib/rules/jsx-indent-props.js b/lib/rules/jsx-indent-props.js index 1509b64c89..c2bbe511dd 100644 --- a/lib/rules/jsx-indent-props.js +++ b/lib/rules/jsx-indent-props.js @@ -27,6 +27,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + 'use strict'; const astUtil = require('../util/ast'); @@ -54,7 +55,7 @@ module.exports = { }] }, - create: function(context) { + create(context) { const MESSAGE = 'Expected indentation of {{needed}} {{type}} {{characters}} but found {{gotten}}.'; const extraColumnStart = 0; @@ -62,8 +63,6 @@ module.exports = { /** @type {number|'first'} */ let indentSize = 4; - const sourceCode = context.getSourceCode(); - if (context.options.length) { if (context.options[0] === 'first') { indentSize = 'first'; @@ -85,17 +84,17 @@ module.exports = { */ function report(node, needed, gotten) { const msgContext = { - needed: needed, + needed, type: indentType, characters: needed === 1 ? 'character' : 'characters', - gotten: gotten + gotten }; context.report({ - node: node, + node, message: MESSAGE, data: msgContext, - fix: function(fixer) { + fix(fixer) { return fixer.replaceTextRange([node.range[0] - node.loc.start.column, node.range[0]], Array(needed + 1).join(indentType === 'space' ? ' ' : '\t')); } @@ -108,7 +107,7 @@ module.exports = { * @return {Number} Indent */ function getNodeIndent(node) { - let src = sourceCode.getText(node, node.loc.start.column + extraColumnStart); + let src = context.getSourceCode().getText(node, node.loc.start.column + extraColumnStart); const lines = src.split('\n'); src = lines[0]; @@ -129,7 +128,7 @@ module.exports = { * @param {Number} indent needed indent */ function checkNodesIndent(nodes, indent) { - nodes.forEach(node => { + nodes.forEach((node) => { const nodeIndent = getNodeIndent(node); if ( node.type !== 'ArrayExpression' && node.type !== 'ObjectExpression' && @@ -141,7 +140,7 @@ module.exports = { } return { - JSXOpeningElement: function(node) { + JSXOpeningElement(node) { if (!node.attributes.length) { return; } diff --git a/lib/rules/jsx-indent.js b/lib/rules/jsx-indent.js index 4b133fc364..9373e02616 100644 --- a/lib/rules/jsx-indent.js +++ b/lib/rules/jsx-indent.js @@ -27,6 +27,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + 'use strict'; const astUtil = require('../util/ast'); @@ -64,15 +65,13 @@ module.exports = { }] }, - create: function(context) { + create(context) { const MESSAGE = 'Expected indentation of {{needed}} {{type}} {{characters}} but found {{gotten}}.'; const extraColumnStart = 0; let indentType = 'space'; let indentSize = 4; - const sourceCode = context.getSourceCode(); - if (context.options.length) { if (context.options[0] === 'tab') { indentSize = 1; @@ -96,7 +95,7 @@ module.exports = { * @private */ function getFixerFunction(node, needed) { - return function(fixer) { + return function (fixer) { const indent = Array(needed + 1).join(indentChar); return fixer.replaceTextRange( [node.range[0] - node.loc.start.column, node.range[0]], @@ -114,23 +113,23 @@ module.exports = { */ function report(node, needed, gotten, loc) { const msgContext = { - needed: needed, + needed, type: indentType, characters: needed === 1 ? 'character' : 'characters', - gotten: gotten + gotten }; if (loc) { context.report({ - node: node, - loc: loc, + node, + loc, message: MESSAGE, data: msgContext, fix: getFixerFunction(node, needed) }); } else { context.report({ - node: node, + node, message: MESSAGE, data: msgContext, fix: getFixerFunction(node, needed) @@ -149,7 +148,7 @@ module.exports = { byLastLine = byLastLine || false; excludeCommas = excludeCommas || false; - let src = sourceCode.getText(node, node.loc.start.column + extraColumnStart); + let src = context.getSourceCode().getText(node, node.loc.start.column + extraColumnStart); const lines = src.split('\n'); if (byLastLine) { src = lines[lines.length - 1]; @@ -196,7 +195,7 @@ module.exports = { node.parent.parent && node.parent.parent.type === 'ConditionalExpression' && node.parent.parent.alternate === node.parent && - sourceCode.getTokenBefore(node).value !== '(' + context.getSourceCode().getTokenBefore(node).value !== '(' ); } @@ -221,6 +220,7 @@ module.exports = { } function handleOpeningElement(node) { + const sourceCode = context.getSourceCode(); let prevToken = sourceCode.getTokenBefore(node); if (!prevToken) { return; @@ -262,7 +262,7 @@ module.exports = { return; } const nameIndent = getNodeIndent(node.name); - const lastToken = sourceCode.getLastToken(node.value); + const lastToken = context.getSourceCode().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); @@ -274,7 +274,7 @@ module.exports = { JSXClosingElement: handleClosingElement, JSXClosingFragment: handleClosingElement, JSXAttribute: handleAttribute, - JSXExpressionContainer: function(node) { + JSXExpressionContainer(node) { if (!node.parent) { return; } diff --git a/lib/rules/jsx-key.js b/lib/rules/jsx-key.js index 4583531f38..83e7873cfd 100644 --- a/lib/rules/jsx-key.js +++ b/lib/rules/jsx-key.js @@ -2,6 +2,7 @@ * @fileoverview Report missing `key` props in iterators/collection literals. * @author Ben Mosher */ + 'use strict'; const hasProp = require('jsx-ast-utils/hasProp'); @@ -23,11 +24,11 @@ module.exports = { schema: [] }, - create: function(context) { + create(context) { function checkIteratorElement(node) { if (node.type === 'JSXElement' && !hasProp(node.openingElement.attributes, 'key')) { context.report({ - node: node, + node, message: 'Missing "key" prop for element in iterator' }); } @@ -38,21 +39,21 @@ module.exports = { } return { - JSXElement: function(node) { + JSXElement(node) { if (hasProp(node.openingElement.attributes, 'key')) { return; } if (node.parent.type === 'ArrayExpression') { context.report({ - node: node, + node, message: 'Missing "key" prop for element in array' }); } }, // Array.prototype.map - CallExpression: function (node) { + CallExpression(node) { if (node.callee && node.callee.type !== 'MemberExpression') { return; } diff --git a/lib/rules/jsx-max-depth.js b/lib/rules/jsx-max-depth.js index 7c64f93b64..1e0edd5369 100644 --- a/lib/rules/jsx-max-depth.js +++ b/lib/rules/jsx-max-depth.js @@ -2,6 +2,7 @@ * @fileoverview Validate JSX maximum depth * @author Chris */ + 'use strict'; const has = require('has'); @@ -33,7 +34,7 @@ module.exports = { } ] }, - create: function(context) { + create(context) { const MESSAGE = 'Expected the depth of nested jsx elements to be <= {{needed}}, but found {{found}}.'; const DEFAULT_DEPTH = 2; @@ -70,7 +71,7 @@ module.exports = { function report(node, depth) { context.report({ - node: node, + node, message: MESSAGE, data: { found: depth, @@ -87,10 +88,10 @@ module.exports = { if (has(refs[i], 'writeExpr')) { const writeExpr = refs[i].writeExpr; - return jsxUtil.isJSX(writeExpr) - && writeExpr - || (writeExpr && writeExpr.type === 'Identifier') - && findJSXElementOrFragment(variables, writeExpr.name); + return jsxUtil.isJSX(writeExpr) && + writeExpr || + (writeExpr && writeExpr.type === 'Identifier') && + findJSXElementOrFragment(variables, writeExpr.name); } } @@ -103,7 +104,7 @@ module.exports = { function checkDescendant(baseDepth, children) { baseDepth++; - (children || []).forEach(node => { + (children || []).forEach((node) => { if (!hasJSX(node)) { return; } @@ -131,7 +132,7 @@ module.exports = { JSXElement: handleJSX, JSXFragment: handleJSX, - JSXExpressionContainer: function(node) { + JSXExpressionContainer(node) { if (node.expression.type !== 'Identifier') { return; } diff --git a/lib/rules/jsx-max-props-per-line.js b/lib/rules/jsx-max-props-per-line.js index 2fa8d96423..06dad9d147 100644 --- a/lib/rules/jsx-max-props-per-line.js +++ b/lib/rules/jsx-max-props-per-line.js @@ -35,20 +35,20 @@ module.exports = { }] }, - create: function (context) { - const sourceCode = context.getSourceCode(); + create(context) { const configuration = context.options[0] || {}; const maximum = configuration.maximum || 1; const when = configuration.when || 'always'; function getPropName(propNode) { if (propNode.type === 'JSXSpreadAttribute') { - return sourceCode.getText(propNode.argument); + return context.getSourceCode().getText(propNode.argument); } return propNode.name.name; } function generateFixFunction(line, max) { + const sourceCode = context.getSourceCode(); const output = []; const front = line[0].range[0]; const back = line[line.length - 1].range[1]; @@ -62,13 +62,13 @@ module.exports = { }, '')); } const code = output.join('\n'); - return function(fixer) { + return function (fixer) { return fixer.replaceTextRange([front, back], code); }; } return { - JSXOpeningElement: function (node) { + JSXOpeningElement(node) { if (!node.attributes.length) { return; } @@ -89,7 +89,7 @@ module.exports = { return decl; }); - linePartitionedProps.forEach(propsInLine => { + linePartitionedProps.forEach((propsInLine) => { if (propsInLine.length > maximum) { const name = getPropName(propsInLine[maximum]); context.report({ diff --git a/lib/rules/jsx-no-bind.js b/lib/rules/jsx-no-bind.js index f6e87b22b7..612f492cea 100644 --- a/lib/rules/jsx-no-bind.js +++ b/lib/rules/jsx-no-bind.js @@ -4,6 +4,7 @@ * @author Daniel Lo Nigro * @author Jacky Ho */ + 'use strict'; const propName = require('jsx-ast-utils/propName'); @@ -59,7 +60,7 @@ module.exports = { }] }, - create: Components.detect(context => { + create: Components.detect((context) => { const configuration = context.options[0] || {}; // Keep track of all the variable names pointing to a bind call, @@ -86,26 +87,19 @@ module.exports = { node.callee.property.name === 'bind' ) { return 'bindCall'; - } else if ( - nodeType === 'ConditionalExpression' - ) { + } + if (nodeType === 'ConditionalExpression') { return getNodeViolationType(node.test) || getNodeViolationType(node.consequent) || getNodeViolationType(node.alternate); - } else if ( - !configuration.allowArrowFunctions && - nodeType === 'ArrowFunctionExpression' - ) { + } + if (!configuration.allowArrowFunctions && nodeType === 'ArrowFunctionExpression') { return 'arrowFunc'; - } else if ( - !configuration.allowFunctions && - nodeType === 'FunctionExpression' - ) { + } + if (!configuration.allowFunctions && nodeType === 'FunctionExpression') { return 'func'; - } else if ( - !configuration.allowBind && - nodeType === 'BindExpression' - ) { + } + if (!configuration.allowBind && nodeType === 'BindExpression') { return 'bindExpression'; } @@ -126,9 +120,9 @@ module.exports = { const blockSets = blockVariableNameSets[blockStart]; const violationTypes = Object.keys(blockSets); - return violationTypes.find(type => { + return violationTypes.find((type) => { if (blockSets[type].has(name)) { - context.report({node: node, message: violationMessageStore[type]}); + context.report({node, message: violationMessageStore[type]}); return true; } @@ -165,7 +159,7 @@ module.exports = { } }, - JSXAttribute: function (node) { + JSXAttribute(node) { const isRef = configuration.ignoreRefs && propName(node) === 'ref'; if (isRef || !node.value || !node.value.expression) { return; @@ -182,7 +176,7 @@ module.exports = { findVariableViolation(node, valueNode.name); } else if (nodeViolationType) { context.report({ - node: node, message: violationMessageStore[nodeViolationType] + node, message: violationMessageStore[nodeViolationType] }); } } diff --git a/lib/rules/jsx-no-comment-textnodes.js b/lib/rules/jsx-no-comment-textnodes.js index 62da4e5b73..8f7ddfb68e 100644 --- a/lib/rules/jsx-no-comment-textnodes.js +++ b/lib/rules/jsx-no-comment-textnodes.js @@ -2,6 +2,7 @@ * @fileoverview Comments inside children section of tag should be placed inside braces. * @author Ben Vinegar */ + 'use strict'; const docsUrl = require('../util/docsUrl'); @@ -26,7 +27,7 @@ module.exports = { }] }, - create: function(context) { + create(context) { function reportLiteralNode(node) { context.report(node, 'Comments inside children section of tag should be placed inside braces'); } @@ -36,10 +37,9 @@ module.exports = { // -------------------------------------------------------------------------- return { - Literal: function(node) { - const sourceCode = context.getSourceCode(); + Literal(node) { // since babel-eslint has the wrong node.raw, we'll get the source text - const rawValue = sourceCode.getText(node); + const rawValue = context.getSourceCode().getText(node); if (/^\s*\/(\/|\*)/m.test(rawValue)) { // inside component, e.g.
literal
if (node.parent.type !== 'JSXAttribute' && diff --git a/lib/rules/jsx-no-duplicate-props.js b/lib/rules/jsx-no-duplicate-props.js index 7fc2c51db7..7d9e00689e 100644 --- a/lib/rules/jsx-no-duplicate-props.js +++ b/lib/rules/jsx-no-duplicate-props.js @@ -32,15 +32,15 @@ module.exports = { }] }, - create: function (context) { + create(context) { const configuration = context.options[0] || {}; const ignoreCase = configuration.ignoreCase || false; return { - JSXOpeningElement: function (node) { + JSXOpeningElement(node) { const props = {}; - node.attributes.forEach(decl => { + node.attributes.forEach((decl) => { if (decl.type === 'JSXSpreadAttribute') { return; } diff --git a/lib/rules/jsx-no-literals.js b/lib/rules/jsx-no-literals.js index d6bff3ad00..bebddb067a 100644 --- a/lib/rules/jsx-no-literals.js +++ b/lib/rules/jsx-no-literals.js @@ -3,6 +3,7 @@ * @author Caleb Morris * @author David Buchan-Swanson */ + 'use strict'; const docsUrl = require('../util/docsUrl'); @@ -31,9 +32,8 @@ module.exports = { }] }, - create: function(context) { + create(context) { const isNoStrings = context.options[0] ? context.options[0].noStrings : false; - const sourceCode = context.getSourceCode(); const message = isNoStrings ? 'Strings not allowed in JSX files' : @@ -41,8 +41,8 @@ module.exports = { function reportLiteralNode(node) { context.report({ - node: node, - message: `${message}: “${sourceCode.getText(node).trim()}”` + node, + message: `${message}: “${context.getSourceCode().getText(node).trim()}”` }); } @@ -72,19 +72,19 @@ module.exports = { return { - Literal: function(node) { + Literal(node) { if (getValidation(node)) { reportLiteralNode(node); } }, - JSXText: function(node) { + JSXText(node) { if (getValidation(node)) { reportLiteralNode(node); } }, - TemplateLiteral: function(node) { + TemplateLiteral(node) { const parent = getParentIgnoringBinaryExpressions(node); if (isNoStrings && parent.type === 'JSXExpressionContainer') { reportLiteralNode(node); diff --git a/lib/rules/jsx-no-target-blank.js b/lib/rules/jsx-no-target-blank.js index 07aae2fa1c..46c266ab85 100644 --- a/lib/rules/jsx-no-target-blank.js +++ b/lib/rules/jsx-no-target-blank.js @@ -2,6 +2,7 @@ * @fileoverview Forbid target='_blank' attribute * @author Kevin Miller */ + 'use strict'; const docsUrl = require('../util/docsUrl'); @@ -33,7 +34,7 @@ function hasDynamicLink(element, linkAttribute) { } function hasSecureRel(element) { - return element.attributes.find(attr => { + return element.attributes.find((attr) => { if (attr.type === 'JSXAttribute' && attr.name.name === 'rel') { const tags = attr.value && attr.value.type === 'Literal' && attr.value.value.toLowerCase().split(' '); return tags && (tags.indexOf('noopener') >= 0 && tags.indexOf('noreferrer') >= 0); @@ -61,13 +62,13 @@ module.exports = { }] }, - create: function(context) { + create(context) { const configuration = context.options[0] || {}; const enforceDynamicLinks = configuration.enforceDynamicLinks || 'always'; const components = linkComponentsUtil.getLinkComponents(context); return { - JSXAttribute: function(node) { + JSXAttribute(node) { if (!components.has(node.parent.name.name) || !isTargetBlank(node) || hasSecureRel(node.parent)) { return; } diff --git a/lib/rules/jsx-no-undef.js b/lib/rules/jsx-no-undef.js index fc8a3bd2e5..a887e48bb1 100644 --- a/lib/rules/jsx-no-undef.js +++ b/lib/rules/jsx-no-undef.js @@ -31,7 +31,7 @@ module.exports = { }] }, - create: function(context) { + create(context) { const config = context.options[0] || {}; const allowGlobals = config.allowGlobals || false; @@ -77,13 +77,13 @@ module.exports = { } context.report({ - node: node, + node, message: `'${node.name}' is not defined.` }); } return { - JSXOpeningElement: function(node) { + JSXOpeningElement(node) { switch (node.name.type) { case 'JSXIdentifier': if (jsxUtil.isDOMComponent(node)) { diff --git a/lib/rules/jsx-one-expression-per-line.js b/lib/rules/jsx-one-expression-per-line.js index d733a03d79..4ce00a9063 100644 --- a/lib/rules/jsx-one-expression-per-line.js +++ b/lib/rules/jsx-one-expression-per-line.js @@ -38,16 +38,15 @@ module.exports = { ] }, - create: function (context) { + create(context) { const options = Object.assign({}, optionDefaults, context.options[0]); - const sourceCode = context.getSourceCode(); - function nodeKey (node) { + function nodeKey(node) { return `${node.loc.start.line},${node.loc.start.column}`; } - function nodeDescriptor (n) { - return n.openingElement ? n.openingElement.name.name : sourceCode.getText(n).replace(/\n/g, ''); + function nodeDescriptor(n) { + return n.openingElement ? n.openingElement.name.name : context.getSourceCode().getText(n).replace(/\n/g, ''); } function handleJSX(node) { @@ -85,7 +84,7 @@ module.exports = { const childrenGroupedByLine = {}; const fixDetailsByNode = {}; - children.forEach(child => { + children.forEach((child) => { let countNewLinesBeforeContent = 0; let countNewLinesAfterContent = 0; @@ -118,7 +117,7 @@ module.exports = { } }); - Object.keys(childrenGroupedByLine).forEach(_line => { + Object.keys(childrenGroupedByLine).forEach((_line) => { const line = parseInt(_line, 10); const firstIndex = 0; const lastIndex = childrenGroupedByLine[line].length - 1; @@ -144,23 +143,23 @@ module.exports = { // nextChild = childrenGroupedByLine[line][i + 1]; } - function spaceBetweenPrev () { + function spaceBetweenPrev() { return ((prevChild.type === 'Literal' || prevChild.type === 'JSXText') && / $/.test(prevChild.raw)) || ((child.type === 'Literal' || child.type === 'JSXText') && /^ /.test(child.raw)) || - sourceCode.isSpaceBetweenTokens(prevChild, child); + context.getSourceCode().isSpaceBetweenTokens(prevChild, child); } - function spaceBetweenNext () { + function spaceBetweenNext() { return ((nextChild.type === 'Literal' || nextChild.type === 'JSXText') && /^ /.test(nextChild.raw)) || ((child.type === 'Literal' || child.type === 'JSXText') && / $/.test(child.raw)) || - sourceCode.isSpaceBetweenTokens(child, nextChild); + context.getSourceCode().isSpaceBetweenTokens(child, nextChild); } if (!prevChild && !nextChild) { return; } - const source = sourceCode.getText(child); + const source = context.getSourceCode().getText(child); const leadingSpace = !!(prevChild && spaceBetweenPrev()); const trailingSpace = !!(nextChild && spaceBetweenNext()); const leadingNewLine = !!prevChild; @@ -171,7 +170,7 @@ module.exports = { if (!fixDetailsByNode[key]) { fixDetailsByNode[key] = { node: child, - source: source, + source, descriptor: nodeDescriptor(child) }; } @@ -191,7 +190,7 @@ module.exports = { }); }); - Object.keys(fixDetailsByNode).forEach(key => { + Object.keys(fixDetailsByNode).forEach((key) => { const details = fixDetailsByNode[key]; const nodeToReport = details.node; @@ -208,7 +207,7 @@ module.exports = { context.report({ node: nodeToReport, message: `\`${descriptor}\` must be placed on a new line`, - fix: function (fixer) { + fix(fixer) { return fixer.replaceText(nodeToReport, replaceText); } }); diff --git a/lib/rules/jsx-pascal-case.js b/lib/rules/jsx-pascal-case.js index e7629c57cd..34de9ae67b 100644 --- a/lib/rules/jsx-pascal-case.js +++ b/lib/rules/jsx-pascal-case.js @@ -43,13 +43,13 @@ module.exports = { }] }, - create: function(context) { + create(context) { const configuration = context.options[0] || {}; const allowAllCaps = configuration.allowAllCaps || false; const ignore = configuration.ignore || []; return { - JSXOpeningElement: function(node) { + JSXOpeningElement(node) { let name = elementType(node); // Get namespace if the type is JSXNamespacedName or JSXMemberExpression @@ -66,7 +66,7 @@ module.exports = { if (!isPascalCase && !isCompatTag && !isAllowedAllCaps && !isIgnored) { context.report({ - node: node, + node, message: `Imported JSX component ${name} must be in PascalCase` }); } diff --git a/lib/rules/jsx-props-no-multi-spaces.js b/lib/rules/jsx-props-no-multi-spaces.js index 8ce2cb48ff..a44494e27c 100644 --- a/lib/rules/jsx-props-no-multi-spaces.js +++ b/lib/rules/jsx-props-no-multi-spaces.js @@ -23,13 +23,11 @@ module.exports = { schema: [] }, - create: function (context) { - const sourceCode = context.getSourceCode(); - + create(context) { function getPropName(propNode) { switch (propNode.type) { case 'JSXSpreadAttribute': - return sourceCode.getText(propNode.argument); + return context.getSourceCode().getText(propNode.argument); case 'JSXIdentifier': return propNode.name; case 'JSXMemberExpression': @@ -43,12 +41,12 @@ module.exports = { if (prev.loc.end.line !== node.loc.end.line) { return; } - const between = sourceCode.text.slice(prev.range[1], node.range[0]); + const between = context.getSourceCode().text.slice(prev.range[1], node.range[0]); if (between !== ' ') { context.report({ - node: node, + node, message: `Expected only one space between "${getPropName(prev)}" and "${getPropName(node)}"`, - fix: function(fixer) { + fix(fixer) { return fixer.replaceTextRange([prev.range[1], node.range[0]], ' '); } }); @@ -81,7 +79,7 @@ module.exports = { } return { - JSXOpeningElement: function (node) { + JSXOpeningElement(node) { node.attributes.reduce((prev, prop) => { checkSpacing(prev, prop); return prop; diff --git a/lib/rules/jsx-props-no-spreading.js b/lib/rules/jsx-props-no-spreading.js index f8915d8337..f5ad8087f5 100644 --- a/lib/rules/jsx-props-no-spreading.js +++ b/lib/rules/jsx-props-no-spreading.js @@ -2,6 +2,7 @@ * @fileoverview Prevent JSX prop spreading * @author Ashish Gambhir */ + 'use strict'; const docsUrl = require('../util/docsUrl'); @@ -65,14 +66,14 @@ module.exports = { }] }, - create: function (context) { + create(context) { const configuration = context.options[0] || {}; const ignoreHtmlTags = (configuration.html || DEFAULTS.html) === OPTIONS.ignore; const ignoreCustomTags = (configuration.custom || DEFAULTS.custom) === OPTIONS.ignore; const exceptions = configuration.exceptions || DEFAULTS.exceptions; const isException = (tag, allExceptions) => allExceptions.indexOf(tag) !== -1; return { - JSXSpreadAttribute: function (node) { + JSXSpreadAttribute(node) { const tagName = node.parent.name.name; const isHTMLTag = tagName && tagName[0] !== tagName[0].toUpperCase(); const isCustomTag = tagName && tagName[0] === tagName[0].toUpperCase(); @@ -87,7 +88,7 @@ module.exports = { return; } context.report({ - node: node, + node, message: 'Prop spreading is forbidden' }); } diff --git a/lib/rules/jsx-sort-default-props.js b/lib/rules/jsx-sort-default-props.js index 13e869fec0..7ba0447f90 100644 --- a/lib/rules/jsx-sort-default-props.js +++ b/lib/rules/jsx-sort-default-props.js @@ -2,6 +2,7 @@ * @fileoverview Enforce default props alphabetical sorting * @author Vladimir Kattsov */ + 'use strict'; const variableUtil = require('../util/variable'); @@ -32,8 +33,7 @@ module.exports = { }] }, - create: function(context) { - const sourceCode = context.getSourceCode(); + create(context) { const configuration = context.options[0] || {}; const ignoreCase = configuration.ignoreCase || false; @@ -45,11 +45,13 @@ module.exports = { function getPropertyName(node) { if (node.key || ['MethodDefinition', 'Property'].indexOf(node.type) !== -1) { return node.key.name; - } else if (node.type === 'MemberExpression') { + } + if (node.type === 'MemberExpression') { return node.property.name; // Special case for class properties // (babel-eslint@5 does not expose property name so we have to rely on tokens) - } else if (node.type === 'ClassProperty') { + } + if (node.type === 'ClassProperty') { const tokens = context.getFirstTokens(node, 2); return tokens[1] && tokens[1].type === 'Identifier' ? tokens[1].value : tokens[0].value; } @@ -67,7 +69,7 @@ module.exports = { } function getKey(node) { - return sourceCode.getText(node.key || node.argument); + return context.getSourceCode().getText(node.key || node.argument); } /** @@ -126,18 +128,20 @@ module.exports = { case 'ObjectExpression': checkSorted(node.properties); break; - case 'Identifier': + case 'Identifier': { const propTypesObject = findVariableByName(node.name); if (propTypesObject && propTypesObject.properties) { checkSorted(propTypesObject.properties); } break; - case 'CallExpression': + } + case 'CallExpression': { const innerNode = node.arguments && node.arguments[0]; if (propWrapperUtil.isPropWrapperFunction(context, node.callee.name) && innerNode) { checkNode(innerNode); } break; + } default: break; } @@ -148,7 +152,7 @@ module.exports = { // -------------------------------------------------------------------------- return { - ClassProperty: function(node) { + ClassProperty(node) { if (!isDefaultPropsDeclaration(node)) { return; } @@ -156,7 +160,7 @@ module.exports = { checkNode(node.value); }, - MemberExpression: function(node) { + MemberExpression(node) { if (!isDefaultPropsDeclaration(node)) { return; } diff --git a/lib/rules/jsx-sort-props.js b/lib/rules/jsx-sort-props.js index 75a5d45e78..a3ff43ab4e 100644 --- a/lib/rules/jsx-sort-props.js +++ b/lib/rules/jsx-sort-props.js @@ -2,6 +2,7 @@ * @fileoverview Enforce props alphabetical sorting * @author Ilya Volodin, Yannick Croissant */ + 'use strict'; const propName = require('jsx-ast-utils/propName'); @@ -36,7 +37,8 @@ function contextCompare(a, b, options) { const bIsReserved = isReservedPropName(bProp, options.reservedList); if (aIsReserved && !bIsReserved) { return -1; - } else if (!aIsReserved && bIsReserved) { + } + if (!aIsReserved && bIsReserved) { return 1; } } @@ -46,7 +48,8 @@ function contextCompare(a, b, options) { const bIsCallback = isCallbackPropName(bProp); if (aIsCallback && !bIsCallback) { return 1; - } else if (!aIsCallback && bIsCallback) { + } + if (!aIsCallback && bIsCallback) { return -1; } } @@ -55,7 +58,8 @@ function contextCompare(a, b, options) { const shorthandSign = options.shorthandFirst ? -1 : 1; if (!a.value && b.value) { return shorthandSign; - } else if (a.value && !b.value) { + } + if (a.value && !b.value) { return -shorthandSign; } } @@ -114,16 +118,21 @@ const generateFixerFunction = (node, context, reservedList) => { // Sort props according to the context. Only supports ignoreCase. // Since we cannot safely move JSXSpreadAttribute (due to potential variable overrides), // we only consider groups of sortable attributes. - const options = {ignoreCase, callbacksLast, shorthandFirst, shorthandLast, - noSortAlphabetically, reservedFirst, reservedList}; + const options = { + ignoreCase, + callbacksLast, + shorthandFirst, + shorthandLast, + noSortAlphabetically, + reservedFirst, + reservedList + }; const sortableAttributeGroups = getGroupsOfSortableAttributes(attributes); - const sortedAttributeGroups = sortableAttributeGroups.slice(0).map(group => - group.slice(0).sort((a, b) => - contextCompare(a, b, options) - ) - ); + const sortedAttributeGroups = sortableAttributeGroups + .slice(0) + .map(group => group.slice(0).sort((a, b) => contextCompare(a, b, options))); - return function(fixer) { + return function (fixer) { const fixers = []; let source = sourceCode.getText(); @@ -144,7 +153,7 @@ const generateFixerFunction = (node, context, reservedList) => { const rangeStart = fixers[fixers.length - 1].range[0]; const rangeEnd = fixers[0].range[1]; - fixers.forEach(fix => { + fixers.forEach((fix) => { source = `${source.substr(0, fix.range[0])}${fix.text}${source.substr(fix.range[1])}`; }); @@ -163,22 +172,21 @@ function validateReservedFirstConfig(context, reservedFirst) { if (reservedFirst) { if (Array.isArray(reservedFirst)) { // Only allow a subset of reserved words in customized lists - // eslint-disable-next-line consistent-return - const nonReservedWords = reservedFirst.filter(word => { - if (!isReservedPropName(word, RESERVED_PROPS_LIST)) { - return true; - } - }); + const nonReservedWords = reservedFirst.filter(word => !isReservedPropName( + word, + RESERVED_PROPS_LIST + )); if (reservedFirst.length === 0) { - return function(decl) { + return function (decl) { context.report({ node: decl, message: 'A customized reserved first list must not be empty' }); }; - } else if (nonReservedWords.length > 0) { - return function(decl) { + } + if (nonReservedWords.length > 0) { + return function (decl) { context.report({ node: decl, message: 'A customized reserved first list must only contain a subset of React reserved props.' + @@ -233,7 +241,7 @@ module.exports = { }] }, - create: function(context) { + create(context) { const configuration = context.options[0] || {}; const ignoreCase = configuration.ignoreCase || false; const callbacksLast = configuration.callbacksLast || false; @@ -245,7 +253,7 @@ module.exports = { let reservedList = Array.isArray(reservedFirst) ? reservedFirst : RESERVED_PROPS_LIST; return { - JSXOpeningElement: function(node) { + JSXOpeningElement(node) { // `dangerouslySetInnerHTML` is only "reserved" on DOM components if (reservedFirst && !jsxUtil.isDOMComponent(node)) { reservedList = reservedList.filter(prop => prop !== 'dangerouslySetInnerHTML'); diff --git a/lib/rules/jsx-space-before-closing.js b/lib/rules/jsx-space-before-closing.js index a436fe9e09..284aa06784 100644 --- a/lib/rules/jsx-space-before-closing.js +++ b/lib/rules/jsx-space-before-closing.js @@ -3,6 +3,7 @@ * @author ryym * @deprecated */ + 'use strict'; const getTokenBeforeClosingBracket = require('../util/getTokenBeforeClosingBracket'); @@ -31,9 +32,8 @@ module.exports = { }] }, - create: function(context) { + create(context) { const configuration = context.options[0] || 'always'; - const sourceCode = context.getSourceCode(); const NEVER_MESSAGE = 'A space is forbidden before closing bracket'; const ALWAYS_MESSAGE = 'A space is required before closing bracket'; @@ -43,11 +43,13 @@ module.exports = { // -------------------------------------------------------------------------- return { - JSXOpeningElement: function(node) { + JSXOpeningElement(node) { if (!node.selfClosing) { return; } + const sourceCode = context.getSourceCode(); + const leftToken = getTokenBeforeClosingBracket(node); const closingSlash = sourceCode.getTokenAfter(leftToken); @@ -59,7 +61,7 @@ module.exports = { context.report({ loc: closingSlash.loc.start, message: ALWAYS_MESSAGE, - fix: function(fixer) { + fix(fixer) { return fixer.insertTextBefore(closingSlash, ' '); } }); @@ -67,7 +69,7 @@ module.exports = { context.report({ loc: closingSlash.loc.start, message: NEVER_MESSAGE, - fix: function(fixer) { + fix(fixer) { const previousToken = sourceCode.getTokenBefore(closingSlash); return fixer.removeRange([previousToken.range[1], closingSlash.range[0]]); } @@ -75,7 +77,7 @@ module.exports = { } }, - Program: function() { + Program() { if (isWarnedForDeprecation) { return; } diff --git a/lib/rules/jsx-tag-spacing.js b/lib/rules/jsx-tag-spacing.js index 95ab10090e..9568672ac6 100644 --- a/lib/rules/jsx-tag-spacing.js +++ b/lib/rules/jsx-tag-spacing.js @@ -2,6 +2,7 @@ * @fileoverview Validates whitespace in and around the JSX opening and closing brackets * @author Diogo Franco (Kovensky) */ + 'use strict'; const getTokenBeforeClosingBracket = require('../util/getTokenBeforeClosingBracket'); @@ -29,26 +30,26 @@ function validateClosingSlash(context, node, option) { if (option === 'never') { if (!adjacent) { context.report({ - node: node, + node, loc: { start: lastTokens[0].loc.start, end: lastTokens[1].loc.end }, message: SELF_CLOSING_NEVER_MESSAGE, - fix: function(fixer) { + fix(fixer) { return fixer.removeRange([lastTokens[0].range[1], lastTokens[1].range[0]]); } }); } } else if (option === 'always' && adjacent) { context.report({ - node: node, + node, loc: { start: lastTokens[0].loc.start, end: lastTokens[1].loc.end }, message: SELF_CLOSING_ALWAYS_MESSAGE, - fix: function(fixer) { + fix(fixer) { return fixer.insertTextBefore(lastTokens[1], ' '); } }); @@ -61,26 +62,26 @@ function validateClosingSlash(context, node, option) { if (option === 'never') { if (!adjacent) { context.report({ - node: node, + node, loc: { start: firstTokens[0].loc.start, end: firstTokens[1].loc.end }, message: NEVER_MESSAGE, - fix: function(fixer) { + fix(fixer) { return fixer.removeRange([firstTokens[0].range[1], firstTokens[1].range[0]]); } }); } } else if (option === 'always' && adjacent) { context.report({ - node: node, + node, loc: { start: firstTokens[0].loc.start, end: firstTokens[1].loc.end }, message: ALWAYS_MESSAGE, - fix: function(fixer) { + fix(fixer) { return fixer.insertTextBefore(firstTokens[1], ' '); } }); @@ -103,19 +104,19 @@ function validateBeforeSelfClosing(context, node, option) { if (option === 'always' && !sourceCode.isSpaceBetweenTokens(leftToken, closingSlash)) { context.report({ - node: node, + node, loc: closingSlash.loc.start, message: ALWAYS_MESSAGE, - fix: function(fixer) { + fix(fixer) { return fixer.insertTextBefore(closingSlash, ' '); } }); } else if (option === 'never' && sourceCode.isSpaceBetweenTokens(leftToken, closingSlash)) { context.report({ - node: node, + node, loc: closingSlash.loc.start, message: NEVER_MESSAGE, - fix: function(fixer) { + fix(fixer) { const previousToken = sourceCode.getTokenBefore(closingSlash); return fixer.removeRange([previousToken.range[1], closingSlash.range[0]]); } @@ -142,26 +143,26 @@ function validateAfterOpening(context, node, option) { if (option === 'never' || option === 'allow-multiline') { if (!adjacent) { context.report({ - node: node, + node, loc: { start: openingToken.loc.start, end: node.name.loc.start }, message: NEVER_MESSAGE, - fix: function(fixer) { + fix(fixer) { return fixer.removeRange([openingToken.range[1], node.name.range[0]]); } }); } } else if (option === 'always' && adjacent) { context.report({ - node: node, + node, loc: { start: openingToken.loc.start, end: node.name.loc.start }, message: ALWAYS_MESSAGE, - fix: function(fixer) { + fix(fixer) { return fixer.insertTextBefore(node.name, ' '); } }); @@ -188,25 +189,25 @@ function validateBeforeClosing(context, node, option) { if (option === 'never' && !adjacent) { context.report({ - node: node, + node, loc: { start: leftToken.loc.end, end: closingToken.loc.start }, message: NEVER_MESSAGE, - fix: function(fixer) { + fix(fixer) { return fixer.removeRange([leftToken.range[1], closingToken.range[0]]); } }); } else if (option === 'always' && adjacent) { context.report({ - node: node, + node, loc: { start: leftToken.loc.end, end: closingToken.loc.start }, message: ALWAYS_MESSAGE, - fix: function(fixer) { + fix(fixer) { return fixer.insertTextBefore(closingToken, ' '); } }); @@ -256,11 +257,11 @@ module.exports = { } ] }, - create: function (context) { + create(context) { const options = Object.assign({}, optionDefaults, context.options[0]); return { - JSXOpeningElement: function (node) { + JSXOpeningElement(node) { if (options.closingSlash !== 'allow' && node.selfClosing) { validateClosingSlash(context, node, options.closingSlash); } @@ -274,7 +275,7 @@ module.exports = { validateBeforeClosing(context, node, options.beforeClosing); } }, - JSXClosingElement: function (node) { + JSXClosingElement(node) { if (options.afterOpening !== 'allow') { validateAfterOpening(context, node, options.afterOpening); } diff --git a/lib/rules/jsx-uses-react.js b/lib/rules/jsx-uses-react.js index ffafc4049d..bebcdf3cfa 100644 --- a/lib/rules/jsx-uses-react.js +++ b/lib/rules/jsx-uses-react.js @@ -2,6 +2,7 @@ * @fileoverview Prevent React to be marked as unused * @author Glen Mailer */ + 'use strict'; const pragmaUtil = require('../util/pragma'); @@ -22,7 +23,7 @@ module.exports = { schema: [] }, - create: function(context) { + create(context) { const pragma = pragmaUtil.getFromContext(context); function handleOpeningElement() { diff --git a/lib/rules/jsx-uses-vars.js b/lib/rules/jsx-uses-vars.js index 8fb0967c37..86a32f1f46 100644 --- a/lib/rules/jsx-uses-vars.js +++ b/lib/rules/jsx-uses-vars.js @@ -2,6 +2,7 @@ * @fileoverview Prevent variables used in JSX to be marked as unused * @author Yannick Croissant */ + 'use strict'; const docsUrl = require('../util/docsUrl'); @@ -21,9 +22,9 @@ module.exports = { schema: [] }, - create: function(context) { + create(context) { return { - JSXOpeningElement: function(node) { + JSXOpeningElement(node) { let name; if (node.name.namespace && node.name.namespace.name) { // diff --git a/lib/rules/jsx-wrap-multilines.js b/lib/rules/jsx-wrap-multilines.js index 53434daf3f..374d0db5dc 100644 --- a/lib/rules/jsx-wrap-multilines.js +++ b/lib/rules/jsx-wrap-multilines.js @@ -2,6 +2,7 @@ * @fileoverview Prevent missing parentheses around multilines JSX * @author Yannick Croissant */ + 'use strict'; const has = require('has'); @@ -69,9 +70,7 @@ module.exports = { }] }, - create: function(context) { - const sourceCode = context.getSourceCode(); - + create(context) { function getOption(type) { const userOptions = context.options[0] || {}; if (has(userOptions, type)) { @@ -86,6 +85,7 @@ module.exports = { } function isParenthesised(node) { + const sourceCode = context.getSourceCode(); const previousToken = sourceCode.getTokenBefore(node); const nextToken = sourceCode.getTokenAfter(node); @@ -95,7 +95,7 @@ module.exports = { } function needsOpeningNewLine(node) { - const previousToken = sourceCode.getTokenBefore(node); + const previousToken = context.getSourceCode().getTokenBefore(node); if (!isParenthesised(node)) { return false; @@ -109,7 +109,7 @@ module.exports = { } function needsClosingNewLine(node) { - const nextToken = sourceCode.getTokenAfter(node); + const nextToken = context.getSourceCode().getTokenAfter(node); if (!isParenthesised(node)) { return false; @@ -146,6 +146,7 @@ module.exports = { return; } + const sourceCode = context.getSourceCode(); const option = getOption(type); if ((option === true || option === 'parens') && !isParenthesised(node) && isMultilines(node)) { @@ -173,7 +174,7 @@ module.exports = { const needsOpening = needsOpeningNewLine(node); const needsClosing = needsClosingNewLine(node); if (needsOpening || needsClosing) { - report(node, PARENS_NEW_LINES, fixer => { + report(node, PARENS_NEW_LINES, (fixer) => { const text = sourceCode.getText(node); let fixed = text; if (needsOpening) { @@ -195,7 +196,7 @@ module.exports = { return { - VariableDeclarator: function(node) { + VariableDeclarator(node) { const type = 'declaration'; if (!isEnabled(type)) { return; @@ -208,7 +209,7 @@ module.exports = { check(node.init, type); }, - AssignmentExpression: function(node) { + AssignmentExpression(node) { const type = 'assignment'; if (!isEnabled(type)) { return; @@ -221,14 +222,14 @@ module.exports = { check(node.right, type); }, - ReturnStatement: function(node) { + ReturnStatement(node) { const type = 'return'; if (isEnabled(type)) { check(node.argument, type); } }, - 'ArrowFunctionExpression:exit': function(node) { + 'ArrowFunctionExpression:exit': function (node) { const arrowBody = node.body; const type = 'arrow'; @@ -237,7 +238,7 @@ module.exports = { } }, - ConditionalExpression: function(node) { + ConditionalExpression(node) { const type = 'condition'; if (isEnabled(type)) { check(node.consequent, type); @@ -245,14 +246,14 @@ module.exports = { } }, - LogicalExpression: function(node) { + LogicalExpression(node) { const type = 'logical'; if (isEnabled(type)) { check(node.right, type); } }, - JSXAttribute: function(node) { + JSXAttribute(node) { const type = 'prop'; if (isEnabled(type) && node.value && node.value.type === 'JSXExpressionContainer') { check(node.value.expression, type); diff --git a/lib/rules/no-access-state-in-setstate.js b/lib/rules/no-access-state-in-setstate.js index e656d57f2b..068219c173 100644 --- a/lib/rules/no-access-state-in-setstate.js +++ b/lib/rules/no-access-state-in-setstate.js @@ -21,7 +21,7 @@ module.exports = { } }, - create: function(context) { + create(context) { function isSetStateCall(node) { return node.type === 'CallExpression' && node.callee.property && @@ -48,7 +48,7 @@ module.exports = { CallExpression(node) { // Appends all the methods that are calling another // method containing this.state to the methods array - methods.map(method => { + methods.forEach((method) => { if (node.callee.name === method.methodName) { let current = node.parent; while (current.type !== 'Program') { @@ -70,7 +70,7 @@ module.exports = { while (current.type !== 'Program') { if (isFirstArgumentInSetStateCall(current, node)) { const methodName = node.callee.name; - methods.map(method => { + methods.forEach((method) => { if (method.methodName === methodName) { context.report( method.node, @@ -105,13 +105,13 @@ module.exports = { if (current.type === 'MethodDefinition') { methods.push({ methodName: current.key.name, - node: node + node }); break; } else if (current.type === 'FunctionExpression' && current.parent.key) { methods.push({ methodName: current.parent.key.name, - node: node + node }); break; } @@ -119,7 +119,7 @@ module.exports = { // Storing all variables containg this.state if (current.type === 'VariableDeclarator') { vars.push({ - node: node, + node, scope: context.getScope(), variableName: current.id.name }); @@ -145,10 +145,12 @@ module.exports = { if (isFirstArgumentInSetStateCall(current, node)) { vars .filter(v => v.scope === context.getScope() && v.variableName === node.name) - .map(v => context.report( - v.node, - 'Use callback in setState when referencing the previous state.' - )); + .forEach((v) => { + context.report( + v.node, + 'Use callback in setState when referencing the previous state.' + ); + }); } current = current.parent; } @@ -157,7 +159,7 @@ module.exports = { ObjectPattern(node) { const isDerivedFromThis = node.parent.init && node.parent.init.type === 'ThisExpression'; - node.properties.forEach(property => { + node.properties.forEach((property) => { if (property && property.key && property.key.name === 'state' && isDerivedFromThis) { vars.push({ node: property.key, diff --git a/lib/rules/no-array-index-key.js b/lib/rules/no-array-index-key.js index 0d0a7f3fdc..d3cbddd2d9 100644 --- a/lib/rules/no-array-index-key.js +++ b/lib/rules/no-array-index-key.js @@ -2,6 +2,7 @@ * @fileoverview Prevent usage of Array index in keys * @author Joe Lencioni */ + 'use strict'; const has = require('has'); @@ -25,7 +26,7 @@ module.exports = { schema: [] }, - create: function(context) { + create(context) { // -------------------------------------------------------------------------- // Public // -------------------------------------------------------------------------- @@ -44,16 +45,16 @@ module.exports = { const ERROR_MESSAGE = 'Do not use Array index in keys'; function isArrayIndex(node) { - return node.type === 'Identifier' - && indexParamNames.indexOf(node.name) !== -1; + return node.type === 'Identifier' && + indexParamNames.indexOf(node.name) !== -1; } function isUsingReactChildren(node) { const callee = node.callee; if ( - !callee - || !callee.property - || !callee.object + !callee || + !callee.property || + !callee.object ) { return null; } @@ -86,9 +87,9 @@ module.exports = { return null; } - const callbackArg = isUsingReactChildren(node) - ? node.arguments[1] - : node.arguments[0]; + const callbackArg = isUsingReactChildren(node) ? + node.arguments[1] : + node.arguments[0]; if (!callbackArg) { return null; @@ -127,7 +128,7 @@ module.exports = { if (isArrayIndex(node)) { // key={bar} context.report({ - node: node, + node, message: ERROR_MESSAGE }); return; @@ -136,7 +137,7 @@ module.exports = { if (node.type === 'TemplateLiteral') { // key={`foo-${bar}`} node.expressions.filter(isArrayIndex).forEach(() => { - context.report({node: node, message: ERROR_MESSAGE}); + context.report({node, message: ERROR_MESSAGE}); }); return; @@ -147,20 +148,18 @@ module.exports = { const identifiers = getIdentifiersFromBinaryExpression(node); identifiers.filter(isArrayIndex).forEach(() => { - context.report({node: node, message: ERROR_MESSAGE}); + context.report({node, message: ERROR_MESSAGE}); }); - - return; } } return { - CallExpression: function(node) { + CallExpression(node) { if ( - node.callee - && node.callee.type === 'MemberExpression' - && ['createElement', 'cloneElement'].indexOf(node.callee.property.name) !== -1 - && node.arguments.length > 1 + node.callee && + node.callee.type === 'MemberExpression' && + ['createElement', 'cloneElement'].indexOf(node.callee.property.name) !== -1 && + node.arguments.length > 1 ) { // React.createElement if (!indexParamNames.length) { @@ -173,7 +172,7 @@ module.exports = { return; } - props.properties.forEach(prop => { + props.properties.forEach((prop) => { if (!prop.key || prop.key.name !== 'key') { // { ...foo } // { foo: bar } @@ -194,7 +193,7 @@ module.exports = { indexParamNames.push(mapIndexParamName); }, - JSXAttribute: function(node) { + JSXAttribute(node) { if (node.name.name !== 'key') { // foo={bar} return; @@ -214,7 +213,7 @@ module.exports = { checkPropValue(value.expression); }, - 'CallExpression:exit': function(node) { + 'CallExpression:exit': function (node) { const mapIndexParamName = getMapIndexParamName(node); if (!mapIndexParamName) { return; diff --git a/lib/rules/no-children-prop.js b/lib/rules/no-children-prop.js index f8e341dab7..0e9e011bf7 100644 --- a/lib/rules/no-children-prop.js +++ b/lib/rules/no-children-prop.js @@ -2,6 +2,7 @@ * @fileoverview Prevent passing of children as props * @author Benjamin Stepp */ + 'use strict'; const docsUrl = require('../util/docsUrl'); @@ -17,11 +18,11 @@ const docsUrl = require('../util/docsUrl'); * object literal, False if not. */ function isCreateElementWithProps(node) { - return node.callee - && node.callee.type === 'MemberExpression' - && node.callee.property.name === 'createElement' - && node.arguments.length > 1 - && node.arguments[1].type === 'ObjectExpression'; + return node.callee && + node.callee.type === 'MemberExpression' && + node.callee.property.name === 'createElement' && + node.arguments.length > 1 && + node.arguments[1].type === 'ObjectExpression'; } // ------------------------------------------------------------------------------ @@ -38,19 +39,19 @@ module.exports = { }, schema: [] }, - create: function(context) { + create(context) { return { - JSXAttribute: function(node) { + JSXAttribute(node) { if (node.name.name !== 'children') { return; } context.report({ - node: node, + node, message: 'Do not pass children as props. Instead, nest children between the opening and closing tags.' }); }, - CallExpression: function(node) { + CallExpression(node) { if (!isCreateElementWithProps(node)) { return; } @@ -60,7 +61,7 @@ module.exports = { if (childrenProp) { context.report({ - node: node, + node, message: 'Do not pass children as props. Instead, pass them as additional arguments to React.createElement.' }); } diff --git a/lib/rules/no-danger-with-children.js b/lib/rules/no-danger-with-children.js index af2af06672..46ac070b27 100644 --- a/lib/rules/no-danger-with-children.js +++ b/lib/rules/no-danger-with-children.js @@ -2,6 +2,7 @@ * @fileoverview Report when a DOM element is using both children and dangerouslySetInnerHTML * @author David Petersen */ + 'use strict'; const variableUtil = require('../util/variable'); @@ -20,7 +21,7 @@ module.exports = { }, schema: [] // no options }, - create: function(context) { + create(context) { function findSpreadVariable(name) { return variableUtil.variablesInScope(context).find(item => item.name === name); } @@ -34,10 +35,11 @@ module.exports = { if (!node.properties) { return false; } - return node.properties.find(prop => { + return node.properties.find((prop) => { if (prop.type === 'Property') { return prop.key.name === propName; - } else if (prop.type === 'ExperimentalSpreadProperty' || prop.type === 'SpreadElement') { + } + if (prop.type === 'ExperimentalSpreadProperty' || prop.type === 'SpreadElement') { const variable = findSpreadVariable(prop.argument.name); if (variable && variable.defs.length && variable.defs[0].node.init) { if (seenProps.indexOf(prop.argument.name) > -1) { @@ -58,7 +60,7 @@ module.exports = { */ function findJsxProp(node, propName) { const attributes = node.openingElement.attributes; - return attributes.find(attribute => { + return attributes.find((attribute) => { if (attribute.type === 'JSXSpreadAttribute') { const variable = findSpreadVariable(attribute.argument.name); if (variable && variable.defs.length && variable.defs[0].node.init) { @@ -83,7 +85,7 @@ module.exports = { } return { - JSXElement: function (node) { + JSXElement(node) { let hasChildren = false; if (node.children.length && !isLineBreak(node.children[0])) { @@ -93,19 +95,19 @@ module.exports = { } if ( - node.openingElement.attributes - && hasChildren - && findJsxProp(node, 'dangerouslySetInnerHTML') + node.openingElement.attributes && + hasChildren && + findJsxProp(node, 'dangerouslySetInnerHTML') ) { context.report(node, 'Only set one of `children` or `props.dangerouslySetInnerHTML`'); } }, - CallExpression: function (node) { + CallExpression(node) { if ( - node.callee - && node.callee.type === 'MemberExpression' - && node.callee.property.name === 'createElement' - && node.arguments.length > 1 + node.callee && + node.callee.type === 'MemberExpression' && + node.callee.property.name === 'createElement' && + node.arguments.length > 1 ) { let hasChildren = false; diff --git a/lib/rules/no-danger.js b/lib/rules/no-danger.js index 8fca290438..9322c090fe 100644 --- a/lib/rules/no-danger.js +++ b/lib/rules/no-danger.js @@ -2,6 +2,7 @@ * @fileoverview Prevent usage of dangerous JSX props * @author Scott Andrews */ + 'use strict'; const docsUrl = require('../util/docsUrl'); @@ -50,13 +51,13 @@ module.exports = { schema: [] }, - create: function(context) { + create(context) { return { - JSXAttribute: function(node) { + JSXAttribute(node) { if (jsxUtil.isDOMComponent(node.parent) && isDangerous(node.name.name)) { context.report({ - node: node, + node, message: DANGEROUS_MESSAGE, data: { name: node.name.name diff --git a/lib/rules/no-deprecated.js b/lib/rules/no-deprecated.js index 401c8df5aa..25dda5f780 100644 --- a/lib/rules/no-deprecated.js +++ b/lib/rules/no-deprecated.js @@ -4,9 +4,10 @@ * @author Scott Feeney * @author Sergei Startsev */ + 'use strict'; -const has = require('has'); +const values = require('object.values'); const Components = require('../util/Components'); const astUtil = require('../util/ast'); @@ -41,7 +42,6 @@ module.exports = { }, create: Components.detect((context, components, utils) => { - const sourceCode = context.getSourceCode(); const pragma = pragmaUtil.getFromContext(context); function getDeprecated() { @@ -137,15 +137,12 @@ module.exports = { if (!node.init) { return moduleName; } - for (const module in MODULES) { - if (!has(MODULES, module)) { - continue; - } - moduleName = MODULES[module].find(name => name === node.init.name); - if (moduleName) { - break; - } - } + + values(MODULES).some((moduleNames) => { + moduleName = moduleNames.find(name => name === node.init.name); + return moduleName; + }); + return moduleName; } @@ -178,17 +175,16 @@ module.exports = { // -------------------------------------------------------------------------- return { - - MemberExpression: function(node) { - checkDeprecation(node, sourceCode.getText(node)); + MemberExpression(node) { + checkDeprecation(node, context.getSourceCode().getText(node)); }, - ImportDeclaration: function(node) { + ImportDeclaration(node) { const isReactImport = typeof MODULES[node.source.value] !== 'undefined'; if (!isReactImport) { return; } - node.specifiers.forEach(specifier => { + node.specifiers.forEach((specifier) => { if (!specifier.imported) { return; } @@ -196,13 +192,13 @@ module.exports = { }); }, - VariableDeclarator: function(node) { + VariableDeclarator(node) { const reactModuleName = getReactModuleName(node); const isRequire = node.init && node.init.callee && node.init.callee.name === 'require'; - const isReactRequire = node.init - && node.init.arguments - && node.init.arguments.length - && typeof MODULES[node.init.arguments[0].value] !== 'undefined'; + const isReactRequire = node.init && + node.init.arguments && + node.init.arguments.length && + typeof MODULES[node.init.arguments[0].value] !== 'undefined'; const isDestructuring = node.id && node.id.type === 'ObjectPattern'; if ( @@ -211,7 +207,7 @@ module.exports = { ) { return; } - node.id.properties.forEach(property => { + node.id.properties.forEach((property) => { checkDeprecation(node, `${reactModuleName || pragma}.${property.key.name}`); }); }, diff --git a/lib/rules/no-did-mount-set-state.js b/lib/rules/no-did-mount-set-state.js index a3d378b2f3..a5705ef2df 100644 --- a/lib/rules/no-did-mount-set-state.js +++ b/lib/rules/no-did-mount-set-state.js @@ -2,6 +2,7 @@ * @fileoverview Prevent usage of setState in componentDidMount * @author Yannick Croissant */ + 'use strict'; const makeNoMethodSetStateRule = require('../util/makeNoMethodSetStateRule'); diff --git a/lib/rules/no-did-update-set-state.js b/lib/rules/no-did-update-set-state.js index eb14688187..5c76a1ff5b 100644 --- a/lib/rules/no-did-update-set-state.js +++ b/lib/rules/no-did-update-set-state.js @@ -2,6 +2,7 @@ * @fileoverview Prevent usage of setState in componentDidUpdate * @author Yannick Croissant */ + 'use strict'; const makeNoMethodSetStateRule = require('../util/makeNoMethodSetStateRule'); diff --git a/lib/rules/no-direct-mutation-state.js b/lib/rules/no-direct-mutation-state.js index bbb9c62a12..30a88cfbff 100644 --- a/lib/rules/no-direct-mutation-state.js +++ b/lib/rules/no-direct-mutation-state.js @@ -3,6 +3,7 @@ * @author David Petersen * @author Nicolas Fernandez <@burabure> */ + 'use strict'; const Components = require('../util/Components'); @@ -80,7 +81,7 @@ module.exports = { } }, - CallExpression: function(node) { + CallExpression(node) { components.set(node, { inCallExpression: true }); @@ -118,7 +119,7 @@ module.exports = { } }, - 'CallExpression:exit': function(node) { + 'CallExpression:exit': function (node) { components.set(node, { inCallExpression: false }); @@ -135,7 +136,7 @@ module.exports = { 'Program:exit': function () { const list = components.list(); - Object.keys(list).forEach(key => { + Object.keys(list).forEach((key) => { if (!isValid(list[key])) { reportMutations(list[key]); } diff --git a/lib/rules/no-find-dom-node.js b/lib/rules/no-find-dom-node.js index 1beb30c84e..f62e821136 100644 --- a/lib/rules/no-find-dom-node.js +++ b/lib/rules/no-find-dom-node.js @@ -2,6 +2,7 @@ * @fileoverview Prevent usage of findDOMNode * @author Yannick Croissant */ + 'use strict'; const docsUrl = require('../util/docsUrl'); @@ -21,21 +22,18 @@ module.exports = { schema: [] }, - create: function(context) { + create(context) { // -------------------------------------------------------------------------- // Public // -------------------------------------------------------------------------- return { - CallExpression: function(node) { + CallExpression(node) { const callee = node.callee; - const isfindDOMNode = - (callee.name === 'findDOMNode') || - (callee.property && callee.property.name === 'findDOMNode') - ; - + const isfindDOMNode = (callee.name === 'findDOMNode') || + (callee.property && callee.property.name === 'findDOMNode'); if (!isfindDOMNode) { return; } diff --git a/lib/rules/no-is-mounted.js b/lib/rules/no-is-mounted.js index 23a9084d23..3132079831 100644 --- a/lib/rules/no-is-mounted.js +++ b/lib/rules/no-is-mounted.js @@ -2,6 +2,7 @@ * @fileoverview Prevent usage of isMounted * @author Joe Lencioni */ + 'use strict'; const docsUrl = require('../util/docsUrl'); @@ -21,14 +22,14 @@ module.exports = { schema: [] }, - create: function(context) { + create(context) { // -------------------------------------------------------------------------- // Public // -------------------------------------------------------------------------- return { - CallExpression: function(node) { + CallExpression(node) { const callee = node.callee; if (callee.type !== 'MemberExpression') { return; diff --git a/lib/rules/no-multi-comp.js b/lib/rules/no-multi-comp.js index 9675bfec55..a86662be58 100644 --- a/lib/rules/no-multi-comp.js +++ b/lib/rules/no-multi-comp.js @@ -2,6 +2,7 @@ * @fileoverview Prevent multiple component definition per file * @author Yannick Croissant */ + 'use strict'; const Components = require('../util/Components'); @@ -57,7 +58,7 @@ module.exports = { // -------------------------------------------------------------------------- return { - 'Program:exit': function() { + 'Program:exit': function () { if (components.length() <= 1) { return; } diff --git a/lib/rules/no-redundant-should-component-update.js b/lib/rules/no-redundant-should-component-update.js index de0b6e7fad..3321dffe6f 100644 --- a/lib/rules/no-redundant-should-component-update.js +++ b/lib/rules/no-redundant-should-component-update.js @@ -1,6 +1,7 @@ /** * @fileoverview Flag shouldComponentUpdate when extending PureComponent */ + 'use strict'; const Components = require('../util/Components'); @@ -34,7 +35,7 @@ module.exports = { */ function hasShouldComponentUpdate(node) { const properties = astUtil.getComponentProperties(node); - return properties.some(property => { + return properties.some((property) => { const name = astUtil.getPropertyName(property); return name === 'shouldComponentUpdate'; }); @@ -48,7 +49,8 @@ module.exports = { function getNodeName(node) { if (node.id) { return node.id.name; - } else if (node.parent && node.parent.id) { + } + if (node.parent && node.parent.id) { return node.parent.id.name; } return ''; @@ -64,7 +66,7 @@ module.exports = { if (hasScu) { const className = getNodeName(node); context.report({ - node: node, + node, message: errorMessage(className) }); } diff --git a/lib/rules/no-render-return-value.js b/lib/rules/no-render-return-value.js index 6303c35224..d0349f3e01 100644 --- a/lib/rules/no-render-return-value.js +++ b/lib/rules/no-render-return-value.js @@ -2,6 +2,7 @@ * @fileoverview Prevent usage of the return value of React.render * @author Dustan Kasten */ + 'use strict'; const versionUtil = require('../util/version'); @@ -22,7 +23,7 @@ module.exports = { schema: [] }, - create: function(context) { + create(context) { // -------------------------------------------------------------------------- // Public // -------------------------------------------------------------------------- @@ -38,7 +39,7 @@ module.exports = { return { - CallExpression: function(node) { + CallExpression(node) { const callee = node.callee; const parent = node.parent; if (callee.type !== 'MemberExpression') { diff --git a/lib/rules/no-set-state.js b/lib/rules/no-set-state.js index f5dc97f699..7af70116f8 100644 --- a/lib/rules/no-set-state.js +++ b/lib/rules/no-set-state.js @@ -2,6 +2,7 @@ * @fileoverview Prevent usage of setState * @author Mark Dalgleish */ + 'use strict'; const Components = require('../util/Components'); @@ -53,7 +54,7 @@ module.exports = { return { - CallExpression: function(node) { + CallExpression(node) { const callee = node.callee; if ( callee.type !== 'MemberExpression' || @@ -67,13 +68,13 @@ module.exports = { setStateUsages.push(callee); components.set(node, { useSetState: true, - setStateUsages: setStateUsages + setStateUsages }); }, - 'Program:exit': function() { + 'Program:exit': function () { const list = components.list(); - Object.keys(list).filter(component => !isValid(list[component])).forEach(component => { + Object.keys(list).filter(component => !isValid(list[component])).forEach((component) => { reportSetStateUsages(list[component]); }); } diff --git a/lib/rules/no-string-refs.js b/lib/rules/no-string-refs.js index 7af866f6b4..96ebc2e48c 100644 --- a/lib/rules/no-string-refs.js +++ b/lib/rules/no-string-refs.js @@ -2,6 +2,7 @@ * @fileoverview Prevent string definitions for references and prevent referencing this.refs * @author Tom Hastjarjanto */ + 'use strict'; const Components = require('../util/Components'); @@ -84,27 +85,27 @@ module.exports = { node.value && node.value.type === 'JSXExpressionContainer' && node.value.expression && - ((node.value.expression.type === 'Literal' && typeof node.value.expression.value === 'string') || - (node.value.expression.type === 'TemplateLiteral' && detectTemplateLiterals)) + ((node.value.expression.type === 'Literal' && typeof node.value.expression.value === 'string') || + (node.value.expression.type === 'TemplateLiteral' && detectTemplateLiterals)) ); } return { - MemberExpression: function(node) { + MemberExpression(node) { if (isRefsUsage(node)) { context.report({ - node: node, + node, message: 'Using this.refs is deprecated.' }); } }, - JSXAttribute: function(node) { + JSXAttribute(node) { if ( isRefAttribute(node) && (containsStringLiteral(node) || containsStringExpressionContainer(node)) ) { context.report({ - node: node, + node, message: 'Using string literals in ref attributes is deprecated.' }); } diff --git a/lib/rules/no-this-in-sfc.js b/lib/rules/no-this-in-sfc.js index 5d3749d71f..df86ba55ca 100644 --- a/lib/rules/no-this-in-sfc.js +++ b/lib/rules/no-this-in-sfc.js @@ -1,6 +1,7 @@ /** * @fileoverview Report "this" being used in stateless functional components. */ + 'use strict'; const Components = require('../util/Components'); @@ -35,7 +36,7 @@ module.exports = { return; } context.report({ - node: node, + node, message: ERROR_MESSAGE }); } diff --git a/lib/rules/no-typos.js b/lib/rules/no-typos.js index 47e2b7fdf1..96be82b943 100644 --- a/lib/rules/no-typos.js +++ b/lib/rules/no-typos.js @@ -1,8 +1,10 @@ /** * @fileoverview Prevent common casing typos */ + 'use strict'; +const PROP_TYPES = Object.keys(require('prop-types')); const Components = require('../util/Components'); const docsUrl = require('../util/docsUrl'); @@ -28,8 +30,6 @@ const LIFECYCLE_METHODS = [ 'render' ]; -const PROP_TYPES = Object.keys(require('prop-types')); - module.exports = { meta: { docs: { @@ -48,7 +48,7 @@ module.exports = { function checkValidPropTypeQualifier(node) { if (node.name !== 'isRequired') { context.report({ - node: node, + node, message: `Typo in prop type chain qualifier: ${node.name}` }); } @@ -57,7 +57,7 @@ module.exports = { function checkValidPropType(node) { if (node.name && !PROP_TYPES.some(propTypeName => propTypeName === node.name)) { context.report({ - node: node, + node, message: `Typo in declared prop type: ${node.name}` }); } @@ -83,7 +83,7 @@ module.exports = { } else if (callee.type === 'MemberExpression' && callee.property.name === 'oneOfType') { const args = node.arguments[0]; if (args && args.type === 'ArrayExpression') { - args.elements.forEach(el => { + args.elements.forEach((el) => { checkValidProp(el); }); } @@ -118,7 +118,7 @@ module.exports = { /* eslint-enable no-use-before-define */ - function checkValidPropObject (node) { + function checkValidPropObject(node) { if (node && node.type === 'ObjectExpression') { node.properties.forEach(prop => checkValidProp(prop.value)); } @@ -128,24 +128,24 @@ module.exports = { if (propertyName === 'propTypes' || propertyName === 'contextTypes' || propertyName === 'childContextTypes') { checkValidPropObject(node); } - STATIC_CLASS_PROPERTIES.forEach(CLASS_PROP => { + STATIC_CLASS_PROPERTIES.forEach((CLASS_PROP) => { if (propertyName && CLASS_PROP.toLowerCase() === propertyName.toLowerCase() && CLASS_PROP !== propertyName) { - const message = isClassProperty - ? 'Typo in static class property declaration' - : 'Typo in property declaration'; + const message = isClassProperty ? + 'Typo in static class property declaration' : + 'Typo in property declaration'; context.report({ - node: node, - message: message + node, + message }); } }); } function reportErrorIfLifecycleMethodCasingTypo(node) { - LIFECYCLE_METHODS.forEach(method => { + LIFECYCLE_METHODS.forEach((method) => { if (method.toLowerCase() === node.key.name.toLowerCase() && method !== node.key.name) { context.report({ - node: node, + node, message: 'Typo in component lifecycle method declaration' }); } @@ -153,7 +153,7 @@ module.exports = { } return { - ImportDeclaration: function(node) { + ImportDeclaration(node) { if (node.source && node.source.value === 'prop-types') { // import PropType from "prop-types" propTypesPackageName = node.specifiers[0].local.name; } else if (node.source && node.source.value === 'react') { // import { PropTypes } from "react" @@ -171,7 +171,7 @@ module.exports = { } }, - ClassProperty: function(node) { + ClassProperty(node) { if (!node.static || !utils.isES6Component(node.parent.parent)) { return; } @@ -181,7 +181,7 @@ module.exports = { reportErrorIfPropertyCasingTypo(node.value, propertyName, true); }, - MemberExpression: function(node) { + MemberExpression(node) { const propertyName = node.property.name; if ( @@ -202,7 +202,7 @@ module.exports = { } }, - MethodDefinition: function(node) { + MethodDefinition(node) { if (!utils.isES6Component(node.parent.parent)) { return; } @@ -210,14 +210,14 @@ module.exports = { reportErrorIfLifecycleMethodCasingTypo(node); }, - ObjectExpression: function(node) { + ObjectExpression(node) { const component = utils.isES5Component(node) && components.get(node); if (!component) { return; } - node.properties.forEach(property => { + node.properties.forEach((property) => { reportErrorIfPropertyCasingTypo(property.value, property.key.name, false); reportErrorIfLifecycleMethodCasingTypo(property); }); diff --git a/lib/rules/no-unescaped-entities.js b/lib/rules/no-unescaped-entities.js index 3a2f263c2d..b8f5e76e38 100644 --- a/lib/rules/no-unescaped-entities.js +++ b/lib/rules/no-unescaped-entities.js @@ -2,6 +2,7 @@ * @fileoverview HTML special characters should be escaped. * @author Patrick Hayes */ + 'use strict'; const docsUrl = require('../util/docsUrl'); @@ -66,7 +67,7 @@ module.exports = { }] }, - create: function(context) { + create(context) { function reportInvalidEntity(node) { const configuration = context.options[0] || {}; const entities = configuration.forbid || DEFAULTS; @@ -92,14 +93,14 @@ module.exports = { context.report({ loc: {line: i, column: start + index}, message: `HTML entity, \`${entities[j]}\` , must be escaped.`, - node: node + node }); } } else if (c === entities[j].char) { context.report({ loc: {line: i, column: start + index}, message: `\`${entities[j].char}\` can be escaped with ${entities[j].alternatives.map(alt => `\`${alt}\``).join(', ')}.`, - node: node + node }); } } @@ -108,7 +109,7 @@ module.exports = { } return { - 'Literal, JSXText': function(node) { + 'Literal, JSXText': function (node) { if (jsxUtil.isJSX(node.parent)) { reportInvalidEntity(node); } diff --git a/lib/rules/no-unknown-property.js b/lib/rules/no-unknown-property.js index 25508df044..b3c643514d 100644 --- a/lib/rules/no-unknown-property.js +++ b/lib/rules/no-unknown-property.js @@ -2,6 +2,7 @@ * @fileoverview Prevent usage of unknown DOM property * @author Yannick Croissant */ + 'use strict'; const docsUrl = require('../util/docsUrl'); @@ -229,18 +230,15 @@ module.exports = { }] }, - create: function(context) { + create(context) { function getIgnoreConfig() { return context.options[0] && context.options[0].ignore || DEFAULTS.ignore; } - const sourceCode = context.getSourceCode(); - return { - - JSXAttribute: function(node) { + JSXAttribute(node) { const ignoreNames = getIgnoreConfig(); - const name = sourceCode.getText(node.name); + const name = context.getSourceCode().getText(node.name); if (ignoreNames.indexOf(name) >= 0) { return; } @@ -254,11 +252,11 @@ module.exports = { const allowedTags = ATTRIBUTE_TAGS_MAP[name]; if (tagName && allowedTags && /[^A-Z]/.test(tagName.charAt(0)) && allowedTags.indexOf(tagName) === -1) { context.report({ - node: node, + node, message: WRONG_TAG_MESSAGE, data: { - name: name, - tagName: tagName, + name, + tagName, allowedTags: allowedTags.join(', ') } }); @@ -269,13 +267,13 @@ module.exports = { return; } context.report({ - node: node, + node, message: UNKNOWN_MESSAGE, data: { - name: name, - standardName: standardName + name, + standardName }, - fix: function(fixer) { + fix(fixer) { return fixer.replaceText(node.name, standardName); } }); diff --git a/lib/rules/no-unsafe.js b/lib/rules/no-unsafe.js index 704e004a65..59b691b461 100644 --- a/lib/rules/no-unsafe.js +++ b/lib/rules/no-unsafe.js @@ -101,7 +101,7 @@ module.exports = { const details = meta.details; context.report({ - node: node, + node, message: `${method} is unsafe for use in async rendering. Update the component to use ${newMethod} instead. ${details}` }); } diff --git a/lib/rules/no-unused-prop-types.js b/lib/rules/no-unused-prop-types.js index 79488f16c0..1f095a1cc7 100644 --- a/lib/rules/no-unused-prop-types.js +++ b/lib/rules/no-unused-prop-types.js @@ -2,6 +2,7 @@ * @fileoverview Prevent definitions of unused prop types * @author Evgueni Naverniouk */ + 'use strict'; // As for exceptions for props.children or props.className (and alike) look at @@ -84,13 +85,13 @@ module.exports = { * @param {Object} component The component to process * @param {ASTNode[]|true} props List of props to validate */ - function reportUnusedPropType (component, props) { + function reportUnusedPropType(component, props) { // Skip props that check instances if (props === true) { return; } - Object.keys(props || {}).forEach(key => { + Object.keys(props || {}).forEach((key) => { const prop = props[key]; // Skip props that check instances if (prop === true) { @@ -129,10 +130,10 @@ module.exports = { // -------------------------------------------------------------------------- return { - 'Program:exit': function() { + 'Program:exit': function () { const list = components.list(); // Report undeclared proptypes for all classes - Object.keys(list).filter(component => mustBeValidated(list[component])).forEach(component => { + Object.keys(list).filter(component => mustBeValidated(list[component])).forEach((component) => { if (!mustBeValidated(list[component])) { return; } diff --git a/lib/rules/no-unused-state.js b/lib/rules/no-unused-state.js index 0e5e392a18..be0796e37f 100644 --- a/lib/rules/no-unused-state.js +++ b/lib/rules/no-unused-state.js @@ -30,9 +30,11 @@ function getName(node) { if (type === 'Identifier') { return node.name; - } else if (type === 'Literal') { + } + if (type === 'Literal') { return String(node.value); - } else if (type === 'TemplateLiteral' && node.expressions.length === 0) { + } + if (type === 'TemplateLiteral' && node.expressions.length === 0) { return node.quasis[0].value.raw; } return null; @@ -120,13 +122,11 @@ module.exports = { function isStateReference(node) { node = uncast(node); - const isDirectStateReference = - node.type === 'MemberExpression' && + const isDirectStateReference = node.type === 'MemberExpression' && isThisExpression(node.object) && node.property.name === 'state'; - const isAliasedStateReference = - node.type === 'Identifier' && + const isAliasedStateReference = node.type === 'Identifier' && classInfo.aliases && classInfo.aliases.has(node.name); @@ -228,7 +228,7 @@ module.exports = { } }, - 'ObjectExpression:exit'(node) { + 'ObjectExpression:exit': function (node) { if (!classInfo) { return; } @@ -239,7 +239,7 @@ module.exports = { } }, - 'ClassDeclaration:exit'() { + 'ClassDeclaration:exit': function () { if (!classInfo) { return; } @@ -303,7 +303,7 @@ module.exports = { } }, - 'ClassProperty:exit'(node) { + 'ClassProperty:exit': function (node) { if ( classInfo && !node.static && @@ -323,7 +323,7 @@ module.exports = { classInfo.aliases = new Set(); }, - 'MethodDefinition:exit'() { + 'MethodDefinition:exit': function () { if (!classInfo) { return; } @@ -419,7 +419,7 @@ module.exports = { } }, - 'ExperimentalSpreadProperty, SpreadElement'(node) { + 'ExperimentalSpreadProperty, SpreadElement': function (node) { if (classInfo && isStateReference(node.argument)) { classInfo = null; } diff --git a/lib/rules/no-will-update-set-state.js b/lib/rules/no-will-update-set-state.js index 22bb633114..0267227a73 100644 --- a/lib/rules/no-will-update-set-state.js +++ b/lib/rules/no-will-update-set-state.js @@ -2,6 +2,7 @@ * @fileoverview Prevent usage of setState in componentWillUpdate * @author Yannick Croissant */ + 'use strict'; const makeNoMethodSetStateRule = require('../util/makeNoMethodSetStateRule'); diff --git a/lib/rules/prefer-es6-class.js b/lib/rules/prefer-es6-class.js index c5e510a27e..b35a85b969 100644 --- a/lib/rules/prefer-es6-class.js +++ b/lib/rules/prefer-es6-class.js @@ -2,6 +2,7 @@ * @fileoverview Enforce ES5 or ES6 class for React Components * @author Dan Hamilton */ + 'use strict'; const Components = require('../util/Components'); @@ -29,18 +30,18 @@ module.exports = { const configuration = context.options[0] || 'always'; return { - ObjectExpression: function(node) { + ObjectExpression(node) { if (utils.isES5Component(node) && configuration === 'always') { context.report({ - node: node, + node, message: 'Component should use es6 class instead of createClass' }); } }, - ClassDeclaration: function(node) { + ClassDeclaration(node) { if (utils.isES6Component(node) && configuration === 'never') { context.report({ - node: node, + node, message: 'Component should use createClass instead of es6 class' }); } diff --git a/lib/rules/prefer-read-only-props.js b/lib/rules/prefer-read-only-props.js index 31d9993b78..225dac0b27 100644 --- a/lib/rules/prefer-read-only-props.js +++ b/lib/rules/prefer-read-only-props.js @@ -2,6 +2,7 @@ * @fileoverview Require component props to be typed as read-only. * @author Luke Zapart */ + 'use strict'; const Components = require('../util/Components'); @@ -35,14 +36,14 @@ module.exports = { 'Program:exit': function () { const list = components.list(); - Object.keys(list).forEach(key => { + Object.keys(list).forEach((key) => { const component = list[key]; if (!component.declaredPropTypes) { return; } - Object.keys(component.declaredPropTypes).forEach(propName => { + Object.keys(component.declaredPropTypes).forEach((propName) => { const prop = component.declaredPropTypes[propName]; if (!isFlowPropertyType(prop.node)) { @@ -56,7 +57,7 @@ module.exports = { data: { propName }, - fix: fixer => { + fix: (fixer) => { if (!prop.node.variance) { // Insert covariance return fixer.insertTextBefore(prop.node, '+'); diff --git a/lib/rules/prefer-stateless-function.js b/lib/rules/prefer-stateless-function.js index 0b848db87a..ce5637399e 100644 --- a/lib/rules/prefer-stateless-function.js +++ b/lib/rules/prefer-stateless-function.js @@ -4,6 +4,7 @@ * @author Alberto Rodríguez * @copyright 2015 Alberto Rodríguez. All rights reserved. */ + 'use strict'; const Components = require('../util/Components'); @@ -39,8 +40,6 @@ module.exports = { const configuration = context.options[0] || {}; const ignorePureComponents = configuration.ignorePureComponents || false; - const sourceCode = context.getSourceCode(); - // -------------------------------------------------------------------------- // Public // -------------------------------------------------------------------------- @@ -180,16 +179,14 @@ module.exports = { */ function hasOtherProperties(node) { const properties = astUtil.getComponentProperties(node); - return properties.some(property => { + return properties.some((property) => { const name = astUtil.getPropertyName(property); const isDisplayName = name === 'displayName'; const isPropTypes = name === 'propTypes' || name === 'props' && property.typeAnnotation; const contextTypes = name === 'contextTypes'; const defaultProps = name === 'defaultProps'; - const isUselessConstructor = - property.kind === 'constructor' && - isRedundantSuperCall(property.value.body.body, property.value.params) - ; + const isUselessConstructor = property.kind === 'constructor' && + isRedundantSuperCall(property.value.body.body, property.value.params); const isRender = name === 'render'; return !isDisplayName && !isPropTypes && !contextTypes && !defaultProps && !isUselessConstructor && !isRender; }); @@ -280,13 +277,13 @@ module.exports = { ClassExpression: visitClass, // Mark `this` destructuring as a usage of `this` - VariableDeclarator: function(node) { + VariableDeclarator(node) { // Ignore destructuring on other than `this` if (!node.id || node.id.type !== 'ObjectPattern' || !node.init || node.init.type !== 'ThisExpression') { return; } // Ignore `props` and `context` - const useThis = node.id.properties.some(property => { + const useThis = node.id.properties.some((property) => { const name = astUtil.getPropertyName(property); return name !== 'props' && name !== 'context'; }); @@ -298,7 +295,7 @@ module.exports = { }, // Mark `this` usage - MemberExpression: function(node) { + MemberExpression(node) { if (node.object.type !== 'ThisExpression') { if (node.property && node.property.name === 'childContextTypes') { const component = utils.getRelatedComponent(node); @@ -306,11 +303,11 @@ module.exports = { return; } markChildContextTypesAsDeclared(component.node); - return; } return; // Ignore calls to `this.props` and `this.context` - } else if ( + } + if ( (node.property.name || node.property.value) === 'props' || (node.property.name || node.property.value) === 'context' ) { @@ -321,8 +318,8 @@ module.exports = { }, // Mark `ref` usage - JSXAttribute: function(node) { - const name = sourceCode.getText(node.name); + JSXAttribute(node) { + const name = context.getSourceCode().getText(node.name); if (name !== 'ref') { return; } @@ -330,7 +327,7 @@ module.exports = { }, // Mark `render` that do not return some JSX - ReturnStatement: function(node) { + ReturnStatement(node) { let blockNode; let scope = context.getScope(); while (scope) { @@ -354,9 +351,9 @@ module.exports = { markReturnAsInvalid(node); }, - 'Program:exit': function() { + 'Program:exit': function () { const list = components.list(); - Object.keys(list).forEach(component => { + Object.keys(list).forEach((component) => { if ( hasOtherProperties(list[component].node) || list[component].useThis || diff --git a/lib/rules/prop-types.js b/lib/rules/prop-types.js index 089536d12b..9131e0311c 100644 --- a/lib/rules/prop-types.js +++ b/lib/rules/prop-types.js @@ -2,6 +2,7 @@ * @fileoverview Prevent missing props validation in a React component definition * @author Yannick Croissant */ + 'use strict'; // As for exceptions for props.children or props.className (and alike) look at @@ -83,14 +84,14 @@ module.exports = { * @param {String[]} keyList Dot separated name of the prop to check. * @returns {Boolean} True if the prop is declared, false if not. */ - function _isDeclaredInComponent(declaredPropTypes, keyList) { + function internalIsDeclaredInComponent(declaredPropTypes, keyList) { for (let i = 0, j = keyList.length; i < j; i++) { const key = keyList[i]; const propType = ( declaredPropTypes && ( // Check if this key is declared (declaredPropTypes[key] || // If not, check if this type accepts any key - declaredPropTypes.__ANY_KEY__) + declaredPropTypes.__ANY_KEY__) // eslint-disable-line no-underscore-dangle ) ); @@ -119,7 +120,7 @@ module.exports = { const unionPropType = {}; for (let k = 0, z = unionTypes.length; k < z; k++) { unionPropType[key] = unionTypes[k]; - const isValid = _isDeclaredInComponent( + const isValid = internalIsDeclaredInComponent( unionPropType, keyList.slice(i) ); @@ -146,10 +147,8 @@ module.exports = { while (node) { const component = components.get(node); - const isDeclared = - component && component.confidence === 2 && - _isDeclaredInComponent(component.declaredPropTypes || {}, names) - ; + const isDeclared = component && component.confidence === 2 && + internalIsDeclaredInComponent(component.declaredPropTypes || {}, names); if (isDeclared) { return true; } @@ -163,23 +162,19 @@ module.exports = { * @param {Object} component The component to process */ function reportUndeclaredPropTypes(component) { - for (let i = 0, j = component.usedPropTypes.length; i < j; i++) { - const allNames = component.usedPropTypes[i].allNames; - const node = component.usedPropTypes[i].node; - if ( - isIgnored(allNames[0]) || - isDeclaredInComponent(component.node, allNames) || - !node - ) { - continue; - } + const undeclareds = component.usedPropTypes.filter(propType => ( + propType.node && + !isIgnored(propType.allNames[0]) && + !isDeclaredInComponent(component.node, propType.allNames) + )); + undeclareds.forEach((propType) => { context.report( - node, + propType.node, MISSING_MESSAGE, { - name: allNames.join('.').replace(/\.__COMPUTED_PROP__/g, '[]') + name: propType.allNames.join('.').replace(/\.__COMPUTED_PROP__/g, '[]') } ); - } + }); } // -------------------------------------------------------------------------- @@ -187,10 +182,10 @@ module.exports = { // -------------------------------------------------------------------------- return { - 'Program:exit': function() { + 'Program:exit': function () { const list = components.list(); // Report undeclared proptypes for all classes - Object.keys(list).filter(component => mustBeValidated(list[component])).forEach(component => { + Object.keys(list).filter(component => mustBeValidated(list[component])).forEach((component) => { reportUndeclaredPropTypes(list[component]); }); } diff --git a/lib/rules/react-in-jsx-scope.js b/lib/rules/react-in-jsx-scope.js index c9a39f92a1..9df35dab76 100644 --- a/lib/rules/react-in-jsx-scope.js +++ b/lib/rules/react-in-jsx-scope.js @@ -2,6 +2,7 @@ * @fileoverview Prevent missing React when using JSX * @author Glen Mailer */ + 'use strict'; const variableUtil = require('../util/variable'); @@ -23,7 +24,7 @@ module.exports = { schema: [] }, - create: function(context) { + create(context) { const pragma = pragmaUtil.getFromContext(context); const NOT_DEFINED_MESSAGE = '\'{{name}}\' must be in scope when using JSX'; @@ -33,7 +34,7 @@ module.exports = { return; } context.report({ - node: node, + node, message: NOT_DEFINED_MESSAGE, data: { name: pragma diff --git a/lib/rules/require-default-props.js b/lib/rules/require-default-props.js index 72b67272ba..f128090822 100644 --- a/lib/rules/require-default-props.js +++ b/lib/rules/require-default-props.js @@ -2,6 +2,7 @@ * @fileOverview Enforce a defaultProps definition for every prop that is not a required prop. * @author Vitor Balocco */ + 'use strict'; const Components = require('../util/Components'); @@ -49,7 +50,7 @@ module.exports = { return; } - Object.keys(propTypes).forEach(propName => { + Object.keys(propTypes).forEach((propName) => { const prop = propTypes[propName]; if (prop.isRequired) { if (forbidDefaultForRequired && defaultProps[propName]) { @@ -79,10 +80,10 @@ module.exports = { // -------------------------------------------------------------------------- return { - 'Program:exit': function() { + 'Program:exit': function () { const list = components.list(); - Object.keys(list).filter(component => list[component].declaredPropTypes).forEach(component => { + Object.keys(list).filter(component => list[component].declaredPropTypes).forEach((component) => { reportPropTypesWithoutDefault( list[component].declaredPropTypes, list[component].defaultProps || {} diff --git a/lib/rules/require-optimization.js b/lib/rules/require-optimization.js index e447bdcc5a..329354581d 100644 --- a/lib/rules/require-optimization.js +++ b/lib/rules/require-optimization.js @@ -2,6 +2,7 @@ * @fileoverview Enforce React components to have a shouldComponentUpdate method * @author Evgueni Naverniouk */ + 'use strict'; const Components = require('../util/Components'); @@ -91,7 +92,7 @@ module.exports = { * @param {ASTNode} node The AST node being checked. * @returns {Boolean} True if we are declaring a shouldComponentUpdate method, false if not. */ - const isSCUDeclarеd = function (node) { + const isSCUDeclared = function (node) { return Boolean( node && node.name === 'shouldComponentUpdate' @@ -164,19 +165,19 @@ module.exports = { }; return { - ArrowFunctionExpression: function (node) { + ArrowFunctionExpression(node) { // Stateless Functional Components cannot be optimized (yet) markSCUAsDeclared(node); }, - ClassDeclaration: function (node) { + ClassDeclaration(node) { if (!(hasPureRenderDecorator(node) || hasCustomDecorator(node) || utils.isPureComponent(node))) { return; } markSCUAsDeclared(node); }, - FunctionDeclaration: function (node) { + FunctionDeclaration(node) { // Skip if the function is declared in the class if (isFunctionInClass()) { return; @@ -185,7 +186,7 @@ module.exports = { markSCUAsDeclared(node); }, - FunctionExpression: function (node) { + FunctionExpression(node) { // Skip if the function is declared in the class if (isFunctionInClass()) { return; @@ -194,24 +195,20 @@ module.exports = { markSCUAsDeclared(node); }, - MethodDefinition: function (node) { - if (!isSCUDeclarеd(node.key)) { + MethodDefinition(node) { + if (!isSCUDeclared(node.key)) { return; } markSCUAsDeclared(node); }, - ObjectExpression: function (node) { + ObjectExpression(node) { // Search for the shouldComponentUpdate declaration - for (let i = 0, l = node.properties.length; i < l; i++) { - if ( - !node.properties[i].key || ( - !isSCUDeclarеd(node.properties[i].key) && - !isPureRenderDeclared(node.properties[i]) - ) - ) { - continue; - } + const found = node.properties.some(property => ( + property.key && + (isSCUDeclared(property.key) || isPureRenderDeclared(property)) + )); + if (found) { markSCUAsDeclared(node); } }, @@ -220,7 +217,7 @@ module.exports = { const list = components.list(); // Report missing shouldComponentUpdate for all components - Object.keys(list).filter(component => !list[component].hasSCU).forEach(component => { + Object.keys(list).filter(component => !list[component].hasSCU).forEach((component) => { reportMissingOptimization(list[component]); }); } diff --git a/lib/rules/require-render-return.js b/lib/rules/require-render-return.js index 6e56ec9848..c659e1f50d 100644 --- a/lib/rules/require-render-return.js +++ b/lib/rules/require-render-return.js @@ -2,6 +2,7 @@ * @fileoverview Enforce ES5 or ES6 class for returning value in render function. * @author Mark Orel */ + 'use strict'; const Components = require('../util/Components'); @@ -47,34 +48,33 @@ module.exports = { } return { - ReturnStatement: function(node) { + ReturnStatement(node) { const ancestors = context.getAncestors(node).reverse(); let depth = 0; - for (let i = 0, j = ancestors.length; i < j; i++) { - if (/Function(Expression|Declaration)$/.test(ancestors[i].type)) { + ancestors.forEach((ancestor) => { + if (/Function(Expression|Declaration)$/.test(ancestor.type)) { depth++; } if ( - !/(MethodDefinition|(Class)?Property)$/.test(ancestors[i].type) || - astUtil.getPropertyName(ancestors[i]) !== 'render' || - depth > 1 + /(MethodDefinition|(Class)?Property)$/.test(ancestor.type) && + astUtil.getPropertyName(ancestor) === 'render' && + depth <= 1 ) { - continue; + markReturnStatementPresent(node); } - markReturnStatementPresent(node); - } + }); }, - ArrowFunctionExpression: function(node) { + ArrowFunctionExpression(node) { if (node.expression === false || astUtil.getPropertyName(node.parent) !== 'render') { return; } markReturnStatementPresent(node); }, - 'Program:exit': function() { + 'Program:exit': function () { const list = components.list(); - Object.keys(list).forEach(component => { + Object.keys(list).forEach((component) => { if ( !findRenderMethod(list[component].node) || list[component].hasReturnStatement || diff --git a/lib/rules/self-closing-comp.js b/lib/rules/self-closing-comp.js index 5a46c5bd0b..94ca78a374 100644 --- a/lib/rules/self-closing-comp.js +++ b/lib/rules/self-closing-comp.js @@ -2,6 +2,7 @@ * @fileoverview Prevent extra closing tags for components without children * @author Yannick Croissant */ + 'use strict'; const docsUrl = require('../util/docsUrl'); @@ -39,7 +40,7 @@ module.exports = { }] }, - create: function(context) { + create(context) { function isComponent(node) { return node.name && node.name.type === 'JSXIdentifier' && !jsxUtil.isDOMComponent(node); } @@ -73,14 +74,14 @@ module.exports = { return { - JSXOpeningElement: function(node) { + JSXOpeningElement(node) { if (!isShouldBeSelfClosed(node)) { return; } context.report({ - node: node, + node, message: 'Empty components are self-closing', - fix: function(fixer) { + fix(fixer) { // Represents the last character of the JSXOpeningElement, the '>' character const openingElementEnding = node.range[1] - 1; // Represents the last character of the JSXClosingElement, the '>' character diff --git a/lib/rules/sort-comp.js b/lib/rules/sort-comp.js index cdb8704156..293b830f8f 100644 --- a/lib/rules/sort-comp.js +++ b/lib/rules/sort-comp.js @@ -2,12 +2,14 @@ * @fileoverview Enforce component methods order * @author Yannick Croissant */ + 'use strict'; const has = require('has'); +const entries = require('object.entries'); +const arrayIncludes = require('array-includes'); const Components = require('../util/Components'); -const arrayIncludes = require('array-includes'); const astUtil = require('../util/ast'); const docsUrl = require('../util/docsUrl'); @@ -276,17 +278,15 @@ module.exports = { */ function dedupeErrors() { for (const i in errors) { - if (!has(errors, i)) { - continue; - } - const index = errors[i].closest.ref.index; - if (!errors[index]) { - continue; - } - if (errors[i].score > errors[index].score) { - delete errors[index]; - } else { - delete errors[i]; + if (has(errors, i)) { + const index = errors[i].closest.ref.index; + if (errors[index]) { + if (errors[i].score > errors[index].score) { + delete errors[index]; + } else { + delete errors[i]; + } + } } } } @@ -297,19 +297,11 @@ module.exports = { function reportErrors() { dedupeErrors(); - let nodeA; - let nodeB; - let indexA; - let indexB; - for (const i in errors) { - if (!has(errors, i)) { - continue; - } - - nodeA = errors[i].node; - nodeB = errors[i].closest.ref.node; - indexA = i; - indexB = errors[i].closest.ref.index; + entries(errors).forEach((entry) => { + const nodeA = entry[1].node; + const nodeB = entry[1].closest.ref.node; + const indexA = entry[0]; + const indexB = entry[1].closest.ref.index; context.report({ node: nodeA, @@ -320,7 +312,7 @@ module.exports = { position: indexA < indexB ? 'before' : 'after' } }); - } + }); } /** @@ -399,50 +391,35 @@ module.exports = { typeAnnotation: !!node.typeAnnotation && node.value === null })); - let i; - let j; - let k; - let l; - let propA; - let propB; - let order; - // Loop around the properties - for (i = 0, j = propertiesInfos.length; i < j; i++) { - propA = propertiesInfos[i]; - + propertiesInfos.forEach((propA, i) => { // Loop around the properties a second time (for comparison) - for (k = 0, l = propertiesInfos.length; k < l; k++) { + propertiesInfos.forEach((propB, k) => { if (i === k) { - continue; + return; } - propB = propertiesInfos[k]; - // Compare the properties order - order = comparePropsOrder(propertiesInfos, propA, propB); - - // Continue to next comparison is order is correct - if (order.correct === true) { - continue; + const order = comparePropsOrder(propertiesInfos, propA, propB); + + if (!order.correct) { + // Store an error if the order is incorrect + storeError({ + node: properties[i], + index: order.indexA + }, { + node: properties[k], + index: order.indexB + }); } - - // Store an error if the order is incorrect - storeError({ - node: properties[i], - index: order.indexA - }, { - node: properties[k], - index: order.indexB - }); - } - } + }); + }); } return { - 'Program:exit': function() { + 'Program:exit': function () { const list = components.list(); - Object.keys(list).forEach(component => { + Object.keys(list).forEach((component) => { const properties = astUtil.getComponentProperties(list[component].node); checkPropsOrder(properties); }); diff --git a/lib/rules/sort-prop-types.js b/lib/rules/sort-prop-types.js index 71cff4a88b..84fd8adc13 100644 --- a/lib/rules/sort-prop-types.js +++ b/lib/rules/sort-prop-types.js @@ -1,6 +1,7 @@ /** * @fileoverview Enforce propTypes declarations alphabetical sorting */ + 'use strict'; const variableUtil = require('../util/variable'); @@ -47,8 +48,7 @@ module.exports = { }] }, - create: function(context) { - const sourceCode = context.getSourceCode(); + create(context) { const configuration = context.options[0] || {}; const requiredFirst = configuration.requiredFirst || false; const callbacksLast = configuration.callbacksLast || false; @@ -60,7 +60,7 @@ module.exports = { if (node.key && node.key.value) { return node.key.value; } - return sourceCode.getText(node.key || node.argument); + return context.getSourceCode().getText(node.key || node.argument); } function getValueName(node) { @@ -81,7 +81,7 @@ module.exports = { ); } - function getShapeProperties (node) { + function getShapeProperties(node) { return node.arguments && node.arguments[0] && node.arguments[0].properties; } @@ -149,13 +149,13 @@ module.exports = { return acc; }, [[]]); - nodeGroups.forEach(nodes => { + nodeGroups.forEach((nodes) => { const sortedAttributes = nodes.slice().sort(sorter); for (let i = nodes.length - 1; i >= 0; i--) { const sortedAttr = sortedAttributes[i]; const attr = nodes[i]; - let sortedAttrText = sourceCode.getText(sortedAttr); + let sortedAttrText = context.getSourceCode().getText(sortedAttr); if (sortShapeProp && isShapeProp(sortedAttr.value)) { const shape = getShapeProperties(sortedAttr.value); if (shape) { @@ -246,39 +246,41 @@ module.exports = { case 'ObjectExpression': checkSorted(node.properties); break; - case 'Identifier': + case 'Identifier': { const propTypesObject = variableUtil.findVariableByName(context, node.name); if (propTypesObject && propTypesObject.properties) { checkSorted(propTypesObject.properties); } break; - case 'CallExpression': + } + case 'CallExpression': { const innerNode = node.arguments && node.arguments[0]; if (propWrapperUtil.isPropWrapperFunction(context, node.callee.name) && innerNode) { checkNode(innerNode); } break; + } default: break; } } return { - CallExpression: function(node) { + CallExpression(node) { if (!sortShapeProp || !isShapeProp(node) || !(node.arguments && node.arguments[0])) { return; } checkSorted(node.arguments[0].properties); }, - ClassProperty: function(node) { + ClassProperty(node) { if (!propsUtil.isPropTypesDeclaration(node)) { return; } checkNode(node.value); }, - MemberExpression: function(node) { + MemberExpression(node) { if (!propsUtil.isPropTypesDeclaration(node)) { return; } @@ -286,8 +288,8 @@ module.exports = { checkNode(node.parent.right); }, - ObjectExpression: function(node) { - node.properties.forEach(property => { + ObjectExpression(node) { + node.properties.forEach((property) => { if (!property.key) { return; } diff --git a/lib/rules/state-in-constructor.js b/lib/rules/state-in-constructor.js index ebfeb65605..91a5d5d9db 100644 --- a/lib/rules/state-in-constructor.js +++ b/lib/rules/state-in-constructor.js @@ -2,6 +2,7 @@ * @fileoverview Enforce the state initialization style to be either in a constructor or with a class property * @author Kanitkorn Sujautra */ + 'use strict'; const Components = require('../util/Components'); @@ -27,7 +28,7 @@ module.exports = { create: Components.detect((context, components, utils) => { const option = context.options[0] || 'always'; return { - ClassProperty: function (node) { + ClassProperty(node) { if ( option === 'always' && !node.static && diff --git a/lib/rules/static-property-placement.js b/lib/rules/static-property-placement.js index ddeebefc7a..e8d3046d5c 100644 --- a/lib/rules/static-property-placement.js +++ b/lib/rules/static-property-placement.js @@ -2,13 +2,14 @@ * @fileoverview Defines where React component static properties should be positioned. * @author Daniel Mason */ + 'use strict'; +const fromEntries = require('object.fromentries'); const Components = require('../util/Components'); const docsUrl = require('../util/docsUrl'); const astUtil = require('../util/ast'); const propsUtil = require('../util/props'); -const fromEntries = require('object.fromentries'); // ------------------------------------------------------------------------------ // Positioning Options @@ -107,7 +108,7 @@ module.exports = { */ function reportNodeIncorrectlyPositioned(node, expectedRule) { // Detect if this node is an expected property declaration adn return the property name - const name = classProperties.find(propertyName => { + const name = classProperties.find((propertyName) => { if (propertiesToCheck[propertyName](node)) { return !!propertyName; } @@ -128,7 +129,7 @@ module.exports = { return { ClassProperty: node => reportNodeIncorrectlyPositioned(node, STATIC_PUBLIC_FIELD), - MemberExpression: node => { + MemberExpression: (node) => { // 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; @@ -148,7 +149,7 @@ module.exports = { reportNodeIncorrectlyPositioned(node, PROPERTY_ASSIGNMENT); }, - MethodDefinition: node => { + MethodDefinition: (node) => { // If the function is inside a class and is static getter then check if correctly positioned if (isContextInClass() && node.static && node.kind === 'get') { // Report error if needed diff --git a/lib/rules/style-prop-object.js b/lib/rules/style-prop-object.js index 4b9f750be5..d062359f80 100644 --- a/lib/rules/style-prop-object.js +++ b/lib/rules/style-prop-object.js @@ -2,6 +2,7 @@ * @fileoverview Enforce style prop value is an object * @author David Petersen */ + 'use strict'; const variableUtil = require('../util/variable'); @@ -22,7 +23,7 @@ module.exports = { schema: [] }, - create: function(context) { + create(context) { /** * @param {ASTNode} expression An Identifier node */ @@ -46,12 +47,12 @@ module.exports = { } return { - CallExpression: function(node) { + CallExpression(node) { if ( - node.callee - && node.callee.type === 'MemberExpression' - && node.callee.property.name === 'createElement' - && node.arguments.length > 1 + node.callee && + node.callee.type === 'MemberExpression' && + node.callee.property.name === 'createElement' && + node.arguments.length > 1 ) { if (node.arguments[1].type === 'ObjectExpression') { const style = node.arguments[1].properties.find(property => property.key && property.key.name === 'style' && !property.computed); @@ -66,7 +67,7 @@ module.exports = { } }, - JSXAttribute: function(node) { + JSXAttribute(node) { if (!node.value || node.name.name !== 'style') { return; } diff --git a/lib/rules/void-dom-elements-no-children.js b/lib/rules/void-dom-elements-no-children.js index 758ba4b04f..e4e1ce6643 100644 --- a/lib/rules/void-dom-elements-no-children.js +++ b/lib/rules/void-dom-elements-no-children.js @@ -3,6 +3,7 @@ * children * @author Joe Lencioni */ + 'use strict'; const has = require('has'); @@ -59,7 +60,7 @@ module.exports = { }, create: Components.detect((context, components, utils) => ({ - JSXElement: function(node) { + JSXElement(node) { const elementName = node.openingElement.name.name; if (!isVoidDOMElement(elementName)) { @@ -70,14 +71,14 @@ module.exports = { if (node.children.length > 0) { // e.g.
Foo
context.report({ - node: node, + node, message: errorMessage(elementName) }); } const attributes = node.openingElement.attributes; - const hasChildrenAttributeOrDanger = attributes.some(attribute => { + const hasChildrenAttributeOrDanger = attributes.some((attribute) => { if (!attribute.name) { return false; } @@ -88,13 +89,13 @@ module.exports = { if (hasChildrenAttributeOrDanger) { // e.g.
context.report({ - node: node, + node, message: errorMessage(elementName) }); } }, - CallExpression: function(node) { + CallExpression(node) { if (node.callee.type !== 'MemberExpression' && node.callee.type !== 'Identifier') { return; } @@ -125,14 +126,14 @@ module.exports = { if (firstChild) { // e.g. React.createElement('br', undefined, 'Foo') context.report({ - node: node, + node, message: errorMessage(elementName) }); } const props = args[1].properties; - const hasChildrenPropOrDanger = props.some(prop => { + const hasChildrenPropOrDanger = props.some((prop) => { if (!prop.key) { return false; } @@ -143,7 +144,7 @@ module.exports = { if (hasChildrenPropOrDanger) { // e.g. React.createElement('br', { children: 'Foo' }) context.report({ - node: node, + node, message: errorMessage(elementName) }); } diff --git a/lib/util/Components.js b/lib/util/Components.js index a2dd6f72e7..684fb7cd65 100644 --- a/lib/util/Components.js +++ b/lib/util/Components.js @@ -2,6 +2,7 @@ * @fileoverview Utility class and functions for React components detection * @author Yannick Croissant */ + 'use strict'; const doctrine = require('doctrine'); @@ -23,7 +24,8 @@ function usedPropTypesAreEquivalent(propA, propB) { if (propA.name === propB.name) { if (!propA.allNames && !propB.allNames) { return true; - } else if (Array.isArray(propA.allNames) && Array.isArray(propB.allNames) && propA.allNames.join('') === propB.allNames.join('')) { + } + if (Array.isArray(propA.allNames) && Array.isArray(propB.allNames) && propA.allNames.join('') === propB.allNames.join('')) { return true; } return false; @@ -33,7 +35,7 @@ function usedPropTypesAreEquivalent(propA, propB) { function mergeUsedPropTypes(propsList, newPropsList) { const propsToAdd = []; - newPropsList.forEach(newProp => { + newPropsList.forEach((newProp) => { const newPropisAlreadyInTheList = propsList.some(prop => usedPropTypesAreEquivalent(prop, newProp)); if (!newPropisAlreadyInTheList) { propsToAdd.push(newProp); @@ -43,12 +45,14 @@ function mergeUsedPropTypes(propsList, newPropsList) { return propsList.concat(propsToAdd); } +const Lists = new WeakMap(); + /** * Components */ class Components { constructor() { - this._list = {}; + Lists.set(this, {}); } /** @@ -60,19 +64,20 @@ class Components { */ add(node, confidence) { const id = getId(node); - if (this._list[id]) { - if (confidence === 0 || this._list[id].confidence === 0) { - this._list[id].confidence = 0; + const list = Lists.get(this); + if (list[id]) { + if (confidence === 0 || list[id].confidence === 0) { + list[id].confidence = 0; } else { - this._list[id].confidence = Math.max(this._list[id].confidence, confidence); + list[id].confidence = Math.max(list[id].confidence, confidence); } - return this._list[id]; + return list[id]; } - this._list[id] = { - node: node, - confidence: confidence + list[id] = { + node, + confidence }; - return this._list[id]; + return list[id]; } /** @@ -83,8 +88,9 @@ class Components { */ get(node) { const id = getId(node); - if (this._list[id] && this._list[id].confidence >= 1) { - return this._list[id]; + const item = Lists.get(this)[id]; + if (item && item.confidence >= 1) { + return item; } return null; } @@ -96,13 +102,14 @@ class Components { * @param {Object} props Additional properties to add to the component. */ set(node, props) { - let component = this._list[getId(node)]; + const list = Lists.get(this); + let component = list[getId(node)]; while (!component) { node = node.parent; if (!node) { return; } - component = this._list[getId(node)]; + component = list[getId(node)]; } Object.assign( @@ -124,14 +131,15 @@ class Components { * @returns {Object} Components list */ list() { + const thisList = Lists.get(this); const list = {}; const usedPropTypes = {}; // Find props used in components for which we are not confident - Object.keys(this._list).filter(i => this._list[i].confidence < 2).forEach(i => { + Object.keys(thisList).filter(i => thisList[i].confidence < 2).forEach((i) => { let component = null; let node = null; - node = this._list[i].node; + node = thisList[i].node; while (!component && node.parent) { node = node.parent; // Stop moving up if we reach a decorator @@ -141,7 +149,7 @@ class Components { component = this.get(node); } if (component) { - const newUsedProps = (this._list[i].usedPropTypes || []).filter(propType => !propType.node || propType.node.kind !== 'init'); + const newUsedProps = (thisList[i].usedPropTypes || []).filter(propType => !propType.node || propType.node.kind !== 'init'); const componentId = getId(component.node); @@ -150,9 +158,9 @@ class Components { }); // Assign used props in not confident components to the parent component - Object.keys(this._list).filter(j => this._list[j].confidence >= 2).forEach(j => { - const id = getId(this._list[j].node); - list[j] = this._list[j]; + Object.keys(thisList).filter(j => thisList[j].confidence >= 2).forEach((j) => { + const id = getId(thisList[j].node); + list[j] = thisList[j]; if (usedPropTypes[id]) { list[j].usedPropTypes = mergeUsedPropTypes(list[j].usedPropTypes || [], usedPropTypes[id]); } @@ -167,7 +175,8 @@ class Components { * @returns {Number} Components list length */ length() { - return Object.keys(this._list).filter(i => this._list[i].confidence >= 2).length; + const list = Lists.get(this); + return Object.keys(list).filter(i => list[i].confidence >= 2).length; } } @@ -186,7 +195,7 @@ function componentRule(rule, context) { * @param {ASTNode} node The AST node being checked. * @returns {Boolean} True if the node is a React ES5 component, false if not */ - isES5Component: function(node) { + isES5Component(node) { if (!node.parent) { return false; } @@ -199,7 +208,7 @@ function componentRule(rule, context) { * @param {ASTNode} node The AST node being checked. * @returns {Boolean} True if the node is a React ES6 component, false if not */ - isES6Component: function(node) { + isES6Component(node) { if (utils.isExplicitComponent(node)) { return true; } @@ -216,7 +225,7 @@ function componentRule(rule, context) { * @param {ASTNode} node The AST node being checked (can be a ReturnStatement or an ArrowFunctionExpression). * @returns {Boolean} True if the node is explicitly declared as a descendant of a React Component, false if not */ - isExplicitComponent: function(node) { + isExplicitComponent(node) { 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 @@ -248,7 +257,7 @@ function componentRule(rule, context) { * @param {ASTNode} node The AST node being checked. * @returns {Boolean} True if node extends React.PureComponent, false if not */ - isPureComponent: function (node) { + isPureComponent(node) { if (node.superClass) { return new RegExp(`^(${pragma}\\.)?PureComponent$`).test(sourceCode.getText(node.superClass)); } @@ -261,7 +270,7 @@ function componentRule(rule, context) { * @param {string} variable The variable name to check * @returns {Boolean} True if createElement is destructured from the pragma */ - isDestructuredFromPragmaImport: function(variable) { + isDestructuredFromPragmaImport(variable) { const variables = variableUtil.variablesInScope(context); const variableInScope = variableUtil.getVariable(variables, variable); if (variableInScope) { @@ -277,7 +286,7 @@ function componentRule(rule, context) { * @param {ASTNode} node The AST node being checked. * @returns {Boolean} True if createElement called from pragma */ - isCreateElement: function(node) { + isCreateElement(node) { const calledOnPragma = ( node && node.callee && @@ -303,7 +312,7 @@ function componentRule(rule, context) { * Check if we are in a class constructor * @return {boolean} true if we are in a class constructor, false if not */ - inConstructor: function() { + inConstructor() { let scope = context.getScope(); while (scope) { if (scope.block && scope.block.parent && scope.block.parent.kind === 'constructor') { @@ -319,7 +328,7 @@ function componentRule(rule, context) { * @param {Object} node The node to process * @returns {Boolean} */ - isStateMemberExpression: function(node) { + isStateMemberExpression(node) { return node.type === 'MemberExpression' && node.object.type === 'ThisExpression' && node.property.name === 'state'; }, @@ -342,8 +351,8 @@ function componentRule(rule, context) { property = 'argument'; } return { - node: node, - property: property + node, + property }; }, @@ -354,7 +363,7 @@ function componentRule(rule, context) { * @param {Boolean} [strict] If true, in a ternary condition the node must return JSX in both cases * @returns {Boolean} True if the node is returning JSX, false if not */ - isReturningJSX: function(ASTnode, strict) { + isReturningJSX(ASTnode, strict) { const nodeAndProperty = utils.getReturnPropertyAndNode(ASTnode); const node = nodeAndProperty.node; const property = nodeAndProperty.property; @@ -363,25 +372,18 @@ function componentRule(rule, context) { return false; } - const returnsConditionalJSXConsequent = - node[property] && + const returnsConditionalJSXConsequent = node[property] && node[property].type === 'ConditionalExpression' && - jsxUtil.isJSX(node[property].consequent) - ; - const returnsConditionalJSXAlternate = - node[property] && + jsxUtil.isJSX(node[property].consequent); + const returnsConditionalJSXAlternate = node[property] && node[property].type === 'ConditionalExpression' && - jsxUtil.isJSX(node[property].alternate) - ; - const returnsConditionalJSX = - strict ? - (returnsConditionalJSXConsequent && returnsConditionalJSXAlternate) : - (returnsConditionalJSXConsequent || returnsConditionalJSXAlternate); - - const returnsJSX = - node[property] && - jsxUtil.isJSX(node[property]) - ; + jsxUtil.isJSX(node[property].alternate); + const returnsConditionalJSX = strict ? + (returnsConditionalJSXConsequent && returnsConditionalJSXAlternate) : + (returnsConditionalJSXConsequent || returnsConditionalJSXAlternate); + + const returnsJSX = node[property] && + jsxUtil.isJSX(node[property]); const returnsPragmaCreateElement = this.isCreateElement(node[property]); return Boolean( @@ -444,7 +446,7 @@ function componentRule(rule, context) { * * @returns {ASTNode} component node, null if we are not in a component */ - getParentComponent: function() { + getParentComponent() { return ( utils.getParentES6Component() || utils.getParentES5Component() || @@ -457,7 +459,7 @@ function componentRule(rule, context) { * * @returns {ASTNode} component node, null if we are not in a component */ - getParentES5Component: function() { + getParentES5Component() { let scope = context.getScope(); while (scope) { const node = scope.block && scope.block.parent && scope.block.parent.parent; @@ -474,7 +476,7 @@ function componentRule(rule, context) { * * @returns {ASTNode} component node, null if we are not in a component */ - getParentES6Component: function() { + getParentES6Component() { let scope = context.getScope(); while (scope && scope.type !== 'class') { scope = scope.upper; @@ -491,7 +493,7 @@ function componentRule(rule, context) { * * @returns {ASTNode} component node, null if we are not in a component */ - getParentStatelessComponent: function() { + getParentStatelessComponent() { let scope = context.getScope(); while (scope) { const node = scope.block; @@ -542,7 +544,7 @@ function componentRule(rule, context) { * @param {ASTNode} node The AST node being checked (must be a MemberExpression). * @returns {ASTNode} component node, null if we cannot find the component */ - getRelatedComponent: function(node) { + getRelatedComponent(node) { let i; let j; let k; @@ -581,27 +583,26 @@ function componentRule(rule, context) { // Try to find the component using variable references const refs = variableInScope.references; - let refId; - for (i = 0, j = refs.length; i < j; i++) { - refId = refs[i].identifier; + refs.some((ref) => { + let refId = ref.identifier; if (refId.parent && refId.parent.type === 'MemberExpression') { refId = refId.parent; } if (sourceCode.getText(refId) !== componentName) { - continue; + return false; } if (refId.type === 'MemberExpression') { componentNode = refId.parent.right; } else if ( - refId.parent - && refId.parent.type === 'VariableDeclarator' - && refId.parent.init - && refId.parent.init.type !== 'Identifier' + refId.parent && + refId.parent.type === 'VariableDeclarator' && + refId.parent.init && + refId.parent.init.type !== 'Identifier' ) { componentNode = refId.parent.init; } - break; - } + return true; + }); if (componentNode) { // Return the component @@ -609,14 +610,12 @@ function componentRule(rule, context) { } // Try to find the component using variable declarations - let defInScope; const defs = variableInScope.defs; - for (i = 0, j = defs.length; i < j; i++) { - if (defs[i].type === 'ClassName' || defs[i].type === 'FunctionName' || defs[i].type === 'Variable') { - defInScope = defs[i]; - break; - } - } + const defInScope = defs.find(def => ( + def.type === 'ClassName' || + def.type === 'FunctionName' || + def.type === 'Variable' + )); if (!defInScope || !defInScope.node) { return null; } @@ -625,7 +624,7 @@ function componentRule(rule, context) { // Traverse the node properties to the component declaration for (i = 0, j = componentPath.length; i < j; i++) { if (!componentNode.properties) { - continue; + continue; // eslint-disable-line no-continue } for (k = 0, l = componentNode.properties.length; k < l; k++) { if (componentNode.properties[k].key && componentNode.properties[k].key.name === componentPath[i]) { @@ -646,7 +645,7 @@ function componentRule(rule, context) { // Component detection instructions const detectionInstructions = { - CallExpression: function(node) { + CallExpression(node) { if (!utils.isPragmaComponentWrapper(node)) { return; } @@ -655,21 +654,21 @@ function componentRule(rule, context) { } }, - ClassExpression: function(node) { + ClassExpression(node) { if (!utils.isES6Component(node)) { return; } components.add(node, 2); }, - ClassDeclaration: function(node) { + ClassDeclaration(node) { if (!utils.isES6Component(node)) { return; } components.add(node, 2); }, - ClassProperty: function(node) { + ClassProperty(node) { node = utils.getParentComponent(); if (!node) { return; @@ -677,14 +676,14 @@ function componentRule(rule, context) { components.add(node, 2); }, - ObjectExpression: function(node) { + ObjectExpression(node) { if (!utils.isES5Component(node)) { return; } components.add(node, 2); }, - FunctionExpression: function(node) { + FunctionExpression(node) { if (node.async) { components.add(node, 0); return; @@ -701,7 +700,7 @@ function componentRule(rule, context) { components.add(component, 1); }, - FunctionDeclaration: function(node) { + FunctionDeclaration(node) { if (node.async) { components.add(node, 0); return; @@ -713,7 +712,7 @@ function componentRule(rule, context) { components.add(node, 1); }, - ArrowFunctionExpression: function(node) { + ArrowFunctionExpression(node) { if (node.async) { components.add(node, 0); return; @@ -734,7 +733,7 @@ function componentRule(rule, context) { } }, - ThisExpression: function(node) { + ThisExpression(node) { const component = utils.getParentComponent(); if (!component || !/Function/.test(component.type) || !node.parent.property) { return; @@ -743,7 +742,7 @@ function componentRule(rule, context) { components.add(node, 0); }, - ReturnStatement: function(node) { + ReturnStatement(node) { if (!utils.isReturningJSX(node)) { return; } @@ -769,8 +768,8 @@ function componentRule(rule, context) { Object.keys(defaultPropsInstructions) )); - allKeys.forEach(instruction => { - updatedRuleInstructions[instruction] = function(node) { + allKeys.forEach((instruction) => { + updatedRuleInstructions[instruction] = function (node) { if (instruction in detectionInstructions) { detectionInstructions[instruction](node); } @@ -783,7 +782,9 @@ function componentRule(rule, context) { if (instruction in defaultPropsInstructions) { defaultPropsInstructions[instruction](node); } - return ruleInstructions[instruction] ? ruleInstructions[instruction](node) : void 0; + if (ruleInstructions[instruction]) { + return ruleInstructions[instruction](node); + } }; }); diff --git a/lib/util/annotations.js b/lib/util/annotations.js index 9586cec8c9..3161381b5b 100644 --- a/lib/util/annotations.js +++ b/lib/util/annotations.js @@ -3,6 +3,7 @@ * @author Yannick Croissant * @author Vitor Balocco */ + 'use strict'; /** @@ -25,5 +26,5 @@ function isAnnotatedFunctionPropsDeclaration(node, context) { } module.exports = { - isAnnotatedFunctionPropsDeclaration: isAnnotatedFunctionPropsDeclaration + isAnnotatedFunctionPropsDeclaration }; diff --git a/lib/util/ast.js b/lib/util/ast.js index 746bdb9ade..3f39beebce 100644 --- a/lib/util/ast.js +++ b/lib/util/ast.js @@ -1,6 +1,7 @@ /** * @fileoverview Utility functions for AST */ + 'use strict'; /** @@ -43,7 +44,8 @@ function findReturnStatement(node) { function getPropertyNameNode(node) { if (node.key || ['MethodDefinition', 'Property'].indexOf(node.type) !== -1) { return node.key; - } else if (node.type === 'MemberExpression') { + } + if (node.type === 'MemberExpression') { return node.property; } return null; @@ -89,9 +91,9 @@ function getFirstNodeInLine(context, node) { let lines; do { token = sourceCode.getTokenBefore(token); - lines = token.type === 'JSXText' - ? token.value.split('\n') - : null; + lines = token.type === 'JSXText' ? + token.value.split('\n') : + null; } while ( token.type === 'JSXText' && /^\s*$/.test(lines[lines.length - 1]) @@ -148,15 +150,56 @@ function isClass(node) { return node.type === 'ClassDeclaration' || node.type === 'ClassExpression'; } +/** + * Removes quotes from around an identifier. + * @param {string} string the identifier to strip + */ +function stripQuotes(string) { + return string.replace(/^'|'$/g, ''); +} + +/** + * Retrieve the name of a key node + * @param {Context} context The AST node with the key. + * @param {ASTNode} node The AST node with the key. + * @return {string} the name of the key + */ +function getKeyValue(context, node) { + if (node.type === 'ObjectTypeProperty') { + const tokens = context.getFirstTokens(node, 2); + return (tokens[0].value === '+' || tokens[0].value === '-' ? + tokens[1].value : + stripQuotes(tokens[0].value) + ); + } + const key = node.key || node.argument; + return key.type === 'Identifier' ? key.name : key.value; +} + +/** + * Checks if a node is being assigned a value: props.bar = 'bar' + * @param {ASTNode} node The AST node being checked. + * @returns {Boolean} + */ +function isAssignmentLHS(node) { + return ( + node.parent && + node.parent.type === 'AssignmentExpression' && + node.parent.left === node + ); +} + module.exports = { - findReturnStatement: findReturnStatement, - getFirstNodeInLine: getFirstNodeInLine, - getPropertyName: getPropertyName, - getPropertyNameNode: getPropertyNameNode, - getComponentProperties: getComponentProperties, - isArrowFunction: isArrowFunction, - isClass: isClass, - isFunction: isFunction, - isFunctionLikeExpression: isFunctionLikeExpression, - isNodeFirstInLine: isNodeFirstInLine + findReturnStatement, + getFirstNodeInLine, + getPropertyName, + getPropertyNameNode, + getComponentProperties, + getKeyValue, + isArrowFunction, + isAssignmentLHS, + isClass, + isFunction, + isFunctionLikeExpression, + isNodeFirstInLine }; diff --git a/lib/util/defaultProps.js b/lib/util/defaultProps.js index b7287e4b90..cf8ab46d14 100644 --- a/lib/util/defaultProps.js +++ b/lib/util/defaultProps.js @@ -1,6 +1,7 @@ /** * @fileoverview Common defaultProps detection functionality. */ + 'use strict'; const fromEntries = require('object.fromentries'); @@ -98,7 +99,7 @@ module.exports = function defaultPropsInstructions(context, components, utils) { } return { - MemberExpression: function(node) { + MemberExpression(node) { const isDefaultProp = propsUtil.isDefaultPropsDeclaration(node); if (!isDefaultProp) { @@ -160,7 +161,7 @@ module.exports = function defaultPropsInstructions(context, components, utils) { // return
Hello {this.props.name}
; // } // } - MethodDefinition: function(node) { + MethodDefinition(node) { if (!node.static || node.kind !== 'get') { return; } @@ -200,7 +201,7 @@ module.exports = function defaultPropsInstructions(context, components, utils) { // bar: 'baz' // }; // } - ClassProperty: function(node) { + ClassProperty(node) { if (!(node.static && node.value)) { return; } @@ -237,7 +238,7 @@ module.exports = function defaultPropsInstructions(context, components, utils) { // }; // } // }); - ObjectExpression: function(node) { + ObjectExpression(node) { // find component this propTypes/defaultProps belongs to const component = utils.isES5Component(node) && components.get(node); if (!component) { @@ -245,7 +246,7 @@ module.exports = function defaultPropsInstructions(context, components, utils) { } // Search for the proptypes declaration - node.properties.forEach(property => { + node.properties.forEach((property) => { if (property.type === 'ExperimentalSpreadProperty' || property.type === 'SpreadElement') { return; } diff --git a/lib/util/error.js b/lib/util/error.js index 95a92cf488..bbfc3d6f09 100644 --- a/lib/util/error.js +++ b/lib/util/error.js @@ -5,7 +5,7 @@ * @param {String} message - Message to log. */ function error(message) { - if (!/\=-(f|-format)=/.test(process.argv.join('='))) { + if (!/=-(f|-format)=/.test(process.argv.join('='))) { // eslint-disable-next-line no-console console.error(message); } diff --git a/lib/util/jsx.js b/lib/util/jsx.js index 69849df733..3b7aac324b 100644 --- a/lib/util/jsx.js +++ b/lib/util/jsx.js @@ -1,11 +1,12 @@ /** * @fileoverview Utility functions for JSX */ + 'use strict'; const elementType = require('jsx-ast-utils/elementType'); -const COMPAT_TAG_REGEX = /^[a-z]|\-/; +const COMPAT_TAG_REGEX = /^[a-z]|-/; /** * Checks if a node represents a DOM element. @@ -35,6 +36,6 @@ function isJSX(node) { } module.exports = { - isDOMComponent: isDOMComponent, - isJSX: isJSX + isDOMComponent, + isJSX }; diff --git a/lib/util/linkComponents.js b/lib/util/linkComponents.js index cb9847d1f0..066b2ea249 100644 --- a/lib/util/linkComponents.js +++ b/lib/util/linkComponents.js @@ -1,6 +1,7 @@ /** * @fileoverview Utility functions for propWrapperFunctions setting */ + 'use strict'; /** TODO: type {(string | { name: string, linkAttribute: string })[]} */ @@ -13,7 +14,7 @@ function getLinkComponents(context) { const linkComponents = /** @type {typeof DEFAULT_LINK_COMPONENTS} */ ( DEFAULT_LINK_COMPONENTS.concat(settings.linkComponents || []) ); - return new Map(linkComponents.map(value => { + return new Map(linkComponents.map((value) => { if (typeof value === 'string') { return [value, DEFAULT_LINK_ATTRIBUTE]; } @@ -22,5 +23,5 @@ function getLinkComponents(context) { } module.exports = { - getLinkComponents: getLinkComponents + getLinkComponents }; diff --git a/lib/util/log.js b/lib/util/log.js index ac3bfe8c3a..9782b21990 100644 --- a/lib/util/log.js +++ b/lib/util/log.js @@ -5,7 +5,7 @@ * @param {String} message - Message to log. */ function log(message) { - if (!/\=-(f|-format)=/.test(process.argv.join('='))) { + if (!/=-(f|-format)=/.test(process.argv.join('='))) { // eslint-disable-next-line no-console console.log(message); } diff --git a/lib/util/makeNoMethodSetStateRule.js b/lib/util/makeNoMethodSetStateRule.js index 0b7e164312..dfa02e23ab 100644 --- a/lib/util/makeNoMethodSetStateRule.js +++ b/lib/util/makeNoMethodSetStateRule.js @@ -2,6 +2,7 @@ * @fileoverview Prevent usage of setState in lifecycle methods * @author Yannick Croissant */ + 'use strict'; const docsUrl = require('./docsUrl'); @@ -38,7 +39,7 @@ function makeNoMethodSetStateRule(methodName, shouldCheckUnsafeCb) { }] }, - create: function(context) { + create(context) { const mode = context.options[0] || 'allow-in-func'; function nameMatches(name) { @@ -59,7 +60,7 @@ function makeNoMethodSetStateRule(methodName, shouldCheckUnsafeCb) { return { - CallExpression: function(node) { + CallExpression(node) { const callee = node.callee; if ( callee.type !== 'MemberExpression' || @@ -70,8 +71,7 @@ function makeNoMethodSetStateRule(methodName, shouldCheckUnsafeCb) { } const ancestors = context.getAncestors(callee).reverse(); let depth = 0; - for (let i = 0, j = ancestors.length; i < j; i++) { - const ancestor = ancestors[i]; + ancestors.some((ancestor) => { if (/Function(Expression|Declaration)$/.test(ancestor.type)) { depth++; } @@ -80,14 +80,14 @@ function makeNoMethodSetStateRule(methodName, shouldCheckUnsafeCb) { !nameMatches(ancestor.key.name) || (mode !== 'disallow-in-func' && depth > 1) ) { - continue; + return false; } context.report({ node: callee, message: `Do not use setState in ${ancestor.key.name}` }); - break; - } + return true; + }); } }; } diff --git a/lib/util/pragma.js b/lib/util/pragma.js index 27e3ed7fa7..47682847d5 100644 --- a/lib/util/pragma.js +++ b/lib/util/pragma.js @@ -2,6 +2,7 @@ * @fileoverview Utility functions for React pragma configuration * @author Yannick Croissant */ + 'use strict'; const JSX_ANNOTATION_REGEX = /^\*\s*@jsx\s+([^\s]+)/; @@ -54,7 +55,7 @@ function getFromContext(context) { } module.exports = { - getCreateClassFromContext: getCreateClassFromContext, - getFragmentFromContext: getFragmentFromContext, - getFromContext: getFromContext + getCreateClassFromContext, + getFragmentFromContext, + getFromContext }; diff --git a/lib/util/propTypes.js b/lib/util/propTypes.js index 8485591822..b38eaf0dcc 100644 --- a/lib/util/propTypes.js +++ b/lib/util/propTypes.js @@ -1,13 +1,15 @@ /** * @fileoverview Common propTypes detection functionality. */ + 'use strict'; const annotations = require('./annotations'); const propsUtil = require('./props'); const variableUtil = require('./variable'); const versionUtil = require('./version'); -const propWrapperUtil = require('../util/propWrapper'); +const propWrapperUtil = require('./propWrapper'); +const getKeyValue = require('./ast').getKeyValue; /** * Checks if we are declaring a props as a generic type in a flow-annotated class. @@ -24,32 +26,6 @@ function isSuperTypeParameterPropsDeclaration(node) { return false; } -/** - * Removes quotes from around an identifier. - * @param {string} string the identifier to strip - */ -function stripQuotes(string) { - return string.replace(/^\'|\'$/g, ''); -} - -/** - * Retrieve the name of a key node - * @param {Context} context The AST node with the key. - * @param {ASTNode} node The AST node with the key. - * @return {string} the name of the key - */ -function getKeyValue(context, node) { - if (node.type === 'ObjectTypeProperty') { - const tokens = context.getFirstTokens(node, 2); - return (tokens[0].value === '+' || tokens[0].value === '-' - ? tokens[1].value - : stripQuotes(tokens[0].value) - ); - } - const key = node.key || node.argument; - return key.type === 'Identifier' ? key.name : key.value; -} - /** * Iterates through a properties node, like a customized forEach. * @param {Object} context Array of properties to iterate. @@ -96,7 +72,6 @@ module.exports = function propTypesInstructions(context, components, utils) { const defaults = {customValidators: []}; const configuration = Object.assign({}, defaults, context.options[0] || {}); const customValidators = configuration.customValidators; - const sourceCode = context.getSourceCode(); /** * Returns the full scope. @@ -138,14 +113,14 @@ module.exports = function propTypesInstructions(context, components, utils) { /* eslint-disable no-use-before-define */ /** @type {TypeDeclarationBuilders} */ const typeDeclarationBuilders = { - GenericTypeAnnotation: function(annotation, parentName, seen) { + GenericTypeAnnotation(annotation, parentName, seen) { if (getInTypeScope(annotation.id.name)) { return buildTypeAnnotationDeclarationTypes(getInTypeScope(annotation.id.name), parentName, seen); } return {}; }, - ObjectTypeAnnotation: function(annotation, parentName, seen) { + ObjectTypeAnnotation(annotation, parentName, seen) { let containsObjectTypeSpread = false; const shapeTypeDefinition = { type: 'shape', @@ -172,7 +147,7 @@ module.exports = function propTypesInstructions(context, components, utils) { return shapeTypeDefinition; }, - UnionTypeAnnotation: function(annotation, parentName, seen) { + UnionTypeAnnotation(annotation, parentName, seen) { /** @type {UnionTypeDefinition} */ const unionTypeDefinition = { type: 'union', @@ -198,7 +173,7 @@ module.exports = function propTypesInstructions(context, components, utils) { return unionTypeDefinition; }, - ArrayTypeAnnotation: function(annotation, parentName, seen) { + ArrayTypeAnnotation(annotation, parentName, seen) { const fullName = [parentName, '*'].join('.'); const child = buildTypeAnnotationDeclarationTypes(annotation.elementType, fullName, seen); child.fullName = fullName; @@ -301,7 +276,7 @@ module.exports = function propTypesInstructions(context, components, utils) { * @returns {Boolean} True if propTypes should be ignored (e.g. when a type can't be resolved, when it is imported) */ function declarePropTypesForIntersectionTypeAnnotation(propTypes, declaredPropTypes) { - return propTypes.types.some(annotation => { + return propTypes.types.some((annotation) => { if (annotation.type === 'ObjectTypeAnnotation') { return declarePropTypesForObjectTypeAnnotation(annotation, declaredPropTypes); } @@ -319,7 +294,8 @@ module.exports = function propTypesInstructions(context, components, utils) { if (!typeNode) { return true; - } else if (typeNode.type === 'IntersectionTypeAnnotation') { + } + if (typeNode.type === 'IntersectionTypeAnnotation') { return declarePropTypesForIntersectionTypeAnnotation(typeNode, declaredPropTypes); } @@ -368,7 +344,7 @@ module.exports = function propTypesInstructions(context, components, utils) { const callName = value.callee.property.name; const argument = value.arguments[0]; switch (callName) { - case 'shape': + case 'shape': { if (argument.type !== 'ObjectExpression') { // Invalid proptype or cannot analyse statically return {}; @@ -388,8 +364,9 @@ module.exports = function propTypesInstructions(context, components, utils) { } }); return shapeTypeDefinition; + } case 'arrayOf': - case 'objectOf': + case 'objectOf': { const fullName = [parentName, '*'].join('.'); const child = buildReactDeclarationTypes(argument, fullName); child.fullName = fullName; @@ -401,7 +378,8 @@ module.exports = function propTypesInstructions(context, components, utils) { __ANY_KEY__: child } }; - case 'oneOfType': + } + case 'oneOfType': { if ( !argument.elements || !argument.elements.length @@ -433,6 +411,7 @@ module.exports = function propTypesInstructions(context, components, utils) { return {}; } return unionTypeDefinition; + } case 'instanceOf': return { type: 'instance', @@ -480,7 +459,7 @@ module.exports = function propTypesInstructions(context, components, utils) { declaredPropTypes[key] = types; }); break; - case 'MemberExpression': + case 'MemberExpression': { let curDeclaredPropTypes = declaredPropTypes; // Walk the list of properties, until we reach the assignment // ie: ClassX.propTypes.a.b.c = ... @@ -535,27 +514,32 @@ module.exports = function propTypesInstructions(context, components, utils) { } } break; - case 'Identifier': + } + case 'Identifier': { const variablesInScope = variableUtil.variablesInScope(context); - for (let i = 0, j = variablesInScope.length; i < j; i++) { - if (variablesInScope[i].name !== propTypes.name) { - continue; - } - const defInScope = variablesInScope[i].defs[variablesInScope[i].defs.length - 1]; + const firstMatchingVariable = variablesInScope + .find(variableInScope => variableInScope.name === propTypes.name); + if (firstMatchingVariable) { + const defInScope = firstMatchingVariable.defs[firstMatchingVariable.defs.length - 1]; markPropTypesAsDeclared(node, defInScope.node && defInScope.node.init); return; } ignorePropsValidation = true; break; - case 'CallExpression': + } + case 'CallExpression': { if ( - propWrapperUtil.isPropWrapperFunction(context, sourceCode.getText(propTypes.callee)) && + propWrapperUtil.isPropWrapperFunction( + context, + context.getSourceCode().getText(propTypes.callee) + ) && propTypes.arguments && propTypes.arguments[0] ) { markPropTypesAsDeclared(node, propTypes.arguments[0]); return; } break; + } case 'IntersectionTypeAnnotation': ignorePropsValidation = declarePropTypesForIntersectionTypeAnnotation(propTypes, declaredPropTypes); break; @@ -567,8 +551,8 @@ module.exports = function propTypesInstructions(context, components, utils) { } components.set(node, { - declaredPropTypes: declaredPropTypes, - ignorePropsValidation: ignorePropsValidation + declaredPropTypes, + ignorePropsValidation }); } @@ -587,7 +571,7 @@ module.exports = function propTypesInstructions(context, components, utils) { const param = node.params[0]; if (param.typeAnnotation && param.typeAnnotation.typeAnnotation && param.typeAnnotation.typeAnnotation.type === 'UnionTypeAnnotation') { - param.typeAnnotation.typeAnnotation.types.forEach(annotation => { + param.typeAnnotation.typeAnnotation.types.forEach((annotation) => { if (annotation.type === 'GenericTypeAnnotation') { markPropTypesAsDeclared(node, resolveTypeAnnotation(annotation)); } else { @@ -648,20 +632,20 @@ module.exports = function propTypesInstructions(context, components, utils) { } return { - ClassExpression: function(node) { + ClassExpression(node) { // TypeParameterDeclaration need to be added to typeScope in order to handle ClassExpressions. // This visitor is executed before TypeParameterDeclaration are scoped, therefore we postpone // processing class expressions until when the program exists. classExpressions.push(node); }, - ClassDeclaration: function(node) { + ClassDeclaration(node) { if (isSuperTypeParameterPropsDeclaration(node)) { markPropTypesAsDeclared(node, resolveSuperParameterPropsType(node)); } }, - ClassProperty: function(node) { + ClassProperty(node) { if (isAnnotatedClassPropsDeclaration(node)) { markPropTypesAsDeclared(node, resolveTypeAnnotation(node)); } else if (propsUtil.isPropTypesDeclaration(node)) { @@ -669,9 +653,9 @@ module.exports = function propTypesInstructions(context, components, utils) { } }, - ObjectExpression: function(node) { + ObjectExpression(node) { // Search for the proptypes declaration - node.properties.forEach(property => { + node.properties.forEach((property) => { if (!propsUtil.isPropTypesDeclaration(property)) { return; } @@ -679,7 +663,7 @@ module.exports = function propTypesInstructions(context, components, utils) { }); }, - FunctionExpression: function(node) { + FunctionExpression(node) { if (node.parent.type !== 'MethodDefinition') { markAnnotatedFunctionArgumentsAsDeclared(node); } @@ -689,7 +673,7 @@ module.exports = function propTypesInstructions(context, components, utils) { ArrowFunctionExpression: markAnnotatedFunctionArgumentsAsDeclared, - MemberExpression: function(node) { + MemberExpression(node) { if (propsUtil.isPropTypesDeclaration(node)) { const component = utils.getRelatedComponent(node); if (!component) { @@ -699,7 +683,7 @@ module.exports = function propTypesInstructions(context, components, utils) { } }, - MethodDefinition: function(node) { + MethodDefinition(node) { if (!node.static || node.kind !== 'get' || !propsUtil.isPropTypesDeclaration(node)) { return; } @@ -716,11 +700,11 @@ module.exports = function propTypesInstructions(context, components, utils) { } }, - TypeAlias: function(node) { + TypeAlias(node) { setInTypeScope(node.id.name, node.right); }, - TypeParameterDeclaration: function(node) { + TypeParameterDeclaration(node) { const identifier = node.params[0]; if (identifier.typeAnnotation) { @@ -728,11 +712,11 @@ module.exports = function propTypesInstructions(context, components, utils) { } }, - Program: function() { + Program() { stack = [{}]; }, - BlockStatement: function () { + BlockStatement() { stack.push(Object.create(typeScope())); }, @@ -740,8 +724,8 @@ module.exports = function propTypesInstructions(context, components, utils) { stack.pop(); }, - 'Program:exit': function() { - classExpressions.forEach(node => { + 'Program:exit': function () { + classExpressions.forEach((node) => { if (isSuperTypeParameterPropsDeclaration(node)) { markPropTypesAsDeclared(node, resolveSuperParameterPropsType(node)); } diff --git a/lib/util/propWrapper.js b/lib/util/propWrapper.js index 24092c43aa..7202b13a16 100644 --- a/lib/util/propWrapper.js +++ b/lib/util/propWrapper.js @@ -1,6 +1,7 @@ /** * @fileoverview Utility functions for propWrapperFunctions setting */ + 'use strict'; function getPropWrapperFunctions(context) { @@ -13,7 +14,7 @@ function isPropWrapperFunction(context, name) { } const propWrapperFunctions = getPropWrapperFunctions(context); const splitName = name.split('.'); - return Array.from(propWrapperFunctions).some(func => { + return Array.from(propWrapperFunctions).some((func) => { if (splitName.length === 2 && func.object === splitName[0] && func.property === splitName[1]) { return true; } @@ -22,6 +23,6 @@ function isPropWrapperFunction(context, name) { } module.exports = { - getPropWrapperFunctions: getPropWrapperFunctions, - isPropWrapperFunction: isPropWrapperFunction + getPropWrapperFunctions, + isPropWrapperFunction }; diff --git a/lib/util/props.js b/lib/util/props.js index f8b7171b30..1177200972 100644 --- a/lib/util/props.js +++ b/lib/util/props.js @@ -1,6 +1,7 @@ /** * @fileoverview Utility functions for props */ + 'use strict'; const astUtil = require('./ast'); @@ -91,11 +92,11 @@ function isRequiredPropType(propTypeExpression) { } module.exports = { - isPropTypesDeclaration: isPropTypesDeclaration, - isContextTypesDeclaration: isContextTypesDeclaration, - isContextTypeDeclaration: isContextTypeDeclaration, - isChildContextTypesDeclaration: isChildContextTypesDeclaration, - isDefaultPropsDeclaration: isDefaultPropsDeclaration, - isDisplayNameDeclaration: isDisplayNameDeclaration, - isRequiredPropType: isRequiredPropType + isPropTypesDeclaration, + isContextTypesDeclaration, + isContextTypeDeclaration, + isChildContextTypesDeclaration, + isDefaultPropsDeclaration, + isDisplayNameDeclaration, + isRequiredPropType }; diff --git a/lib/util/usedPropTypes.js b/lib/util/usedPropTypes.js index f57d2e636b..13f5162e68 100755 --- a/lib/util/usedPropTypes.js +++ b/lib/util/usedPropTypes.js @@ -1,10 +1,12 @@ /** * @fileoverview Common used propTypes detection functionality. */ + 'use strict'; const astUtil = require('./ast'); const versionUtil = require('./version'); +const ast = require('./ast'); // ------------------------------------------------------------------------------ // Constants @@ -21,7 +23,7 @@ const ASYNC_SAFE_LIFE_CYCLE_METHODS = ['getDerivedStateFromProps', 'getSnapshotB * @param {ASTNode} node The AST node being checked. * @returns {Boolean} True if the prop name matches */ -function isPropAttributeName (node) { +function isPropAttributeName(node) { return ( node.init.name === 'props' || node.init.name === 'nextProps' || @@ -38,247 +40,210 @@ function mustBeValidated(component) { return !!(component && !component.ignorePropsValidation); } -module.exports = function usedPropTypesInstructions(context, components, utils) { - const sourceCode = context.getSourceCode(); - const checkAsyncSafeLifeCycles = versionUtil.testReactVersion(context, '16.3.0'); - - /** - * Check if we are in a class constructor - * @return {boolean} true if we are in a class constructor, false if not - */ - function inComponentWillReceiveProps() { - let scope = context.getScope(); - while (scope) { - if ( - scope.block - && scope.block.parent - && scope.block.parent.key - && scope.block.parent.key.name === 'componentWillReceiveProps' - ) { - return true; - } - scope = scope.upper; +/** + * Check if we are in a class constructor + * @return {boolean} true if we are in a class constructor, false if not + */ +function inComponentWillReceiveProps(context) { + let scope = context.getScope(); + while (scope) { + if ( + scope.block && + scope.block.parent && + scope.block.parent.key && + scope.block.parent.key.name === 'componentWillReceiveProps' + ) { + return true; } - return false; + scope = scope.upper; } + return false; +} - /** - * Check if we are in a lifecycle method - * @return {boolean} true if we are in a class constructor, false if not - **/ - function inLifeCycleMethod() { - let scope = context.getScope(); - while (scope) { - if (scope.block && scope.block.parent && scope.block.parent.key) { - const name = scope.block.parent.key.name; - - if (LIFE_CYCLE_METHODS.indexOf(name) >= 0) { - return true; - } - if (checkAsyncSafeLifeCycles && ASYNC_SAFE_LIFE_CYCLE_METHODS.indexOf(name) >= 0) { - return true; - } +/** + * Check if we are in a lifecycle method + * @return {boolean} true if we are in a class constructor, false if not + */ +function inLifeCycleMethod(context, checkAsyncSafeLifeCycles) { + let scope = context.getScope(); + while (scope) { + if (scope.block && scope.block.parent && scope.block.parent.key) { + const name = scope.block.parent.key.name; + + if (LIFE_CYCLE_METHODS.indexOf(name) >= 0) { + return true; + } + if (checkAsyncSafeLifeCycles && ASYNC_SAFE_LIFE_CYCLE_METHODS.indexOf(name) >= 0) { + return true; } - scope = scope.upper; } - return false; + scope = scope.upper; } + return false; +} - /** - * Returns true if the given node is a React Component lifecycle method - * @param {ASTNode} node The AST node being checked. - * @return {Boolean} True if the node is a lifecycle method - */ - function isNodeALifeCycleMethod(node) { - const nodeKeyName = (node.key || /** @type {ASTNode} */ ({})).name; - - if (node.kind === 'constructor') { - return true; - } - if (LIFE_CYCLE_METHODS.indexOf(nodeKeyName) >= 0) { - return true; - } - if (checkAsyncSafeLifeCycles && ASYNC_SAFE_LIFE_CYCLE_METHODS.indexOf(nodeKeyName) >= 0) { - return true; - } +/** + * Returns true if the given node is a React Component lifecycle method + * @param {ASTNode} node The AST node being checked. + * @return {Boolean} True if the node is a lifecycle method + */ +function isNodeALifeCycleMethod(node, checkAsyncSafeLifeCycles) { + const nodeKeyName = (node.key || /** @type {ASTNode} */ ({})).name; - return false; + if (node.kind === 'constructor') { + return true; + } + if (LIFE_CYCLE_METHODS.indexOf(nodeKeyName) >= 0) { + return true; + } + if (checkAsyncSafeLifeCycles && ASYNC_SAFE_LIFE_CYCLE_METHODS.indexOf(nodeKeyName) >= 0) { + return true; } - /** - * Returns true if the given node is inside a React Component lifecycle - * method. - * @param {ASTNode} node The AST node being checked. - * @return {Boolean} True if the node is inside a lifecycle method - */ - function isInLifeCycleMethod(node) { - if ((node.type === 'MethodDefinition' || node.type === 'Property') && isNodeALifeCycleMethod(node)) { - return true; - } + return false; +} - if (node.parent) { - return isInLifeCycleMethod(node.parent); - } +/** + * Returns true if the given node is inside a React Component lifecycle + * method. + * @param {ASTNode} node The AST node being checked. + * @return {Boolean} True if the node is inside a lifecycle method + */ +function isInLifeCycleMethod(node, checkAsyncSafeLifeCycles) { + if ((node.type === 'MethodDefinition' || node.type === 'Property') && isNodeALifeCycleMethod(node, checkAsyncSafeLifeCycles)) { + return true; + } - return false; + if (node.parent) { + return isInLifeCycleMethod(node.parent, checkAsyncSafeLifeCycles); } - /** - * Check if the current node is in a setState updater method - * @return {boolean} true if we are in a setState updater, false if not - */ - function inSetStateUpdater() { - let scope = context.getScope(); - while (scope) { - if ( - scope.block && scope.block.parent - && scope.block.parent.type === 'CallExpression' - && scope.block.parent.callee.property - && scope.block.parent.callee.property.name === 'setState' - // Make sure we are in the updater not the callback - && scope.block.parent.arguments[0].start === scope.block.start - ) { - return true; - } - scope = scope.upper; + return false; +} + +/** + * Check if the current node is in a setState updater method + * @return {boolean} true if we are in a setState updater, false if not + */ +function inSetStateUpdater(context) { + let scope = context.getScope(); + while (scope) { + if ( + scope.block && scope.block.parent && + scope.block.parent.type === 'CallExpression' && + scope.block.parent.callee.property && + scope.block.parent.callee.property.name === 'setState' && + // Make sure we are in the updater not the callback + scope.block.parent.arguments[0].start === scope.block.start + ) { + return true; } - return false; + scope = scope.upper; } + return false; +} - function isPropArgumentInSetStateUpdater(node) { - let scope = context.getScope(); - while (scope) { - if ( - scope.block && scope.block.parent - && scope.block.parent.type === 'CallExpression' - && scope.block.parent.callee.property - && scope.block.parent.callee.property.name === 'setState' - // Make sure we are in the updater not the callback - && scope.block.parent.arguments[0].start === scope.block.start - && scope.block.parent.arguments[0].params - && scope.block.parent.arguments[0].params.length > 1 - ) { - return scope.block.parent.arguments[0].params[1].name === node.object.name; - } - scope = scope.upper; +function isPropArgumentInSetStateUpdater(context, node) { + let scope = context.getScope(); + while (scope) { + if ( + scope.block && scope.block.parent && + scope.block.parent.type === 'CallExpression' && + scope.block.parent.callee.property && + scope.block.parent.callee.property.name === 'setState' && + // Make sure we are in the updater not the callback + scope.block.parent.arguments[0].start === scope.block.start && + scope.block.parent.arguments[0].params && + scope.block.parent.arguments[0].params.length > 1 + ) { + return scope.block.parent.arguments[0].params[1].name === node.object.name; } - return false; + scope = scope.upper; } + return false; +} - /** - * Checks if the prop has spread operator. - * @param {ASTNode} node The AST node being marked. - * @returns {Boolean} True if the prop has spread operator, false if not. - */ - function hasSpreadOperator(node) { - const tokens = sourceCode.getTokens(node); - return tokens.length && tokens[0].value === '...'; - } +/** + * Checks if the prop has spread operator. + * @param {ASTNode} node The AST node being marked. + * @returns {Boolean} True if the prop has spread operator, false if not. + */ +function hasSpreadOperator(context, node) { + const tokens = context.getSourceCode().getTokens(node); + return tokens.length && tokens[0].value === '...'; +} - /** - * Removes quotes from around an identifier. - * @param {string} string the identifier to strip - */ - function stripQuotes(string) { - return string.replace(/^\'|\'$/g, ''); +/** + * Retrieve the name of a property node + * @param {ASTNode} node The AST node with the property. + * @return {string|undefined} the name of the property or undefined if not found + */ +function getPropertyName(node, context, utils, checkAsyncSafeLifeCycles) { + const sourceCode = context.getSourceCode(); + const isDirectProp = DIRECT_PROPS_REGEX.test(sourceCode.getText(node)); + const isDirectNextProp = DIRECT_NEXT_PROPS_REGEX.test(sourceCode.getText(node)); + const isDirectPrevProp = DIRECT_PREV_PROPS_REGEX.test(sourceCode.getText(node)); + const isDirectSetStateProp = isPropArgumentInSetStateUpdater(context, node); + const isInClassComponent = utils.getParentES6Component() || utils.getParentES5Component(); + const isNotInConstructor = !utils.inConstructor(node); + const isNotInLifeCycleMethod = !inLifeCycleMethod(context, checkAsyncSafeLifeCycles); + const isNotInSetStateUpdater = !inSetStateUpdater(context); + if ((isDirectProp || isDirectNextProp || isDirectPrevProp || isDirectSetStateProp) && + isInClassComponent && + isNotInConstructor && + isNotInLifeCycleMethod && + isNotInSetStateUpdater + ) { + return; } - - /** - * Retrieve the name of a key node - * @param {ASTNode} node The AST node with the key. - * @return {string} the name of the key - */ - function getKeyValue(node) { - if (node.type === 'ObjectTypeProperty') { - const tokens = context.getFirstTokens(node, 2); - return (tokens[0].value === '+' || tokens[0].value === '-' - ? tokens[1].value - : stripQuotes(tokens[0].value) - ); - } - const key = node.key || node.argument; - return key.type === 'Identifier' ? key.name : key.value; + if (!isDirectProp && !isDirectNextProp && !isDirectPrevProp && !isDirectSetStateProp) { + node = node.parent; } - - /** - * Retrieve the name of a property node - * @param {ASTNode} node The AST node with the property. - * @return {string|undefined} the name of the property or undefined if not found - */ - function getPropertyName(node) { - const isDirectProp = DIRECT_PROPS_REGEX.test(sourceCode.getText(node)); - const isDirectNextProp = DIRECT_NEXT_PROPS_REGEX.test(sourceCode.getText(node)); - const isDirectPrevProp = DIRECT_PREV_PROPS_REGEX.test(sourceCode.getText(node)); - const isDirectSetStateProp = isPropArgumentInSetStateUpdater(node); - const isInClassComponent = utils.getParentES6Component() || utils.getParentES5Component(); - const isNotInConstructor = !utils.inConstructor(node); - const isNotInLifeCycleMethod = !inLifeCycleMethod(); - const isNotInSetStateUpdater = !inSetStateUpdater(); - if ((isDirectProp || isDirectNextProp || isDirectPrevProp || isDirectSetStateProp) - && isInClassComponent - && isNotInConstructor - && isNotInLifeCycleMethod - && isNotInSetStateUpdater - ) { - return void 0; - } - if (!isDirectProp && !isDirectNextProp && !isDirectPrevProp && !isDirectSetStateProp) { - node = node.parent; - } - const property = node.property; - if (property) { - switch (property.type) { - case 'Identifier': - if (node.computed) { - return '__COMPUTED_PROP__'; - } - return property.name; - case 'MemberExpression': - return void 0; - case 'Literal': - // Accept computed properties that are literal strings - if (typeof property.value === 'string') { - return property.value; - } - // falls through - default: - if (node.computed) { - return '__COMPUTED_PROP__'; - } - break; - } + const property = node.property; + if (property) { + switch (property.type) { + case 'Identifier': + if (node.computed) { + return '__COMPUTED_PROP__'; + } + return property.name; + case 'MemberExpression': + return; + case 'Literal': + // Accept computed properties that are literal strings + if (typeof property.value === 'string') { + return property.value; + } + // falls through + default: + if (node.computed) { + return '__COMPUTED_PROP__'; + } + break; } - return void 0; } +} - /** - * Checks if a prop is being assigned a value props.bar = 'bar' - * @param {ASTNode} node The AST node being checked. - * @returns {Boolean} - */ - function isAssignmentToProp(node) { - return ( - node.parent && - node.parent.type === 'AssignmentExpression' && - node.parent.left === node - ); - } +/** + * Checks if we are using a prop + * @param {ASTNode} node The AST node being checked. + * @returns {Boolean} True if we are using a prop, false if not. + */ +function isPropTypesUsage(node, context, utils, checkAsyncSafeLifeCycles) { + const isThisPropsUsage = node.object.type === 'ThisExpression' && node.property.name === 'props'; + const isPropsUsage = isThisPropsUsage || node.object.name === 'nextProps' || node.object.name === 'prevProps'; + const isClassUsage = ( + (utils.getParentES6Component() || utils.getParentES5Component()) && + (isThisPropsUsage || isPropArgumentInSetStateUpdater(context, node)) + ); + const isStatelessFunctionUsage = node.object.name === 'props' && !ast.isAssignmentLHS(node); + return isClassUsage || + isStatelessFunctionUsage || + (isPropsUsage && inLifeCycleMethod(context, checkAsyncSafeLifeCycles)); +} - /** - * Checks if we are using a prop - * @param {ASTNode} node The AST node being checked. - * @returns {Boolean} True if we are using a prop, false if not. - */ - function isPropTypesUsage(node) { - const isThisPropsUsage = node.object.type === 'ThisExpression' && node.property.name === 'props'; - const isPropsUsage = isThisPropsUsage || node.object.name === 'nextProps' || node.object.name === 'prevProps'; - const isClassUsage = ( - (utils.getParentES6Component() || utils.getParentES5Component()) && - (isThisPropsUsage || isPropArgumentInSetStateUpdater(node)) - ); - const isStatelessFunctionUsage = node.object.name === 'props' && !isAssignmentToProp(node); - return isClassUsage || isStatelessFunctionUsage || (isPropsUsage && inLifeCycleMethod()); - } +module.exports = function usedPropTypesInstructions(context, components, utils) { + const checkAsyncSafeLifeCycles = versionUtil.testReactVersion(context, '16.3.0'); /** * Mark a prop type as used @@ -293,7 +258,7 @@ module.exports = function usedPropTypesInstructions(context, components, utils) let properties; switch (node.type) { case 'MemberExpression': - name = getPropertyName(node); + name = getPropertyName(node, context, utils, checkAsyncSafeLifeCycles); if (name) { allNames = parentNames.concat(name); if ( @@ -309,7 +274,7 @@ module.exports = function usedPropTypesInstructions(context, components, utils) node.parent.id && node.parent.id.properties && node.parent.id.properties.length && - getKeyValue(node.parent.id.properties[0]) + ast.getKeyValue(context, node.parent.id.properties[0]) ) { type = 'destructuring'; properties = node.parent.id.properties; @@ -322,37 +287,37 @@ module.exports = function usedPropTypesInstructions(context, components, utils) break; } type = 'destructuring'; - const propParam = inSetStateUpdater() ? node.params[1] : node.params[0]; - properties = propParam.type === 'AssignmentPattern' - ? propParam.left.properties - : propParam.properties; + const propParam = inSetStateUpdater(context) ? node.params[1] : node.params[0]; + properties = propParam.type === 'AssignmentPattern' ? + propParam.left.properties : + propParam.properties; break; } case 'VariableDeclarator': - for (let i = 0, j = node.id.properties.length; i < j; i++) { + node.id.properties.some((property) => { // let {props: {firstname}} = this const thisDestructuring = ( - node.id.properties[i].key && ( - (node.id.properties[i].key.name === 'props' || node.id.properties[i].key.value === 'props') && - node.id.properties[i].value.type === 'ObjectPattern' + property.key && ( + (property.key.name === 'props' || property.key.value === 'props') && + property.value.type === 'ObjectPattern' ) ); // let {firstname} = props const genericDestructuring = isPropAttributeName(node) && ( utils.getParentStatelessComponent() || - isInLifeCycleMethod(node) + isInLifeCycleMethod(node, checkAsyncSafeLifeCycles) ); if (thisDestructuring) { - properties = node.id.properties[i].value.properties; + properties = property.value.properties; } else if (genericDestructuring) { properties = node.id.properties; } else { - continue; + return false; } type = 'destructuring'; - break; - } + return true; + }); break; default: throw new Error(`${node.type} ASTNodes are not handled by markPropTypesAsUsed`); @@ -363,34 +328,35 @@ module.exports = function usedPropTypesInstructions(context, components, utils) let ignoreUnusedPropTypesValidation = component && component.ignoreUnusedPropTypesValidation || false; switch (type) { - case 'direct': + case 'direct': { // Ignore Object methods if (name in Object.prototype) { break; } - const nodeSource = sourceCode.getText(node); - const isDirectProp = DIRECT_PROPS_REGEX.test(nodeSource) - || DIRECT_NEXT_PROPS_REGEX.test(nodeSource) - || DIRECT_PREV_PROPS_REGEX.test(nodeSource); + const nodeSource = context.getSourceCode().getText(node); + const isDirectProp = DIRECT_PROPS_REGEX.test(nodeSource) || + DIRECT_NEXT_PROPS_REGEX.test(nodeSource) || + DIRECT_PREV_PROPS_REGEX.test(nodeSource); const reportedNode = ( - !isDirectProp && !utils.inConstructor() && !inComponentWillReceiveProps() ? + !isDirectProp && !utils.inConstructor() && !inComponentWillReceiveProps(context) ? node.parent.property : node.property ); usedPropTypes.push({ - name: name, - allNames: allNames, + name, + allNames, node: reportedNode }); break; - case 'destructuring': + } + case 'destructuring': { for (let k = 0, l = (properties || []).length; k < l; k++) { - if (hasSpreadOperator(properties[k]) || properties[k].computed) { + if (hasSpreadOperator(context, properties[k]) || properties[k].computed) { ignoreUnusedPropTypesValidation = true; break; } - const propName = getKeyValue(properties[k]); + const propName = ast.getKeyValue(context, properties[k]); let currentNode = node; allNames = []; @@ -401,20 +367,21 @@ module.exports = function usedPropTypesInstructions(context, components, utils) allNames.push(propName); if (propName) { usedPropTypes.push({ - allNames: allNames, + allNames, name: propName, node: properties[k] }); } } break; + } default: break; } components.set(component ? component.node : node, { - usedPropTypes: usedPropTypes, - ignoreUnusedPropTypesValidation: ignoreUnusedPropTypesValidation + usedPropTypes, + ignoreUnusedPropTypesValidation }); } @@ -423,7 +390,7 @@ module.exports = function usedPropTypesInstructions(context, components, utils) * FunctionDeclaration, or FunctionExpression */ function markDestructuredFunctionArgumentsAsUsed(node) { - const param = node.params && inSetStateUpdater() ? node.params[1] : node.params[0]; + const param = node.params && inSetStateUpdater(context) ? node.params[1] : node.params[0]; const destructuring = param && ( param.type === 'ObjectPattern' || @@ -436,7 +403,7 @@ module.exports = function usedPropTypesInstructions(context, components, utils) } function handleSetStateUpdater(node) { - if (!node.params || node.params.length < 2 || !inSetStateUpdater()) { + if (!node.params || node.params.length < 2 || !inSetStateUpdater(context)) { return; } markPropTypesAsUsed(node); @@ -458,7 +425,7 @@ module.exports = function usedPropTypesInstructions(context, components, utils) return; } - Object.keys(propTypes).forEach(key => { + Object.keys(propTypes).forEach((key) => { const node = propTypes[key].node; if (node.value && astUtil.isFunctionLikeExpression(node.value)) { @@ -468,14 +435,14 @@ module.exports = function usedPropTypesInstructions(context, components, utils) } return { - VariableDeclarator: function(node) { + VariableDeclarator(node) { const destructuring = node.init && node.id && node.id.type === 'ObjectPattern'; // let {props: {firstname}} = this const thisDestructuring = destructuring && node.init.type === 'ThisExpression'; // let {firstname} = props const statelessDestructuring = destructuring && isPropAttributeName(node) && ( utils.getParentStatelessComponent() || - isInLifeCycleMethod(node) + isInLifeCycleMethod(node, checkAsyncSafeLifeCycles) ); if (!thisDestructuring && !statelessDestructuring) { @@ -490,31 +457,31 @@ module.exports = function usedPropTypesInstructions(context, components, utils) FunctionExpression: handleFunctionLikeExpressions, - JSXSpreadAttribute: function(node) { + JSXSpreadAttribute(node) { const component = components.get(utils.getParentComponent()); components.set(component ? component.node : node, { ignoreUnusedPropTypesValidation: true }); }, - MemberExpression: function(node) { - if (isPropTypesUsage(node)) { + MemberExpression(node) { + if (isPropTypesUsage(node, context, utils, checkAsyncSafeLifeCycles)) { markPropTypesAsUsed(node); } }, - ObjectPattern: function(node) { + ObjectPattern(node) { // If the object pattern is a destructured props object in a lifecycle // method -- mark it for used props. - if (isNodeALifeCycleMethod(node.parent.parent) && node.properties.length > 0) { + if (isNodeALifeCycleMethod(node.parent.parent, checkAsyncSafeLifeCycles) && node.properties.length > 0) { markPropTypesAsUsed(node.parent); } }, - 'Program:exit': function() { + 'Program:exit': function () { const list = components.list(); - Object.keys(list).filter(component => mustBeValidated(list[component])).forEach(component => { + Object.keys(list).filter(component => mustBeValidated(list[component])).forEach((component) => { handleCustomValidators(list[component]); }); } diff --git a/lib/util/variable.js b/lib/util/variable.js index 73b761c164..cbc30f4ad0 100644 --- a/lib/util/variable.js +++ b/lib/util/variable.js @@ -2,6 +2,7 @@ * @fileoverview Utility functions for React components detection * @author Yannick Croissant */ + 'use strict'; /** @@ -72,8 +73,8 @@ function findVariableByName(context, name) { } module.exports = { - findVariable: findVariable, - findVariableByName: findVariableByName, - getVariable: getVariable, - variablesInScope: variablesInScope + findVariable, + findVariableByName, + getVariable, + variablesInScope }; diff --git a/lib/util/version.js b/lib/util/version.js index f28288afd9..3e8af90c01 100644 --- a/lib/util/version.js +++ b/lib/util/version.js @@ -2,6 +2,7 @@ * @fileoverview Utility functions for React and Flow version configuration * @author Yannick Croissant */ + 'use strict'; const resolve = require('resolve'); @@ -16,7 +17,7 @@ function resetWarningFlag() { function detectReactVersion() { try { const reactPath = resolve.sync('react', {basedir: process.cwd()}); - const react = require(reactPath); + const react = require(reactPath); // eslint-disable-line import/no-dynamic-require return react.version; } catch (e) { if (!warnedForMissingVersion && e.code === 'MODULE_NOT_FOUND') { @@ -54,7 +55,7 @@ function getReactVersionFromContext(context) { function detectFlowVersion() { try { const flowPackageJsonPath = resolve.sync('flow-bin/package.json', {basedir: process.cwd()}); - const flowPackageJson = require(flowPackageJsonPath); + const flowPackageJson = require(flowPackageJsonPath); // eslint-disable-line import/no-dynamic-require return flowPackageJson.version; } catch (e) { if (e.code === 'MODULE_NOT_FOUND') { @@ -80,7 +81,7 @@ function getFlowVersionFromContext(context) { } confVer = String(flowVersion); } else { - throw 'Could not retrieve flowVersion from settings'; + throw 'Could not retrieve flowVersion from settings'; // eslint-disable-line no-throw-literal } confVer = /^[0-9]+\.[0-9]+$/.test(confVer) ? `${confVer}.0` : confVer; return confVer.split('.').map(part => Number(part)); @@ -95,9 +96,9 @@ function test(context, methodVer, confVer) { const confVers = normalizeParts(confVer); const higherMajor = methodVers[0] < confVers[0]; const higherMinor = methodVers[0] === confVers[0] && methodVers[1] < confVers[1]; - const higherOrEqualPatch = methodVers[0] === confVers[0] - && methodVers[1] === confVers[1] - && methodVers[2] <= confVers[2]; + const higherOrEqualPatch = methodVers[0] === confVers[0] && + methodVers[1] === confVers[1] && + methodVers[2] <= confVers[2]; return higherMajor || higherMinor || higherOrEqualPatch; } diff --git a/package.json b/package.json index 0c499017e3..cc2140d354 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,9 @@ "doctrine": "^2.1.0", "has": "^1.0.3", "jsx-ast-utils": "^2.1.0", + "object.entries": "^1.1.0", "object.fromentries": "^2.0.0", + "object.values": "^1.1.0", "prop-types": "^15.7.2", "resolve": "^1.10.1" }, @@ -41,6 +43,8 @@ "babel-eslint": "^8.2.6", "coveralls": "^3.0.2", "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0", + "eslint-config-airbnb-base": "^13.1.0", + "eslint-plugin-import": "^2.17.2", "istanbul": "^0.4.5", "mocha": "^5.2.0", "sinon": "^7.2.2", diff --git a/tests/index.js b/tests/index.js index 69c8051fae..2a05f350f6 100644 --- a/tests/index.js +++ b/tests/index.js @@ -1,4 +1,5 @@ /* eslint-env mocha */ + 'use strict'; const plugin = require('..'); @@ -11,11 +12,11 @@ const ruleFiles = fs.readdirSync(path.resolve(__dirname, '../lib/rules/')) .map(f => path.basename(f, '.js')); describe('all rule files should be exported by the plugin', () => { - ruleFiles.forEach(ruleName => { + ruleFiles.forEach((ruleName) => { it(`should export ${ruleName}`, () => { assert.equal( plugin.rules[ruleName], - require(path.join('../lib/rules', ruleName)) + require(path.join('../lib/rules', ruleName)) // eslint-disable-line import/no-dynamic-require ); }); }); @@ -23,7 +24,7 @@ describe('all rule files should be exported by the plugin', () => { describe('deprecated rules', () => { it('marks all deprecated rules as deprecated', () => { - ruleFiles.forEach(ruleName => { + ruleFiles.forEach((ruleName) => { const inDeprecatedRules = Boolean(plugin.deprecatedRules[ruleName]); const isDeprecated = plugin.rules[ruleName].meta.deprecated; if (inDeprecatedRules) { @@ -36,16 +37,16 @@ describe('deprecated rules', () => { }); describe('configurations', () => { - it('should export a \'recommended\' configuration', () => { + it('should export a ‘recommended’ configuration', () => { assert(plugin.configs.recommended); - Object.keys(plugin.configs.recommended.rules).forEach(configName => { + Object.keys(plugin.configs.recommended.rules).forEach((configName) => { assert.equal(configName.indexOf('react/'), 0); - const ruleName = configName.substring('react/'.length); + const ruleName = configName.slice('react/'.length); assert(plugin.rules[ruleName]); }); - ruleFiles.forEach(ruleName => { - const inRecommendedConfig = Boolean(plugin.configs.recommended.rules[`react/${ruleName}`]); + ruleFiles.forEach((ruleName) => { + const inRecommendedConfig = !!plugin.configs.recommended.rules[`react/${ruleName}`]; const isRecommended = plugin.rules[ruleName].meta.docs.recommended; if (inRecommendedConfig) { assert(isRecommended, `${ruleName} metadata should mark it as recommended`); @@ -54,16 +55,19 @@ describe('configurations', () => { } }); }); - it('should export a \'all\' configuration', () => { + + it('should export an ‘all’ configuration', () => { assert(plugin.configs.all); - Object.keys(plugin.configs.all.rules).forEach(configName => { + + Object.keys(plugin.configs.all.rules).forEach((configName) => { assert.equal(configName.indexOf('react/'), 0); assert.equal(plugin.configs.all.rules[configName], 2); }); - ruleFiles.forEach(ruleName => { + + ruleFiles.forEach((ruleName) => { const inDeprecatedRules = Boolean(plugin.deprecatedRules[ruleName]); const inAllConfig = Boolean(plugin.configs.all.rules[`react/${ruleName}`]); - assert(inDeprecatedRules ^ inAllConfig); + assert(inDeprecatedRules ^ inAllConfig); // eslint-disable-line no-bitwise }); }); }); diff --git a/tests/lib/rules/boolean-prop-naming.js b/tests/lib/rules/boolean-prop-naming.js index 0850ef682e..a74a9c913f 100644 --- a/tests/lib/rules/boolean-prop-naming.js +++ b/tests/lib/rules/boolean-prop-naming.js @@ -2,14 +2,15 @@ * @fileoverview Enforces consistent naming for boolean props * @author Ev Haus */ + 'use strict'; // ------------------------------------------------------------------------------ // Requirements // ------------------------------------------------------------------------------ -const rule = require('../../../lib/rules/boolean-prop-naming'); const RuleTester = require('eslint').RuleTester; +const rule = require('../../../lib/rules/boolean-prop-naming'); const parsers = require('../../helpers/parsers'); diff --git a/tests/lib/rules/button-has-type.js b/tests/lib/rules/button-has-type.js index 44989a6c1c..948626ec42 100644 --- a/tests/lib/rules/button-has-type.js +++ b/tests/lib/rules/button-has-type.js @@ -2,14 +2,15 @@ * @fileoverview Forbid "button" element without an explicit "type" attribute * @author Filipp Riabchun */ + 'use strict'; // ------------------------------------------------------------------------------ // Requirements // ------------------------------------------------------------------------------ -const rule = require('../../../lib/rules/button-has-type'); const RuleTester = require('eslint').RuleTester; +const rule = require('../../../lib/rules/button-has-type'); const parserOptions = { ecmaVersion: 2018, diff --git a/tests/lib/rules/default-props-match-prop-types.js b/tests/lib/rules/default-props-match-prop-types.js index 8c1f3643dc..0e6c6d0be6 100644 --- a/tests/lib/rules/default-props-match-prop-types.js +++ b/tests/lib/rules/default-props-match-prop-types.js @@ -3,14 +3,15 @@ * @author Vitor Balocco * @author Roy Sutton */ + 'use strict'; // ------------------------------------------------------------------------------ // Requirements // ------------------------------------------------------------------------------ -const rule = require('../../../lib/rules/default-props-match-prop-types'); const RuleTester = require('eslint').RuleTester; +const rule = require('../../../lib/rules/default-props-match-prop-types'); const parsers = require('../../helpers/parsers'); diff --git a/tests/lib/rules/destructuring-assignment.js b/tests/lib/rules/destructuring-assignment.js index c2bf3accc9..6d91a3205c 100644 --- a/tests/lib/rules/destructuring-assignment.js +++ b/tests/lib/rules/destructuring-assignment.js @@ -1,11 +1,12 @@ /** * @fileoverview Rule to forbid or enforce destructuring assignment consistency. - **/ + */ + 'use strict'; -const rule = require('../../../lib/rules/destructuring-assignment'); const RuleTester = require('eslint').RuleTester; +const rule = require('../../../lib/rules/destructuring-assignment'); const parsers = require('../../helpers/parsers'); diff --git a/tests/lib/rules/display-name.js b/tests/lib/rules/display-name.js index 70e25e4538..c3c27675cb 100644 --- a/tests/lib/rules/display-name.js +++ b/tests/lib/rules/display-name.js @@ -2,14 +2,15 @@ * @fileoverview Prevent missing displayName in a React component definition * @author Yannick Croissant */ + 'use strict'; // ------------------------------------------------------------------------------ // Requirements // ------------------------------------------------------------------------------ -const rule = require('../../../lib/rules/display-name'); const RuleTester = require('eslint').RuleTester; +const rule = require('../../../lib/rules/display-name'); const parsers = require('../../helpers/parsers'); diff --git a/tests/lib/rules/forbid-component-props.js b/tests/lib/rules/forbid-component-props.js index 29d625390e..7cb904999b 100644 --- a/tests/lib/rules/forbid-component-props.js +++ b/tests/lib/rules/forbid-component-props.js @@ -1,14 +1,15 @@ /** * @fileoverview Tests for forbid-component-props */ + 'use strict'; // ----------------------------------------------------------------------------- // Requirements // ----------------------------------------------------------------------------- -const rule = require('../../../lib/rules/forbid-component-props'); const RuleTester = require('eslint').RuleTester; +const rule = require('../../../lib/rules/forbid-component-props'); const parserOptions = { ecmaVersion: 2018, diff --git a/tests/lib/rules/forbid-dom-props.js b/tests/lib/rules/forbid-dom-props.js index ba0f1366c3..0f3e737398 100644 --- a/tests/lib/rules/forbid-dom-props.js +++ b/tests/lib/rules/forbid-dom-props.js @@ -1,14 +1,15 @@ /** * @fileoverview Tests for forbid-dom-props */ + 'use strict'; // ----------------------------------------------------------------------------- // Requirements // ----------------------------------------------------------------------------- -const rule = require('../../../lib/rules/forbid-dom-props'); const RuleTester = require('eslint').RuleTester; +const rule = require('../../../lib/rules/forbid-dom-props'); const parserOptions = { ecmaVersion: 2018, diff --git a/tests/lib/rules/forbid-elements.js b/tests/lib/rules/forbid-elements.js index dfd155c94e..d1b269521b 100644 --- a/tests/lib/rules/forbid-elements.js +++ b/tests/lib/rules/forbid-elements.js @@ -1,14 +1,15 @@ /** * @fileoverview Tests for forbid-elements */ + 'use strict'; // ----------------------------------------------------------------------------- // Requirements // ----------------------------------------------------------------------------- -const rule = require('../../../lib/rules/forbid-elements'); const RuleTester = require('eslint').RuleTester; +const rule = require('../../../lib/rules/forbid-elements'); const parserOptions = { ecmaVersion: 2018, @@ -104,16 +105,20 @@ ruleTester.run('forbid-elements', rule, { }, { code: '', - options: [{forbid: [ - {element: 'dotted.Component', message: 'that ain\'t cool'} - ]}], + options: [{ + forbid: [ + {element: 'dotted.Component', message: 'that ain\'t cool'} + ] + }], errors: [{message: ' is forbidden, that ain\'t cool'}] }, { code: '