component-definition-name-casing.js 2.9 KB

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