Skip to content

Commit

Permalink
refactor!: make terser an optional dependency (#8049)
Browse files Browse the repository at this point in the history
  • Loading branch information
sapphi-red authored Jun 8, 2022
1 parent 1f1ca5e commit 164f528
Show file tree
Hide file tree
Showing 12 changed files with 76 additions and 58 deletions.
6 changes: 6 additions & 0 deletions docs/config/build-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ Set to `false` to disable minification, or specify the minifier to use. The defa

Note the `build.minify` option is not available when using the `'es'` format in lib mode.

Terser must be installed when it is set to `'terser'`.

```sh
npm add -D terser
```

## build.terserOptions

- **Type:** `TerserOptions`
Expand Down
6 changes: 6 additions & 0 deletions packages/plugin-legacy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ export default {
}
```

Terser must be installed because plugin-legacy uses Terser for minification.

```sh
npm add -D terser
```

## Options

### `targets`
Expand Down
1 change: 1 addition & 0 deletions packages/plugin-legacy/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"systemjs": "^6.12.1"
},
"peerDependencies": {
"terser": "^5.4.0",
"vite": "^3.0.0-alpha"
},
"devDependencies": {
Expand Down
12 changes: 6 additions & 6 deletions packages/vite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@
"./client": {
"types": "./client.d.ts"
},
"./dist/client/*": "./dist/client/*",
"./terser": {
"require": "./dist/node-cjs/terser.cjs"
}
"./dist/client/*": "./dist/client/*"
},
"files": [
"bin",
Expand Down Expand Up @@ -118,7 +115,6 @@
"source-map-support": "^0.5.21",
"strip-ansi": "^6.0.1",
"strip-literal": "^0.3.0",
"terser": "^5.14.0",
"tsconfck": "^2.0.1",
"tslib": "^2.4.0",
"types": "link:./types",
Expand All @@ -128,7 +124,8 @@
"peerDependencies": {
"less": "*",
"sass": "*",
"stylus": "*"
"stylus": "*",
"terser": "^5.4.0"
},
"peerDependenciesMeta": {
"sass": {
Expand All @@ -139,6 +136,9 @@
},
"less": {
"optional": true
},
"terser": {
"optional": true
}
}
}
28 changes: 1 addition & 27 deletions packages/vite/rollup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,6 @@ function createNodePlugins(
// Shim them with eval() so rollup can skip these calls.
isProduction &&
shimDepsPlugin({
'plugins/terser.ts': {
src: `require.resolve('terser'`,
replacement: `require.resolve('vite/terser'`
},
// chokidar -> fsevents
'fsevents-handler.js': {
src: `require('fsevents')`,
Expand Down Expand Up @@ -191,27 +187,6 @@ function createNodeConfig(isProduction: boolean) {
})
}

/**
* 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.
*/
const terserConfig = defineConfig({
...sharedNodeOptions,
output: {
...sharedNodeOptions.output,
entryFileNames: `node-cjs/[name].cjs`,
exports: 'default',
format: 'cjs',
sourcemap: false
},
input: {
// eslint-disable-next-line node/no-restricted-require
terser: require.resolve('terser')
},
plugins: [nodeResolve(), commonjs()]
})

function createCjsConfig(isProduction: boolean) {
return defineConfig({
...sharedNodeOptions,
Expand Down Expand Up @@ -245,8 +220,7 @@ export default (commandLineArgs: any) => {
envConfig,
clientConfig,
createNodeConfig(isProduction),
createCjsConfig(isProduction),
...(isProduction ? [terserConfig] : [])
createCjsConfig(isProduction)
])
}

Expand Down
8 changes: 3 additions & 5 deletions packages/vite/src/node/plugins/css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ import {
isRelativeBase,
normalizePath,
parseRequest,
processSrcSet
processSrcSet,
requireResolveFromRootWithFallback
} from '../utils'
import type { Logger } from '../logger'
import { addToHTMLProxyTransformResult } from './html'
Expand Down Expand Up @@ -1296,10 +1297,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') {
Expand Down
45 changes: 30 additions & 15 deletions packages/vite/src/node/plugins/terser.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,40 @@
import { dirname } from 'path'
import { fileURLToPath } from 'url'
import { Worker } from 'okie'
import type { Terser } from 'types/terser'
import type { Plugin } from '../plugin'
import type { ResolvedConfig } from '..'
import { requireResolveFromRootWithFallback } from '../utils'

// TODO: use import()
const _dirname = dirname(fileURLToPath(import.meta.url))
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. Since Vite v3, terser has become an optional dependency. You need to 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(
async (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, no-restricted-globals
const terserPath = require.resolve('terser', {
paths: [basedir]
})
// eslint-disable-next-line no-restricted-globals
return require(terserPath).minify(code, options) as Terser.MinifyOutput
async (
terserPath: string,
code: string,
options: Terser.MinifyOptions
) => {
// test fails when using `import`. maybe related: https://github.com/nodejs/node/issues/43205
// eslint-disable-next-line no-restricted-globals -- this function runs inside cjs
const terser = require(terserPath)
return terser.minify(code, options) as Terser.MinifyOutput
}
)

Expand Down Expand Up @@ -50,7 +64,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,
Expand Down
12 changes: 12 additions & 0 deletions packages/vite/src/node/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,18 @@ export function getHash(text: Buffer | string): string {
return createHash('sha256').update(text).digest('hex').substring(0, 8)
}

export const requireResolveFromRootWithFallback = (
root: string,
id: string
): string => {
// Search in the root directory first, and fallback to the default require paths.
const fallbackPaths = _require.resolve.paths?.(id) || []
const path = _require.resolve(id, {
paths: [root, ...fallbackPaths]
})
return path
}

// Based on node-graceful-fs

// The ISC License
Expand Down
3 changes: 2 additions & 1 deletion playground/legacy/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
},
"devDependencies": {
"@vitejs/plugin-legacy": "workspace:*",
"express": "^4.18.1"
"express": "^4.18.1",
"terser": "^5.13.1"
}
}
3 changes: 2 additions & 1 deletion playground/preload/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"vue-router": "^4.0.15"
},
"devDependencies": {
"@vitejs/plugin-vue": "workspace:*"
"@vitejs/plugin-vue": "workspace:*",
"terser": "^5.13.1"
}
}
4 changes: 3 additions & 1 deletion playground/vitestSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,9 @@ export async function startDefaultServe() {
build: {
// esbuild do not minify ES lib output since that would remove pure annotations and break tree-shaking
// skip transpilation during tests to make it faster
target: 'esnext'
target: 'esnext',
// tests are flaky when `emptyOutDir` is `true`
emptyOutDir: false
},
customLogger: createInMemoryLogger(serverLogs)
}
Expand Down
6 changes: 4 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 164f528

Please sign in to comment.