feat: Minify with terser (#789)

closes #742
This commit is contained in:
Puru Vijay 2023-02-07 16:49:00 +05:30 committed by GitHub
parent 31b2e7261f
commit fdd4dfa283
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 172 additions and 8 deletions

3
.gitignore vendored
View File

@ -1,4 +1,5 @@
node_modules
*.log
dist
.cache
.cache
playground

View File

@ -409,6 +409,16 @@ You can also minify the output, resulting into lower bundle sizes by using the `
tsup src/index.ts --minify
```
To use [Terser](https://github.com/terser/terser) instead of esbuild for minification, pass terser as argument value
```bash
tsup src/index.ts --minify terser
```
> NOTE: You must have terser installed. Install it with `npm install -D terser`
In `tsup.config.js`, you can pass `terserOptions` which will be passed to `terser.minify` as it is.
### Custom loader
Esbuild loader list:

View File

@ -62,6 +62,7 @@
"rollup-plugin-hashbang": "2.2.2",
"strip-json-comments": "4.0.0",
"svelte": "3.46.4",
"terser": "^5.16.0",
"ts-essentials": "9.1.2",
"tsconfig-paths": "3.12.0",
"tsup": "6.4.0",

85
pnpm-lock.yaml generated
View File

@ -33,6 +33,7 @@ specifiers:
strip-json-comments: 4.0.0
sucrase: ^3.20.3
svelte: 3.46.4
terser: ^5.16.0
tree-kill: ^1.2.2
ts-essentials: 9.1.2
tsconfig-paths: 3.12.0
@ -77,11 +78,12 @@ devDependencies:
rollup-plugin-hashbang: 2.2.2
strip-json-comments: 4.0.0
svelte: 3.46.4
terser: 5.16.0
ts-essentials: 9.1.2_typescript@4.6.3
tsconfig-paths: 3.12.0
tsup: 6.4.0_ien5tfzdggmpmrmtxysw6xj5lu
typescript: 4.6.3
vitest: 0.21.1
vitest: 0.21.1_terser@5.16.0
wait-for-expect: 3.0.2
packages:
@ -119,6 +121,43 @@ packages:
requiresBuild: true
optional: true
/@jridgewell/gen-mapping/0.3.2:
resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==}
engines: {node: '>=6.0.0'}
dependencies:
'@jridgewell/set-array': 1.1.2
'@jridgewell/sourcemap-codec': 1.4.14
'@jridgewell/trace-mapping': 0.3.17
dev: true
/@jridgewell/resolve-uri/3.1.0:
resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
engines: {node: '>=6.0.0'}
dev: true
/@jridgewell/set-array/1.1.2:
resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
engines: {node: '>=6.0.0'}
dev: true
/@jridgewell/source-map/0.3.2:
resolution: {integrity: sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==}
dependencies:
'@jridgewell/gen-mapping': 0.3.2
'@jridgewell/trace-mapping': 0.3.17
dev: true
/@jridgewell/sourcemap-codec/1.4.14:
resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
dev: true
/@jridgewell/trace-mapping/0.3.17:
resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==}
dependencies:
'@jridgewell/resolve-uri': 3.1.0
'@jridgewell/sourcemap-codec': 1.4.14
dev: true
/@nodelib/fs.scandir/2.1.5:
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@ -353,6 +392,12 @@ packages:
resolution: {integrity: sha512-Ku5+GPFa12S3W26Uwtw+xyrtIpaZsGYHH6zxNbZlstmlvMYSZRzOwzwsXbxlVUbHyUucctSyuFtu6bNxwYomIw==}
dev: true
/acorn/8.8.1:
resolution: {integrity: sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==}
engines: {node: '>=0.4.0'}
hasBin: true
dev: true
/ansi-styles/3.2.1:
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
engines: {node: '>=4'}
@ -398,6 +443,10 @@ packages:
dependencies:
fill-range: 7.0.1
/buffer-from/1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
dev: true
/bundle-require/3.1.2_esbuild@0.15.1:
resolution: {integrity: sha512-Of6l6JBAxiyQ5axFxUM6dYeP/W7X2Sozeo/4EYB9sJhL+dqL7TKjg+shwxp6jlu/6ZSERfsYtIpSJ1/x3XkAEA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@ -468,6 +517,10 @@ packages:
resolution: {integrity: sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==}
dev: true
/commander/2.20.3:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
dev: true
/commander/4.1.1:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
engines: {node: '>= 6'}
@ -1438,6 +1491,18 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/source-map-support/0.5.21:
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
dependencies:
buffer-from: 1.1.2
source-map: 0.6.1
dev: true
/source-map/0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
dev: true
/source-map/0.8.0-beta.0:
resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==}
engines: {node: '>= 8'}
@ -1492,6 +1557,17 @@ packages:
engines: {node: '>= 8'}
dev: true
/terser/5.16.0:
resolution: {integrity: sha512-KjTV81QKStSfwbNiwlBXfcgMcOloyuRdb62/iLFPGBcVNF4EXjhdYBhYHmbJpiBrVxZhDvltE11j+LBQUxEEJg==}
engines: {node: '>=10'}
hasBin: true
dependencies:
'@jridgewell/source-map': 0.3.2
acorn: 8.8.1
commander: 2.20.3
source-map-support: 0.5.21
dev: true
/thenify-all/1.6.0:
resolution: {integrity: sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=}
engines: {node: '>=0.8'}
@ -1602,7 +1678,7 @@ packages:
engines: {node: '>= 10.0.0'}
dev: true
/vite/3.0.3:
/vite/3.0.3_terser@5.16.0:
resolution: {integrity: sha512-sDIpIcl3mv1NUaSzZwiXGEy1ZoWwwC2vkxUHY6yiDacR6zf//ZFuBJrozO62gedpE43pmxnLATNR5IYUdAEkMQ==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
@ -1625,11 +1701,12 @@ packages:
postcss: 8.4.14
resolve: 1.22.1
rollup: 2.77.0
terser: 5.16.0
optionalDependencies:
fsevents: 2.3.2
dev: true
/vitest/0.21.1:
/vitest/0.21.1_terser@5.16.0:
resolution: {integrity: sha512-WBIxuFmIDPuK47GO6Lu9eNeRMqHj/FWL3dk73OHH3eyPPWPiu+UB3QHLkLK2PEggCqJW4FaWoWg8R68S7p9+9Q==}
engines: {node: '>=v14.16.0'}
hasBin: true
@ -1662,7 +1739,7 @@ packages:
local-pkg: 0.4.2
tinypool: 0.2.4
tinyspy: 1.0.0
vite: 3.0.3
vite: 3.0.3_terser@5.16.0
transitivePeerDependencies:
- less
- sass

View File

@ -20,7 +20,7 @@ export async function main(options: Options = {}) {
.option('--format <format>', 'Bundle format, "cjs", "iife", "esm"', {
default: 'cjs',
})
.option('--minify', 'Minify bundle')
.option('--minify [terser]', 'Minify bundle')
.option('--minify-whitespace', 'Minify whitespace')
.option('--minify-identifiers', 'Minify identifiers')
.option('--minify-syntax', 'Minify syntax')

View File

@ -257,7 +257,7 @@ export async function runEsbuild(
write: false,
splitting,
logLevel: 'error',
minify: options.minify,
minify: options.minify === 'terser' ? false : options.minify,
minifyWhitespace: options.minifyWhitespace,
minifyIdentifiers: options.minifyIdentifiers,
minifySyntax: options.minifySyntax,

View File

@ -20,6 +20,7 @@ import { es5 } from './plugins/es5'
import { sizeReporter } from './plugins/size-reporter'
import { treeShakingPlugin } from './plugins/tree-shaking'
import { copyPublicDir, isInPublicDir } from './lib/public-dir'
import { terserPlugin } from './plugins/terser'
export type { Format, Options, NormalizedOptions }
@ -260,6 +261,11 @@ export async function build(_options: Options) {
cjsSplitting(),
es5(),
sizeReporter(),
terserPlugin({
minifyOptions: options.minify,
format,
terserOptions: options.terserOptions,
}),
])
await runEsbuild(options, {
pluginContainer,

View File

@ -3,6 +3,7 @@ import type { InputOption } from 'rollup'
import { MarkRequired } from 'ts-essentials'
import type { Plugin } from './plugin'
import type { TreeshakingStrategy } from './plugins/tree-shaking'
import type { MinifyOptions } from 'terser'
export type Format = 'cjs' | 'esm' | 'iife'
@ -65,7 +66,8 @@ export type Options = {
* default to `node14`
*/
target?: string | string[]
minify?: boolean
minify?: boolean | 'terser'
terserOptions?: MinifyOptions
minifyWhitespace?: boolean
minifyIdentifiers?: boolean
minifySyntax?: boolean

67
src/plugins/terser.ts Normal file
View File

@ -0,0 +1,67 @@
import { MinifyOptions } from 'terser'
import { PrettyError } from '../errors'
import { createLogger } from '../log'
import { Format, Options } from '../options'
import { Plugin } from '../plugin'
import { localRequire } from '../utils'
const logger = createLogger()
export const terserPlugin = ({
minifyOptions,
format,
terserOptions = {},
}: {
minifyOptions: Options['minify']
format: Format
terserOptions?: MinifyOptions
}): Plugin => {
return {
name: 'terser',
async renderChunk(code, info) {
if (minifyOptions !== 'terser' || !/\.(cjs|js|mjs)$/.test(info.path))
return
const terser: typeof import('terser') | undefined = localRequire('terser')
if (!terser) {
throw new PrettyError(
'terser is required for terser minification. Please install it with `npm install terser -D`'
)
}
const { minify } = terser
const defaultOptions: MinifyOptions = {}
if (format === 'esm') {
defaultOptions.module = true
} else {
defaultOptions.toplevel = true
}
try {
const minifiedOutput = await minify(
{ [info.path]: code },
{ ...defaultOptions, ...terserOptions }
)
logger.info('TERSER', 'Minifying with Terser')
if (!minifiedOutput.code) {
logger.error('TERSER', 'Failed to minify with terser')
}
logger.success('TERSER', 'Terser Minification success')
return { code: minifiedOutput.code!, map: minifiedOutput.map }
} catch (e) {
logger.error('TERSER', 'Failed to minify with terser')
logger.error('TERSER', e)
}
return { code, map: info.map }
},
}
}