From fae50bd72c817f53546e4d07623c612c09a0094e Mon Sep 17 00:00:00 2001 From: Yang Jun Date: Sun, 10 May 2026 15:23:01 +0800 Subject: [PATCH] refactor(strip_html): inline block kinds to match file style Drop the module-level STRIP_BLOCKS table; the rest of the file keeps each filter self-contained (only escapeMap/unescapeMap are top-level maps shared across filters). Two openers don't justify a table. Co-authored-by: Cursor --- src/filters/html.ts | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/filters/html.ts b/src/filters/html.ts index 90a83f096..c44ef92da 100644 --- a/src/filters/html.ts +++ b/src/filters/html.ts @@ -42,32 +42,28 @@ export function newline_to_br (this: FilterImpl, v: string) { return str.replace(/\r?\n/gm, '
\n') } -// Linear-time replacement for the previous backtracking regex +// Linear-time strip via indexOf scan. A regex equivalent to the old // /||<[\s\S]*?>|/g -// which is O(n^2) in V8 on inputs with many unclosed openers. JS regex has no atomic -// groups / possessive quantifiers, so unrolled-loop patterns don't help asymptotically. -// We scan forward with indexOf and cache the next known closer per kind so unclosed -// openers don't re-scan the tail. Each character is visited O(1) times. -const STRIP_BLOCKS = [[''], ['']] as const - +// is O(n^2) on unclosed openers (JS regex has no atomic groups / memoization), +// so we cache "next known / position" across openers. export function strip_html (this: FilterImpl, v: string) { const str = stringify(v) this.context.memoryLimit.use(str.length) - const closers = [0, 0] let out = '' let i = 0 + let scriptEnd = 0 + let styleEnd = 0 while (i < str.length) { const lt = str.indexOf('<', i) if (lt < 0) return out + str.slice(i) out += str.slice(i, lt) let end = -1 - for (let k = 0; k < STRIP_BLOCKS.length; k++) { - const [opener, closer] = STRIP_BLOCKS[k] - if (!str.startsWith(opener, lt)) continue - const from = lt + opener.length - if (closers[k] !== -1 && closers[k] < from) closers[k] = str.indexOf(closer, from) - if (closers[k] >= 0) end = closers[k] + closer.length - break + if (str.startsWith('= 0 && scriptEnd < lt + 7) scriptEnd = str.indexOf('', lt + 7) + if (scriptEnd >= 0) end = scriptEnd + 9 + } else if (str.startsWith('= 0 && styleEnd < lt + 6) styleEnd = str.indexOf('', lt + 6) + if (styleEnd >= 0) end = styleEnd + 8 } if (end < 0) end = str.indexOf('>', lt + 1) + 1 if (end <= 0) return out + str.slice(lt)