Skip to content

fix(core): a sub-composition's stylesheet from the project root loads in the preview - #5143

Draft
miguel-heygen wants to merge 2 commits into
mainfrom
fix/core-subcomp-link-root
Draft

miguel-heygen wants to merge 2 commits into
mainfrom
fix/core-subcomp-link-root

Conversation

@miguel-heygen

Copy link
Copy Markdown
Collaborator

What

A <link rel="stylesheet" href="assets/theme.css"> inside a sub-composition now loads in the preview from the same place the render loads it, instead of 404ing at compositions/assets/theme.css and leaving the sub-composition unstyled.

Why

The compiler and the runtime loader resolve a sub-composition's relative paths by one rule (rewriteAssetPath in parsers/src/rewriteSubCompPaths.ts): a ../ path is relative to the composition's file; a plain path is the file beside the composition when one exists, otherwise the project root's. The runtime loader follows that rule for [src]/[href] attributes and <script src>, but the <link> it hoists into the document head resolved every href against the composition's URL. So a stylesheet authored the way the render accepts it (assets/theme.css from compositions/scene.html) rendered styled and previewed unstyled.

To reproduce on main: a project with assets/theme.css and compositions/scene.html whose <template> holds <link rel="stylesheet" href="assets/theme.css">, mounted from index.html with data-composition-src="compositions/scene.html". The preview requests compositions/assets/theme.css (404); the render inlines assets/theme.css.

Related work

None.

How

hoistedLinkHref in runtime/compositionLoader.ts gives the hoisted link the compiler's rule. The runtime cannot read the disk, so for a plain href whose sibling and project-root URLs differ it sends one HEAD for the sibling (the preview server answers a missing file with 404) and uses the sibling only when it is there; otherwise the project-root URL. ../ hrefs are already absolute by then (rewriteSubCompositionAssetPaths), and absolute, root and data: hrefs resolve as before. A sibling stylesheet beside its composition (_shared.css) still resolves beside it.

Test plan

  • Unit tests added/updated: compositionLoader.test.ts, "a plain stylesheet path resolves as the render does", with no sibling file (the project root) and with a sibling (beside the composition). On main the no-sibling case fails (it gets compositions/assets/theme.css) and the sibling case passes; with the fix both pass. The file's 68 tests pass 3 runs in a row; the existing sibling-link tests are unchanged and pass.
  • Manual testing performed: not driven in a live preview. The probe's behaviour against the preview server was checked by reading studio-server/src/routes/preview.ts (a missing file answers 404; Hono answers HEAD from the GET route), not by a request.
  • Documentation updated (if applicable): not needed.
  • Comments follow CONTRIBUTING.md "Comments".

packages/core typecheck and oxlint/oxfmt on the changed files are clean. The full core suite was not compared against main locally (the run reported failures in files that do not import the loader and fail without built sibling packages; the baseline comparison could not complete), so CI is the full-suite check.

@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 2059 (base branch 2059), smooth 1601 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (0)

This branch has not been deployed

No deployments
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.

1 participant