no-multiple-template-root.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. /**
  2. * @fileoverview disallow adding multiple root nodes to the template
  3. * @author Przemyslaw Falowski (@przemkow)
  4. */
  5. 'use strict'
  6. const utils = require('../utils')
  7. module.exports = {
  8. meta: {
  9. type: 'problem',
  10. docs: {
  11. description: 'disallow adding multiple root nodes to the template',
  12. categories: ['vue2-essential'],
  13. url: 'https://eslint.vuejs.org/rules/no-multiple-template-root.html'
  14. },
  15. fixable: null,
  16. schema: [],
  17. messages: {
  18. multipleRoot: 'The template root requires exactly one element.',
  19. textRoot: 'The template root requires an element rather than texts.',
  20. disallowedElement: "The template root disallows '<{{name}}>' elements.",
  21. disallowedDirective: "The template root disallows 'v-for' directives."
  22. }
  23. },
  24. /**
  25. * @param {RuleContext} context - The rule context.
  26. * @returns {RuleListener} AST event handlers.
  27. */
  28. create(context) {
  29. const sourceCode = context.getSourceCode()
  30. return {
  31. Program(program) {
  32. const element = program.templateBody
  33. if (element == null) {
  34. return
  35. }
  36. const rootElements = []
  37. let extraText = null
  38. let extraElement = null
  39. let vIf = false
  40. for (const child of element.children) {
  41. if (child.type === 'VElement') {
  42. if (rootElements.length === 0) {
  43. rootElements.push(child)
  44. vIf = utils.hasDirective(child, 'if')
  45. } else if (vIf && utils.hasDirective(child, 'else-if')) {
  46. rootElements.push(child)
  47. } else if (vIf && utils.hasDirective(child, 'else')) {
  48. rootElements.push(child)
  49. vIf = false
  50. } else {
  51. extraElement = child
  52. }
  53. } else if (sourceCode.getText(child).trim() !== '') {
  54. extraText = child
  55. }
  56. }
  57. if (extraText != null) {
  58. context.report({
  59. node: extraText,
  60. loc: extraText.loc,
  61. messageId: 'textRoot'
  62. })
  63. } else if (extraElement == null) {
  64. for (const element of rootElements) {
  65. const tag = element.startTag
  66. const name = element.name
  67. if (name === 'template' || name === 'slot') {
  68. context.report({
  69. node: tag,
  70. loc: tag.loc,
  71. messageId: 'disallowedElement',
  72. data: { name }
  73. })
  74. }
  75. if (utils.hasDirective(element, 'for')) {
  76. context.report({
  77. node: tag,
  78. loc: tag.loc,
  79. messageId: 'disallowedDirective'
  80. })
  81. }
  82. }
  83. } else {
  84. context.report({
  85. node: extraElement,
  86. loc: extraElement.loc,
  87. messageId: 'multipleRoot'
  88. })
  89. }
  90. }
  91. }
  92. }
  93. }