mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 09:16:55 +00:00
fix(lint): locate findings in linear time on large compositions (#4373)
Since the source-location mapping landed, building the whitespace-collapsed copy of a file grew a string with += and called endsWith on it per character. V8 flattens that string on every call, so a 1.2 MB index.html cost ~31 s of CPU in every lint run, including the one preview runs before it listens.
This commit is contained in:
@@ -201,3 +201,12 @@ it("uses classic-script Acorn semantics for new.target and hashbang", async () =
|
||||
const hashbang = await lintHyperframeHtml("<script>#!/usr/bin/env node\nconst a=1;</script>");
|
||||
expect(hashbang.findings.some((f) => f.code === "invalid_inline_script_syntax")).toBe(false);
|
||||
});
|
||||
|
||||
it("locates a snippet at the end of a large source in linear time", () => {
|
||||
const filler = "<div>\n <p>a b</p>\n</div>\n".repeat(12_000);
|
||||
const html = `${filler}<style>\n .late {\n position: fixed;\n }\n</style>`;
|
||||
const start = performance.now();
|
||||
const location = sourceLocationFor(html, finding({ snippet: ".late { position: fixed; }" }));
|
||||
expect(location).toEqual({ line: 36_002, column: 3 });
|
||||
expect(performance.now() - start).toBeLessThan(1_000);
|
||||
});
|
||||
|
||||
@@ -5,17 +5,18 @@ import { parseHtmlStructure, readDecodedAttr } from "./utils";
|
||||
type Location = Pick<HyperframeLintFinding, "line" | "column">;
|
||||
|
||||
function normalized(source: string): { text: string; offsets: number[] } {
|
||||
let text = "";
|
||||
// An array, not +=: V8 flattens a += string on every endsWith, which made this quadratic.
|
||||
const chars: string[] = [];
|
||||
const offsets: number[] = [];
|
||||
let lastWasSpace = false;
|
||||
for (let i = 0; i < source.length; i++) {
|
||||
const char = source[i]!;
|
||||
if (/\s/.test(char)) {
|
||||
if (text.endsWith(" ")) continue;
|
||||
text += " ";
|
||||
} else text += char;
|
||||
const isSpace = /\s/.test(source[i]!);
|
||||
if (isSpace && lastWasSpace) continue;
|
||||
chars.push(isSpace ? " " : source[i]!);
|
||||
offsets.push(i);
|
||||
lastWasSpace = isSpace;
|
||||
}
|
||||
return { text, offsets };
|
||||
return { text: chars.join(""), offsets };
|
||||
}
|
||||
|
||||
/** Resolve against original bytes, not the comment-stripped/template-unwrapped rule input.
|
||||
|
||||
Reference in New Issue
Block a user