diff --git a/packages/vite/src/node/plugins/terser.ts b/packages/vite/src/node/plugins/terser.ts index ab17c3fb98dd47..bd64b0a6910662 100644 --- a/packages/vite/src/node/plugins/terser.ts +++ b/packages/vite/src/node/plugins/terser.ts @@ -96,12 +96,6 @@ export function terserPlugin(config: ResolvedConfig): Plugin { 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') { - return null - } - // Lazy load worker. worker ||= makeWorker() @@ -110,6 +104,14 @@ export function terserPlugin(config: ResolvedConfig): Plugin { const res = await worker.run(terserPath, code, { safari10: true, ...terserOptions, + format: { + ...terserOptions.format, + // For ES lib mode, preserve comments to keep pure annotations for tree-shaking + preserve_annotations: + config.build.lib && outputOptions.format === 'es' + ? true + : terserOptions.format?.preserve_annotations, + }, sourceMap: !!outputOptions.sourcemap, module: outputOptions.format.startsWith('es'), toplevel: outputOptions.format === 'cjs', diff --git a/playground/lib/__tests__/lib.spec.ts b/playground/lib/__tests__/lib.spec.ts index 6ce3e1092df6b8..c9983e0b9f2047 100644 --- a/playground/lib/__tests__/lib.spec.ts +++ b/playground/lib/__tests__/lib.spec.ts @@ -82,6 +82,21 @@ describe.runIf(isBuild)('build', () => { expect(umd).toMatch('process.env.NODE_ENV') }) + test('debugger statements are removed by terser for es', () => { + const terserEs = readFile('dist/terser/my-lib-custom-filename.js') + expect(terserEs).not.toMatch('debugger') + }) + + test('pure annotations are not removed by terser for es', () => { + const terserEs = readFile('dist/terser/my-lib-custom-filename.js') + expect(terserEs).toMatch(/[@#]__PURE__/) + }) + + test('pure annotations are removed by terser for non-es output', () => { + const terserIife = readFile('dist/terser/my-lib-custom-filename.iife.js') + expect(terserIife).not.toMatch(/[@#]__PURE__/) + }) + test('single entry with css', () => { const css = readFile('dist/css-single-entry/test-my-lib.css') const js = readFile('dist/css-single-entry/test-my-lib.js') diff --git a/playground/lib/__tests__/serve.ts b/playground/lib/__tests__/serve.ts index 0401264ecb1ece..ffedf250d31f62 100644 --- a/playground/lib/__tests__/serve.ts +++ b/playground/lib/__tests__/serve.ts @@ -108,6 +108,12 @@ export async function serve(): Promise<{ close(): Promise }> { configFile: path.resolve(__dirname, '../vite.css-code-split.config.js'), }) + await build({ + root: rootDir, + logLevel: 'warn', // output esbuild warns + configFile: path.resolve(__dirname, '../vite.terser.config.js'), + }) + // start static file server const serve = sirv(path.resolve(rootDir, 'dist')) const httpServer = http.createServer((req, res) => { diff --git a/playground/lib/src/main.js b/playground/lib/src/main.js index 8be8ec37e635ee..e9a311b1af8c6f 100644 --- a/playground/lib/src/main.js +++ b/playground/lib/src/main.js @@ -1,4 +1,4 @@ -export default function myLib(sel) { +export default /* @__PURE__ */ Object.assign(function myLib(sel) { // Force esbuild spread helpers (https://github.com/evanw/esbuild/issues/951) console.log({ ...'foo' }) @@ -9,7 +9,10 @@ export default function myLib(sel) { // make sure umd helper has been moved to the right position console.log(`amd function(){ "use strict"; }`) -} + + // eslint-disable-next-line no-debugger + debugger +}) // For triggering unhandled global esbuild helpers in previous regex-based implementation for injection ;(function () {})()?.foo diff --git a/playground/lib/vite.terser.config.js b/playground/lib/vite.terser.config.js new file mode 100644 index 00000000000000..a9e04a0661fe9e --- /dev/null +++ b/playground/lib/vite.terser.config.js @@ -0,0 +1,24 @@ +import path from 'node:path' +import { defineConfig } from 'vite' +import baseConfig from './vite.config' + +export default defineConfig({ + ...baseConfig, + build: { + ...baseConfig.build, + minify: 'terser', + terserOptions: { + compress: { + drop_debugger: true, + }, + }, + outDir: 'dist/terser', + lib: { + ...baseConfig.build.lib, + entry: path.resolve(__dirname, 'src/main.js'), + formats: ['es', 'iife'], + }, + }, + plugins: [], + cacheDir: 'node_modules/.vite-terser', +})