From 038da916c3fb7075f478b2bd37228227dfffb115 Mon Sep 17 00:00:00 2001 From: chouchouji <1305974212@qq.com> Date: Fri, 14 Feb 2025 22:33:20 +0800 Subject: [PATCH 1/6] feat(no-multiple-template-root): support disallowComments property --- lib/rules/no-multiple-template-root.js | 45 +++++++++++- tests/lib/rules/no-multiple-template-root.js | 76 ++++++++++++++++++++ 2 files changed, 120 insertions(+), 1 deletion(-) diff --git a/lib/rules/no-multiple-template-root.js b/lib/rules/no-multiple-template-root.js index 45c22389f..7d89c696f 100644 --- a/lib/rules/no-multiple-template-root.js +++ b/lib/rules/no-multiple-template-root.js @@ -15,8 +15,19 @@ module.exports = { url: 'https://eslint.vuejs.org/rules/no-multiple-template-root.html' }, fixable: null, - schema: [], + schema: [ + { + type: 'object', + properties: { + disallowComments: { + type: 'boolean' + } + }, + additionalProperties: false + } + ], messages: { + commentRoot: 'The template root disallows comments.', multipleRoot: 'The template root requires exactly one element.', textRoot: 'The template root requires an element rather than texts.', disallowedElement: "The template root disallows '<{{name}}>' elements.", @@ -28,6 +39,8 @@ module.exports = { * @returns {RuleListener} AST event handlers. */ create(context) { + const options = context.options[0] || {} + const disallowComments = options.disallowComments const sourceCode = context.getSourceCode() return { @@ -37,6 +50,36 @@ module.exports = { return } + const commentRangesMap = new Map() + const comments = element.comments + if (disallowComments && comments.length > 0) { + for (const comment of comments) { + const [start, end] = comment.range + commentRangesMap.set(`${start}-${end}`, comment) + } + + for (const child of element.children) { + if (child.type === 'VElement') { + for (const range of commentRangesMap.keys()) { + const ranges = range.split('-') + if (ranges[0] > child.range[0] && ranges[1] < child.range[1]) { + commentRangesMap.delete(range) + } + } + } + } + + if (commentRangesMap.size > 0) { + for (const node of commentRangesMap.values()) { + context.report({ + node, + loc: node.loc, + messageId: 'commentRoot' + }) + } + } + } + const rootElements = [] let extraText = null let extraElement = null diff --git a/tests/lib/rules/no-multiple-template-root.js b/tests/lib/rules/no-multiple-template-root.js index 7ab80af7e..c452ebe45 100644 --- a/tests/lib/rules/no-multiple-template-root.js +++ b/tests/lib/rules/no-multiple-template-root.js @@ -62,6 +62,30 @@ ruleTester.run('no-multiple-template-root', rule, { ` + }, + { + filename: 'test.vue', + code: ` + + `, + options: [{ disallowComments: false }] + }, + { + filename: 'test.vue', + code: ` + + `, + options: [{ disallowComments: false }] } ], invalid: [ @@ -104,6 +128,58 @@ ruleTester.run('no-multiple-template-root', rule, { filename: 'test.vue', code: '', errors: ["The template root disallows ' `, options: [{ disallowComments: false }] + }, + { + filename: 'test.vue', + code: ` + + `, + options: [{ disallowComments: true }] + }, + { + filename: 'test.vue', + code: ` + + `, + options: [{ disallowComments: true }] } ], invalid: [ From f802a5fc4fa40a27f7fc321a696179e20337fd13 Mon Sep 17 00:00:00 2001 From: chouchouji <1305974212@qq.com> Date: Sat, 15 Feb 2025 15:58:35 +0800 Subject: [PATCH 3/6] refactor(no-multiple-template-root): optimize code --- lib/rules/no-multiple-template-root.js | 95 +++++++++++--------- tests/lib/rules/no-multiple-template-root.js | 54 +++++++++++ 2 files changed, 107 insertions(+), 42 deletions(-) diff --git a/lib/rules/no-multiple-template-root.js b/lib/rules/no-multiple-template-root.js index e25f0b94f..1d728b1f5 100644 --- a/lib/rules/no-multiple-template-root.js +++ b/lib/rules/no-multiple-template-root.js @@ -6,6 +6,58 @@ const utils = require('../utils') +/** + * Get all comments that need to be reported + * @param {(HTMLComment | HTMLBogusComment | Comment)[]} comments + * @param {VRootElement} element + * @returns {(HTMLComment | HTMLBogusComment | Comment)[]} + */ +function getReportComments(comments, element) { + const commentRanges = comments.map((comment) => comment.range) + const elementRanges = element.children + .filter((child) => child.type === 'VElement') + .map((child) => child.range) + + // should return comment directly when no any elements + if (elementRanges.length === 0) { + return comments + } + + let commentIndex = 0 + let elementIndex = 0 + + const needReportComments = [] + while (commentIndex < commentRanges.length) { + const [commentStart, commentEnd] = commentRanges[commentIndex] + const [elementStart, elementEnd] = elementRanges[elementIndex] + // if the comment is in the range of element, should skip + if (commentStart > elementStart && commentEnd < elementEnd) { + commentIndex += 1 + continue + } + + if (commentEnd < elementStart) { + needReportComments.push(comments[commentIndex]) + commentIndex += 1 + } + + // the element array has no any element, but comment still has some elements + if ( + elementIndex === elementRanges.length - 1 && + commentStart > elementEnd + ) { + needReportComments.push(comments[commentIndex]) + commentIndex += 1 + } + + if (elementIndex < elementRanges.length - 1 && commentStart > elementEnd) { + elementIndex += 1 + } + } + + return needReportComments +} + module.exports = { meta: { type: 'problem', @@ -52,48 +104,7 @@ module.exports = { const comments = element.comments if (disallowComments && comments.length > 0) { - const commentRanges = comments.map((comment) => comment.range) - const elementRanges = element.children - .filter((child) => child.type === 'VElement') - .map((child) => child.range) - - let commentIndex = 0 - let elementIndex = 0 - - const needReportComments = elementRanges.length === 0 ? comments : [] - while ( - commentIndex < commentRanges.length && - elementRanges.length > 0 - ) { - const [commentStart, commentEnd] = commentRanges[commentIndex] - const [elementStart, elementEnd] = elementRanges[elementIndex] - if (commentStart > elementStart && commentEnd < elementEnd) { - commentIndex += 1 - continue - } - - if (commentEnd < elementStart) { - needReportComments.push(comments[commentIndex]) - commentIndex += 1 - } - - // the element array has no any element, but comment still has some elements - if ( - elementIndex === elementRanges.length - 1 && - commentStart > elementEnd - ) { - needReportComments.push(comments[commentIndex]) - commentIndex += 1 - } - - if ( - elementIndex < elementRanges.length - 1 && - commentStart > elementEnd - ) { - elementIndex += 1 - } - } - + const needReportComments = getReportComments(comments, element) if (needReportComments.length > 0) { for (const comment of needReportComments) { context.report({ diff --git a/tests/lib/rules/no-multiple-template-root.js b/tests/lib/rules/no-multiple-template-root.js index 517fdeccd..0bc425e93 100644 --- a/tests/lib/rules/no-multiple-template-root.js +++ b/tests/lib/rules/no-multiple-template-root.js @@ -203,6 +203,60 @@ ruleTester.run('no-multiple-template-root', rule, { } ] }, + { + code: ` + + `, + options: [{ disallowComments: true }], + errors: [ + { + message: 'The template root disallows comments.', + line: 3 + }, + { + message: 'The template root disallows comments.', + line: 12 + }, + { + message: 'The template root disallows comments.', + line: 17 + } + ] + }, + { + code: ` + + `, + options: [{ disallowComments: true }], + errors: [ + { + message: 'The template root disallows comments.', + line: 7 + } + ] + }, { code: `