component-definition-name-casing.js 2.9 KB

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