From 9f1b7e64e11ad758fc29a8f9ca83d8170074d72c Mon Sep 17 00:00:00 2001 From: Aron Griffis Date: Mon, 27 Dec 2021 10:36:33 -0500 Subject: [PATCH] fix: don't force terser on non-legacy (fix #6266) This fixes two misbehaviors of the legacy plugin: 1. Respect {minify: false} for legacy assets. 2. Don't inflict es2019/terser on non-legacy chunks. For the first problem, we could have fixed by checking for false in viteLegacyPlugin.config(). Unfortunately that would have left the second problem unsolved. Without adding significant complexity to the config, there's no easy way to use different minifiers in the build depending on the individual chunk. So instead we include terserPlugin() whenever minify is enabled, even true or 'esbuild', then check the actual configuration in the plugin. This allows the legacy plugin to inject its special override, leaving all the non-legacy stuff intact and uncomplicated. See also, previous attempts: #5157 #5168 --- .../legacy/__tests__/legacy.spec.ts | 17 +++++++++++++- packages/plugin-legacy/index.js | 22 +++++-------------- packages/vite/src/node/build.ts | 2 +- packages/vite/src/node/plugins/terser.ts | 11 ++++++++++ 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/packages/playground/legacy/__tests__/legacy.spec.ts b/packages/playground/legacy/__tests__/legacy.spec.ts index f1e317246ee852..6b5885a6af8d36 100644 --- a/packages/playground/legacy/__tests__/legacy.spec.ts +++ b/packages/playground/legacy/__tests__/legacy.spec.ts @@ -1,4 +1,4 @@ -import { isBuild, readManifest, untilUpdated } from '../../testUtils' +import { findAssetFile, isBuild, readManifest, untilUpdated } from '../../testUtils' test('should work', async () => { expect(await page.textContent('#app')).toMatch('Hello') @@ -53,4 +53,19 @@ if (isBuild) { '../../../vite/legacy-polyfills' ) }) + + test('should minify legacy chunks with terser', async () => { + // This is a ghetto heuristic, but terser output seems to reliably start + // with one of the following, and non-terser output (including unminified or + // ebuild-minified) does not! + const terserPatt = /^(?:!function|System.register)/ + + expect(findAssetFile(/chunk-async-legacy/)).toMatch(terserPatt) + expect(findAssetFile(/chunk-async\./)).not.toMatch(terserPatt) + expect(findAssetFile(/immutable-chunk-legacy/)).toMatch(terserPatt) + expect(findAssetFile(/immutable-chunk\./)).not.toMatch(terserPatt) + expect(findAssetFile(/index-legacy/)).toMatch(terserPatt) + expect(findAssetFile(/index\./)).not.toMatch(terserPatt) + expect(findAssetFile(/polyfills-legacy/)).toMatch(terserPatt) + }) } diff --git a/packages/plugin-legacy/index.js b/packages/plugin-legacy/index.js index 2267cd9cf82b42..d030d7fc43344e 100644 --- a/packages/plugin-legacy/index.js +++ b/packages/plugin-legacy/index.js @@ -105,23 +105,6 @@ function viteLegacyPlugin(options = {}) { name: 'vite:legacy-generate-polyfill-chunk', apply: 'build', - config() { - return { - build: { - minify: 'terser' - } - } - }, - - configResolved(config) { - if (!config.build.ssr && genLegacy && config.build.minify === 'esbuild') { - throw new Error( - `Can't use esbuild as the minifier when targeting legacy browsers ` + - `because esbuild minification is not legacy safe.` - ) - } - }, - async generateBundle(opts, bundle) { if (config.build.ssr) { return @@ -297,6 +280,11 @@ function viteLegacyPlugin(options = {}) { // legacy-unsafe code - e.g. rewriting object properties into shorthands opts.__vite_skip_esbuild__ = true + // @ts-ignore force terser for legacy chunks. This only takes effect if + // minification isn't disabled, because that leaves out the terser plugin + // entirely. + opts.__vite_force_terser__ = true + const needPolyfills = options.polyfills !== false && !Array.isArray(options.polyfills) diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index e64cc618135bb0..b22af65e07e895 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -365,7 +365,7 @@ export function resolveBuildPlugins(config: ResolvedConfig): { post: [ buildImportAnalysisPlugin(config), buildEsbuildPlugin(config), - ...(options.minify === 'terser' ? [terserPlugin(config)] : []), + ...(options.minify ? [terserPlugin(config)] : []), ...(options.manifest ? [manifestPlugin(config)] : []), ...(options.ssrManifest ? [ssrManifestPlugin(config)] : []), buildReporterPlugin(config), diff --git a/packages/vite/src/node/plugins/terser.ts b/packages/vite/src/node/plugins/terser.ts index eb8864a2e00001..e51f63a3e7544a 100644 --- a/packages/vite/src/node/plugins/terser.ts +++ b/packages/vite/src/node/plugins/terser.ts @@ -21,6 +21,17 @@ export function terserPlugin(config: ResolvedConfig): Plugin { name: 'vite:terser', async renderChunk(code, _chunk, outputOptions) { + // This plugin is included for any non-false value of config.build.minify, + // so that normal chunks can use the preferred minifier, and legacy chunks + // can use terser. + if ( + config.build.minify !== 'terser' && + // @ts-ignore injected by @vitejs/plugin-legacy + !outputOptions.__vite_force_terser__ + ) { + return null + } + // Do not minify ES lib output since that would remove pure annotations // and break tree-shaking if (config.build.lib && outputOptions.format === 'es') {