Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: webpack-contrib/terser-webpack-plugin
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: aa12914c34f3eac7280052dad3de465a163630d7
Choose a base ref
...
head repository: webpack-contrib/terser-webpack-plugin
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 491b2fd25dd42723ba3368ff15a8aa32f647d8a4
Choose a head ref

Commits on Sep 2, 2019

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    ec9450c View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    c896de6 View commit details
  3. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    4f88a97 View commit details
  4. chore(defaults): update (#127)

    BREAKING CHANGE: minimum require Node.js version is `8.9.0`
    evilebottnawi authored Sep 2, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    edbd3e0 View commit details
  5. fix: make extractComments API more consistent (#129)

    BREAKING CHANGE: using the `extractComments.condition` option with `true` value extract only `some` comments
    evilebottnawi authored Sep 2, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    37d2df0 View commit details

Commits on Sep 3, 2019

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    41a9fff View commit details
  2. feat: enable the parallel option by default (#131)

    BREAKING CHANGE: the `parallel` option is `true` by default
    evilebottnawi authored Sep 3, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    7b342af View commit details

Commits on Sep 4, 2019

  1. feat: enable the cache option by default (#132)

    BREAKING CHANGE: the `cache` option is `true` by default
    evilebottnawi authored Sep 4, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    960d249 View commit details
  2. feat: enable the extractComments option by default

    BREAKING CHANGE: the `extractComments` option is `true` by default
    alexander-akait committed Sep 4, 2019
    Copy the full SHA
    ad2471c View commit details
  3. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    84fed6b View commit details
  4. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    8b88b39 View commit details

Commits on Sep 5, 2019

  1. docs: fix examples (#135)

    gaokun authored and evilebottnawi committed Sep 5, 2019
    Copy the full SHA
    a169061 View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    cc1c081 View commit details
  3. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    527a055 View commit details
  4. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    0537591 View commit details
  5. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    080baff View commit details
  6. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    dd37ca1 View commit details
  7. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    dcf875b View commit details
  8. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    ae56c17 View commit details
  9. Copy the full SHA
    42a0f9c View commit details

Commits on Sep 6, 2019

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    815e533 View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    6613a97 View commit details
  3. Copy the full SHA
    0b9681d View commit details

Commits on Sep 9, 2019

  1. Copy the full SHA
    cf91c43 View commit details

Commits on Sep 12, 2019

  1. docs: clarify default value for sourceMap option (#151)

    See more context here:
    #150
    koggdal authored and evilebottnawi committed Sep 12, 2019
    Copy the full SHA
    5821456 View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    cea7243 View commit details
  3. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    5fe3337 View commit details

Commits on Sep 13, 2019

  1. docs: add missing breaking change for 2.0.0 (#155)

    See more in:
    #150
    koggdal authored and evilebottnawi committed Sep 13, 2019
    Copy the full SHA
    efbd383 View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    2b4d2a4 View commit details
  3. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    aba8ba7 View commit details

Commits on Sep 16, 2019

  1. refactor: code (#158)

    evilebottnawi authored Sep 16, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    9bbe824 View commit details
  2. chore(release): 2.1.0

    alexander-akait committed Sep 16, 2019
    Copy the full SHA
    d5df728 View commit details

Commits on Sep 24, 2019

  1. refactor: code (#165)

    evilebottnawi authored Sep 24, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    6355274 View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    6bdee64 View commit details

Commits on Sep 27, 2019

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    62b3677 View commit details
  2. chore(release): 2.1.1

    alexander-akait committed Sep 27, 2019
    Copy the full SHA
    8c68450 View commit details

Commits on Sep 28, 2019

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    4c4b1f1 View commit details
  2. chore(release): 2.1.2

    alexander-akait committed Sep 28, 2019
    Copy the full SHA
    f4eec75 View commit details

Commits on Oct 9, 2019

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    c2efe18 View commit details

Commits on Oct 10, 2019

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    7e1d370 View commit details
  2. chore(release): 2.1.3

    alexander-akait committed Oct 10, 2019
    Copy the full SHA
    92c56ad View commit details

Commits on Oct 21, 2019

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    9a0a575 View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    f4c47aa View commit details
  3. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    d01c1b5 View commit details
  4. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    a11e66b View commit details
  5. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    504ea8b View commit details

Commits on Oct 22, 2019

  1. chore(release): 2.2.0

    alexander-akait committed Oct 22, 2019
    Copy the full SHA
    cd80227 View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    0e9b780 View commit details
  3. chore(release): 2.2.1

    alexander-akait committed Oct 22, 2019
    Copy the full SHA
    1b03b6b View commit details

Commits on Dec 6, 2019

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    3c80be8 View commit details
Showing with 38,126 additions and 17,523 deletions.
  1. +1 −2 .editorconfig
  2. +4 −1 .eslintrc.js
  3. +1 −1 .github/ISSUE_TEMPLATE.md
  4. +95 −0 .github/workflows/nodejs.yml
  5. +1 −0 .gitignore
  6. +1 −0 .husky/.gitignore
  7. +4 −0 .husky/commit-msg
  8. +4 −0 .husky/pre-commit
  9. +0 −5 .prettierrc.js
  10. +369 −0 CHANGELOG.md
  11. +75 −230 README.md
  12. +0 −201 azure-pipelines.yml
  13. +2 −2 babel.config.js
  14. +1 −1 commitlint.config.js
  15. +0 −6 husky.config.js
  16. +3 −0 jest.config.js
  17. +2 −2 lint-staged.config.js
  18. +22,521 −8,116 package-lock.json
  19. +52 −46 package.json
  20. +0 −112 src/TaskRunner.js
  21. +1 −1 src/cjs.js
  22. +556 −299 src/index.js
  23. +205 −111 src/minify.js
  24. +57 −75 src/options.json
  25. +0 −21 src/worker.js
  26. +1,544 −132 test/TerserPlugin.test.js
  27. +2,620 −49 test/__snapshots__/TerserPlugin.test.js.snap
  28. +327 −587 test/__snapshots__/cache-option.test.js.snap
  29. +0 −108 test/__snapshots__/chunkFilter-option.test.js.snap
  30. +210 −510 test/__snapshots__/exclude-option.test.js.snap
  31. +5,256 −1,133 test/__snapshots__/extractComments-option.test.js.snap
  32. +213 −521 test/__snapshots__/include-option.test.js.snap
  33. +64 −403 test/__snapshots__/minify-option.test.js.snap
  34. +0 −231 test/__snapshots__/parallel-option-failure.test.js.snap
  35. +102 −35 test/__snapshots__/parallel-option.test.js.snap
  36. +0 −161 test/__snapshots__/sourceMap-option.test.js.snap
  37. +0 −153 test/__snapshots__/supports-multicompiler.test.js.snap
  38. +545 −565 test/__snapshots__/terserOptions-option.test.js.snap
  39. +632 −1,082 test/__snapshots__/test-option.test.js.snap
  40. +178 −0 test/__snapshots__/validate-options.test.js.snap
  41. +0 −209 test/__snapshots__/validation.test.js.snap
  42. +0 −48 test/__snapshots__/warningsFilter-option.test.js.snap
  43. +141 −25 test/__snapshots__/worker.test.js.snap
  44. +395 −280 test/cache-option.test.js
  45. +0 −44 test/chunkFilter-option.test.js
  46. +5 −5 test/cjs.test.js
  47. +37 −69 test/exclude-option.test.js
  48. +502 −226 test/extractComments-option.test.js
  49. +7 −0 test/fixtures/asset-resource.js
  50. +2 −0 test/fixtures/comments.js
  51. +2 −0 test/fixtures/copy.cjs
  52. +2 −0 test/fixtures/copy.js
  53. +2 −0 test/fixtures/copy.mjs
  54. +5 −0 test/fixtures/emit-loader.js
  55. +1 −0 test/fixtures/emitted.js
  56. 0 test/fixtures/empty.js
  57. +7 −4 test/fixtures/entry.mjs
  58. +7 −0 test/fixtures/file-loader.js
  59. +1 −0 test/fixtures/file.js
  60. +7 −0 test/fixtures/file.worker.js
  61. +2 −0 test/fixtures/import-export/entry.js
  62. +2 −0 test/fixtures/minify/es6.js
  63. +1 −0 test/fixtures/shebang-1.js
  64. +3 −0 test/fixtures/shebang.js
  65. +10 −0 test/fixtures/worker-loader.js
  66. +0 −96 test/helpers.js
  67. +16 −0 test/helpers/BrokenCodePlugin.js
  68. +31 −0 test/helpers/EmitNewAsset.js
  69. +16 −0 test/helpers/ExistingCommentsFile.js
  70. +24 −0 test/helpers/ModifyExistingAsset.js
  71. +10 −0 test/helpers/compile.js
  72. +9 −0 test/helpers/countPlugins.js
  73. +22 −0 test/helpers/execute.js
  74. +31 −0 test/helpers/getCompiler.js
  75. +3 −0 test/helpers/getErrors.js
  76. +3 −0 test/helpers/getWarnings.js
  77. +29 −0 test/helpers/index.js
  78. +18 −0 test/helpers/normalizeErrors.js
  79. +23 −0 test/helpers/readAsset.js
  80. +11 −0 test/helpers/readAssets.js
  81. +28 −0 test/helpers/snapshotResolver.js
  82. +37 −69 test/include-option.test.js
  83. +189 −196 test/minify-option.test.js
  84. +0 −105 test/parallel-option-failure.test.js
  85. +223 −89 test/parallel-option.test.js
  86. +0 −177 test/sourceMap-option.test.js
  87. +0 −102 test/supports-multicompiler.test.js
  88. +261 −481 test/terserOptions-option.test.js
  89. +71 −111 test/test-option.test.js
  90. +16 −69 test/{validation.test.js → validate-options.test.js}
  91. +0 −144 test/warningsFilter-option.test.js
  92. +255 −72 test/worker.test.js
  93. +13 −0 tsconfig.json
3 changes: 1 addition & 2 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -9,5 +9,4 @@ insert_final_newline = true
trim_trailing_whitespace = true

[*.md]
insert_final_newline = true
trim_trailing_whitespace = false
trim_trailing_whitespace = false
5 changes: 4 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
module.exports = {
root: true,
extends: ['@webpack-contrib/eslint-config-webpack', 'prettier'],
extends: ["@webpack-contrib/eslint-config-webpack", "prettier"],
rules: {
"import/no-namespace": "off",
},
};
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@
You arrived at this template because you felt none of the other options
matched the kind of issue you'd like to report. Please use this opportunity to
tell us about your particular type of issue so we can try to accomodate
tell us about your particular type of issue so we can try to accommodate
similar issues in the future.
PLEASE do note, if you're using this to report an issue already covered by the
95 changes: 95 additions & 0 deletions .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
name: terser-webpack-plugin

on:
push:
branches:
- master
- next
pull_request:
branches:
- master
- next

jobs:
lint:
name: Lint - ${{ matrix.os }} - Node v${{ matrix.node-version }}

env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

strategy:
matrix:
os: [ubuntu-latest]
node-version: [12.x]

runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}

- name: Use latest NPM
run: sudo npm i -g npm

- name: Install dependencies
run: npm ci

- name: Lint
run: npm run lint

- name: Security audit
run: npm run security

- name: Check commit message
uses: wagoid/commitlint-github-action@v1

test:
name: Test - ${{ matrix.os }} - Node v${{ matrix.node-version }}, Webpack ${{ matrix.webpack-version }}

strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [10.x, 12.x, 14.x]
webpack-version: [latest]

runs-on: ${{ matrix.os }}

steps:
- name: Setup Git
if: matrix.os == 'windows-latest'
run: git config --global core.autocrlf input

- uses: actions/checkout@v2

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}

- name: Use latest NPM on ubuntu/macos
if: matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest'
run: sudo npm i -g npm

- name: Use latest NPM on windows
if: matrix.os == 'windows-latest'
run: npm i -g npm

- name: Install dependencies
run: npm ci

- name: Install webpack ${{ matrix.webpack-version }}
run: npm i webpack@${{ matrix.webpack-version }}

- name: Run tests for webpack version ${{ matrix.webpack-version }}
run: npm run test:coverage -- --ci

- name: Submit coverage data to codecov
uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ npm-debug.log*
/local
/reports
/node_modules
/test/outputs

.DS_Store
Thumbs.db
1 change: 1 addition & 0 deletions .husky/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
_
4 changes: 4 additions & 0 deletions .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx --no-install commitlint --edit $1
4 changes: 4 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx lint-staged
5 changes: 0 additions & 5 deletions .prettierrc.js

This file was deleted.

369 changes: 369 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

305 changes: 75 additions & 230 deletions README.md

Large diffs are not rendered by default.

201 changes: 0 additions & 201 deletions azure-pipelines.yml

This file was deleted.

4 changes: 2 additions & 2 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -7,10 +7,10 @@ module.exports = (api) => {
return {
presets: [
[
'@babel/preset-env',
"@babel/preset-env",
{
targets: {
node: '6.9.0',
node: "10.13.0",
},
},
],
2 changes: 1 addition & 1 deletion commitlint.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module.exports = {
extends: ['@commitlint/config-conventional'],
extends: ["@commitlint/config-conventional"],
};
6 changes: 0 additions & 6 deletions husky.config.js

This file was deleted.

3 changes: 3 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
testEnvironment: "node",
};
4 changes: 2 additions & 2 deletions lint-staged.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module.exports = {
'*.js': ['prettier --write', 'eslint --fix', 'git add'],
'*.{json,md,yml,css}': ['prettier --write', 'git add'],
"*.js": ["eslint --fix", "prettier --write"],
"*.{json,md,yml,css,ts}": ["prettier --write"],
};
30,637 changes: 22,521 additions & 8,116 deletions package-lock.json

Large diffs are not rendered by default.

98 changes: 52 additions & 46 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,78 +1,84 @@
{
"name": "terser-webpack-plugin",
"version": "1.4.1",
"version": "5.1.4",
"description": "Terser plugin for webpack",
"license": "MIT",
"repository": "webpack-contrib/terser-webpack-plugin",
"author": "webpack Contrib Team",
"homepage": "https://github.com/webpack-contrib/terser-webpack-plugin",
"bugs": "https://github.com/webpack-contrib/terser-webpack-plugin/issues",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
},
"main": "dist/cjs.js",
"engines": {
"node": ">= 6.9.0"
"node": ">= 10.13.0"
},
"scripts": {
"start": "npm run build -- -w",
"prebuild": "npm run clean",
"build": "cross-env NODE_ENV=production babel src -d dist --ignore \"src/**/*.test.js\" --copy-files",
"clean": "del-cli dist",
"prebuild": "npm run clean",
"_build:types": "tsc --declaration --emitDeclarationOnly --outDir types && prettier \"types/**/*.ts\" --write",
"build:code": "cross-env NODE_ENV=production babel src -d dist --copy-files",
"build": "npm-run-all -p \"build:**\"",
"commitlint": "commitlint --from=master",
"lint:prettier": "prettier \"{**/*,*}.{js,json,md,yml,css}\" --list-different",
"lint:js": "eslint --cache src test",
"security": "npm audit --production",
"lint:prettier": "prettier --list-different .",
"lint:js": "eslint --cache .",
"lint:types": "tsc --pretty --noEmit",
"lint": "npm-run-all -l -p \"lint:**\"",
"prepare": "npm run build",
"release": "standard-version",
"security": "npm audit",
"test:only": "cross-env NODE_ENV=test jest",
"test:watch": "cross-env NODE_ENV=test jest --watch",
"test:coverage": "cross-env NODE_ENV=test jest --collectCoverageFrom=\"src/**/*.js\" --coverage",
"test:watch": "npm run test:only -- --watch",
"test:coverage": "npm run test:only -- --collectCoverageFrom=\"src/**/*.js\" --coverage",
"pretest": "npm run lint",
"test": "cross-env NODE_ENV=test npm run test:coverage",
"defaults": "webpack-defaults"
"test": "npm run test:coverage",
"prepare": "husky install && npm run build",
"release": "standard-version"
},
"files": [
"dist"
"dist",
"types"
],
"peerDependencies": {
"webpack": "^4.0.0"
"webpack": "^5.1.0"
},
"dependencies": {
"cacache": "^12.0.2",
"find-cache-dir": "^2.1.0",
"is-wsl": "^1.1.0",
"schema-utils": "^1.0.0",
"serialize-javascript": "^1.7.0",
"jest-worker": "^27.0.2",
"p-limit": "^3.1.0",
"schema-utils": "^3.0.0",
"serialize-javascript": "^6.0.0",
"source-map": "^0.6.1",
"terser": "^4.1.2",
"webpack-sources": "^1.4.0",
"worker-farm": "^1.7.0"
"terser": "^5.7.0"
},
"devDependencies": {
"@babel/cli": "^7.5.5",
"@babel/core": "^7.5.5",
"@babel/preset-env": "^7.5.5",
"@commitlint/cli": "^8.1.0",
"@commitlint/config-conventional": "^8.1.0",
"@webpack-contrib/defaults": "^5.0.2",
"@babel/cli": "^7.14.5",
"@babel/core": "^7.14.6",
"@babel/preset-env": "^7.14.7",
"@commitlint/cli": "^12.1.4",
"@commitlint/config-conventional": "^12.1.4",
"@types/serialize-javascript": "^5.0.0",
"@webpack-contrib/eslint-config-webpack": "^3.0.0",
"babel-jest": "^24.8.0",
"commitlint-azure-pipelines-cli": "^1.0.2",
"cross-env": "^5.2.0",
"del": "^4.1.1",
"del-cli": "^1.1.0",
"eslint": "^6.1.0",
"eslint-config-prettier": "^6.0.0",
"eslint-plugin-import": "^2.18.2",
"husky": "^3.0.2",
"jest": "^24.8.0",
"jest-junit": "^7.0.0",
"lint-staged": "^9.2.1",
"memory-fs": "^0.4.1",
"babel-jest": "^27.0.2",
"copy-webpack-plugin": "^9.0.0",
"cross-env": "^7.0.3",
"del": "^6.0.0",
"del-cli": "^3.0.1",
"eslint": "^7.29.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.22.1",
"file-loader": "^6.2.0",
"husky": "^6.0.0",
"jest": "^27.0.5",
"lint-staged": "^11.0.0",
"memfs": "^3.2.2",
"npm-run-all": "^4.1.5",
"prettier": "^1.18.2",
"standard-version": "^7.0.0",
"uglify-js": "^3.6.0",
"webpack": "^4.38.0"
"prettier": "^2.3.1",
"standard-version": "^9.3.0",
"typescript": "^4.3.4",
"uglify-js": "^3.13.9",
"webpack": "^5.40.0",
"worker-loader": "^3.0.8"
},
"keywords": [
"uglify",
112 changes: 0 additions & 112 deletions src/TaskRunner.js

This file was deleted.

2 changes: 1 addition & 1 deletion src/cjs.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
const plugin = require('./index');
const plugin = require("./index");

module.exports = plugin.default;
855 changes: 556 additions & 299 deletions src/index.js

Large diffs are not rendered by default.

316 changes: 205 additions & 111 deletions src/minify.js
Original file line number Diff line number Diff line change
@@ -1,134 +1,190 @@
/* eslint-disable
arrow-body-style
*/
import { minify as terserMinify } from 'terser';

const buildTerserOptions = ({
ecma,
warnings,
parse = {},
compress = {},
mangle,
module,
output,
toplevel,
nameCache,
ie8,
/* eslint-disable camelcase */
keep_classnames,
keep_fnames,
/* eslint-enable camelcase */
safari10,
} = {}) => ({
ecma,
warnings,
parse: { ...parse },
compress: typeof compress === 'boolean' ? compress : { ...compress },
// eslint-disable-next-line no-nested-ternary
mangle:
mangle == null
? true
: typeof mangle === 'boolean'
? mangle
: { ...mangle },
output: {
shebang: true,
comments: false,
beautify: false,
semicolons: true,
...output,
},
module,
// Ignoring sourceMap from options
sourceMap: null,
toplevel,
nameCache,
ie8,
keep_classnames,
keep_fnames,
safari10,
});

const buildComments = (options, terserOptions, extractedComments) => {
const { minify: terserMinify } = require("terser");

/** @typedef {import("source-map").RawSourceMap} RawSourceMap */
/** @typedef {import("./index.js").ExtractCommentsOptions} ExtractCommentsOptions */
/** @typedef {import("./index.js").CustomMinifyFunction} CustomMinifyFunction */
/** @typedef {import("terser").MinifyOptions} TerserMinifyOptions */
/** @typedef {import("terser").MinifyOutput} MinifyOutput */
/** @typedef {import("terser").FormatOptions} FormatOptions */
/** @typedef {import("terser").MangleOptions} MangleOptions */
/** @typedef {import("./index.js").ExtractCommentsFunction} ExtractCommentsFunction */
/** @typedef {import("./index.js").ExtractCommentsCondition} ExtractCommentsCondition */

/**
* @typedef {Object.<any, any>} CustomMinifyOptions
*/

/**
* @typedef {Object} InternalMinifyOptions
* @property {string} name
* @property {string} input
* @property {RawSourceMap} [inputSourceMap]
* @property {ExtractCommentsOptions} extractComments
* @property {CustomMinifyFunction} [minify]
* @property {TerserMinifyOptions | CustomMinifyOptions} minifyOptions
*/

/**
* @typedef {Array<string>} ExtractedComments
*/

/**
* @typedef {Promise<MinifyOutput & { extractedComments?: ExtractedComments}>} InternalMinifyResult
*/

/**
* @typedef {TerserMinifyOptions & { sourceMap: undefined } & ({ output: FormatOptions & { beautify: boolean } } | { format: FormatOptions & { beautify: boolean } })} NormalizedTerserMinifyOptions
*/

/**
* @param {TerserMinifyOptions} [terserOptions={}]
* @returns {NormalizedTerserMinifyOptions}
*/
function buildTerserOptions(terserOptions = {}) {
// Need deep copy objects to avoid https://github.com/terser/terser/issues/366
return {
...terserOptions,
compress:
typeof terserOptions.compress === "boolean"
? terserOptions.compress
: { ...terserOptions.compress },
// ecma: terserOptions.ecma,
// ie8: terserOptions.ie8,
// keep_classnames: terserOptions.keep_classnames,
// keep_fnames: terserOptions.keep_fnames,
mangle:
terserOptions.mangle == null
? true
: typeof terserOptions.mangle === "boolean"
? terserOptions.mangle
: { ...terserOptions.mangle },
// module: terserOptions.module,
// nameCache: { ...terserOptions.toplevel },
// the `output` option is deprecated
...(terserOptions.format
? { format: { beautify: false, ...terserOptions.format } }
: { output: { beautify: false, ...terserOptions.output } }),
parse: { ...terserOptions.parse },
// safari10: terserOptions.safari10,
// Ignoring sourceMap from options
// eslint-disable-next-line no-undefined
sourceMap: undefined,
// toplevel: terserOptions.toplevel
};
}

/**
* @param {any} value
* @returns {boolean}
*/
function isObject(value) {
const type = typeof value;

return value != null && (type === "object" || type === "function");
}

/**
* @param {ExtractCommentsOptions} extractComments
* @param {NormalizedTerserMinifyOptions} terserOptions
* @param {ExtractedComments} extractedComments
* @returns {ExtractCommentsFunction}
*/
function buildComments(extractComments, terserOptions, extractedComments) {
/** @type {{ [index: string]: ExtractCommentsCondition }} */
const condition = {};
const commentsOpts = terserOptions.output.comments;

// Use /^\**!|@preserve|@license|@cc_on/i RegExp
if (typeof options.extractComments === 'boolean') {
condition.preserve = commentsOpts;
condition.extract = /^\**!|@preserve|@license|@cc_on/i;
} else if (
typeof options.extractComments === 'string' ||
options.extractComments instanceof RegExp
) {
// extractComments specifies the extract condition and commentsOpts specifies the preserve condition
condition.preserve = commentsOpts;
condition.extract = options.extractComments;
} else if (typeof options.extractComments === 'function') {
condition.preserve = commentsOpts;
condition.extract = options.extractComments;
let comments;

if (terserOptions.format) {
({ comments } = terserOptions.format);
} else if (terserOptions.output) {
({ comments } = terserOptions.output);
}

condition.preserve = typeof comments !== "undefined" ? comments : false;

if (typeof extractComments === "boolean" && extractComments) {
condition.extract = "some";
} else if (
Object.prototype.hasOwnProperty.call(options.extractComments, 'condition')
typeof extractComments === "string" ||
extractComments instanceof RegExp
) {
// Extract condition is given in extractComments.condition
condition.preserve = commentsOpts;
condition.extract = options.extractComments.condition;
condition.extract = extractComments;
} else if (typeof extractComments === "function") {
condition.extract = extractComments;
} else if (extractComments && isObject(extractComments)) {
condition.extract =
typeof extractComments.condition === "boolean" &&
extractComments.condition
? "some"
: typeof extractComments.condition !== "undefined"
? extractComments.condition
: "some";
} else {
// No extract condition is given. Extract comments that match commentsOpts instead of preserving them
condition.preserve = false;
condition.extract = commentsOpts;
// No extract
// Preserve using "commentsOpts" or "some"
condition.preserve = typeof comments !== "undefined" ? comments : "some";
condition.extract = false;
}

// Ensure that both conditions are functions
['preserve', 'extract'].forEach((key) => {
["preserve", "extract"].forEach((key) => {
/** @type {undefined | string} */
let regexStr;
/** @type {undefined | RegExp} */
let regex;

switch (typeof condition[key]) {
case 'boolean':
case "boolean":
condition[key] = condition[key] ? () => true : () => false;

break;
case 'function':
case "function":
break;
case 'string':
if (condition[key] === 'all') {
case "string":
if (condition[key] === "all") {
condition[key] = () => true;

break;
}

if (condition[key] === 'some') {
condition[key] = (astNode, comment) => {
return (
comment.type === 'comment2' &&
/^\**!|@preserve|@license|@cc_on/i.test(comment.value)
);
};
if (condition[key] === "some") {
condition[key] = /** @type {ExtractCommentsFunction} */ (
(astNode, comment) =>
(comment.type === "comment2" || comment.type === "comment1") &&
/@preserve|@lic|@cc_on|^\**!/i.test(comment.value)
);

break;
}

regexStr = condition[key];
regexStr = /** @type {string} */ (condition[key]);

condition[key] = (astNode, comment) => {
return new RegExp(regexStr).test(comment.value);
};
condition[key] = /** @type {ExtractCommentsFunction} */ (
(astNode, comment) =>
new RegExp(/** @type {string} */ (regexStr)).test(comment.value)
);

break;
default:
regex = condition[key];
regex = /** @type {RegExp} */ (condition[key]);

condition[key] = (astNode, comment) => regex.test(comment.value);
condition[key] = /** @type {ExtractCommentsFunction} */ (
(astNode, comment) =>
/** @type {RegExp} */ (regex).test(comment.value)
);
}
});

// Redefine the comments function to extract and preserve
// comments according to the two conditions
return (astNode, comment) => {
if (condition.extract(astNode, comment)) {
if (
/** @type {{ extract: ExtractCommentsFunction }} */
(condition).extract(astNode, comment)
) {
const commentText =
comment.type === 'comment2'
comment.type === "comment2"
? `/*${comment.value}*/`
: `//${comment.value}`;

@@ -138,47 +194,85 @@ const buildComments = (options, terserOptions, extractedComments) => {
}
}

return condition.preserve(astNode, comment);
return /** @type {{ preserve: ExtractCommentsFunction }} */ (
condition
).preserve(astNode, comment);
};
};
}

const minify = (options) => {
/**
* @param {InternalMinifyOptions} options
* @returns {InternalMinifyResult}
*/
async function minify(options) {
const {
file,
name,
input,
inputSourceMap,
extractComments,
minify: minifyFn,
minifyOptions,
} = options;

if (minifyFn) {
return minifyFn({ [file]: input }, inputSourceMap);
return minifyFn({ [name]: input }, inputSourceMap, minifyOptions);
}

// Copy terser options
const terserOptions = buildTerserOptions(options.terserOptions);
const terserOptions = buildTerserOptions(minifyOptions);

// Let terser generate a SourceMap
if (inputSourceMap) {
terserOptions.sourceMap = true;
// @ts-ignore
terserOptions.sourceMap = { asObject: true };
}

/** @type {ExtractedComments} */
const extractedComments = [];
const { extractComments } = options;

if (extractComments) {
if (terserOptions.output) {
terserOptions.output.comments = buildComments(
options,
extractComments,
terserOptions,
extractedComments
);
} else if (terserOptions.format) {
terserOptions.format.comments = buildComments(
extractComments,
terserOptions,
extractedComments
);
}

const { error, map, code, warnings } = terserMinify(
{ [file]: input },
terserOptions
);
const result = await terserMinify({ [name]: input }, terserOptions);

return { ...result, extractedComments };
}

/**
* @param {string} options
* @returns {InternalMinifyResult}
*/
function transform(options) {
// 'use strict' => this === undefined (Clean Scope)
// Safer for possible security issues, albeit not critical at all here
// eslint-disable-next-line no-param-reassign
const evaluatedOptions =
/** @type {InternalMinifyOptions} */
(
// eslint-disable-next-line no-new-func
new Function(
"exports",
"require",
"module",
"__filename",
"__dirname",
`'use strict'\nreturn ${options}`
)(exports, require, module, __filename, __dirname)
);

return { error, map, code, warnings, extractedComments };
};
return minify(evaluatedOptions);
}

export default minify;
module.exports.minify = minify;
module.exports.transform = transform;
132 changes: 57 additions & 75 deletions src/options.json
Original file line number Diff line number Diff line change
@@ -1,112 +1,80 @@
{
"additionalProperties": false,
"definitions": {
"file-conditions": {
"Rule": {
"description": "Filtering rule as regex or string.",
"anyOf": [
{
"instanceof": "RegExp"
"instanceof": "RegExp",
"tsType": "RegExp"
},
{
"type": "string"
"type": "string",
"minLength": 1
}
]
}
},
"properties": {
"test": {
},
"Rules": {
"description": "Filtering rules.",
"anyOf": [
{
"$ref": "#/definitions/file-conditions"
},
{
"type": "array",
"items": {
"anyOf": [
"description": "A rule condition.",
"oneOf": [
{
"$ref": "#/definitions/file-conditions"
"$ref": "#/definitions/Rule"
}
]
},
"type": "array"
}
]
},
"include": {
"anyOf": [
{
"$ref": "#/definitions/file-conditions"
}
},
{
"items": {
"anyOf": [
{
"$ref": "#/definitions/file-conditions"
}
]
},
"type": "array"
"$ref": "#/definitions/Rule"
}
]
},
"exclude": {
"anyOf": [
{
"$ref": "#/definitions/file-conditions"
},
}
},
"title": "TerserPluginOptions",
"type": "object",
"additionalProperties": false,
"properties": {
"test": {
"description": "Include all modules that pass test assertion.",
"oneOf": [
{
"items": {
"anyOf": [
{
"$ref": "#/definitions/file-conditions"
}
]
},
"type": "array"
"$ref": "#/definitions/Rules"
}
]
},
"chunkFilter": {
"instanceof": "Function"
},
"cache": {
"anyOf": [
{
"type": "boolean"
},
"include": {
"description": "Include all modules matching any of these conditions.",
"oneOf": [
{
"type": "string"
"$ref": "#/definitions/Rules"
}
]
},
"cacheKeys": {
"instanceof": "Function"
},
"parallel": {
"anyOf": [
{
"type": "boolean"
},
"exclude": {
"description": "Exclude all modules matching any of these conditions.",
"oneOf": [
{
"type": "integer"
"$ref": "#/definitions/Rules"
}
]
},
"sourceMap": {
"type": "boolean"
},
"minify": {
"instanceof": "Function"
},
"terserOptions": {
"description": "Options for `terser`.",
"additionalProperties": true,
"type": "object"
},
"extractComments": {
"description": "Whether comments shall be extracted to a separate file.",
"anyOf": [
{
"type": "boolean"
},
{
"type": "string"
"type": "string",
"minLength": 1
},
{
"instanceof": "RegExp"
@@ -123,7 +91,8 @@
"type": "boolean"
},
{
"type": "string"
"type": "string",
"minLength": 1
},
{
"instanceof": "RegExp"
@@ -136,7 +105,8 @@
"filename": {
"anyOf": [
{
"type": "string"
"type": "string",
"minLength": 1
},
{
"instanceof": "Function"
@@ -149,7 +119,8 @@
"type": "boolean"
},
{
"type": "string"
"type": "string",
"minLength": 1
},
{
"instanceof": "Function"
@@ -161,9 +132,20 @@
}
]
},
"warningsFilter": {
"parallel": {
"description": "Use multi-process parallel running to improve the build speed.",
"anyOf": [
{
"type": "boolean"
},
{
"type": "integer"
}
]
},
"minify": {
"description": "Allows you to override default minify function.",
"instanceof": "Function"
}
},
"type": "object"
}
}
21 changes: 0 additions & 21 deletions src/worker.js

This file was deleted.

1,676 changes: 1,544 additions & 132 deletions test/TerserPlugin.test.js

Large diffs are not rendered by default.

2,669 changes: 2,620 additions & 49 deletions test/__snapshots__/TerserPlugin.test.js.snap

Large diffs are not rendered by default.

914 changes: 327 additions & 587 deletions test/__snapshots__/cache-option.test.js.snap

Large diffs are not rendered by default.

108 changes: 0 additions & 108 deletions test/__snapshots__/chunkFilter-option.test.js.snap

This file was deleted.

720 changes: 210 additions & 510 deletions test/__snapshots__/exclude-option.test.js.snap

Large diffs are not rendered by default.

6,389 changes: 5,256 additions & 1,133 deletions test/__snapshots__/extractComments-option.test.js.snap

Large diffs are not rendered by default.

734 changes: 213 additions & 521 deletions test/__snapshots__/include-option.test.js.snap

Large diffs are not rendered by default.

467 changes: 64 additions & 403 deletions test/__snapshots__/minify-option.test.js.snap

Large diffs are not rendered by default.

231 changes: 0 additions & 231 deletions test/__snapshots__/parallel-option-failure.test.js.snap

This file was deleted.

137 changes: 102 additions & 35 deletions test/__snapshots__/parallel-option.test.js.snap

Large diffs are not rendered by default.

161 changes: 0 additions & 161 deletions test/__snapshots__/sourceMap-option.test.js.snap

This file was deleted.

153 changes: 0 additions & 153 deletions test/__snapshots__/supports-multicompiler.test.js.snap

This file was deleted.

1,110 changes: 545 additions & 565 deletions test/__snapshots__/terserOptions-option.test.js.snap

Large diffs are not rendered by default.

1,714 changes: 632 additions & 1,082 deletions test/__snapshots__/test-option.test.js.snap

Large diffs are not rendered by default.

178 changes: 178 additions & 0 deletions test/__snapshots__/validate-options.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`validation 1`] = `
"Invalid options object. Terser Plugin has been initialized using an options object that does not match the API schema.
- options.test should be one of these:
[RegExp | non-empty string, ...] | RegExp | non-empty string
-> Filtering rules.
Details:
* options.test should be an array:
[RegExp | non-empty string, ...]
* options.test should be one of these:
RegExp | non-empty string
-> Filtering rule as regex or string.
Details:
* options.test should be an instance of RegExp.
* options.test should be a non-empty string."
`;

exports[`validation 2`] = `
"Invalid options object. Terser Plugin has been initialized using an options object that does not match the API schema.
- options.test should be one of these:
[RegExp | non-empty string, ...] | RegExp | non-empty string
-> Filtering rules.
Details:
* options.test[0] should be one of these:
RegExp | non-empty string
-> Filtering rule as regex or string.
Details:
* options.test[0] should be an instance of RegExp.
* options.test[0] should be a non-empty string."
`;

exports[`validation 3`] = `
"Invalid options object. Terser Plugin has been initialized using an options object that does not match the API schema.
- options.include should be one of these:
[RegExp | non-empty string, ...] | RegExp | non-empty string
-> Filtering rules.
Details:
* options.include should be an array:
[RegExp | non-empty string, ...]
* options.include should be one of these:
RegExp | non-empty string
-> Filtering rule as regex or string.
Details:
* options.include should be an instance of RegExp.
* options.include should be a non-empty string."
`;

exports[`validation 4`] = `
"Invalid options object. Terser Plugin has been initialized using an options object that does not match the API schema.
- options.include should be one of these:
[RegExp | non-empty string, ...] | RegExp | non-empty string
-> Filtering rules.
Details:
* options.include[0] should be one of these:
RegExp | non-empty string
-> Filtering rule as regex or string.
Details:
* options.include[0] should be an instance of RegExp.
* options.include[0] should be a non-empty string."
`;

exports[`validation 5`] = `
"Invalid options object. Terser Plugin has been initialized using an options object that does not match the API schema.
- options.exclude should be one of these:
[RegExp | non-empty string, ...] | RegExp | non-empty string
-> Filtering rules.
Details:
* options.exclude should be an array:
[RegExp | non-empty string, ...]
* options.exclude should be one of these:
RegExp | non-empty string
-> Filtering rule as regex or string.
Details:
* options.exclude should be an instance of RegExp.
* options.exclude should be a non-empty string."
`;

exports[`validation 6`] = `
"Invalid options object. Terser Plugin has been initialized using an options object that does not match the API schema.
- options.exclude should be one of these:
[RegExp | non-empty string, ...] | RegExp | non-empty string
-> Filtering rules.
Details:
* options.exclude[0] should be one of these:
RegExp | non-empty string
-> Filtering rule as regex or string.
Details:
* options.exclude[0] should be an instance of RegExp.
* options.exclude[0] should be a non-empty string."
`;

exports[`validation 7`] = `
"Invalid options object. Terser Plugin has been initialized using an options object that does not match the API schema.
- options.parallel should be one of these:
boolean | integer
-> Use multi-process parallel running to improve the build speed.
Details:
* options.parallel should be a boolean.
* options.parallel should be a integer."
`;

exports[`validation 8`] = `
"Invalid options object. Terser Plugin has been initialized using an options object that does not match the API schema.
- options.parallel should be one of these:
boolean | integer
-> Use multi-process parallel running to improve the build speed.
Details:
* options.parallel should be a boolean.
* options.parallel should be a integer."
`;

exports[`validation 9`] = `
"Invalid options object. Terser Plugin has been initialized using an options object that does not match the API schema.
- options.minify should be an instance of function.
-> Allows you to override default minify function."
`;

exports[`validation 10`] = `
"Invalid options object. Terser Plugin has been initialized using an options object that does not match the API schema.
- options.terserOptions should be an object:
object {}
-> Options for \`terser\`."
`;

exports[`validation 11`] = `
"Invalid options object. Terser Plugin has been initialized using an options object that does not match the API schema.
- options.extractComments should be one of these:
boolean | non-empty string | RegExp | function | object { condition?, filename?, banner? }
-> Whether comments shall be extracted to a separate file.
Details:
* options.extractComments.condition should be one of these:
boolean | non-empty string | RegExp | function
Details:
* options.extractComments.condition should be a boolean.
* options.extractComments.condition should be a non-empty string.
* options.extractComments.condition should be an instance of RegExp.
* options.extractComments.condition should be an instance of function."
`;
exports[`validation 12`] = `
"Invalid options object. Terser Plugin has been initialized using an options object that does not match the API schema.
- options.extractComments should be one of these:
boolean | non-empty string | RegExp | function | object { condition?, filename?, banner? }
-> Whether comments shall be extracted to a separate file.
Details:
* options.extractComments.filename should be one of these:
non-empty string | function
Details:
* options.extractComments.filename should be a non-empty string.
* options.extractComments.filename should be an instance of function."
`;
exports[`validation 13`] = `
"Invalid options object. Terser Plugin has been initialized using an options object that does not match the API schema.
- options.extractComments should be one of these:
boolean | non-empty string | RegExp | function | object { condition?, filename?, banner? }
-> Whether comments shall be extracted to a separate file.
Details:
* options.extractComments.banner should be one of these:
boolean | non-empty string | function
Details:
* options.extractComments.banner should be a boolean.
* options.extractComments.banner should be a non-empty string.
* options.extractComments.banner should be an instance of function."
`;
exports[`validation 14`] = `
"Invalid options object. Terser Plugin has been initialized using an options object that does not match the API schema.
- options.extractComments has an unknown property 'unknown'. These properties are valid:
object { condition?, filename?, banner? }"
`;
exports[`validation 15`] = `
"Invalid options object. Terser Plugin has been initialized using an options object that does not match the API schema.
- options has an unknown property 'unknown'. These properties are valid:
object { test?, include?, exclude?, terserOptions?, extractComments?, parallel?, minify? }"
`;
209 changes: 0 additions & 209 deletions test/__snapshots__/validation.test.js.snap

This file was deleted.

48 changes: 0 additions & 48 deletions test/__snapshots__/warningsFilter-option.test.js.snap

This file was deleted.

166 changes: 141 additions & 25 deletions test/__snapshots__/worker.test.js.snap
Original file line number Diff line number Diff line change
@@ -1,61 +1,177 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`matches snapshot normalizes when options.extractComments is regex: test1.js 1`] = `
exports[`worker normalizes when minimizerOptions.output.comments is string: all 1`] = `
Object {
"code": "var foo=1;/* hello */
// Comment
/* duplicate */
/* duplicate */",
"extractedComments": Array [],
}
`;

exports[`worker should match snapshot when minimizerOptions.compress.comments is boolean 1`] = `
Object {
"code": "var foo=1;",
"error": undefined,
"extractedComments": Array [],
"map": undefined,
"warnings": undefined,
}
`;

exports[`matches snapshot normalizes when terserOptions.extractComments is number: test4.js 1`] = `
exports[`worker should match snapshot when minimizerOptions.compress.comments is object 1`] = `
Object {
"code": "var foo=1;",
"error": undefined,
"extractedComments": Array [],
"map": undefined,
"warnings": undefined,
}
`;

exports[`matches snapshot normalizes when terserOptions.output.comments is string: all: test2.js 1`] = `
exports[`worker should match snapshot when minimizerOptions.extractComments is number 1`] = `
Object {
"code": "var foo=1;/* hello */",
"error": undefined,
"code": "var foo=1;",
"extractedComments": Array [],
"map": undefined,
"warnings": undefined,
}
`;

exports[`matches snapshot normalizes when terserOptions.output.comments is string: some: test3.js 1`] = `
exports[`worker should match snapshot when minimizerOptions.mangle is "null" 1`] = `
Object {
"code": "var foo=1;",
"extractedComments": Array [],
}
`;

exports[`worker should match snapshot when minimizerOptions.mangle is boolean 1`] = `
Object {
"code": "var foo=1;",
"extractedComments": Array [],
}
`;

exports[`worker should match snapshot when minimizerOptions.mangle is object 1`] = `
Object {
"code": "var a=1;",
"extractedComments": Array [],
}
`;

exports[`worker should match snapshot when minimizerOptions.output.comments is string: some 1`] = `
Object {
"code": "var foo=1;",
"extractedComments": Array [],
}
`;

exports[`worker should match snapshot when options.extractComments is "all" value 1`] = `
Object {
"code": "var foo=1;",
"extractedComments": Array [
"/* hello */",
"// Comment",
"/* duplicate */",
],
}
`;

exports[`worker should match snapshot when options.extractComments is "false" 1`] = `
Object {
"code": "var foo=1;",
"extractedComments": Array [],
}
`;

exports[`worker should match snapshot when options.extractComments is "some" value 1`] = `
Object {
"code": "var foo=1;",
"extractedComments": Array [],
}
`;

exports[`worker should match snapshot when options.extractComments is "true" 1`] = `
Object {
"code": "var foo=1;",
"extractedComments": Array [],
}
`;

exports[`worker should match snapshot when options.extractComments is "true" 2`] = `
"var foo = 1;/* hello */
// Comment
/* duplicate */
/* duplicate */"
`;

exports[`worker should match snapshot when options.extractComments is Function 1`] = `
Object {
"code": "var foo=1;",
"extractedComments": Array [
"/* hello */",
"// Comment",
"/* duplicate */",
],
}
`;

exports[`worker should match snapshot when options.extractComments is Object with "all" value 1`] = `
Object {
"code": "var foo=1;",
"extractedComments": Array [
"/* hello */",
"// Comment",
"/* duplicate */",
],
}
`;

exports[`worker should match snapshot when options.extractComments is Object with "some" value 1`] = `
Object {
"code": "var foo=1;",
"extractedComments": Array [],
}
`;

exports[`worker should match snapshot when options.extractComments is Object with "true" value 1`] = `
Object {
"code": "var foo=1;",
"extractedComments": Array [],
}
`;

exports[`worker should match snapshot when options.extractComments is RegExp 1`] = `
Object {
"code": "var foo=1;",
"extractedComments": Array [
"/* hello */",
],
}
`;

exports[`worker should match snapshot when options.extractComments is empty Object 1`] = `
Object {
"code": "var foo=1;",
"error": undefined,
"extractedComments": Array [],
"map": undefined,
"warnings": undefined,
}
`;

exports[`matches snapshot when applied with extract option set to a single file: test5.js 1`] = `
exports[`worker should match snapshot with extract option set to a single file 1`] = `
Object {
"code": "/******/function hello(o){console.log(o)}",
"error": undefined,
"extractedComments": Array [],
"map": undefined,
"warnings": undefined,
}
`;

exports[`matches snapshot when options.inputSourceMap: test6.js 1`] = `
exports[`worker should match snapshot with options.inputSourceMap 1`] = `
Object {
"code": "function foo(f){if(f)return bar()}",
"error": undefined,
"extractedComments": Array [],
"map": "{\\"version\\":3,\\"sources\\":[\\"test6.js\\"],\\"names\\":[\\"foo\\",\\"x\\",\\"bar\\"],\\"mappings\\":\\"AAAA,SAASA,IAAIC,GAAK,GAAIA,EAAK,OAAOC\\"}",
"warnings": undefined,
"map": Object {
"mappings": "AAAA,SAASA,IAAIC,GAAK,GAAIA,EAAK,OAAOC",
"names": Array [
"foo",
"x",
"bar",
],
"sources": Array [
"test6.js",
],
"version": 3,
},
}
`;
675 changes: 395 additions & 280 deletions test/cache-option.test.js

Large diffs are not rendered by default.

44 changes: 0 additions & 44 deletions test/chunkFilter-option.test.js

This file was deleted.

10 changes: 5 additions & 5 deletions test/cjs.test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import TerserPlugin from '../src';
import CJSTerserPlugin from '../src/cjs';
import src from "../src";
import cjs from "../src/cjs";

describe('CJS', () => {
it('should exported plugin', () => {
expect(CJSTerserPlugin).toEqual(TerserPlugin);
describe("CJS", () => {
it("should export loader", () => {
expect(cjs).toEqual(src);
});
});
106 changes: 37 additions & 69 deletions test/exclude-option.test.js
Original file line number Diff line number Diff line change
@@ -1,105 +1,73 @@
import TerserPlugin from '../src/index';
import path from "path";

import { cleanErrorStack, createCompiler, compile } from './helpers';
import TerserPlugin from "../src/index";

describe('when applied with `exclude` option', () => {
import {
compile,
getCompiler,
getErrors,
getWarnings,
readsAssets,
} from "./helpers";

describe("exclude option", () => {
let compiler;

beforeEach(() => {
compiler = createCompiler({
compiler = getCompiler({
entry: {
excluded1: `${__dirname}/fixtures/excluded1.js`,
excluded2: `${__dirname}/fixtures/excluded2.js`,
entry: `${__dirname}/fixtures/entry.js`,
excluded1: path.resolve(__dirname, "./fixtures/excluded1.js"),
excluded2: path.resolve(__dirname, "./fixtures/excluded2.js"),
entry: path.resolve(__dirname, "./fixtures/entry.js"),
},
});
});

it('matches snapshot for a single `exclude` value ({RegExp})', () => {
it("should match snapshot for a single RegExp value", async () => {
new TerserPlugin({
exclude: /excluded1/i,
}).apply(compiler);

return compile(compiler).then((stats) => {
const errors = stats.compilation.errors.map(cleanErrorStack);
const warnings = stats.compilation.warnings.map(cleanErrorStack);

expect(errors).toMatchSnapshot('errors');
expect(warnings).toMatchSnapshot('warnings');
const stats = await compile(compiler);

for (const file in stats.compilation.assets) {
if (
Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
) {
expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
}
}
});
expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});

it('matches snapshot for a single `exclude` value ({String})', () => {
it("should match snapshot for a single String value", async () => {
new TerserPlugin({
exclude: 'excluded1',
exclude: "excluded1",
}).apply(compiler);

return compile(compiler).then((stats) => {
const errors = stats.compilation.errors.map(cleanErrorStack);
const warnings = stats.compilation.warnings.map(cleanErrorStack);
const stats = await compile(compiler);

expect(errors).toMatchSnapshot('errors');
expect(warnings).toMatchSnapshot('warnings');

for (const file in stats.compilation.assets) {
if (
Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
) {
expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
}
}
});
expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});

it('matches snapshot for multiple `exclude` values ({RegExp})', () => {
it("should match snapshot for multiple RegExp values", async () => {
new TerserPlugin({
exclude: [/excluded1/i, /excluded2/i],
}).apply(compiler);

return compile(compiler).then((stats) => {
const errors = stats.compilation.errors.map(cleanErrorStack);
const warnings = stats.compilation.warnings.map(cleanErrorStack);
const stats = await compile(compiler);

expect(errors).toMatchSnapshot('errors');
expect(warnings).toMatchSnapshot('warnings');

for (const file in stats.compilation.assets) {
if (
Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
) {
expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
}
}
});
expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});

it('matches snapshot for multiple `exclude` values ({String})', () => {
it("should match snapshot for multiple String values", async () => {
new TerserPlugin({
exclude: ['excluded1', 'excluded2'],
exclude: ["excluded1", "excluded2"],
}).apply(compiler);

return compile(compiler).then((stats) => {
const errors = stats.compilation.errors.map(cleanErrorStack);
const warnings = stats.compilation.warnings.map(cleanErrorStack);
const stats = await compile(compiler);

expect(errors).toMatchSnapshot('errors');
expect(warnings).toMatchSnapshot('warnings');

for (const file in stats.compilation.assets) {
if (
Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
) {
expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
}
}
});
expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});
});
728 changes: 502 additions & 226 deletions test/extractComments-option.test.js

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions test/fixtures/asset-resource.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import script from './emitted.js'

const myVar = 12;

console.log(myVar, script);

export default 12;
2 changes: 2 additions & 0 deletions test/fixtures/comments.js
Original file line number Diff line number Diff line change
@@ -26,4 +26,6 @@ import('./nested/comments.js');
* Foo
*/

// @lic

module.exports = Math.random();
2 changes: 2 additions & 0 deletions test/fixtures/copy.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
var foo = 12;
console.log(foo);
2 changes: 2 additions & 0 deletions test/fixtures/copy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
var foo = 12;
console.log(foo);
2 changes: 2 additions & 0 deletions test/fixtures/copy.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
var foo = 12;
console.log(foo);
5 changes: 5 additions & 0 deletions test/fixtures/emit-loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = function(content) {
this.emitFile("extra-file.js", 'var a = 1; console.log(a);');

return content;
};
1 change: 1 addition & 0 deletions test/fixtures/emitted.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('HERE');
Empty file added test/fixtures/empty.js
Empty file.
11 changes: 7 additions & 4 deletions test/fixtures/entry.mjs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
// foo
/* @preserve*/
// bar
const a = 2 + 2;

module.exports = function Foo() {
function test() {
const b = 2 + 2;
console.log(b + 1 + 2);
};
console.log(b + 1 + 2 + a);
}

test();

module.exports = test;
7 changes: 7 additions & 0 deletions test/fixtures/file-loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import script from '!!file-loader?name=[path][name].[ext]!./emitted.js'

const myVar = 12;

console.log(myVar, script);

export default 12;
1 change: 1 addition & 0 deletions test/fixtures/file.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = "ok";
7 changes: 7 additions & 0 deletions test/fixtures/file.worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*! Legal Comment 1 */

/*! Legal Comment 2 */

/*! Legal Comment 3 */

onmessage = function (event) {};
2 changes: 2 additions & 0 deletions test/fixtures/import-export/entry.js
Original file line number Diff line number Diff line change
@@ -12,4 +12,6 @@ function Foo() {
};
}

console.log(Foo());

export default Foo;
2 changes: 2 additions & 0 deletions test/fixtures/minify/es6.js
Original file line number Diff line number Diff line change
@@ -12,4 +12,6 @@ class Point {
}
}

console.log('HERE');

export default Point;
1 change: 1 addition & 0 deletions test/fixtures/shebang-1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('HERE');
3 changes: 3 additions & 0 deletions test/fixtures/shebang.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/* Comment */
/*! Legal Comment */
console.log('HERE');
10 changes: 10 additions & 0 deletions test/fixtures/worker-loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*! Legal Comment */

import Worker from './file.worker.js';

const worker = new Worker();

worker.postMessage({ a: 1 });
worker.onmessage = function (event) {};

worker.addEventListener('message', function (event) {});
96 changes: 0 additions & 96 deletions test/helpers.js

This file was deleted.

16 changes: 16 additions & 0 deletions test/helpers/BrokenCodePlugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import webpack from "webpack";

export default class BrokenCodePlugin {
apply(compiler) {
const plugin = { name: this.constructor.name };

compiler.hooks.thisCompilation.tap(plugin, (compilation) => {
compilation.hooks.additionalAssets.tap(plugin, () => {
// eslint-disable-next-line no-param-reassign
compilation.assets["broken.js"] = new webpack.sources.RawSource(
"`Broken==="
);
});
});
}
}
31 changes: 31 additions & 0 deletions test/helpers/EmitNewAsset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export default class EmitNewAsset {
constructor(options = {}) {
this.options = options;
}

apply(compiler) {
const pluginName = this.constructor.name;

const { RawSource } = compiler.webpack.sources;

compiler.hooks.compilation.tap(pluginName, (compilation) => {
compilation.hooks.processAssets.tap(
{
name: pluginName,
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_REPORT,
},
() => {
// eslint-disable-next-line no-param-reassign
compilation.emitAsset(
this.options.name,
new RawSource(`
var foo = 'bar';
var bar = 'foo';
`)
);
}
);
});
}
}
16 changes: 16 additions & 0 deletions test/helpers/ExistingCommentsFile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import webpack from "webpack";

export default class ExistingCommentsFile {
apply(compiler) {
const plugin = { name: this.constructor.name };

compiler.hooks.thisCompilation.tap(plugin, (compilation) => {
compilation.hooks.additionalAssets.tap(plugin, () => {
// eslint-disable-next-line no-param-reassign
compilation.assets["licenses.txt"] = new webpack.sources.RawSource(
"// Existing Comment"
);
});
});
}
}
24 changes: 24 additions & 0 deletions test/helpers/ModifyExistingAsset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import webpack from "webpack";

export default class ExistingCommentsFile {
constructor(options = {}) {
this.options = options;
}

apply(compiler) {
const plugin = { name: this.constructor.name };

compiler.hooks.thisCompilation.tap(plugin, (compilation) => {
compilation.hooks.additionalAssets.tap(plugin, () => {
// eslint-disable-next-line no-param-reassign
compilation.assets[this.options.name] =
new webpack.sources.ConcatSource(
`function changed() {} ${
this.options.comment ? "/*! CHANGED */" : ""
}`,
compilation.assets[this.options.name]
);
});
});
}
}
10 changes: 10 additions & 0 deletions test/helpers/compile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export default (compiler) =>
new Promise((resolve, reject) => {
compiler.run((error, stats) => {
if (error) {
return reject(error);
}

return resolve(stats);
});
});
9 changes: 9 additions & 0 deletions test/helpers/countPlugins.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default function countPlugins({ hooks }) {
return Object.keys(hooks).reduce((aggregate, name) => {
// eslint-disable-next-line no-param-reassign
aggregate[name] = Array.isArray(hooks[name].taps)
? hooks[name].taps.length
: 0;
return aggregate;
}, {});
}
22 changes: 22 additions & 0 deletions test/helpers/execute.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Module from "module";
import path from "path";

const parentModule = module;

export default (code) => {
const resource = "test.js";
const module = new Module(resource, parentModule);
// eslint-disable-next-line no-underscore-dangle
module.paths = Module._nodeModulePaths(
path.resolve(__dirname, "../fixtures")
);
module.filename = resource;

// eslint-disable-next-line no-underscore-dangle
module._compile(
`let __export__;${code};module.exports = __export__;`,
resource
);

return module.exports;
};
31 changes: 31 additions & 0 deletions test/helpers/getCompiler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import path from "path";

import webpack from "webpack";
import { createFsFromVolume, Volume } from "memfs";

export default function getCompiler(options = {}) {
const compiler = webpack(
Array.isArray(options)
? options
: {
mode: "production",
bail: true,
entry: path.resolve(__dirname, "../fixtures/entry.js"),
optimization: {
minimize: false,
},
output: {
pathinfo: false,
path: path.resolve(__dirname, "dist"),
filename: "[name].js",
chunkFilename: "[id].[name].js",
},
plugins: [],
...options,
}
);

compiler.outputFileSystem = createFsFromVolume(new Volume());

return compiler;
}
3 changes: 3 additions & 0 deletions test/helpers/getErrors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import normalizeErrors from "./normalizeErrors";

export default (stats) => normalizeErrors(stats.compilation.errors).sort();
3 changes: 3 additions & 0 deletions test/helpers/getWarnings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import normalizeErrors from "./normalizeErrors";

export default (stats) => normalizeErrors(stats.compilation.warnings).sort();
29 changes: 29 additions & 0 deletions test/helpers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import BrokenCodePlugin from "./BrokenCodePlugin";
import compile from "./compile";
import countPlugins from "./countPlugins";
import EmitNewAsset from "./EmitNewAsset";
import execute from "./execute";
import ExistingCommentsFile from "./ExistingCommentsFile";
import getCompiler from "./getCompiler";
import getErrors from "./getErrors";
import getWarnings from "./getWarnings";
import ModifyExistingAsset from "./ModifyExistingAsset";
import readAsset from "./readAsset";
import readsAssets from "./readAssets";
import normalizeErrors from "./normalizeErrors";

export {
BrokenCodePlugin,
compile,
countPlugins,
EmitNewAsset,
ExistingCommentsFile,
execute,
getCompiler,
getErrors,
getWarnings,
ModifyExistingAsset,
normalizeErrors,
readAsset,
readsAssets,
};
18 changes: 18 additions & 0 deletions test/helpers/normalizeErrors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
function removeCWD(str) {
const isWin = process.platform === "win32";
let cwd = process.cwd();

if (isWin) {
// eslint-disable-next-line no-param-reassign
str = str.replace(/\\/g, "/");
// eslint-disable-next-line no-param-reassign
cwd = cwd.replace(/\\/g, "/");
}

return str.replace(new RegExp(cwd, "g"), "");
}

export default (errors) =>
errors.map((error) =>
removeCWD(error.toString().split("\n").slice(0, 2).join("\n"))
);
23 changes: 23 additions & 0 deletions test/helpers/readAsset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import path from "path";

export default (asset, compiler, stats) => {
const usedFs = compiler.outputFileSystem;
const outputPath = stats.compilation.outputOptions.path;

let data = "";
let targetFile = asset;

const queryStringIdx = targetFile.indexOf("?");

if (queryStringIdx >= 0) {
targetFile = targetFile.substr(0, queryStringIdx);
}

try {
data = usedFs.readFileSync(path.join(outputPath, targetFile)).toString();
} catch (error) {
data = error.toString();
}

return data;
};
11 changes: 11 additions & 0 deletions test/helpers/readAssets.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import readAsset from "./readAsset";

export default function readAssets(compiler, stats) {
const assets = {};

Object.keys(stats.compilation.assets).forEach((asset) => {
assets[asset] = readAsset(asset, compiler, stats);
});

return assets;
}
28 changes: 28 additions & 0 deletions test/helpers/snapshotResolver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const path = require("path");

const webpack = require("webpack");

// eslint-disable-next-line global-require
const [webpackVersion] = webpack.version;
const snapshotExtension = `.snap.webpack${webpackVersion}`;

// eslint-disable-next-line no-console
console.log("Current webpack version:", webpackVersion);

module.exports = {
resolveSnapshotPath: (testPath) =>
path.join(
path.dirname(testPath),
"__snapshots__",
`${path.basename(testPath)}${snapshotExtension}`
),
resolveTestPath: (snapshotPath) =>
snapshotPath
.replace(`${path.sep}__snapshots__`, "")
.slice(0, -snapshotExtension.length),
testPathForConsistencyCheck: path.join(
"consistency_check",
"__tests__",
"example.test.js"
),
};
106 changes: 37 additions & 69 deletions test/include-option.test.js
Original file line number Diff line number Diff line change
@@ -1,105 +1,73 @@
import TerserPlugin from '../src/index';
import path from "path";

import { cleanErrorStack, createCompiler, compile } from './helpers';
import TerserPlugin from "../src/index";

describe('when applied with `include` option', () => {
import {
compile,
getCompiler,
getErrors,
getWarnings,
readsAssets,
} from "./helpers";

describe("include option", () => {
let compiler;

beforeEach(() => {
compiler = createCompiler({
compiler = getCompiler({
entry: {
included1: `${__dirname}/fixtures/included1.js`,
included2: `${__dirname}/fixtures/included2.js`,
entry: `${__dirname}/fixtures/entry.js`,
included1: path.resolve(__dirname, "./fixtures/included1.js"),
included2: path.resolve(__dirname, "./fixtures/included2.js"),
entry: path.resolve(__dirname, "./fixtures/entry.js"),
},
});
});

it('matches snapshot for a single `include` value ({RegExp})', () => {
it("should match snapshot for a single RegExp value", async () => {
new TerserPlugin({
include: /included1/i,
}).apply(compiler);

return compile(compiler).then((stats) => {
const errors = stats.compilation.errors.map(cleanErrorStack);
const warnings = stats.compilation.warnings.map(cleanErrorStack);

expect(errors).toMatchSnapshot('errors');
expect(warnings).toMatchSnapshot('warnings');
const stats = await compile(compiler);

for (const file in stats.compilation.assets) {
if (
Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
) {
expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
}
}
});
expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});

it('matches snapshot for a single `include` value ({String})', () => {
it("should match snapshot for a single String value", async () => {
new TerserPlugin({
include: 'included1',
include: "included1",
}).apply(compiler);

return compile(compiler).then((stats) => {
const errors = stats.compilation.errors.map(cleanErrorStack);
const warnings = stats.compilation.warnings.map(cleanErrorStack);
const stats = await compile(compiler);

expect(errors).toMatchSnapshot('errors');
expect(warnings).toMatchSnapshot('warnings');

for (const file in stats.compilation.assets) {
if (
Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
) {
expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
}
}
});
expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});

it('matches snapshot for multiple `include` values ({RegExp})', () => {
it("should match snapshot for multiple RegExp values", async () => {
new TerserPlugin({
include: [/included1/i, /included2/i],
}).apply(compiler);

return compile(compiler).then((stats) => {
const errors = stats.compilation.errors.map(cleanErrorStack);
const warnings = stats.compilation.warnings.map(cleanErrorStack);
const stats = await compile(compiler);

expect(errors).toMatchSnapshot('errors');
expect(warnings).toMatchSnapshot('warnings');

for (const file in stats.compilation.assets) {
if (
Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
) {
expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
}
}
});
expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});

it('matches snapshot for multiple `include` values ({String})', () => {
it("should match snapshot for multiple String values", async () => {
new TerserPlugin({
include: ['included1', 'included2'],
include: ["included1", "included2"],
}).apply(compiler);

return compile(compiler).then((stats) => {
const errors = stats.compilation.errors.map(cleanErrorStack);
const warnings = stats.compilation.warnings.map(cleanErrorStack);
const stats = await compile(compiler);

expect(errors).toMatchSnapshot('errors');
expect(warnings).toMatchSnapshot('warnings');

for (const file in stats.compilation.assets) {
if (
Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
) {
expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
}
}
});
expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});
});
385 changes: 189 additions & 196 deletions test/minify-option.test.js

Large diffs are not rendered by default.

105 changes: 0 additions & 105 deletions test/parallel-option-failure.test.js

This file was deleted.

312 changes: 223 additions & 89 deletions test/parallel-option.test.js
Original file line number Diff line number Diff line change
@@ -1,128 +1,262 @@
import os from 'os';
import path from "path";
import os from "os";

import workerFarm from 'worker-farm';
import { Worker } from "jest-worker";

import TerserPlugin from '../src/index';
import TerserPlugin from "../src/index";

import { createCompiler, compile, cleanErrorStack } from './helpers';
import {
compile,
getCompiler,
getErrors,
getWarnings,
readsAssets,
} from "./helpers";

jest.mock('os', () => {
const actualOs = require.requireActual('os');
jest.mock("os", () => {
const actualOs = jest.requireActual("os");

actualOs.cpus = jest.fn(() => {
return { length: 4 };
});
const mocked = {
cpus: jest.fn(() => {
return { length: 4 };
}),
};

return actualOs;
return { ...actualOs, ...mocked };
});

// Based on https://github.com/facebook/jest/blob/edde20f75665c2b1e3c8937f758902b5cf28a7b4/packages/jest-runner/src/__tests__/test_runner.test.js
let workerFarmMock;
let workerTransform;
let workerEnd;

const ENABLE_WORKER_THREADS =
typeof process.env.ENABLE_WORKER_THREADS !== "undefined"
? process.env.ENABLE_WORKER_THREADS === "true"
: true;

jest.mock('worker-farm', () => {
const mock = jest.fn(
(options, worker) =>
(workerFarmMock = jest.fn((data, callback) =>
jest.mock("jest-worker", () => {
return {
Worker: jest.fn().mockImplementation((workerPath) => {
return {
// eslint-disable-next-line global-require, import/no-dynamic-require
require(worker)(data, callback)
))
);
mock.end = jest.fn();
return mock;
transform: (workerTransform = jest.fn((data) =>
// eslint-disable-next-line global-require, import/no-dynamic-require
require(workerPath).transform(data)
)),
end: (workerEnd = jest.fn()),
getStderr: jest.fn(),
getStdout: jest.fn(),
};
}),
};
});

describe('when applied with `parallel` option', () => {
const workerPath = require.resolve("../src/minify");

describe("parallel option", () => {
let compiler;

beforeEach(() => {
os.cpus.mockClear();
workerFarm.mockClear();
workerFarm.end.mockClear();
jest.clearAllMocks();

compiler = createCompiler({
compiler = getCompiler({
entry: {
one: `${__dirname}/fixtures/entry.js`,
two: `${__dirname}/fixtures/entry.js`,
three: `${__dirname}/fixtures/entry.js`,
four: `${__dirname}/fixtures/entry.js`,
one: path.resolve(__dirname, "./fixtures/entry.js"),
two: path.resolve(__dirname, "./fixtures/entry.js"),
three: path.resolve(__dirname, "./fixtures/entry.js"),
four: path.resolve(__dirname, "./fixtures/entry.js"),
},
});
});

it('matches snapshot for `false` value', () => {
it("should match snapshot when a value is not specify", async () => {
new TerserPlugin().apply(compiler);

const stats = await compile(compiler);

expect(Worker).toHaveBeenCalledTimes(1);
expect(Worker).toHaveBeenLastCalledWith(workerPath, {
enableWorkerThreads: ENABLE_WORKER_THREADS,
numWorkers: os.cpus().length - 1,
});
expect(workerTransform).toHaveBeenCalledTimes(
Object.keys(stats.compilation.assets).length
);
expect(workerEnd).toHaveBeenCalledTimes(1);

expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});

it('should match snapshot for the "false" value', async () => {
new TerserPlugin({ parallel: false }).apply(compiler);

return compile(compiler).then((stats) => {
const errors = stats.compilation.errors.map(cleanErrorStack);
const warnings = stats.compilation.warnings.map(cleanErrorStack);
const stats = await compile(compiler);

expect(Worker).toHaveBeenCalledTimes(0);

expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});

it('should match snapshot for the "true" value', async () => {
new TerserPlugin({ parallel: true }).apply(compiler);

const stats = await compile(compiler);

expect(workerFarm.mock.calls.length).toBe(0);
expect(workerFarm.end.mock.calls.length).toBe(0);
expect(Worker).toHaveBeenCalledTimes(1);
expect(Worker).toHaveBeenLastCalledWith(workerPath, {
enableWorkerThreads: ENABLE_WORKER_THREADS,
numWorkers: Math.min(4, os.cpus().length - 1),
});
expect(workerTransform).toHaveBeenCalledTimes(
Object.keys(stats.compilation.assets).length
);
expect(workerEnd).toHaveBeenCalledTimes(1);

expect(errors).toMatchSnapshot('errors');
expect(warnings).toMatchSnapshot('warnings');
expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});

for (const file in stats.compilation.assets) {
if (
Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
) {
expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
}
}
it('should match snapshot for the "2" value', async () => {
new TerserPlugin({ parallel: 2 }).apply(compiler);

const stats = await compile(compiler);

expect(Worker).toHaveBeenCalledTimes(1);
expect(Worker).toHaveBeenLastCalledWith(workerPath, {
enableWorkerThreads: ENABLE_WORKER_THREADS,
numWorkers: 2,
});
expect(workerTransform).toHaveBeenCalledTimes(
Object.keys(stats.compilation.assets).length
);
expect(workerEnd).toHaveBeenCalledTimes(1);

expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});

it('matches snapshot for `true` value', () => {
it('should match snapshot for the "true" value when only one file passed', async () => {
compiler = getCompiler({
entry: path.resolve(__dirname, "./fixtures/entry.js"),
});

new TerserPlugin({ parallel: true }).apply(compiler);

return compile(compiler).then((stats) => {
const errors = stats.compilation.errors.map(cleanErrorStack);
const warnings = stats.compilation.warnings.map(cleanErrorStack);

expect(workerFarm.mock.calls.length).toBe(1);
expect(workerFarm.mock.calls[0][0].maxConcurrentWorkers).toBe(
os.cpus().length - 1
);
expect(workerFarmMock.mock.calls.length).toBe(
Object.keys(stats.compilation.assets).length
);
expect(workerFarm.end.mock.calls.length).toBe(1);
expect(errors).toMatchSnapshot('errors');
expect(warnings).toMatchSnapshot('warnings');

for (const file in stats.compilation.assets) {
if (
Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
) {
expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
}
}
const stats = await compile(compiler);

expect(Worker).toHaveBeenCalledTimes(1);
expect(Worker).toHaveBeenLastCalledWith(workerPath, {
enableWorkerThreads: ENABLE_WORKER_THREADS,
numWorkers: Math.min(1, os.cpus().length - 1),
});
expect(workerTransform).toHaveBeenCalledTimes(
Object.keys(stats.compilation.assets).length
);
expect(workerEnd).toHaveBeenCalledTimes(1);

expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});

it('matches snapshot for `2` value (number)', () => {
new TerserPlugin({ parallel: 2 }).apply(compiler);
it('should match snapshot for the "true" value and the number of files is less than the number of cores', async () => {
const entries = {};

for (let i = 0; i < os.cpus().length / 2; i++) {
entries[`entry-${i}`] = path.resolve(__dirname, "./fixtures/entry.js");
}

compiler = getCompiler({ entry: entries });

return compile(compiler).then((stats) => {
const errors = stats.compilation.errors.map(cleanErrorStack);
const warnings = stats.compilation.warnings.map(cleanErrorStack);

expect(workerFarm.mock.calls.length).toBe(1);
expect(workerFarm.mock.calls[0][0].maxConcurrentWorkers).toBe(2);
expect(workerFarmMock.mock.calls.length).toBe(
Object.keys(stats.compilation.assets).length
);
expect(workerFarm.end.mock.calls.length).toBe(1);
expect(errors).toMatchSnapshot('errors');
expect(warnings).toMatchSnapshot('warnings');

for (const file in stats.compilation.assets) {
if (
Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
) {
expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
}
}
new TerserPlugin({ parallel: true }).apply(compiler);

const stats = await compile(compiler);

expect(Worker).toHaveBeenCalledTimes(1);
expect(Worker).toHaveBeenLastCalledWith(workerPath, {
enableWorkerThreads: ENABLE_WORKER_THREADS,
numWorkers: Math.min(Object.keys(entries).length, os.cpus().length - 1),
});
expect(workerTransform).toHaveBeenCalledTimes(
Object.keys(stats.compilation.assets).length
);
expect(workerEnd).toHaveBeenCalledTimes(1);

expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});

it('should match snapshot for the "true" value and the number of files is same than the number of cores', async () => {
const entries = {};

for (let i = 0; i < os.cpus().length; i++) {
entries[`entry-${i}`] = path.resolve(__dirname, "./fixtures/entry.js");
}

compiler = getCompiler({ entry: entries });

new TerserPlugin({ parallel: true }).apply(compiler);

const stats = await compile(compiler);

expect(Worker).toHaveBeenCalledTimes(1);
expect(Worker).toHaveBeenLastCalledWith(workerPath, {
enableWorkerThreads: ENABLE_WORKER_THREADS,
numWorkers: Math.min(Object.keys(entries).length, os.cpus().length - 1),
});
expect(workerTransform).toHaveBeenCalledTimes(
Object.keys(stats.compilation.assets).length
);
expect(workerEnd).toHaveBeenCalledTimes(1);

expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});

it('should match snapshot for the "true" value and the number of files is more than the number of cores', async () => {
const entries = {};

for (let i = 0; i < os.cpus().length * 2; i++) {
entries[`entry-${i}`] = path.resolve(__dirname, "./fixtures/entry.js");
}

compiler = getCompiler({
entry: {
one: path.resolve(__dirname, "./fixtures/entry.js"),
two: path.resolve(__dirname, "./fixtures/entry.js"),
three: path.resolve(__dirname, "./fixtures/entry.js"),
four: path.resolve(__dirname, "./fixtures/entry.js"),
five: path.resolve(__dirname, "./fixtures/entry.js"),
six: path.resolve(__dirname, "./fixtures/entry.js"),
seven: path.resolve(__dirname, "./fixtures/entry.js"),
eight: path.resolve(__dirname, "./fixtures/entry.js"),
},
});

new TerserPlugin({ parallel: true }).apply(compiler);

const stats = await compile(compiler);

expect(Worker).toHaveBeenCalledTimes(1);
expect(Worker).toHaveBeenLastCalledWith(workerPath, {
enableWorkerThreads: ENABLE_WORKER_THREADS,
numWorkers: Math.min(Object.keys(entries).length, os.cpus().length - 1),
});
expect(workerTransform).toHaveBeenCalledTimes(
Object.keys(stats.compilation.assets).length
);
expect(workerEnd).toHaveBeenCalledTimes(1);

expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});
});
177 changes: 0 additions & 177 deletions test/sourceMap-option.test.js

This file was deleted.

102 changes: 0 additions & 102 deletions test/supports-multicompiler.test.js

This file was deleted.

742 changes: 261 additions & 481 deletions test/terserOptions-option.test.js

Large diffs are not rendered by default.

182 changes: 71 additions & 111 deletions test/test-option.test.js
Original file line number Diff line number Diff line change
@@ -1,165 +1,125 @@
import TerserPlugin from '../src/index';
import path from "path";

import { cleanErrorStack, createCompiler, compile } from './helpers';
import TerserPlugin from "../src/index";

describe('when applied with `test` option', () => {
import {
compile,
getCompiler,
getErrors,
getWarnings,
readsAssets,
} from "./helpers";

describe("test option", () => {
let compiler;

beforeEach(() => {
compiler = createCompiler({
compiler = getCompiler({
entry: {
js: `${__dirname}/fixtures/entry.js`,
mjs: `${__dirname}/fixtures/entry.mjs`,
importExport: `${__dirname}/fixtures/import-export/entry.js`,
AsyncImportExport: `${__dirname}/fixtures/async-import-export/entry.js`,
js: path.resolve(__dirname, "./fixtures/entry.js"),
mjs: path.resolve(__dirname, "./fixtures/entry.mjs"),
importExport: path.resolve(
__dirname,
"./fixtures/import-export/entry.js"
),
AsyncImportExport: path.resolve(
__dirname,
"./fixtures/async-import-export/entry.js"
),
},
output: {
path: `${__dirname}/dist`,
filename: '[name].js?var=[hash]',
chunkFilename: '[id].[name].js?ver=[hash]',
path: path.resolve(__dirname, "./dist"),
filename: `[name].js?var=[fullhash]`,
chunkFilename: `[id].[name].js?ver=[fullhash]`,
},
});
});

it('matches snapshot with empty value', () => {
it("should match snapshot with empty value", async () => {
new TerserPlugin().apply(compiler);

return compile(compiler).then((stats) => {
const errors = stats.compilation.errors.map(cleanErrorStack);
const warnings = stats.compilation.warnings.map(cleanErrorStack);

expect(errors).toMatchSnapshot('errors');
expect(warnings).toMatchSnapshot('warnings');
const stats = await compile(compiler);

for (const file in stats.compilation.assets) {
if (
Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
) {
expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
}
}
});
expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});

it('matches snapshot for a single `test` value ({RegExp})', () => {
it("should match snapshot for a single `test` value ({RegExp})", async () => {
new TerserPlugin({
test: /(m)?js\.js(\?.*)?$/i,
}).apply(compiler);

return compile(compiler).then((stats) => {
const errors = stats.compilation.errors.map(cleanErrorStack);
const warnings = stats.compilation.warnings.map(cleanErrorStack);

expect(errors).toMatchSnapshot('errors');
expect(warnings).toMatchSnapshot('warnings');
const stats = await compile(compiler);

for (const file in stats.compilation.assets) {
if (
Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
) {
expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
}
}
});
expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});

it('matches snapshot for a single `test` value ({String})', () => {
it('should match snapshot for a single "test" value ({String})', async () => {
new TerserPlugin({
test: 'js.js',
test: "js.js",
}).apply(compiler);

return compile(compiler).then((stats) => {
const errors = stats.compilation.errors.map(cleanErrorStack);
const warnings = stats.compilation.warnings.map(cleanErrorStack);

expect(errors).toMatchSnapshot('errors');
expect(warnings).toMatchSnapshot('warnings');
const stats = await compile(compiler);

for (const file in stats.compilation.assets) {
if (
Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
) {
expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
}
}
});
expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});

it('matches snapshot for multiple `test` values ({RegExp})', () => {
it('should match snapshot for multiple "test" values ({RegExp})', async () => {
new TerserPlugin({
test: [/(m)?js\.js(\?.*)?$/i, /AsyncImportExport\.js(\?.*)?$/i],
}).apply(compiler);

return compile(compiler).then((stats) => {
const errors = stats.compilation.errors.map(cleanErrorStack);
const warnings = stats.compilation.warnings.map(cleanErrorStack);
const stats = await compile(compiler);

expect(errors).toMatchSnapshot('errors');
expect(warnings).toMatchSnapshot('warnings');

for (const file in stats.compilation.assets) {
if (
Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
) {
expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
}
}
});
expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});

it('matches snapshot for multiple `test` values ({String})', () => {
it('should match snapshot for multiple "test" values ({String})', async () => {
new TerserPlugin({
test: ['js.js', 'AsyncImportExport.js'],
test: ["js.js", "AsyncImportExport.js"],
}).apply(compiler);

return compile(compiler).then((stats) => {
const errors = stats.compilation.errors.map(cleanErrorStack);
const warnings = stats.compilation.warnings.map(cleanErrorStack);

expect(errors).toMatchSnapshot('errors');
expect(warnings).toMatchSnapshot('warnings');
const stats = await compile(compiler);

for (const file in stats.compilation.assets) {
if (
Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
) {
expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
}
}
});
expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});

it('matches snapshot and uglify `mjs`', () => {
compiler = createCompiler({
it('should match snapshot and uglify "mjs"', async () => {
compiler = getCompiler({
entry: {
js: `${__dirname}/fixtures/entry.js`,
mjs: `${__dirname}/fixtures/entry.mjs`,
importExport: `${__dirname}/fixtures/import-export/entry.js`,
AsyncImportExport: `${__dirname}/fixtures/async-import-export/entry.js`,
js: path.resolve(__dirname, "./fixtures/entry.js"),
mjs: path.resolve(__dirname, "./fixtures/entry.mjs"),
importExport: path.resolve(
__dirname,
"./fixtures/import-export/entry.js"
),
AsyncImportExport: path.resolve(
__dirname,
"./fixtures/async-import-export/entry.js"
),
},
output: {
path: `${__dirname}/dist`,
filename: '[name].mjs?var=[hash]',
chunkFilename: '[id].[name].mjs?ver=[hash]',
path: path.resolve(__dirname, "./dist"),
filename: `[name].mjs?var=[fullhash]`,
chunkFilename: `[id].[name].mjs?ver=[fullhash]`,
},
});

new TerserPlugin().apply(compiler);

return compile(compiler).then((stats) => {
const errors = stats.compilation.errors.map(cleanErrorStack);
const warnings = stats.compilation.warnings.map(cleanErrorStack);

expect(errors).toMatchSnapshot('errors');
expect(warnings).toMatchSnapshot('warnings');
const stats = await compile(compiler);

for (const file in stats.compilation.assets) {
if (
Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
) {
expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
}
}
});
expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});
});
85 changes: 16 additions & 69 deletions test/validation.test.js → test/validate-options.test.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import TerserPlugin from '../src';
import TerserPlugin from "../src";

it('validation', () => {
it("validation", () => {
/* eslint-disable no-new */
expect(() => {
new TerserPlugin({ test: /foo/ });
}).not.toThrow();

expect(() => {
new TerserPlugin({ test: 'foo' });
new TerserPlugin({ test: "foo" });
}).not.toThrow();

expect(() => {
@@ -19,11 +19,11 @@ it('validation', () => {
}).not.toThrow();

expect(() => {
new TerserPlugin({ test: ['foo', 'bar'] });
new TerserPlugin({ test: ["foo", "bar"] });
}).not.toThrow();

expect(() => {
new TerserPlugin({ test: [/foo/, 'bar'] });
new TerserPlugin({ test: [/foo/, "bar"] });
}).not.toThrow();

expect(() => {
@@ -39,7 +39,7 @@ it('validation', () => {
}).not.toThrow();

expect(() => {
new TerserPlugin({ include: 'foo' });
new TerserPlugin({ include: "foo" });
}).not.toThrow();

expect(() => {
@@ -51,11 +51,11 @@ it('validation', () => {
}).not.toThrow();

expect(() => {
new TerserPlugin({ include: ['foo', 'bar'] });
new TerserPlugin({ include: ["foo", "bar"] });
}).not.toThrow();

expect(() => {
new TerserPlugin({ include: [/foo/, 'bar'] });
new TerserPlugin({ include: [/foo/, "bar"] });
}).not.toThrow();

expect(() => {
@@ -71,7 +71,7 @@ it('validation', () => {
}).not.toThrow();

expect(() => {
new TerserPlugin({ exclude: 'foo' });
new TerserPlugin({ exclude: "foo" });
}).not.toThrow();

expect(() => {
@@ -83,11 +83,11 @@ it('validation', () => {
}).not.toThrow();

expect(() => {
new TerserPlugin({ exclude: ['foo', 'bar'] });
new TerserPlugin({ exclude: ["foo", "bar"] });
}).not.toThrow();

expect(() => {
new TerserPlugin({ exclude: [/foo/, 'bar'] });
new TerserPlugin({ exclude: [/foo/, "bar"] });
}).not.toThrow();

expect(() => {
@@ -98,38 +98,6 @@ it('validation', () => {
new TerserPlugin({ exclude: [true] });
}).toThrowErrorMatchingSnapshot();

expect(() => {
new TerserPlugin({ chunkFilter: () => {} });
}).not.toThrow();

expect(() => {
new TerserPlugin({ chunkFilter: true });
}).toThrowErrorMatchingSnapshot();

expect(() => {
new TerserPlugin({ cache: true });
}).not.toThrow();

expect(() => {
new TerserPlugin({ cache: false });
}).not.toThrow();

expect(() => {
new TerserPlugin({ cache: 'path/to/cache/directory' });
}).not.toThrow();

expect(() => {
new TerserPlugin({ cache: {} });
}).toThrowErrorMatchingSnapshot();

expect(() => {
new TerserPlugin({ cacheKeys() {} });
}).not.toThrow();

expect(() => {
new TerserPlugin({ cacheKeys: 'test' });
}).toThrowErrorMatchingSnapshot();

expect(() => {
new TerserPlugin({ parallel: true });
}).not.toThrow();
@@ -143,25 +111,13 @@ it('validation', () => {
}).not.toThrow();

expect(() => {
new TerserPlugin({ parallel: '2' });
new TerserPlugin({ parallel: "2" });
}).toThrowErrorMatchingSnapshot();

expect(() => {
new TerserPlugin({ parallel: {} });
}).toThrowErrorMatchingSnapshot();

expect(() => {
new TerserPlugin({ sourceMap: true });
}).not.toThrow();

expect(() => {
new TerserPlugin({ sourceMap: false });
}).not.toThrow();

expect(() => {
new TerserPlugin({ sourceMap: 'true' });
}).toThrowErrorMatchingSnapshot();

expect(() => {
new TerserPlugin({ minify() {} });
}).not.toThrow();
@@ -183,7 +139,6 @@ it('validation', () => {
terserOptions: {
// eslint-disable-next-line no-undefined
ecma: undefined,
warnings: false,
parse: {},
compress: {},
mangle: true,
@@ -212,7 +167,7 @@ it('validation', () => {
}).not.toThrow();

expect(() => {
new TerserPlugin({ extractComments: 'comment' });
new TerserPlugin({ extractComments: "comment" });
}).not.toThrow();

expect(() => {
@@ -234,7 +189,7 @@ it('validation', () => {
expect(() => {
new TerserPlugin({
extractComments: {
condition: 'comment',
condition: "comment",
},
});
}).not.toThrow();
@@ -266,7 +221,7 @@ it('validation', () => {
expect(() => {
new TerserPlugin({
extractComments: {
filename: 'test.js',
filename: "test.js",
},
});
}).not.toThrow();
@@ -298,7 +253,7 @@ it('validation', () => {
expect(() => {
new TerserPlugin({
extractComments: {
banner: 'banner',
banner: "banner",
},
});
}).not.toThrow();
@@ -323,14 +278,6 @@ it('validation', () => {
new TerserPlugin({ extractComments: { unknown: true } });
}).toThrowErrorMatchingSnapshot();

expect(() => {
new TerserPlugin({ warningsFilter() {} });
}).not.toThrow();

expect(() => {
new TerserPlugin({ warningsFilter: true });
}).toThrowErrorMatchingSnapshot();

expect(() => {
new TerserPlugin({ unknown: true });
}).toThrowErrorMatchingSnapshot();
144 changes: 0 additions & 144 deletions test/warningsFilter-option.test.js

This file was deleted.

327 changes: 255 additions & 72 deletions test/worker.test.js
Original file line number Diff line number Diff line change
@@ -1,118 +1,301 @@
import serialize from 'serialize-javascript';
import serialize from "serialize-javascript";

import worker from '../src/worker';
import { transform } from "../src/minify";

describe('matches snapshot', () => {
it('normalizes when options.extractComments is regex', () => {
describe("worker", () => {
it('should match snapshot when options.extractComments is "false"', async () => {
const options = {
file: 'test1.js',
input: 'var foo = 1;/* hello */',
extractComments: /foo/,
name: "test1.js",
input:
"var foo = 1;/* hello */\n// Comment\n/* duplicate */\n/* duplicate */",
extractComments: false,
};
worker(serialize(options), (error, data) => {
if (error) {
throw error;
}
expect(data).toMatchSnapshot(options.file);
});
const workerResult = await transform(serialize(options));

expect(workerResult).toMatchSnapshot();
});

it('should match snapshot when options.extractComments is "true"', async () => {
const options = {
name: "test1.js",
input:
"var foo = 1;/* hello */\n// Comment\n/* duplicate */\n/* duplicate */",
extractComments: true,
};
const workerResult = await transform(serialize(options));

expect(workerResult).toMatchSnapshot();
});

it("should match snapshot when options.extractComments is RegExp", async () => {
const options = {
name: "test1.js",
input:
"var foo = 1;/* hello */\n// Comment\n/* duplicate */\n/* duplicate */",
extractComments: /hello/,
};
const workerResult = await transform(serialize(options));

expect(workerResult).toMatchSnapshot();
});

it("should match snapshot when options.extractComments is Function", async () => {
const options = {
name: "test1.js",
input:
"var foo = 1;/* hello */\n// Comment\n/* duplicate */\n/* duplicate */",
extractComments: () => true,
};
const workerResult = await transform(serialize(options));

expect(workerResult).toMatchSnapshot();
});

it("should match snapshot when options.extractComments is empty Object", async () => {
const options = {
name: "test1.js",
input:
"var foo = 1;/* hello */\n// Comment\n/* duplicate */\n/* duplicate */",
extractComments: {},
};
const workerResult = await transform(serialize(options));

expect(workerResult).toMatchSnapshot();
});

it('should match snapshot when options.extractComments is Object with "true" value', async () => {
const options = {
name: "test1.js",
input:
"var foo = 1;/* hello */\n// Comment\n/* duplicate */\n/* duplicate */",
extractComments: {
condition: true,
},
};
const workerResult = await transform(serialize(options));

expect(workerResult).toMatchSnapshot();
});

it('should match snapshot when options.extractComments is Object with "some" value', async () => {
const options = {
name: "test1.js",
input:
"var foo = 1;/* hello */\n// Comment\n/* duplicate */\n/* duplicate */",
extractComments: {
condition: "some",
},
};
const workerResult = await transform(serialize(options));

expect(workerResult).toMatchSnapshot();
});

it('should match snapshot when options.extractComments is Object with "all" value', async () => {
const options = {
name: "test1.js",
input:
"var foo = 1;/* hello */\n// Comment\n/* duplicate */\n/* duplicate */",
extractComments: {
condition: "all",
},
};
const workerResult = await transform(serialize(options));

expect(workerResult).toMatchSnapshot();
});

it('should match snapshot when options.extractComments is "all" value', async () => {
const options = {
name: "test1.js",
input:
"var foo = 1;/* hello */\n// Comment\n/* duplicate */\n/* duplicate */",
extractComments: "all",
};
const workerResult = await transform(serialize(options));

expect(workerResult).toMatchSnapshot();
});

it('normalizes when terserOptions.output.comments is string: all', () => {
it('should match snapshot when options.extractComments is "some" value', async () => {
const options = {
file: 'test2.js',
input: 'var foo = 1;/* hello */',
terserOptions: {
name: "test1.js",
input:
"var foo = 1;/* hello */\n// Comment\n/* duplicate */\n/* duplicate */",
extractComments: "some",
};
const workerResult = await transform(serialize(options));

expect(workerResult).toMatchSnapshot();
});

it("normalizes when minimizerOptions.output.comments is string: all", async () => {
const options = {
name: "test2.js",
input:
"var foo = 1;/* hello */\n// Comment\n/* duplicate */\n/* duplicate */",
minifyOptions: {
output: {
comments: 'all',
comments: "all",
},
},
};
worker(serialize(options), (error, data) => {
if (error) {
throw error;
}
expect(data).toMatchSnapshot(options.file);
});
const workerResult = await transform(serialize(options));

expect(workerResult).toMatchSnapshot();
});

it("should match snapshot when minimizerOptions.compress.comments is boolean", async () => {
const options = {
name: "test3.js",
input:
"var foo = 1;/* hello */\n// Comment\n/* duplicate */\n/* duplicate */",
minifyOptions: {
compress: true,
},
};
const workerResult = await transform(serialize(options));

expect(workerResult).toMatchSnapshot();
});

it('normalizes when terserOptions.output.comments is string: some', () => {
it("should match snapshot when minimizerOptions.compress.comments is object", async () => {
const options = {
file: 'test3.js',
input: 'var foo = 1;/* hello */',
terserOptions: {
name: "test3.js",
input:
"var foo = 1;/* hello */\n// Comment\n/* duplicate */\n/* duplicate */",
minifyOptions: {
compress: {
passes: 2,
},
},
};
const workerResult = await transform(serialize(options));

expect(workerResult).toMatchSnapshot();
});

it("should match snapshot when minimizerOptions.output.comments is string: some", async () => {
const options = {
name: "test3.js",
input:
"var foo = 1;/* hello */\n// Comment\n/* duplicate */\n/* duplicate */",
minifyOptions: {
output: {
comments: 'some',
comments: "some",
},
},
};
worker(serialize(options), (error, data) => {
if (error) {
throw error;
}
expect(data).toMatchSnapshot(options.file);
});
const workerResult = await transform(serialize(options));

expect(workerResult).toMatchSnapshot();
});

it('normalizes when terserOptions.extractComments is number', () => {
it("should match snapshot when minimizerOptions.extractComments is number", async () => {
const options = {
file: 'test4.js',
input: 'var foo = 1;/* hello */',
terserOptions: {
name: "test4.js",
input:
"var foo = 1;/* hello */\n// Comment\n/* duplicate */\n/* duplicate */",
minifyOptions: {
output: {
comments: 'some',
comments: "some",
},
},
extractComments: 1,
};
worker(serialize(options), (error, data) => {
if (error) {
throw error;
}
expect(data).toMatchSnapshot(options.file);
});
const workerResult = await transform(serialize(options));

expect(workerResult).toMatchSnapshot();
});

it('when applied with extract option set to a single file', () => {
it("should match snapshot with extract option set to a single file", async () => {
const options = {
file: 'test5.js',
input: '/******/ function hello(a) {console.log(a)}',
terserOptions: {
name: "test5.js",
input: "/******/ function hello(a) {console.log(a)}",
minifyOptions: {
output: {
comments: 'all',
comments: "all",
},
},
extractComments: {
condition: 'should be extracted',
// TODO https://github.com/yahoo/serialize-javascript/issues/44
// filename: (file) => file.replace(/(\.\w+)$/, '.license$1'),
condition: "should be extracted",
filename: (file) => file.filename.replace(/(\.\w+)$/, ".license$1"),
banner: (licenseFile) =>
`License information can be found in ${licenseFile}`,
},
};
worker(serialize(options), (error, data) => {
if (error) {
throw error;
}
expect(data).toMatchSnapshot(options.file);
});
const workerResult = await transform(serialize(options));

expect(workerResult).toMatchSnapshot();
});

it('when options.inputSourceMap', () => {
it("should match snapshot with options.inputSourceMap", async () => {
const options = {
file: 'test6.js',
input: 'function foo(x) { if (x) { return bar(); not_called1(); } }',
name: "test6.js",
input: "function foo(x) { if (x) { return bar(); not_called1(); } }",
inputSourceMap: {
version: 3,
sources: ['test1.js'],
names: ['foo', 'x', 'bar', 'not_called1'],
mappings: 'AAAA,QAASA,KAAIC,GACT,GAAIA,EAAG,CACH,MAAOC,MACPC',
sources: ["test1.js"],
names: ["foo", "x", "bar", "not_called1"],
mappings: "AAAA,QAASA,KAAIC,GACT,GAAIA,EAAG,CACH,MAAOC,MACPC",
},
};
const workerResult = await transform(serialize(options));

expect(workerResult).toMatchSnapshot();
});

it('should match snapshot when options.extractComments is "true"', async () => {
const options = {
name: "test1.js",
input:
"var foo = 1;/* hello */\n// Comment\n/* duplicate */\n/* duplicate */",
minify: (item) => item["test1.js"],
};
const workerResult = await transform(serialize(options));

expect(workerResult).toMatchSnapshot();
});

it('should match snapshot when minimizerOptions.mangle is "null"', async () => {
const options = {
name: "test4.js",
input:
"var foo = 1;/* hello */\n// Comment\n/* duplicate */\n/* duplicate */",
minifyOptions: {
mangle: null,
},
};
worker(serialize(options), (error, data) => {
if (error) {
throw error;
}
expect(data).toMatchSnapshot(options.file);
});
const workerResult = await transform(serialize(options));

expect(workerResult).toMatchSnapshot();
});

it("should match snapshot when minimizerOptions.mangle is boolean", async () => {
const options = {
name: "test4.js",
input:
"var foo = 1;/* hello */\n// Comment\n/* duplicate */\n/* duplicate */",
minifyOptions: {
mangle: true,
},
};
const workerResult = await transform(serialize(options));

expect(workerResult).toMatchSnapshot();
});

it("should match snapshot when minimizerOptions.mangle is object", async () => {
const options = {
name: "test4.js",
input:
"var foo = 1;/* hello */\n// Comment\n/* duplicate */\n/* duplicate */",
minifyOptions: {
mangle: { toplevel: true },
},
};
const workerResult = await transform(serialize(options));

expect(workerResult).toMatchSnapshot();
});
});
13 changes: 13 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "esnext",
"moduleResolution": "node",
"allowJs": true,
"checkJs": true,
"strict": true,
"types": ["node"],
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true
},
"include": ["./src/**/*"]
}