fix(i18n): keep production sourcemaps usable when the i18n plugin rewrites a chunk - #25691
Merged
Merged
Conversation
The i18n Vite plugin rewrote every chunk in generateBundle and replaced the chunk sourcemap with a MagicString map that has no sources, so all .map files of an application built with build.sourcemap enabled came out empty. The rewriting now happens in renderChunk, where the bundler composes the returned map with the one it already has for the chunk, and chunks the plugin does not touch keep their sourcemap as is. The registerChunk calls are now matched with a pattern that tolerates renamed bindings and rewritten string quotes, so the duplicate calls are removed and every chunk name marker is replaced also when the bundler has reformatted the rendered chunk. Related to #16679
The production test application now uses Hilla translations in two frontend modules, so that the i18n build plugin rewrites the chunk they end up in. Without them the plugin left every chunk untouched and the sourcemap test only covered the chunks the plugin does not rewrite. The sourcemap test also follows the imports of the entry bundle transitively and checks that the sources of a map are the original files, with their contents, instead of only counting them.
Contributor
Test Results 1 474 files 1 558 suites 1h 37m 22s ⏱️ Results for commit 9523867. ♻️ This comment has been updated with latest results. |
|
platosha
approved these changes
Sep 14, 2026
vaadin-bot
added a commit
that referenced
this pull request
Sep 14, 2026
…rites a chunk (#25691) (CP: 25.3) (#25707) This PR cherry-picks changes from the original PR #25691 to branch 25.3. --- #### Original PR description > ## Summary > > The Vaadin i18n build plugin replaced the sourcemap of every chunk with an empty one, so all `.map` files of an app built with `build.sourcemap` enabled were useless. The plugin now rewrites chunks at a point where the bundler keeps the original sourcemap, and leaves untouched chunks alone. > > Fixes #16679 > > ## What changed > > **Behavior change:** only apps that build with `build.sourcemap` enabled are affected, and only for the better — their `.map` files now contain the real sources instead of being empty. Chunk output itself is unchanged, so no API or runtime behavior changes for anyone else. > > - `rollup-plugin-vaadin-i18n.js` moves the chunk rewriting from `generateBundle` to `renderChunk`. The bundler then composes the map returned by the plugin with the map it already has for that chunk. Chunks the plugin does not touch return `null` and keep their sourcemap as is. `generateBundle` still collects the translation keys. > - The `registerChunk` calls are now found with a regular expression instead of an exact string match. It tolerates a renamed `i18n` binding and rewritten string quotes, so duplicate calls are removed and the chunk name marker is replaced also when the bundler has reformatted the rendered chunk. > - The `vite-production` test app now uses Hilla translations in two frontend modules (via a small stub of `@vaadin/hilla-react-i18n`) and builds with sourcemaps on, so the tests actually run through the rewriting path. > > ## Test summary > > | # | Status | What the test verifies | Why it matters | > |---|--------|------------------------|----------------| > | 1 | ✅ | Every `.map` file reachable from the entry bundle has non-empty `sources`, non-empty `mappings`, and the contents of each source | This is the bug: a rewritten chunk got a map with no sources, so the browser could not map the bundle back to the original files | > | 2 | ✅ | Chunks the plugin does not rewrite still have a usable sourcemap | The early `return null` must not drop maps the bundler produced | > | 3 | ✅ | The chunk that contains translations calls `registerChunk` with its own served file name, and the marker is gone | A wrong or unreplaced name means the runtime cannot load the translations of that chunk | > | 4 | ✅ | That chunk has exactly one `registerChunk` call | Duplicate calls from several modules must be removed; a missed match would also leave a stale marker behind | > | 5 | ❗ **gap** | Matching when the bundler renames the `i18n` binding or rewrites the string quotes | The regex exists for this case, but no test forces that output shape; whether it happens depends on the minifier settings of the test app | > > Tests added on this branch: > > - `SourceMapsIT.bundleSourceMapsPointToOriginalSources` — rows 1, 2 > - `I18nChunkIT.chunkRegistersItselfUnderItsOwnName` — rows 3, 4 > > Left untested on purpose: `BundleAccess` is a test helper that only downloads bundle files, so it is covered indirectly by both ITs. Co-authored-by: totally-not-ai[bot] <290682512+totally-not-ai[bot]@users.noreply.github.com> Co-authored-by: Artur Signell <artur@vaadin.com>
This was referenced Sep 14, 2026
vaadin-bot
added a commit
that referenced
this pull request
Sep 15, 2026
…lugin (#25708) (CP: 25.3) (#25715) This PR cherry-picks changes from the original PR #25708 to branch 25.3. --- #### Original PR description > ## Summary > The Vite plugin that preserves the Vaadin usage statistics comment returned the code of every frontend module without a sourcemap, so the bundler dropped all of them from the emitted `.map` files. Dev bundles built with `build.sourcemap` enabled got maps with no sources, and the browser could not show the original frontend files. The plugin now leaves untouched modules alone and returns a real sourcemap for the one module it rewrites. > > ## What changed > **Behavior change:** for anyone building a dev bundle with `build.sourcemap` enabled, the emitted `.map` files now contain the original frontend sources instead of being empty. No public Java API behaviour changes, and no application code needs updating. > > - `vite.generated.ts`: the `vaadin:preserve-usage-stats` plugin returns `null` for modules it does not change, instead of returning the unchanged code with no map. Returning code without a map makes the bundler drop that module from the sourcemap of its chunk, and this hook sees every module in the bundle. > - The one module it does change (the usage statistics comment rewrite, `/**` → `/*!` so minifiers keep it) is now rewritten with `magic-string`, so the plugin can return a proper sourcemap along with the code. > - Error cases (`vaadin-dev-mode:start` tag missing, rewritten comment no longer matching the expected pattern) still log the same messages and now leave the module untouched. > - Test support: the dev bundle test app builds with `build.sourcemap` enabled, gets a `vaadin-usage-statistics-stub.js` frontend module that carries the comment in its plain form (the published `@vaadin/vaadin-usage-statistics` already carries it rewritten), and the sourcemap assertions shared by the dev and production bundle tests moved into a new `SourceMapTestUtil` in `flow-test-util`. > > Fixes #16679, follow-up to #25691. > > ## API Changes > > ### com.vaadin.flow.testutil.SourceMapTestUtil > > ```java > // Added > public final class SourceMapTestUtil > public static List<String> assertSourceMapUsable(String name, String sourceMap) // asserts the map has sources, mappings and source contents; returns the source file names > ``` > > ## Test summary > > | # | Status | What the test verifies | Why it matters | > |---|--------|------------------------|----------------| > | 1 | ✅ | Every `.js.map` of the dev bundle has sources, non-empty mappings, and the content of each source | This is the bug: the maps were emitted empty and the browser could not map the bundle back to the sources | > | 2 | ✅ | A dev bundle sourcemap refers to a normal frontend file (`lit-view.ts`) the plugin does not touch | Covers the `return null` path — modules the plugin skips must stay in the chunk map | > | 3 | ✅ | A dev bundle sourcemap refers to the rewritten usage statistics module itself | Covers the path where the plugin does change code — its own map must chain onto the bundler's | > | 4 | ✅ | In every chunk, the usage statistics dev mode comment appears in the rewritten `/*!` form | The rewrite is the only thing the plugin does; a broken rewrite would let a minifier strip the comment | > | 5 | ✅ | Production bundle sourcemaps still point to the original sources after the assertions moved to the shared helper | Guards against the refactor weakening the existing production check | > | 6 | ❗ **gap** | The error paths (missing `vaadin-dev-mode:start` tag, rewritten comment failing the expected pattern) log and leave the module unchanged | Only reachable with a malformed module; not exercised by a test | > > - `DevBundleSourceMapsIT.devBundleSourceMapsPointToOriginalSources` — rows 1, 2, 3 > - `DevBundleSourceMapsIT.usageStatisticsCommentIsRewrittenInTheBundle` — row 4 > - `SourceMapsIT.bundleSourceMapsPointToOriginalSources` (changed to use the shared helper) — row 5 > > Deliberately untested: `SourceMapTestUtil` itself, which is exercised through both ITs, and the plugin's console error branches (row 6), which need a deliberately malformed usage statistics module. Co-authored-by: totally-not-ai[bot] <290682512+totally-not-ai[bot]@users.noreply.github.com>
javier-godoy
pushed a commit
to javier-godoy/flow
that referenced
this pull request
Sep 29, 2026
…lugin (vaadin#25708) ## Summary The Vite plugin that preserves the Vaadin usage statistics comment returned the code of every frontend module without a sourcemap, so the bundler dropped all of them from the emitted `.map` files. Dev bundles built with `build.sourcemap` enabled got maps with no sources, and the browser could not show the original frontend files. The plugin now leaves untouched modules alone and returns a real sourcemap for the one module it rewrites. ## What changed **Behavior change:** for anyone building a dev bundle with `build.sourcemap` enabled, the emitted `.map` files now contain the original frontend sources instead of being empty. No public Java API behaviour changes, and no application code needs updating. - `vite.generated.ts`: the `vaadin:preserve-usage-stats` plugin returns `null` for modules it does not change, instead of returning the unchanged code with no map. Returning code without a map makes the bundler drop that module from the sourcemap of its chunk, and this hook sees every module in the bundle. - The one module it does change (the usage statistics comment rewrite, `/**` → `/*!` so minifiers keep it) is now rewritten with `magic-string`, so the plugin can return a proper sourcemap along with the code. - Error cases (`vaadin-dev-mode:start` tag missing, rewritten comment no longer matching the expected pattern) still log the same messages and now leave the module untouched. - Test support: the dev bundle test app builds with `build.sourcemap` enabled, gets a `vaadin-usage-statistics-stub.js` frontend module that carries the comment in its plain form (the published `@vaadin/vaadin-usage-statistics` already carries it rewritten), and the sourcemap assertions shared by the dev and production bundle tests moved into a new `SourceMapTestUtil` in `flow-test-util`. Fixes vaadin#16679, follow-up to vaadin#25691. ## API Changes ### com.vaadin.flow.testutil.SourceMapTestUtil ```java // Added public final class SourceMapTestUtil public static List<String> assertSourceMapUsable(String name, String sourceMap) // asserts the map has sources, mappings and source contents; returns the source file names ``` ## Test summary | # | Status | What the test verifies | Why it matters | |---|--------|------------------------|----------------| | 1 | ✅ | Every `.js.map` of the dev bundle has sources, non-empty mappings, and the content of each source | This is the bug: the maps were emitted empty and the browser could not map the bundle back to the sources | | 2 | ✅ | A dev bundle sourcemap refers to a normal frontend file (`lit-view.ts`) the plugin does not touch | Covers the `return null` path — modules the plugin skips must stay in the chunk map | | 3 | ✅ | A dev bundle sourcemap refers to the rewritten usage statistics module itself | Covers the path where the plugin does change code — its own map must chain onto the bundler's | | 4 | ✅ | In every chunk, the usage statistics dev mode comment appears in the rewritten `/*!` form | The rewrite is the only thing the plugin does; a broken rewrite would let a minifier strip the comment | | 5 | ✅ | Production bundle sourcemaps still point to the original sources after the assertions moved to the shared helper | Guards against the refactor weakening the existing production check | | 6 | ❗ **gap** | The error paths (missing `vaadin-dev-mode:start` tag, rewritten comment failing the expected pattern) log and leave the module unchanged | Only reachable with a malformed module; not exercised by a test | - `DevBundleSourceMapsIT.devBundleSourceMapsPointToOriginalSources` — rows 1, 2, 3 - `DevBundleSourceMapsIT.usageStatisticsCommentIsRewrittenInTheBundle` — row 4 - `SourceMapsIT.bundleSourceMapsPointToOriginalSources` (changed to use the shared helper) — row 5 Deliberately untested: `SourceMapTestUtil` itself, which is exercised through both ITs, and the plugin's console error branches (row 6), which need a deliberately malformed usage statistics module. --------- Co-authored-by: totally-not-ai[bot] <290682512+totally-not-ai[bot]@users.noreply.github.com>
Collaborator
|
This ticket/PR has been released with Vaadin 25.4.0-alpha1. |
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
The Vaadin i18n build plugin replaced the sourcemap of every chunk with an empty one, so all
.mapfiles of an app built withbuild.sourcemapenabled were useless. The plugin now rewrites chunks at a point where the bundler keeps the original sourcemap, and leaves untouched chunks alone.Fixes #16679
What changed
Behavior change: only apps that build with
build.sourcemapenabled are affected, and only for the better — their.mapfiles now contain the real sources instead of being empty. Chunk output itself is unchanged, so no API or runtime behavior changes for anyone else.rollup-plugin-vaadin-i18n.jsmoves the chunk rewriting fromgenerateBundletorenderChunk. The bundler then composes the map returned by the plugin with the map it already has for that chunk. Chunks the plugin does not touch returnnulland keep their sourcemap as is.generateBundlestill collects the translation keys.registerChunkcalls are now found with a regular expression instead of an exact string match. It tolerates a renamedi18nbinding and rewritten string quotes, so duplicate calls are removed and the chunk name marker is replaced also when the bundler has reformatted the rendered chunk.vite-productiontest app now uses Hilla translations in two frontend modules (via a small stub of@vaadin/hilla-react-i18n) and builds with sourcemaps on, so the tests actually run through the rewriting path.Test summary
.mapfile reachable from the entry bundle has non-emptysources, non-emptymappings, and the contents of each sourcereturn nullmust not drop maps the bundler producedregisterChunkwith its own served file name, and the marker is goneregisterChunkcalli18nbinding or rewrites the string quotesTests added on this branch:
SourceMapsIT.bundleSourceMapsPointToOriginalSources— rows 1, 2I18nChunkIT.chunkRegistersItselfUnderItsOwnName— rows 3, 4Left untested on purpose:
BundleAccessis a test helper that only downloads bundle files, so it is covered indirectly by both ITs.