Skip to content

fix: rewrite asset URLs in route stylesheets, not just those in _assets/ - #151

Merged
mmcky merged 1 commit into
mainfrom
fix/route-css-asset-urls
Aug 26, 2026
Merged

fix: rewrite asset URLs in route stylesheets, not just those in _assets/#151
mmcky merged 1 commit into
mainfrom
fix/route-css-asset-urls

Conversation

@mmcky

@mmcky mmcky commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Closes #150. Found while verifying the published v2.3.1 artifact — #139 fixed the _assets/ stylesheets, but Remix emits others that were never in the rewriter's scope, and four of them shipped in v2.3.1 still carrying absolute /myst_assets_folder/… URLs.

What was still broken

scripts/relative-css-asset-urls.mjs enumerated one directory:

const assetsDir = path.resolve(assetsBuildDirectory, '_assets');
for (const name of fs.readdirSync(assetsDir).filter((f) => f.endsWith('.css'))) {

Route and shared-chunk CSS lands in public/build/, public/build/_shared/ and public/build/routes/, so readdirSync never saw it. Unzipping the released quantecon-theme.zip:

Location Files Absolute refs
public/build/_assets/ 5 0 — #139 working as intended
public/build/ 1 1
public/build/_shared/ 1 1
public/build/routes/ 2 1 each

All four are the same reference, :root{--jp-icon-plotly: url(/myst_assets_folder/_assets/plotly-6TYK7N2P.svg)}. The SVG is present in the bundle — only the reference is wrong, so it resolves under myst start and 404s in myst build --html output and under a baseurl.

#139's scoping note ("only the KaTeX stylesheet is affected; app.css and thebe-core.css emit no url() references") was measured against the _assets/ set, which is exactly the set that was in scope — so the gap was invisible from inside it.

Why it isn't a one-line glob change

The old substitution hardcoded url($1./, correct only for stylesheets sitting inside _assets/. The newly-included files reference _assets/ from a different depth:

Stylesheet Correct reference
_assets/*.css ./plotly-*.svg
root-*.css ./_assets/plotly-*.svg
_shared/*.css, routes/*.css ../_assets/plotly-*.svg

So the script now walks the build directory and derives each stylesheet's prefix from its own location via path.relative. The existence guard resolves from that stylesheet's directory too — the previous guard resolved everything against _assets/, so even had these files been in scope it would have validated them against the wrong base. That assumption is the thing that failed, so it is now the thing being checked.

Verification

Against a real npm run prod:build (not just the Playwright suite, which only exercises the myst start route where the absolute form happens to work):

  • [css-assets] rewrote 78 asset URL(s) in 7 stylesheet(s) — was 60 in 1.
  • Zero myst_assets_folder references remain anywhere under public/build.
  • Prefixes are per-location as intended: root-*.css./_assets/…, _shared/ and routes/../_assets/….
  • An independent resolver walking every url() and resolving it from its own stylesheet's directory reports 78 checked, 0 broken.
  • Re-running the script is a no-op (rewrote 0 asset URL(s) in 0 stylesheet(s), exit 0).
  • npm run compile clean · npm run test:unit 20/20.

No behaviour change under myst start: routes/x.css../_assets/y resolves to /myst_assets_folder/_assets/y, the same path it named before.

Not urgent

The visible symptom is a missing plotly icon on statically built sites that render plotly outputs — not the maths-wide degradation #138 caused. This does not warrant re-tagging v2.3.1; it can ride the next release.

🤖 Generated with Claude Code

The #139 rewriter enumerated a single directory, `public/build/_assets`. Remix
also emits route and shared-chunk CSS into the build root, `_shared/` and
`routes/`, so those files were never in scope and shipped in v2.3.1 still
carrying absolute `/myst_assets_folder/_assets/plotly-*.svg` references — the
same defect #138 described, in the files that fix did not cover.

Walk the build directory instead, and compute the prefix per stylesheet from
its own location: a stylesheet in `_assets/` still gets `./`, one in the build
root gets `./_assets/`, and one in `routes/` gets `../_assets/`. The existence
guard now resolves from each stylesheet's own directory too — that assumption
is what silently failed here, so it should be the thing being checked.

Verified against a real `npm run prod:build`: 78 asset URLs rewritten across 7
stylesheets, zero absolute references remain anywhere under `public/build`, and
an independent resolver confirms all 78 exist at the paths the stylesheets now
name. Re-running the script is a no-op.

Refs #150

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings August 26, 2026 01:38
@github-actions

github-actions Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-08-26 01:46 UTC

@github-actions

Copy link
Copy Markdown
Contributor

🎭 Visual regression results

passed  17 passed
skipped  3 skipped

Details

stats  20 tests across 1 suite
duration  37.8 seconds
commit  f09f980

Skipped tests

mobile-chrome › theme.spec.ts › QuantEcon theme — visual regression › launch-colab
mobile-chrome › theme.spec.ts › QuantEcon theme — visual regression › live-compute-toggle
mobile-chrome › theme.spec.ts › QuantEcon theme — visual regression › live-compute-toggle-absent-without-thebe

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes issue #150, a residual case of the #138 defect. The post-build script scripts/relative-css-asset-urls.mjs (added in #139) rewrites Remix's absolute ${publicPath}_assets/… url() references in built stylesheets to be relative, so they resolve in myst build --html output and under a baseurl. The prior version only enumerated the _assets/ directory, missing route- and shared-chunk CSS that Remix emits into the build root, _shared/, and routes/. Four such stylesheets shipped in v2.3.1 still carrying an absolute --jp-icon-plotly reference.

The fix walks the whole build tree and derives each stylesheet's _assets/ prefix from its own location (./, ./_assets/, or ../_assets/) via path.relative, and validates each rewritten target relative to that stylesheet's own directory.

Changes:

  • Recursively walk public/build for all .css files instead of only _assets/.
  • Compute the relative _assets/ prefix per stylesheet based on its depth, replacing the hardcoded ./.
  • Resolve the existence guard from each stylesheet's own directory; add a CHANGELOG entry.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
scripts/relative-css-asset-urls.mjs Adds a recursive stylesheet walk and per-location prefix computation, and resolves the existence check from each stylesheet's directory.
CHANGELOG.md Documents the fix under [Unreleased] / Fixed, referencing #139, #138, and #150.

I reviewed the path-prefix logic against all four layouts (build root, _assets/, _shared/, routes/), the updated URL/existence regexes, the cross-platform path.sep/ normalization, and the remix.config.prod.js values the script reads. The logic is correct and self-consistent, and the CHANGELOG links are accurate. I found no concrete issues to flag. Note this is a release-affecting build-pipeline step with subtle cross-platform path manipulation and no automated regression test guarding it.


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@mmcky
mmcky merged commit 47e37ed into main Aug 26, 2026
5 checks passed
@mmcky
mmcky deleted the fix/route-css-asset-urls branch August 26, 2026 01:46
mmcky added a commit that referenced this pull request Sep 4, 2026
Move the [Unreleased] entries under ## [2.4.0] - 2026-09-04 with a headline
blockquote, add the compare link, re-point the [Unreleased] compare base at
v2.4.0, and bump package.json (npm version 2.4.0 --no-git-tag-version).
template.yml is stamped by release.yml at build time.

Minor rather than patch: #155 adds the site footer part and back-to-top
styling alongside its fixes.

The release carries three commits that have been on main since 2026-09-03:

- #151 completes the static-build asset fix that v2.3.1 began. #139 covered
  the stylesheets in _assets/, but Remix also emits route and shared-chunk
  CSS, and four such files shipped in 2.3.1 still pointing --jp-icon-plotly
  at an absolute /myst_assets_folder/ URL. A production build now emits zero
  absolute asset URLs and all 78 references resolve.
- #155 brings lecture content typography into line with the Sphinx builds and
  fixes inline code rendering wrapped in literal backticks — a regression that
  reached every code span on every page.
- #161 bumps fast-uri 3.1.5 to 3.1.7.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

Residual #138: route-level stylesheets outside _assets/ still carry absolute /myst_assets_folder asset URLs

2 participants