/** * @fileoverview Enforce PascalCase for user-defined JSX components * @author Jake Marsh */ 'use strict'; const elementType = require('jsx-ast-utils/elementType'); const docsUrl = require('../util/docsUrl'); const jsxUtil = require('../util/jsx'); // ------------------------------------------------------------------------------ // Constants // ------------------------------------------------------------------------------ const PASCAL_CASE_REGEX = /^([A-Z0-9]|[A-Z0-9]+[a-z0-9]+(?:[A-Z0-9]+[a-z0-9]*)*)$/; const ALL_CAPS_TAG_REGEX = /^[A-Z0-9]+$/; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ module.exports = { meta: { docs: { description: 'Enforce PascalCase for user-defined JSX components', category: 'Stylistic Issues', recommended: false, url: docsUrl('jsx-pascal-case') }, schema: [{ type: 'object', properties: { allowAllCaps: { type: 'boolean' }, ignore: { type: 'array' } }, additionalProperties: false }] }, create(context) { const configuration = context.options[0] || {}; const allowAllCaps = configuration.allowAllCaps || false; const ignore = configuration.ignore || []; return { JSXOpeningElement(node) { let name = elementType(node); // 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('.')); } 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) { context.report({ node, message: `Imported JSX component ${name} must be in PascalCase` }); } } }; } };