no-reserved-component-names.js 3.1 KB

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