diff --git a/docs/config/index.md b/docs/config/index.md index 5862419405aa89..ba03277901f1c1 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -861,6 +861,12 @@ export default defineConfig({ Set to `false` to disable minification, or specify the minifier to use. The default is [esbuild](https://github.com/evanw/esbuild) which is 20 ~ 40x faster than terser and only 1 ~ 2% worse compression. [Benchmarks](https://github.com/privatenumber/minification-benchmarks) + terser must be installed when it is set to `'terser'`. + + ```sh + npm add -D terser + ``` + Note the `build.minify` option is not available when using the `'es'` format in lib mode. ### build.terserOptions diff --git a/packages/plugin-legacy/README.md b/packages/plugin-legacy/README.md index fc4167f4f1010f..bbfd3ac154b1ea 100644 --- a/packages/plugin-legacy/README.md +++ b/packages/plugin-legacy/README.md @@ -27,6 +27,12 @@ export default { } ``` +terser must be installed because plugin-legacy uses terser for minification. + +```sh +npm add -D terser +``` + ## Options ### `targets` diff --git a/packages/vite/package.json b/packages/vite/package.json index 2e930f0f1f6741..73ffcdeac7553d 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -113,7 +113,6 @@ "source-map-support": "^0.5.21", "strip-ansi": "^6.0.1", "strip-literal": "^0.3.0", - "terser": "^5.13.1", "tsconfck": "^1.2.2", "tslib": "^2.4.0", "types": "link:./types", @@ -123,7 +122,8 @@ "peerDependencies": { "less": "*", "sass": "*", - "stylus": "*" + "stylus": "*", + "terser": "^5.13.1" }, "peerDependenciesMeta": { "sass": { @@ -134,6 +134,9 @@ }, "less": { "optional": true + }, + "terser": { + "optional": true } } } diff --git a/packages/vite/rollup.config.js b/packages/vite/rollup.config.js index 93f4f33bdec398..62e0167477914d 100644 --- a/packages/vite/rollup.config.js +++ b/packages/vite/rollup.config.js @@ -149,10 +149,6 @@ const createNodeConfig = (isProduction) => { // Shim them with eval() so rollup can skip these calls. isProduction && shimDepsPlugin({ - 'plugins/terser.ts': { - src: `require.resolve('terser'`, - replacement: `require.resolve('vite/dist/node/terser'` - }, // chokidar -> fsevents 'fsevents-handler.js': { src: `require('fsevents')`, @@ -192,26 +188,6 @@ const createNodeConfig = (isProduction) => { return nodeConfig } -/** - * Terser needs to be run inside a worker, so it cannot be part of the main - * bundle. We produce a separate bundle for it and shims plugin/terser.ts to - * use the production path during build. - * - * @type { import('rollup').RollupOptions } - */ -const terserConfig = { - ...sharedNodeOptions, - output: { - ...sharedNodeOptions.output, - exports: 'default', - sourcemap: false - }, - input: { - terser: require.resolve('terser') - }, - plugins: [nodeResolve(), commonjs()] -} - /** * @type { (deps: Record) => import('rollup').Plugin } */ @@ -388,10 +364,5 @@ export default (commandLineArgs) => { const isDev = commandLineArgs.watch const isProduction = !isDev - return [ - envConfig, - clientConfig, - createNodeConfig(isProduction), - ...(isProduction ? [terserConfig] : []) - ] + return [envConfig, clientConfig, createNodeConfig(isProduction)] } diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 3ffd93bb9f9910..44327c77d056e0 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -12,7 +12,8 @@ import { normalizePath, processSrcSet, parseRequest, - combineSourcemaps + combineSourcemaps, + requireResolveFromRootWithFallback } from '../utils' import type { Plugin } from '../plugin' import type { ResolvedConfig } from '../config' @@ -1227,10 +1228,7 @@ function loadPreprocessor(lang: PreprocessLang, root: string): any { return loadedPreprocessors[lang] } try { - // Search for the preprocessor in the root directory first, and fall back - // to the default require paths. - const fallbackPaths = require.resolve.paths?.(lang) || [] - const resolved = require.resolve(lang, { paths: [root, ...fallbackPaths] }) + const resolved = requireResolveFromRootWithFallback(root, lang) return (loadedPreprocessors[lang] = require(resolved)) } catch (e) { if (e.code === 'MODULE_NOT_FOUND') { diff --git a/packages/vite/src/node/plugins/terser.ts b/packages/vite/src/node/plugins/terser.ts index 29f4d5c172ce67..412a68db055d02 100644 --- a/packages/vite/src/node/plugins/terser.ts +++ b/packages/vite/src/node/plugins/terser.ts @@ -2,18 +2,29 @@ import type { Plugin } from '../plugin' import { Worker } from 'okie' import type { Terser } from 'types/terser' import type { ResolvedConfig } from '..' +import { requireResolveFromRootWithFallback } from '../utils' + +let terserPath: string | undefined +const loadTerserPath = (root: string) => { + if (terserPath) return terserPath + try { + terserPath = requireResolveFromRootWithFallback(root, 'terser') + } catch (e) { + if (e.code === 'MODULE_NOT_FOUND') { + throw new Error(`terser not found. Did you install it?`) + } else { + const message = new Error(`terser failed to load:\n${e.message}`) + message.stack = e.stack + '\n' + message.stack + throw message + } + } + return terserPath +} export function terserPlugin(config: ResolvedConfig): Plugin { const makeWorker = () => new Worker( - (basedir: string, code: string, options: Terser.MinifyOptions) => { - // when vite is linked, the worker thread won't share the same resolve - // root with vite itself, so we have to pass in the basedir and resolve - // terser first. - // eslint-disable-next-line node/no-restricted-require - const terserPath = require.resolve('terser', { - paths: [basedir] - }) + (terserPath: string, code: string, options: Terser.MinifyOptions) => { return require(terserPath).minify(code, options) as Terser.MinifyOutput } ) @@ -44,7 +55,8 @@ export function terserPlugin(config: ResolvedConfig): Plugin { // Lazy load worker. worker ||= makeWorker() - const res = await worker.run(__dirname, code, { + const terserPath = loadTerserPath(config.root) + const res = await worker.run(terserPath, code, { safari10: true, ...config.build.terserOptions, sourceMap: !!outputOptions.sourcemap, diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index ddcaced9832bc1..87ef6da955988f 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -748,6 +748,19 @@ export function parseRequest(id: string): Record | null { export const blankReplacer = (match: string) => ' '.repeat(match.length) +export const requireResolveFromRootWithFallback = ( + root: string, + id: string +) => { + // Search in the root directory first, and fallback to the default require paths. + const fallbackPaths = require.resolve.paths?.(id) || [] + // eslint-disable-next-line node/no-missing-require + const path = require.resolve(id, { + paths: [root, ...fallbackPaths] + }) + return path +} + // Based on node-graceful-fs // The ISC License diff --git a/playground/legacy/package.json b/playground/legacy/package.json index efc3292fcad872..f59d9c5cf8b739 100644 --- a/playground/legacy/package.json +++ b/playground/legacy/package.json @@ -11,6 +11,7 @@ }, "devDependencies": { "@vitejs/plugin-legacy": "workspace:*", - "express": "^4.17.1" + "express": "^4.17.1", + "terser": "^5.13.1" } } diff --git a/playground/preload/package.json b/playground/preload/package.json index 6caaed63c2151d..d7c256c9ce6f7e 100644 --- a/playground/preload/package.json +++ b/playground/preload/package.json @@ -13,6 +13,7 @@ "vue-router": "^4.0.0" }, "devDependencies": { - "@vitejs/plugin-vue": "workspace:*" + "@vitejs/plugin-vue": "workspace:*", + "terser": "^5.13.1" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 388027b85b2541..7876707c8fcdc8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -232,7 +232,6 @@ importers: source-map-support: ^0.5.21 strip-ansi: ^6.0.1 strip-literal: ^0.3.0 - terser: ^5.13.1 tsconfck: ^1.2.2 tslib: ^2.4.0 types: link:./types @@ -307,7 +306,6 @@ importers: source-map-support: 0.5.21 strip-ansi: 6.0.1 strip-literal: 0.3.0 - terser: 5.13.1 tsconfck: 1.2.2_typescript@4.5.4 tslib: 2.4.0 types: link:types @@ -471,9 +469,11 @@ importers: specifiers: '@vitejs/plugin-legacy': workspace:* express: ^4.17.1 + terser: ^5.13.1 devDependencies: '@vitejs/plugin-legacy': link:../../packages/plugin-legacy express: 4.17.2 + terser: 5.13.1 playground/lib: specifiers: {} @@ -640,6 +640,7 @@ importers: playground/preload: specifiers: '@vitejs/plugin-vue': workspace:* + terser: ^5.13.1 vue: ^3.2.25 vue-router: ^4.0.0 dependencies: @@ -647,6 +648,7 @@ importers: vue-router: 4.0.12_vue@3.2.33 devDependencies: '@vitejs/plugin-vue': link:../../packages/plugin-vue + terser: 5.13.1 playground/preserve-symlinks: specifiers: