-
Notifications
You must be signed in to change notification settings - Fork 165
fix: minify .bundle assets with terser-webpack-plugin 5.6.0 and newer - #1444
fix: minify .bundle assets with terser-webpack-plugin 5.6.0 and newer #1444giaBaoJS wants to merge 1 commit into
Conversation
@giaBaoJS is attempting to deploy a commit to the Callstack Team on Vercel.
A member of the Team first needs to authorize it.
🦋 Changeset detectedLatest commit: f2caf84 The changes in this PR will be included in the next version bump. This PR includes changesets to release 6 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
I understand the desire to fix this but honestly my personal preference will be to fix the rspack minifier rather than continuing to default to terser
MikitasK
commented
Aug 25, 2026
I understand the desire to fix this but honestly my personal preference will be to fix the rspack minifier rather than continuing to default to terser
makes sense 👍 since webpack is affected too, wdyt about keeping this fallback for webpack, but using/fixing native minimizer for rspack?
dannyhw
commented
Aug 25, 2026
but i believe terser still works for webpack, don't we already prefer the users version for that?
giaBaoJS
commented
Aug 26, 2026
|
I measured it before answering, and terser does not still work for webpack. Same root cause as Rspack. terser-webpack-plugin 5.6.0 added Real builds in
And preferring the user's version is what triggers this rather than what protects against it. webpack itself depends on Two things you may want before deciding on this PR:
new TerserPlugin({ test: /\.(js)?bundle(\?.*)?$/i, extractComments: false, minify: function repackTerserMinify(input, sourceMap, minimizerOptions, extractComments) { return require('terser-webpack-plugin').terserMinify( input, sourceMap, minimizerOptions, extractComments ); }, terserOptions: { format: { comments: false } }, }) I ran that with 5.6.1 in both the project and Re.Pack's own node_modules, so the default terser was a confirmed no-op and only the wrapper could do work: 1,969,176 bytes, byte identical to the 5.5.0 output. Happy to rewrite #1444 as that wrapper, or to close it if you would rather fix the Rspack minimizer first and handle webpack separately. |
dannyhw
commented
Aug 26, 2026
I see, seems like i may have misunderstood the issue then. What do you propose as the right solution? I.e do you think what you mentioned in your comment (wrapper) is better or the solution presented in the pr already?
giaBaoJS
commented
Aug 27, 2026
The wrapper, and I would rather rewrite this PR as that than merge what is here now.
What is here reacts to the symptom. It probes whether the installed plugin will refuse .bundle, then falls back to Re.Pack's own pinned copy and warns. It works, but it needs a capability probe, a fallback path and a warning string to do it, and the user ends up minifying with a different terser than the one in their lockfile.
The wrapper removes the condition instead. In 5.6.1 the dispatch is index.js:357:
if (typeof impl.filter !== "function" || ... impl.filter(name, info) !== false) {
so the filter is only consulted when the configured minify has one. Every built-in gets .filter assigned in utils.js, but a plain function does not, so passing our own named function makes every version from 5.5.0 up minify the asset, and the version that runs is the user's own. That is what "we prefer the user's version" was supposed to give them in the first place.
The judgement call worth saying out loud: we would be deliberately stepping around a filter the plugin author added. I think it is defensible here, since the doc comment on that option describes it as "return true when the minimizer supports the asset" and terser genuinely does support this asset. .bundle is JavaScript, and JS_FILE_RE is an extension heuristic rather than a capability test. But it is a call, not a fact, so it is yours to make rather than mine.
Say the word and I will rewrite it. If you would rather land the Rspack minimizer switch first and treat webpack on its own, I am equally happy to close this and open the wrapper as a separate small PR whenever it suits you.
dannyhw
commented
Aug 28, 2026
@giaBaoJS ok that makes sense, sounds like the wrapper would be better. If you have the capacity that would be really great if you add those changes
giaBaoJS
commented
Aug 29, 2026
|
Rewritten as the wrapper, pushed as a separate commit (878f75c) so the delta from the probe version is visible. Against
All four "after" bundles are byte-identical to their 5.5.0 baseline. Two things came out differently from the sketch in my earlier comment. The wrapper cannot say new Function( `return function repackTerserMinify(input, sourceMap, minimizerOptions, extractComments) { return require(${JSON.stringify(pluginPath)}).terserMinify(input, sourceMap, minimizerOptions, extractComments); }` )()
Tests rewritten. The old ones asserted which copy of the plugin got selected, which is machinery that no longer exists. The new ones put a fake project plugin through the same two steps the real one does, so it goes red because the asset comes back untouched, not because of a missing import. The warning has nothing left to report, since every version now minifies. Changeset description updated to match. |
878f75c to
a5bf5f5
Compare
giaBaoJS
commented
Sep 2, 2026
@dannyhw wrapper is in. It replaces the version check, the fallback to the bundled copy and the warning, so nothing version specific is left in getMinimizerConfig.
Measured on apps/tester-app with terser-webpack-plugin@5.6.1 resolved at the app root: the iOS production bundle goes from 4,846,598 bytes unminified to 1,969,176 on webpack, and from 4,822,812 to 1,969,836 on Rspack.
Since 5.6.0 the plugin only passes assets accepted by `terserMinify.filter` (`.js`, `.cjs`, `.mjs`) to terser, so Re.Pack's `index.bundle` and `*.chunk.bundle` are dropped before minification with no error or warning. Configure a `minify` wrapper instead. A plain function carries no filter, so every asset reaches terser on old and new plugin versions alike. The wrapper forwards `getMinimizerVersion` so terser's version stays in the chunk hash.
a5bf5f5 to
f2caf84
Compare
thanks @giaBaoJS this looks good, I will try it soon and get back to you. Sorry about the delay.
Uh oh!
There was an error while loading. Please reload this page.
Summary
Configure Re.Pack's own
minifywrapper onTerserPlugininstead of relying on the plugin's built-interserMinify.Why
terser-webpack-plugin5.6.0 added per-minimizer asset filters, and its terser implementation declaresterserMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name)(dist/utils.js:312). Re.Pack emitsindex.bundleand[name].chunk.bundle, so every asset is rejected by that filter and dropped fromassetsForMinifybefore minification runs. Nothing is reported: no error, no warning, and the asset is not flagged[minimized]in stats. Production bundles simply ship unminified.This hits webpack and Rspack alike, and pinning
terser-webpack-pluginin this repo does not cover it:getTerserPluginprefers the copy resolved from the project root, so any app that resolves 5.6.0 or newer still gets the silent no-op.Implementation
The plugin only consults
filteron the implementation it is handed. A plain function has nofilterproperty, so wrappingterserMinifymakes every version accept.bundleassets:terserMinifyis exported by 5.5.0 as well, so the wrapper behaves identically on old and new versions. The plugin serializes it into a jest worker (getWorker().transform(serialize(options))) and re-evaluates it there with its ownrequire, which is why the wrapper closes over nothing and resolvesterser-webpack-pluginby name.getMinimizerVersionis forwarded because the plugin reads it on the main thread to build the chunk hash (index.js:686on 5.5.0,index.js:724on 5.6.1). Without the forward that slot silently degrades to the"0.0.0"literal and a bareterserupgrade stops invalidating persistent cache. It is never read inside the worker, and serialization drops function statics anyway, so attaching it cannot affect the worker path.Closes #1390.
Validation
pnpm --filter @callstack/repack test: 35 suites, 315 tests passedpnpm --filter @callstack/repack typecheckandbiome check: cleangetMinimizerConfig.tswhile keeping the new tests turnsshould minify .bundle assets with a plugin that only accepts .jsred onexpect(implementation.filter).toBeUndefined()withReceived: [Function acceptsJsOnly], so it fails because the.bundleasset is filtered out, not on an import or compile errorapps/tester-appwithterser-webpack-plugin@5.6.1resolved at the app root,react-native bundle --platform ios --entry-file index.js --dev=false: