fix: run router-swapped sub-app scripts in the fragment; test real mermaid rendering - #108
Merged
Merged
Conversation
reframed runs a fragment script in its iframe only when the script reaches the fragment's DOM through a host-realm insertion method: it patches the host window's Node/Element prototypes and nothing else. The ClientRouter parses the fetched page in the iframe, so its nodes are iframe-realm, and Astro's post-swap `script.replaceWith(fresh)` resolved to the iframe's unpatched replaceWith. Every sub-app script swapped in by a navigation executed in the HOST window: a vendored mermaid bundle defined window.mermaid on the host page, the init script found no diagrams there, and the flowchart stayed source text. A hard load was unaffected, which is why only in-fragment navigation showed it. The embedded swap now imports the fetched head and body into the host document before swapping them in. data-astro-exec marks survive the copy, so scripts already running are not run twice. Also moves the shadow-DOM design tokens ahead of the page content. They came last in <body>, so a blocking <script src> in a sub-app body (the mermaid bundle is 2.5 MB) left an embedded hard load with no tokens until the script finished: black text, no theme. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The single-page fixture shipped a no-op mermaid stub, so nothing could tell a rendered diagram from source text. setup-test-apps now copies the real bundle from a root `mermaid` devDependency, pinned to the version actions/ vendors (artifact-checks.spec keeps them equal), into the fixture. The copy is gitignored rather than committing 2.5 MB of minified JS; CI's build job runs setup first so the container suite gets it too. web-fragment.spec, both embeddings: the flowchart is an SVG with its three nodes, no source text, both assets 200 and no page errors, on a hard load, after router navigation into the doc, and after leaving and coming back; and after navigation window.mermaid exists in the fragment's iframe and not on the host window. container.spec: the diagram renders under the real nginx CSP. tsconfig excludes gitignored build output and fixtures: `astro check` walked tmp/, apps/ and public/ locally and ran out of heap on the vendored bundles there. Closes #105 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #105.
The bug the new coverage found
With the real mermaid bundle in the fixture, the diagram rendered on a hard load and after returning to the page, but not after the first in-fragment navigation into it, in both embeddings. After that navigation
window.mermaidwas defined on the host window and undefined in the fragment's iframe.reframed runs a fragment script in its iframe only when it reaches the fragment's DOM through a host-realm insertion method (it patches the host window's
Node/Elementprototypes only). The ClientRouter parses the fetched page in the iframe, so its nodes are iframe-realm; Astro's post-swapscript.replaceWith(fresh)then used the iframe's unpatchedreplaceWith, and the script executed in the host window. So every sub-app script swapped in by a navigation ran on the host page, not only mermaid's.Fix (
src/scripts/embedded-transitions.js): the embedded swap imports the fetched head and body into the host document before swapping them in.data-astro-execmarks survive the copy, so already-running scripts are not re-run.Second fix (
src/layouts/Base.astro): the shadow-DOM design tokens were the last thing in<body>. With a blocking 2.5 MB<script src>in the sub-app body, an embedded hard load rendered with no tokens (black text, no theme) until the script finished.css-isolation.speccaught this once the real bundle was in place. They now follow the knowledge base stylesheet, ahead of the content, which is the order CLAUDE.md already described.Coverage (hermetic, public fixture only)
scripts/setup-test-apps.mjscopies the realmermaid.min.jsinstead of the no-op stub. It comes from a new root devDependency pinned to the versionactions/vendors (11.4.1), andartifact-checks.speckeeps the two pins equal. The copy is gitignored rather than committing 2.5 MB. CI's build job now runs setup first, so the container suite gets it too.web-fragment.spec.js, bothchromiumandchromium-pierced: the flowchart becomes an SVG with its 3 nodes, no source text,mermaid.min.jsandmermaid-init.jsreturn 200, and there are no page errors. Checked on a hard load, after router navigation into the doc, and after leaving and coming back. After navigationwindow.mermaidexists in the fragment iframe and not on the host.container.spec.js: the diagram renders under the real nginx CSP.tsconfig.jsonexcludes gitignored build output and fixtures (tmp/,apps/,public/,tests/fixtures/). Locallyastro checkwalked the vendored bundles there and ran out of heap. CI typecheck (fresh checkout) was unaffected.Verification
npm test: 402 passed. Before the fix, "renders after in-fragment navigation" failed in both projects.playwright.config.ci.js: 22 passednpm run test:container: 29 passed (twice, fresh image)npx astro check: 0 errorsNote
actions/vendors mermaid 11.4.1, whichnpm auditflags through@mermaid-js/parser(fixed in ≥ 11.17). That is out of scope here, but it is what published docs ship. The root pin only mirrors it, and the production audit gate (--omit=dev) is unaffected.🤖 Generated with Claude Code