You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

934 lines
21 KiB

3 months ago
  1. <div align="center">
  2. <a href="https://github.com/webpack/webpack">
  3. <img width="200" height="200" src="https://webpack.js.org/assets/icon-square-big.svg">
  4. </a>
  5. </div>
  6. [![npm][npm]][npm-url]
  7. [![node][node]][node-url]
  8. [![tests][tests]][tests-url]
  9. [![cover][cover]][cover-url]
  10. [![discussion][discussion]][discussion-url]
  11. [![size][size]][size-url]
  12. # terser-webpack-plugin
  13. This plugin uses [terser](https://github.com/terser/terser) to minify/minimize your JavaScript.
  14. ## Getting Started
  15. Webpack v5 comes with the latest `terser-webpack-plugin` out of the box. If you are using Webpack v5 or above and wish to customize the options, you will still need to install `terser-webpack-plugin`. Using Webpack v4, you have to install `terser-webpack-plugin` v4.
  16. To begin, you'll need to install `terser-webpack-plugin`:
  17. ```console
  18. npm install terser-webpack-plugin --save-dev
  19. ```
  20. or
  21. ```console
  22. yarn add -D terser-webpack-plugin
  23. ```
  24. or
  25. ```console
  26. pnpm add -D terser-webpack-plugin
  27. ```
  28. Then add the plugin to your `webpack` config. For example:
  29. **webpack.config.js**
  30. ```js
  31. const TerserPlugin = require("terser-webpack-plugin");
  32. module.exports = {
  33. optimization: {
  34. minimize: true,
  35. minimizer: [new TerserPlugin()],
  36. },
  37. };
  38. ```
  39. And run `webpack` via your preferred method.
  40. ## Note about source maps
  41. **Works only with `source-map`, `inline-source-map`, `hidden-source-map` and `nosources-source-map` values for the [`devtool`](https://webpack.js.org/configuration/devtool/) option.**
  42. Why?
  43. - `eval` wraps modules in `eval("string")` and the minimizer does not handle strings.
  44. - `cheap` has not column information and minimizer generate only a single line, which leave only a single mapping.
  45. Using supported `devtool` values enable source map generation.
  46. ## Options
  47. - **[`test`](#test)**
  48. - **[`include`](#include)**
  49. - **[`exclude`](#exclude)**
  50. - **[`parallel`](#parallel)**
  51. - **[`minify`](#minify)**
  52. - **[`terserOptions`](#terseroptions)**
  53. - **[`extractComments`](#extractcomments)**
  54. ### `test`
  55. Type:
  56. ```ts
  57. type test = string | RegExp | Array<string | RegExp>;
  58. ```
  59. Default: `/\.m?js(\?.*)?$/i`
  60. Test to match files against.
  61. **webpack.config.js**
  62. ```js
  63. module.exports = {
  64. optimization: {
  65. minimize: true,
  66. minimizer: [
  67. new TerserPlugin({
  68. test: /\.js(\?.*)?$/i,
  69. }),
  70. ],
  71. },
  72. };
  73. ```
  74. ### `include`
  75. Type:
  76. ```ts
  77. type include = string | RegExp | Array<string | RegExp>;
  78. ```
  79. Default: `undefined`
  80. Files to include.
  81. **webpack.config.js**
  82. ```js
  83. module.exports = {
  84. optimization: {
  85. minimize: true,
  86. minimizer: [
  87. new TerserPlugin({
  88. include: /\/includes/,
  89. }),
  90. ],
  91. },
  92. };
  93. ```
  94. ### `exclude`
  95. Type:
  96. ```ts
  97. type exclude = string | RegExp | Array<string | RegExp>;
  98. ```
  99. Default: `undefined`
  100. Files to exclude.
  101. **webpack.config.js**
  102. ```js
  103. module.exports = {
  104. optimization: {
  105. minimize: true,
  106. minimizer: [
  107. new TerserPlugin({
  108. exclude: /\/excludes/,
  109. }),
  110. ],
  111. },
  112. };
  113. ```
  114. ### `parallel`
  115. Type:
  116. ```ts
  117. type parallel = boolean | number;
  118. ```
  119. Default: `true`
  120. Use multi-process parallel running to improve the build speed.
  121. Default number of concurrent runs: `os.cpus().length - 1`.
  122. > **Note**
  123. >
  124. > Parallelization can speedup your build significantly and is therefore **highly recommended**.
  125. > **Warning**
  126. >
  127. > If you use **Circle CI** or any other environment that doesn't provide real available count of CPUs then you need to setup explicitly number of CPUs to avoid `Error: Call retries were exceeded` (see [#143](https://github.com/webpack-contrib/terser-webpack-plugin/issues/143), [#202](https://github.com/webpack-contrib/terser-webpack-plugin/issues/202)).
  128. #### `boolean`
  129. Enable/disable multi-process parallel running.
  130. **webpack.config.js**
  131. ```js
  132. module.exports = {
  133. optimization: {
  134. minimize: true,
  135. minimizer: [
  136. new TerserPlugin({
  137. parallel: true,
  138. }),
  139. ],
  140. },
  141. };
  142. ```
  143. #### `number`
  144. Enable multi-process parallel running and set number of concurrent runs.
  145. **webpack.config.js**
  146. ```js
  147. module.exports = {
  148. optimization: {
  149. minimize: true,
  150. minimizer: [
  151. new TerserPlugin({
  152. parallel: 4,
  153. }),
  154. ],
  155. },
  156. };
  157. ```
  158. ### `minify`
  159. Type:
  160. ```ts
  161. type minify = (
  162. input: {
  163. [file: string]: string;
  164. },
  165. sourceMap: import("@jridgewell/trace-mapping").SourceMapInput | undefined,
  166. minifyOptions: {
  167. module?: boolean | undefined;
  168. ecma?: import("terser").ECMA | undefined;
  169. },
  170. extractComments:
  171. | boolean
  172. | "all"
  173. | "some"
  174. | RegExp
  175. | ((
  176. astNode: any,
  177. comment: {
  178. value: string;
  179. type: "comment1" | "comment2" | "comment3" | "comment4";
  180. pos: number;
  181. line: number;
  182. col: number;
  183. }
  184. ) => boolean)
  185. | {
  186. condition?:
  187. | boolean
  188. | "all"
  189. | "some"
  190. | RegExp
  191. | ((
  192. astNode: any,
  193. comment: {
  194. value: string;
  195. type: "comment1" | "comment2" | "comment3" | "comment4";
  196. pos: number;
  197. line: number;
  198. col: number;
  199. }
  200. ) => boolean)
  201. | undefined;
  202. filename?: string | ((fileData: any) => string) | undefined;
  203. banner?:
  204. | string
  205. | boolean
  206. | ((commentsFile: string) => string)
  207. | undefined;
  208. }
  209. | undefined
  210. ) => Promise<{
  211. code: string;
  212. map?: import("@jridgewell/trace-mapping").SourceMapInput | undefined;
  213. errors?: (string | Error)[] | undefined;
  214. warnings?: (string | Error)[] | undefined;
  215. extractedComments?: string[] | undefined;
  216. }>;
  217. ```
  218. Default: `TerserPlugin.terserMinify`
  219. Allows you to override default minify function.
  220. By default plugin uses [terser](https://github.com/terser/terser) package.
  221. Useful for using and testing unpublished versions or forks.
  222. > **Warning**
  223. >
  224. > **Always use `require` inside `minify` function when `parallel` option enabled**.
  225. **webpack.config.js**
  226. ```js
  227. // Can be async
  228. const minify = (input, sourceMap, minimizerOptions, extractsComments) => {
  229. // The `minimizerOptions` option contains option from the `terserOptions` option
  230. // You can use `minimizerOptions.myCustomOption`
  231. // Custom logic for extract comments
  232. const { map, code } = require("uglify-module") // Or require('./path/to/uglify-module')
  233. .minify(input, {
  234. /* Your options for minification */
  235. });
  236. return { map, code, warnings: [], errors: [], extractedComments: [] };
  237. };
  238. // Used to regenerate `fullhash`/`chunkhash` between different implementation
  239. // Example: you fix a bug in custom minimizer/custom function, but unfortunately webpack doesn't know about it, so you will get the same fullhash/chunkhash
  240. // to avoid this you can provide version of your custom minimizer
  241. // You don't need if you use only `contenthash`
  242. minify.getMinimizerVersion = () => {
  243. let packageJson;
  244. try {
  245. // eslint-disable-next-line global-require, import/no-extraneous-dependencies
  246. packageJson = require("uglify-module/package.json");
  247. } catch (error) {
  248. // Ignore
  249. }
  250. return packageJson && packageJson.version;
  251. };
  252. module.exports = {
  253. optimization: {
  254. minimize: true,
  255. minimizer: [
  256. new TerserPlugin({
  257. terserOptions: {
  258. myCustomOption: true,
  259. },
  260. minify,
  261. }),
  262. ],
  263. },
  264. };
  265. ```
  266. ### `terserOptions`
  267. Type:
  268. ```ts
  269. type terserOptions = {
  270. compress?: boolean | CompressOptions;
  271. ecma?: ECMA;
  272. enclose?: boolean | string;
  273. ie8?: boolean;
  274. keep_classnames?: boolean | RegExp;
  275. keep_fnames?: boolean | RegExp;
  276. mangle?: boolean | MangleOptions;
  277. module?: boolean;
  278. nameCache?: object;
  279. format?: FormatOptions;
  280. /** @deprecated */
  281. output?: FormatOptions;
  282. parse?: ParseOptions;
  283. safari10?: boolean;
  284. sourceMap?: boolean | SourceMapOptions;
  285. toplevel?: boolean;
  286. };
  287. ```
  288. Default: [default](https://github.com/terser/terser#minify-options)
  289. Terser [options](https://github.com/terser/terser#minify-options).
  290. **webpack.config.js**
  291. ```js
  292. module.exports = {
  293. optimization: {
  294. minimize: true,
  295. minimizer: [
  296. new TerserPlugin({
  297. terserOptions: {
  298. ecma: undefined,
  299. parse: {},
  300. compress: {},
  301. mangle: true, // Note `mangle.properties` is `false` by default.
  302. module: false,
  303. // Deprecated
  304. output: null,
  305. format: null,
  306. toplevel: false,
  307. nameCache: null,
  308. ie8: false,
  309. keep_classnames: undefined,
  310. keep_fnames: false,
  311. safari10: false,
  312. },
  313. }),
  314. ],
  315. },
  316. };
  317. ```
  318. ### `extractComments`
  319. Type:
  320. ```ts
  321. type extractComments =
  322. | boolean
  323. | string
  324. | RegExp
  325. | ((
  326. astNode: any,
  327. comment: {
  328. value: string;
  329. type: "comment1" | "comment2" | "comment3" | "comment4";
  330. pos: number;
  331. line: number;
  332. col: number;
  333. }
  334. ) => boolean)
  335. | {
  336. condition?:
  337. | boolean
  338. | "all"
  339. | "some"
  340. | RegExp
  341. | ((
  342. astNode: any,
  343. comment: {
  344. value: string;
  345. type: "comment1" | "comment2" | "comment3" | "comment4";
  346. pos: number;
  347. line: number;
  348. col: number;
  349. }
  350. ) => boolean)
  351. | undefined;
  352. filename?: string | ((fileData: any) => string) | undefined;
  353. banner?:
  354. | string
  355. | boolean
  356. | ((commentsFile: string) => string)
  357. | undefined;
  358. };
  359. ```
  360. Default: `true`
  361. Whether comments shall be extracted to a separate file, (see [details](https://github.com/webpack/webpack/commit/71933e979e51c533b432658d5e37917f9e71595a)).
  362. By default extract only comments using `/^\**!|@preserve|@license|@cc_on/i` regexp condition and remove remaining comments.
  363. If the original file is named `foo.js`, then the comments will be stored to `foo.js.LICENSE.txt`.
  364. The `terserOptions.format.comments` option specifies whether the comment will be preserved, i.e. it is possible to preserve some comments (e.g. annotations) while extracting others or even preserving comments that have been extracted.
  365. #### `boolean`
  366. Enable/disable extracting comments.
  367. **webpack.config.js**
  368. ```js
  369. module.exports = {
  370. optimization: {
  371. minimize: true,
  372. minimizer: [
  373. new TerserPlugin({
  374. extractComments: true,
  375. }),
  376. ],
  377. },
  378. };
  379. ```
  380. #### `string`
  381. Extract `all` or `some` (use `/^\**!|@preserve|@license|@cc_on/i` RegExp) comments.
  382. **webpack.config.js**
  383. ```js
  384. module.exports = {
  385. optimization: {
  386. minimize: true,
  387. minimizer: [
  388. new TerserPlugin({
  389. extractComments: "all",
  390. }),
  391. ],
  392. },
  393. };
  394. ```
  395. #### `RegExp`
  396. All comments that match the given expression will be extracted to the separate file.
  397. **webpack.config.js**
  398. ```js
  399. module.exports = {
  400. optimization: {
  401. minimize: true,
  402. minimizer: [
  403. new TerserPlugin({
  404. extractComments: /@extract/i,
  405. }),
  406. ],
  407. },
  408. };
  409. ```
  410. #### `function`
  411. All comments that match the given expression will be extracted to the separate file.
  412. **webpack.config.js**
  413. ```js
  414. module.exports = {
  415. optimization: {
  416. minimize: true,
  417. minimizer: [
  418. new TerserPlugin({
  419. extractComments: (astNode, comment) => {
  420. if (/@extract/i.test(comment.value)) {
  421. return true;
  422. }
  423. return false;
  424. },
  425. }),
  426. ],
  427. },
  428. };
  429. ```
  430. #### `object`
  431. Allow to customize condition for extract comments, specify extracted file name and banner.
  432. **webpack.config.js**
  433. ```js
  434. module.exports = {
  435. optimization: {
  436. minimize: true,
  437. minimizer: [
  438. new TerserPlugin({
  439. extractComments: {
  440. condition: /^\**!|@preserve|@license|@cc_on/i,
  441. filename: (fileData) => {
  442. // The "fileData" argument contains object with "filename", "basename", "query" and "hash"
  443. return `${fileData.filename}.LICENSE.txt${fileData.query}`;
  444. },
  445. banner: (licenseFile) => {
  446. return `License information can be found in ${licenseFile}`;
  447. },
  448. },
  449. }),
  450. ],
  451. },
  452. };
  453. ```
  454. ##### `condition`
  455. Type:
  456. ```ts
  457. type condition =
  458. | boolean
  459. | "all"
  460. | "some"
  461. | RegExp
  462. | ((
  463. astNode: any,
  464. comment: {
  465. value: string;
  466. type: "comment1" | "comment2" | "comment3" | "comment4";
  467. pos: number;
  468. line: number;
  469. col: number;
  470. }
  471. ) => boolean)
  472. | undefined;
  473. ```
  474. Condition what comments you need extract.
  475. **webpack.config.js**
  476. ```js
  477. module.exports = {
  478. optimization: {
  479. minimize: true,
  480. minimizer: [
  481. new TerserPlugin({
  482. extractComments: {
  483. condition: "some",
  484. filename: (fileData) => {
  485. // The "fileData" argument contains object with "filename", "basename", "query" and "hash"
  486. return `${fileData.filename}.LICENSE.txt${fileData.query}`;
  487. },
  488. banner: (licenseFile) => {
  489. return `License information can be found in ${licenseFile}`;
  490. },
  491. },
  492. }),
  493. ],
  494. },
  495. };
  496. ```
  497. ##### `filename`
  498. Type:
  499. ```ts
  500. type filename = string | ((fileData: any) => string) | undefined;
  501. ```
  502. Default: `[file].LICENSE.txt[query]`
  503. Available placeholders: `[file]`, `[query]` and `[filebase]` (`[base]` for webpack 5).
  504. The file where the extracted comments will be stored.
  505. Default is to append the suffix `.LICENSE.txt` to the original filename.
  506. > **Warning**
  507. >
  508. > We highly recommend using the `txt` extension. Using `js`/`cjs`/`mjs` extensions may conflict with existing assets which leads to broken code.
  509. **webpack.config.js**
  510. ```js
  511. module.exports = {
  512. optimization: {
  513. minimize: true,
  514. minimizer: [
  515. new TerserPlugin({
  516. extractComments: {
  517. condition: /^\**!|@preserve|@license|@cc_on/i,
  518. filename: "extracted-comments.js",
  519. banner: (licenseFile) => {
  520. return `License information can be found in ${licenseFile}`;
  521. },
  522. },
  523. }),
  524. ],
  525. },
  526. };
  527. ```
  528. ##### `banner`
  529. Type:
  530. ```ts
  531. type banner = string | boolean | ((commentsFile: string) => string) | undefined;
  532. ```
  533. Default: `/*! For license information please see ${commentsFile} */`
  534. The banner text that points to the extracted file and will be added on top of the original file.
  535. Can be `false` (no banner), a `String`, or a `Function<(string) -> String>` that will be called with the filename where extracted comments have been stored.
  536. Will be wrapped into comment.
  537. **webpack.config.js**
  538. ```js
  539. module.exports = {
  540. optimization: {
  541. minimize: true,
  542. minimizer: [
  543. new TerserPlugin({
  544. extractComments: {
  545. condition: true,
  546. filename: (fileData) => {
  547. // The "fileData" argument contains object with "filename", "basename", "query" and "hash"
  548. return `${fileData.filename}.LICENSE.txt${fileData.query}`;
  549. },
  550. banner: (commentsFile) => {
  551. return `My custom banner about license information ${commentsFile}`;
  552. },
  553. },
  554. }),
  555. ],
  556. },
  557. };
  558. ```
  559. ## Examples
  560. ### Preserve Comments
  561. Extract all legal comments (i.e. `/^\**!|@preserve|@license|@cc_on/i`) and preserve `/@license/i` comments.
  562. **webpack.config.js**
  563. ```js
  564. module.exports = {
  565. optimization: {
  566. minimize: true,
  567. minimizer: [
  568. new TerserPlugin({
  569. terserOptions: {
  570. format: {
  571. comments: /@license/i,
  572. },
  573. },
  574. extractComments: true,
  575. }),
  576. ],
  577. },
  578. };
  579. ```
  580. ### Remove Comments
  581. If you avoid building with comments, use this config:
  582. **webpack.config.js**
  583. ```js
  584. module.exports = {
  585. optimization: {
  586. minimize: true,
  587. minimizer: [
  588. new TerserPlugin({
  589. terserOptions: {
  590. format: {
  591. comments: false,
  592. },
  593. },
  594. extractComments: false,
  595. }),
  596. ],
  597. },
  598. };
  599. ```
  600. ### [`uglify-js`](https://github.com/mishoo/UglifyJS)
  601. [`UglifyJS`](https://github.com/mishoo/UglifyJS) is a JavaScript parser, minifier, compressor and beautifier toolkit.
  602. **webpack.config.js**
  603. ```js
  604. module.exports = {
  605. optimization: {
  606. minimize: true,
  607. minimizer: [
  608. new TerserPlugin({
  609. minify: TerserPlugin.uglifyJsMinify,
  610. // `terserOptions` options will be passed to `uglify-js`
  611. // Link to options - https://github.com/mishoo/UglifyJS#minify-options
  612. terserOptions: {},
  613. }),
  614. ],
  615. },
  616. };
  617. ```
  618. ### [`swc`](https://github.com/swc-project/swc)
  619. [`swc`](https://github.com/swc-project/swc) is a super-fast compiler written in rust; producing widely-supported javascript from modern standards and typescript.
  620. > **Warning**
  621. >
  622. > the `extractComments` option is not supported and all comments will be removed by default, it will be fixed in future
  623. **webpack.config.js**
  624. ```js
  625. module.exports = {
  626. optimization: {
  627. minimize: true,
  628. minimizer: [
  629. new TerserPlugin({
  630. minify: TerserPlugin.swcMinify,
  631. // `terserOptions` options will be passed to `swc` (`@swc/core`)
  632. // Link to options - https://swc.rs/docs/config-js-minify
  633. terserOptions: {},
  634. }),
  635. ],
  636. },
  637. };
  638. ```
  639. ### [`esbuild`](https://github.com/evanw/esbuild)
  640. [`esbuild`](https://github.com/evanw/esbuild) is an extremely fast JavaScript bundler and minifier.
  641. > **Warning**
  642. >
  643. > the `extractComments` option is not supported and all legal comments (i.e. copyright, licenses and etc) will be preserved
  644. **webpack.config.js**
  645. ```js
  646. module.exports = {
  647. optimization: {
  648. minimize: true,
  649. minimizer: [
  650. new TerserPlugin({
  651. minify: TerserPlugin.esbuildMinify,
  652. // `terserOptions` options will be passed to `esbuild`
  653. // Link to options - https://esbuild.github.io/api/#minify
  654. // Note: the `minify` options is true by default (and override other `minify*` options), so if you want to disable the `minifyIdentifiers` option (or other `minify*` options) please use:
  655. // terserOptions: {
  656. // minify: false,
  657. // minifyWhitespace: true,
  658. // minifyIdentifiers: false,
  659. // minifySyntax: true,
  660. // },
  661. terserOptions: {},
  662. }),
  663. ],
  664. },
  665. };
  666. ```
  667. ### Custom Minify Function
  668. Override default minify function - use `uglify-js` for minification.
  669. **webpack.config.js**
  670. ```js
  671. module.exports = {
  672. optimization: {
  673. minimize: true,
  674. minimizer: [
  675. new TerserPlugin({
  676. minify: (file, sourceMap) => {
  677. // https://github.com/mishoo/UglifyJS2#minify-options
  678. const uglifyJsOptions = {
  679. /* your `uglify-js` package options */
  680. };
  681. if (sourceMap) {
  682. uglifyJsOptions.sourceMap = {
  683. content: sourceMap,
  684. };
  685. }
  686. return require("uglify-js").minify(file, uglifyJsOptions);
  687. },
  688. }),
  689. ],
  690. },
  691. };
  692. ```
  693. ### Typescript
  694. With default terser minify function:
  695. ```ts
  696. module.exports = {
  697. optimization: {
  698. minimize: true,
  699. minimizer: [
  700. new TerserPlugin({
  701. terserOptions: {
  702. compress: true,
  703. },
  704. }),
  705. ],
  706. },
  707. };
  708. ```
  709. With built-in minify functions:
  710. ```ts
  711. import type { JsMinifyOptions as SwcOptions } from "@swc/core";
  712. import type { MinifyOptions as UglifyJSOptions } from "uglify-js";
  713. import type { TransformOptions as EsbuildOptions } from "esbuild";
  714. import type { MinifyOptions as TerserOptions } from "terser";
  715. module.exports = {
  716. optimization: {
  717. minimize: true,
  718. minimizer: [
  719. new TerserPlugin<SwcOptions>({
  720. minify: TerserPlugin.swcMinify,
  721. terserOptions: {
  722. // `swc` options
  723. },
  724. }),
  725. new TerserPlugin<UglifyJSOptions>({
  726. minify: TerserPlugin.uglifyJsMinify,
  727. terserOptions: {
  728. // `uglif-js` options
  729. },
  730. }),
  731. new TerserPlugin<EsbuildOptions>({
  732. minify: TerserPlugin.esbuildMinify,
  733. terserOptions: {
  734. // `esbuild` options
  735. },
  736. }),
  737. // Alternative usage:
  738. new TerserPlugin<TerserOptions>({
  739. minify: TerserPlugin.terserMinify,
  740. terserOptions: {
  741. // `terser` options
  742. },
  743. }),
  744. ],
  745. },
  746. };
  747. ```
  748. ## Contributing
  749. Please take a moment to read our contributing guidelines if you haven't yet done so.
  750. [CONTRIBUTING](./.github/CONTRIBUTING.md)
  751. ## License
  752. [MIT](./LICENSE)
  753. [npm]: https://img.shields.io/npm/v/terser-webpack-plugin.svg
  754. [npm-url]: https://npmjs.com/package/terser-webpack-plugin
  755. [node]: https://img.shields.io/node/v/terser-webpack-plugin.svg
  756. [node-url]: https://nodejs.org
  757. [tests]: https://github.com/webpack-contrib/terser-webpack-plugin/workflows/terser-webpack-plugin/badge.svg
  758. [tests-url]: https://github.com/webpack-contrib/terser-webpack-plugin/actions
  759. [cover]: https://codecov.io/gh/webpack-contrib/terser-webpack-plugin/branch/master/graph/badge.svg
  760. [cover-url]: https://codecov.io/gh/webpack-contrib/terser-webpack-plugin
  761. [discussion]: https://img.shields.io/github/discussions/webpack/webpack
  762. [discussion-url]: https://github.com/webpack/webpack/discussions
  763. [size]: https://packagephobia.now.sh/badge?p=terser-webpack-plugin
  764. [size-url]: https://packagephobia.now.sh/result?p=terser-webpack-plugin