|  | 
|  | 1 | +/** | 
|  | 2 | + * @fileoverview disallow the use of reserved names in component definitions | 
|  | 3 | + * @author Jake Hassel <https://github.com/shadskii> | 
|  | 4 | + */ | 
|  | 5 | +'use strict' | 
|  | 6 | + | 
|  | 7 | +const utils = require('../utils') | 
|  | 8 | +const casing = require('../utils/casing') | 
|  | 9 | + | 
|  | 10 | +const htmlElements = require('../utils/html-elements.json') | 
|  | 11 | +const deprecatedHtmlElements = require('../utils/deprecated-html-elements.json') | 
|  | 12 | +const svgElements = require('../utils/svg-elements.json') | 
|  | 13 | + | 
|  | 14 | +const kebabCaseElements = [ | 
|  | 15 | +  'annotation-xml', | 
|  | 16 | +  'color-profile', | 
|  | 17 | +  'font-face', | 
|  | 18 | +  'font-face-src', | 
|  | 19 | +  'font-face-uri', | 
|  | 20 | +  'font-face-format', | 
|  | 21 | +  'font-face-name', | 
|  | 22 | +  'missing-glyph' | 
|  | 23 | +] | 
|  | 24 | + | 
|  | 25 | +const isLowercase = (word) => /^[a-z]*$/.test(word) | 
|  | 26 | +const capitalizeFirstLetter = (word) => word[0].toUpperCase() + word.substring(1, word.length) | 
|  | 27 | + | 
|  | 28 | +const RESERVED_NAMES = new Set( | 
|  | 29 | +  [ | 
|  | 30 | +    ...kebabCaseElements, | 
|  | 31 | +    ...kebabCaseElements.map(casing.pascalCase), | 
|  | 32 | +    ...htmlElements, | 
|  | 33 | +    ...htmlElements.map(capitalizeFirstLetter), | 
|  | 34 | +    ...deprecatedHtmlElements, | 
|  | 35 | +    ...deprecatedHtmlElements.map(capitalizeFirstLetter), | 
|  | 36 | +    ...svgElements, | 
|  | 37 | +    ...svgElements.filter(isLowercase).map(capitalizeFirstLetter) | 
|  | 38 | +  ]) | 
|  | 39 | + | 
|  | 40 | +// ------------------------------------------------------------------------------ | 
|  | 41 | +// Rule Definition | 
|  | 42 | +// ------------------------------------------------------------------------------ | 
|  | 43 | + | 
|  | 44 | +module.exports = { | 
|  | 45 | +  meta: { | 
|  | 46 | +    type: 'suggestion', | 
|  | 47 | +    docs: { | 
|  | 48 | +      description: 'disallow the use of reserved names in component definitions', | 
|  | 49 | +      category: undefined, // 'essential' | 
|  | 50 | +      url: 'https://eslint.vuejs.org/rules/no-reserved-component-names.html' | 
|  | 51 | +    }, | 
|  | 52 | +    fixable: null, | 
|  | 53 | +    schema: [] | 
|  | 54 | +  }, | 
|  | 55 | + | 
|  | 56 | +  create (context) { | 
|  | 57 | +    function canVerify (node) { | 
|  | 58 | +      return node.type === 'Literal' || ( | 
|  | 59 | +        node.type === 'TemplateLiteral' && | 
|  | 60 | +        node.expressions.length === 0 && | 
|  | 61 | +        node.quasis.length === 1 | 
|  | 62 | +      ) | 
|  | 63 | +    } | 
|  | 64 | + | 
|  | 65 | +    function reportIfInvalid (node) { | 
|  | 66 | +      let name | 
|  | 67 | +      if (node.type === 'TemplateLiteral') { | 
|  | 68 | +        const quasis = node.quasis[0] | 
|  | 69 | +        name = quasis.value.cooked | 
|  | 70 | +      } else { | 
|  | 71 | +        name = node.value | 
|  | 72 | +      } | 
|  | 73 | +      if (RESERVED_NAMES.has(name)) { | 
|  | 74 | +        report(node, name) | 
|  | 75 | +      } | 
|  | 76 | +    } | 
|  | 77 | + | 
|  | 78 | +    function report (node, name) { | 
|  | 79 | +      context.report({ | 
|  | 80 | +        node: node, | 
|  | 81 | +        message: 'Name "{{name}}" is reserved.', | 
|  | 82 | +        data: { | 
|  | 83 | +          name: name | 
|  | 84 | +        } | 
|  | 85 | +      }) | 
|  | 86 | +    } | 
|  | 87 | + | 
|  | 88 | +    return Object.assign({}, | 
|  | 89 | +      { | 
|  | 90 | +        "CallExpression > MemberExpression > Identifier[name='component']" (node) { | 
|  | 91 | +          const parent = node.parent.parent | 
|  | 92 | +          const calleeObject = utils.unwrapTypes(parent.callee.object) | 
|  | 93 | + | 
|  | 94 | +          if (calleeObject.type === 'Identifier' && | 
|  | 95 | +              calleeObject.name === 'Vue' && | 
|  | 96 | +              parent.arguments && | 
|  | 97 | +              parent.arguments.length === 2 | 
|  | 98 | +          ) { | 
|  | 99 | +            const argument = parent.arguments[0] | 
|  | 100 | + | 
|  | 101 | +            if (canVerify(argument)) { | 
|  | 102 | +              reportIfInvalid(argument) | 
|  | 103 | +            } | 
|  | 104 | +          } | 
|  | 105 | +        } | 
|  | 106 | +      }, | 
|  | 107 | +      utils.executeOnVue(context, (obj) => { | 
|  | 108 | +        // Report if a component has been registered locally with a reserved name. | 
|  | 109 | +        utils.getRegisteredComponents(obj) | 
|  | 110 | +          .filter(({ name }) => RESERVED_NAMES.has(name)) | 
|  | 111 | +          .forEach(({ node, name }) => report(node, name)) | 
|  | 112 | + | 
|  | 113 | +        const node = obj.properties | 
|  | 114 | +          .find(item => ( | 
|  | 115 | +            item.type === 'Property' && | 
|  | 116 | +            item.key.name === 'name' && | 
|  | 117 | +            canVerify(item.value) | 
|  | 118 | +          )) | 
|  | 119 | + | 
|  | 120 | +        if (!node) return | 
|  | 121 | +        reportIfInvalid(node.value) | 
|  | 122 | +      }) | 
|  | 123 | +    ) | 
|  | 124 | +  } | 
|  | 125 | +} | 
0 commit comments