From 1cf4975d2f6fc58d148561c1e682eb8667bf1c55 Mon Sep 17 00:00:00 2001 From: James Russo Date: Thu, 22 Oct 2026 21:31:43 +0000 Subject: [PATCH] fix(parsers): preserve Unicode offsets when stamping IDs (#6079) --- packages/parsers/src/hfIds.test.ts | 8 ++++++++ packages/parsers/src/hfIds.ts | 18 +++++------------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/parsers/src/hfIds.test.ts b/packages/parsers/src/hfIds.test.ts index 5f120f49d5..662aec881f 100644 --- a/packages/parsers/src/hfIds.test.ts +++ b/packages/parsers/src/hfIds.test.ts @@ -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 = `
${"İ".repeat(20)}<${tag}>/* İ */

after

`; + 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 = ''; diff --git a/packages/parsers/src/hfIds.ts b/packages/parsers/src/hfIds.ts index 6aa2f30645..52e23db83d 100644 --- a/packages/parsers/src/hfIds.ts +++ b/packages/parsers/src/hfIds.ts @@ -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 = `= 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(`]|$)`, "gi"); + endTag.lastIndex = from; + return endTag.exec(html)?.index ?? -1; } function nextTagAfter(html: string, lt: number): number {