fix(site-import): resolve cache-busted asset URLs - #539
Open
spcxai2025 wants to merge 1 commit into
Open
spcxai2025 wants to merge 1 commit into
spcxai2025 wants to merge 1 commit into
Conversation
A query string or fragment was never stripped before looking a path up in the FileMap, so `assets/site.css?v=8f3a1c` resolved to a key that cannot exist. The reference was reported as `missing-stylesheet` / `missing-script` while the file sat in the archive, and every style rule, colour token and font in that sheet was silently dropped from the import. Static site generators commonly emit a content hash on stylesheet, script and media URLs, so this affects a whole class of input. Measured on one real 6-page export: 401 style rules and 18 colour tokens imported, against 659 and 26 for the byte-identical site without the queries — a 39% and 31% loss, reported only as warnings a user can click past. The handling was inconsistent rather than absent, which is why it survived: `linkRewrite` stripped the query itself before calling `resolveHref`, so internal page links resolved correctly while stylesheets did not, and `assetPlan` stripped one for MIME sniffing but not for resolution. Fixed at both resolution primitives: - `resolveHref` (stylesheets, scripts, CSS @import, and page links via linkRewrite) - `resolveRelativePath` (CSS url(), node src/href/srcset) and removed the now-redundant strip in `linkRewrite`. Its fragment split stays — the fragment is re-attached to the page ref and is not addressing. This cannot affect the rewrite step. Node props are replaced by the resolved FileMap key and `rewriteProps` keys on that key, while CSS passes the original `rawUrl` to `replaceRawUrlInValue` as a separate argument, so stripping changes only which entry is found. Tests: a unit group on `resolveHref` covering query, fragment, both, and empty-after-strip, plus an assertion that a query does not change traversal handling; and an end-to-end group over `buildImportPlan` asserting that a cache-busted site produces a plan identical to the same site without queries. The end-to-end shape matters here — the unit returned a plausible-looking key and every consumer dutifully failed to find it, so the defect was only ever visible downstream. Verified the new tests fail without the source change (11 failures) and pass with it. bun run build, bun test (7067), bun run lint all pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
spcxai2025
force-pushed
the
fix/import-cache-busted-asset-urls
branch
from
September 17, 2026 20:35
9bad9a4 to
6a64675
Compare
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.
Summary
A query string or fragment was never stripped before looking a path up in the
FileMap, soassets/site.css?v=8f3a1cresolved to a key that cannot exist. The reference was reported asmissing-stylesheet/missing-scriptwhile the file sat in the archive, and every style rule, colour token and font in that sheet was dropped from the import.Static site generators commonly emit a content hash on stylesheet, script and media URLs, so this affects a whole class of input. Measured on one real 6-page export:
?v=A 39% and 31% loss, surfaced only as warnings a user can click past.
The handling was inconsistent rather than absent, which is probably why it survived:
linkRewritestripped the query itself before callingresolveHref, so internal page links resolved correctly while stylesheets did not — andassetPlanstripped one for MIME sniffing but not for resolution.The change
Fixed at both resolution primitives:
resolveHref—<link rel=stylesheet>,<script src>, CSS@import, and page links vialinkRewriteresolveRelativePath— CSSurl(), nodesrc/href/srcsetand removed the now-redundant strip in
linkRewrite. Its fragment split stays — the fragment is re-attached to the page ref bymakePageRefand is not addressing.This cannot affect the rewrite step. Node props are replaced by the resolved FileMap key and
rewritePropskeys on that key; CSS passes the originalrawUrltoreplaceRawUrlInValueas a separate argument. Stripping changes only which entry is found.Tests
htmlPagePlan.test.ts) — query, fragment, both, empty-after-strip, and that a query does not change traversal handling.cacheBustedUrls.test.ts) — a cache-busted site produces a plan identical to the same site without queries.The end-to-end shape matters here: the unit returned a plausible-looking key and every consumer dutifully failed to find it, so the defect was only ever visible downstream as warnings. I verified the new tests fail without the source change (11 failures) and pass with it.
One note on the traversal assertion — I initially wrote it expecting
../../x.cssto be rejected, then foundjoinPathsclamps an escaping..to the root by design. The test now pins the property this change is actually responsible for: a query string must not alter that handling.Verification
bun run buildbun test— 7067 pass, 0 failbun run lintChecklist
docs/features/site-import.md: themissing-stylesheetrow plus a short "Cache-busted URLs" note).linkRewritewas removed rather than left in place.Happy to split the
linkRewritecleanup into its own commit, or adjust the stripping to?only if you'd rather leave fragments alone on asset URLs.