html-comment-content-spacing.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /**
  2. * @author Yosuke ota
  3. * See LICENSE file in root directory for full license.
  4. */
  5. 'use strict'
  6. // -----------------------------------------------------------------------------
  7. // Requirements
  8. // -----------------------------------------------------------------------------
  9. const htmlComments = require('../utils/html-comments')
  10. /**
  11. * @typedef { import('../utils/html-comments').ParsedHTMLComment } ParsedHTMLComment
  12. */
  13. // ------------------------------------------------------------------------------
  14. // Rule Definition
  15. // ------------------------------------------------------------------------------
  16. module.exports = {
  17. meta: {
  18. type: 'layout',
  19. docs: {
  20. description: 'enforce unified spacing in HTML comments',
  21. categories: undefined,
  22. url: 'https://eslint.vuejs.org/rules/html-comment-content-spacing.html'
  23. },
  24. fixable: 'whitespace',
  25. schema: [
  26. {
  27. enum: ['always', 'never']
  28. },
  29. {
  30. type: 'object',
  31. properties: {
  32. exceptions: {
  33. type: 'array',
  34. items: {
  35. type: 'string'
  36. }
  37. }
  38. },
  39. additionalProperties: false
  40. }
  41. ],
  42. messages: {
  43. expectedAfterHTMLCommentOpen: "Expected space after '<!--'.",
  44. expectedBeforeHTMLCommentOpen: "Expected space before '-->'.",
  45. expectedAfterExceptionBlock: 'Expected space after exception block.',
  46. expectedBeforeExceptionBlock: 'Expected space before exception block.',
  47. unexpectedAfterHTMLCommentOpen: "Unexpected space after '<!--'.",
  48. unexpectedBeforeHTMLCommentOpen: "Unexpected space before '-->'."
  49. }
  50. },
  51. /** @param {RuleContext} context */
  52. create(context) {
  53. // Unless the first option is never, require a space
  54. const requireSpace = context.options[0] !== 'never'
  55. return htmlComments.defineVisitor(
  56. context,
  57. context.options[1],
  58. (comment) => {
  59. checkCommentOpen(comment)
  60. checkCommentClose(comment)
  61. },
  62. { includeDirectives: true }
  63. )
  64. /**
  65. * Reports the space before the contents of a given comment if it's invalid.
  66. * @param {ParsedHTMLComment} comment - comment data.
  67. * @returns {void}
  68. */
  69. function checkCommentOpen(comment) {
  70. const { value, openDecoration, open } = comment
  71. if (!value) {
  72. return
  73. }
  74. const beforeToken = openDecoration || open
  75. if (beforeToken.loc.end.line !== value.loc.start.line) {
  76. // Ignore newline
  77. return
  78. }
  79. if (requireSpace) {
  80. if (beforeToken.range[1] < value.range[0]) {
  81. // Is valid
  82. return
  83. }
  84. context.report({
  85. loc: {
  86. start: beforeToken.loc.end,
  87. end: value.loc.start
  88. },
  89. messageId: openDecoration
  90. ? 'expectedAfterExceptionBlock'
  91. : 'expectedAfterHTMLCommentOpen',
  92. fix: openDecoration
  93. ? undefined
  94. : (fixer) => fixer.insertTextAfter(beforeToken, ' ')
  95. })
  96. } else {
  97. if (openDecoration) {
  98. // Ignore expection block
  99. return
  100. }
  101. if (beforeToken.range[1] === value.range[0]) {
  102. // Is valid
  103. return
  104. }
  105. context.report({
  106. loc: {
  107. start: beforeToken.loc.end,
  108. end: value.loc.start
  109. },
  110. messageId: 'unexpectedAfterHTMLCommentOpen',
  111. fix: (fixer) =>
  112. fixer.removeRange([beforeToken.range[1], value.range[0]])
  113. })
  114. }
  115. }
  116. /**
  117. * Reports the space after the contents of a given comment if it's invalid.
  118. * @param {ParsedHTMLComment} comment - comment data.
  119. * @returns {void}
  120. */
  121. function checkCommentClose(comment) {
  122. const { value, closeDecoration, close } = comment
  123. if (!value) {
  124. return
  125. }
  126. const afterToken = closeDecoration || close
  127. if (value.loc.end.line !== afterToken.loc.start.line) {
  128. // Ignore newline
  129. return
  130. }
  131. if (requireSpace) {
  132. if (value.range[1] < afterToken.range[0]) {
  133. // Is valid
  134. return
  135. }
  136. context.report({
  137. loc: {
  138. start: value.loc.end,
  139. end: afterToken.loc.start
  140. },
  141. messageId: closeDecoration
  142. ? 'expectedBeforeExceptionBlock'
  143. : 'expectedBeforeHTMLCommentOpen',
  144. fix: closeDecoration
  145. ? undefined
  146. : (fixer) => fixer.insertTextBefore(afterToken, ' ')
  147. })
  148. } else {
  149. if (closeDecoration) {
  150. // Ignore expection block
  151. return
  152. }
  153. if (value.range[1] === afterToken.range[0]) {
  154. // Is valid
  155. return
  156. }
  157. context.report({
  158. loc: {
  159. start: value.loc.end,
  160. end: afterToken.loc.start
  161. },
  162. messageId: 'unexpectedBeforeHTMLCommentOpen',
  163. fix: (fixer) =>
  164. fixer.removeRange([value.range[1], afterToken.range[0]])
  165. })
  166. }
  167. }
  168. }
  169. }