Skip to content

fix: run router-swapped sub-app scripts in the fragment; test real mermaid rendering - #108

Merged
oto-macenauer-absa merged 2 commits into
masterfrom
test/mermaid-rendering
Sep 24, 2026
Merged

oto-macenauer-absa merged 2 commits into
masterfrom
test/mermaid-rendering

Conversation

@oto-macenauer-absa

Copy link
Copy Markdown
Collaborator

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.mermaid was 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/Element prototypes only). The ClientRouter parses the fetched page in the iframe, so its nodes are iframe-realm; Astro's post-swap script.replaceWith(fresh) then used the iframe's unpatched replaceWith, 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-exec marks 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.spec caught 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.mjs copies the real mermaid.min.js instead of the no-op stub. It comes from a new root devDependency pinned to the version actions/ vendors (11.4.1), and artifact-checks.spec keeps 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, both chromium and chromium-pierced: the flowchart becomes an SVG with its 3 nodes, no source text, mermaid.min.js and mermaid-init.js return 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 navigation window.mermaid exists in the fragment iframe and not on the host.
  • container.spec.js: the diagram renders under the real nginx CSP.
  • tsconfig.json excludes gitignored build output and fixtures (tmp/, apps/, public/, tests/fixtures/). Locally astro check walked 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 passed
  • npm run test:container: 29 passed (twice, fresh image)
  • npx astro check: 0 errors

Note

actions/ vendors mermaid 11.4.1, which npm audit flags 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

oto-macenauer-absa and others added 2 commits September 24, 2026 17:03
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>
@oto-macenauer-absa
oto-macenauer-absa merged commit 48390c0 into master Sep 24, 2026
8 checks passed
@oto-macenauer-absa
oto-macenauer-absa deleted the test/mermaid-rendering branch September 24, 2026 15:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add hermetic coverage for Mermaid rendering in embedded docs

1 participant