diff --git a/lib/rules/jsx-pascal-case.js b/lib/rules/jsx-pascal-case.js index e6e1339cc2..343bcd3b55 100644 --- a/lib/rules/jsx-pascal-case.js +++ b/lib/rules/jsx-pascal-case.js @@ -54,22 +54,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 = PASCAL_CASE_REGEX.test(name); - const isCompatTag = jsxUtil.isDOMComponent(node); const isAllowedAllCaps = allowAllCaps && ALL_CAPS_TAG_REGEX.test(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/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