no-duplicate-attributes.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. /**
  2. * @author Toru Nagashima
  3. * @copyright 2017 Toru Nagashima. All rights reserved.
  4. * See LICENSE file in root directory for full license.
  5. */
  6. 'use strict'
  7. // ------------------------------------------------------------------------------
  8. // Requirements
  9. // ------------------------------------------------------------------------------
  10. const utils = require('../utils')
  11. // ------------------------------------------------------------------------------
  12. // Helpers
  13. // ------------------------------------------------------------------------------
  14. /**
  15. * Get the name of the given attribute node.
  16. * @param {ASTNode} attribute The attribute node to get.
  17. * @returns {string} The name of the attribute.
  18. */
  19. function getName (attribute) {
  20. if (!attribute.directive) {
  21. return attribute.key.name
  22. }
  23. if (attribute.key.name === 'bind') {
  24. return attribute.key.argument || null
  25. }
  26. return null
  27. }
  28. // ------------------------------------------------------------------------------
  29. // Rule Definition
  30. // ------------------------------------------------------------------------------
  31. module.exports = {
  32. meta: {
  33. docs: {
  34. description: 'disallow duplication of attributes',
  35. category: 'essential',
  36. url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v4.7.1/docs/rules/no-duplicate-attributes.md'
  37. },
  38. fixable: null,
  39. schema: [
  40. {
  41. type: 'object',
  42. properties: {
  43. allowCoexistClass: {
  44. type: 'boolean'
  45. },
  46. allowCoexistStyle: {
  47. type: 'boolean'
  48. }
  49. }
  50. }
  51. ]
  52. },
  53. create (context) {
  54. const options = context.options[0] || {}
  55. const allowCoexistStyle = options.allowCoexistStyle !== false
  56. const allowCoexistClass = options.allowCoexistClass !== false
  57. const directiveNames = new Set()
  58. const attributeNames = new Set()
  59. function isDuplicate (name, isDirective) {
  60. if ((allowCoexistStyle && name === 'style') || (allowCoexistClass && name === 'class')) {
  61. return isDirective ? directiveNames.has(name) : attributeNames.has(name)
  62. }
  63. return directiveNames.has(name) || attributeNames.has(name)
  64. }
  65. return utils.defineTemplateBodyVisitor(context, {
  66. 'VStartTag' () {
  67. directiveNames.clear()
  68. attributeNames.clear()
  69. },
  70. 'VAttribute' (node) {
  71. const name = getName(node)
  72. if (name == null) {
  73. return
  74. }
  75. if (isDuplicate(name, node.directive)) {
  76. context.report({
  77. node,
  78. loc: node.loc,
  79. message: "Duplicate attribute '{{name}}'.",
  80. data: { name }
  81. })
  82. }
  83. if (node.directive) {
  84. directiveNames.add(name)
  85. } else {
  86. attributeNames.add(name)
  87. }
  88. }
  89. })
  90. }
  91. }