mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
test(daemon): add Red regression for inlined-body tag-literal corruption
Reproduces the correctness bug Siri-Ray (looper) and codex-bot flagged on PR #1312: the reduce/split-join approach in inlineRelativeAssets re-scans the progressively mutated HTML, so a tag literal that happens to appear inside an already-inlined asset body gets the inner literal also replaced — corrupting the body and producing duplicate inlining. Concrete reproducer (CSS, where </style escape doesn't touch <link>): HTML: <link rel="stylesheet" href="a.css"> <link rel="stylesheet" href="b.css"> a.css: /* see also <link rel="stylesheet" href="b.css"> */ b.css: body{color:red} Under split/join the second pass splits on `<link rel="stylesheet" href="b.css">` and matches BOTH the real outer tag AND the literal inside a.css's comment. Result: b.css's <style> block is injected inside a.css's comment, and b.css gets inlined twice. Phase F-R of plan declarative-roaming-gosling.md (post-PR-#1312 review round). F-G will rewrite the helper to collect matches by position in the original HTML and concat slices in a single pass, so already-inlined content is never re-scanned. Refs nexu-io/open-design#1312 review threads at apps/daemon/src/inline-assets.ts:122 (Siri-Ray looper + codex bot).
This commit is contained in:
@@ -170,6 +170,41 @@ describe('inlineRelativeAssets', () => {
|
||||
expect(out).toContain('DEEP');
|
||||
expect(out).not.toContain('src="../../shared/util.js"');
|
||||
});
|
||||
|
||||
it('does not re-replace a tag literal that appears inside an already-inlined asset body', async () => {
|
||||
// Regression for nexu-io/open-design#1312 review feedback (Siri-Ray
|
||||
// looper + codex bot): the previous reduce/split-join approach
|
||||
// re-scanned the progressively mutated HTML, so a tag literal that
|
||||
// happened to appear inside an inlined asset body got the inner
|
||||
// literal also replaced — corrupting the body.
|
||||
//
|
||||
// The reproducer uses two <link rel=stylesheet> tags where a.css's
|
||||
// body contains the literal text of b.css's <link> tag (e.g. inside
|
||||
// a CSS comment or content: declaration). The </style escape on
|
||||
// CSS bodies doesn't touch <link>, so split/join over the mutated
|
||||
// HTML finds the literal inside a.css's inline body and replaces
|
||||
// it on the second pass — injecting b.css's inline body where the
|
||||
// literal comment text used to be.
|
||||
const html =
|
||||
'<link rel="stylesheet" href="a.css"><link rel="stylesheet" href="b.css">';
|
||||
const aCssBody = '/* see also <link rel="stylesheet" href="b.css"> */';
|
||||
const bCssBody = 'body{color:red}';
|
||||
const out = await inlineRelativeAssets(
|
||||
html,
|
||||
'index.html',
|
||||
readerFrom({ 'a.css': aCssBody, 'b.css': bCssBody }),
|
||||
);
|
||||
// The literal <link> string inside a.css's comment must survive
|
||||
// verbatim — position-based replacement only touches the original
|
||||
// outer-tag spans, not text introduced by earlier replacements.
|
||||
expect(out).toContain('/* see also <link rel="stylesheet" href="b.css"> */');
|
||||
// b.css's body is inlined exactly once, at the real outer tag's
|
||||
// position — not injected inside a.css's inline body.
|
||||
expect(out.match(/body\{color:red\}/g)?.length).toBe(1);
|
||||
// Neither original outer <link href="…"> survives as a URL ref.
|
||||
expect(out).not.toMatch(/<link\b[^>]*\bhref="a\.css"/);
|
||||
expect(out).not.toMatch(/<link\b[^>]*\bhref="b\.css"(?![^<]*\*\/)/);
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user