diff --git a/README.md b/README.md index 4d30bd8033..3a06ef705d 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,7 @@ Enable the rules that you would like to use. ## Other useful plugins +- Rules of Hooks: [eslint-plugin-react-hooks](https://github.com/facebook/react/tree/master/packages/eslint-plugin-react-hooks) - JSX accessibility: [eslint-plugin-jsx-a11y](https://github.com/evcohen/eslint-plugin-jsx-a11y) - React Native: [eslint-plugin-react-native](https://github.com/Intellicode/eslint-plugin-react-native) diff --git a/lib/rules/jsx-curly-spacing.js b/lib/rules/jsx-curly-spacing.js index a53f24a740..b587d2fe52 100644 --- a/lib/rules/jsx-curly-spacing.js +++ b/lib/rules/jsx-curly-spacing.js @@ -244,7 +244,7 @@ module.exports = { // Take comments into consideration to narrow the fix range to what is actually affected. (See #1414) if (nextComment.length > 0) { - return fixByTrimmingWhitespace(fixer, token.range[1], Math.min(nextToken.range[0], nextComment[0].start), 'start'); + return fixByTrimmingWhitespace(fixer, token.range[1], Math.min(nextToken.range[0], nextComment[0].range[0]), 'start'); } return fixByTrimmingWhitespace(fixer, token.range[1], nextToken.range[0], 'start'); @@ -279,7 +279,7 @@ module.exports = { // Take comments into consideration to narrow the fix range to what is actually affected. (See #1414) if (previousComment.length > 0) { - return fixByTrimmingWhitespace(fixer, Math.max(previousToken.range[1], previousComment[0].end), token.range[0], 'end'); + return fixByTrimmingWhitespace(fixer, Math.max(previousToken.range[1], previousComment[0].range[1]), token.range[0], 'end'); } return fixByTrimmingWhitespace(fixer, previousToken.range[1], token.range[0], 'end'); diff --git a/lib/rules/jsx-no-bind.js b/lib/rules/jsx-no-bind.js index 612f492cea..35f1c8a2d3 100644 --- a/lib/rules/jsx-no-bind.js +++ b/lib/rules/jsx-no-bind.js @@ -132,13 +132,13 @@ module.exports = { function findVariableViolation(node, name) { getBlockStatementAncestors(node).find( - block => reportVariableViolation(node, name, block.start) + block => reportVariableViolation(node, name, block.range[0]) ); } return { BlockStatement(node) { - setBlockVariableNameSet(node.start); + setBlockVariableNameSet(node.range[0]); }, VariableDeclarator(node) { @@ -154,7 +154,7 @@ module.exports = { node.parent.kind === 'const' // only support const right now ) { addVariableNameToSet( - variableViolationType, node.id.name, blockAncestors[0].start + variableViolationType, node.id.name, blockAncestors[0].range[0] ); } }, diff --git a/lib/rules/jsx-pascal-case.js b/lib/rules/jsx-pascal-case.js index 13f65eaa10..c8be42538e 100644 --- a/lib/rules/jsx-pascal-case.js +++ b/lib/rules/jsx-pascal-case.js @@ -93,22 +93,24 @@ module.exports = { return { JSXOpeningElement(node) { + const isCompatTag = jsxUtil.isDOMComponent(node); + if (isCompatTag) return undefined; + let name = elementType(node); if (name.length === 1) return undefined; - // Get namespace if the type is JSXNamespacedName or JSXMemberExpression - if (name.indexOf(':') > -1) { - name = name.substring(0, name.indexOf(':')); - } else if (name.indexOf('.') > -1) { - name = name.substring(0, name.indexOf('.')); + // Get JSXIdentifier if the type is JSXNamespacedName or JSXMemberExpression + if (name.lastIndexOf(':') > -1) { + name = name.substring(name.lastIndexOf(':') + 1); + } else if (name.lastIndexOf('.') > -1) { + name = name.substring(name.lastIndexOf('.') + 1); } const isPascalCase = testPascalCase(name); - const isCompatTag = jsxUtil.isDOMComponent(node); const isAllowedAllCaps = allowAllCaps && testAllCaps(name); const isIgnored = ignore.indexOf(name) !== -1; - if (!isPascalCase && !isCompatTag && !isAllowedAllCaps && !isIgnored) { + if (!isPascalCase && !isAllowedAllCaps && !isIgnored) { let message = `Imported JSX component ${name} must be in PascalCase`; if (allowAllCaps) { diff --git a/lib/util/jsx.js b/lib/util/jsx.js index e3bcc23e3a..f41c3b42fe 100644 --- a/lib/util/jsx.js +++ b/lib/util/jsx.js @@ -6,23 +6,17 @@ const elementType = require('jsx-ast-utils/elementType'); -const COMPAT_TAG_REGEX = /^[a-z]|-/; +// See https://github.com/babel/babel/blob/ce420ba51c68591e057696ef43e028f41c6e04cd/packages/babel-types/src/validators/react/isCompatTag.js +// for why we only test for the first character +const COMPAT_TAG_REGEX = /^[a-z]/; /** - * Checks if a node represents a DOM element. + * Checks if a node represents a DOM element according to React. * @param {object} node - JSXOpeningElement to check. * @returns {boolean} Whether or not the node corresponds to a DOM element. */ function isDOMComponent(node) { - let name = elementType(node); - - // Get namespace if the type is JSXNamespacedName or JSXMemberExpression - if (name.indexOf(':') > -1) { - name = name.slice(0, name.indexOf(':')); - } else if (name.indexOf('.') > -1) { - name = name.slice(0, name.indexOf('.')); - } - + const name = elementType(node); return COMPAT_TAG_REGEX.test(name); } diff --git a/lib/util/usedPropTypes.js b/lib/util/usedPropTypes.js index f1ac14622c..ab0c188f32 100755 --- a/lib/util/usedPropTypes.js +++ b/lib/util/usedPropTypes.js @@ -170,7 +170,7 @@ function isPropArgumentInSetStateUpdater(context, name) { unwrappedParentCalleeNode.property && unwrappedParentCalleeNode.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].range[0] === scope.block.range[0] && scope.block.parent.arguments[0].params && scope.block.parent.arguments[0].params.length > 1 ) { diff --git a/tests/lib/rules/jsx-pascal-case.js b/tests/lib/rules/jsx-pascal-case.js index 8428783818..af9d914d55 100644 --- a/tests/lib/rules/jsx-pascal-case.js +++ b/tests/lib/rules/jsx-pascal-case.js @@ -29,6 +29,10 @@ const parserOptions = { const ruleTester = new RuleTester({parserOptions}); ruleTester.run('jsx-pascal-case', rule, { valid: [{ + // The rule must not warn on components that start with a lowercase + // because they are interpreted as HTML elements by React + code: '' + }, { code: '' }, { code: '' @@ -52,6 +56,8 @@ ruleTester.run('jsx-pascal-case', rule, { code: '' }, { code: '' + }, { + code: '' }, { code: '', parser: parsers.BABEL_ESLINT