attributes-order.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. /**
  2. * @fileoverview enforce ordering of attributes
  3. * @author Erin Depew
  4. */
  5. 'use strict'
  6. const utils = require('../utils')
  7. // ------------------------------------------------------------------------------
  8. // Rule Definition
  9. // ------------------------------------------------------------------------------
  10. function getAttributeType (name, isDirective) {
  11. if (isDirective) {
  12. if (name === 'for') {
  13. return 'LIST_RENDERING'
  14. } else if (name === 'if' || name === 'else-if' || name === 'else' || name === 'show' || name === 'cloak') {
  15. return 'CONDITIONALS'
  16. } else if (name === 'pre' || name === 'once') {
  17. return 'RENDER_MODIFIERS'
  18. } else if (name === 'model') {
  19. return 'TWO_WAY_BINDING'
  20. } else if (name === 'on') {
  21. return 'EVENTS'
  22. } else if (name === 'html' || name === 'text') {
  23. return 'CONTENT'
  24. } else {
  25. return 'OTHER_DIRECTIVES'
  26. }
  27. } else {
  28. if (name === 'is') {
  29. return 'DEFINITION'
  30. } else if (name === 'id') {
  31. return 'GLOBAL'
  32. } else if (name === 'ref' || name === 'key' || name === 'slot' || name === 'slot-scope') {
  33. return 'UNIQUE'
  34. } else {
  35. return 'OTHER_ATTR'
  36. }
  37. }
  38. }
  39. function getPosition (attribute, attributePosition) {
  40. const attributeType = attribute.directive && attribute.key.name === 'bind'
  41. ? getAttributeType(attribute.key.argument, false)
  42. : getAttributeType(attribute.key.name, attribute.directive)
  43. return attributePosition.hasOwnProperty(attributeType) ? attributePosition[attributeType] : -1
  44. }
  45. function create (context) {
  46. const sourceCode = context.getSourceCode()
  47. let attributeOrder = ['DEFINITION', 'LIST_RENDERING', 'CONDITIONALS', 'RENDER_MODIFIERS', 'GLOBAL', 'UNIQUE', 'TWO_WAY_BINDING', 'OTHER_DIRECTIVES', 'OTHER_ATTR', 'EVENTS', 'CONTENT']
  48. if (context.options[0] && context.options[0].order) {
  49. attributeOrder = context.options[0].order
  50. }
  51. const attributePosition = {}
  52. attributeOrder.forEach((item, i) => {
  53. if (item instanceof Array) {
  54. item.forEach((attr) => {
  55. attributePosition[attr] = i
  56. })
  57. } else attributePosition[item] = i
  58. })
  59. let currentPosition
  60. let previousNode
  61. function reportIssue (node, previousNode) {
  62. const currentNode = sourceCode.getText(node.key)
  63. const prevNode = sourceCode.getText(previousNode.key)
  64. context.report({
  65. node: node.key,
  66. loc: node.loc,
  67. message: `Attribute "${currentNode}" should go before "${prevNode}".`,
  68. data: {
  69. currentNode
  70. },
  71. fix (fixer) {
  72. const attributes = node.parent.attributes
  73. const shiftAttrs = attributes.slice(attributes.indexOf(previousNode), attributes.indexOf(node) + 1)
  74. return shiftAttrs.map((attr, i) => {
  75. const text = attr === previousNode ? sourceCode.getText(node) : sourceCode.getText(shiftAttrs[i - 1])
  76. return fixer.replaceText(attr, text)
  77. })
  78. }
  79. })
  80. }
  81. return utils.defineTemplateBodyVisitor(context, {
  82. 'VStartTag' () {
  83. currentPosition = -1
  84. previousNode = null
  85. },
  86. 'VAttribute' (node) {
  87. if ((currentPosition === -1) || (currentPosition <= getPosition(node, attributePosition))) {
  88. currentPosition = getPosition(node, attributePosition)
  89. previousNode = node
  90. } else {
  91. reportIssue(node, previousNode)
  92. }
  93. }
  94. })
  95. }
  96. module.exports = {
  97. meta: {
  98. type: 'suggestion',
  99. docs: {
  100. description: 'enforce order of attributes',
  101. category: 'recommended',
  102. url: 'https://eslint.vuejs.org/rules/attributes-order.html'
  103. },
  104. fixable: 'code',
  105. schema: {
  106. type: 'array',
  107. properties: {
  108. order: {
  109. items: {
  110. type: 'string'
  111. },
  112. maxItems: 10,
  113. minItems: 10
  114. }
  115. }
  116. }
  117. },
  118. create
  119. }