From 164f528838f3a146c82d68992d38316b9214f9b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Wed, 8 Jun 2022 17:39:57 +0900 Subject: [PATCH] refactor!: make terser an optional dependency (#8049) --- docs/config/build-options.md | 6 ++++ packages/plugin-legacy/README.md | 6 ++++ packages/plugin-legacy/package.json | 1 + packages/vite/package.json | 12 +++---- packages/vite/rollup.config.ts | 28 +-------------- packages/vite/src/node/plugins/css.ts | 8 ++--- packages/vite/src/node/plugins/terser.ts | 45 ++++++++++++++++-------- packages/vite/src/node/utils.ts | 12 +++++++ playground/legacy/package.json | 3 +- playground/preload/package.json | 3 +- playground/vitestSetup.ts | 4 ++- pnpm-lock.yaml | 6 ++-- 12 files changed, 76 insertions(+), 58 deletions(-) diff --git a/docs/config/build-options.md b/docs/config/build-options.md index 11b4685aa72555..f0f213d5d44428 100644 --- a/docs/config/build-options.md +++ b/docs/config/build-options.md @@ -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` diff --git a/packages/plugin-legacy/README.md b/packages/plugin-legacy/README.md index f7cd62b4dbe15c..4cec856f95d7af 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/plugin-legacy/package.json b/packages/plugin-legacy/package.json index 57786e8b2dcb86..0c77100be3b56f 100644 --- a/packages/plugin-legacy/package.json +++ b/packages/plugin-legacy/package.json @@ -42,6 +42,7 @@ "systemjs": "^6.12.1" }, "peerDependencies": { + "terser": "^5.4.0", "vite": "^3.0.0-alpha" }, "devDependencies": { diff --git a/packages/vite/package.json b/packages/vite/package.json index ee03a8fe615565..36b58b122fbf44 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -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", @@ -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", @@ -128,7 +124,8 @@ "peerDependencies": { "less": "*", "sass": "*", - "stylus": "*" + "stylus": "*", + "terser": "^5.4.0" }, "peerDependenciesMeta": { "sass": { @@ -139,6 +136,9 @@ }, "less": { "optional": true + }, + "terser": { + "optional": true } } } diff --git a/packages/vite/rollup.config.ts b/packages/vite/rollup.config.ts index 7232fa2484fff5..62f14f7790fa1d 100644 --- a/packages/vite/rollup.config.ts +++ b/packages/vite/rollup.config.ts @@ -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')`, @@ -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, @@ -245,8 +220,7 @@ export default (commandLineArgs: any) => { envConfig, clientConfig, createNodeConfig(isProduction), - createCjsConfig(isProduction), - ...(isProduction ? [terserConfig] : []) + createCjsConfig(isProduction) ]) } diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 89bbfd6c8c46f4..ce068ac6104f81 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -40,7 +40,8 @@ import { isRelativeBase, normalizePath, parseRequest, - processSrcSet + processSrcSet, + requireResolveFromRootWithFallback } from '../utils' import type { Logger } from '../logger' import { addToHTMLProxyTransformResult } from './html' @@ -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') { diff --git a/packages/vite/src/node/plugins/terser.ts b/packages/vite/src/node/plugins/terser.ts index 4e6b7b681a2cea..6362ca0b726fbf 100644 --- a/packages/vite/src/node/plugins/terser.ts +++ b/packages/vite/src/node/plugins/terser.ts @@ -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 } ) @@ -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, diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 121505f566f6e9..8d52dc2bfe1e53 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -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 diff --git a/playground/legacy/package.json b/playground/legacy/package.json index 4f11c234573f40..347e410e2bf4d6 100644 --- a/playground/legacy/package.json +++ b/playground/legacy/package.json @@ -12,6 +12,7 @@ }, "devDependencies": { "@vitejs/plugin-legacy": "workspace:*", - "express": "^4.18.1" + "express": "^4.18.1", + "terser": "^5.13.1" } } diff --git a/playground/preload/package.json b/playground/preload/package.json index f9318c47277b33..cb160b90482524 100644 --- a/playground/preload/package.json +++ b/playground/preload/package.json @@ -13,6 +13,7 @@ "vue-router": "^4.0.15" }, "devDependencies": { - "@vitejs/plugin-vue": "workspace:*" + "@vitejs/plugin-vue": "workspace:*", + "terser": "^5.13.1" } } diff --git a/playground/vitestSetup.ts b/playground/vitestSetup.ts index 70ac682d7bec27..13e7592ad380f2 100644 --- a/playground/vitestSetup.ts +++ b/playground/vitestSetup.ts @@ -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) } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 438930772bdd48..08492822578d23 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -274,7 +274,6 @@ importers: 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 @@ -337,7 +336,6 @@ importers: 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 @@ -505,9 +503,11 @@ importers: specifiers: '@vitejs/plugin-legacy': workspace:* express: ^4.18.1 + terser: ^5.13.1 devDependencies: '@vitejs/plugin-legacy': link:../../packages/plugin-legacy express: 4.18.1 + terser: 5.14.0 playground/lib: specifiers: {} @@ -689,6 +689,7 @@ importers: playground/preload: specifiers: '@vitejs/plugin-vue': workspace:* + terser: ^5.13.1 vue: ^3.2.37 vue-router: ^4.0.15 dependencies: @@ -696,6 +697,7 @@ importers: vue-router: 4.0.15_vue@3.2.37 devDependencies: '@vitejs/plugin-vue': link:../../packages/plugin-vue + terser: 5.14.0 playground/preserve-symlinks: specifiers: