attribute-hyphenation.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. /**
  2. * @fileoverview Define a style for the props casing in templates.
  3. * @author Armano
  4. */
  5. 'use strict'
  6. const utils = require('../utils')
  7. const casing = require('../utils/casing')
  8. // ------------------------------------------------------------------------------
  9. // Rule Definition
  10. // ------------------------------------------------------------------------------
  11. module.exports = {
  12. meta: {
  13. docs: {
  14. description: 'enforce attribute naming style on custom components in template',
  15. category: 'strongly-recommended',
  16. url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v4.7.1/docs/rules/attribute-hyphenation.md'
  17. },
  18. fixable: 'code',
  19. schema: [
  20. {
  21. enum: ['always', 'never']
  22. },
  23. {
  24. type: 'object',
  25. properties: {
  26. 'ignore': {
  27. type: 'array',
  28. items: {
  29. allOf: [
  30. { type: 'string' },
  31. { not: { type: 'string', pattern: ':exit$' }},
  32. { not: { type: 'string', pattern: '^\\s*$' }}
  33. ]
  34. },
  35. uniqueItems: true,
  36. additionalItems: false
  37. }
  38. },
  39. additionalProperties: false
  40. }
  41. ]
  42. },
  43. create (context) {
  44. const sourceCode = context.getSourceCode()
  45. const option = context.options[0]
  46. const optionsPayload = context.options[1]
  47. const useHyphenated = option !== 'never'
  48. let ignoredAttributes = ['data-', 'aria-', 'slot-scope']
  49. if (optionsPayload && optionsPayload.ignore) {
  50. ignoredAttributes = ignoredAttributes.concat(optionsPayload.ignore)
  51. }
  52. const caseConverter = casing.getConverter(useHyphenated ? 'kebab-case' : 'camelCase')
  53. function reportIssue (node, name) {
  54. const text = sourceCode.getText(node.key)
  55. context.report({
  56. node: node.key,
  57. loc: node.loc,
  58. message: useHyphenated ? "Attribute '{{text}}' must be hyphenated." : "Attribute '{{text}}' can't be hyphenated.",
  59. data: {
  60. text
  61. },
  62. fix: fixer => fixer.replaceText(node.key, text.replace(name, caseConverter(name)))
  63. })
  64. }
  65. function isIgnoredAttribute (value) {
  66. const isIgnored = ignoredAttributes.some(function (attr) {
  67. return value.indexOf(attr) !== -1
  68. })
  69. if (isIgnored) {
  70. return true
  71. }
  72. return useHyphenated ? value.toLowerCase() === value : !/-/.test(value)
  73. }
  74. // ----------------------------------------------------------------------
  75. // Public
  76. // ----------------------------------------------------------------------
  77. return utils.defineTemplateBodyVisitor(context, {
  78. VAttribute (node) {
  79. if (!utils.isCustomComponent(node.parent.parent)) return
  80. const name = !node.directive ? node.key.rawName : node.key.name === 'bind' ? node.key.raw.argument : false
  81. if (!name || isIgnoredAttribute(name)) return
  82. reportIssue(node, name)
  83. }
  84. })
  85. }
  86. }