component-definition-name-casing.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. /**
  2. * @fileoverview enforce specific casing for component definition name
  3. * @author Armano
  4. */
  5. 'use strict'
  6. const utils = require('../utils')
  7. const casing = require('../utils/casing')
  8. const allowedCaseOptions = ['PascalCase', 'kebab-case']
  9. /**
  10. * @param {Expression | SpreadElement} node
  11. * @returns {node is (Literal | TemplateLiteral)}
  12. */
  13. function canConvert(node) {
  14. return (
  15. node.type === 'Literal' ||
  16. (node.type === 'TemplateLiteral' &&
  17. node.expressions.length === 0 &&
  18. node.quasis.length === 1)
  19. )
  20. }
  21. module.exports = {
  22. meta: {
  23. type: 'suggestion',
  24. docs: {
  25. description: 'enforce specific casing for component definition name',
  26. categories: ['vue3-strongly-recommended', 'strongly-recommended'],
  27. url: 'https://eslint.vuejs.org/rules/component-definition-name-casing.html'
  28. },
  29. fixable: 'code', // or "code" or "whitespace"
  30. schema: [
  31. {
  32. enum: allowedCaseOptions
  33. }
  34. ]
  35. },
  36. /** @param {RuleContext} context */
  37. create(context) {
  38. const options = context.options[0]
  39. const caseType = allowedCaseOptions.includes(options)
  40. ? options
  41. : 'PascalCase'
  42. /**
  43. * @param {Literal | TemplateLiteral} node
  44. */
  45. function convertName(node) {
  46. /** @type {string} */
  47. let nodeValue
  48. /** @type {Range} */
  49. let range
  50. if (node.type === 'TemplateLiteral') {
  51. const quasis = node.quasis[0]
  52. nodeValue = quasis.value.cooked
  53. range = quasis.range
  54. } else {
  55. nodeValue = `${node.value}`
  56. range = node.range
  57. }
  58. if (!casing.getChecker(caseType)(nodeValue)) {
  59. context.report({
  60. node,
  61. message: 'Property name "{{value}}" is not {{caseType}}.',
  62. data: {
  63. value: nodeValue,
  64. caseType
  65. },
  66. fix: (fixer) =>
  67. fixer.replaceTextRange(
  68. [range[0] + 1, range[1] - 1],
  69. casing.getExactConverter(caseType)(nodeValue)
  70. )
  71. })
  72. }
  73. }
  74. return Object.assign(
  75. {},
  76. utils.executeOnCallVueComponent(context, (node) => {
  77. if (node.arguments.length === 2) {
  78. const argument = node.arguments[0]
  79. if (canConvert(argument)) {
  80. convertName(argument)
  81. }
  82. }
  83. }),
  84. utils.executeOnVue(context, (obj) => {
  85. const node = utils.findProperty(obj, 'name')
  86. if (!node) return
  87. if (!canConvert(node.value)) return
  88. convertName(node.value)
  89. })
  90. )
  91. }
  92. }