Skip to content

Commit 3cbfcfc

Browse files
committed
chore: make terser an optional dependency
BREAKING CHANGE: `terser` must be installed for minification with terser
1 parent aeb5b74 commit 3cbfcfc

File tree

10 files changed

+63
-50
lines changed

10 files changed

+63
-50
lines changed

docs/config/index.md

+6
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,12 @@ export default defineConfig({
861861

862862
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)
863863

864+
terser must be installed when it is set to `'terser'`.
865+
866+
```sh
867+
npm add -D terser
868+
```
869+
864870
Note the `build.minify` option is not available when using the `'es'` format in lib mode.
865871

866872
### build.terserOptions

packages/plugin-legacy/README.md

+6
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ export default {
2727
}
2828
```
2929

30+
terser must be installed because plugin-legacy uses terser for minification.
31+
32+
```sh
33+
npm add -D terser
34+
```
35+
3036
## Options
3137

3238
### `targets`

packages/vite/package.json

+5-2
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,6 @@
113113
"source-map-support": "^0.5.21",
114114
"strip-ansi": "^6.0.1",
115115
"strip-literal": "^0.3.0",
116-
"terser": "^5.13.1",
117116
"tsconfck": "^1.2.2",
118117
"tslib": "^2.4.0",
119118
"types": "link:./types",
@@ -123,7 +122,8 @@
123122
"peerDependencies": {
124123
"less": "*",
125124
"sass": "*",
126-
"stylus": "*"
125+
"stylus": "*",
126+
"terser": "^5.13.1"
127127
},
128128
"peerDependenciesMeta": {
129129
"sass": {
@@ -134,6 +134,9 @@
134134
},
135135
"less": {
136136
"optional": true
137+
},
138+
"terser": {
139+
"optional": true
137140
}
138141
}
139142
}

packages/vite/rollup.config.js

+1-30
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,6 @@ const createNodeConfig = (isProduction) => {
149149
// Shim them with eval() so rollup can skip these calls.
150150
isProduction &&
151151
shimDepsPlugin({
152-
'plugins/terser.ts': {
153-
src: `require.resolve('terser'`,
154-
replacement: `require.resolve('vite/dist/node/terser'`
155-
},
156152
// chokidar -> fsevents
157153
'fsevents-handler.js': {
158154
src: `require('fsevents')`,
@@ -192,26 +188,6 @@ const createNodeConfig = (isProduction) => {
192188
return nodeConfig
193189
}
194190

195-
/**
196-
* Terser needs to be run inside a worker, so it cannot be part of the main
197-
* bundle. We produce a separate bundle for it and shims plugin/terser.ts to
198-
* use the production path during build.
199-
*
200-
* @type { import('rollup').RollupOptions }
201-
*/
202-
const terserConfig = {
203-
...sharedNodeOptions,
204-
output: {
205-
...sharedNodeOptions.output,
206-
exports: 'default',
207-
sourcemap: false
208-
},
209-
input: {
210-
terser: require.resolve('terser')
211-
},
212-
plugins: [nodeResolve(), commonjs()]
213-
}
214-
215191
/**
216192
* @type { (deps: Record<string, { src?: string, replacement: string, pattern?: RegExp }>) => import('rollup').Plugin }
217193
*/
@@ -388,10 +364,5 @@ export default (commandLineArgs) => {
388364
const isDev = commandLineArgs.watch
389365
const isProduction = !isDev
390366

391-
return [
392-
envConfig,
393-
clientConfig,
394-
createNodeConfig(isProduction),
395-
...(isProduction ? [terserConfig] : [])
396-
]
367+
return [envConfig, clientConfig, createNodeConfig(isProduction)]
397368
}

packages/vite/src/node/plugins/css.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import {
1212
normalizePath,
1313
processSrcSet,
1414
parseRequest,
15-
combineSourcemaps
15+
combineSourcemaps,
16+
requireResolveFromRootWithFallback
1617
} from '../utils'
1718
import type { Plugin } from '../plugin'
1819
import type { ResolvedConfig } from '../config'
@@ -1227,10 +1228,7 @@ function loadPreprocessor(lang: PreprocessLang, root: string): any {
12271228
return loadedPreprocessors[lang]
12281229
}
12291230
try {
1230-
// Search for the preprocessor in the root directory first, and fall back
1231-
// to the default require paths.
1232-
const fallbackPaths = require.resolve.paths?.(lang) || []
1233-
const resolved = require.resolve(lang, { paths: [root, ...fallbackPaths] })
1231+
const resolved = requireResolveFromRootWithFallback(root, lang)
12341232
return (loadedPreprocessors[lang] = require(resolved))
12351233
} catch (e) {
12361234
if (e.code === 'MODULE_NOT_FOUND') {

packages/vite/src/node/plugins/terser.ts

+21-9
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,29 @@ import type { Plugin } from '../plugin'
22
import { Worker } from 'okie'
33
import type { Terser } from 'types/terser'
44
import type { ResolvedConfig } from '..'
5+
import { requireResolveFromRootWithFallback } from '../utils'
6+
7+
let terserPath: string | undefined
8+
const loadTerserPath = (root: string) => {
9+
if (terserPath) return terserPath
10+
try {
11+
terserPath = requireResolveFromRootWithFallback(root, 'terser')
12+
} catch (e) {
13+
if (e.code === 'MODULE_NOT_FOUND') {
14+
throw new Error(`terser not found. Did you install it?`)
15+
} else {
16+
const message = new Error(`terser failed to load:\n${e.message}`)
17+
message.stack = e.stack + '\n' + message.stack
18+
throw message
19+
}
20+
}
21+
return terserPath
22+
}
523

624
export function terserPlugin(config: ResolvedConfig): Plugin {
725
const makeWorker = () =>
826
new Worker(
9-
(basedir: string, code: string, options: Terser.MinifyOptions) => {
10-
// when vite is linked, the worker thread won't share the same resolve
11-
// root with vite itself, so we have to pass in the basedir and resolve
12-
// terser first.
13-
// eslint-disable-next-line node/no-restricted-require
14-
const terserPath = require.resolve('terser', {
15-
paths: [basedir]
16-
})
27+
(terserPath: string, code: string, options: Terser.MinifyOptions) => {
1728
return require(terserPath).minify(code, options) as Terser.MinifyOutput
1829
}
1930
)
@@ -44,7 +55,8 @@ export function terserPlugin(config: ResolvedConfig): Plugin {
4455
// Lazy load worker.
4556
worker ||= makeWorker()
4657

47-
const res = await worker.run(__dirname, code, {
58+
const terserPath = loadTerserPath(config.root)
59+
const res = await worker.run(terserPath, code, {
4860
safari10: true,
4961
...config.build.terserOptions,
5062
sourceMap: !!outputOptions.sourcemap,

packages/vite/src/node/utils.ts

+13
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,19 @@ export function parseRequest(id: string): Record<string, string> | null {
748748

749749
export const blankReplacer = (match: string) => ' '.repeat(match.length)
750750

751+
export const requireResolveFromRootWithFallback = (
752+
root: string,
753+
id: string
754+
) => {
755+
// Search in the root directory first, and fallback to the default require paths.
756+
const fallbackPaths = require.resolve.paths?.(id) || []
757+
// eslint-disable-next-line node/no-missing-require
758+
const path = require.resolve(id, {
759+
paths: [root, ...fallbackPaths]
760+
})
761+
return path
762+
}
763+
751764
// Based on node-graceful-fs
752765

753766
// The ISC License

playground/legacy/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
},
1212
"devDependencies": {
1313
"@vitejs/plugin-legacy": "workspace:*",
14-
"express": "^4.18.1"
14+
"express": "^4.18.1",
15+
"terser": "^5.13.1"
1516
}
1617
}

playground/preload/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"vue-router": "^4.0.15"
1414
},
1515
"devDependencies": {
16-
"@vitejs/plugin-vue": "workspace:*"
16+
"@vitejs/plugin-vue": "workspace:*",
17+
"terser": "^5.13.1"
1718
}
1819
}

pnpm-lock.yaml

+4-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)