fix(studio): avoid inline style regex backtracking (#3708)

This commit is contained in:
James Russo
2026-09-05 12:58:26 -04:00
committed by GitHub
parent eec04340b3
commit c564daf210
2 changed files with 19 additions and 2 deletions
@@ -66,6 +66,23 @@ describe("applyPatchByTarget", () => {
expect(result).not.toContain("/ style");
});
it.each(["", " ", "\t\r\n", "\u00a0\u2028", " ".repeat(100_000)])(
"preserves self-closing whitespace handling (case %#)",
(whitespace) => {
const prefix = '<img id="hero" class="hero"';
const op: PatchOperation = { type: "inline-style", property: "opacity", value: "0.5" };
for (const suffix of ["/", "/\n", "/\r", "/\r\n", "/\u2028", "", "x", "/ "]) {
const tag = prefix + whitespace + suffix;
const selfClosing = suffix === "/";
// The original operation removes whitespace before the slash only.
const expectedBase = selfClosing ? prefix + suffix.slice(1) : tag;
const expected = `${expectedBase} style="opacity: 0.5"${selfClosing ? " /" : ""}>`;
expect(applyPatch(tag + ">", "hero", op)).toBe(expected);
expect(applyPatchByTarget(tag + ">", { selector: ".hero" }, op)).toBe(expected);
}
},
);
it("patches inline move styles by target", () => {
const html = `<div id="card" style="position: absolute; left: 108px; top: 112px"></div>`;
+2 -2
View File
@@ -212,8 +212,8 @@ function patchInlineStyleInTag(
} else {
// No existing style attribute
if (value === null) return html; // nothing to remove
const selfClosing = /\s*\/$/.test(tag);
const base = selfClosing ? tag.replace(/\s*\/$/, "") : tag;
const selfClosing = tag.endsWith("/");
const base = selfClosing ? tag.slice(0, -1).trimEnd() : tag;
const newTag = `${base} style="${prop}: ${escapeStyleAttributeValue(value, '"')}"${selfClosing ? " /" : ""}`;
return html.replace(tag, newTag);
}