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 |
makes sense 👍 since webpack is affected too, wdyt about keeping this fallback for webpack, but using/fixing native minimizer for rspack? |
|
but i believe terser still works for webpack, don't we already prefer the users version for that? |
|
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. |
|
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? |
|
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 The wrapper removes the condition instead. In 5.6.1 the dispatch is if (typeof impl.filter !== "function" || ... impl.filter(name, info) !== false) {so the filter is only consulted when the configured 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. 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. |
|
@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 |
|
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
|
@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 Measured on |
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
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: