├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitattributes
├── .github
├── CODEOWNERS
├── CONTRIBUTING.md
├── FUNDING.yml
├── ISSUE_TEMPLATE.md
├── ISSUE_TEMPLATE
│ ├── BUG.md
│ ├── DOCS.md
│ ├── FEATURE.md
│ ├── MODIFICATION.md
│ └── SUPPORT.md
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .prettierignore
├── .prettierrc.js
├── CHANGELOG.md
├── LICENSE
├── README.md
├── azure-pipelines.yml
├── babel.config.js
├── commitlint.config.js
├── husky.config.js
├── lint-staged.config.js
├── package-lock.json
├── package.json
├── src
├── TaskRunner.js
├── cjs.js
├── index.js
├── minify.js
├── options.json
└── worker.js
└── test
├── UglifyJsPlugin.test.js
├── __snapshots__
├── UglifyJsPlugin.test.js.snap
├── cache-option.test.js.snap
├── chunkFilter-option.test.js.snap
├── exclude-option.test.js.snap
├── extractComments-option.test.js.snap
├── include-option.test.js.snap
├── minify-option.test.js.snap
├── parallel-option-failure.test.js.snap
├── parallel-option.test.js.snap
├── sourceMap-option.test.js.snap
├── supports-multicompiler.test.js.snap
├── test-option.test.js.snap
├── uglifyOptions-option.test.js.snap
├── validation.test.js.snap
├── warningsFilter-option.test.js.snap
└── worker.test.js.snap
├── cache-option.test.js
├── chunkFilter-option.test.js
├── cjs.test.js
├── exclude-option.test.js
├── extractComments-option.test.js
├── fixtures
├── async-import-export
│ ├── async-dep.js
│ └── entry.js
├── cache-1.js
├── cache-2.js
├── cache-3.js
├── cache-4.js
├── cache.js
├── comments-2.js
├── comments-3.js
├── comments-4.js
├── comments.js
├── entry.js
├── entry.mjs
├── excluded1.js
├── excluded2.js
├── import-export
│ ├── async-dep.js
│ ├── dep.js
│ └── entry.js
├── included1.js
├── included2.js
├── minify
│ ├── es5.js
│ └── es6.js
├── nested
│ └── comments.js
├── unreachable-code-2.js
└── unreachable-code.js
├── helpers.js
├── include-option.test.js
├── minify-option.test.js
├── parallel-option-failure.test.js
├── parallel-option.test.js
├── sourceMap-option.test.js
├── supports-multicompiler.test.js
├── test-option.test.js
├── uglifyOptions-option.test.js
├── validation.test.js
├── warningsFilter-option.test.js
└── worker.test.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | insert_final_newline = true
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /coverage
2 | /dist
3 | /node_modules
4 | /test/fixtures
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: ['@webpack-contrib/eslint-config-webpack', 'prettier'],
4 | };
5 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | package-lock.json -diff
2 | * text=auto
3 | bin/* eol=lf
4 | yarn.lock -diff
5 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # These are the default owners for everything in
2 | # webpack-contrib
3 | @webpack-contrib/org-maintainers
4 |
5 | # Add repository specific users / groups
6 | # below here for libs that are not maintained by the org.
7 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing in @webpack-contrib
2 |
3 | We'd always love contributions to further improve the webpack / webpack-contrib ecosystem!
4 | Here are the guidelines we'd like you to follow:
5 |
6 | - [Questions and Problems](#question)
7 | - [Issues and Bugs](#issue)
8 | - [Feature Requests](#feature)
9 | - [Pull Request Submission Guidelines](#submit-pr)
10 | - [Commit Message Conventions](#commit)
11 |
12 | ## Got a Question or Problem?
13 |
14 | Please submit support requests and questions to StackOverflow using the tag [[webpack]](http://stackoverflow.com/tags/webpack).
15 | StackOverflow is better suited for this kind of support though you may also inquire in [Webpack Gitter](https://gitter.im/webpack/webpack).
16 | The issue tracker is for bug reports and feature discussions.
17 |
18 | ## Found an Issue or Bug?
19 |
20 | Before you submit an issue, please search the issue tracker, maybe an issue for your problem already exists and the discussion might inform you of workarounds readily available.
21 |
22 | We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it. In order to reproduce bugs, we ask that you to provide a minimal reproduction scenario (github repo or failing test case). Having a live, reproducible scenario gives us a wealth of important information without going back & forth to you with additional questions like:
23 |
24 | - version of Webpack used
25 | - version of the loader / plugin you are creating a bug report for
26 | - the use-case that fails
27 |
28 | A minimal reproduce scenario allows us to quickly confirm a bug (or point out config problems) as well as confirm that we are fixing the right problem.
29 |
30 | We will be insisting on a minimal reproduce scenario in order to save maintainers time and ultimately be able to fix more bugs. We understand that sometimes it might be hard to extract essentials bits of code from a larger code-base but we really need to isolate the problem before we can fix it.
31 |
32 | Unfortunately, we are not able to investigate / fix bugs without a minimal reproduction, so if we don't hear back from you we are going to close an issue that doesn't have enough info to be reproduced.
33 |
34 | ## Feature Requests?
35 |
36 | You can _request_ a new feature by creating an issue on Github.
37 |
38 | If you would like to _implement_ a new feature, please submit an issue with a proposal for your work `first`, to be sure that particular makes sense for the project.
39 |
40 | ## Pull Request Submission Guidelines
41 |
42 | Before you submit your Pull Request (PR) consider the following guidelines:
43 |
44 | - Search Github for an open or closed PR that relates to your submission. You don't want to duplicate effort.
45 | - Commit your changes using a descriptive commit message that follows our [commit message conventions](#commit). Adherence to these conventions is necessary because release notes are automatically generated from these messages.
46 | - Fill out our `Pull Request Template`. Your pull request will not be considered if it is ignored.
47 | - Please sign the `Contributor License Agreement (CLA)` when a pull request is opened. We cannot accept your pull request without this. Make sure you sign with the primary email address associated with your local / github account.
48 |
49 | ## Webpack Contrib Commit Conventions
50 |
51 | Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
52 | format that includes a **type**, a **scope** and a **subject**:
53 |
54 | ```
55 | ():
56 |
57 |
58 |
59 |
60 | ```
61 |
62 | The **header** is mandatory and the **scope** of the header is optional.
63 |
64 | Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
65 | to read on GitHub as well as in various git tools.
66 |
67 | The footer should contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages/) if any.
68 |
69 | Examples:
70 |
71 | ```
72 | docs(readme): update install instructions
73 | ```
74 |
75 | ```
76 | fix: refer to the `entrypoint` instead of the first `module`
77 | ```
78 |
79 | ### Revert
80 |
81 | If the commit reverts a previous commit, it should begin with `revert:`, followed by the header of the reverted commit.
82 | In the body it should say: `This reverts commit .`, where the hash is the SHA of the commit being reverted.
83 |
84 | ### Type
85 |
86 | Must be one of the following:
87 |
88 | - **build**: Changes that affect the build system or external dependencies (example scopes: babel, npm)
89 | - **chore**: Changes that fall outside of build / docs that do not effect source code (example scopes: package, defaults)
90 | - **ci**: Changes to our CI configuration files and scripts (example scopes: circleci, travis)
91 | - **docs**: Documentation only changes (example scopes: readme, changelog)
92 | - **feat**: A new feature
93 | - **fix**: A bug fix
94 | - **perf**: A code change that improves performance
95 | - **refactor**: A code change that neither fixes a bug nor adds a feature
96 | - **revert**: Used when reverting a committed change
97 | - **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons)
98 | - **test**: Addition of or updates to Jest tests
99 |
100 | ### Scope
101 |
102 | The scope is subjective & depends on the `type` see above. A good example would be a change to a particular class / module.
103 |
104 | ### Subject
105 |
106 | The subject contains a succinct description of the change:
107 |
108 | - use the imperative, present tense: "change" not "changed" nor "changes"
109 | - don't capitalize the first letter
110 | - no dot (.) at the end
111 |
112 | ### Body
113 |
114 | Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes".
115 | The body should include the motivation for the change and contrast this with previous behavior.
116 |
117 | ### Footer
118 |
119 | The footer should contain any information about **Breaking Changes** and is also the place to
120 | reference GitHub issues that this commit **Closes**.
121 |
122 | **Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
123 |
124 | Example
125 |
126 | ```
127 | BREAKING CHANGE: Updates to `Chunk.mapModules`.
128 |
129 | This release is not backwards compatible with `Webpack 2.x` due to breaking changes in webpack/webpack#4764
130 | Migration: see webpack/webpack#5225
131 |
132 | ```
133 |
134 | ## Testing Your Pull Request
135 |
136 | You may have the need to test your changes in a real-world project or dependent
137 | module. Thankfully, Github provides a means to do this. Add a dependency to the
138 | `package.json` for such a project as follows:
139 |
140 | ```json
141 | {
142 | "devDependencies": {
143 | "uglifyjs-webpack-plugin": "webpack-contrib/uglifyjs-webpack-plugin#{id}/head"
144 | }
145 | }
146 | ```
147 |
148 | Where `{id}` is the # ID of your Pull Request.
149 |
150 | ## Contributor License Agreement
151 |
152 | When submitting your contribution, a CLA (Contributor License Agreement) bot will come by to verify that you signed the [CLA](https://cla.js.foundation/webpack-contrib/uglifyjs-webpack-plugin).
153 | If it is your first time, it will link you to the right place to sign it.
154 | However, if you have committed your contributions using an email that is not the same as your email used on GitHub, the CLA bot can't accept your contribution.
155 |
156 | Run `git config user.email` to see your Git email, and verify it with [your GitHub email](https://github.com/settings/emails).
157 |
158 | ## Thanks
159 |
160 | For your interest, time, understanding, and for following this simple guide.
161 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | open_collective: webpack
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
17 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/BUG.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 🐛 Bug Report
3 | about: Something went awry and you'd like to tell us about it.
4 | ---
5 |
6 |
16 |
17 | - Operating System:
18 | - Node Version:
19 | - NPM Version:
20 | - webpack Version:
21 | - uglifyjs-webpack-plugin Version:
22 |
23 | ### Expected Behavior
24 |
25 |
26 |
27 | ### Actual Behavior
28 |
29 |
30 |
31 | ### Code
32 |
33 | ```js
34 | // webpack.config.js
35 | // If your code blocks are over 20 lines, please paste a link to a gist
36 | // (https://gist.github.com).
37 | ```
38 |
39 | ```js
40 | // additional code, HEY YO remove this block if you don't need it
41 | ```
42 |
43 | ### How Do We Reproduce?
44 |
45 |
51 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/DOCS.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 📚 Documentation
3 | about: Are the docs lacking or missing something? Do they need some new 🔥 hotness? Tell us here.
4 | ---
5 |
6 |
16 |
17 | Documentation Is:
18 |
19 |
20 |
21 | - [ ] Missing
22 | - [ ] Needed
23 | - [ ] Confusing
24 | - [ ] Not Sure?
25 |
26 | ### Please Explain in Detail...
27 |
28 | ### Your Proposal for Changes
29 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/FEATURE.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: ✨ Feature Request
3 | about: Suggest an idea for this project
4 | ---
5 |
6 |
16 |
17 | - Operating System:
18 | - Node Version:
19 | - NPM Version:
20 | - webpack Version:
21 | - uglifyjs-webpack-plugin Version:
22 |
23 | ### Feature Proposal
24 |
25 | ### Feature Use Case
26 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/MODIFICATION.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 🔧 Modification Request
3 | about: Would you like something work differently? Have an alternative approach? This is the template for you.
4 | ---
5 |
6 |
16 |
17 | - Operating System:
18 | - Node Version:
19 | - NPM Version:
20 | - webpack Version:
21 | - uglifyjs-webpack-plugin Version:
22 |
23 | ### Expected Behavior / Situation
24 |
25 | ### Actual Behavior / Situation
26 |
27 | ### Modification Proposal
28 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/SUPPORT.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 🆘 Support, Help, and Advice
3 | about: 👉🏽 Need support, help, or advice? Don't open an issue! Head to StackOverflow or https://gitter.im/webpack/webpack.
4 | ---
5 |
6 | Hey there! If you need support, help, or advice then this is not the place to ask.
7 | Please visit [StackOverflow](https://stackoverflow.com/questions/tagged/webpack)
8 | or [the Webpack Gitter](https://gitter.im/webpack/webpack) instead.
9 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
10 |
11 | This PR contains a:
12 |
13 | - [ ] **bugfix**
14 | - [ ] new **feature**
15 | - [ ] **code refactor**
16 | - [ ] **test update**
17 | - [ ] **typo fix**
18 | - [ ] **metadata update**
19 |
20 | ### Motivation / Use-Case
21 |
22 |
27 |
28 | ### Breaking Changes
29 |
30 |
34 |
35 | ### Additional Info
36 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | build/
3 | dist/
4 | coverage/
5 | *.log
6 | .eslintcache
7 |
8 |
9 | logs
10 | npm-debug.log*
11 | yarn-debug.log*
12 | /coverage
13 | /dist
14 | /local
15 | /reports
16 | /node_modules
17 | .DS_Store
18 | Thumbs.db
19 | .idea
20 | .vscode
21 | *.sublime-project
22 | *.sublime-workspace
23 | *.iml
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | /coverage
2 | /dist
3 | /node_modules
4 | /test/fixtures
5 | CHANGELOG.md
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | singleQuote: true,
3 | trailingComma: 'es5',
4 | arrowParens: 'always',
5 | };
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright JS Foundation and other contributors
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | 'Software'), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/azure-pipelines.yml:
--------------------------------------------------------------------------------
1 | trigger:
2 | - master
3 | - next
4 |
5 | jobs:
6 | - job: Lint
7 | pool:
8 | vmImage: ubuntu-16.04
9 | steps:
10 | - task: NodeTool@0
11 | inputs:
12 | versionSpec: ^10.13.0
13 | displayName: 'Install Node.js'
14 | - task: Npm@1
15 | inputs:
16 | command: custom
17 | customCommand: i -g npm@latest
18 | displayName: 'Install latest NPM'
19 | - script: |
20 | node -v
21 | npm -v
22 | displayName: 'Print versions'
23 | - task: Npm@1
24 | inputs:
25 | command: custom
26 | customCommand: ci
27 | displayName: 'Install dependencies'
28 | - script: npm run lint
29 | displayName: 'Run lint'
30 | - script: npm run security
31 | displayName: 'Run NPM audit'
32 | - script: ./node_modules/.bin/commitlint-azure-pipelines
33 | displayName: 'Run lint commit message'
34 |
35 | - job: Linux
36 | pool:
37 | vmImage: ubuntu-16.04
38 | strategy:
39 | maxParallel: 5
40 | matrix:
41 | node-12:
42 | node_version: ^12.0.0
43 | webpack_version: latest
44 | node-10:
45 | node_version: ^10.13.0
46 | webpack_version: latest
47 | node-8:
48 | node_version: ^8.9.0
49 | webpack_version: latest
50 | node-6:
51 | node_version: ^6.9.0
52 | webpack_version: latest
53 | node-8-canary:
54 | node_version: ^8.9.0
55 | webpack_version: next
56 | continue_on_error: true
57 | steps:
58 | - task: NodeTool@0
59 | inputs:
60 | versionSpec: $(node_version)
61 | displayName: 'Install Node.js $(node_version)'
62 | - task: Npm@1
63 | inputs:
64 | command: custom
65 | customCommand: i -g npm@latest
66 | displayName: 'Install latest NPM'
67 | - script: |
68 | node -v
69 | npm -v
70 | displayName: 'Print versions'
71 | - task: Npm@1
72 | inputs:
73 | command: custom
74 | customCommand: ci
75 | displayName: 'Install dependencies'
76 | - script: npm i webpack@$(webpack_version)
77 | displayName: 'Install "webpack@$(webpack_version)"'
78 | - script: npm run test:coverage -- --ci --reporters="default" --reporters="jest-junit" || $(continue_on_error)
79 | displayName: 'Run tests with coverage'
80 | - task: PublishTestResults@2
81 | inputs:
82 | testRunTitle: 'Linux with Node.js $(node_version)'
83 | testResultsFiles: '**/junit.xml'
84 | condition: succeededOrFailed()
85 | displayName: 'Publish test results'
86 | - script: curl -s https://codecov.io/bash | bash -s -- -t $(CODECOV_TOKEN)
87 | condition: succeededOrFailed()
88 | displayName: 'Submit coverage data to codecov'
89 |
90 | - job: macOS
91 | pool:
92 | vmImage: macOS-10.14
93 | strategy:
94 | maxParallel: 5
95 | matrix:
96 | node-12:
97 | node_version: ^12.0.0
98 | webpack_version: latest
99 | node-10:
100 | node_version: ^10.13.0
101 | webpack_version: latest
102 | node-8:
103 | node_version: ^8.9.0
104 | webpack_version: latest
105 | node-6:
106 | node_version: ^6.9.0
107 | webpack_version: latest
108 | node-8-canary:
109 | node_version: ^8.9.0
110 | webpack_version: next
111 | continue_on_error: true
112 | steps:
113 | - task: NodeTool@0
114 | inputs:
115 | versionSpec: $(node_version)
116 | displayName: 'Install Node.js $(node_version)'
117 | - task: Npm@1
118 | inputs:
119 | command: custom
120 | customCommand: i -g npm@latest
121 | displayName: 'Install latest NPM'
122 | - script: |
123 | node -v
124 | npm -v
125 | displayName: 'Print versions'
126 | - task: Npm@1
127 | inputs:
128 | command: custom
129 | customCommand: ci
130 | displayName: 'Install dependencies'
131 | - script: npm i webpack@$(webpack_version)
132 | displayName: 'Install "webpack@$(webpack_version)"'
133 | - script: npm run test:coverage -- --ci --reporters="default" --reporters="jest-junit" || $(continue_on_error)
134 | displayName: 'Run tests with coverage'
135 | - task: PublishTestResults@2
136 | inputs:
137 | testRunTitle: 'Linux with Node.js $(node_version)'
138 | testResultsFiles: '**/junit.xml'
139 | condition: succeededOrFailed()
140 | displayName: 'Publish test results'
141 | - script: curl -s https://codecov.io/bash | bash -s -- -t $(CODECOV_TOKEN)
142 | condition: succeededOrFailed()
143 | displayName: 'Submit coverage data to codecov'
144 |
145 | - job: Windows
146 | pool:
147 | vmImage: windows-2019
148 | strategy:
149 | maxParallel: 5
150 | matrix:
151 | node-12:
152 | node_version: ^12.0.0
153 | webpack_version: latest
154 | node-10:
155 | node_version: ^10.13.0
156 | webpack_version: latest
157 | node-8:
158 | node_version: ^8.9.0
159 | webpack_version: latest
160 | node-6:
161 | node_version: ^6.9.0
162 | webpack_version: latest
163 | node-8-canary:
164 | node_version: ^8.9.0
165 | webpack_version: next
166 | continue_on_error: true
167 | steps:
168 | - script: 'git config --global core.autocrlf input'
169 | displayName: 'Config git core.autocrlf'
170 | - checkout: self
171 | - task: NodeTool@0
172 | inputs:
173 | versionSpec: $(node_version)
174 | displayName: 'Install Node.js $(node_version)'
175 | - task: Npm@1
176 | inputs:
177 | command: custom
178 | customCommand: i -g npm@latest
179 | displayName: 'Install latest NPM'
180 | - script: |
181 | node -v
182 | npm -v
183 | displayName: 'Print versions'
184 | - task: Npm@1
185 | inputs:
186 | command: custom
187 | customCommand: ci
188 | displayName: 'Install dependencies'
189 | - script: npm i webpack@$(webpack_version)
190 | displayName: 'Install "webpack@$(webpack_version)"'
191 | - script: npm run test:coverage -- --ci --reporters="default" --reporters="jest-junit" || $(continue_on_error)
192 | displayName: 'Run tests with coverage'
193 | - task: PublishTestResults@2
194 | inputs:
195 | testRunTitle: 'Linux with Node.js $(node_version)'
196 | testResultsFiles: '**/junit.xml'
197 | condition: succeededOrFailed()
198 | displayName: 'Publish test results'
199 | - script: curl -s https://codecov.io/bash | bash -s -- -t $(CODECOV_TOKEN)
200 | condition: succeededOrFailed()
201 | displayName: 'Submit coverage data to codecov'
202 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | const MIN_BABEL_VERSION = 7;
2 |
3 | module.exports = (api) => {
4 | api.assertVersion(MIN_BABEL_VERSION);
5 | api.cache(true);
6 |
7 | return {
8 | presets: [
9 | [
10 | '@babel/preset-env',
11 | {
12 | targets: {
13 | node: '6.9.0',
14 | },
15 | },
16 | ],
17 | ],
18 | };
19 | };
20 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['@commitlint/config-conventional'],
3 | };
4 |
--------------------------------------------------------------------------------
/husky.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | hooks: {
3 | 'pre-commit': 'lint-staged',
4 | 'commit-msg': 'commitlint -E HUSKY_GIT_PARAMS',
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/lint-staged.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | '*.js': ['prettier --write', 'eslint --fix', 'git add'],
3 | '*.{json,md,yml,css}': ['prettier --write', 'git add'],
4 | };
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "uglifyjs-webpack-plugin",
3 | "version": "2.2.0",
4 | "description": "UglifyJS plugin for webpack",
5 | "license": "MIT",
6 | "repository": "webpack-contrib/uglifyjs-webpack-plugin",
7 | "author": "webpack Contrib Team",
8 | "homepage": "https://github.com/webpack-contrib/uglifyjs-webpack-plugin",
9 | "bugs": "https://github.com/webpack-contrib/uglifyjs-webpack-plugin/issues",
10 | "main": "dist/cjs.js",
11 | "engines": {
12 | "node": ">= 6.9.0"
13 | },
14 | "scripts": {
15 | "start": "npm run build -- -w",
16 | "prebuild": "npm run clean",
17 | "build": "cross-env NODE_ENV=production babel src -d dist --ignore \"src/**/*.test.js\" --copy-files",
18 | "clean": "del-cli dist",
19 | "commitlint": "commitlint --from=master",
20 | "lint:prettier": "prettier \"{**/*,*}.{js,json,md,yml,css}\" --list-different",
21 | "lint:js": "eslint --cache src test",
22 | "lint": "npm-run-all -l -p \"lint:**\"",
23 | "prepare": "npm run build",
24 | "release": "standard-version",
25 | "security": "npm audit",
26 | "test:only": "cross-env NODE_ENV=test jest",
27 | "test:watch": "cross-env NODE_ENV=test jest --watch",
28 | "test:coverage": "cross-env NODE_ENV=test jest --collectCoverageFrom=\"src/**/*.js\" --coverage",
29 | "pretest": "npm run lint",
30 | "test": "cross-env NODE_ENV=test npm run test:coverage",
31 | "defaults": "webpack-defaults"
32 | },
33 | "files": [
34 | "dist"
35 | ],
36 | "peerDependencies": {
37 | "webpack": "^4.0.0"
38 | },
39 | "dependencies": {
40 | "cacache": "^12.0.2",
41 | "find-cache-dir": "^2.1.0",
42 | "is-wsl": "^1.1.0",
43 | "schema-utils": "^1.0.0",
44 | "serialize-javascript": "^1.7.0",
45 | "source-map": "^0.6.1",
46 | "uglify-js": "^3.6.0",
47 | "webpack-sources": "^1.4.0",
48 | "worker-farm": "^1.7.0"
49 | },
50 | "devDependencies": {
51 | "@babel/cli": "^7.5.5",
52 | "@babel/core": "^7.5.5",
53 | "@babel/preset-env": "^7.5.5",
54 | "@commitlint/cli": "^8.1.0",
55 | "@commitlint/config-conventional": "^8.1.0",
56 | "@webpack-contrib/defaults": "^5.0.2",
57 | "@webpack-contrib/eslint-config-webpack": "^3.0.0",
58 | "babel-jest": "^24.8.0",
59 | "commitlint-azure-pipelines-cli": "^1.0.2",
60 | "cross-env": "^5.2.0",
61 | "del": "^4.1.1",
62 | "del-cli": "^1.1.0",
63 | "eslint": "^6.1.0",
64 | "eslint-config-prettier": "^6.0.0",
65 | "eslint-plugin-import": "^2.18.2",
66 | "husky": "^3.0.2",
67 | "jest": "^24.8.0",
68 | "jest-junit": "^7.0.0",
69 | "lint-staged": "^9.2.1",
70 | "memory-fs": "^0.4.1",
71 | "npm-run-all": "^4.1.5",
72 | "prettier": "^1.18.2",
73 | "standard-version": "^7.0.0",
74 | "terser": "^4.1.2",
75 | "webpack": "^4.38.0"
76 | },
77 | "keywords": [
78 | "uglify",
79 | "uglify-js",
80 | "uglify-es",
81 | "webpack",
82 | "webpack-plugin"
83 | ]
84 | }
85 |
--------------------------------------------------------------------------------
/src/TaskRunner.js:
--------------------------------------------------------------------------------
1 | import os from 'os';
2 |
3 | import cacache from 'cacache';
4 | import findCacheDir from 'find-cache-dir';
5 | import workerFarm from 'worker-farm';
6 | import serialize from 'serialize-javascript';
7 | import isWsl from 'is-wsl';
8 |
9 | import minify from './minify';
10 |
11 | const workerFile = require.resolve('./worker');
12 |
13 | export default class TaskRunner {
14 | constructor(options = {}) {
15 | const { cache, parallel } = options;
16 | this.cacheDir =
17 | cache === true
18 | ? findCacheDir({ name: 'uglifyjs-webpack-plugin' }) || os.tmpdir()
19 | : cache;
20 | // In some cases cpus() returns undefined
21 | // https://github.com/nodejs/node/issues/19022
22 | const cpus = os.cpus() || { length: 1 };
23 | this.maxConcurrentWorkers = isWsl
24 | ? 1
25 | : parallel === true
26 | ? cpus.length - 1
27 | : Math.min(Number(parallel) || 0, cpus.length - 1);
28 | }
29 |
30 | run(tasks, callback) {
31 | /* istanbul ignore if */
32 | if (!tasks.length) {
33 | callback(null, []);
34 | return;
35 | }
36 |
37 | if (this.maxConcurrentWorkers > 1) {
38 | const workerOptions =
39 | process.platform === 'win32'
40 | ? {
41 | maxConcurrentWorkers: this.maxConcurrentWorkers,
42 | maxConcurrentCallsPerWorker: 1,
43 | }
44 | : { maxConcurrentWorkers: this.maxConcurrentWorkers };
45 | this.workers = workerFarm(workerOptions, workerFile);
46 | this.boundWorkers = (options, cb) => {
47 | try {
48 | this.workers(serialize(options), cb);
49 | } catch (error) {
50 | // worker-farm can fail with ENOMEM or something else
51 | cb(error);
52 | }
53 | };
54 | } else {
55 | this.boundWorkers = (options, cb) => {
56 | try {
57 | cb(null, minify(options));
58 | } catch (error) {
59 | cb(error);
60 | }
61 | };
62 | }
63 |
64 | let toRun = tasks.length;
65 | const results = [];
66 | const step = (index, data) => {
67 | toRun -= 1;
68 | results[index] = data;
69 |
70 | if (!toRun) {
71 | callback(null, results);
72 | }
73 | };
74 |
75 | tasks.forEach((task, index) => {
76 | const enqueue = () => {
77 | this.boundWorkers(task, (error, data) => {
78 | const result = error ? { error } : data;
79 | const done = () => step(index, result);
80 |
81 | if (this.cacheDir && !result.error) {
82 | cacache
83 | .put(
84 | this.cacheDir,
85 | serialize(task.cacheKeys),
86 | JSON.stringify(data)
87 | )
88 | .then(done, done);
89 | } else {
90 | done();
91 | }
92 | });
93 | };
94 |
95 | if (this.cacheDir) {
96 | cacache
97 | .get(this.cacheDir, serialize(task.cacheKeys))
98 | .then(({ data }) => step(index, JSON.parse(data)), enqueue);
99 | } else {
100 | enqueue();
101 | }
102 | });
103 | }
104 |
105 | exit() {
106 | if (this.workers) {
107 | workerFarm.end(this.workers);
108 | }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/cjs.js:
--------------------------------------------------------------------------------
1 | const plugin = require('./index');
2 |
3 | module.exports = plugin.default;
4 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable
2 | no-param-reassign
3 | */
4 | import crypto from 'crypto';
5 | import path from 'path';
6 |
7 | import { SourceMapConsumer } from 'source-map';
8 | import { SourceMapSource, RawSource, ConcatSource } from 'webpack-sources';
9 | import RequestShortener from 'webpack/lib/RequestShortener';
10 | import ModuleFilenameHelpers from 'webpack/lib/ModuleFilenameHelpers';
11 | import validateOptions from 'schema-utils';
12 | import serialize from 'serialize-javascript';
13 | import uglifyJsPackageJson from 'uglify-js/package.json';
14 |
15 | import schema from './options.json';
16 | import TaskRunner from './TaskRunner';
17 |
18 | const warningRegex = /\[.+:([0-9]+),([0-9]+)\]/;
19 |
20 | class UglifyJsPlugin {
21 | constructor(options = {}) {
22 | validateOptions(schema, options, 'UglifyJs Plugin');
23 |
24 | const {
25 | minify,
26 | uglifyOptions = {},
27 | test = /\.js(\?.*)?$/i,
28 | chunkFilter = () => true,
29 | warningsFilter = () => true,
30 | extractComments = false,
31 | sourceMap = false,
32 | cache = false,
33 | cacheKeys = (defaultCacheKeys) => defaultCacheKeys,
34 | parallel = false,
35 | include,
36 | exclude,
37 | } = options;
38 |
39 | this.options = {
40 | test,
41 | chunkFilter,
42 | warningsFilter,
43 | extractComments,
44 | sourceMap,
45 | cache,
46 | cacheKeys,
47 | parallel,
48 | include,
49 | exclude,
50 | minify,
51 | uglifyOptions: {
52 | output: {
53 | comments: extractComments
54 | ? false
55 | : /^\**!|@preserve|@license|@cc_on/i,
56 | },
57 | ...uglifyOptions,
58 | },
59 | };
60 | }
61 |
62 | static isSourceMap(input) {
63 | // All required options for `new SourceMapConsumer(...options)`
64 | // https://github.com/mozilla/source-map#new-sourcemapconsumerrawsourcemap
65 | return Boolean(
66 | input &&
67 | input.version &&
68 | input.sources &&
69 | Array.isArray(input.sources) &&
70 | typeof input.mappings === 'string'
71 | );
72 | }
73 |
74 | static buildSourceMap(inputSourceMap) {
75 | if (!inputSourceMap || !UglifyJsPlugin.isSourceMap(inputSourceMap)) {
76 | return null;
77 | }
78 |
79 | return new SourceMapConsumer(inputSourceMap);
80 | }
81 |
82 | static buildError(err, file, sourceMap, requestShortener) {
83 | // Handling error which should have line, col, filename and message
84 | if (err.line) {
85 | const original =
86 | sourceMap &&
87 | sourceMap.originalPositionFor({
88 | line: err.line,
89 | column: err.col,
90 | });
91 |
92 | if (original && original.source && requestShortener) {
93 | return new Error(
94 | `${file} from UglifyJs\n${err.message} [${requestShortener.shorten(
95 | original.source
96 | )}:${original.line},${original.column}][${file}:${err.line},${
97 | err.col
98 | }]`
99 | );
100 | }
101 |
102 | return new Error(
103 | `${file} from UglifyJs\n${err.message} [${file}:${err.line},${err.col}]`
104 | );
105 | } else if (err.stack) {
106 | return new Error(`${file} from UglifyJs\n${err.stack}`);
107 | }
108 |
109 | return new Error(`${file} from UglifyJs\n${err.message}`);
110 | }
111 |
112 | static buildWarning(
113 | warning,
114 | file,
115 | sourceMap,
116 | requestShortener,
117 | warningsFilter
118 | ) {
119 | let warningMessage = warning;
120 | let locationMessage = '';
121 | let source = null;
122 |
123 | if (sourceMap) {
124 | const match = warningRegex.exec(warning);
125 |
126 | if (match) {
127 | const line = +match[1];
128 | const column = +match[2];
129 | const original = sourceMap.originalPositionFor({
130 | line,
131 | column,
132 | });
133 |
134 | if (
135 | original &&
136 | original.source &&
137 | original.source !== file &&
138 | requestShortener
139 | ) {
140 | ({ source } = original);
141 | warningMessage = `${warningMessage.replace(warningRegex, '')}`;
142 |
143 | locationMessage = `[${requestShortener.shorten(original.source)}:${
144 | original.line
145 | },${original.column}]`;
146 | }
147 | }
148 | }
149 |
150 | if (warningsFilter && !warningsFilter(warning, source)) {
151 | return null;
152 | }
153 |
154 | return `UglifyJs Plugin: ${warningMessage}${locationMessage}`;
155 | }
156 |
157 | apply(compiler) {
158 | const buildModuleFn = (moduleArg) => {
159 | // to get detailed location info about errors
160 | moduleArg.useSourceMap = true;
161 | };
162 |
163 | const optimizeFn = (compilation, chunks, callback) => {
164 | const taskRunner = new TaskRunner({
165 | cache: this.options.cache,
166 | parallel: this.options.parallel,
167 | });
168 |
169 | const processedAssets = new WeakSet();
170 | const tasks = [];
171 |
172 | const { chunkFilter } = this.options;
173 |
174 | Array.from(chunks)
175 | .filter((chunk) => chunkFilter && chunkFilter(chunk))
176 | .reduce((acc, chunk) => acc.concat(chunk.files || []), [])
177 | .concat(compilation.additionalChunkAssets || [])
178 | .filter(ModuleFilenameHelpers.matchObject.bind(null, this.options))
179 | .forEach((file) => {
180 | let inputSourceMap;
181 |
182 | const asset = compilation.assets[file];
183 |
184 | if (processedAssets.has(asset)) {
185 | return;
186 | }
187 |
188 | try {
189 | let input;
190 |
191 | if (this.options.sourceMap && asset.sourceAndMap) {
192 | const { source, map } = asset.sourceAndMap();
193 |
194 | input = source;
195 |
196 | if (UglifyJsPlugin.isSourceMap(map)) {
197 | inputSourceMap = map;
198 | } else {
199 | inputSourceMap = map;
200 |
201 | compilation.warnings.push(
202 | new Error(`${file} contains invalid source map`)
203 | );
204 | }
205 | } else {
206 | input = asset.source();
207 | inputSourceMap = null;
208 | }
209 |
210 | // Handling comment extraction
211 | let commentsFile = false;
212 |
213 | if (this.options.extractComments) {
214 | commentsFile =
215 | this.options.extractComments.filename || `${file}.LICENSE`;
216 |
217 | if (typeof commentsFile === 'function') {
218 | commentsFile = commentsFile(file);
219 | }
220 | }
221 |
222 | const task = {
223 | file,
224 | input,
225 | inputSourceMap,
226 | commentsFile,
227 | extractComments: this.options.extractComments,
228 | uglifyOptions: this.options.uglifyOptions,
229 | minify: this.options.minify,
230 | };
231 |
232 | if (this.options.cache) {
233 | const defaultCacheKeys = {
234 | 'uglify-js': uglifyJsPackageJson.version,
235 | node_version: process.version,
236 | // eslint-disable-next-line global-require
237 | 'uglifyjs-webpack-plugin': require('../package.json').version,
238 | 'uglifyjs-webpack-plugin-options': this.options,
239 | hash: crypto
240 | .createHash('md4')
241 | .update(input)
242 | .digest('hex'),
243 | };
244 |
245 | task.cacheKeys = this.options.cacheKeys(defaultCacheKeys, file);
246 | }
247 |
248 | tasks.push(task);
249 | } catch (error) {
250 | compilation.errors.push(
251 | UglifyJsPlugin.buildError(
252 | error,
253 | file,
254 | UglifyJsPlugin.buildSourceMap(inputSourceMap),
255 | new RequestShortener(compiler.context)
256 | )
257 | );
258 | }
259 | });
260 |
261 | taskRunner.run(tasks, (tasksError, results) => {
262 | if (tasksError) {
263 | compilation.errors.push(tasksError);
264 |
265 | return;
266 | }
267 |
268 | results.forEach((data, index) => {
269 | const { file, input, inputSourceMap, commentsFile } = tasks[index];
270 | const { error, map, code, warnings } = data;
271 | let { extractedComments } = data;
272 |
273 | let sourceMap = null;
274 |
275 | if (error || (warnings && warnings.length > 0)) {
276 | sourceMap = UglifyJsPlugin.buildSourceMap(inputSourceMap);
277 | }
278 |
279 | // Handling results
280 | // Error case: add errors, and go to next file
281 | if (error) {
282 | compilation.errors.push(
283 | UglifyJsPlugin.buildError(
284 | error,
285 | file,
286 | sourceMap,
287 | new RequestShortener(compiler.context)
288 | )
289 | );
290 |
291 | return;
292 | }
293 |
294 | let outputSource;
295 |
296 | if (map) {
297 | outputSource = new SourceMapSource(
298 | code,
299 | file,
300 | JSON.parse(map),
301 | input,
302 | inputSourceMap,
303 | true
304 | );
305 | } else {
306 | outputSource = new RawSource(code);
307 | }
308 |
309 | // Write extracted comments to commentsFile
310 | if (
311 | commentsFile &&
312 | extractedComments &&
313 | extractedComments.length > 0
314 | ) {
315 | if (commentsFile in compilation.assets) {
316 | const commentsFileSource = compilation.assets[
317 | commentsFile
318 | ].source();
319 |
320 | extractedComments = extractedComments.filter(
321 | (comment) => !commentsFileSource.includes(comment)
322 | );
323 | }
324 |
325 | if (extractedComments.length > 0) {
326 | // Add a banner to the original file
327 | if (this.options.extractComments.banner !== false) {
328 | let banner =
329 | this.options.extractComments.banner ||
330 | `For license information please see ${path.posix.basename(
331 | commentsFile
332 | )}`;
333 |
334 | if (typeof banner === 'function') {
335 | banner = banner(commentsFile);
336 | }
337 |
338 | if (banner) {
339 | outputSource = new ConcatSource(
340 | `/*! ${banner} */\n`,
341 | outputSource
342 | );
343 | }
344 | }
345 |
346 | const commentsSource = new RawSource(
347 | `${extractedComments.join('\n\n')}\n`
348 | );
349 |
350 | if (commentsFile in compilation.assets) {
351 | // commentsFile already exists, append new comments...
352 | if (compilation.assets[commentsFile] instanceof ConcatSource) {
353 | compilation.assets[commentsFile].add('\n');
354 | compilation.assets[commentsFile].add(commentsSource);
355 | } else {
356 | compilation.assets[commentsFile] = new ConcatSource(
357 | compilation.assets[commentsFile],
358 | '\n',
359 | commentsSource
360 | );
361 | }
362 | } else {
363 | compilation.assets[commentsFile] = commentsSource;
364 | }
365 | }
366 | }
367 |
368 | // Updating assets
369 | processedAssets.add((compilation.assets[file] = outputSource));
370 |
371 | // Handling warnings
372 | if (warnings && warnings.length > 0) {
373 | warnings.forEach((warning) => {
374 | const builtWarning = UglifyJsPlugin.buildWarning(
375 | warning,
376 | file,
377 | sourceMap,
378 | new RequestShortener(compiler.context),
379 | this.options.warningsFilter
380 | );
381 |
382 | if (builtWarning) {
383 | compilation.warnings.push(builtWarning);
384 | }
385 | });
386 | }
387 | });
388 |
389 | taskRunner.exit();
390 |
391 | callback();
392 | });
393 | };
394 |
395 | const plugin = { name: this.constructor.name };
396 |
397 | compiler.hooks.compilation.tap(plugin, (compilation) => {
398 | if (this.options.sourceMap) {
399 | compilation.hooks.buildModule.tap(plugin, buildModuleFn);
400 | }
401 |
402 | const { mainTemplate, chunkTemplate } = compilation;
403 |
404 | // Regenerate `contenthash` for minified assets
405 | for (const template of [mainTemplate, chunkTemplate]) {
406 | template.hooks.hashForChunk.tap(plugin, (hash) => {
407 | const data = serialize({
408 | uglifyjs: uglifyJsPackageJson.version,
409 | uglifyjsOptions: this.options.uglifyOptions,
410 | });
411 |
412 | hash.update('UglifyJsPlugin');
413 | hash.update(data);
414 | });
415 | }
416 |
417 | compilation.hooks.optimizeChunkAssets.tapAsync(
418 | plugin,
419 | optimizeFn.bind(this, compilation)
420 | );
421 | });
422 | }
423 | }
424 |
425 | export default UglifyJsPlugin;
426 |
--------------------------------------------------------------------------------
/src/minify.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable
2 | arrow-body-style
3 | */
4 | import uglify from 'uglify-js';
5 |
6 | const buildUglifyOptions = ({
7 | warnings,
8 | parse = {},
9 | compress = {},
10 | mangle,
11 | output,
12 | toplevel,
13 | nameCache,
14 | ie8,
15 | /* eslint-disable camelcase */
16 | keep_fnames,
17 | /* eslint-enable camelcase */
18 | } = {}) => ({
19 | warnings,
20 | parse: { ...parse },
21 | compress: typeof compress === 'boolean' ? compress : { ...compress },
22 | // eslint-disable-next-line no-nested-ternary
23 | mangle:
24 | mangle == null
25 | ? true
26 | : typeof mangle === 'boolean'
27 | ? mangle
28 | : { ...mangle },
29 | output: {
30 | shebang: true,
31 | comments: false,
32 | beautify: false,
33 | semicolons: true,
34 | ...output,
35 | },
36 | // Ignoring sourceMap from options
37 | sourceMap: null,
38 | toplevel,
39 | nameCache,
40 | ie8,
41 | keep_fnames,
42 | });
43 |
44 | const buildComments = (options, uglifyOptions, extractedComments) => {
45 | const condition = {};
46 | const commentsOpts = uglifyOptions.output.comments;
47 |
48 | // Use /^\**!|@preserve|@license|@cc_on/i RegExp
49 | if (typeof options.extractComments === 'boolean') {
50 | condition.preserve = commentsOpts;
51 | condition.extract = /^\**!|@preserve|@license|@cc_on/i;
52 | } else if (
53 | typeof options.extractComments === 'string' ||
54 | options.extractComments instanceof RegExp
55 | ) {
56 | // extractComments specifies the extract condition and commentsOpts specifies the preserve condition
57 | condition.preserve = commentsOpts;
58 | condition.extract = options.extractComments;
59 | } else if (typeof options.extractComments === 'function') {
60 | condition.preserve = commentsOpts;
61 | condition.extract = options.extractComments;
62 | } else if (
63 | Object.prototype.hasOwnProperty.call(options.extractComments, 'condition')
64 | ) {
65 | // Extract condition is given in extractComments.condition
66 | condition.preserve = commentsOpts;
67 | condition.extract = options.extractComments.condition;
68 | } else {
69 | // No extract condition is given. Extract comments that match commentsOpts instead of preserving them
70 | condition.preserve = false;
71 | condition.extract = commentsOpts;
72 | }
73 |
74 | // Ensure that both conditions are functions
75 | ['preserve', 'extract'].forEach((key) => {
76 | let regexStr;
77 | let regex;
78 |
79 | switch (typeof condition[key]) {
80 | case 'boolean':
81 | condition[key] = condition[key] ? () => true : () => false;
82 |
83 | break;
84 | case 'function':
85 | break;
86 | case 'string':
87 | if (condition[key] === 'all') {
88 | condition[key] = () => true;
89 |
90 | break;
91 | }
92 |
93 | if (condition[key] === 'some') {
94 | condition[key] = (astNode, comment) => {
95 | return (
96 | comment.type === 'comment2' &&
97 | /^\**!|@preserve|@license|@cc_on/i.test(comment.value)
98 | );
99 | };
100 |
101 | break;
102 | }
103 |
104 | regexStr = condition[key];
105 |
106 | condition[key] = (astNode, comment) => {
107 | return new RegExp(regexStr).test(comment.value);
108 | };
109 |
110 | break;
111 | default:
112 | regex = condition[key];
113 |
114 | condition[key] = (astNode, comment) => regex.test(comment.value);
115 | }
116 | });
117 |
118 | // Redefine the comments function to extract and preserve
119 | // comments according to the two conditions
120 | // Redefine the comments function to extract and preserve
121 | // comments according to the two conditions
122 | return (astNode, comment) => {
123 | if (condition.extract(astNode, comment)) {
124 | const commentText =
125 | comment.type === 'comment2'
126 | ? `/*${comment.value}*/`
127 | : `//${comment.value}`;
128 |
129 | // Don't include duplicate comments
130 | if (!extractedComments.includes(commentText)) {
131 | extractedComments.push(commentText);
132 | }
133 | }
134 |
135 | return condition.preserve(astNode, comment);
136 | };
137 | };
138 |
139 | const minify = (options) => {
140 | const {
141 | file,
142 | input,
143 | inputSourceMap,
144 | extractComments,
145 | minify: minifyFn,
146 | } = options;
147 |
148 | if (minifyFn) {
149 | return minifyFn({ [file]: input }, inputSourceMap);
150 | }
151 |
152 | // Copy uglify options
153 | const uglifyOptions = buildUglifyOptions(options.uglifyOptions);
154 |
155 | // Let uglify-js generate a SourceMap
156 | if (inputSourceMap) {
157 | uglifyOptions.sourceMap = true;
158 | }
159 |
160 | const extractedComments = [];
161 |
162 | if (extractComments) {
163 | uglifyOptions.output.comments = buildComments(
164 | options,
165 | uglifyOptions,
166 | extractedComments
167 | );
168 | }
169 |
170 | const { error, map, code, warnings } = uglify.minify(
171 | { [file]: input },
172 | uglifyOptions
173 | );
174 |
175 | return { error, map, code, warnings, extractedComments };
176 | };
177 |
178 | export default minify;
179 |
--------------------------------------------------------------------------------
/src/options.json:
--------------------------------------------------------------------------------
1 | {
2 | "additionalProperties": false,
3 | "definitions": {
4 | "file-conditions": {
5 | "anyOf": [
6 | {
7 | "instanceof": "RegExp"
8 | },
9 | {
10 | "type": "string"
11 | }
12 | ]
13 | }
14 | },
15 | "properties": {
16 | "test": {
17 | "anyOf": [
18 | {
19 | "$ref": "#/definitions/file-conditions"
20 | },
21 | {
22 | "items": {
23 | "anyOf": [
24 | {
25 | "$ref": "#/definitions/file-conditions"
26 | }
27 | ]
28 | },
29 | "type": "array"
30 | }
31 | ]
32 | },
33 | "include": {
34 | "anyOf": [
35 | {
36 | "$ref": "#/definitions/file-conditions"
37 | },
38 | {
39 | "items": {
40 | "anyOf": [
41 | {
42 | "$ref": "#/definitions/file-conditions"
43 | }
44 | ]
45 | },
46 | "type": "array"
47 | }
48 | ]
49 | },
50 | "exclude": {
51 | "anyOf": [
52 | {
53 | "$ref": "#/definitions/file-conditions"
54 | },
55 | {
56 | "items": {
57 | "anyOf": [
58 | {
59 | "$ref": "#/definitions/file-conditions"
60 | }
61 | ]
62 | },
63 | "type": "array"
64 | }
65 | ]
66 | },
67 | "chunkFilter": {
68 | "instanceof": "Function"
69 | },
70 | "cache": {
71 | "anyOf": [
72 | {
73 | "type": "boolean"
74 | },
75 | {
76 | "type": "string"
77 | }
78 | ]
79 | },
80 | "cacheKeys": {
81 | "instanceof": "Function"
82 | },
83 | "parallel": {
84 | "anyOf": [
85 | {
86 | "type": "boolean"
87 | },
88 | {
89 | "type": "integer"
90 | }
91 | ]
92 | },
93 | "sourceMap": {
94 | "type": "boolean"
95 | },
96 | "minify": {
97 | "instanceof": "Function"
98 | },
99 | "uglifyOptions": {
100 | "additionalProperties": true,
101 | "type": "object"
102 | },
103 | "extractComments": {
104 | "anyOf": [
105 | {
106 | "type": "boolean"
107 | },
108 | {
109 | "type": "string"
110 | },
111 | {
112 | "instanceof": "RegExp"
113 | },
114 | {
115 | "instanceof": "Function"
116 | },
117 | {
118 | "additionalProperties": false,
119 | "properties": {
120 | "condition": {
121 | "anyOf": [
122 | {
123 | "type": "boolean"
124 | },
125 | {
126 | "type": "string"
127 | },
128 | {
129 | "instanceof": "RegExp"
130 | },
131 | {
132 | "instanceof": "Function"
133 | }
134 | ]
135 | },
136 | "filename": {
137 | "anyOf": [
138 | {
139 | "type": "string"
140 | },
141 | {
142 | "instanceof": "Function"
143 | }
144 | ]
145 | },
146 | "banner": {
147 | "anyOf": [
148 | {
149 | "type": "boolean"
150 | },
151 | {
152 | "type": "string"
153 | },
154 | {
155 | "instanceof": "Function"
156 | }
157 | ]
158 | }
159 | },
160 | "type": "object"
161 | }
162 | ]
163 | },
164 | "warningsFilter": {
165 | "instanceof": "Function"
166 | }
167 | },
168 | "type": "object"
169 | }
170 |
--------------------------------------------------------------------------------
/src/worker.js:
--------------------------------------------------------------------------------
1 | import minify from './minify';
2 |
3 | module.exports = (options, callback) => {
4 | try {
5 | // 'use strict' => this === undefined (Clean Scope)
6 | // Safer for possible security issues, albeit not critical at all here
7 | // eslint-disable-next-line no-new-func, no-param-reassign
8 | options = new Function(
9 | 'exports',
10 | 'require',
11 | 'module',
12 | '__filename',
13 | '__dirname',
14 | `'use strict'\nreturn ${options}`
15 | )(exports, require, module, __filename, __dirname);
16 |
17 | callback(null, minify(options));
18 | } catch (errors) {
19 | callback(errors);
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/test/UglifyJsPlugin.test.js:
--------------------------------------------------------------------------------
1 | import RequestShortener from 'webpack/lib/RequestShortener';
2 | import MainTemplate from 'webpack/lib/MainTemplate';
3 | import ChunkTemplate from 'webpack/lib/ChunkTemplate';
4 |
5 | import UglifyJsPlugin from '../src/index';
6 |
7 | import { cleanErrorStack, compile, createCompiler } from './helpers';
8 |
9 | describe('UglifyJsPlugin', () => {
10 | const rawSourceMap = {
11 | version: 3,
12 | file: 'test.js',
13 | names: ['bar', 'baz', 'n'],
14 | sources: ['one.js', 'two.js'],
15 | sourceRoot: 'http://example.com/www/js/',
16 | mappings:
17 | 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA',
18 | };
19 | const emptyRawSourceMap = {
20 | version: 3,
21 | sources: [],
22 | mappings: '',
23 | };
24 |
25 | it('should works (without options)', () => {
26 | const compiler = createCompiler();
27 |
28 | new UglifyJsPlugin().apply(compiler);
29 |
30 | return compile(compiler).then((stats) => {
31 | const errors = stats.compilation.errors.map(cleanErrorStack);
32 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
33 |
34 | expect(errors).toMatchSnapshot('errors');
35 | expect(warnings).toMatchSnapshot('warnings');
36 |
37 | for (const file in stats.compilation.assets) {
38 | if (
39 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
40 | ) {
41 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
42 | }
43 | }
44 | });
45 | });
46 |
47 | it('should regenerate hash', () => {
48 | const originalMainTemplateUpdateHashForChunk =
49 | MainTemplate.prototype.updateHashForChunk;
50 | const originalChunkTemplateUpdateHashForChunk =
51 | ChunkTemplate.prototype.updateHashForChunk;
52 | const mockMainTemplateUpdateHashForChunk = jest.fn();
53 | const mockChunkTemplateUpdateHashFocChunk = jest.fn();
54 |
55 | MainTemplate.prototype.updateHashForChunk = mockMainTemplateUpdateHashForChunk;
56 | ChunkTemplate.prototype.updateHashForChunk = mockChunkTemplateUpdateHashFocChunk;
57 |
58 | const compiler = createCompiler({
59 | entry: {
60 | js: `${__dirname}/fixtures/entry.js`,
61 | mjs: `${__dirname}/fixtures/entry.mjs`,
62 | importExport: `${__dirname}/fixtures/import-export/entry.js`,
63 | AsyncImportExport: `${__dirname}/fixtures/async-import-export/entry.js`,
64 | },
65 | output: {
66 | path: `${__dirname}/dist`,
67 | filename: '[name].[contenthash].js',
68 | chunkFilename: '[id].[name].[contenthash].js',
69 | },
70 | });
71 |
72 | new UglifyJsPlugin().apply(compiler);
73 |
74 | return compile(compiler).then((stats) => {
75 | const errors = stats.compilation.errors.map(cleanErrorStack);
76 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
77 |
78 | expect(errors).toMatchSnapshot('errors');
79 | expect(warnings).toMatchSnapshot('warnings');
80 |
81 | // On each chunk we have 2 calls (we have 1 async chunk and 4 initial).
82 | // First call do `webpack`.
83 | // Second call do `TerserPlugin`.
84 |
85 | // We have 1 async chunk (1 * 2 = 2 calls for ChunkTemplate)
86 | expect(mockMainTemplateUpdateHashForChunk).toHaveBeenCalledTimes(8);
87 | // We have 4 initial chunks (4 * 2 = 8 calls for MainTemplate)
88 | expect(mockChunkTemplateUpdateHashFocChunk).toHaveBeenCalledTimes(2);
89 |
90 | for (const file in stats.compilation.assets) {
91 | if (
92 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
93 | ) {
94 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
95 | }
96 | }
97 |
98 | MainTemplate.prototype.updateHashForChunk = originalMainTemplateUpdateHashForChunk;
99 | ChunkTemplate.prototype.updateHashForChunk = originalChunkTemplateUpdateHashForChunk;
100 | });
101 | });
102 |
103 | it('isSourceMap method', () => {
104 | expect(UglifyJsPlugin.isSourceMap(null)).toBe(false);
105 | expect(UglifyJsPlugin.isSourceMap()).toBe(false);
106 | expect(UglifyJsPlugin.isSourceMap({})).toBe(false);
107 | expect(UglifyJsPlugin.isSourceMap([])).toBe(false);
108 | expect(UglifyJsPlugin.isSourceMap('foo')).toBe(false);
109 | expect(UglifyJsPlugin.isSourceMap({ version: 3 })).toBe(false);
110 | expect(UglifyJsPlugin.isSourceMap({ sources: '' })).toBe(false);
111 | expect(UglifyJsPlugin.isSourceMap({ mappings: [] })).toBe(false);
112 | expect(UglifyJsPlugin.isSourceMap({ version: 3, sources: '' })).toBe(false);
113 | expect(UglifyJsPlugin.isSourceMap({ version: 3, mappings: [] })).toBe(
114 | false
115 | );
116 | expect(UglifyJsPlugin.isSourceMap({ sources: '', mappings: [] })).toBe(
117 | false
118 | );
119 | expect(
120 | UglifyJsPlugin.isSourceMap({ version: 3, sources: '', mappings: [] })
121 | ).toBe(false);
122 | expect(UglifyJsPlugin.isSourceMap(rawSourceMap)).toBe(true);
123 | expect(UglifyJsPlugin.isSourceMap(emptyRawSourceMap)).toBe(true);
124 | });
125 |
126 | it('buildSourceMap method', () => {
127 | expect(UglifyJsPlugin.buildSourceMap()).toBe(null);
128 | expect(UglifyJsPlugin.buildSourceMap('invalid')).toBe(null);
129 | expect(UglifyJsPlugin.buildSourceMap({})).toBe(null);
130 | expect(UglifyJsPlugin.buildSourceMap(rawSourceMap)).toMatchSnapshot();
131 | });
132 |
133 | it('buildError method', () => {
134 | const error = new Error('Message');
135 |
136 | error.stack = null;
137 |
138 | expect(UglifyJsPlugin.buildError(error, 'test.js')).toMatchSnapshot();
139 |
140 | const errorWithLineAndCol = new Error('Message');
141 |
142 | errorWithLineAndCol.stack = null;
143 | errorWithLineAndCol.line = 1;
144 | errorWithLineAndCol.col = 1;
145 |
146 | expect(
147 | UglifyJsPlugin.buildError(
148 | errorWithLineAndCol,
149 | 'test.js',
150 | UglifyJsPlugin.buildSourceMap(rawSourceMap)
151 | )
152 | ).toMatchSnapshot();
153 |
154 | const otherErrorWithLineAndCol = new Error('Message');
155 |
156 | otherErrorWithLineAndCol.stack = null;
157 | otherErrorWithLineAndCol.line = 1;
158 | otherErrorWithLineAndCol.col = 1;
159 |
160 | expect(
161 | UglifyJsPlugin.buildError(
162 | otherErrorWithLineAndCol,
163 | 'test.js',
164 | UglifyJsPlugin.buildSourceMap(rawSourceMap),
165 | new RequestShortener('http://example.com/www/js/')
166 | )
167 | ).toMatchSnapshot();
168 |
169 | const errorWithStack = new Error('Message');
170 |
171 | errorWithStack.stack = 'Stack';
172 |
173 | expect(
174 | UglifyJsPlugin.buildError(errorWithStack, 'test.js')
175 | ).toMatchSnapshot();
176 | });
177 |
178 | it('buildWarning method', () => {
179 | expect(
180 | UglifyJsPlugin.buildWarning('Warning [test.js:1,1]')
181 | ).toMatchSnapshot();
182 | expect(
183 | UglifyJsPlugin.buildWarning('Warning [test.js:1,1]', 'test.js')
184 | ).toMatchSnapshot();
185 | expect(
186 | UglifyJsPlugin.buildWarning(
187 | 'Warning [test.js:1,1]',
188 | 'test.js',
189 | UglifyJsPlugin.buildSourceMap(rawSourceMap)
190 | )
191 | ).toMatchSnapshot();
192 | expect(
193 | UglifyJsPlugin.buildWarning(
194 | 'Warning [test.js:1,1]',
195 | 'test.js',
196 | UglifyJsPlugin.buildSourceMap(rawSourceMap),
197 | new RequestShortener('http://example.com/www/js/')
198 | )
199 | ).toMatchSnapshot();
200 | expect(
201 | UglifyJsPlugin.buildWarning(
202 | 'Warning [test.js:1,1]',
203 | 'test.js',
204 | UglifyJsPlugin.buildSourceMap(rawSourceMap),
205 | new RequestShortener('http://example.com/www/js/'),
206 | () => true
207 | )
208 | ).toMatchSnapshot();
209 | expect(
210 | UglifyJsPlugin.buildWarning(
211 | 'Warning [test.js:1,1]',
212 | 'test.js',
213 | UglifyJsPlugin.buildSourceMap(rawSourceMap),
214 | new RequestShortener('http://example.com/www/js/'),
215 | () => false
216 | )
217 | ).toMatchSnapshot();
218 | });
219 | });
220 |
--------------------------------------------------------------------------------
/test/__snapshots__/UglifyJsPlugin.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`UglifyJsPlugin buildError method 1`] = `
4 | [Error: test.js from UglifyJs
5 | Message]
6 | `;
7 |
8 | exports[`UglifyJsPlugin buildError method 2`] = `
9 | [Error: test.js from UglifyJs
10 | Message [test.js:1,1]]
11 | `;
12 |
13 | exports[`UglifyJsPlugin buildError method 3`] = `
14 | [Error: test.js from UglifyJs
15 | Message [./one.js:1,1][test.js:1,1]]
16 | `;
17 |
18 | exports[`UglifyJsPlugin buildError method 4`] = `
19 | [Error: test.js from UglifyJs
20 | Stack]
21 | `;
22 |
23 | exports[`UglifyJsPlugin buildSourceMap method 1`] = `
24 | SourceMapConsumer {
25 | "_absoluteSources": Array [
26 | "http://example.com/www/js/one.js",
27 | "http://example.com/www/js/two.js",
28 | ],
29 | "_mappings": "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA",
30 | "_names": ArraySet {
31 | "_array": Array [
32 | "bar",
33 | "baz",
34 | "n",
35 | ],
36 | "_set": Map {
37 | "bar" => 0,
38 | "baz" => 1,
39 | "n" => 2,
40 | },
41 | },
42 | "_sourceMapURL": undefined,
43 | "_sources": ArraySet {
44 | "_array": Array [
45 | "one.js",
46 | "two.js",
47 | ],
48 | "_set": Map {
49 | "one.js" => 0,
50 | "two.js" => 1,
51 | },
52 | },
53 | "file": "test.js",
54 | "sourceRoot": "http://example.com/www/js/",
55 | "sourcesContent": null,
56 | }
57 | `;
58 |
59 | exports[`UglifyJsPlugin buildWarning method 1`] = `"UglifyJs Plugin: Warning [test.js:1,1]"`;
60 |
61 | exports[`UglifyJsPlugin buildWarning method 2`] = `"UglifyJs Plugin: Warning [test.js:1,1]"`;
62 |
63 | exports[`UglifyJsPlugin buildWarning method 3`] = `"UglifyJs Plugin: Warning [test.js:1,1]"`;
64 |
65 | exports[`UglifyJsPlugin buildWarning method 4`] = `"UglifyJs Plugin: Warning [./one.js:1,1]"`;
66 |
67 | exports[`UglifyJsPlugin buildWarning method 5`] = `"UglifyJs Plugin: Warning [./one.js:1,1]"`;
68 |
69 | exports[`UglifyJsPlugin buildWarning method 6`] = `null`;
70 |
71 | exports[`UglifyJsPlugin should regenerate hash: 4.4.b3a3e1129148263bd259.js 1`] = `"(window.webpackJsonp=window.webpackJsonp||[]).push([[4],{5:function(n,p){n.exports=\\"async-dep\\"}}]);"`;
72 |
73 | exports[`UglifyJsPlugin should regenerate hash: AsyncImportExport.80ec742362058f8c35a8.js 1`] = `"!function(a){function e(e){for(var t,n,r=e[0],o=e[1],u=0,i=[];u {
12 | let compiler;
13 |
14 | beforeEach(() => {
15 | compiler = createCompiler({
16 | entry: {
17 | one: `${__dirname}/fixtures/cache.js`,
18 | two: `${__dirname}/fixtures/cache-1.js`,
19 | three: `${__dirname}/fixtures/cache-2.js`,
20 | four: `${__dirname}/fixtures/cache-3.js`,
21 | five: `${__dirname}/fixtures/cache-4.js`,
22 | },
23 | });
24 |
25 | return Promise.all([
26 | cacache.rm.all(cacheDir),
27 | cacache.rm.all(otherCacheDir),
28 | ]);
29 | });
30 |
31 | afterEach(() =>
32 | Promise.all([cacache.rm.all(cacheDir), cacache.rm.all(otherCacheDir)])
33 | );
34 |
35 | it('matches snapshot for `false` value', () => {
36 | new UglifyJsPlugin({ cache: false }).apply(compiler);
37 |
38 | cacache.get = jest.fn(cacache.get);
39 | cacache.put = jest.fn(cacache.put);
40 |
41 | return compile(compiler).then((stats) => {
42 | const errors = stats.compilation.errors.map(cleanErrorStack);
43 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
44 |
45 | expect(errors).toMatchSnapshot('errors');
46 | expect(warnings).toMatchSnapshot('warnings');
47 |
48 | for (const file in stats.compilation.assets) {
49 | if (
50 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
51 | ) {
52 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
53 | }
54 | }
55 |
56 | // Cache disabled so we don't run `get` or `put`
57 | expect(cacache.get.mock.calls.length).toBe(0);
58 | expect(cacache.put.mock.calls.length).toBe(0);
59 |
60 | return Promise.resolve()
61 | .then(() => cacache.ls(cacheDir))
62 | .then((cacheEntriesList) => {
63 | const cacheKeys = Object.keys(cacheEntriesList);
64 |
65 | expect(cacheKeys.length).toBe(0);
66 | });
67 | });
68 | });
69 |
70 | it('matches snapshot for `true` value', () => {
71 | new UglifyJsPlugin({ cache: true }).apply(compiler);
72 |
73 | cacache.get = jest.fn(cacache.get);
74 | cacache.put = jest.fn(cacache.put);
75 |
76 | return compile(compiler).then((stats) => {
77 | const errors = stats.compilation.errors.map(cleanErrorStack);
78 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
79 |
80 | expect(errors).toMatchSnapshot('errors');
81 | expect(warnings).toMatchSnapshot('warnings');
82 |
83 | for (const file in stats.compilation.assets) {
84 | if (
85 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
86 | ) {
87 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
88 | }
89 | }
90 |
91 | const countAssets = Object.keys(stats.compilation.assets).length;
92 |
93 | // Try to found cached files, but we don't have their in cache
94 | expect(cacache.get.mock.calls.length).toBe(countAssets);
95 | // Put files in cache
96 | expect(cacache.put.mock.calls.length).toBe(countAssets);
97 |
98 | return (
99 | Promise.resolve()
100 | .then(() => cacache.ls(cacheDir))
101 | .then((cacheEntriesList) => {
102 | const cacheKeys = Object.keys(cacheEntriesList);
103 |
104 | // Make sure that we cached files
105 | expect(cacheKeys.length).toBe(countAssets);
106 |
107 | cacache.get.mockClear();
108 | cacache.put.mockClear();
109 | })
110 | // Run second compilation to ensure cached files will be taken from cache
111 | .then(() => compile(compiler))
112 | .then((newStats) => {
113 | const newErrors = newStats.compilation.errors.map(cleanErrorStack);
114 | const newWarnings = newStats.compilation.warnings.map(
115 | cleanErrorStack
116 | );
117 |
118 | expect(newErrors).toMatchSnapshot('errors');
119 | expect(newWarnings).toMatchSnapshot('warnings');
120 |
121 | for (const file in newStats.compilation.assets) {
122 | if (
123 | Object.prototype.hasOwnProperty.call(
124 | newStats.compilation.assets,
125 | file
126 | )
127 | ) {
128 | expect(
129 | newStats.compilation.assets[file].source()
130 | ).toMatchSnapshot(file);
131 | }
132 | }
133 |
134 | const newCountAssets = Object.keys(newStats.compilation.assets)
135 | .length;
136 |
137 | // Now we have cached files so we get their and don't put
138 | expect(cacache.get.mock.calls.length).toBe(newCountAssets);
139 | expect(cacache.put.mock.calls.length).toBe(0);
140 | })
141 | );
142 | });
143 | });
144 |
145 | it('matches snapshot for `other-cache-directory` value (string)', () => {
146 | new UglifyJsPlugin({ cache: otherCacheDir }).apply(compiler);
147 |
148 | cacache.get = jest.fn(cacache.get);
149 | cacache.put = jest.fn(cacache.put);
150 |
151 | return compile(compiler).then((stats) => {
152 | const errors = stats.compilation.errors.map(cleanErrorStack);
153 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
154 |
155 | expect(errors).toMatchSnapshot('errors');
156 | expect(warnings).toMatchSnapshot('warnings');
157 |
158 | for (const file in stats.compilation.assets) {
159 | if (
160 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
161 | ) {
162 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
163 | }
164 | }
165 |
166 | const countAssets = Object.keys(stats.compilation.assets).length;
167 |
168 | // Try to found cached files, but we don't have their in cache
169 | expect(cacache.get.mock.calls.length).toBe(countAssets);
170 | // Put files in cache
171 | expect(cacache.put.mock.calls.length).toBe(countAssets);
172 |
173 | return (
174 | Promise.resolve()
175 | .then(() => cacache.ls(otherCacheDir))
176 | .then((cacheEntriesList) => {
177 | const cacheKeys = Object.keys(cacheEntriesList);
178 |
179 | // Make sure that we cached files
180 | expect(cacheKeys.length).toBe(countAssets);
181 |
182 | cacache.get.mockClear();
183 | cacache.put.mockClear();
184 | })
185 | // Run second compilation to ensure cached files will be taken from cache
186 | .then(() => compile(compiler))
187 | .then((newStats) => {
188 | const newErrors = newStats.compilation.errors.map(cleanErrorStack);
189 | const newWarnings = newStats.compilation.warnings.map(
190 | cleanErrorStack
191 | );
192 |
193 | expect(newErrors).toMatchSnapshot('errors');
194 | expect(newWarnings).toMatchSnapshot('warnings');
195 |
196 | for (const file in newStats.compilation.assets) {
197 | if (
198 | Object.prototype.hasOwnProperty.call(
199 | newStats.compilation.assets,
200 | file
201 | )
202 | ) {
203 | expect(
204 | newStats.compilation.assets[file].source()
205 | ).toMatchSnapshot(file);
206 | }
207 | }
208 |
209 | const newCountAssets = Object.keys(newStats.compilation.assets)
210 | .length;
211 |
212 | // Now we have cached files so we get their and don't put
213 | expect(cacache.get.mock.calls.length).toBe(newCountAssets);
214 | expect(cacache.put.mock.calls.length).toBe(0);
215 | })
216 | );
217 | });
218 | });
219 |
220 | it('matches snapshot for `true` value and `cacheKey` is custom `function`', () => {
221 | new UglifyJsPlugin({
222 | cache: true,
223 | cacheKeys: (defaultCacheKeys, file) => {
224 | // eslint-disable-next-line no-param-reassign
225 | defaultCacheKeys.myCacheKey = 1;
226 | // eslint-disable-next-line no-param-reassign
227 | defaultCacheKeys.myCacheKeyBasedOnFile = `file-${file}`;
228 |
229 | return defaultCacheKeys;
230 | },
231 | }).apply(compiler);
232 |
233 | cacache.get = jest.fn(cacache.get);
234 | cacache.put = jest.fn(cacache.put);
235 |
236 | return compile(compiler).then((stats) => {
237 | const errors = stats.compilation.errors.map(cleanErrorStack);
238 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
239 |
240 | expect(errors).toMatchSnapshot('errors');
241 | expect(warnings).toMatchSnapshot('warnings');
242 |
243 | for (const file in stats.compilation.assets) {
244 | if (
245 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
246 | ) {
247 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
248 | }
249 | }
250 |
251 | const countAssets = Object.keys(stats.compilation.assets).length;
252 |
253 | // Try to found cached files, but we don't have their in cache
254 | expect(cacache.get.mock.calls.length).toBe(countAssets);
255 | // Put files in cache
256 | expect(cacache.put.mock.calls.length).toBe(countAssets);
257 |
258 | return (
259 | Promise.resolve()
260 | .then(() => cacache.ls(cacheDir))
261 | .then((cacheEntriesList) => {
262 | const cacheKeys = Object.keys(cacheEntriesList);
263 |
264 | // Make sure that we cached files
265 | expect(cacheKeys.length).toBe(countAssets);
266 |
267 | cacheKeys.forEach((cacheEntry) => {
268 | // eslint-disable-next-line no-new-func
269 | const cacheEntryOptions = new Function(
270 | `'use strict'\nreturn ${cacheEntry}`
271 | )();
272 |
273 | expect(cacheEntryOptions.myCacheKey).toBe(1);
274 | expect(cacheEntryOptions.myCacheKeyBasedOnFile).toMatch(
275 | /file-(.+)?\.js/
276 | );
277 | });
278 |
279 | cacache.get.mockClear();
280 | cacache.put.mockClear();
281 | })
282 | // Run second compilation to ensure cached files will be taken from cache
283 | .then(() => compile(compiler))
284 | .then((newStats) => {
285 | const newErrors = newStats.compilation.errors.map(cleanErrorStack);
286 | const newWarnings = newStats.compilation.warnings.map(
287 | cleanErrorStack
288 | );
289 |
290 | expect(newErrors).toMatchSnapshot('errors');
291 | expect(newWarnings).toMatchSnapshot('warnings');
292 |
293 | for (const file in newStats.compilation.assets) {
294 | if (
295 | Object.prototype.hasOwnProperty.call(
296 | newStats.compilation.assets,
297 | file
298 | )
299 | ) {
300 | expect(
301 | newStats.compilation.assets[file].source()
302 | ).toMatchSnapshot(file);
303 | }
304 | }
305 |
306 | const newCountAssets = Object.keys(newStats.compilation.assets)
307 | .length;
308 |
309 | // Now we have cached files so we get their and don't put
310 | expect(cacache.get.mock.calls.length).toBe(newCountAssets);
311 | expect(cacache.put.mock.calls.length).toBe(0);
312 | })
313 | );
314 | });
315 | });
316 |
317 | it('matches snapshot for errors into `cacheKeys` option', () => {
318 | new UglifyJsPlugin({
319 | cache: true,
320 | cacheKeys: () => {
321 | throw new Error('message');
322 | },
323 | }).apply(compiler);
324 |
325 | return compile(compiler).then((stats) => {
326 | const errors = stats.compilation.errors.map(cleanErrorStack);
327 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
328 |
329 | expect(errors).toMatchSnapshot('errors');
330 | expect(warnings).toMatchSnapshot('warnings');
331 |
332 | for (const file in stats.compilation.assets) {
333 | if (
334 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
335 | ) {
336 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
337 | }
338 | }
339 | });
340 | });
341 | });
342 |
--------------------------------------------------------------------------------
/test/chunkFilter-option.test.js:
--------------------------------------------------------------------------------
1 | import UglifyJsPlugin from '../src/index';
2 |
3 | import { cleanErrorStack, createCompiler, compile } from './helpers';
4 |
5 | describe('when applied with `chunkFilter` option', () => {
6 | let compiler;
7 |
8 | beforeEach(() => {
9 | compiler = createCompiler({
10 | entry: {
11 | included: `${__dirname}/fixtures/included1.js`,
12 | entry: `${__dirname}/fixtures/entry.js`,
13 | },
14 | });
15 | });
16 |
17 | it('matches snapshot for a single `chunkFilter`', () => {
18 | new UglifyJsPlugin({
19 | chunkFilter: (chunk) => {
20 | if (chunk.name === 'included') {
21 | return false;
22 | }
23 |
24 | return true;
25 | },
26 | }).apply(compiler);
27 |
28 | return compile(compiler).then((stats) => {
29 | const errors = stats.compilation.errors.map(cleanErrorStack);
30 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
31 |
32 | expect(errors).toMatchSnapshot('errors');
33 | expect(warnings).toMatchSnapshot('warnings');
34 |
35 | for (const file in stats.compilation.assets) {
36 | if (
37 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
38 | ) {
39 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
40 | }
41 | }
42 | });
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/test/cjs.test.js:
--------------------------------------------------------------------------------
1 | import UglifyJsPlugin from '../src';
2 | import CJSUglifyJsPlugin from '../src/cjs';
3 |
4 | describe('CJS', () => {
5 | it('should exported plugin', () => {
6 | expect(CJSUglifyJsPlugin).toEqual(UglifyJsPlugin);
7 | });
8 | });
9 |
--------------------------------------------------------------------------------
/test/exclude-option.test.js:
--------------------------------------------------------------------------------
1 | import UglifyJsPlugin from '../src/index';
2 |
3 | import { cleanErrorStack, createCompiler, compile } from './helpers';
4 |
5 | describe('when applied with `exclude` option', () => {
6 | let compiler;
7 |
8 | beforeEach(() => {
9 | compiler = createCompiler({
10 | entry: {
11 | excluded1: `${__dirname}/fixtures/excluded1.js`,
12 | excluded2: `${__dirname}/fixtures/excluded2.js`,
13 | entry: `${__dirname}/fixtures/entry.js`,
14 | },
15 | });
16 | });
17 |
18 | it('matches snapshot for a single `exclude` value ({RegExp})', () => {
19 | new UglifyJsPlugin({
20 | exclude: /excluded1/i,
21 | }).apply(compiler);
22 |
23 | return compile(compiler).then((stats) => {
24 | const errors = stats.compilation.errors.map(cleanErrorStack);
25 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
26 |
27 | expect(errors).toMatchSnapshot('errors');
28 | expect(warnings).toMatchSnapshot('warnings');
29 |
30 | for (const file in stats.compilation.assets) {
31 | if (
32 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
33 | ) {
34 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
35 | }
36 | }
37 | });
38 | });
39 |
40 | it('matches snapshot for a single `exclude` value ({String})', () => {
41 | new UglifyJsPlugin({
42 | exclude: 'excluded1',
43 | }).apply(compiler);
44 |
45 | return compile(compiler).then((stats) => {
46 | const errors = stats.compilation.errors.map(cleanErrorStack);
47 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
48 |
49 | expect(errors).toMatchSnapshot('errors');
50 | expect(warnings).toMatchSnapshot('warnings');
51 |
52 | for (const file in stats.compilation.assets) {
53 | if (
54 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
55 | ) {
56 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
57 | }
58 | }
59 | });
60 | });
61 |
62 | it('matches snapshot for multiple `exclude` values ({RegExp})', () => {
63 | new UglifyJsPlugin({
64 | exclude: [/excluded1/i, /excluded2/i],
65 | }).apply(compiler);
66 |
67 | return compile(compiler).then((stats) => {
68 | const errors = stats.compilation.errors.map(cleanErrorStack);
69 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
70 |
71 | expect(errors).toMatchSnapshot('errors');
72 | expect(warnings).toMatchSnapshot('warnings');
73 |
74 | for (const file in stats.compilation.assets) {
75 | if (
76 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
77 | ) {
78 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
79 | }
80 | }
81 | });
82 | });
83 |
84 | it('matches snapshot for multiple `exclude` values ({String})', () => {
85 | new UglifyJsPlugin({
86 | exclude: ['excluded1', 'excluded2'],
87 | }).apply(compiler);
88 |
89 | return compile(compiler).then((stats) => {
90 | const errors = stats.compilation.errors.map(cleanErrorStack);
91 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
92 |
93 | expect(errors).toMatchSnapshot('errors');
94 | expect(warnings).toMatchSnapshot('warnings');
95 |
96 | for (const file in stats.compilation.assets) {
97 | if (
98 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
99 | ) {
100 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
101 | }
102 | }
103 | });
104 | });
105 | });
106 |
--------------------------------------------------------------------------------
/test/extractComments-option.test.js:
--------------------------------------------------------------------------------
1 | import UglifyJsPlugin from '../src/index';
2 |
3 | import { cleanErrorStack, compile, createCompiler } from './helpers';
4 |
5 | describe('when applied with `extractComments` option', () => {
6 | let compiler;
7 |
8 | beforeEach(() => {
9 | compiler = createCompiler({
10 | entry: {
11 | one: `${__dirname}/fixtures/comments.js`,
12 | two: `${__dirname}/fixtures/comments-2.js`,
13 | three: `${__dirname}/fixtures/comments-3.js`,
14 | four: `${__dirname}/fixtures/comments-4.js`,
15 | },
16 | output: {
17 | filename: 'filename/[name].[chunkhash].js',
18 | chunkFilename: 'chunks/[id].[name].[chunkhash].js',
19 | },
20 | });
21 | });
22 |
23 | it('matches snapshot when is not specify', () => {
24 | new UglifyJsPlugin().apply(compiler);
25 |
26 | return compile(compiler).then((stats) => {
27 | const errors = stats.compilation.errors.map(cleanErrorStack);
28 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
29 |
30 | expect(errors).toMatchSnapshot('errors');
31 | expect(warnings).toMatchSnapshot('warnings');
32 |
33 | for (const file in stats.compilation.assets) {
34 | if (
35 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
36 | ) {
37 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
38 | }
39 | }
40 | });
41 | });
42 |
43 | it('matches snapshot for a `false` value', () => {
44 | new UglifyJsPlugin({ extractComments: false }).apply(compiler);
45 |
46 | return compile(compiler).then((stats) => {
47 | const errors = stats.compilation.errors.map(cleanErrorStack);
48 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
49 |
50 | expect(errors).toMatchSnapshot('errors');
51 | expect(warnings).toMatchSnapshot('warnings');
52 |
53 | for (const file in stats.compilation.assets) {
54 | if (
55 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
56 | ) {
57 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
58 | }
59 | }
60 | });
61 | });
62 |
63 | it('matches snapshot for a `true` value', () => {
64 | new UglifyJsPlugin({ extractComments: true }).apply(compiler);
65 |
66 | return compile(compiler).then((stats) => {
67 | const errors = stats.compilation.errors.map(cleanErrorStack);
68 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
69 |
70 | expect(errors).toMatchSnapshot('errors');
71 | expect(warnings).toMatchSnapshot('warnings');
72 |
73 | for (const file in stats.compilation.assets) {
74 | if (
75 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
76 | ) {
77 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
78 | }
79 | }
80 | });
81 | });
82 |
83 | it('matches snapshot for a `/Foo/` value (regexp)', () => {
84 | new UglifyJsPlugin({ extractComments: /Foo/ }).apply(compiler);
85 |
86 | return compile(compiler).then((stats) => {
87 | const errors = stats.compilation.errors.map(cleanErrorStack);
88 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
89 |
90 | expect(errors).toMatchSnapshot('errors');
91 | expect(warnings).toMatchSnapshot('warnings');
92 |
93 | for (const file in stats.compilation.assets) {
94 | if (
95 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
96 | ) {
97 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
98 | }
99 | }
100 | });
101 | });
102 |
103 | it('matches snapshot for a `all` value (string)', () => {
104 | new UglifyJsPlugin({ extractComments: 'all' }).apply(compiler);
105 |
106 | return compile(compiler).then((stats) => {
107 | const errors = stats.compilation.errors.map(cleanErrorStack);
108 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
109 |
110 | expect(errors).toMatchSnapshot('errors');
111 | expect(warnings).toMatchSnapshot('warnings');
112 |
113 | for (const file in stats.compilation.assets) {
114 | if (
115 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
116 | ) {
117 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
118 | }
119 | }
120 | });
121 | });
122 |
123 | it('matches snapshot for a `some` value (string)', () => {
124 | new UglifyJsPlugin({ extractComments: 'some' }).apply(compiler);
125 |
126 | return compile(compiler).then((stats) => {
127 | const errors = stats.compilation.errors.map(cleanErrorStack);
128 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
129 |
130 | expect(errors).toMatchSnapshot('errors');
131 | expect(warnings).toMatchSnapshot('warnings');
132 |
133 | for (const file in stats.compilation.assets) {
134 | if (
135 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
136 | ) {
137 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
138 | }
139 | }
140 | });
141 | });
142 |
143 | it('matches snapshot for a `Foo` value (string)', () => {
144 | new UglifyJsPlugin({ extractComments: 'Foo' }).apply(compiler);
145 |
146 | return compile(compiler).then((stats) => {
147 | const errors = stats.compilation.errors.map(cleanErrorStack);
148 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
149 |
150 | expect(errors).toMatchSnapshot('errors');
151 | expect(warnings).toMatchSnapshot('warnings');
152 |
153 | for (const file in stats.compilation.assets) {
154 | if (
155 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
156 | ) {
157 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
158 | }
159 | }
160 | });
161 | });
162 |
163 | it('matches snapshot for a `function value', () => {
164 | new UglifyJsPlugin({ extractComments: () => true }).apply(compiler);
165 |
166 | return compile(compiler).then((stats) => {
167 | const errors = stats.compilation.errors.map(cleanErrorStack);
168 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
169 |
170 | expect(errors).toMatchSnapshot('errors');
171 | expect(warnings).toMatchSnapshot('warnings');
172 |
173 | for (const file in stats.compilation.assets) {
174 | if (
175 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
176 | ) {
177 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
178 | }
179 | }
180 | });
181 | });
182 |
183 | it('matches snapshot for a object value (extracts comments to multiple files)', () => {
184 | new UglifyJsPlugin({
185 | extractComments: {
186 | condition: true,
187 | filename(file) {
188 | return file.replace(/(\.\w+)$/, '.license$1');
189 | },
190 | banner(licenseFile) {
191 | return `License information can be found in ${licenseFile}`;
192 | },
193 | },
194 | }).apply(compiler);
195 |
196 | return compile(compiler).then((stats) => {
197 | const errors = stats.compilation.errors.map(cleanErrorStack);
198 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
199 |
200 | expect(errors).toMatchSnapshot('errors');
201 | expect(warnings).toMatchSnapshot('warnings');
202 |
203 | for (const file in stats.compilation.assets) {
204 | if (
205 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
206 | ) {
207 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
208 | }
209 | }
210 | });
211 | });
212 |
213 | it('matches snapshot for a object value (extracts comments to a single file)', () => {
214 | new UglifyJsPlugin({
215 | extractComments: {
216 | condition: true,
217 | filename: 'extracted-comments.js',
218 | banner(licenseFile) {
219 | return `License information can be found in ${licenseFile}`;
220 | },
221 | },
222 | }).apply(compiler);
223 |
224 | return compile(compiler).then((stats) => {
225 | const errors = stats.compilation.errors.map(cleanErrorStack);
226 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
227 |
228 | expect(errors).toMatchSnapshot('errors');
229 | expect(warnings).toMatchSnapshot('warnings');
230 |
231 | for (const file in stats.compilation.assets) {
232 | if (
233 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
234 | ) {
235 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
236 | }
237 | }
238 | });
239 | });
240 |
241 | it('matches snapshot for a `true` value and preserve `@license` comments', () => {
242 | new UglifyJsPlugin({
243 | uglifyOptions: {
244 | output: {
245 | comments: /@license/i,
246 | },
247 | },
248 | extractComments: true,
249 | }).apply(compiler);
250 |
251 | return compile(compiler).then((stats) => {
252 | const errors = stats.compilation.errors.map(cleanErrorStack);
253 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
254 |
255 | expect(errors).toMatchSnapshot('errors');
256 | expect(warnings).toMatchSnapshot('warnings');
257 |
258 | for (const file in stats.compilation.assets) {
259 | if (
260 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
261 | ) {
262 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
263 | }
264 | }
265 | });
266 | });
267 |
268 | it('matches snapshot for a object value (no codition, extract only `/@license/i` comments)', () => {
269 | new UglifyJsPlugin({
270 | uglifyOptions: {
271 | output: {
272 | comments: /@license/i,
273 | },
274 | },
275 | extractComments: {
276 | filename(file) {
277 | return file.replace(/(\.\w+)$/, '.license$1');
278 | },
279 | banner(licenseFile) {
280 | return `License information can be found in ${licenseFile}`;
281 | },
282 | },
283 | }).apply(compiler);
284 |
285 | return compile(compiler).then((stats) => {
286 | const errors = stats.compilation.errors.map(cleanErrorStack);
287 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
288 |
289 | expect(errors).toMatchSnapshot('errors');
290 | expect(warnings).toMatchSnapshot('warnings');
291 |
292 | for (const file in stats.compilation.assets) {
293 | if (
294 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
295 | ) {
296 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
297 | }
298 | }
299 | });
300 | });
301 |
302 | it('matches snapshot for a `true` value and dedupe duplicate comments', () => {
303 | new UglifyJsPlugin({ extractComments: true }).apply(compiler);
304 |
305 | return compile(compiler).then((stats) => {
306 | const errors = stats.compilation.errors.map(cleanErrorStack);
307 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
308 |
309 | expect(errors).toMatchSnapshot('errors');
310 | expect(warnings).toMatchSnapshot('warnings');
311 |
312 | for (const file in stats.compilation.assets) {
313 | if (
314 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
315 | ) {
316 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
317 | }
318 | }
319 | });
320 | });
321 |
322 | it('matches snapshot for a object value (extracts comments to a single file) and dedupe duplicate comments', () => {
323 | new UglifyJsPlugin({
324 | extractComments: {
325 | condition: true,
326 | filename: 'extracted-comments.js',
327 | banner(licenseFile) {
328 | return `License information can be found in ${licenseFile}`;
329 | },
330 | },
331 | }).apply(compiler);
332 |
333 | return compile(compiler).then((stats) => {
334 | const errors = stats.compilation.errors.map(cleanErrorStack);
335 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
336 |
337 | expect(errors).toMatchSnapshot('errors');
338 | expect(warnings).toMatchSnapshot('warnings');
339 |
340 | for (const file in stats.compilation.assets) {
341 | if (
342 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
343 | ) {
344 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
345 | }
346 | }
347 | });
348 | });
349 | });
350 |
--------------------------------------------------------------------------------
/test/fixtures/async-import-export/async-dep.js:
--------------------------------------------------------------------------------
1 | module.exports = "async-dep";
2 |
--------------------------------------------------------------------------------
/test/fixtures/async-import-export/entry.js:
--------------------------------------------------------------------------------
1 | require.ensure([], function() {
2 | require('./async-dep');
3 |
4 | console.log('Good')
5 | });
6 |
7 | module.exports = "Awesome";
8 |
--------------------------------------------------------------------------------
/test/fixtures/cache-1.js:
--------------------------------------------------------------------------------
1 | module.exports = 'string';
2 |
--------------------------------------------------------------------------------
/test/fixtures/cache-2.js:
--------------------------------------------------------------------------------
1 | module.exports = function getRegExp() { return /test/ };
2 |
--------------------------------------------------------------------------------
/test/fixtures/cache-3.js:
--------------------------------------------------------------------------------
1 | module.exports = function foo() {};
2 |
--------------------------------------------------------------------------------
/test/fixtures/cache-4.js:
--------------------------------------------------------------------------------
1 | // foo
2 | /* @preserve*/
3 | // bar
4 | var a = 2 + 2;
5 |
6 | module.exports = function Foo() {
7 | var b = 2 + 2;
8 | console.log(b + 1 + 2);
9 | };
10 |
--------------------------------------------------------------------------------
/test/fixtures/cache.js:
--------------------------------------------------------------------------------
1 | // foo
2 | /* @preserve*/
3 | // bar
4 | var a = 2 + 2;
5 |
6 | module.exports = function Foo() {
7 | var b = 2 + 2;
8 | console.log(b + 1 + 2);
9 | };
10 |
--------------------------------------------------------------------------------
/test/fixtures/comments-2.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Information.
3 | * @license MIT
4 | */
5 |
6 | module.exports = Math.random();
7 |
--------------------------------------------------------------------------------
/test/fixtures/comments-3.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Duplicate comment in same file.
3 | * @license MIT
4 | */
5 |
6 | /**
7 | * Duplicate comment in same file.
8 | * @license MIT
9 | */
10 |
11 | /**
12 | * Duplicate comment in difference files.
13 | * @license MIT
14 | */
15 |
16 | module.exports = Math.random();
17 |
--------------------------------------------------------------------------------
/test/fixtures/comments-4.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Duplicate comment in difference files.
3 | * @license MIT
4 | */
5 |
6 | module.exports = Math.random();
7 |
--------------------------------------------------------------------------------
/test/fixtures/comments.js:
--------------------------------------------------------------------------------
1 | require('./nested/comments.js');
2 |
3 | /*! Legal Comment */
4 |
5 | /**
6 | * @preserve Copyright 2009 SomeThirdParty.
7 | * Here is the full license text and copyright
8 | * notice for this file. Note that the notice can span several
9 | * lines and is only terminated by the closing star and slash:
10 | */
11 |
12 | /**
13 | * Utility functions for the foo package.
14 | * @license Apache-2.0
15 | */
16 |
17 | /*! Legal Foo */
18 |
19 | // Foo
20 |
21 | /*
22 | Foo Bar
23 | */
24 |
25 | /*
26 | * Foo
27 | */
28 |
29 | module.exports = Math.random();
30 |
--------------------------------------------------------------------------------
/test/fixtures/entry.js:
--------------------------------------------------------------------------------
1 | // foo
2 | /* @preserve*/
3 | // bar
4 | var a = 2 + 2;
5 |
6 | module.exports = function Foo() {
7 | var b = 2 + 2;
8 | console.log(b + 1 + 2);
9 | };
10 |
--------------------------------------------------------------------------------
/test/fixtures/entry.mjs:
--------------------------------------------------------------------------------
1 | // foo
2 | /* @preserve*/
3 | // bar
4 | var a = 2 + 2;
5 |
6 | module.exports = function Foo() {
7 | var b = 2 + 2;
8 | console.log(b + 1 + 2);
9 | };
10 |
--------------------------------------------------------------------------------
/test/fixtures/excluded1.js:
--------------------------------------------------------------------------------
1 | module.exports = function Bar1() {
2 | var b = 2 + 2;
3 | console.log(b + 1 + 2);
4 | };
5 |
--------------------------------------------------------------------------------
/test/fixtures/excluded2.js:
--------------------------------------------------------------------------------
1 | module.exports = function Bar2() {
2 | var b = 2 + 2;
3 | console.log(b + 1 + 2);
4 | };
5 |
--------------------------------------------------------------------------------
/test/fixtures/import-export/async-dep.js:
--------------------------------------------------------------------------------
1 | module.exports = "async-dep";
2 |
--------------------------------------------------------------------------------
/test/fixtures/import-export/dep.js:
--------------------------------------------------------------------------------
1 | module.exports = 'foo';
2 | module.exports.bar = 'bar';
3 |
--------------------------------------------------------------------------------
/test/fixtures/import-export/entry.js:
--------------------------------------------------------------------------------
1 | var foo = require('./dep');
2 |
3 | function Foo() {
4 | var b = foo;
5 | var baz = 'baz' + Math.random();
6 | return function () {
7 | return {
8 | a: b + foo.bar + baz,
9 | b: b,
10 | baz: baz,
11 | };
12 | };
13 | }
14 |
15 | module.exports = Foo;
16 |
--------------------------------------------------------------------------------
/test/fixtures/included1.js:
--------------------------------------------------------------------------------
1 | module.exports = function Bar1() {
2 | var b = 2 + 2;
3 | console.log(b + 1 + 2);
4 | };
5 |
--------------------------------------------------------------------------------
/test/fixtures/included2.js:
--------------------------------------------------------------------------------
1 | module.exports = function Bar2() {
2 | var b = 2 + 2;
3 | console.log(b + 1 + 2);
4 | };
5 |
--------------------------------------------------------------------------------
/test/fixtures/minify/es5.js:
--------------------------------------------------------------------------------
1 | function myFunction() {
2 | var baz = document.getElementById("root").innerHTML;
3 |
4 | document.getElementById("demo").innerHTML = "Paragraph changed." + baz;
5 | }
6 |
7 | module.exports = myFunction;
8 |
--------------------------------------------------------------------------------
/test/fixtures/minify/es6.js:
--------------------------------------------------------------------------------
1 | class Point {
2 | constructor(x, y) {
3 | this.x = x;
4 | this.y = y;
5 | }
6 |
7 | static distance(a, b) {
8 | const dx = a.x - b.x;
9 | const dy = a.y - b.y;
10 |
11 | return Math.hypot(dx, dy);
12 | }
13 | }
14 |
15 | export default Point;
16 |
--------------------------------------------------------------------------------
/test/fixtures/nested/comments.js:
--------------------------------------------------------------------------------
1 | /*! Legal Comment */
2 |
3 | /** @license Copyright 2112 Moon. **/
4 |
5 | module.exports = Math.random();
6 |
--------------------------------------------------------------------------------
/test/fixtures/unreachable-code-2.js:
--------------------------------------------------------------------------------
1 | function foo(x) { if (x) { return bar(); not_called1(); } }
2 |
--------------------------------------------------------------------------------
/test/fixtures/unreachable-code.js:
--------------------------------------------------------------------------------
1 | function foo(x) { if (x) { return bar(); not_called1(); } }
2 |
--------------------------------------------------------------------------------
/test/helpers.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 |
3 | import MemoryFileSystem from 'memory-fs'; // eslint-disable-line import/no-extraneous-dependencies
4 | import webpack from 'webpack';
5 |
6 | export class PluginEnvironment {
7 | constructor() {
8 | this.events = [];
9 | }
10 |
11 | getEnvironmentStub() {
12 | return {
13 | plugin: (name, handler) => {
14 | this.events.push({
15 | name,
16 | handler,
17 | });
18 | },
19 | };
20 | }
21 |
22 | getEventBindings() {
23 | return this.events;
24 | }
25 | }
26 |
27 | export function compile(compiler) {
28 | return new Promise((resolve, reject) => {
29 | compiler.run((err, stats) => {
30 | if (err) {
31 | return reject(err);
32 | }
33 |
34 | return resolve(stats);
35 | });
36 | });
37 | }
38 |
39 | export function createCompiler(options = {}) {
40 | const compiler = webpack(
41 | Array.isArray(options)
42 | ? options
43 | : {
44 | mode: 'production',
45 | bail: true,
46 | cache: false,
47 | entry: `${__dirname}/fixtures/entry.js`,
48 | optimization: {
49 | minimize: false,
50 | },
51 | output: {
52 | pathinfo: false,
53 | path: `${__dirname}/dist`,
54 | filename: '[name].[chunkhash].js',
55 | chunkFilename: '[id].[name].[chunkhash].js',
56 | },
57 | plugins: [],
58 | ...options,
59 | }
60 | );
61 | compiler.outputFileSystem = new MemoryFileSystem();
62 | return compiler;
63 | }
64 |
65 | export function countPlugins({ hooks }) {
66 | return Object.keys(hooks).reduce((aggregate, name) => {
67 | // eslint-disable-next-line no-param-reassign
68 | aggregate[name] = Array.isArray(hooks[name].taps)
69 | ? hooks[name].taps.length
70 | : 0;
71 | return aggregate;
72 | }, {});
73 | }
74 |
75 | export function removeCWD(str) {
76 | return str.split(`${process.cwd()}/`).join('');
77 | }
78 |
79 | export function normalizeSourceMap(source) {
80 | if (source.map && source.map.sources) {
81 | // eslint-disable-next-line no-param-reassign
82 | source.map.sources = source.map.sources.map((sourceFromMap) =>
83 | path.relative(process.cwd(), sourceFromMap).replace(/\\/g, '/')
84 | );
85 | }
86 |
87 | return source;
88 | }
89 |
90 | export function cleanErrorStack(error) {
91 | return removeCWD(error.toString())
92 | .split('\n')
93 | .slice(0, 2)
94 | .join('\n');
95 | }
96 |
--------------------------------------------------------------------------------
/test/include-option.test.js:
--------------------------------------------------------------------------------
1 | import UglifyJsPlugin from '../src/index';
2 |
3 | import { cleanErrorStack, createCompiler, compile } from './helpers';
4 |
5 | describe('when applied with `include` option', () => {
6 | let compiler;
7 |
8 | beforeEach(() => {
9 | compiler = createCompiler({
10 | entry: {
11 | included1: `${__dirname}/fixtures/included1.js`,
12 | included2: `${__dirname}/fixtures/included2.js`,
13 | entry: `${__dirname}/fixtures/entry.js`,
14 | },
15 | });
16 | });
17 |
18 | it('matches snapshot for a single `include` value ({RegExp})', () => {
19 | new UglifyJsPlugin({
20 | include: /included1/i,
21 | }).apply(compiler);
22 |
23 | return compile(compiler).then((stats) => {
24 | const errors = stats.compilation.errors.map(cleanErrorStack);
25 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
26 |
27 | expect(errors).toMatchSnapshot('errors');
28 | expect(warnings).toMatchSnapshot('warnings');
29 |
30 | for (const file in stats.compilation.assets) {
31 | if (
32 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
33 | ) {
34 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
35 | }
36 | }
37 | });
38 | });
39 |
40 | it('matches snapshot for a single `include` value ({String})', () => {
41 | new UglifyJsPlugin({
42 | include: 'included1',
43 | }).apply(compiler);
44 |
45 | return compile(compiler).then((stats) => {
46 | const errors = stats.compilation.errors.map(cleanErrorStack);
47 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
48 |
49 | expect(errors).toMatchSnapshot('errors');
50 | expect(warnings).toMatchSnapshot('warnings');
51 |
52 | for (const file in stats.compilation.assets) {
53 | if (
54 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
55 | ) {
56 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
57 | }
58 | }
59 | });
60 | });
61 |
62 | it('matches snapshot for multiple `include` values ({RegExp})', () => {
63 | new UglifyJsPlugin({
64 | include: [/included1/i, /included2/i],
65 | }).apply(compiler);
66 |
67 | return compile(compiler).then((stats) => {
68 | const errors = stats.compilation.errors.map(cleanErrorStack);
69 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
70 |
71 | expect(errors).toMatchSnapshot('errors');
72 | expect(warnings).toMatchSnapshot('warnings');
73 |
74 | for (const file in stats.compilation.assets) {
75 | if (
76 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
77 | ) {
78 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
79 | }
80 | }
81 | });
82 | });
83 |
84 | it('matches snapshot for multiple `include` values ({String})', () => {
85 | new UglifyJsPlugin({
86 | include: ['included1', 'included2'],
87 | }).apply(compiler);
88 |
89 | return compile(compiler).then((stats) => {
90 | const errors = stats.compilation.errors.map(cleanErrorStack);
91 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
92 |
93 | expect(errors).toMatchSnapshot('errors');
94 | expect(warnings).toMatchSnapshot('warnings');
95 |
96 | for (const file in stats.compilation.assets) {
97 | if (
98 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
99 | ) {
100 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
101 | }
102 | }
103 | });
104 | });
105 | });
106 |
--------------------------------------------------------------------------------
/test/minify-option.test.js:
--------------------------------------------------------------------------------
1 | import UglifyJsPlugin from '../src';
2 |
3 | import {
4 | cleanErrorStack,
5 | compile,
6 | createCompiler,
7 | normalizeSourceMap,
8 | } from './helpers';
9 |
10 | // Based on https://github.com/facebook/jest/blob/edde20f75665c2b1e3c8937f758902b5cf28a7b4/packages/jest-runner/src/__tests__/test_runner.test.js
11 |
12 | jest.mock('worker-farm', () => {
13 | const mock = jest.fn((options, worker) =>
14 | jest.fn((data, callback) =>
15 | // eslint-disable-next-line global-require, import/no-dynamic-require
16 | require(worker)(data, callback)
17 | )
18 | );
19 | mock.end = jest.fn();
20 | return mock;
21 | });
22 |
23 | describe('when applied with `minify` option', () => {
24 | it('matches snapshot for `terser` minifier', () => {
25 | const compiler = createCompiler({
26 | entry: `${__dirname}/fixtures/minify/es6.js`,
27 | output: {
28 | path: `${__dirname}/dist-terser`,
29 | filename: '[name].js',
30 | chunkFilename: '[id].[name].js',
31 | },
32 | });
33 |
34 | new UglifyJsPlugin({
35 | minify(file) {
36 | // eslint-disable-next-line global-require
37 | return require('terser').minify(file, {
38 | mangle: {
39 | reserved: ['baz'],
40 | },
41 | });
42 | },
43 | }).apply(compiler);
44 |
45 | return compile(compiler).then((stats) => {
46 | const errors = stats.compilation.errors.map(cleanErrorStack);
47 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
48 |
49 | expect(errors).toMatchSnapshot('errors');
50 | expect(warnings).toMatchSnapshot('warnings');
51 |
52 | for (const file in stats.compilation.assets) {
53 | if (
54 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
55 | ) {
56 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
57 | }
58 | }
59 | });
60 | });
61 |
62 | it('matches snapshot for `terser` minifier and `sourceMap` is `true`', () => {
63 | const compiler = createCompiler({
64 | entry: `${__dirname}/fixtures/minify/es6.js`,
65 | output: {
66 | path: `${__dirname}/dist-terser`,
67 | filename: '[name].js',
68 | chunkFilename: '[id].[name].js',
69 | },
70 | });
71 |
72 | new UglifyJsPlugin({
73 | sourceMap: true,
74 | minify(file, sourceMap) {
75 | const terserOption = {
76 | mangle: {
77 | reserved: ['baz'],
78 | },
79 | };
80 |
81 | if (sourceMap) {
82 | terserOption.sourceMap = {
83 | content: sourceMap,
84 | };
85 | }
86 |
87 | // eslint-disable-next-line global-require
88 | return require('terser').minify(file, terserOption);
89 | },
90 | }).apply(compiler);
91 |
92 | return compile(compiler).then((stats) => {
93 | const errors = stats.compilation.errors.map(cleanErrorStack);
94 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
95 |
96 | expect(errors).toMatchSnapshot('errors');
97 | expect(warnings).toMatchSnapshot('warnings');
98 |
99 | for (const file in stats.compilation.assets) {
100 | if (
101 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
102 | ) {
103 | expect(
104 | normalizeSourceMap(stats.compilation.assets[file].sourceAndMap())
105 | ).toMatchSnapshot(file);
106 | }
107 | }
108 | });
109 | });
110 |
111 | it('matches snapshot for `terser` minifier and `parallel` is `true`', () => {
112 | const compiler = createCompiler({
113 | entry: `${__dirname}/fixtures/minify/es6.js`,
114 | output: {
115 | path: `${__dirname}/dist-terser`,
116 | filename: '[name].js',
117 | chunkFilename: '[id].[name].js',
118 | },
119 | });
120 |
121 | new UglifyJsPlugin({
122 | parallel: true,
123 | minify(file) {
124 | // eslint-disable-next-line global-require
125 | return require('terser').minify(file, {
126 | mangle: {
127 | reserved: ['baz'],
128 | },
129 | });
130 | },
131 | }).apply(compiler);
132 |
133 | return compile(compiler).then((stats) => {
134 | const errors = stats.compilation.errors.map(cleanErrorStack);
135 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
136 |
137 | expect(errors).toMatchSnapshot('errors');
138 | expect(warnings).toMatchSnapshot('warnings');
139 |
140 | for (const file in stats.compilation.assets) {
141 | if (
142 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
143 | ) {
144 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
145 | }
146 | }
147 | });
148 | });
149 |
150 | it('matches snapshot for errors into `minify` option', () => {
151 | const compiler = createCompiler({
152 | entry: `${__dirname}/fixtures/minify/es6.js`,
153 | output: {
154 | path: `${__dirname}/dist-terser`,
155 | filename: '[name].js',
156 | chunkFilename: '[id].[name].js',
157 | },
158 | });
159 |
160 | new UglifyJsPlugin({
161 | minify() {
162 | throw Error('Error');
163 | },
164 | }).apply(compiler);
165 |
166 | return compile(compiler).then((stats) => {
167 | const errors = stats.compilation.errors.map(cleanErrorStack);
168 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
169 |
170 | expect(errors).toMatchSnapshot('errors');
171 | expect(warnings).toMatchSnapshot('warnings');
172 |
173 | for (const file in stats.compilation.assets) {
174 | if (
175 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
176 | ) {
177 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
178 | }
179 | }
180 | });
181 | });
182 |
183 | it('matches snapshot for errors into `minify` option and `parallel` is `true`', () => {
184 | const compiler = createCompiler({
185 | entry: `${__dirname}/fixtures/minify/es6.js`,
186 | output: {
187 | path: `${__dirname}/dist-terser`,
188 | filename: '[name].js',
189 | chunkFilename: '[id].[name].js',
190 | },
191 | });
192 |
193 | new UglifyJsPlugin({
194 | parallel: true,
195 | minify: function minify() {
196 | throw Error('Error');
197 | },
198 | }).apply(compiler);
199 |
200 | return compile(compiler).then((stats) => {
201 | const errors = stats.compilation.errors.map(cleanErrorStack);
202 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
203 |
204 | expect(errors).toMatchSnapshot('errors');
205 | expect(warnings).toMatchSnapshot('warnings');
206 |
207 | for (const file in stats.compilation.assets) {
208 | if (
209 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
210 | ) {
211 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
212 | }
213 | }
214 | });
215 | });
216 |
217 | it('matches snapshot for `uglify-js` minifier while extracting comments', () => {
218 | const compiler = createCompiler({
219 | entry: `${__dirname}/fixtures/minify/es5.js`,
220 | output: {
221 | path: `${__dirname}/dist-uglify-js`,
222 | filename: '[name].js',
223 | chunkFilename: '[id].[name].js',
224 | },
225 | });
226 |
227 | new UglifyJsPlugin({
228 | extractComments: true,
229 | minify(file) {
230 | // eslint-disable-next-line global-require
231 | return require('terser').minify(file, {
232 | mangle: {
233 | reserved: ['baz'],
234 | },
235 | });
236 | },
237 | }).apply(compiler);
238 |
239 | return compile(compiler).then((stats) => {
240 | const errors = stats.compilation.errors.map(cleanErrorStack);
241 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
242 |
243 | expect(errors).toMatchSnapshot('errors');
244 | expect(warnings).toMatchSnapshot('warnings');
245 |
246 | for (const file in stats.compilation.assets) {
247 | if (
248 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
249 | ) {
250 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
251 | }
252 | }
253 | });
254 | });
255 | });
256 |
--------------------------------------------------------------------------------
/test/parallel-option-failure.test.js:
--------------------------------------------------------------------------------
1 | import os from 'os';
2 |
3 | import cacache from 'cacache';
4 | import workerFarm from 'worker-farm';
5 | import findCacheDir from 'find-cache-dir';
6 |
7 | import UglifyJsPlugin from '../src/index';
8 |
9 | import { createCompiler, compile, cleanErrorStack } from './helpers';
10 |
11 | const cacheDir = findCacheDir({ name: 'uglifyjs-webpack-plugin' });
12 |
13 | // Based on https://github.com/facebook/jest/blob/edde20f75665c2b1e3c8937f758902b5cf28a7b4/packages/jest-runner/src/__tests__/test_runner.test.js
14 | let workerFarmMock;
15 |
16 | jest.mock('os', () => {
17 | const actualOs = require.requireActual('os');
18 |
19 | actualOs.cpus = jest.fn(() => {
20 | return { length: 4 };
21 | });
22 |
23 | return actualOs;
24 | });
25 |
26 | jest.mock('worker-farm', () => {
27 | const mock = jest.fn(
28 | () =>
29 | (workerFarmMock = jest.fn(() => {
30 | throw new Error('worker-farm failed');
31 | }))
32 | );
33 |
34 | mock.end = jest.fn();
35 |
36 | return mock;
37 | });
38 |
39 | describe('when applied with `parallel` option', () => {
40 | let compiler;
41 |
42 | beforeEach(() => {
43 | os.cpus.mockClear();
44 | workerFarm.mockClear();
45 | workerFarm.end.mockClear();
46 |
47 | compiler = createCompiler({
48 | entry: {
49 | one: `${__dirname}/fixtures/entry.js`,
50 | },
51 | });
52 |
53 | return cacache.rm.all(cacheDir);
54 | });
55 |
56 | it('matches snapshot for errors into `worker-farm`', () => {
57 | new UglifyJsPlugin({ parallel: true, cache: false }).apply(compiler);
58 |
59 | return compile(compiler).then((stats) => {
60 | const errors = stats.compilation.errors.map(cleanErrorStack);
61 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
62 |
63 | expect(workerFarm.mock.calls.length).toBe(1);
64 | expect(workerFarmMock.mock.calls.length).toBe(
65 | Object.keys(stats.compilation.assets).length
66 | );
67 | expect(workerFarm.end.mock.calls.length).toBe(1);
68 | expect(errors).toMatchSnapshot('errors');
69 | expect(warnings).toMatchSnapshot('warnings');
70 |
71 | for (const file in stats.compilation.assets) {
72 | if (
73 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
74 | ) {
75 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
76 | }
77 | }
78 | });
79 | });
80 |
81 | it('matches snapshot for errors into `worker-farm` and `cache` is `true`', () => {
82 | new UglifyJsPlugin({ parallel: true, cache: true }).apply(compiler);
83 |
84 | return compile(compiler).then((stats) => {
85 | const errors = stats.compilation.errors.map(cleanErrorStack);
86 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
87 |
88 | expect(workerFarm.mock.calls.length).toBe(1);
89 | expect(workerFarmMock.mock.calls.length).toBe(
90 | Object.keys(stats.compilation.assets).length
91 | );
92 | expect(workerFarm.end.mock.calls.length).toBe(1);
93 | expect(errors).toMatchSnapshot('errors');
94 | expect(warnings).toMatchSnapshot('warnings');
95 |
96 | for (const file in stats.compilation.assets) {
97 | if (
98 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
99 | ) {
100 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
101 | }
102 | }
103 | });
104 | });
105 | });
106 |
--------------------------------------------------------------------------------
/test/parallel-option.test.js:
--------------------------------------------------------------------------------
1 | import os from 'os';
2 |
3 | import workerFarm from 'worker-farm';
4 |
5 | import UglifyJsPlugin from '../src/index';
6 |
7 | import { createCompiler, compile, cleanErrorStack } from './helpers';
8 |
9 | jest.mock('os', () => {
10 | const actualOs = require.requireActual('os');
11 |
12 | actualOs.cpus = jest.fn(() => {
13 | return { length: 4 };
14 | });
15 |
16 | return actualOs;
17 | });
18 |
19 | // Based on https://github.com/facebook/jest/blob/edde20f75665c2b1e3c8937f758902b5cf28a7b4/packages/jest-runner/src/__tests__/test_runner.test.js
20 | let workerFarmMock;
21 |
22 | jest.mock('worker-farm', () => {
23 | const mock = jest.fn(
24 | (options, worker) =>
25 | (workerFarmMock = jest.fn((data, callback) =>
26 | // eslint-disable-next-line global-require, import/no-dynamic-require
27 | require(worker)(data, callback)
28 | ))
29 | );
30 | mock.end = jest.fn();
31 | return mock;
32 | });
33 |
34 | describe('when applied with `parallel` option', () => {
35 | let compiler;
36 |
37 | beforeEach(() => {
38 | os.cpus.mockClear();
39 | workerFarm.mockClear();
40 | workerFarm.end.mockClear();
41 |
42 | compiler = createCompiler({
43 | entry: {
44 | one: `${__dirname}/fixtures/entry.js`,
45 | two: `${__dirname}/fixtures/entry.js`,
46 | three: `${__dirname}/fixtures/entry.js`,
47 | four: `${__dirname}/fixtures/entry.js`,
48 | },
49 | });
50 | });
51 |
52 | it('matches snapshot for `false` value', () => {
53 | new UglifyJsPlugin({ parallel: false }).apply(compiler);
54 |
55 | return compile(compiler).then((stats) => {
56 | const errors = stats.compilation.errors.map(cleanErrorStack);
57 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
58 |
59 | expect(workerFarm.mock.calls.length).toBe(0);
60 | expect(workerFarm.end.mock.calls.length).toBe(0);
61 |
62 | expect(errors).toMatchSnapshot('errors');
63 | expect(warnings).toMatchSnapshot('warnings');
64 |
65 | for (const file in stats.compilation.assets) {
66 | if (
67 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
68 | ) {
69 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
70 | }
71 | }
72 | });
73 | });
74 |
75 | it('matches snapshot for `true` value', () => {
76 | new UglifyJsPlugin({ parallel: true }).apply(compiler);
77 |
78 | return compile(compiler).then((stats) => {
79 | const errors = stats.compilation.errors.map(cleanErrorStack);
80 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
81 |
82 | expect(workerFarm.mock.calls.length).toBe(1);
83 | expect(workerFarm.mock.calls[0][0].maxConcurrentWorkers).toBe(
84 | os.cpus().length - 1
85 | );
86 | expect(workerFarmMock.mock.calls.length).toBe(
87 | Object.keys(stats.compilation.assets).length
88 | );
89 | expect(workerFarm.end.mock.calls.length).toBe(1);
90 | expect(errors).toMatchSnapshot('errors');
91 | expect(warnings).toMatchSnapshot('warnings');
92 |
93 | for (const file in stats.compilation.assets) {
94 | if (
95 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
96 | ) {
97 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
98 | }
99 | }
100 | });
101 | });
102 |
103 | it('matches snapshot for `2` value (number)', () => {
104 | new UglifyJsPlugin({ parallel: 2 }).apply(compiler);
105 |
106 | return compile(compiler).then((stats) => {
107 | const errors = stats.compilation.errors.map(cleanErrorStack);
108 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
109 |
110 | expect(workerFarm.mock.calls.length).toBe(1);
111 | expect(workerFarm.mock.calls[0][0].maxConcurrentWorkers).toBe(2);
112 | expect(workerFarmMock.mock.calls.length).toBe(
113 | Object.keys(stats.compilation.assets).length
114 | );
115 | expect(workerFarm.end.mock.calls.length).toBe(1);
116 | expect(errors).toMatchSnapshot('errors');
117 | expect(warnings).toMatchSnapshot('warnings');
118 |
119 | for (const file in stats.compilation.assets) {
120 | if (
121 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
122 | ) {
123 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
124 | }
125 | }
126 | });
127 | });
128 | });
129 |
--------------------------------------------------------------------------------
/test/sourceMap-option.test.js:
--------------------------------------------------------------------------------
1 | import UglifyJsPlugin from '../src/index';
2 |
3 | import { createCompiler, compile, cleanErrorStack } from './helpers';
4 |
5 | expect.addSnapshotSerializer({
6 | test: (value) => {
7 | // For string that are valid JSON
8 | if (typeof value !== 'string') {
9 | return false;
10 | }
11 |
12 | try {
13 | return typeof JSON.parse(value) === 'object';
14 | } catch (e) {
15 | return false;
16 | }
17 | },
18 | print: (value) => JSON.stringify(JSON.parse(value), null, 2),
19 | });
20 |
21 | describe('when options.sourceMap', () => {
22 | it('matches snapshot for a single `false` value (`devtool` is `source-map`)', () => {
23 | const compiler = createCompiler({
24 | entry: `${__dirname}/fixtures/entry.js`,
25 | devtool: 'source-map',
26 | });
27 |
28 | new UglifyJsPlugin({ sourceMap: false }).apply(compiler);
29 |
30 | return compile(compiler).then((stats) => {
31 | const errors = stats.compilation.errors.map(cleanErrorStack);
32 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
33 |
34 | expect(errors).toMatchSnapshot('errors');
35 | expect(warnings).toMatchSnapshot('warnings');
36 |
37 | for (const file in stats.compilation.assets) {
38 | if (
39 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
40 | ) {
41 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
42 | }
43 | }
44 | });
45 | });
46 |
47 | it('matches snapshot for a single `false` value (`devtool` is `false`)', () => {
48 | const compiler = createCompiler({
49 | entry: `${__dirname}/fixtures/entry.js`,
50 | devtool: false,
51 | });
52 |
53 | new UglifyJsPlugin({ sourceMap: false }).apply(compiler);
54 |
55 | return compile(compiler).then((stats) => {
56 | const errors = stats.compilation.errors.map(cleanErrorStack);
57 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
58 |
59 | expect(errors).toMatchSnapshot('errors');
60 | expect(warnings).toMatchSnapshot('warnings');
61 |
62 | for (const file in stats.compilation.assets) {
63 | if (
64 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
65 | ) {
66 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
67 | }
68 | }
69 | });
70 | });
71 |
72 | it('matches snapshot for a single `true` value (`devtool` is `source-map`)', () => {
73 | const compiler = createCompiler({
74 | entry: `${__dirname}/fixtures/entry.js`,
75 | devtool: 'source-map',
76 | });
77 |
78 | new UglifyJsPlugin({ sourceMap: true }).apply(compiler);
79 |
80 | return compile(compiler).then((stats) => {
81 | const errors = stats.compilation.errors.map(cleanErrorStack);
82 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
83 |
84 | expect(errors).toMatchSnapshot('errors');
85 | expect(warnings).toMatchSnapshot('warnings');
86 |
87 | for (const file in stats.compilation.assets) {
88 | if (
89 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
90 | ) {
91 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
92 | }
93 | }
94 | });
95 | });
96 |
97 | it('matches snapshot for a single `true` value (`devtool` is `false`)', () => {
98 | const compiler = createCompiler({
99 | entry: `${__dirname}/fixtures/entry.js`,
100 | devtool: false,
101 | });
102 |
103 | new UglifyJsPlugin({ sourceMap: true }).apply(compiler);
104 |
105 | return compile(compiler).then((stats) => {
106 | const errors = stats.compilation.errors.map(cleanErrorStack);
107 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
108 |
109 | expect(errors).toMatchSnapshot('errors');
110 | expect(warnings).toMatchSnapshot('warnings');
111 |
112 | for (const file in stats.compilation.assets) {
113 | if (
114 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
115 | ) {
116 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
117 | }
118 | }
119 | });
120 | });
121 |
122 | it('matches snapshot for a single `true` value (`devtool` is `source-map`) and source map invalid', () => {
123 | const emitBrokenSourceMapPlugin = new (class EmitBrokenSourceMapPlugin {
124 | apply(pluginCompiler) {
125 | pluginCompiler.hooks.compilation.tap(
126 | { name: this.constructor.name },
127 | (compilation) => {
128 | compilation.hooks.additionalChunkAssets.tap(
129 | { name: this.constructor.name },
130 | () => {
131 | compilation.additionalChunkAssets.push('broken-source-map.js');
132 |
133 | const assetContent = 'var test = 1;';
134 |
135 | // eslint-disable-next-line no-param-reassign
136 | compilation.assets['broken-source-map.js'] = {
137 | size() {
138 | return assetContent.length;
139 | },
140 | source() {
141 | return assetContent;
142 | },
143 | sourceAndMap() {
144 | return {
145 | source: this.source(),
146 | map: {},
147 | };
148 | },
149 | };
150 | }
151 | );
152 | }
153 | );
154 | }
155 | })();
156 | const compiler = createCompiler({
157 | entry: `${__dirname}/fixtures/entry.js`,
158 | devtool: 'source-map',
159 | plugins: [emitBrokenSourceMapPlugin],
160 | });
161 |
162 | new UglifyJsPlugin({ sourceMap: true }).apply(compiler);
163 |
164 | return compile(compiler).then((stats) => {
165 | const errors = stats.compilation.errors.map(cleanErrorStack);
166 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
167 |
168 | expect(errors).toMatchSnapshot('errors');
169 | expect(warnings).toMatchSnapshot('warnings');
170 |
171 | for (const file in stats.compilation.assets) {
172 | if (
173 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
174 | ) {
175 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
176 | }
177 | }
178 | });
179 | });
180 | });
181 |
--------------------------------------------------------------------------------
/test/supports-multicompiler.test.js:
--------------------------------------------------------------------------------
1 | import MultiCompiler from 'webpack/lib/MultiCompiler';
2 | import MultiStats from 'webpack/lib/MultiStats';
3 |
4 | import UglifyJsPlugin from '../src/index';
5 |
6 | import {
7 | cleanErrorStack,
8 | createCompiler,
9 | countPlugins,
10 | compile,
11 | } from './helpers';
12 |
13 | describe('when using MultiCompiler', () => {
14 | it('matches snapshot with empty options', () => {
15 | const multiCompiler = createCompiler([
16 | {
17 | mode: 'production',
18 | bail: true,
19 | cache: false,
20 | entry: `${__dirname}/fixtures/entry.js`,
21 | output: {
22 | path: `${__dirname}/dist`,
23 | filename: '[name].[chunkhash].js',
24 | chunkFilename: '[id].[name].[chunkhash].js',
25 | },
26 | optimization: {
27 | minimize: false,
28 | },
29 | },
30 | {
31 | mode: 'production',
32 | bail: true,
33 | cache: false,
34 | entry: `${__dirname}/fixtures/entry.js`,
35 | output: {
36 | path: `${__dirname}/dist`,
37 | filename: '[name].[chunkhash].js',
38 | chunkFilename: '[id].[name].[chunkhash].js',
39 | },
40 | optimization: {
41 | minimize: false,
42 | },
43 | plugins: [new UglifyJsPlugin()],
44 | },
45 | {
46 | mode: 'production',
47 | bail: true,
48 | cache: false,
49 | entry: `${__dirname}/fixtures/import-export/entry.js`,
50 | output: {
51 | path: `${__dirname}/dist-MultiCompiler`,
52 | filename: '[name].[chunkhash].js',
53 | chunkFilename: '[id].[name].[chunkhash].js',
54 | },
55 | optimization: {
56 | minimize: false,
57 | },
58 | plugins: [new UglifyJsPlugin()],
59 | },
60 | ]);
61 |
62 | const emptyPluginCount = countPlugins(multiCompiler.compilers[0]);
63 | const expectedPluginCount = countPlugins(multiCompiler.compilers[1]);
64 |
65 | expect(emptyPluginCount).not.toEqual(expectedPluginCount);
66 | expect(multiCompiler).toBeInstanceOf(MultiCompiler);
67 |
68 | multiCompiler.compilers.slice(2).forEach((compiler) => {
69 | const pluginCount = countPlugins(compiler);
70 | expect(pluginCount).not.toEqual(emptyPluginCount);
71 | expect(pluginCount).toEqual(expectedPluginCount);
72 | expect(pluginCount).toMatchSnapshot('compiler plugin count');
73 | });
74 |
75 | expect(multiCompiler).toBeInstanceOf(MultiCompiler);
76 |
77 | return compile(multiCompiler).then((multiStats) => {
78 | expect(multiStats).toBeInstanceOf(MultiStats);
79 |
80 | multiStats.stats.forEach((stats) => {
81 | const errors = stats.compilation.errors.map(cleanErrorStack);
82 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
83 |
84 | expect(errors.length).toEqual(0);
85 | expect(warnings.length).toEqual(0);
86 |
87 | expect(errors).toMatchSnapshot('errors');
88 | expect(warnings).toMatchSnapshot('warnings');
89 |
90 | for (const file in stats.compilation.assets) {
91 | if (
92 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
93 | ) {
94 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(
95 | file
96 | );
97 | }
98 | }
99 | });
100 | });
101 | });
102 | });
103 |
--------------------------------------------------------------------------------
/test/test-option.test.js:
--------------------------------------------------------------------------------
1 | import UglifyJsPlugin from '../src/index';
2 |
3 | import { cleanErrorStack, createCompiler, compile } from './helpers';
4 |
5 | describe('when applied with `test` option', () => {
6 | let compiler;
7 |
8 | beforeEach(() => {
9 | compiler = createCompiler({
10 | entry: {
11 | js: `${__dirname}/fixtures/entry.js`,
12 | mjs: `${__dirname}/fixtures/entry.mjs`,
13 | importExport: `${__dirname}/fixtures/import-export/entry.js`,
14 | AsyncImportExport: `${__dirname}/fixtures/async-import-export/entry.js`,
15 | },
16 | output: {
17 | path: `${__dirname}/dist`,
18 | filename: '[name].js?var=[hash]',
19 | chunkFilename: '[id].[name].js?ver=[hash]',
20 | },
21 | });
22 | });
23 |
24 | it('matches snapshot with empty value', () => {
25 | new UglifyJsPlugin().apply(compiler);
26 |
27 | return compile(compiler).then((stats) => {
28 | const errors = stats.compilation.errors.map(cleanErrorStack);
29 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
30 |
31 | expect(errors).toMatchSnapshot('errors');
32 | expect(warnings).toMatchSnapshot('warnings');
33 |
34 | for (const file in stats.compilation.assets) {
35 | if (
36 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
37 | ) {
38 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
39 | }
40 | }
41 | });
42 | });
43 |
44 | it('matches snapshot for a single `test` value ({RegExp})', () => {
45 | new UglifyJsPlugin({
46 | test: /(m)?js\.js(\?.*)?$/i,
47 | }).apply(compiler);
48 |
49 | return compile(compiler).then((stats) => {
50 | const errors = stats.compilation.errors.map(cleanErrorStack);
51 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
52 |
53 | expect(errors).toMatchSnapshot('errors');
54 | expect(warnings).toMatchSnapshot('warnings');
55 |
56 | for (const file in stats.compilation.assets) {
57 | if (
58 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
59 | ) {
60 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
61 | }
62 | }
63 | });
64 | });
65 |
66 | it('matches snapshot for a single `test` value ({String})', () => {
67 | new UglifyJsPlugin({
68 | test: 'js.js',
69 | }).apply(compiler);
70 |
71 | return compile(compiler).then((stats) => {
72 | const errors = stats.compilation.errors.map(cleanErrorStack);
73 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
74 |
75 | expect(errors).toMatchSnapshot('errors');
76 | expect(warnings).toMatchSnapshot('warnings');
77 |
78 | for (const file in stats.compilation.assets) {
79 | if (
80 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
81 | ) {
82 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
83 | }
84 | }
85 | });
86 | });
87 |
88 | it('matches snapshot for multiple `test` values ({RegExp})', () => {
89 | new UglifyJsPlugin({
90 | test: [/(m)?js\.js(\?.*)?$/i, /AsyncImportExport\.js(\?.*)?$/i],
91 | }).apply(compiler);
92 |
93 | return compile(compiler).then((stats) => {
94 | const errors = stats.compilation.errors.map(cleanErrorStack);
95 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
96 |
97 | expect(errors).toMatchSnapshot('errors');
98 | expect(warnings).toMatchSnapshot('warnings');
99 |
100 | for (const file in stats.compilation.assets) {
101 | if (
102 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
103 | ) {
104 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
105 | }
106 | }
107 | });
108 | });
109 |
110 | it('matches snapshot for multiple `test` values ({String})', () => {
111 | new UglifyJsPlugin({
112 | test: ['js.js', 'AsyncImportExport.js'],
113 | }).apply(compiler);
114 |
115 | return compile(compiler).then((stats) => {
116 | const errors = stats.compilation.errors.map(cleanErrorStack);
117 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
118 |
119 | expect(errors).toMatchSnapshot('errors');
120 | expect(warnings).toMatchSnapshot('warnings');
121 |
122 | for (const file in stats.compilation.assets) {
123 | if (
124 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
125 | ) {
126 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
127 | }
128 | }
129 | });
130 | });
131 | });
132 |
--------------------------------------------------------------------------------
/test/validation.test.js:
--------------------------------------------------------------------------------
1 | import UglifyJsPlugin from '../src';
2 |
3 | it('validation', () => {
4 | /* eslint-disable no-new */
5 | expect(() => {
6 | new UglifyJsPlugin({ test: /foo/ });
7 | }).not.toThrow();
8 |
9 | expect(() => {
10 | new UglifyJsPlugin({ test: [/foo/] });
11 | }).not.toThrow();
12 |
13 | expect(() => {
14 | new UglifyJsPlugin({ include: /foo/ });
15 | }).not.toThrow();
16 |
17 | expect(() => {
18 | new UglifyJsPlugin({ include: [/foo/] });
19 | }).not.toThrow();
20 |
21 | expect(() => {
22 | new UglifyJsPlugin({ exclude: /foo/ });
23 | }).not.toThrow();
24 |
25 | expect(() => {
26 | new UglifyJsPlugin({ exclude: [/foo/] });
27 | }).not.toThrow();
28 |
29 | expect(() => {
30 | new UglifyJsPlugin({ chunkFilter: () => {} });
31 | }).not.toThrow();
32 |
33 | expect(() => {
34 | new UglifyJsPlugin({ chunkFilter: true });
35 | }).toThrowErrorMatchingSnapshot();
36 |
37 | expect(() => {
38 | new UglifyJsPlugin({ doesntExist: true });
39 | }).toThrowErrorMatchingSnapshot();
40 |
41 | expect(() => {
42 | new UglifyJsPlugin({ cache: true });
43 | }).not.toThrow();
44 |
45 | expect(() => {
46 | new UglifyJsPlugin({ cache: false });
47 | }).not.toThrow();
48 |
49 | expect(() => {
50 | new UglifyJsPlugin({ cache: 'path/to/cache/directory' });
51 | }).not.toThrow();
52 |
53 | expect(() => {
54 | new UglifyJsPlugin({ cache: {} });
55 | }).toThrowErrorMatchingSnapshot();
56 |
57 | expect(() => {
58 | new UglifyJsPlugin({ cacheKeys() {} });
59 | }).not.toThrow();
60 |
61 | expect(() => {
62 | new UglifyJsPlugin({ parallel: true });
63 | }).not.toThrow();
64 |
65 | expect(() => {
66 | new UglifyJsPlugin({ parallel: false });
67 | }).not.toThrow();
68 |
69 | expect(() => {
70 | new UglifyJsPlugin({ parallel: 2 });
71 | }).not.toThrow();
72 |
73 | expect(() => {
74 | new UglifyJsPlugin({ parallel: '2' });
75 | }).toThrowErrorMatchingSnapshot();
76 |
77 | expect(() => {
78 | new UglifyJsPlugin({ parallel: {} });
79 | }).toThrowErrorMatchingSnapshot();
80 |
81 | expect(() => {
82 | new UglifyJsPlugin({ sourceMap: true });
83 | }).not.toThrow();
84 |
85 | expect(() => {
86 | new UglifyJsPlugin({ sourceMap: false });
87 | }).not.toThrow();
88 |
89 | expect(() => {
90 | new UglifyJsPlugin({ sourceMap: 'true' });
91 | }).toThrowErrorMatchingSnapshot();
92 |
93 | expect(() => {
94 | new UglifyJsPlugin({ minify() {} });
95 | }).not.toThrow();
96 |
97 | expect(() => {
98 | new UglifyJsPlugin({ uglifyOptions: null });
99 | }).toThrowErrorMatchingSnapshot();
100 |
101 | expect(() => {
102 | new UglifyJsPlugin({ uglifyOptions: {} });
103 | }).not.toThrow();
104 |
105 | expect(() => {
106 | new UglifyJsPlugin({
107 | uglifyOptions: {
108 | // eslint-disable-next-line no-undefined
109 | ecma: undefined,
110 | warnings: false,
111 | parse: {},
112 | compress: {},
113 | mangle: true,
114 | module: false,
115 | output: null,
116 | toplevel: false,
117 | nameCache: null,
118 | ie8: false,
119 | keep_classnames: false,
120 | keep_fnames: false,
121 | safari10: false,
122 | },
123 | });
124 | }).not.toThrow();
125 |
126 | expect(() => {
127 | new UglifyJsPlugin({ uglifyOptions: { emca: 5 } });
128 | }).not.toThrow();
129 |
130 | expect(() => {
131 | new UglifyJsPlugin({ extractComments: true });
132 | }).not.toThrow();
133 |
134 | expect(() => {
135 | new UglifyJsPlugin({ extractComments: false });
136 | }).not.toThrow();
137 |
138 | expect(() => {
139 | new UglifyJsPlugin({ extractComments: /comment/ });
140 | }).not.toThrow();
141 |
142 | expect(() => {
143 | new UglifyJsPlugin({ extractComments() {} });
144 | }).not.toThrow();
145 |
146 | expect(() => {
147 | new UglifyJsPlugin({ warningsFilter() {} });
148 | }).not.toThrow();
149 |
150 | expect(() => {
151 | new UglifyJsPlugin({ warningsFilter: true });
152 | }).toThrowErrorMatchingSnapshot();
153 | /* eslint-enable no-new */
154 | });
155 |
--------------------------------------------------------------------------------
/test/warningsFilter-option.test.js:
--------------------------------------------------------------------------------
1 | import UglifyJsPlugin from '../src/index';
2 |
3 | import { cleanErrorStack, createCompiler, compile } from './helpers';
4 |
5 | describe('when applied with `warningsFilter` option', () => {
6 | let compiler;
7 |
8 | beforeEach(() => {
9 | compiler = createCompiler({
10 | entry: {
11 | one: `${__dirname}/fixtures/unreachable-code.js`,
12 | two: `${__dirname}/fixtures/unreachable-code-2.js`,
13 | },
14 | });
15 | });
16 |
17 | it('matches snapshot for a `function` value and `sourceMap` is `false` (filter by message)', () => {
18 | new UglifyJsPlugin({
19 | warningsFilter(warning) {
20 | if (/Dropping unreachable code/.test(warning)) {
21 | return true;
22 | }
23 |
24 | return false;
25 | },
26 | uglifyOptions: {
27 | warnings: true,
28 | },
29 | sourceMap: false,
30 | }).apply(compiler);
31 |
32 | return compile(compiler).then((stats) => {
33 | const errors = stats.compilation.errors.map(cleanErrorStack);
34 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
35 |
36 | expect(errors).toMatchSnapshot('errors');
37 | expect(warnings).toMatchSnapshot('warnings');
38 |
39 | for (const file in stats.compilation.assets) {
40 | if (
41 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
42 | ) {
43 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
44 | }
45 | }
46 | });
47 | });
48 |
49 | it('matches snapshot for a `function` value and `sourceMap` is `true` (filter by message)', () => {
50 | new UglifyJsPlugin({
51 | warningsFilter(warning) {
52 | if (/Dropping unreachable code/.test(warning)) {
53 | return true;
54 | }
55 |
56 | return false;
57 | },
58 | uglifyOptions: {
59 | warnings: true,
60 | },
61 | sourceMap: true,
62 | }).apply(compiler);
63 |
64 | return compile(compiler).then((stats) => {
65 | const errors = stats.compilation.errors.map(cleanErrorStack);
66 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
67 |
68 | expect(errors).toMatchSnapshot('errors');
69 | expect(warnings).toMatchSnapshot('warnings');
70 |
71 | for (const file in stats.compilation.assets) {
72 | if (
73 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
74 | ) {
75 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
76 | }
77 | }
78 | });
79 | });
80 |
81 | it('matches snapshot for a `function` value and `sourceMap` is `true` (filter by source)', () => {
82 | new UglifyJsPlugin({
83 | warningsFilter(warning, source) {
84 | if (/unreachable-code\.js/.test(source)) {
85 | return true;
86 | }
87 |
88 | return false;
89 | },
90 | uglifyOptions: {
91 | warnings: true,
92 | },
93 | sourceMap: true,
94 | }).apply(compiler);
95 |
96 | return compile(compiler).then((stats) => {
97 | const errors = stats.compilation.errors.map(cleanErrorStack);
98 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
99 |
100 | expect(errors).toMatchSnapshot('errors');
101 | expect(warnings).toMatchSnapshot('warnings');
102 |
103 | for (const file in stats.compilation.assets) {
104 | if (
105 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
106 | ) {
107 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
108 | }
109 | }
110 | });
111 | });
112 |
113 | it('matches snapshot for a `function` value and `sourceMap` is `true` (filter by file)', () => {
114 | new UglifyJsPlugin({
115 | warningsFilter(warning, source, file) {
116 | if (/two\.(.*)?\.js/.test(file)) {
117 | return true;
118 | }
119 |
120 | return false;
121 | },
122 | uglifyOptions: {
123 | warnings: true,
124 | },
125 | sourceMap: true,
126 | }).apply(compiler);
127 |
128 | return compile(compiler).then((stats) => {
129 | const errors = stats.compilation.errors.map(cleanErrorStack);
130 | const warnings = stats.compilation.warnings.map(cleanErrorStack);
131 |
132 | expect(errors).toMatchSnapshot('errors');
133 | expect(warnings).toMatchSnapshot('warnings');
134 |
135 | for (const file in stats.compilation.assets) {
136 | if (
137 | Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)
138 | ) {
139 | expect(stats.compilation.assets[file].source()).toMatchSnapshot(file);
140 | }
141 | }
142 | });
143 | });
144 | });
145 |
--------------------------------------------------------------------------------
/test/worker.test.js:
--------------------------------------------------------------------------------
1 | import serialize from 'serialize-javascript';
2 |
3 | import worker from '../src/worker';
4 |
5 | describe('matches snapshot', () => {
6 | it('normalizes when options.extractComments is regex', () => {
7 | const options = {
8 | file: 'test1.js',
9 | input: 'var foo = 1;/* hello */',
10 | extractComments: /foo/,
11 | };
12 | worker(serialize(options), (error, data) => {
13 | if (error) {
14 | throw error;
15 | }
16 | expect(data).toMatchSnapshot(options.file);
17 | });
18 | });
19 |
20 | it('normalizes when uglifyOptions.output.comments is string: all', () => {
21 | const options = {
22 | file: 'test2.js',
23 | input: 'var foo = 1;/* hello */',
24 | uglifyOptions: {
25 | output: {
26 | comments: 'all',
27 | },
28 | },
29 | };
30 | worker(serialize(options), (error, data) => {
31 | if (error) {
32 | throw error;
33 | }
34 | expect(data).toMatchSnapshot(options.file);
35 | });
36 | });
37 |
38 | it('normalizes when uglifyOptions.output.comments is string: some', () => {
39 | const options = {
40 | file: 'test3.js',
41 | input: 'var foo = 1;/* hello */',
42 | uglifyOptions: {
43 | output: {
44 | comments: 'some',
45 | },
46 | },
47 | };
48 | worker(serialize(options), (error, data) => {
49 | if (error) {
50 | throw error;
51 | }
52 | expect(data).toMatchSnapshot(options.file);
53 | });
54 | });
55 |
56 | it('normalizes when uglifyOptions.extractComments is number', () => {
57 | const options = {
58 | file: 'test4.js',
59 | input: 'var foo = 1;/* hello */',
60 | uglifyOptions: {
61 | output: {
62 | comments: 'some',
63 | },
64 | },
65 | extractComments: 1,
66 | };
67 | worker(serialize(options), (error, data) => {
68 | if (error) {
69 | throw error;
70 | }
71 | expect(data).toMatchSnapshot(options.file);
72 | });
73 | });
74 |
75 | it('when applied with extract option set to a single file', () => {
76 | const options = {
77 | file: 'test5.js',
78 | input: '/******/ function hello(a) {console.log(a)}',
79 | uglifyOptions: {
80 | output: {
81 | comments: 'all',
82 | },
83 | },
84 | extractComments: {
85 | condition: 'should be extracted',
86 | filename(file) {
87 | return file.replace(/(\.\w+)$/, '.license$1');
88 | },
89 | banner(licenseFile) {
90 | return `License information can be found in ${licenseFile}`;
91 | },
92 | },
93 | };
94 | worker(serialize(options), (error, data) => {
95 | if (error) {
96 | throw error;
97 | }
98 | expect(data).toMatchSnapshot(options.file);
99 | });
100 | });
101 |
102 | it('when options.inputSourceMap', () => {
103 | const options = {
104 | file: 'test6.js',
105 | input: 'function foo(x) { if (x) { return bar(); not_called1(); } }',
106 | inputSourceMap: {
107 | version: 3,
108 | sources: ['test1.js'],
109 | names: ['foo', 'x', 'bar', 'not_called1'],
110 | mappings: 'AAAA,QAASA,KAAIC,GACT,GAAIA,EAAG,CACH,MAAOC,MACPC',
111 | },
112 | };
113 | worker(serialize(options), (error, data) => {
114 | if (error) {
115 | throw error;
116 | }
117 | expect(data).toMatchSnapshot(options.file);
118 | });
119 | });
120 | });
121 |
--------------------------------------------------------------------------------