diff --git a/bun.lock b/bun.lock index 379cf361b2..c6eb53154d 100644 --- a/bun.lock +++ b/bun.lock @@ -169,6 +169,7 @@ "version": "0.7.45", "dependencies": { "@hyperframes/parsers": "workspace:*", + "htmlparser2": "^10.1.0", "linkedom": "^0.18.12", "postcss": "^8.5.8", }, diff --git a/packages/lint/package.json b/packages/lint/package.json index 10f2d52838..58b3456fa5 100644 --- a/packages/lint/package.json +++ b/packages/lint/package.json @@ -54,6 +54,7 @@ }, "dependencies": { "@hyperframes/parsers": "workspace:*", + "htmlparser2": "^10.1.0", "linkedom": "^0.18.12", "postcss": "^8.5.8" }, diff --git a/packages/lint/src/context.ts b/packages/lint/src/context.ts index 3e71b29a32..9638f3ac52 100644 --- a/packages/lint/src/context.ts +++ b/packages/lint/src/context.ts @@ -1,13 +1,10 @@ import type { HyperframeLintFinding, HyperframeLinterOptions } from "./types"; import { - extractBlocks, - extractOpenTags, + parseHtmlStructure, findRootTag, collectCompositionIds, readAttr, stripHtmlComments, - STYLE_BLOCK_PATTERN, - SCRIPT_BLOCK_PATTERN, } from "./utils"; import type { OpenTag, ExtractedBlock } from "./utils"; @@ -34,19 +31,31 @@ export function buildLintContext(html: string, options: HyperframeLinterOptions // hijack the boundary match below. Linear + fixpoint (see stripHtmlComments) to // stay ReDoS-free and catch markers that re-form when a comment is removed. let source = stripHtmlComments(rawSource); - const sourceWithoutTemplates = source.replace( - /]*>[\s\S]*?<\/template(?:\s[^>]*)?>/gi, - " ", + const initialStructure = parseHtmlStructure(source); + const templateTags = initialStructure.tags.filter( + (tag) => tag.name === "template" && tag.closeIndex != null, ); - const templateMatch = source.match(/]*>([\s\S]*)<\/template>/i); + let sourceWithoutTemplates = source; + for (const template of [...templateTags].reverse()) { + const end = template.endIndex ?? template.index; + sourceWithoutTemplates = + sourceWithoutTemplates.slice(0, template.index) + + " ".repeat(end - template.index) + + sourceWithoutTemplates.slice(end); + } // Some sub-composition files are HTML shells whose real root lives inside a //