new-line-between-multi-line-property.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. /**
  2. * @fileoverview Enforce new lines between multi-line properties in Vue components.
  3. * @author IWANABETHATGUY
  4. */
  5. 'use strict'
  6. const utils = require('../utils')
  7. /**
  8. * @param {Token} node
  9. */
  10. function isComma(node) {
  11. return node.type === 'Punctuator' && node.value === ','
  12. }
  13. /**
  14. * Check whether the between given nodes has empty line.
  15. * @param {SourceCode} sourceCode
  16. * @param {ASTNode} pre
  17. * @param {ASTNode} cur
  18. */
  19. function* iterateBetweenTokens(sourceCode, pre, cur) {
  20. yield sourceCode.getLastToken(pre)
  21. yield* sourceCode.getTokensBetween(pre, cur, {
  22. includeComments: true
  23. })
  24. yield sourceCode.getFirstToken(cur)
  25. }
  26. /**
  27. * Check whether the between given nodes has empty line.
  28. * @param {SourceCode} sourceCode
  29. * @param {ASTNode} pre
  30. * @param {ASTNode} cur
  31. */
  32. function hasEmptyLine(sourceCode, pre, cur) {
  33. /** @type {Token|null} */
  34. let preToken = null
  35. for (const token of iterateBetweenTokens(sourceCode, pre, cur)) {
  36. if (preToken && token.loc.start.line - preToken.loc.end.line >= 2) {
  37. return true
  38. }
  39. preToken = token
  40. }
  41. return false
  42. }
  43. module.exports = {
  44. meta: {
  45. type: 'layout',
  46. docs: {
  47. description:
  48. 'enforce new lines between multi-line properties in Vue components',
  49. categories: undefined,
  50. url: 'https://eslint.vuejs.org/rules/new-line-between-multi-line-property.html'
  51. },
  52. fixable: 'whitespace', // or "code" or "whitespace"
  53. schema: [
  54. {
  55. type: 'object',
  56. properties: {
  57. // number of line you want to insert after multi-line property
  58. minLineOfMultilineProperty: {
  59. type: 'number',
  60. minimum: 2
  61. }
  62. },
  63. additionalProperties: false
  64. }
  65. ]
  66. },
  67. /** @param {RuleContext} context */
  68. create(context) {
  69. let minLineOfMultilineProperty = 2
  70. if (
  71. context.options &&
  72. context.options[0] &&
  73. context.options[0].minLineOfMultilineProperty
  74. ) {
  75. minLineOfMultilineProperty = context.options[0].minLineOfMultilineProperty
  76. }
  77. /** @type {CallExpression[]} */
  78. const callStack = []
  79. const sourceCode = context.getSourceCode()
  80. return Object.assign(
  81. utils.defineVueVisitor(context, {
  82. CallExpression(node) {
  83. callStack.push(node)
  84. },
  85. 'CallExpression:exit'() {
  86. callStack.pop()
  87. },
  88. /**
  89. * @param {ObjectExpression} node
  90. */
  91. ObjectExpression(node) {
  92. if (callStack.length > 0) {
  93. return
  94. }
  95. const properties = node.properties
  96. for (let i = 1; i < properties.length; i++) {
  97. const cur = properties[i]
  98. const pre = properties[i - 1]
  99. const lineCountOfPreProperty =
  100. pre.loc.end.line - pre.loc.start.line + 1
  101. if (lineCountOfPreProperty < minLineOfMultilineProperty) {
  102. continue
  103. }
  104. if (hasEmptyLine(sourceCode, pre, cur)) {
  105. continue
  106. }
  107. context.report({
  108. node: pre,
  109. loc: {
  110. start: pre.loc.end,
  111. end: cur.loc.start
  112. },
  113. message:
  114. 'Enforce new lines between multi-line properties in Vue components.',
  115. fix(fixer) {
  116. /** @type {Token|null} */
  117. let preToken = null
  118. for (const token of iterateBetweenTokens(
  119. sourceCode,
  120. pre,
  121. cur
  122. )) {
  123. if (
  124. preToken &&
  125. preToken.loc.end.line < token.loc.start.line
  126. ) {
  127. return fixer.insertTextAfter(preToken, '\n')
  128. }
  129. preToken = token
  130. }
  131. const commaToken = sourceCode.getTokenAfter(pre, isComma)
  132. return fixer.insertTextAfter(commaToken || pre, '\n\n')
  133. }
  134. })
  135. }
  136. }
  137. })
  138. )
  139. }
  140. }