Skip to content

fix(theme): keep the css sourcemap chain intact when rewriting theme urls - #25710

Merged
Artur- merged 2 commits into
mainfrom
fix/theme-css-plugin-breaking-sourcemaps
Sep 15, 2026
Merged

Artur- merged 2 commits into
mainfrom
fix/theme-css-plugin-breaking-sourcemaps

Conversation

@totally-not-ai

Copy link
Copy Markdown
Contributor

Summary

The build plugin that rewrites url(...) references in application theme CSS returned the rewritten CSS without a sourcemap, which threw away the map the bundler had for that file. It now returns a sourcemap for the rewrite, and returns nothing at all when a file has no url to rewrite.

What changed

Behavior change (internal API only): rewriteCssUrls() in theme-loader-utils.js no longer returns a string. It now returns { code, map } when it rewrote something, and null when there was nothing to rewrite. Both callers in this repo are updated: the Vite theme plugin in vite.generated.ts returns the object straight to Vite, and the theme-loader.js loader uses rewritten.code. This is an internal build-tool helper, not public API, so applications are not affected.

Build output is unchanged. The rewritten CSS is byte for byte the same as before, and no build enables CSS sourcemaps today, so nothing in the emitted bundle changes.

Other details:

  • The rewrite now runs through MagicString (already pinned at 1.3.1 in the generated Vite package.json, so no new dependency).
  • The map is generated with hires: 'boundary' instead of per character. Since the rewrite replaces whole url(...) tokens, every position a reader needs is on a token boundary, so nothing is lost. On a 94 KB theme CSS file this cut the mappings from 416 KB to 187 KB.
  • Returning null for untouched files lets Vite keep the file's existing map instead of replacing it with an identity map.

Related to #16679

Test summary

This branch adds no tests. There is no JavaScript unit-test harness for the files under flow-build-tools/src/main/resources/plugins/, so the table below is all gaps — it lists what a reviewer would want pinned.

# Status What the test verifies Why it matters
1 ❗ gap rewriteCssUrls produces the same rewritten CSS text as before for the known url shapes (./, ../, extra dot segments, quoted and unquoted) This is the promise that build output does not change; a regression silently breaks theme asset urls
2 ❗ gap The returned map traces a rewritten url(...) token, and the text after it, back to its original position The whole point of the fix; a wrong map sends debuggers to the wrong place
3 ❗ gap rewriteCssUrls returns null for a CSS file with no url to rewrite If it returned a {code, map} pair instead, Vite would drop the file's real upstream map
4 ❗ gap theme-loader.js passes the original source to this.callback when the rewrite returns null, and rewritten.code otherwise A missed null check here would feed undefined CSS into the webpack-style pipeline
  • No test methods were added or changed on this branch, so no row maps to a named test.

Deliberately untested: the map's exact byte size (an optimisation detail, not a contract) and the logger output. The flow-tests/test-frontend/vite-* IT modules build real themes and would catch a crash or broken CSS, but they do not check sourcemap accuracy, so rows 2 and 3 stay uncovered.

The plugin that rewrites the urls of application theme css files handed
back the rewritten css without a sourcemap, which throws away the map
the bundler has for that file. It now returns a map of the rewriting,
and reports having done nothing when a file has no url to rewrite.

The rewritten css is byte for byte the same as before, and no build
emits a css sourcemap today, so the output of every build is unchanged.

Related to #16679
A per-character sourcemap of the url rewriting is over twice the size of
a boundary one for no gain: the rewriting replaces whole url(...) tokens,
so every mapping a reader needs sits on a token boundary. Measured on a
94 KB theme css, the mappings shrink from 416 KB to 187 KB, and every
token after an edit still traces back to its exact original position.
@github-actions

Copy link
Copy Markdown
Contributor

Test Results

 1 474 files  ±0   1 558 suites  ±0   1h 38m 4s ⏱️ - 2m 37s
12 239 tests ±0  12 171 ✅ ±0  68 💤 ±0  0 ❌ ±0 
12 557 runs  ±0  12 489 ✅ ±0  68 💤 ±0  0 ❌ ±0 

Results for commit 1094e3b. ± Comparison against base commit aaf20ce.

@Artur-
Artur- added this pull request to the merge queue Sep 15, 2026
@sonarqubecloud

Copy link
Copy Markdown

Merged via the queue into main with commit dd98e9a Sep 15, 2026
54 checks passed
@Artur-
Artur- deleted the fix/theme-css-plugin-breaking-sourcemaps branch September 15, 2026 07:35
vaadin-bot added a commit that referenced this pull request Sep 15, 2026
…urls (#25710) (CP: 25.3) (#25713)

This PR cherry-picks changes from the original PR #25710 to branch 25.3.
---
#### Original PR description
> ## Summary
> The build plugin that rewrites `url(...)` references in application
theme CSS returned the rewritten CSS without a sourcemap, which threw
away the map the bundler had for that file. It now returns a sourcemap
for the rewrite, and returns nothing at all when a file has no url to
rewrite.
> 
> ## What changed
> **Behavior change (internal API only):** `rewriteCssUrls()` in
`theme-loader-utils.js` no longer returns a string. It now returns `{
code, map }` when it rewrote something, and `null` when there was
nothing to rewrite. Both callers in this repo are updated: the Vite
theme plugin in `vite.generated.ts` returns the object straight to Vite,
and the `theme-loader.js` loader uses `rewritten.code`. This is an
internal build-tool helper, not public API, so applications are not
affected.
> 
> **Build output is unchanged.** The rewritten CSS is byte for byte the
same as before, and no build enables CSS sourcemaps today, so nothing in
the emitted bundle changes.
> 
> Other details:
> - The rewrite now runs through `MagicString` (already pinned at
`1.3.1` in the generated Vite `package.json`, so no new dependency).
> - The map is generated with `hires: 'boundary'` instead of per
character. Since the rewrite replaces whole `url(...)` tokens, every
position a reader needs is on a token boundary, so nothing is lost. On a
94 KB theme CSS file this cut the mappings from 416 KB to 187 KB.
> - Returning `null` for untouched files lets Vite keep the file's
existing map instead of replacing it with an identity map.
> 
> Related to #16679
> 
> ## Test summary
> This branch adds no tests. There is no JavaScript unit-test harness
for the files under `flow-build-tools/src/main/resources/plugins/`, so
the table below is all gaps — it lists what a reviewer would want
pinned.
> 
> | # | Status | What the test verifies | Why it matters |
> |---|--------|------------------------|----------------|
> | 1 | ❗ **gap** | `rewriteCssUrls` produces the same rewritten CSS
text as before for the known url shapes (`./`, `../`, extra dot
segments, quoted and unquoted) | This is the promise that build output
does not change; a regression silently breaks theme asset urls |
> | 2 | ❗ **gap** | The returned `map` traces a rewritten `url(...)`
token, and the text after it, back to its original position | The whole
point of the fix; a wrong map sends debuggers to the wrong place |
> | 3 | ❗ **gap** | `rewriteCssUrls` returns `null` for a CSS file with
no url to rewrite | If it returned a `{code, map}` pair instead, Vite
would drop the file's real upstream map |
> | 4 | ❗ **gap** | `theme-loader.js` passes the original source to
`this.callback` when the rewrite returns `null`, and `rewritten.code`
otherwise | A missed `null` check here would feed `undefined` CSS into
the webpack-style pipeline |
> 
> - No test methods were added or changed on this branch, so no row maps
to a named test.
> 
> Deliberately untested: the map's exact byte size (an optimisation
detail, not a contract) and the logger output. The
`flow-tests/test-frontend/vite-*` IT modules build real themes and would
catch a crash or broken CSS, but they do not check sourcemap accuracy,
so rows 2 and 3 stay uncovered.

Co-authored-by: totally-not-ai[bot] <290682512+totally-not-ai[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants