fix(parsers): preserve Unicode offsets when stamping IDs (#6079)

This commit is contained in:
James Russo
2026-10-22 21:31:43 +00:00
committed by GitHub
parent 36c4a9fd80
commit 1cf4975d2f
2 changed files with 13 additions and 13 deletions
+8
View File
@@ -44,6 +44,14 @@ describe("ensureHfIds fragment detection", () => {
});
describe("stampHfIdsInPlace keeps the source's own formatting", () => {
it.each(["script", "style"])("keeps source offsets after expanding Unicode near %s", (tag) => {
const html = `<div DATA-X='KeepCase'>${"İ".repeat(20)}<${tag}>/* İ <span> */</${tag.toUpperCase()} \n><p class='KeepMe'>after</p></div>`;
const out = stampHfIdsInPlace(html);
expect(out.replace(/ data-hf-id="hf-[a-z0-9]{4}"/g, "")).toBe(html);
expect(ids(out)).toEqual(ids(ensureHfIds(html)));
expect(stampHfIdsInPlace(out)).toBe(out);
});
it("only inserts ids into a composition written without whitespace", () => {
const html =
'<template id="s-template"><div data-composition-id="s" data-start="0" data-duration="4"><style>#t{color:red}</style><img src="a.png"/><input disabled><p class=\'t\' id=t>Hi<br></p></div></template>';
+5 -13
View File
@@ -62,7 +62,6 @@ const IMPLIED = new Set(["html", "head", "body", "tbody"]);
type StartTag = { name: string; nameEnd: number; bracket: number };
function startTags(html: string): StartTag[] | null {
const lower = html.toLowerCase();
const tags: StartTag[] = [];
let i = html.indexOf("<");
while (i >= 0) {
@@ -77,23 +76,16 @@ function startTags(html: string): StartTag[] | null {
if (end < 0) return null;
const tag = name[1]!.toLowerCase();
tags.push({ name: tag, nameEnd: i + name[0].length, bracket: end });
const after = RAW_TEXT.has(tag) ? rawTextEnd(lower, tag, end) : end;
const after = RAW_TEXT.has(tag) ? rawTextEnd(html, tag, end) : end;
i = after < 0 ? -1 : html.indexOf("<", after + 1);
}
return tags;
}
function rawTextEnd(lower: string, tag: string, from: number): number {
const marker = `</${tag}`;
for (
let at = lower.indexOf(marker, from);
at >= 0;
at = lower.indexOf(marker, at + marker.length)
) {
const next = lower[at + marker.length];
if (next !== undefined && /[\s/>]/.test(next)) return at;
}
return -1;
function rawTextEnd(html: string, tag: string, from: number): number {
const endTag = new RegExp(`</${tag}(?=[\\s/>]|$)`, "gi");
endTag.lastIndex = from;
return endTag.exec(html)?.index ?? -1;
}
function nextTagAfter(html: string, lt: number): number {