Conversation
robhogan
added this pull request to stack #1986
September 26, 2026 10:22
When minifying, the transform worker converted Babel's decoded map to tuples and encoded it for the minifier, then decoded the minifier's output map with `source-map` (`toBabelSegments`), converted it back to tuples and encoded it again as the stored VLQ map. Terser already builds that decoded map, and only encodes it when `result.map` is read. This diff: - Adds an optional `decodedMap` to `MinifierResult`. When a minifier provides it, the worker encodes the stored map straight from it with `vlqMapFromBabelDecodedMap`, as it already does for unminified modules. Otherwise it decodes `map` with `@jridgewell/sourcemap-codec`, which is already installed via `@babel/generator`. - Has `metro-minify-terser` return Terser's `decoded_map` as `decodedMap`, and encode `map` only if it's read. - Passes Babel's own encoded `result.map` to the minifier, rather than re-encoding it from tuples. - Deprecates `MinifierResult.map` in favour of `decodedMap`, and `toBabelSegments`, `toSegmentTuple` and `tuplesFromBabelDecodedMap` in `metro-source-map`, which Metro no longer uses. The next diff removes them. Output is unchanged - across every module of our benchmark app*, the minified code, line count and stored map are byte-identical. Custom minifiers that only return `map` keep working as before. Minifying the whole app is 10.4% faster (95% CI 7.4-11.4%; 21.3s -> 19.2s of CPU on our benchmark app*), from no longer converting the map to and from tuples, and from Terser no longer encoding its map at all: | | Minify step (median) | vs `main` | |---|---|---| | `main` | 21,286ms (20,844 to 21,561) | | | This diff | 19,228ms (18,886 to 19,773) | -10.4% (-11.4 to -7.4) | ## Changelog ``` - **[Performance]**: Faster source map handling when minifying, using the minifier's decoded map if it provides one (`MinifierResult.decodedMap`) - **[Deprecated]**: Custom minifiers should return their source map as `decodedMap` - `MinifierResult.map` is deprecated - **[Deprecated]**: `metro-source-map`: Deprecate `toBabelSegments`, `toSegmentTuple` and `tuplesFromBabelDecodedMap` ``` ## Test plan New tests cover the worker using `decodedMap` without reading `map`, and `metro-minify-terser` returning Terser's decoded map and encoding `map` only on request. ``` yarn jest yarn flow check yarn lint yarn verify-api-snapshots ``` \* Benchmark: Mattermost Mobile 2.45.0 (React Native 0.83.9), its release bundle split back into its 7,082 modules (5.13M mapping segments), each minified with `metro-minify-terser` from its code and map as the worker does. Times are single-threaded totals across all modules - medians of 15 rounds interleaving `main` and both diffs in this stack, Node 22.13.1 on an M5 Pro, with 95% bootstrap confidence intervals. Output was checked byte-identical in every run.
This branch has not been deployed
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.
When minifying, the transform worker converted Babel's decoded map to tuples and encoded it for the minifier, then decoded the minifier's output map with
source-map(toBabelSegments), converted it back to tuples and encoded it again as the stored VLQ map. Terser already builds that decoded map, and only encodes it whenresult.mapis read.This diff:
decodedMaptoMinifierResult. When a minifier provides it, the worker encodes the stored map straight from it withvlqMapFromBabelDecodedMap, as it already does for unminified modules. Otherwise it decodesmapwith@jridgewell/sourcemap-codec, which is already installed via@babel/generator.metro-minify-terserreturn Terser'sdecoded_mapasdecodedMap, and encodemaponly if it's read.result.mapto the minifier, rather than re-encoding it from tuples.MinifierResult.mapin favour ofdecodedMap, andtoBabelSegments,toSegmentTupleandtuplesFromBabelDecodedMapinmetro-source-map, which Metro no longer uses. The next diff removes them.Output is unchanged - across every module of our benchmark app*, the minified code, line count and stored map are byte-identical. Custom minifiers that only return
mapkeep working as before.Minifying the whole app is 10.4% faster (95% CI 7.4-11.4%; 21.3s -> 19.2s of CPU on our benchmark app*), from no longer converting the map to and from tuples, and from Terser no longer encoding its map at all:
mainmainChangelog
Test plan
New tests cover the worker using
decodedMapwithout readingmap, andmetro-minify-terserreturning Terser's decoded map and encodingmaponly on request.* Benchmark: Mattermost Mobile 2.45.0 (React Native 0.83.9), its release bundle split back into its 7,082 modules (5.13M mapping segments), each minified with
metro-minify-terserfrom its code and map as the worker does. Times are single-threaded totals across all modules - medians of 15 rounds interleavingmainand both diffs in this stack, Node 22.13.1 on an M5 Pro, with 95% bootstrap confidence intervals. Output was checked byte-identical in every run.