Compare commits

..

134 Commits

Author SHA1 Message Date
Alex Lam S.L
f1f4a4dd82 v3.13.4 2021-04-12 04:33:40 +08:00
Alex Lam S.L
ca49f6f41a fix corner case in collapse_vars (#4853)
fixes #4852
2021-04-08 17:50:40 +08:00
Alex Lam S.L
8a82822654 fix corner case in if_return (#4851)
fixes #4848
2021-04-08 08:57:59 +08:00
Alex Lam S.L
ebe4e1ad28 fix corner case in unused (#4850)
fixes #4849
2021-04-08 06:31:15 +08:00
Alex Lam S.L
a37ca558dd reject invalid for await syntax (#4847) 2021-04-07 22:37:15 +08:00
Alex Lam S.L
73a564343b preserve compatibility of quote_style (#4845) 2021-04-07 06:49:12 +08:00
Alex Lam S.L
4870747306 speed up OutputStream (#4844) 2021-04-07 02:57:23 +08:00
Alex Lam S.L
b179a2459f parse octal literals correctly (#4843) 2021-04-07 02:23:35 +08:00
Alex Lam S.L
231c3d7c84 clean up OutputStream (#4842) 2021-04-06 21:34:27 +08:00
Alex Lam S.L
aed758ed5c enhance comparisons & reduce_vars (#4841) 2021-04-03 22:06:05 +08:00
Alex Lam S.L
0df028187d fix corner case in hoist_vars (#4840)
fixes #4839
2021-04-03 10:07:18 +08:00
Alex Lam S.L
10fbf8e295 support mangle.properties on class (#4838) 2021-04-03 06:00:19 +08:00
Alex Lam S.L
cf38b52afa parse import.meta correctly (#4836) 2021-04-03 04:31:29 +08:00
Alex Lam S.L
e755d01a0b fix corner case in unused (#4835)
fixes #4834
2021-04-02 03:55:53 +08:00
Alex Lam S.L
4084948d3b suppress invalid AST transform in --reduce-test (#4833) 2021-04-01 18:52:29 +08:00
Alex Lam S.L
cea1fb5c58 fix corner case in properties (#4832)
fixes #4831
2021-04-01 14:39:51 +08:00
Alex Lam S.L
1947a21824 fix corner case in properties (#4830)
fixes #4829
2021-04-01 07:05:50 +08:00
Alex Lam S.L
6335b5fd8a v3.13.3 2021-03-29 06:58:06 +08:00
Alex Lam S.L
daa8319b8a fix corner cases with logical assignment operators (#4828)
fixes #4827
2021-03-28 03:44:45 +08:00
Alex Lam S.L
d5599604e8 enhance collapse_vars (#4826) 2021-03-27 22:14:37 +08:00
Alex Lam S.L
072933f1d5 diagnose GitHub Actions (#4825) 2021-03-25 18:44:39 +08:00
Alex Lam S.L
39df3a1680 fix corner case in functions (#4824)
fixes #4823
2021-03-25 08:49:01 +08:00
Alex Lam S.L
03c5ecb2e3 fix corner cases with class (#4822)
fixes #4821
2021-03-25 04:36:50 +08:00
Alex Lam S.L
40ef074cb3 fix corner case in comparisons (#4820)
fixes #4819
2021-03-24 10:10:02 +08:00
Alex Lam S.L
78e3936cd4 fix corner case in inline (#4818)
fixes #4817
2021-03-23 22:33:24 +08:00
Alex Lam S.L
e7be38b42a fix corner cases with logical assignment operators (#4816)
fixes #4815
2021-03-23 13:02:45 +08:00
Alex Lam S.L
44394e61c9 workaround toString() quirks on global context (#4814) 2021-03-23 11:15:41 +08:00
Alex Lam S.L
f9055df44d support logical assignment operators (#4813) 2021-03-23 04:59:43 +08:00
Alex Lam S.L
51bdb7281b improve global context enumeration under sandbox (#4812)
fixes #4811
2021-03-22 22:43:33 +08:00
Alex Lam S.L
9c01511f84 v3.13.2 2021-03-22 04:34:26 +08:00
Alex Lam S.L
9faee3b66a fix corner case in arguments (#4810)
fixes #4809
2021-03-21 10:40:32 +08:00
Alex Lam S.L
8ea1ced389 document various v8 bugs (#4808)
closes #4805
2021-03-21 06:33:45 +08:00
Alex Lam S.L
24619daf68 fix corner cases in collapse_vars & unused (#4807)
fixes #4806
2021-03-21 03:51:03 +08:00
Alex Lam S.L
b89cc84c3a fix corner case in pure_getters (#4804)
fixes #4803
2021-03-19 16:16:53 +08:00
Alex Lam S.L
3016a78d85 fix corner case in reduce_vars (#4802)
fixes #4801
2021-03-18 23:58:35 +08:00
Alex Lam S.L
2508481e33 fix corner case in evaluate (#4800)
fixes #4799
2021-03-18 11:31:20 +08:00
Alex Lam S.L
48c46fa9a7 fix corner case in sequences (#4798) 2021-03-18 08:31:55 +08:00
Alex Lam S.L
7da49b5709 fix corner case in collapse_vars (#4797) 2021-03-18 07:50:55 +08:00
Alex Lam S.L
d837a46ebd fix corner case in reduce_vars (#4796) 2021-03-18 05:14:14 +08:00
Alex Lam S.L
d4303b62cc build Bootstrap for verification testing (#4795) 2021-03-17 22:34:17 +08:00
Alex Lam S.L
7d595e2eac improve comment formatting logic (#4794) 2021-03-17 13:51:52 +08:00
Alex Lam S.L
9fc0ff5953 parse extended Unicode literal correctly (#4792) 2021-03-17 10:34:50 +08:00
Alex Lam S.L
997d09bb33 extend keep_fnames to classes (#4793) 2021-03-17 09:28:27 +08:00
Alex Lam S.L
b244b4ec21 enhance "functions" (#4791) 2021-03-17 05:38:33 +08:00
Alex Lam S.L
b872ffee01 fix corner case in hoist_funs (#4790) 2021-03-17 01:48:23 +08:00
Alex Lam S.L
9a9543013c fix corner case in functions (#4789)
fixes #4788
2021-03-16 20:58:51 +08:00
Alex Lam S.L
b98ce6c84f avoid flaky cases in verification testing (#4785) 2021-03-16 20:50:33 +08:00
Alex Lam S.L
7b43b6396f ensure valid generated cases in --reduce-test (#4787)
closes #4786
2021-03-16 18:14:24 +08:00
Alex Lam S.L
67f8fcb103 build web-tooling-benchmark for verification testing (#4776) 2021-03-16 17:34:10 +08:00
Alex Lam S.L
352a944868 fix corner cases with new.target (#4784) 2021-03-16 14:34:36 +08:00
Alex Lam S.L
77c9116c91 fix corner case in unsafe (#4783) 2021-03-16 09:42:28 +08:00
Alex Lam S.L
e821787095 fix corner case in reduce_vars (#4782) 2021-03-16 08:05:01 +08:00
Alex Lam S.L
aa6e33e208 parse out-of-range numerals correctly (#4781) 2021-03-16 06:52:21 +08:00
Alex Lam S.L
176581d732 fix corner cases with template literals (#4780) 2021-03-15 21:54:05 +08:00
Alex Lam S.L
01aa078e9c support **= (#4778) 2021-03-15 21:53:48 +08:00
Alex Lam S.L
149d75c092 fix corner case in unsafe (#4779) 2021-03-15 17:51:32 +08:00
Alex Lam S.L
2619bff3cf fix corner case in conditionals (#4777) 2021-03-15 12:44:41 +08:00
Alex Lam S.L
4fb54b066f improve Node.js setup on GitHub Actions (#4775) 2021-03-15 06:25:33 +08:00
Alex Lam S.L
e124ef57e3 parse parentheses-terminated statements correctly (#4774)
fixes #4772
2021-03-14 14:09:29 +08:00
Alex Lam S.L
73e6b2550b fix corner cases with yield (#4771)
fixes #4769
2021-03-14 04:39:30 +08:00
Alex Lam S.L
241113200e support shorthand key-symbol output (#4768) 2021-03-13 15:37:01 +08:00
Alex Lam S.L
6f3ab09319 fix corner case in unused (#4767)
fixes #4766
2021-03-13 15:09:07 +08:00
Alex Lam S.L
3b5d5014e0 implement annotations (#4763) 2021-03-13 03:40:49 +08:00
Alex Lam S.L
c36c3cb470 fix corner case in side_effects (#4765)
fixes #4764
2021-03-13 02:40:28 +08:00
Alex Lam S.L
24b73a95fa v3.13.1 2021-03-12 06:50:05 +08:00
Alex Lam S.L
862b1b77b5 fix corner cases in merge_vars & with exports (#4762)
fixes #4761
2021-03-11 09:16:16 +08:00
Alex Lam S.L
b4944a31a4 fix corner case in merge_vars (#4760)
fixes #4759
2021-03-10 23:44:49 +08:00
Alex Lam S.L
58362d5ec7 build Math.js for verification testing (#4758) 2021-03-10 03:34:30 +08:00
Alex Lam S.L
01fa430a3e fix corner case in unused (#4757)
fixes #4756
2021-03-09 07:59:52 +08:00
Alex Lam S.L
f4ee0f651c fix corner case in unsafe (#4755) 2021-03-09 04:40:21 +08:00
Alex Lam S.L
077512d151 fix corner case in inline (#4754)
fixes #4753
2021-03-08 12:38:53 +08:00
Alex Lam S.L
e4848a7f5a speed up ufuzz asynchronous testing (#4750) 2021-03-08 04:25:56 +08:00
Alex Lam S.L
f52b0e7c31 fix corner case in side_effects (#4752)
fixes #4751
2021-03-08 04:19:51 +08:00
Alex Lam S.L
31e7d25cad fix issues uncovered by lgtm (#4749) 2021-03-07 12:44:34 +08:00
Alex Lam S.L
12babdfe20 build external projects for verification testing (#4741) 2021-03-07 12:41:52 +08:00
Alex Lam S.L
397e48b97e fix corner case in collapse_vars & reduce_vars (#4748)
fixes #4747
2021-03-07 10:33:51 +08:00
Alex Lam S.L
c7520b4b97 support new.target (#4746)
fixes #4745
2021-03-07 07:11:36 +08:00
Alex Lam S.L
ad903e9240 fix corner cases with export (#4743)
fixes #4742
2021-03-06 22:49:39 +08:00
Alex Lam S.L
83c3838b07 fix corner case in awaits (#4740)
fixes #4738
2021-03-06 04:25:44 +08:00
Alex Lam S.L
fa09f87589 fix corner case in hoist_vars (#4739)
fixes #4736
2021-03-06 04:25:32 +08:00
Alex Lam S.L
2db1a141ab speed up compress tests (#4737) 2021-03-06 01:11:05 +08:00
Alex Lam S.L
dd30ed6a9b enhance collapse_vars (#4735) 2021-03-05 13:18:01 +08:00
Alex Lam S.L
cb50a2d192 fix corner case in collapse_vars (#4733)
fixes #4732
2021-03-04 17:13:54 +08:00
Alex Lam S.L
20be5209c0 fix corner cases in ie8 & side_effects (#4731)
fixes #4729
fixes #4730
2021-03-04 03:30:07 +08:00
Alex Lam S.L
2a49760032 enhance side_effects (#4727) 2021-03-03 18:47:17 +08:00
Alex Lam S.L
04ed818f0a improve ufuzz test generation (#4724) 2021-03-03 15:42:46 +08:00
Alex Lam S.L
10ca578ee5 fix corner case in inline (#4726)
fixes #4725
2021-03-03 09:18:02 +08:00
Alex Lam S.L
955411e065 fix corner cases with class (#4723)
fixes #4720
fixes #4721
fixes #4722
2021-03-03 03:32:58 +08:00
Alex Lam S.L
adcafce048 fix corner cases in varify (#4719) 2021-03-02 23:33:58 +08:00
Alex Lam S.L
b1e05fd48a fix corner case in inline & sequences (#4718)
fixes #4717
2021-03-02 17:39:34 +08:00
Alex Lam S.L
23b51287aa fix corner case in evaluate (#4716)
fixes #4715
2021-03-02 17:33:28 +08:00
Alex Lam S.L
74dee5c445 enhance reduce_vars & varify (#4714) 2021-03-02 15:27:55 +08:00
Alex Lam S.L
ee27d87a08 enhance reduce_vars & side_effects (#4712) 2021-03-02 13:43:10 +08:00
Alex Lam S.L
62887f2c66 fix corner case with class (#4713) 2021-03-02 12:08:08 +08:00
Alex Lam S.L
68b2dadc58 enhance side_effects & unused (#4711) 2021-03-02 04:55:09 +08:00
Alex Lam S.L
bd73720061 v3.13.0 2021-03-01 23:24:17 +08:00
Alex Lam S.L
ec0440f264 fix corner cases with import (#4709)
fixes #4708
2021-03-01 07:13:49 +08:00
Alex Lam S.L
81254f67e4 support limited ufuzz testing for import (#4707) 2021-03-01 02:27:41 +08:00
Alex Lam S.L
c549ee89b9 fix corner case reduce_vars (#4706)
fixes #4705
2021-02-28 04:41:21 +08:00
Alex Lam S.L
7924a3ae8b improve export compatibility mode (#4704) 2021-02-27 21:06:23 +08:00
Alex Lam S.L
13ad10a6b5 improve export compatibility mode (#4703) 2021-02-27 14:05:36 +08:00
Alex Lam S.L
e6ebf827ce fix corner cases with export (#4699)
fixes #4698
fixes #4700
fixes #4701
fixes #4702
2021-02-27 10:15:14 +08:00
Alex Lam S.L
0a42457df6 fix corner case with arguments (#4697)
fixes #4696
2021-02-27 09:26:15 +08:00
Alex Lam S.L
ba4a771bbc support limited ufuzz testing for export (#4693)
fixes #4692
2021-02-27 04:56:34 +08:00
Alex Lam S.L
ac26993b5a fix corner cases with block-scoped functions (#4695) 2021-02-27 04:16:14 +08:00
Alex Lam S.L
ea52339502 use strict-mode-safe placeholder (#4694) 2021-02-26 16:43:53 +08:00
Alex Lam S.L
992952d8f6 fix corner cases with exports (#4691) 2021-02-26 02:22:49 +08:00
Alex Lam S.L
6d7ab63a66 fix corner cases in sequences (#4690)
fixes #4689
2021-02-25 12:48:40 +08:00
Alex Lam S.L
822b1da5d2 fix corner cases with arrow functions (#4688)
fixes #4687
2021-02-25 09:01:45 +08:00
Alex Lam S.L
72805ea73a fix corner case in collapse_vars (#4686)
fixes #4685
2021-02-25 05:31:12 +08:00
Alex Lam S.L
a5e6946f74 fix corner case in loops (#4684)
fixes #4683
2021-02-25 04:41:21 +08:00
Alex Lam S.L
b8672b55b2 fix corner case in unused (#4682)
fixes #4681
2021-02-25 02:17:28 +08:00
Alex Lam S.L
a5a958beda fix corner case in comparisons (#4680)
fixes #4679
2021-02-24 13:38:44 +00:00
Alex Lam S.L
c885660347 support nullish coalescing operator (#4678) 2021-02-24 05:57:11 +08:00
Alex Lam S.L
d68d155f93 support class literals (#4658) 2021-02-23 22:55:08 +08:00
Alex Lam S.L
e535f19189 fix corner case in templates (#4677)
fixes #4676
2021-02-22 23:59:28 +08:00
Alex Lam S.L
f9a2a9d78e enhance side_effects (#4675) 2021-02-22 15:44:16 +08:00
Alex Lam S.L
960668ccdb improve CLI usability (#4670)
closes #3308
2021-02-22 15:27:03 +08:00
Alex Lam S.L
55b59407e4 fix corner cases in reduce_vars (#4674) 2021-02-22 08:21:21 +08:00
Alex Lam S.L
b726e364c1 fix corner cases with export default (#4673) 2021-02-21 13:01:56 +08:00
Alex Lam S.L
bfe3a8b516 fix corner case with import (#4672) 2021-02-21 10:00:34 +08:00
Alex Lam S.L
ae09773ba0 parse dynamic import correctly (#4671) 2021-02-21 06:54:15 +08:00
Alex Lam S.L
8d668c2963 fix corner case in conditionals & side_effects (#4669)
fixes #4668
2021-02-20 11:13:15 +08:00
Alex Lam S.L
5f60c1b830 fix corner cases in arrow functions & rests (#4667)
fixes #4666
2021-02-19 08:26:57 +08:00
Alex Lam S.L
10de27ca3d fix corner case in reduce_vars (#4665)
fixes #4664
2021-02-19 02:04:33 +08:00
Alex Lam S.L
7b4fd858ba fix corner case in reduce_vars (#4663)
fixes #4662
2021-02-18 23:24:55 +08:00
Alex Lam S.L
a7bcd4d613 fix corner case in inline (#4660)
fixes #4659
2021-02-18 16:15:44 +08:00
Alex Lam S.L
6a2bda52f3 workaround bug in ECMAScript specification (#4656)
closes #4655
2021-02-16 23:39:06 +08:00
Alex Lam S.L
fa8aa204a0 fix corner case in reduce_vars (#4654)
fixes #4653
2021-02-16 06:46:45 +08:00
Alex Lam S.L
76b27891c6 fix syntax error in tests (#4652) 2021-02-15 14:41:07 +08:00
Alex Lam S.L
203ca2586a introduce hoist_exports (#4651) 2021-02-15 09:01:18 +08:00
Alex Lam S.L
c21f096ab8 support export statements (#4650) 2021-02-15 04:13:54 +08:00
Alex Lam S.L
b7219ac489 support import statements (#4646) 2021-02-14 04:26:43 +08:00
Alex Lam S.L
a6bb66931b improve fix for #4325 (#4649) 2021-02-14 02:24:57 +08:00
87 changed files with 10747 additions and 1745 deletions

53
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,53 @@
name: Build testing
on:
pull_request:
push:
branches: [ master ]
jobs:
ufuzz:
strategy:
fail-fast: false
matrix:
options: [ '-mb braces', '--ie8 -c', '-mc', '--toplevel -mc passes=3,pure_getters,unsafe' ]
script:
- acorn.sh
- bootstrap.sh
- buble.sh
- butternut.sh
- mathjs.sh
- rollup-es.sh
- rollup-ts.sh
- sucrase.sh
- web-tooling-benchmark.sh
include:
- node: '14'
script: acorn.sh
- node: '14'
script: bootstrap.sh
- node: '14'
script: buble.sh
- node: '14'
script: butternut.sh
- node: '14'
script: mathjs.sh
- node: '8'
script: rollup-es.sh
- node: '14'
script: rollup-ts.sh
- node: '14'
script: sucrase.sh
- node: '14'
script: web-tooling-benchmark.sh
name: ${{ matrix.script }} ${{ matrix.options }}
runs-on: ubuntu-latest
env:
NODE: ${{ matrix.node }}
OPTIONS: ${{ matrix.options }}
SCRIPT: ${{ matrix.script }}
steps:
- uses: actions/checkout@v2
- name: Perform uglify, build & test
shell: bash
run: |
. ./test/release/install.sh
./test/release/$SCRIPT $OPTIONS

View File

@@ -7,7 +7,7 @@ jobs:
test:
strategy:
matrix:
node: [ '0.8', '0.10', '0.12', '4', '6', '8', '10', '12', latest ]
node: [ '0.8', '0.10', '0.12', '4', '6', '8', '10', '12', '14', latest ]
os: [ ubuntu-latest, windows-latest ]
script: [ compress, mocha, release/benchmark, release/jetstream ]
exclude:
@@ -29,20 +29,5 @@ jobs:
- name: Perform tests
shell: bash
run: |
git clone --branch v1.6.0 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
while ! timeout 60 bash -c '. ~/.nvs/nvs.sh add $NODE && nvs use $NODE'; do
cd ~/.nvs
while !(git clean -xdf); do echo "'git clean' failed - retrying..."; done
cd -
done
. ~/.nvs/nvs.sh --version
nvs use $NODE
node --version
npm config set audit false
npm config set optional false
npm config set save false
npm config set strict-ssl false
npm config set update-notifier false
npm --version
while !(npm install); do echo "'npm install' failed - retrying..."; done
. ./test/release/install.sh
node test/$TYPE

View File

@@ -30,32 +30,10 @@ jobs:
NODE: ${{ matrix.node }}
steps:
- uses: actions/checkout@v2
- name: Install GNU Core Utilities
if: ${{ startsWith(matrix.os, 'macos') }}
env:
HOMEBREW_NO_INSTALL_CLEANUP: 1
shell: bash
run: |
brew install coreutils
- name: Perform fuzzing
shell: bash
run: |
git clone --branch v1.6.0 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
while ! timeout 60 bash -c '. ~/.nvs/nvs.sh add $NODE && nvs use $NODE'; do
cd ~/.nvs
while !(git clean -xdf); do echo "'git clean' failed - retrying..."; done
cd -
done
. ~/.nvs/nvs.sh --version
nvs use $NODE
node --version
npm config set audit false
npm config set optional false
npm config set save false
npm config set strict-ssl false
npm config set update-notifier false
npm --version
while !(npm install); do echo "'npm install' failed - retrying..."; done
. ./test/release/install.sh
if [[ $CAUSE == "schedule" ]]; then
node test/ufuzz/job $BASE_URL $TOKEN $RUN_NUM
else

284
README.md
View File

@@ -4,12 +4,11 @@ UglifyJS 3
UglifyJS is a JavaScript parser, minifier, compressor and beautifier toolkit.
#### Note:
- **`uglify-js@3` has a simplified [API](#api-reference) and [CLI](#command-line-usage)
that is not backwards compatible with [`uglify-js@2`](https://github.com/mishoo/UglifyJS/tree/v2.x)**.
- **Documentation for UglifyJS `2.x` releases can be found [here](https://github.com/mishoo/UglifyJS/tree/v2.x)**.
- `uglify-js` supports ECMAScript 5 and some newer language features.
- To minify ECMAScript 2015 or above, you may need to transpile using tools like
[Babel](https://babeljs.io/).
- `uglify-js` supports JavaScript and most language features in ECMAScript.
- For more exotic parts of ECMAScript, process your source file with transpilers
like [Babel](https://babeljs.io/) before passing onto `uglify-js`.
- `uglify-js@3` has a simplified [API](#api-reference) and [CLI](#command-line-usage)
that is not backwards compatible with [`uglify-js@2`](https://github.com/mishoo/UglifyJS/tree/v2.x).
Install
-------
@@ -86,13 +85,16 @@ a double dash to prevent input files being used as option arguments:
1 - single
2 - double
3 - original
`wrap_iife` Wrap IIFEs in parenthesis. Note: you may
`wrap_iife` Wrap IIFEs in parentheses. Note: you may
want to disable `negate_iife` under
compressor options.
-O, --output-opts [options] Specify output options (`beautify` disabled by default).
-o, --output <file> Output file path (default STDOUT). Specify `ast` or
`spidermonkey` to write UglifyJS or SpiderMonkey AST
as JSON to STDOUT respectively.
--annotations Process and preserve comment annotations.
(`/*@__PURE__*/` or `/*#__PURE__*/`)
--no-annotations Ignore and discard comment annotations.
--comments [filter] Preserve copyright comments in the output. By
default this works like Google Closure, keeping
JSDoc-style comments that contain "@license" or
@@ -222,10 +224,10 @@ Example:
To enable the mangler you need to pass `--mangle` (`-m`). The following
(comma-separated) options are supported:
- `eval` (default `false`) -- mangle names visible in scopes where `eval` or
- `eval` (default: `false`) mangle names visible in scopes where `eval` or
`with` are used.
- `reserved` (default: `[]`) -- when mangling is enabled but you want to
- `reserved` (default: `[]`) when mangling is enabled but you want to
prevent certain names from being mangled, you can declare those names with
`--mangle reserved` — pass a comma-separated list of names. For example:
@@ -492,46 +494,51 @@ if (result.error) throw result.error;
## Minify options
- `compress` (default `{}`) — pass `false` to skip compressing entirely.
- `annotations` — pass `false` to ignore all comment annotations and elide them
from output. Useful when, for instance, external tools incorrectly applied
`/*@__PURE__*/` or `/*#__PURE__*/`. Pass `true` to both compress and retain
comment annotations in output to allow for further processing downstream.
- `compress` (default: `{}`) — pass `false` to skip compressing entirely.
Pass an object to specify custom [compress options](#compress-options).
- `ie8` (default `false`) -- set to `true` to support IE8.
- `ie8` (default: `false`) set to `true` to support IE8.
- `keep_fnames` (default: `false`) -- pass `true` to prevent discarding or mangling
- `keep_fnames` (default: `false`) pass `true` to prevent discarding or mangling
of function names. Useful for code relying on `Function.prototype.name`.
- `mangle` (default `true`) — pass `false` to skip mangling names, or pass
- `mangle` (default: `true`) — pass `false` to skip mangling names, or pass
an object to specify [mangle options](#mangle-options) (see below).
- `mangle.properties` (default `false`) — a subcategory of the mangle option.
- `mangle.properties` (default: `false`) — a subcategory of the mangle option.
Pass an object to specify custom [mangle property options](#mangle-properties-options).
- `nameCache` (default `null`) -- pass an empty object `{}` or a previously
- `nameCache` (default: `null`) pass an empty object `{}` or a previously
used `nameCache` object if you wish to cache mangled variable and
property names across multiple invocations of `minify()`. Note: this is
a read/write property. `minify()` will read the name cache state of this
object and update it during minification so that it may be
reused or externally persisted by the user.
- `output` (default `null`) — pass an object if you wish to specify
- `output` (default: `null`) — pass an object if you wish to specify
additional [output options](#output-options). The defaults are optimized
for best compression.
- `parse` (default `{}`) — pass an object if you wish to specify some
- `parse` (default: `{}`) — pass an object if you wish to specify some
additional [parse options](#parse-options).
- `sourceMap` (default `false`) -- pass an object if you wish to specify
- `sourceMap` (default: `false`) pass an object if you wish to specify
[source map options](#source-map-options).
- `toplevel` (default `false`) -- set to `true` if you wish to enable top level
- `toplevel` (default: `false`) set to `true` if you wish to enable top level
variable and function name mangling and to drop unused variables and functions.
- `v8` (default `false`) -- enable workarounds for Chrome & Node.js bugs.
- `v8` (default: `false`) enable workarounds for Chrome & Node.js bugs.
- `warnings` (default `false`) — pass `true` to return compressor warnings
- `warnings` (default: `false`) — pass `true` to return compressor warnings
in `result.warnings`. Use the value `"verbose"` for more detailed warnings.
- `webkit` (default `false`) -- enable workarounds for Safari/WebKit bugs.
- `webkit` (default: `false`) enable workarounds for Safari/WebKit bugs.
PhantomJS users should set this option to `true`.
## Minify options structure
@@ -616,111 +623,121 @@ to be `false` and all symbol names will be omitted.
## Parse options
- `bare_returns` (default `false`) -- support top level `return` statements
- `bare_returns` (default: `false`) support top level `return` statements
- `html5_comments` (default `true`)
- `html5_comments` (default: `true`)
- `shebang` (default `true`) -- support `#!command` as the first line
- `shebang` (default: `true`) support `#!command` as the first line
## Compress options
- `arguments` (default: `true`) -- replace `arguments[index]` with function
- `annotations` (default: `true`) — Pass `false` to disable potentially dropping
functions marked as "pure". A function call is marked as "pure" if a comment
annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
example: `/*@__PURE__*/foo();`
- `arguments` (default: `true`) — replace `arguments[index]` with function
parameter name whenever possible.
- `arrows` (default: `true`) -- apply optimizations to arrow functions
- `arrows` (default: `true`) apply optimizations to arrow functions
- `assignments` (default: `true`) -- apply optimizations to assignment expressions
- `assignments` (default: `true`) apply optimizations to assignment expressions
- `awaits` (default: `true`) -- apply optimizations to `await` expressions
- `awaits` (default: `true`) apply optimizations to `await` expressions
- `booleans` (default: `true`) -- various optimizations for boolean context,
- `booleans` (default: `true`) various optimizations for boolean context,
for example `!!a ? b : c → a ? b : c`
- `collapse_vars` (default: `true`) -- Collapse single-use non-constant variables,
- `collapse_vars` (default: `true`) Collapse single-use non-constant variables,
side effects permitting.
- `comparisons` (default: `true`) -- apply certain optimizations to binary nodes,
- `comparisons` (default: `true`) apply certain optimizations to binary nodes,
e.g. `!(a <= b) → a > b`, attempts to negate binary nodes, e.g.
`a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
- `conditionals` (default: `true`) -- apply optimizations for `if`-s and conditional
- `conditionals` (default: `true`) apply optimizations for `if`-s and conditional
expressions
- `dead_code` (default: `true`) -- remove unreachable code
- `dead_code` (default: `true`) remove unreachable code
- `default_values` (default: `true`) -- drop overshadowed default values
- `default_values` (default: `true`) drop overshadowed default values
- `directives` (default: `true`) -- remove redundant or non-standard directives
- `directives` (default: `true`) remove redundant or non-standard directives
- `drop_console` (default: `false`) -- Pass `true` to discard calls to
- `drop_console` (default: `false`) Pass `true` to discard calls to
`console.*` functions. If you wish to drop a specific function call
such as `console.info` and/or retain side effects from function arguments
after dropping the function call then use `pure_funcs` instead.
- `drop_debugger` (default: `true`) -- remove `debugger;` statements
- `drop_debugger` (default: `true`) remove `debugger;` statements
- `evaluate` (default: `true`) -- Evaluate expression for shorter constant
- `evaluate` (default: `true`) Evaluate expression for shorter constant
representation. Pass `"eager"` to always replace function calls whenever
possible, or a positive integer to specify an upper bound for each individual
evaluation in number of characters.
- `expression` (default: `false`) -- Pass `true` to preserve completion values
- `expression` (default: `false`) Pass `true` to preserve completion values
from terminal statements without `return`, e.g. in bookmarklets.
- `functions` (default: `true`) -- convert declarations from `var`to `function`
- `functions` (default: `true`) convert declarations from `var` to `function`
whenever possible.
- `global_defs` (default: `{}`) -- see [conditional compilation](#conditional-compilation)
- `global_defs` (default: `{}`) see [conditional compilation](#conditional-compilation)
- `hoist_funs` (default: `false`) -- hoist function declarations
- `hoist_exports` (default: `true`) hoist `export` statements to facilitate
various `compress` and `mangle` optimizations.
- `hoist_props` (default: `true`) -- hoist properties from constant object and
- `hoist_funs` (default: `false`) hoist function declarations
- `hoist_props` (default: `true`) — hoist properties from constant object and
array literals into regular variables subject to a set of constraints. For example:
`var o={p:1, q:2}; f(o.p, o.q);` is converted to `f(1, 2);`. Note: `hoist_props`
works best with `toplevel` and `mangle` enabled, alongside with `compress` option
`passes` set to `2` or higher.
- `hoist_vars` (default: `false`) -- hoist `var` declarations (this is `false`
- `hoist_vars` (default: `false`) hoist `var` declarations (this is `false`
by default because it seems to increase the size of the output in general)
- `if_return` (default: `true`) -- optimizations for if/return and if/continue
- `if_return` (default: `true`) optimizations for if/return and if/continue
- `inline` (default: `true`) -- inline calls to function with simple/`return` statement:
- `false` -- same as `0`
- `0` -- disabled inlining
- `1` -- inline simple functions
- `2` -- inline functions with arguments
- `3` -- inline functions with arguments and variables
- `true` -- same as `3`
- `imports` (default: `true`) — drop unreferenced import symbols when used with `unused`
- `join_vars` (default: `true`) -- join consecutive `var` statements
- `inline` (default: `true`) — inline calls to function with simple/`return` statement:
- `false` — same as `0`
- `0` — disabled inlining
- `1` — inline simple functions
- `2` — inline functions with arguments
- `3` — inline functions with arguments and variables
- `true` — same as `3`
- `keep_fargs` (default: `false`) -- discard unused function arguments except
- `join_vars` (default: `true`) — join consecutive `var` statements
- `keep_fargs` (default: `false`) — discard unused function arguments except
when unsafe to do so, e.g. code which relies on `Function.prototype.length`.
Pass `true` to always retain function arguments.
- `keep_infinity` (default: `false`) -- Pass `true` to prevent `Infinity` from
- `keep_infinity` (default: `false`) Pass `true` to prevent `Infinity` from
being compressed into `1/0`, which may cause performance issues on Chrome.
- `loops` (default: `true`) -- optimizations for `do`, `while` and `for` loops
- `loops` (default: `true`) optimizations for `do`, `while` and `for` loops
when we can statically determine the condition.
- `merge_vars` (default: `true`) -- combine and reuse variables.
- `merge_vars` (default: `true`) combine and reuse variables.
- `negate_iife` (default: `true`) -- negate "Immediately-Called Function Expressions"
- `negate_iife` (default: `true`) negate "Immediately-Called Function Expressions"
where the return value is discarded, to avoid the parens that the
code generator would insert.
- `objects` (default: `true`) -- compact duplicate keys in object literals.
- `objects` (default: `true`) compact duplicate keys in object literals.
- `passes` (default: `1`) -- The maximum number of times to run compress.
- `passes` (default: `1`) The maximum number of times to run compress.
In some cases more than one pass leads to further compressed code. Keep in
mind more passes will take more time.
- `properties` (default: `true`) -- rewrite property access using the dot notation, for
- `properties` (default: `true`) rewrite property access using the dot notation, for
example `foo["bar"] → foo.bar`
- `pure_funcs` (default: `null`) -- You can pass an array of names and
- `pure_funcs` (default: `null`) You can pass an array of names and
UglifyJS will assume that those functions do not produce side
effects. DANGER: will not check if the name is redefined in scope.
An example case here, for instance `var q = Math.floor(a/b)`. If
@@ -732,24 +749,24 @@ to be `false` and all symbol names will be omitted.
overhead (compression will be slower). Make sure symbols under `pure_funcs`
are also under `mangle.reserved` to avoid mangling.
- `pure_getters` (default: `"strict"`) -- If you pass `true` for
- `pure_getters` (default: `"strict"`) If you pass `true` for
this, UglifyJS will assume that object property access
(e.g. `foo.bar` or `foo["bar"]`) doesn't have any side effects.
Specify `"strict"` to treat `foo.bar` as side-effect-free only when
`foo` is certain to not throw, i.e. not `null` or `undefined`.
- `reduce_funcs` (default: `true`) -- Allows single-use functions to be
- `reduce_funcs` (default: `true`) Allows single-use functions to be
inlined as function expressions when permissible allowing further
optimization. Enabled by default. Option depends on `reduce_vars`
being enabled. Some code runs faster in the Chrome V8 engine if this
option is disabled. Does not negatively impact other major browsers.
- `reduce_vars` (default: `true`) -- Improve optimization on variables assigned with and
- `reduce_vars` (default: `true`) Improve optimization on variables assigned with and
used as constant values.
- `rests` (default: `true`) -- apply optimizations to rest parameters
- `rests` (default: `true`) apply optimizations to rest parameters
- `sequences` (default: `true`) -- join consecutive simple statements using the
- `sequences` (default: `true`) join consecutive simple statements using the
comma operator. May be set to a positive integer to specify the maximum number
of consecutive comma sequences that will be generated. If this option is set to
`true` then the default `sequences` limit is `200`. Set option to `false` or `0`
@@ -758,70 +775,68 @@ to be `false` and all symbol names will be omitted.
occasions the default sequences limit leads to very slow compress times in which
case a value of `20` or less is recommended.
- `side_effects` (default: `true`) -- Pass `false` to disable potentially dropping
functions marked as "pure". A function call is marked as "pure" if a comment
annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
example: `/*@__PURE__*/foo();`
- `side_effects` (default: `true`) — drop extraneous code which does not affect
outcome of runtime execution.
- `spreads` (default: `true`) -- flatten spread expressions.
- `spreads` (default: `true`) flatten spread expressions.
- `strings` (default: `true`) -- compact string concatenations.
- `strings` (default: `true`) compact string concatenations.
- `switches` (default: `true`) -- de-duplicate and remove unreachable `switch` branches
- `switches` (default: `true`) de-duplicate and remove unreachable `switch` branches
- `templates` (default: `true`) -- compact template literals by embedding expressions
- `templates` (default: `true`) compact template literals by embedding expressions
and/or converting to string literals, e.g. `` `foo ${42}` → "foo 42"``
- `top_retain` (default: `null`) -- prevent specific toplevel functions and
- `top_retain` (default: `null`) prevent specific toplevel functions and
variables from `unused` removal (can be array, comma-separated, RegExp or
function. Implies `toplevel`)
- `toplevel` (default: `false`) -- drop unreferenced functions (`"funcs"`) and/or
- `toplevel` (default: `false`) drop unreferenced functions (`"funcs"`) and/or
variables (`"vars"`) in the top level scope (`false` by default, `true` to drop
both unreferenced functions and variables)
- `typeofs` (default: `true`) -- Transforms `typeof foo == "undefined"` into
- `typeofs` (default: `true`) Transforms `typeof foo == "undefined"` into
`foo === void 0`. Note: recommend to set this value to `false` for IE10 and
earlier versions due to known issues.
- `unsafe` (default: `false`) -- apply "unsafe" transformations (discussion below)
- `unsafe` (default: `false`) apply "unsafe" transformations (discussion below)
- `unsafe_comps` (default: `false`) -- compress expressions like `a <= b` assuming
- `unsafe_comps` (default: `false`) compress expressions like `a <= b` assuming
none of the operands can be (coerced to) `NaN`.
- `unsafe_Function` (default: `false`) -- compress and mangle `Function(args, code)`
- `unsafe_Function` (default: `false`) compress and mangle `Function(args, code)`
when both `args` and `code` are string literals.
- `unsafe_math` (default: `false`) -- optimize numerical expressions like
- `unsafe_math` (default: `false`) optimize numerical expressions like
`2 * x * 3` into `6 * x`, which may give imprecise floating point results.
- `unsafe_proto` (default: `false`) -- optimize expressions like
- `unsafe_proto` (default: `false`) optimize expressions like
`Array.prototype.slice.call(a)` into `[].slice.call(a)`
- `unsafe_regexp` (default: `false`) -- enable substitutions of variables with
- `unsafe_regexp` (default: `false`) enable substitutions of variables with
`RegExp` values the same way as if they are constants.
- `unsafe_undefined` (default: `false`) -- substitute `void 0` if there is a
- `unsafe_undefined` (default: `false`) substitute `void 0` if there is a
variable named `undefined` in scope (variable name will be mangled, typically
reduced to a single character)
- `unused` (default: `true`) -- drop unreferenced functions and variables (simple
- `unused` (default: `true`) drop unreferenced functions and variables (simple
direct variable assignments do not count as references unless set to `"keep_assign"`)
- `varify` (default: `true`) -- convert block-scoped declaractions into `var`
- `varify` (default: `true`) convert block-scoped declaractions into `var`
whenever safe to do so
- `yields` (default: `true`) -- apply optimizations to `yield` expressions
- `yields` (default: `true`) apply optimizations to `yield` expressions
## Mangle options
- `eval` (default `false`) -- Pass `true` to mangle names visible in scopes
- `eval` (default: `false`) Pass `true` to mangle names visible in scopes
where `eval` or `with` are used.
- `reserved` (default `[]`) -- Pass an array of identifiers that should be
- `reserved` (default: `[]`) Pass an array of identifiers that should be
excluded from mangling. Example: `["foo", "bar"]`.
- `toplevel` (default `false`) -- Pass `true` to mangle names declared in the
- `toplevel` (default: `false`) Pass `true` to mangle names declared in the
top level scope.
Examples:
@@ -848,18 +863,18 @@ UglifyJS.minify(code, { mangle: { toplevel: true } }).code;
### Mangle properties options
- `builtins` (default: `false`) -- Use `true` to allow the mangling of builtin
- `builtins` (default: `false`) Use `true` to allow the mangling of builtin
DOM properties. Not recommended to override this setting.
- `debug` (default: `false`) -— Mangle names with the original name still present.
- `debug` (default: `false`) — Mangle names with the original name still present.
Pass an empty string `""` to enable, or a non-empty string to set the debug suffix.
- `keep_quoted` (default: `false`) -— Only mangle unquoted property names.
- `keep_quoted` (default: `false`) — Only mangle unquoted property names.
- `regex` (default: `null`) -— Pass a RegExp literal to only mangle property
- `regex` (default: `null`) — Pass a RegExp literal to only mangle property
names matching the regular expression.
- `reserved` (default: `[]`) -- Do not mangle property names listed in the
- `reserved` (default: `[]`) Do not mangle property names listed in the
`reserved` array.
## Output options
@@ -868,19 +883,23 @@ The code generator tries to output shortest code possible by default. In
case you want beautified output, pass `--beautify` (`-b`). Optionally you
can pass additional arguments that control the code output:
- `ascii_only` (default `false`) -- escape Unicode characters in strings and
- `annotations` (default: `false`) — pass `true` to retain comment annotations
`/*@__PURE__*/` or `/*#__PURE__*/`, otherwise they will be discarded even if
`comments` is set.
- `ascii_only` (default: `false`) — escape Unicode characters in strings and
regexps (affects directives with non-ascii characters becoming invalid)
- `beautify` (default `true`) -- whether to actually beautify the output.
- `beautify` (default: `true`) whether to actually beautify the output.
Passing `-b` will set this to true, but you might need to pass `-b` even
when you want to generate minified code, in order to specify additional
arguments, so you can use `-b beautify=false` to override it.
- `braces` (default `false`) -- always insert braces in `if`, `for`,
- `braces` (default: `false`) always insert braces in `if`, `for`,
`do`, `while` or `with` statements, even if their body is a single
statement.
- `comments` (default `false`) -- pass `true` or `"all"` to preserve all
- `comments` (default: `false`) pass `true` or `"all"` to preserve all
comments, `"some"` to preserve multi-line comments that contain `@cc_on`,
`@license`, or `@preserve` (case-insensitive), a regular expression string
(e.g. `/^!/`), or a function which returns `boolean`, e.g.
@@ -890,53 +909,53 @@ can pass additional arguments that control the code output:
}
```
- `galio` (default `false`) -- enable workarounds for ANT Galio bugs
- `galio` (default: `false`) enable workarounds for ANT Galio bugs
- `indent_level` (default `4`)
- `indent_level` (default: `4`)
- `indent_start` (default `0`) -- prefix all lines by that many spaces
- `indent_start` (default: `0`) prefix all lines by that many spaces
- `inline_script` (default `true`) -- escape HTML comments and the slash in
- `inline_script` (default: `true`) escape HTML comments and the slash in
occurrences of `</script>` in strings
- `keep_quoted_props` (default `false`) -- when turned on, prevents stripping
- `keep_quoted_props` (default: `false`) when turned on, prevents stripping
quotes from property names in object literals.
- `max_line_len` (default `false`) -- maximum line length (for uglified code)
- `max_line_len` (default: `false`) maximum line length (for uglified code)
- `preamble` (default `null`) -- when passed it must be a string and
- `preamble` (default: `null`) when passed it must be a string and
it will be prepended to the output literally. The source map will
adjust for this text. Can be used to insert a comment containing
licensing information, for example.
- `preserve_line` (default `false`) -- pass `true` to retain line numbering on
- `preserve_line` (default: `false`) pass `true` to retain line numbering on
a best effort basis.
- `quote_keys` (default `false`) -- pass `true` to quote all keys in literal
- `quote_keys` (default: `false`) pass `true` to quote all keys in literal
objects
- `quote_style` (default `0`) -- preferred quote style for strings (affects
- `quote_style` (default: `0`) preferred quote style for strings (affects
quoted property names and directives as well):
- `0` -- prefers double quotes, switches to single quotes when there are
- `0` prefers double quotes, switches to single quotes when there are
more double quotes in the string itself. `0` is best for gzip size.
- `1` -- always use single quotes
- `2` -- always use double quotes
- `3` -- always use the original quotes
- `1` always use single quotes
- `2` always use double quotes
- `3` always use the original quotes
- `semicolons` (default `true`) -- separate statements with semicolons. If
- `semicolons` (default: `true`) separate statements with semicolons. If
you pass `false` then whenever possible we will use a newline instead of a
semicolon, leading to more readable output of uglified code (size before
gzip could be smaller; size after gzip insignificantly larger).
- `shebang` (default `true`) -- preserve shebang `#!` in preamble (bash scripts)
- `shebang` (default: `true`) preserve shebang `#!` in preamble (bash scripts)
- `width` (default `80`) -- only takes effect when beautification is on, this
- `width` (default: `80`) only takes effect when beautification is on, this
specifies an (orientative) line width that the beautifier will try to
obey. It refers to the width of the line text (excluding indentation).
It doesn't work very well currently, but it does make the code generated
by UglifyJS more readable.
- `wrap_iife` (default `false`) -- pass `true` to wrap immediately invoked
- `wrap_iife` (default: `false`) pass `true` to wrap immediately invoked
function expressions. See
[#640](https://github.com/mishoo/UglifyJS/issues/640) for more details.
@@ -1137,18 +1156,14 @@ in total it's a bit more than just using UglifyJS's own parser.
It's not well known, but whitespace removal and symbol mangling accounts
for 95% of the size reduction in minified code for most JavaScript - not
elaborate code transforms. One can simply disable `compress` to speed up
Uglify builds by 3 to 4 times. In this fast `mangle`-only mode Uglify has
comparable minify speeds and gzip sizes to
[`butternut`](https://www.npmjs.com/package/butternut):
Uglify builds by 3 to 5 times.
| d3.js | minify size | gzip size | minify time (seconds) |
| --- | ---: | ---: | ---: |
| original | 451,131 | 108,733 | - |
| uglify-js@3.0.24 mangle=false, compress=false | 316,600 | 85,245 | 0.70 |
| uglify-js@3.0.24 mangle=true, compress=false | 220,216 | 72,730 | 1.13 |
| butternut@0.4.6 | 217,568 | 72,738 | 1.41 |
| uglify-js@3.0.24 mangle=true, compress=true | 212,511 | 71,560 | 3.36 |
| babili@0.1.4 | 210,713 | 72,140 | 12.64 |
| original | 511,371 | 119,932 | - |
| uglify-js@3.13.0 mangle=false, compress=false | 363,988 | 95,695 | 0.56 |
| uglify-js@3.13.0 mangle=true, compress=false | 253,305 | 81,281 | 0.99 |
| uglify-js@3.13.0 mangle=true, compress=true | 244,436 | 79,854 | 5.30 |
To enable fast minify mode from the CLI use:
```
@@ -1281,3 +1296,16 @@ To allow for better optimizations, the compiler makes various assumptions:
// SyntaxError: Identifier 'e' has already been declared
```
UglifyJS may modify the input which in turn may suppress those errors.
- Some versions of Chrome and Node.js will give incorrect results with the
following:
```javascript
console.log({
...{
set 42(v) {},
42: "PASS",
},
});
// Expected: { '42': 'PASS' }
// Actual: { '42': undefined }
```
UglifyJS may modify the input which in turn may suppress those errors.

View File

@@ -10,7 +10,7 @@ var info = require("../package.json");
var path = require("path");
var UglifyJS = require("../tools/node");
var skip_keys = [ "cname", "inlined", "parent_scope", "scope", "uses_eval", "uses_with" ];
var skip_keys = [ "cname", "fixed", "inlined", "parent_scope", "scope", "uses_eval", "uses_with" ];
var files = {};
var options = {};
var short_forms = {
@@ -95,6 +95,8 @@ function process_option(name, no_value) {
" -b, --beautify [options] Beautify output/specify output options.",
" -O, --output-opts <options> Output options (beautify disabled).",
" -o, --output <file> Output file (default STDOUT).",
" --annotations Process and preserve comment annotations.",
" --no-annotations Ignore and discard comment annotations.",
" --comments [filter] Preserve copyright comments in the output.",
" --config-file <file> Read minify() options from JSON file.",
" -d, --define <expr>[=value] Global definitions.",
@@ -113,6 +115,9 @@ function process_option(name, no_value) {
" --warn Print warning messages.",
" --webkit Support non-standard Safari/Webkit.",
" --wrap <name> Embed everything as a function with “exports” corresponding to “name” globally.",
"",
"(internal debug use only)",
" --in-situ Warning: replaces original source files with minified output.",
" --reduce-test Reduce a standalone test case (assumes cloned repository).",
].join("\n"));
}
@@ -139,6 +144,7 @@ function process_option(name, no_value) {
case "enclose":
options[name] = read_value();
break;
case "annotations":
case "ie8":
case "timings":
case "toplevel":
@@ -146,6 +152,9 @@ function process_option(name, no_value) {
case "webkit":
options[name] = true;
break;
case "no-annotations":
options.annotations = false;
break;
case "keep-fnames":
options.keep_fnames = true;
break;
@@ -194,6 +203,7 @@ function process_option(name, no_value) {
case "no-rename":
options.rename = false;
break;
case "in-situ":
case "reduce-test":
case "self":
break;
@@ -254,15 +264,30 @@ if (specified["self"]) {
if (!options.wrap) options.wrap = "UglifyJS";
paths = UglifyJS.FILES;
}
if (paths.length) {
if (specified["in-situ"]) {
if (output || specified["reduce-test"] || specified["self"]) fatal("incompatible options specified");
paths.forEach(function(name) {
print(name);
if (/^ast|spidermonkey$/.test(name)) fatal("invalid file name specified");
files = {};
files[convert_path(name)] = read_file(name);
output = name;
run();
});
} else if (paths.length) {
simple_glob(paths).forEach(function(name) {
files[convert_path(name)] = read_file(name);
});
run();
} else {
var timerId = process.stdin.isTTY && process.argv.length < 3 && setTimeout(function() {
print_error("Waiting for input... (use `--help` to print usage information)");
}, 1500);
var chunks = [];
process.stdin.setEncoding("utf8");
process.stdin.on("data", function(chunk) {
process.stdin.once("data", function() {
clearTimeout(timerId);
}).on("data", function(chunk) {
chunks.push(chunk);
}).on("end", function() {
files = { STDIN: chunks.join("") };
@@ -360,14 +385,14 @@ function run() {
}
print(JSON.stringify(result.ast, function(key, value) {
if (value) switch (key) {
case "thedef":
return symdef(value);
case "enclosed":
return value.length ? value.map(symdef) : undefined;
case "variables":
case "functions":
case "globals":
case "variables":
return value.size() ? value.map(symdef) : undefined;
case "thedef":
return symdef(value);
}
if (skip_key(key)) return;
if (value instanceof UglifyJS.AST_Token) return;

View File

@@ -55,9 +55,10 @@ function DEFNODE(type, props, methods, base) {
props.forEach(function(prop) {
code.push("this.", prop, "=props.", prop, ";");
});
code.push("}");
var proto = base && new base;
if (proto && proto.initialize || methods && methods.initialize) code.push("this.initialize();");
code.push("}}");
code.push("}");
var ctor = new Function(code.join(""))();
if (proto) {
ctor.prototype = proto;
@@ -139,7 +140,7 @@ var AST_Node = DEFNODE("Node", "start end", {
}, null);
(AST_Node.log_function = function(fn, verbose) {
if (!fn) {
if (typeof fn != "function") {
AST_Node.info = AST_Node.warn = noop;
return;
}
@@ -198,13 +199,17 @@ var AST_Debugger = DEFNODE("Debugger", null, {
$documentation: "Represents a debugger statement",
}, AST_Statement);
var AST_Directive = DEFNODE("Directive", "value quote", {
var AST_Directive = DEFNODE("Directive", "quote value", {
$documentation: "Represents a directive, like \"use strict\";",
$propdoc: {
quote: "[string?] the original quote character",
value: "[string] The value of this directive as a plain string (it's not an AST_String!)",
quote: "[string] the original quote character"
},
_validate: function() {
if (this.quote != null) {
if (typeof this.quote != "string") throw new Error("quote must be string");
if (!/^["']$/.test(this.quote)) throw new Error("invalid quote: " + this.quote);
}
if (typeof this.value != "string") throw new Error("value must be string");
},
}, AST_Statement);
@@ -213,6 +218,12 @@ var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
$documentation: "The empty statement (empty block or simply a semicolon)"
}, AST_Statement);
function is_statement(node) {
return node instanceof AST_Statement
&& !(node instanceof AST_ClassExpression)
&& !(node instanceof AST_LambdaExpression);
}
function validate_expression(value, prop, multiple, allow_spread, allow_hole) {
multiple = multiple ? "contain" : "be";
if (!(value instanceof AST_Node)) throw new Error(prop + " must " + multiple + " AST_Node");
@@ -220,9 +231,7 @@ function validate_expression(value, prop, multiple, allow_spread, allow_hole) {
if (value instanceof AST_Destructured) throw new Error(prop + " cannot " + multiple + " AST_Destructured");
if (value instanceof AST_Hole && !allow_hole) throw new Error(prop + " cannot " + multiple + " AST_Hole");
if (value instanceof AST_Spread && !allow_spread) throw new Error(prop + " cannot " + multiple + " AST_Spread");
if (value instanceof AST_Statement && !(value instanceof AST_LambdaExpression)) {
throw new Error(prop + " cannot " + multiple + " AST_Statement");
}
if (is_statement(value)) throw new Error(prop + " cannot " + multiple + " AST_Statement");
if (value instanceof AST_SymbolDeclaration) {
throw new Error(prop + " cannot " + multiple + " AST_SymbolDeclaration");
}
@@ -235,7 +244,7 @@ function must_be_expression(node, prop) {
var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
$documentation: "A statement consisting of an expression, i.e. a = 1 + 2",
$propdoc: {
body: "[AST_Node] an expression node (should not be instanceof AST_Statement)"
body: "[AST_Node] an expression node (should not be instanceof AST_Statement)",
},
walk: function(visitor) {
var node = this;
@@ -297,8 +306,7 @@ var AST_Block = DEFNODE("Block", "body", {
_validate: function() {
if (this.TYPE == "Block") throw new Error("should not instantiate AST_Block");
this.body.forEach(function(node) {
if (!(node instanceof AST_Statement)) throw new Error("body must be AST_Statement[]");
if (node instanceof AST_LambdaExpression) throw new Error("body cannot contain AST_LambdaExpression");
if (!is_statement(node)) throw new Error("body must contain AST_Statement");
});
},
}, AST_BlockScope);
@@ -314,8 +322,7 @@ var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
},
_validate: function() {
if (this.TYPE == "StatementWithBody") throw new Error("should not instantiate AST_StatementWithBody");
if (!(this.body instanceof AST_Statement)) throw new Error("body must be AST_Statement");
if (this.body instanceof AST_LambdaExpression) throw new Error("body cannot be AST_LambdaExpression");
if (!is_statement(this.body)) throw new Error("body must be AST_Statement");
},
}, AST_BlockScope);
@@ -412,8 +419,7 @@ var AST_For = DEFNODE("For", "init condition step", {
_validate: function() {
if (this.init != null) {
if (!(this.init instanceof AST_Node)) throw new Error("init must be AST_Node");
if (this.init instanceof AST_Statement
&& !(this.init instanceof AST_Definitions || this.init instanceof AST_LambdaExpression)) {
if (is_statement(this.init) && !(this.init instanceof AST_Definitions)) {
throw new Error("init cannot be AST_Statement");
}
}
@@ -423,7 +429,7 @@ var AST_For = DEFNODE("For", "init condition step", {
}, AST_IterationStatement);
var AST_ForEnumeration = DEFNODE("ForEnumeration", "init object", {
$documentation: "Base class for enumeration loops, i.e. `for ... in`, `for ... of` & `for await ... of`",
$documentation: "Base class for enumeration loops, i.e. `for ... in`, `for ... of` & `for await ... of`",
$propdoc: {
init: "[AST_Node] the assignment target during iteration",
object: "[AST_Node] the object to iterate over"
@@ -695,7 +701,7 @@ var AST_AsyncArrow = DEFNODE("AsyncArrow", "value", {
var AST_AsyncFunction = DEFNODE("AsyncFunction", "name", {
$documentation: "An asynchronous function expression",
$propdoc: {
name: "[AST_SymbolLambda?] the name of this function",
name: "[AST_SymbolLambda?] the name of this function, or null if not specified",
},
_validate: function() {
if (this.name != null) {
@@ -707,7 +713,7 @@ var AST_AsyncFunction = DEFNODE("AsyncFunction", "name", {
var AST_AsyncGeneratorFunction = DEFNODE("AsyncGeneratorFunction", "name", {
$documentation: "An asynchronous generator function expression",
$propdoc: {
name: "[AST_SymbolLambda?] the name of this function",
name: "[AST_SymbolLambda?] the name of this function, or null if not specified",
},
_validate: function() {
if (this.name != null) {
@@ -719,7 +725,7 @@ var AST_AsyncGeneratorFunction = DEFNODE("AsyncGeneratorFunction", "name", {
var AST_Function = DEFNODE("Function", "name", {
$documentation: "A function expression",
$propdoc: {
name: "[AST_SymbolLambda?] the name of this function",
name: "[AST_SymbolLambda?] the name of this function, or null if not specified",
},
_validate: function() {
if (this.name != null) {
@@ -731,7 +737,7 @@ var AST_Function = DEFNODE("Function", "name", {
var AST_GeneratorFunction = DEFNODE("GeneratorFunction", "name", {
$documentation: "A generator function expression",
$propdoc: {
name: "[AST_SymbolLambda?] the name of this function",
name: "[AST_SymbolLambda?] the name of this function, or null if not specified",
},
_validate: function() {
if (this.name != null) {
@@ -768,6 +774,112 @@ var AST_GeneratorDefun = DEFNODE("GeneratorDefun", null, {
$documentation: "A generator function definition",
}, AST_LambdaDefinition);
/* -----[ classes ]----- */
var AST_Class = DEFNODE("Class", "extends name properties", {
$documentation: "Base class for class literals",
$propdoc: {
extends: "[AST_Node?] the super class, or null if not specified",
properties: "[AST_ClassProperty*] array of class properties",
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
if (node.name) node.name.walk(visitor);
if (node.extends) node.extends.walk(visitor);
node.properties.forEach(function(prop) {
prop.walk(visitor);
});
});
},
_validate: function() {
if (this.TYPE == "Class") throw new Error("should not instantiate AST_Class");
if (this.extends != null) must_be_expression(this, "extends");
this.properties.forEach(function(node) {
if (!(node instanceof AST_ClassProperty)) throw new Error("properties must contain AST_ClassProperty");
});
},
}, AST_BlockScope);
var AST_DefClass = DEFNODE("DefClass", null, {
$documentation: "A class definition",
$propdoc: {
name: "[AST_SymbolDefClass] the name of this class",
},
_validate: function() {
if (!(this.name instanceof AST_SymbolDefClass)) throw new Error("name must be AST_SymbolDefClass");
},
}, AST_Class);
var AST_ClassExpression = DEFNODE("ClassExpression", null, {
$documentation: "A class expression",
$propdoc: {
name: "[AST_SymbolClass?] the name of this class, or null if not specified",
},
_validate: function() {
if (this.name != null) {
if (!(this.name instanceof AST_SymbolClass)) throw new Error("name must be AST_SymbolClass");
}
},
}, AST_Class);
var AST_ClassProperty = DEFNODE("ClassProperty", "key private static value", {
$documentation: "Base class for `class` properties",
$propdoc: {
key: "[string|AST_Node] property name (AST_Node for computed property)",
private: "[boolean] whether this is a private property",
static: "[boolean] whether this is a static property",
value: "[AST_Node?] property value (AST_Accessor for getters/setters, AST_LambdaExpression for methods, null if not specified for fields)",
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
if (node.key instanceof AST_Node) node.key.walk(visitor);
if (node.value) node.value.walk(visitor);
});
},
_validate: function() {
if (this.TYPE == "ClassProperty") throw new Error("should not instantiate AST_ClassProperty");
if (typeof this.key != "string") {
if (!(this.key instanceof AST_Node)) throw new Error("key must be string or AST_Node");
must_be_expression(this, "key");
}
if(this.value != null) {
if (!(this.value instanceof AST_Node)) throw new Error("value must be AST_Node");
}
},
});
var AST_ClassField = DEFNODE("ClassField", null, {
$documentation: "A `class` field",
_validate: function() {
if(this.value != null) must_be_expression(this, "value");
},
}, AST_ClassProperty);
var AST_ClassGetter = DEFNODE("ClassGetter", null, {
$documentation: "A `class` getter",
_validate: function() {
if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor");
},
}, AST_ClassProperty);
var AST_ClassSetter = DEFNODE("ClassSetter", null, {
$documentation: "A `class` setter",
_validate: function() {
if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor");
},
}, AST_ClassProperty);
var AST_ClassMethod = DEFNODE("ClassMethod", null, {
$documentation: "A `class` method",
_validate: function() {
if (!(this.value instanceof AST_LambdaExpression)) throw new Error("value must be AST_LambdaExpression");
if (is_arrow(this.value)) throw new Error("value cannot be AST_Arrow or AST_AsyncArrow");
if (this.value.name != null) throw new Error("name of class method's lambda must be null");
},
}, AST_ClassProperty);
/* -----[ JUMPS ]----- */
var AST_Jump = DEFNODE("Jump", null, {
@@ -853,8 +965,7 @@ var AST_If = DEFNODE("If", "condition alternative", {
_validate: function() {
must_be_expression(this, "condition");
if (this.alternative != null) {
if (!(this.alternative instanceof AST_Statement)) throw new Error("alternative must be AST_Statement");
if (this.alternative instanceof AST_LambdaExpression) throw new error("alternative cannot be AST_LambdaExpression");
if (!is_statement(this.alternative)) throw new Error("alternative must be AST_Statement");
}
},
}, AST_StatementWithBody);
@@ -1035,6 +1146,129 @@ var AST_VarDef = DEFNODE("VarDef", "name value", {
/* -----[ OTHER ]----- */
var AST_ExportDeclaration = DEFNODE("ExportDeclaration", "body", {
$documentation: "An `export` statement",
$propdoc: {
body: "[AST_DefClass|AST_Definitions|AST_LambdaDefinition] the statement to export",
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.body.walk(visitor);
});
},
_validate: function() {
if (!(this.body instanceof AST_DefClass
|| this.body instanceof AST_Definitions
|| this.body instanceof AST_LambdaDefinition)) {
throw new Error("body must be AST_DefClass, AST_Definitions or AST_LambdaDefinition");
}
},
}, AST_Statement);
var AST_ExportDefault = DEFNODE("ExportDefault", "body", {
$documentation: "An `export default` statement",
$propdoc: {
body: "[AST_Node] the default export",
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.body.walk(visitor);
});
},
_validate: function() {
if (!(this.body instanceof AST_DefClass || this.body instanceof AST_LambdaDefinition)) {
must_be_expression(this, "body");
}
},
}, AST_Statement);
var AST_ExportForeign = DEFNODE("ExportForeign", "aliases keys path quote", {
$documentation: "An `export ... from '...'` statement",
$propdoc: {
aliases: "[string*] array of aliases to export",
keys: "[string*] array of keys to import",
path: "[string] the path to import module",
quote: "[string?] the original quote character",
},
_validate: function() {
if (this.aliases.length != this.keys.length) {
throw new Error("aliases:key length mismatch: " + this.aliases.length + " != " + this.keys.length);
}
this.aliases.forEach(function(name) {
if (typeof name != "string") throw new Error("aliases must contain string");
});
this.keys.forEach(function(name) {
if (typeof name != "string") throw new Error("keys must contain string");
});
if (typeof this.path != "string") throw new Error("path must be string");
if (this.quote != null) {
if (typeof this.quote != "string") throw new Error("quote must be string");
if (!/^["']$/.test(this.quote)) throw new Error("invalid quote: " + this.quote);
}
},
}, AST_Statement);
var AST_ExportReferences = DEFNODE("ExportReferences", "properties", {
$documentation: "An `export { ... }` statement",
$propdoc: {
properties: "[AST_SymbolExport*] array of aliases to export",
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.properties.forEach(function(prop) {
prop.walk(visitor);
});
});
},
_validate: function() {
this.properties.forEach(function(prop) {
if (!(prop instanceof AST_SymbolExport)) throw new Error("properties must contain AST_SymbolExport");
});
},
}, AST_Statement);
var AST_Import = DEFNODE("Import", "all default path properties quote", {
$documentation: "An `import` statement",
$propdoc: {
all: "[AST_SymbolImport?] the imported namespace, or null if not specified",
default: "[AST_SymbolImport?] the alias for default `export`, or null if not specified",
path: "[string] the path to import module",
properties: "[(AST_SymbolImport*)?] array of aliases, or null if not specified",
quote: "[string?] the original quote character",
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
if (node.all) node.all.walk(visitor);
if (node.default) node.default.walk(visitor);
if (node.properties) node.properties.forEach(function(prop) {
prop.walk(visitor);
});
});
},
_validate: function() {
if (this.all != null) {
if (!(this.all instanceof AST_SymbolImport)) throw new Error("all must be AST_SymbolImport");
if (this.properties != null) throw new Error("cannot import both * and {} in the same statement");
}
if (this.default != null) {
if (!(this.default instanceof AST_SymbolImport)) throw new Error("default must be AST_SymbolImport");
if (this.default.key !== "") throw new Error("invalid default key: " + this.default.key);
}
if (typeof this.path != "string") throw new Error("path must be string");
if (this.properties != null) this.properties.forEach(function(node) {
if (!(node instanceof AST_SymbolImport)) throw new Error("properties must contain AST_SymbolImport");
});
if (this.quote != null) {
if (typeof this.quote != "string") throw new Error("quote must be string");
if (!/^["']$/.test(this.quote)) throw new Error("invalid quote: " + this.quote);
}
},
}, AST_Statement);
var AST_DefaultValue = DEFNODE("DefaultValue", "name value", {
$documentation: "A default value declaration",
$propdoc: {
@@ -1447,6 +1681,15 @@ var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", null, {
},
}, AST_ObjectProperty);
var AST_ObjectMethod = DEFNODE("ObjectMethod", null, {
$documentation: "A key(){} object property",
_validate: function() {
if (!(this.value instanceof AST_LambdaExpression)) throw new Error("value must be AST_LambdaExpression");
if (is_arrow(this.value)) throw new Error("value cannot be AST_Arrow or AST_AsyncArrow");
if (this.value.name != null) throw new Error("name of class method's lambda must be null");
},
}, AST_ObjectKeyVal);
var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
$documentation: "An object setter property",
_validate: function() {
@@ -1482,6 +1725,16 @@ var AST_SymbolConst = DEFNODE("SymbolConst", null, {
$documentation: "Symbol defining a constant",
}, AST_SymbolDeclaration);
var AST_SymbolImport = DEFNODE("SymbolImport", "key", {
$documentation: "Symbol defined by an `import` statement",
$propdoc: {
key: "[string] the original `export` name",
},
_validate: function() {
if (typeof this.key != "string") throw new Error("key must be string");
},
}, AST_SymbolConst);
var AST_SymbolLet = DEFNODE("SymbolLet", null, {
$documentation: "Symbol defining a lexical-scoped variable",
}, AST_SymbolDeclaration);
@@ -1502,6 +1755,14 @@ var AST_SymbolLambda = DEFNODE("SymbolLambda", null, {
$documentation: "Symbol naming a function expression",
}, AST_SymbolDeclaration);
var AST_SymbolDefClass = DEFNODE("SymbolDefClass", null, {
$documentation: "Symbol defining a class",
}, AST_SymbolLet);
var AST_SymbolClass = DEFNODE("SymbolClass", null, {
$documentation: "Symbol naming a class expression",
}, AST_SymbolLet);
var AST_SymbolCatch = DEFNODE("SymbolCatch", null, {
$documentation: "Symbol naming the exception in catch",
}, AST_SymbolDeclaration);
@@ -1521,16 +1782,50 @@ var AST_SymbolRef = DEFNODE("SymbolRef", "fixed in_arg redef", {
$documentation: "Reference to some symbol (not definition/declaration)",
}, AST_Symbol);
var AST_SymbolExport = DEFNODE("SymbolExport", "alias", {
$documentation: "Reference in an `export` statement",
$propdoc: {
alias: "[string] the `export` alias",
},
_validate: function() {
if (typeof this.alias != "string") throw new Error("alias must be string");
},
}, AST_SymbolRef);
var AST_LabelRef = DEFNODE("LabelRef", null, {
$documentation: "Reference to a label symbol",
}, AST_Symbol);
var AST_ObjectIdentity = DEFNODE("ObjectIdentity", null, {
$documentation: "Base class for `super` & `this`",
_validate: function() {
if (this.TYPE == "ObjectIdentity") throw new Error("should not instantiate AST_ObjectIdentity");
},
}, AST_Symbol);
var AST_Super = DEFNODE("Super", null, {
$documentation: "The `super` symbol",
_validate: function() {
if (this.name !== "super") throw new Error('name must be "super"');
},
}, AST_ObjectIdentity);
var AST_This = DEFNODE("This", null, {
$documentation: "The `this` symbol",
_validate: function() {
if (this.name !== "this") throw new Error('name must be "this"');
if (this.TYPE == "This" && this.name !== "this") throw new Error('name must be "this"');
},
}, AST_Symbol);
}, AST_ObjectIdentity);
var AST_NewTarget = DEFNODE("NewTarget", null, {
$documentation: "The `new.target` symbol",
initialize: function() {
this.name = "new.target";
},
_validate: function() {
if (this.name !== "new.target") throw new Error('name must be "new.target": ' + this.name);
},
}, AST_This);
var AST_Template = DEFNODE("Template", "expressions strings tag", {
$documentation: "A template literal, i.e. tag`str1${expr1}...strN${exprN}strN+1`",
@@ -1567,13 +1862,17 @@ var AST_Constant = DEFNODE("Constant", null, {
},
});
var AST_String = DEFNODE("String", "value quote", {
var AST_String = DEFNODE("String", "quote value", {
$documentation: "A string literal",
$propdoc: {
quote: "[string?] the original quote character",
value: "[string] the contents of this string",
quote: "[string] the original quote character"
},
_validate: function() {
if (this.quote != null) {
if (typeof this.quote != "string") throw new Error("quote must be string");
if (!/^["']$/.test(this.quote)) throw new Error("invalid quote: " + this.quote);
}
if (typeof this.value != "string") throw new Error("value must be string");
},
}, AST_Constant);
@@ -1686,7 +1985,8 @@ TreeWalker.prototype = {
this.stack.push(node);
},
pop: function() {
if (this.stack.pop() instanceof AST_Lambda) {
var node = this.stack.pop();
if (node instanceof AST_Lambda) {
this.directives = Object.getPrototypeOf(this.directives);
}
},

File diff suppressed because it is too large Load Diff

View File

@@ -47,14 +47,12 @@ function parse_source_map(content) {
}
function set_shorthand(name, options, keys) {
if (options[name]) {
keys.forEach(function(key) {
if (options[key]) {
if (typeof options[key] != "object") options[key] = {};
if (!(name in options[key])) options[key][name] = options[name];
}
});
}
keys.forEach(function(key) {
if (options[key]) {
if (typeof options[key] != "object") options[key] = {};
if (!(name in options[key])) options[key][name] = options[name];
}
});
}
function init_cache(cache) {
@@ -75,6 +73,7 @@ function to_json(cache) {
function minify(files, options) {
try {
options = defaults(options, {
annotations: undefined,
compress: {},
enclose: false,
ie8: false,
@@ -94,17 +93,14 @@ function minify(files, options) {
wrap: false,
}, true);
if (options.validate) AST_Node.enable_validation();
var timings = options.timings && {
start: Date.now()
};
if (options.rename === undefined) {
options.rename = options.compress && options.mangle;
}
set_shorthand("ie8", options, [ "compress", "mangle", "output" ]);
set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
set_shorthand("toplevel", options, [ "compress", "mangle" ]);
set_shorthand("v8", options, [ "mangle", "output" ]);
set_shorthand("webkit", options, [ "mangle", "output" ]);
var timings = options.timings && { start: Date.now() };
if (options.rename === undefined) options.rename = options.compress && options.mangle;
if (options.annotations !== undefined) set_shorthand("annotations", options, [ "compress", "output" ]);
if (options.ie8) set_shorthand("ie8", options, [ "compress", "mangle", "output" ]);
if (options.keep_fnames) set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
if (options.toplevel) set_shorthand("toplevel", options, [ "compress", "mangle" ]);
if (options.v8) set_shorthand("v8", options, [ "mangle", "output" ]);
if (options.webkit) set_shorthand("webkit", options, [ "mangle", "output" ]);
var quoted_props;
if (options.mangle) {
options.mangle = defaults(options.mangle, {

View File

@@ -49,9 +49,8 @@ function is_some_comments(comment) {
}
function OutputStream(options) {
var readonly = !options;
options = defaults(options, {
annotations : false,
ascii_only : false,
beautify : false,
braces : false,
@@ -102,12 +101,35 @@ function OutputStream(options) {
}
}
var indentation = options.indent_start;
var current_col = 0;
var current_line = 1;
var current_pos = 0;
var OUTPUT = "";
var indentation = options.indent_start;
var last;
var line_end = 0;
var line_fixed = true;
var mappings = options.source_map && [];
var mapping_name;
var mapping_token;
var might_need_space;
var might_need_semicolon;
var need_newline_indented = false;
var need_space = false;
var newline_insert = -1;
var stack;
var OUTPUT;
function reset() {
last = "";
might_need_space = false;
might_need_semicolon = false;
stack = [];
var str = OUTPUT;
OUTPUT = "";
return str;
}
reset();
var to_utf8 = options.ascii_only ? function(str, identifier) {
if (identifier) str = str.replace(/[\ud800-\udbff][\udc00-\udfff]/g, function(ch) {
return "\\u{" + (ch.charCodeAt(0) - 0xd7c0 << 10 | ch.charCodeAt(1) - 0xdc00).toString(16) + "}";
@@ -140,6 +162,25 @@ function OutputStream(options) {
return j == 0 ? str : s + str.slice(j);
};
function quote_single(str) {
return "'" + str.replace(/\x27/g, "\\'") + "'";
}
function quote_double(str) {
return '"' + str.replace(/\x22/g, '\\"') + '"';
}
var quote_string = [
null,
quote_single,
quote_double,
function(str, quote) {
return quote == "'" ? quote_single(str) : quote_double(str);
},
][options.quote_style] || function(str, quote, dq, sq) {
return dq > sq ? quote_single(str) : quote_double(str);
};
function make_string(str, quote) {
var dq = 0, sq = 0;
str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g, function(s, i) {
@@ -161,54 +202,11 @@ function OutputStream(options) {
}
return s;
});
function quote_single() {
return "'" + str.replace(/\x27/g, "\\'") + "'";
}
function quote_double() {
return '"' + str.replace(/\x22/g, '\\"') + '"';
}
str = to_utf8(str);
switch (options.quote_style) {
case 1:
return quote_single();
case 2:
return quote_double();
case 3:
return quote == "'" ? quote_single() : quote_double();
default:
return dq > sq ? quote_single() : quote_double();
}
}
function encode_string(str, quote) {
var ret = make_string(str, quote);
if (options.inline_script) {
ret = ret.replace(/<\x2f(script)([>\/\t\n\f\r ])/gi, "<\\/$1$2");
ret = ret.replace(/\x3c!--/g, "\\x3c!--");
ret = ret.replace(/--\x3e/g, "--\\x3e");
}
return ret;
}
function make_name(name) {
name = name.toString();
name = to_utf8(name, true);
return name;
return quote_string(to_utf8(str), quote, dq, sq);
}
/* -----[ beautification/minification ]----- */
var has_parens = false;
var line_end = 0;
var line_fixed = true;
var might_need_space = false;
var might_need_semicolon = false;
var need_newline_indented = false;
var need_space = false;
var newline_insert = -1;
var last = "";
var mapping_token, mapping_name, mappings = options.source_map && [];
var adjust_mappings = mappings ? function(line, col) {
mappings.forEach(function(mapping) {
mapping.line += line;
@@ -256,8 +254,14 @@ function OutputStream(options) {
var requireSemicolonChars = makePredicate("( [ + * / - , .");
function print(str) {
str = String(str);
var print = options.beautify
|| options.comments
|| options.max_line_len
|| options.preserve_line
|| options.shebang
|| !options.semicolons
|| options.source_map
|| options.width ? function(str) {
var ch = str.charAt(0);
if (need_newline_indented && ch) {
need_newline_indented = false;
@@ -327,7 +331,6 @@ function OutputStream(options) {
}
OUTPUT += str;
has_parens = str.slice(-1) == "(";
current_pos += str.length;
var a = str.split(/\r?\n/), n = a.length - 1;
current_line += n;
@@ -337,7 +340,30 @@ function OutputStream(options) {
current_col = a[n].length;
}
last = str;
}
} : function(str) {
var ch = str.charAt(0);
var prev = last.slice(-1);
if (might_need_semicolon) {
might_need_semicolon = false;
if (prev == ":" && ch == "}" || (!ch || ";}".indexOf(ch) < 0) && prev != ";") {
OUTPUT += ";";
might_need_space = false;
}
}
if (might_need_space) {
if (is_identifier_char(prev) && (is_identifier_char(ch) || ch == "\\")
|| (ch == "/" && ch == prev)
|| ((ch == "+" || ch == "-") && ch == last)
|| str == "--" && last == "!"
|| str == "in" && prev == "/"
|| last == "--" && ch == ">") {
OUTPUT += " ";
}
if (prev != "<" || str != "!") might_need_space = false;
}
OUTPUT += str;
last = str;
};
var space = options.beautify ? function() {
print(" ");
@@ -346,17 +372,16 @@ function OutputStream(options) {
};
var indent = options.beautify ? function(half) {
if (need_newline_indented) print("\n");
print(repeat_string(" ", half ? indentation - (options.indent_level >> 1) : indentation));
} : noop;
var with_indent = options.beautify ? function(col, cont) {
if (col === true) col = next_indent();
var with_indent = options.beautify ? function(cont) {
var save_indentation = indentation;
indentation = col;
var ret = cont();
indentation += options.indent_level;
cont();
indentation = save_indentation;
return ret;
} : function(col, cont) { return cont() };
} : function(cont) { cont() };
var may_add_newline = options.max_line_len || options.preserve_line ? function() {
fix_line();
@@ -385,41 +410,28 @@ function OutputStream(options) {
print(";");
}
function next_indent() {
return indentation + options.indent_level;
}
function with_block(cont) {
var ret;
print("{");
newline();
with_indent(next_indent(), function() {
ret = cont();
});
with_indent(cont);
indent();
print("}");
return ret;
}
function with_parens(cont) {
print("(");
may_add_newline();
//XXX: still nice to have that for argument lists
//var ret = with_indent(current_col, cont);
var ret = cont();
cont();
may_add_newline();
print(")");
return ret;
}
function with_square(cont) {
print("[");
may_add_newline();
//var ret = with_indent(current_col, cont);
var ret = cont();
cont();
may_add_newline();
print("]");
return ret;
}
function comma() {
@@ -449,6 +461,27 @@ function OutputStream(options) {
return /^ *$/.test(OUTPUT.slice(index + 1));
}
function pad_comment(token, force) {
if (need_newline_indented) return;
if (token.nlb && (force || !has_nlb())) {
need_newline_indented = true;
} else if (force) {
need_space = true;
}
}
function print_comment(comment) {
var value = comment.value.replace(/[@#]__PURE__/g, " ");
if (/^\s*$/.test(value) && !/^\s*$/.test(comment.value)) return false;
if (/comment[134]/.test(comment.type)) {
print("//" + value);
need_newline_indented = true;
} else if (comment.type == "comment2") {
print("/*" + value + "*/");
}
return true;
}
function prepend_comments(node) {
var self = this;
var scan = node instanceof AST_Exit && node.value;
@@ -488,35 +521,12 @@ function OutputStream(options) {
}
comments = comments.filter(comment_filter, node);
if (comments.length == 0) return;
var last_nlb = has_nlb();
comments.forEach(function(c, i) {
if (!last_nlb) {
if (c.nlb) {
print("\n");
indent();
last_nlb = true;
} else if (i > 0) {
space();
}
}
if (/comment[134]/.test(c.type)) {
print("//" + c.value.replace(/[@#]__PURE__/g, " ") + "\n");
indent();
last_nlb = true;
} else if (c.type == "comment2") {
print("/*" + c.value.replace(/[@#]__PURE__/g, " ") + "*/");
last_nlb = false;
}
var printed = false;
comments.forEach(function(comment, index) {
pad_comment(comment, index);
if (print_comment(comment)) printed = true;
});
if (!last_nlb) {
if (node.start.nlb) {
print("\n");
indent();
} else {
space();
}
}
if (printed) pad_comment(node.start, true);
function dump(node) {
var token = node.start;
@@ -546,38 +556,21 @@ function OutputStream(options) {
}))) return;
comments._dumped = self;
var insert = OUTPUT.length;
comments.filter(comment_filter, node).forEach(function(c, i) {
need_space = false;
if (need_newline_indented) {
print("\n");
indent();
need_newline_indented = false;
} else if (c.nlb && (i > 0 || !has_nlb())) {
print("\n");
indent();
} else if (i > 0 || !tail) {
space();
}
if (/comment[134]/.test(c.type)) {
print("//" + c.value.replace(/[@#]__PURE__/g, " "));
need_newline_indented = true;
} else if (c.type == "comment2") {
print("/*" + c.value.replace(/[@#]__PURE__/g, " ") + "*/");
need_space = true;
}
comments.filter(comment_filter, node).forEach(function(comment, index) {
pad_comment(comment, index || !tail);
print_comment(comment);
});
if (OUTPUT.length > insert) newline_insert = insert;
}
var stack = [];
return {
get : get,
toString : get,
reset : reset,
indent : indent,
should_break : readonly ? noop : function() {
return options.width && current_col - indentation >= options.width;
},
has_parens : function() { return has_parens },
should_break : options.width ? function() {
return current_col - indentation >= options.width;
} : return_false,
has_parens : function() { return last.slice(-1) == "(" },
newline : newline,
print : print,
space : space,
@@ -587,20 +580,21 @@ function OutputStream(options) {
semicolon : semicolon,
force_semicolon : force_semicolon,
to_utf8 : to_utf8,
print_name : function(name) { print(make_name(name)) },
print_string : function(str, quote) { print(encode_string(str, quote)) },
next_indent : next_indent,
print_name : function(name) { print(to_utf8(name.toString(), true)) },
print_string : options.inline_script ? function(str, quote) {
str = make_string(str, quote).replace(/<\x2f(script)([>\/\t\n\f\r ])/gi, "<\\/$1$2");
print(str.replace(/\x3c!--/g, "\\x3c!--").replace(/--\x3e/g, "--\\x3e"));
} : function(str, quote) {
print(make_string(str, quote));
},
with_indent : with_indent,
with_block : with_block,
with_parens : with_parens,
with_square : with_square,
add_mapping : add_mapping,
option : function(opt) { return options[opt] },
prepend_comments: readonly ? noop : prepend_comments,
append_comments : readonly || comment_filter === return_false ? noop : append_comments,
line : function() { return current_line },
col : function() { return current_col },
pos : function() { return current_pos },
prepend_comments: options.comments || options.shebang ? prepend_comments : noop,
append_comments : options.comments ? append_comments : noop,
push_node : function(node) { stack.push(node) },
pop_node : options.preserve_line ? function() {
var node = stack.pop();
@@ -645,10 +639,19 @@ function OutputStream(options) {
stream.append_comments(self);
}
});
var readonly = OutputStream({
inline_script: false,
shebang: false,
width: false,
});
AST_Node.DEFMETHOD("print_to_string", function(options) {
var s = OutputStream(options);
this.print(s);
return s.get();
if (options) {
var stream = OutputStream(options);
this.print(stream);
return stream.get();
}
this.print(readonly);
return readonly.reset();
});
/* -----[ PARENTHESES ]----- */
@@ -662,18 +665,21 @@ function OutputStream(options) {
// a function expression needs parens around it when it's provably
// the first token to appear in a statement.
function needs_parens_function(output) {
if (!output.has_parens() && first_in_statement(output)) return true;
if (output.option("webkit")) {
var p = output.parent();
if (p instanceof AST_PropAccess && p.expression === this) return true;
}
if (output.option("wrap_iife")) {
var p = output.parent();
if (p instanceof AST_Call && p.expression === this) return true;
var p = output.parent();
if (!output.has_parens() && first_in_statement(output, false, true)) {
// export default function() {}
// export default (function foo() {});
// export default (function() {})(foo);
// export default (function() {})`foo`;
// export default (function() {}) ? foo : bar;
return this.name || !(p instanceof AST_ExportDefault);
}
if (output.option("webkit") && p instanceof AST_PropAccess && p.expression === this) return true;
if (output.option("wrap_iife") && p instanceof AST_Call && p.expression === this) return true;
}
PARENS(AST_AsyncFunction, needs_parens_function);
PARENS(AST_AsyncGeneratorFunction, needs_parens_function);
PARENS(AST_ClassExpression, needs_parens_function);
PARENS(AST_Function, needs_parens_function);
PARENS(AST_GeneratorFunction, needs_parens_function);
@@ -691,6 +697,9 @@ function OutputStream(options) {
// (await x)(y)
// new (await x)
if (p instanceof AST_Call) return p.expression === this;
// class extends (x++) {}
// class x extends (typeof y) {}
if (p instanceof AST_Class) return true;
// (x++)[y]
// (typeof x).y
if (p instanceof AST_PropAccess) return p.expression === this;
@@ -710,6 +719,12 @@ function OutputStream(options) {
|| p instanceof AST_Binary
// new (foo, bar) or foo(1, (2, 3), 4)
|| p instanceof AST_Call
// class extends (foo, bar) {}
// class foo extends (bar, baz) {}
|| p instanceof AST_Class
// class { foo = (bar, baz) }
// class { [(foo, bar)]() {} }
|| p instanceof AST_ClassProperty
// (false, true) ? (a = 10, b = 20) : (c = 30)
// ---> 20 (side effect, set a := 10 and b := 20)
|| p instanceof AST_Conditional
@@ -718,6 +733,8 @@ function OutputStream(options) {
// { [(1, 2)]: foo } = bar
// { 1: (2, foo) } = bar
|| p instanceof AST_DestructuredKeyVal
// export default (foo, bar)
|| p instanceof AST_ExportDefault
// for (foo of (bar, baz));
|| p instanceof AST_ForOf
// { [(1, 2)]: 3 }[2] ---> 3
@@ -727,6 +744,8 @@ function OutputStream(options) {
|| p instanceof AST_PropAccess && p.expression === this
// ...(foo, bar, baz)
|| p instanceof AST_Spread
// (foo, bar)`baz`
|| p instanceof AST_Template && p.tag === this
// !(foo, bar, baz)
|| p instanceof AST_Unary
// var a = (1, 2), b = a + a; ---> b == 4
@@ -746,10 +765,15 @@ function OutputStream(options) {
if (p instanceof AST_Binary) {
var po = p.operator, pp = PRECEDENCE[po];
var so = this.operator, sp = PRECEDENCE[so];
return pp > sp || (pp == sp && this === p[po == "**" ? "left" : "right"]);
return pp > sp
|| po == "??" && (so == "&&" || so == "||")
|| (pp == sp && this === p[po == "**" ? "left" : "right"]);
}
// (foo && bar)()
if (p instanceof AST_Call) return p.expression === this;
// class extends (foo && bar) {}
// class foo extends (bar || null) {}
if (p instanceof AST_Class) return true;
// (foo && bar)["prop"], (foo && bar).prop
if (p instanceof AST_PropAccess) return p.expression === this;
// typeof (foo && bar)
@@ -813,6 +837,9 @@ function OutputStream(options) {
if (p instanceof AST_Binary) return !(p instanceof AST_Assign);
// (a = func)() —or— new (a = Object)()
if (p instanceof AST_Call) return p.expression === self;
// class extends (a = foo) {}
// class foo extends (bar ? baz : moo) {}
if (p instanceof AST_Class) return true;
// (a = foo) ? bar : baz
if (p instanceof AST_Conditional) return p.condition === self;
// (a = foo)["prop"] —or— (a = foo).prop
@@ -892,10 +919,6 @@ function OutputStream(options) {
use_asm = was_asm;
}
DEFPRINT(AST_Statement, function(output) {
this.body.print(output);
output.semicolon();
});
DEFPRINT(AST_Toplevel, function(output) {
display_body(this.body, true, output, true);
output.print("");
@@ -911,7 +934,7 @@ function OutputStream(options) {
});
function print_braced_empty(self, output) {
output.print("{");
output.with_indent(output.next_indent(), function() {
output.with_indent(function() {
output.append_comments(self, true);
});
output.print("}");
@@ -1011,6 +1034,94 @@ function OutputStream(options) {
output.space();
force_statement(self.body, output);
});
DEFPRINT(AST_ExportDeclaration, function(output) {
output.print("export");
output.space();
this.body.print(output);
});
DEFPRINT(AST_ExportDefault, function(output) {
output.print("export");
output.space();
output.print("default");
output.space();
var body = this.body;
body.print(output);
if (body instanceof AST_ClassExpression) {
if (!body.name) return;
}
if (body instanceof AST_DefClass) return;
if (body instanceof AST_LambdaDefinition) return;
if (body instanceof AST_LambdaExpression) {
if (!body.name && !is_arrow(body)) return;
}
output.semicolon();
});
DEFPRINT(AST_ExportForeign, function(output) {
var self = this;
output.print("export");
output.space();
var len = self.keys.length;
if (len == 0) {
print_braced_empty(self, output);
} else if (self.keys[0] == "*") {
print_entry(0);
} else output.with_block(function() {
output.indent();
print_entry(0);
for (var i = 1; i < len; i++) {
output.print(",");
output.newline();
output.indent();
print_entry(i);
}
output.newline();
});
output.space();
output.print("from");
output.space();
output.print_string(self.path, self.quote);
output.semicolon();
function print_entry(index) {
var alias = self.aliases[index];
var key = self.keys[index];
output.print_name(key);
if (alias != key) {
output.space();
output.print("as");
output.space();
output.print_name(alias);
}
}
});
DEFPRINT(AST_ExportReferences, function(output) {
var self = this;
output.print("export");
output.space();
print_properties(self, output);
output.semicolon();
});
DEFPRINT(AST_Import, function(output) {
var self = this;
output.print("import");
output.space();
if (self.default) self.default.print(output);
if (self.all) {
if (self.default) output.comma();
self.all.print(output);
}
if (self.properties) {
if (self.default) output.comma();
print_properties(self, output);
}
if (self.all || self.default || self.properties) {
output.space();
output.print("from");
output.space();
}
output.print_string(self.path, self.quote);
output.semicolon();
});
/* -----[ functions ]----- */
function print_funargs(self, output) {
@@ -1085,6 +1196,59 @@ function OutputStream(options) {
DEFPRINT(AST_GeneratorDefun, print_generator);
DEFPRINT(AST_GeneratorFunction, print_generator);
/* -----[ classes ]----- */
DEFPRINT(AST_Class, function(output) {
var self = this;
output.print("class");
if (self.name) {
output.space();
self.name.print(output);
}
if (self.extends) {
output.space();
output.print("extends");
output.space();
self.extends.print(output);
}
output.space();
print_properties(self, output, true);
});
DEFPRINT(AST_ClassField, function(output) {
var self = this;
if (self.static) {
output.print("static");
output.space();
}
print_property_key(self, output);
if (self.value) {
output.space();
output.print("=");
output.space();
self.value.print(output);
}
output.semicolon();
});
DEFPRINT(AST_ClassGetter, print_accessor("get"));
DEFPRINT(AST_ClassSetter, print_accessor("set"));
function print_method(self, output) {
var fn = self.value;
if (is_async(fn)) {
output.print("async");
output.space();
}
if (is_generator(fn)) output.print("*");
print_property_key(self, output);
print_lambda(self.value, output);
}
DEFPRINT(AST_ClassMethod, function(output) {
var self = this;
if (self.static) {
output.print("static");
output.space();
}
print_method(self, output);
});
/* -----[ jumps ]----- */
function print_jump(kind, prop) {
return function(output) {
@@ -1277,6 +1441,17 @@ function OutputStream(options) {
});
/* -----[ other expressions ]----- */
function print_annotation(self, output) {
if (!output.option("annotations")) return;
if (!self.pure) return;
var level = 0, parent = self, node;
do {
node = parent;
parent = output.parent(level++);
if (parent instanceof AST_Call && parent.expression === node) return;
} while (parent instanceof AST_PropAccess && parent.expression === node);
output.print("/*" + self.pure + "*/");
}
function print_call_args(self, output) {
if (self.expression instanceof AST_Call || self.expression instanceof AST_Lambda) {
output.add_mapping(self.start);
@@ -1289,11 +1464,14 @@ function OutputStream(options) {
});
}
DEFPRINT(AST_Call, function(output) {
this.expression.print(output);
print_call_args(this, output);
var self = this;
print_annotation(self, output);
self.expression.print(output);
print_call_args(self, output);
});
DEFPRINT(AST_New, function(output) {
var self = this;
print_annotation(self, output);
output.print("new");
output.space();
self.expression.print(output);
@@ -1322,11 +1500,7 @@ function OutputStream(options) {
output.print_string(prop);
output.print("]");
} else {
if (expr instanceof AST_Number && expr.value >= 0) {
if (!/[xa-f.)]/i.test(output.last())) {
output.print(".");
}
}
if (expr instanceof AST_Number && !/[ex.)]/i.test(output.last())) output.print(".");
output.print(".");
// the name after dot would be mapped about here.
output.add_mapping(self.end);
@@ -1429,7 +1603,14 @@ function OutputStream(options) {
output.space();
} : noop);
});
DEFPRINT(AST_DestructuredKeyVal, print_key_value);
DEFPRINT(AST_DestructuredKeyVal, function(output) {
var self = this;
var key = print_property_key(self, output);
var value = self.value;
if (key && value instanceof AST_SymbolDeclaration && key == get_symbol_name(value)) return;
output.colon();
value.print(output);
});
DEFPRINT(AST_DestructuredObject, function(output) {
var props = this.properties, len = props.length, rest = this.rest;
if (len || rest) output.with_block(function() {
@@ -1454,12 +1635,12 @@ function OutputStream(options) {
});
else print_braced_empty(this, output);
});
DEFPRINT(AST_Object, function(output) {
var props = this.properties;
function print_properties(self, output, no_comma) {
var props = self.properties;
if (props.length > 0) output.with_block(function() {
props.forEach(function(prop, i) {
if (i) {
output.print(",");
if (!no_comma) output.print(",");
output.newline();
}
output.indent();
@@ -1467,7 +1648,10 @@ function OutputStream(options) {
});
output.newline();
});
else print_braced_empty(this, output);
else print_braced_empty(self, output);
}
DEFPRINT(AST_Object, function(output) {
print_properties(this, output);
});
function print_property_key(self, output) {
@@ -1482,28 +1666,36 @@ function OutputStream(options) {
output.print(make_num(key));
} else {
var quote = self.start && self.start.quote;
if (RESERVED_WORDS[key] ? !output.option("ie8") : is_identifier_string(key)) {
if (self.private) {
output.print_name(key);
} else if (RESERVED_WORDS[key] ? !output.option("ie8") : is_identifier_string(key)) {
if (quote && output.option("keep_quoted_props")) {
output.print_string(key, quote);
} else {
output.print_name(key);
return key;
}
} else {
output.print_string(key, quote);
}
}
}
function print_key_value(output) {
DEFPRINT(AST_ObjectKeyVal, function(output) {
var self = this;
print_property_key(self, output);
output.colon();
self.value.print(output);
}
DEFPRINT(AST_ObjectKeyVal, print_key_value);
});
DEFPRINT(AST_ObjectMethod, function(output) {
print_method(this, output);
});
function print_accessor(type) {
return function(output) {
var self = this;
if (self.static) {
output.print("static");
output.space();
}
output.print(type);
output.space();
print_property_key(self, output);
@@ -1512,14 +1704,38 @@ function OutputStream(options) {
}
DEFPRINT(AST_ObjectGetter, print_accessor("get"));
DEFPRINT(AST_ObjectSetter, print_accessor("set"));
function get_symbol_name(sym) {
var def = sym.definition();
return def && def.mangled_name || sym.name;
}
DEFPRINT(AST_Symbol, function(output) {
var def = this.definition();
output.print_name(def && def.mangled_name || this.name);
output.print_name(get_symbol_name(this));
});
DEFPRINT(AST_SymbolExport, function(output) {
var self = this;
var name = get_symbol_name(self);
output.print_name(name);
var alias = self.alias;
if (alias != name) {
output.space();
output.print("as");
output.space();
output.print_name(alias);
}
});
DEFPRINT(AST_SymbolImport, function(output) {
var self = this;
var name = get_symbol_name(self);
var key = self.key;
if (key && key != name) {
output.print_name(key);
output.space();
output.print("as");
output.space();
}
output.print_name(name);
});
DEFPRINT(AST_Hole, noop);
DEFPRINT(AST_This, function(output) {
output.print("this");
});
DEFPRINT(AST_Template, function(output) {
var self = this;
if (self.tag) self.tag.print(output);
@@ -1534,7 +1750,7 @@ function OutputStream(options) {
output.print("`");
});
DEFPRINT(AST_Constant, function(output) {
output.print(this.value);
output.print("" + this.value);
});
DEFPRINT(AST_String, function(output) {
output.print_string(this.value, this.quote);
@@ -1590,7 +1806,7 @@ function OutputStream(options) {
function force_statement(stat, output) {
if (output.option("braces") && !(stat instanceof AST_Const || stat instanceof AST_Let)) {
make_block(stat, output);
} else if (!stat || stat instanceof AST_EmptyStatement) {
} else if (stat instanceof AST_EmptyStatement) {
output.force_semicolon();
} else {
stat.print(output);
@@ -1641,11 +1857,11 @@ function OutputStream(options) {
}
function make_block(stmt, output) {
if (!stmt || stmt instanceof AST_EmptyStatement)
output.print("{}");
else if (stmt instanceof AST_BlockStatement)
if (stmt instanceof AST_EmptyStatement) {
print_braced_empty(stmt, output);
} else if (stmt instanceof AST_BlockStatement) {
stmt.print(output);
else output.with_block(function() {
} else output.with_block(function() {
output.indent();
stmt.print(output);
output.newline();

View File

@@ -44,10 +44,10 @@
"use strict";
var KEYWORDS = "break case catch const continue debugger default delete do else finally for function if in instanceof let new return switch throw try typeof var void while with";
var KEYWORDS = "break case catch class const continue debugger default delete do else extends finally for function if in instanceof let new return switch throw try typeof var void while with";
var KEYWORDS_ATOM = "false null true";
var RESERVED_WORDS = [
"await abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile yield",
"abstract async await boolean byte char double enum export final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile yield",
KEYWORDS_ATOM,
KEYWORDS,
].join(" ");
@@ -100,14 +100,19 @@ var OPERATORS = makePredicate([
"/=",
"*=",
"%=",
"**=",
">>=",
"<<=",
">>>=",
"&=",
"|=",
"^=",
"&=",
"&&",
"||"
"||",
"??",
"&&=",
"||=",
"??=",
]);
var NEWLINE_CHARS = "\n\r\u2028\u2029";
@@ -159,10 +164,10 @@ function decode_escape_sequence(seq) {
case "t": return "\t";
case "u":
var code;
if (seq.length == 5) {
code = seq.slice(1);
} else if (seq[1] == "{" && seq.slice(-1) == "}") {
if (seq[1] == "{" && seq.slice(-1) == "}") {
code = seq.slice(2, -1);
} else if (seq.length == 5) {
code = seq.slice(1);
} else {
return;
}
@@ -346,7 +351,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
function read_while(pred) {
var ret = "", ch;
while ((ch = peek()) && pred(ch)) ret += next();
while ((ch = peek()) && pred(ch, ret)) ret += next();
return ret;
}
@@ -354,24 +359,27 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
js_error(err, filename, S.tokline, S.tokcol, S.tokpos);
}
function is_octal(num) {
return /^0[0-7_]+$/.test(num);
}
function read_num(prefix) {
var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
var num = read_while(function(ch) {
var code = ch.charCodeAt(0);
switch (code) {
case 120: case 88: // xX
var num = read_while(function(ch, str) {
switch (ch) {
case "x": case "X":
return has_x ? false : (has_x = true);
case 101: case 69: // eE
case "e": case "E":
return has_x ? true : has_e ? false : (has_e = after_e = true);
case 43: case 45: // +-
case "+": case "-":
return after_e;
case (after_e = false, 46): // .
return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false;
case (after_e = false, "."):
return has_dot || has_e || has_x || is_octal(str) ? false : (has_dot = true);
}
return is_digit(code) || /[_0-9a-fo]/i.test(ch);
return /[_0-9a-dfo]/i.test(ch);
});
if (prefix) num = prefix + num;
if (/^0[0-7_]+$/.test(num)) {
if (is_octal(num)) {
if (next_token.has_directive("use strict")) parse_error("Legacy octal literals are not allowed in strict mode");
} else {
num = num.replace(has_x ? /([1-9a-f]|.0)_(?=[0-9a-f])/gi : /([1-9]|.0)_(?=[0-9])/gi, "$1");
@@ -651,7 +659,7 @@ var UNARY_PREFIX = makePredicate("typeof void delete -- ++ ! ~ - +");
var UNARY_POSTFIX = makePredicate("-- ++");
var ASSIGNMENT = makePredicate("= += -= /= *= %= >>= <<= >>>= |= ^= &=");
var ASSIGNMENT = makePredicate("= += -= /= *= %= **= >>= <<= >>>= &= |= ^= &&= ||= ??=");
var PRECEDENCE = function(a, ret) {
for (var i = 0; i < a.length;) {
@@ -662,6 +670,7 @@ var PRECEDENCE = function(a, ret) {
}
return ret;
}([
["??"],
["||"],
["&&"],
["|"],
@@ -844,6 +853,15 @@ function parse($TEXT, options) {
case "await":
if (S.in_async) return simple_statement();
break;
case "export":
next();
return export_();
case "import":
var token = peek();
if (!(token.type == "punc" && /^[(.]$/.test(token.value))) {
next();
return import_();
}
case "yield":
if (S.in_generator) return simple_statement();
break;
@@ -878,6 +896,10 @@ function parse($TEXT, options) {
next();
return break_cont(AST_Break);
case "class":
next();
return class_(AST_DefClass);
case "const":
next();
var node = const_();
@@ -1039,11 +1061,135 @@ function parse($TEXT, options) {
return stat;
}
function has_modifier(name) {
if (!is("name", name)) return;
var token = peek();
if (!token) return;
if (is_token(token, "operator", "=")) return;
if (token.type == "punc" && /^[(;}]$/.test(token.value)) return;
if (has_newline_before(token)) return;
return next();
}
function class_(ctor) {
var was_async = S.in_async;
var was_gen = S.in_generator;
S.input.push_directives_stack();
S.input.add_directive("use strict");
var name;
if (ctor === AST_DefClass) {
name = as_symbol(AST_SymbolDefClass);
} else {
name = as_symbol(AST_SymbolClass, true);
}
var parent = null;
if (is("keyword", "extends")) {
next();
handle_regexp();
parent = expr_atom(true);
}
expect("{");
var props = [];
while (!is("punc", "}")) {
if (is("punc", ";")) {
next();
continue;
}
var start = S.token;
var fixed = !!has_modifier("static");
var async = has_modifier("async");
if (is("operator", "*")) {
next();
var internal = is("name") && /^#/.test(S.token.value);
var key = as_property_key();
var gen_start = S.token;
var gen = function_(async ? AST_AsyncGeneratorFunction : AST_GeneratorFunction);
gen.start = gen_start;
gen.end = prev();
props.push(new AST_ClassMethod({
start: start,
static: fixed,
private: internal,
key: key,
value: gen,
end: prev(),
}));
continue;
}
var internal = is("name") && /^#/.test(S.token.value);
var key = as_property_key();
if (is("punc", "(")) {
var func_start = S.token;
var func = function_(async ? AST_AsyncFunction : AST_Function);
func.start = func_start;
func.end = prev();
props.push(new AST_ClassMethod({
start: start,
static: fixed,
private: internal,
key: key,
value: func,
end: prev(),
}));
continue;
}
if (async) unexpected(async);
var value = null;
if (is("operator", "=")) {
next();
S.in_async = false;
S.in_generator = false;
value = maybe_assign();
S.in_generator = was_gen;
S.in_async = was_async;
} else if (!(is("punc", ";") || is("punc", "}"))) {
var type = null;
switch (key) {
case "get":
type = AST_ClassGetter;
break;
case "set":
type = AST_ClassSetter;
break;
}
if (type) {
props.push(new type({
start: start,
static: fixed,
private: is("name") && /^#/.test(S.token.value),
key: as_property_key(),
value: create_accessor(),
end: prev(),
}));
continue;
}
}
semicolon();
props.push(new AST_ClassField({
start: start,
static: fixed,
private: internal,
key: key,
value: value,
end: prev(),
}));
}
next();
S.input.pop_directives_stack();
S.in_generator = was_gen;
S.in_async = was_async;
return new ctor({
extends: parent,
name: name,
properties: props,
});
}
function for_() {
var await = is("name", "await") && next();
expect("(");
var init = null;
if (!is("punc", ";")) {
if (await || !is("punc", ";")) {
init = is("keyword", "const")
? (next(), const_(true))
: is("keyword", "let")
@@ -1192,6 +1338,7 @@ function parse($TEXT, options) {
}
} else {
body = [];
handle_regexp();
value = maybe_assign();
}
S.input.pop_directives_stack();
@@ -1272,6 +1419,197 @@ function parse($TEXT, options) {
});
}
function is_alias() {
return is("name") || is_identifier_string(S.token.value);
}
function export_() {
if (is("operator", "*")) {
next();
var alias = "*";
if (is("name", "as")) {
next();
if (!is_alias()) expect_token("name");
alias = S.token.value;
next();
}
expect_token("name", "from");
var path = S.token;
expect_token("string");
semicolon();
return new AST_ExportForeign({
aliases: [ alias ],
keys: [ "*" ],
path: path.value,
quote: path.quote,
});
}
if (is("punc", "{")) {
next();
var aliases = [];
var keys = [];
while (is_alias()) {
var key = S.token;
next();
keys.push(key);
if (is("name", "as")) {
next();
if (!is_alias()) expect_token("name");
aliases.push(S.token.value);
next();
} else {
aliases.push(key.value);
}
if (!is("punc", "}")) expect(",");
}
expect("}");
if (is("name", "from")) {
next();
var path = S.token;
expect_token("string");
semicolon();
return new AST_ExportForeign({
aliases: aliases,
keys: keys.map(function(token) {
return token.value;
}),
path: path.value,
quote: path.quote,
});
}
semicolon();
return new AST_ExportReferences({
properties: keys.map(function(token, index) {
if (!is_token(token, "name")) token_error(token, "Name expected");
var sym = _make_symbol(AST_SymbolExport, token);
sym.alias = aliases[index];
return sym;
}),
});
}
if (is("keyword", "default")) {
next();
var start = S.token;
var body = export_default_decl();
if (body) {
body.start = start;
body.end = prev();
} else {
body = expression();
semicolon();
}
return new AST_ExportDefault({ body: body });
}
return new AST_ExportDeclaration({ body: export_decl() });
}
function maybe_named(def, expr) {
if (expr.name) {
expr = new def(expr);
expr.name = new (def === AST_DefClass ? AST_SymbolDefClass : AST_SymbolDefun)(expr.name);
}
return expr;
}
function export_default_decl() {
if (is("name", "async")) {
if (!is_token(peek(), "keyword", "function")) return;
next();
next();
if (!is("operator", "*")) return maybe_named(AST_AsyncDefun, function_(AST_AsyncFunction));
next();
return maybe_named(AST_AsyncGeneratorDefun, function_(AST_AsyncGeneratorFunction));
} else if (is("keyword")) switch (S.token.value) {
case "class":
next();
return maybe_named(AST_DefClass, class_(AST_ClassExpression));
case "function":
next();
if (!is("operator", "*")) return maybe_named(AST_Defun, function_(AST_Function));
next();
return maybe_named(AST_GeneratorDefun, function_(AST_GeneratorFunction));
}
}
var export_decl = embed_tokens(function() {
if (is("name", "async")) {
next();
expect_token("keyword", "function");
if (!is("operator", "*")) return function_(AST_AsyncDefun);
next();
return function_(AST_AsyncGeneratorDefun);
} else if (is("keyword")) switch (S.token.value) {
case "class":
next();
return class_(AST_DefClass);
case "const":
next();
var node = const_();
semicolon();
return node;
case "function":
next();
if (!is("operator", "*")) return function_(AST_Defun);
next();
return function_(AST_GeneratorDefun);
case "let":
next();
var node = let_();
semicolon();
return node;
case "var":
next();
var node = var_();
semicolon();
return node;
}
unexpected();
});
function import_() {
var all = null;
var def = as_symbol(AST_SymbolImport, true);
var props = null;
if (def ? (def.key = "", is("punc", ",") && next()) : !is("string")) {
if (is("operator", "*")) {
next();
expect_token("name", "as");
all = as_symbol(AST_SymbolImport);
all.key = "*";
} else {
expect("{");
props = [];
while (is_alias()) {
var alias;
if (is_token(peek(), "name", "as")) {
var key = S.token.value;
next();
next();
alias = as_symbol(AST_SymbolImport);
alias.key = key;
} else {
alias = as_symbol(AST_SymbolImport);
alias.key = alias.name;
}
props.push(alias);
if (!is("punc", "}")) expect(",");
}
expect("}");
}
}
if (all || def || props) expect_token("name", "from");
var path = S.token;
expect_token("string");
semicolon();
return new AST_Import({
all: all,
default: def,
path: path.value,
properties: props,
quote: path.quote,
});
}
function block_() {
expect("{");
var a = [];
@@ -1406,58 +1744,66 @@ function parse($TEXT, options) {
var new_ = function(allow_calls) {
var start = S.token;
expect_token("operator", "new");
var newexp = expr_atom(false), args;
if (is("punc", "(")) {
var call;
if (is("punc", ".") && is_token(peek(), "name", "target")) {
next();
args = expr_list(")", !options.strict);
next();
call = new AST_NewTarget();
} else {
args = [];
var exp = expr_atom(false), args;
if (is("punc", "(")) {
next();
args = expr_list(")", !options.strict);
} else {
args = [];
}
call = new AST_New({ expression: exp, args: args });
}
var call = new AST_New({
start : start,
expression : newexp,
args : args,
end : prev()
});
mark_pure(call);
call.start = start;
call.end = prev();
return subscripts(call, allow_calls);
};
function as_atom_node() {
var tok = S.token, ret;
var ret, tok = S.token, value = tok.value;
switch (tok.type) {
case "num":
ret = new AST_Number({ start: tok, end: tok, value: tok.value });
break;
case "bigint":
ret = new AST_BigInt({ start: tok, end: tok, value: tok.value });
break;
case "string":
ret = new AST_String({
start : tok,
end : tok,
value : tok.value,
quote : tok.quote
});
break;
case "regexp":
ret = new AST_RegExp({ start: tok, end: tok, value: tok.value });
break;
case "atom":
switch (tok.value) {
case "false":
ret = new AST_False({ start: tok, end: tok });
break;
case "true":
ret = new AST_True({ start: tok, end: tok });
break;
case "null":
ret = new AST_Null({ start: tok, end: tok });
break;
if (isFinite(value)) {
ret = new AST_Number({ value: value });
} else {
ret = new AST_Infinity();
if (value < 0) ret = new AST_UnaryPrefix({ operator: "-", expression: ret });
}
break;
case "bigint":
ret = new AST_BigInt({ value: value });
break;
case "string":
ret = new AST_String({ value : value, quote : tok.quote });
break;
case "regexp":
ret = new AST_RegExp({ value: value });
break;
case "atom":
switch (value) {
case "false":
ret = new AST_False();
break;
case "true":
ret = new AST_True();
break;
case "null":
ret = new AST_Null();
break;
default:
unexpected();
}
break;
default:
unexpected();
}
next();
ret.start = ret.end = tok;
return ret;
}
@@ -1497,11 +1843,13 @@ function parse($TEXT, options) {
expect(")");
var end = prev();
end.comments_before = ex.end.comments_before;
[].push.apply(ex.end.comments_after, end.comments_after);
end.comments_after.forEach(function(comment) {
ex.end.comments_after.push(comment);
if (comment.nlb) S.token.nlb = true;
});
end.comments_after.length = 0;
end.comments_after = ex.end.comments_after;
ex.end = end;
if (ex instanceof AST_Call) mark_pure(ex);
if (is("punc", "=>")) return arrow(ex instanceof AST_Sequence ? ex.expressions : [ ex ], start);
return subscripts(ex, allow_calls);
case "[":
@@ -1511,7 +1859,14 @@ function parse($TEXT, options) {
}
unexpected();
}
if (is("keyword", "function")) {
if (is("keyword")) switch (start.value) {
case "class":
next();
var clazz = class_(AST_ClassExpression);
clazz.start = start;
clazz.end = prev();
return subscripts(clazz, allow_calls);
case "function":
next();
var func;
if (is("operator", "*")) {
@@ -1618,7 +1973,7 @@ function parse($TEXT, options) {
var gen = function_(AST_GeneratorFunction);
gen.start = gen_start;
gen.end = prev();
a.push(new AST_ObjectKeyVal({
a.push(new AST_ObjectMethod({
start: start,
key: key,
value: gen,
@@ -1667,7 +2022,7 @@ function parse($TEXT, options) {
var func = function_(AST_Function);
func.start = func_start;
func.end = prev();
a.push(new AST_ObjectKeyVal({
a.push(new AST_ObjectMethod({
start: start,
key: key,
value: func,
@@ -1693,7 +2048,7 @@ function parse($TEXT, options) {
var func = function_(is_gen ? AST_AsyncGeneratorFunction : AST_AsyncFunction);
func.start = func_start;
func.end = prev();
a.push(new AST_ObjectKeyVal({
a.push(new AST_ObjectMethod({
start: start,
key: key,
value: func,
@@ -1753,9 +2108,21 @@ function parse($TEXT, options) {
function _make_symbol(type, token) {
var name = token.value;
if (name === "await" && S.in_async) unexpected(token);
if (name === "yield" && S.in_generator) unexpected(token);
return new (name === "this" ? AST_This : type)({
switch (name) {
case "await":
if (S.in_async) unexpected(token);
break;
case "super":
type = AST_Super;
break;
case "this":
type = AST_This;
break;
case "yield":
if (S.in_generator) unexpected(token);
break;
}
return new type({
name: "" + name,
start: token,
end: token,
@@ -1859,19 +2226,6 @@ function parse($TEXT, options) {
});
}
function mark_pure(call) {
var start = call.start;
var comments = start.comments_before;
var i = HOP(start, "comments_before_length") ? start.comments_before_length : comments.length;
while (--i >= 0) {
var comment = comments[i];
if (/[@#]__PURE__/.test(comment.value)) {
call.pure = comment;
break;
}
}
}
function template(tag) {
var read = S.input.context().read_template;
var strings = [];
@@ -1919,7 +2273,6 @@ function parse($TEXT, options) {
args : expr_list(")", !options.strict),
end : prev()
});
mark_pure(call);
return subscripts(call, true);
}
if (is("punc", "`")) {
@@ -1928,6 +2281,18 @@ function parse($TEXT, options) {
tmpl.end = prev();
return subscripts(tmpl, allow_calls);
}
if (expr instanceof AST_Call && !expr.pure) {
var start = expr.start;
var comments = start.comments_before;
var i = HOP(start, "comments_before_length") ? start.comments_before_length : comments.length;
while (--i >= 0) {
var match = /[@#]__PURE__/.exec(comments[i].value);
if (match) {
expr.pure = match[0];
break;
}
}
}
return expr;
};

View File

@@ -81,7 +81,9 @@ var builtins = function() {
function reserve_quoted_keys(ast, reserved) {
ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_ObjectProperty) {
if (node instanceof AST_ClassProperty) {
if (node.start && node.start.quote) add(node.key);
} else if (node instanceof AST_ObjectProperty) {
if (node.start && node.start.quote) add(node.key);
} else if (node instanceof AST_Sub) {
addStrings(node.property, add);
@@ -163,6 +165,8 @@ function mangle_properties(ast, options) {
addStrings(node.args[0], add);
break;
}
} else if (node instanceof AST_ClassProperty) {
if (typeof node.key == "string") add(node.key);
} else if (node instanceof AST_Dot) {
add(node.property);
} else if (node instanceof AST_ObjectProperty) {
@@ -193,6 +197,8 @@ function mangle_properties(ast, options) {
mangleStrings(node.args[0]);
break;
}
} else if (node instanceof AST_ClassProperty) {
if (typeof node.key == "string") node.key = mangle(node.key);
} else if (node instanceof AST_Dot) {
node.property = mangle(node.property);
} else if (node instanceof AST_ObjectProperty) {
@@ -222,9 +228,7 @@ function mangle_properties(ast, options) {
}
function mangle(name) {
if (!should_mangle(name)) {
return name;
}
if (!should_mangle(name)) return name;
var mangled = cache.get(name);
if (!mangled) {
if (debug) {
@@ -236,6 +240,7 @@ function mangle_properties(ast, options) {
if (!mangled) do {
mangled = base54(++cname);
} while (!can_mangle(mangled));
if (/^#/.test(name)) mangled = "#" + mangled;
cache.set(name, mangled);
}
return mangled;

View File

@@ -45,6 +45,7 @@
function SymbolDef(id, scope, orig, init) {
this.eliminated = 0;
this.exported = false;
this.global = false;
this.id = id;
this.init = init;
@@ -91,11 +92,14 @@ SymbolDef.prototype = {
},
unmangleable: function(options) {
return this.global && !options.toplevel
|| this.exported
|| this.undeclared
|| !options.eval && this.scope.pinned()
|| options.keep_fnames
&& (this.orig[0] instanceof AST_SymbolLambda
|| this.orig[0] instanceof AST_SymbolDefun);
&& (this.orig[0] instanceof AST_SymbolClass
|| this.orig[0] instanceof AST_SymbolDefClass
|| this.orig[0] instanceof AST_SymbolDefun
|| this.orig[0] instanceof AST_SymbolLambda);
},
};
@@ -106,6 +110,7 @@ function is_lhs(node, parent) {
if (parent instanceof AST_DefaultValue) return parent.name === node && node;
if (parent instanceof AST_Destructured) return node;
if (parent instanceof AST_DestructuredKeyVal) return node;
if (parent instanceof AST_ForEnumeration) return parent.init === node && node;
if (parent instanceof AST_Unary) return unary_side_effects[parent.operator] && parent.expression;
}
@@ -118,11 +123,35 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
// pass 1: setup scope chaining and handle definitions
var self = this;
var defun = null;
var exported = false;
var next_def_id = 0;
var scope = self.parent_scope = null;
var tw = new TreeWalker(function(node, descend) {
if (node instanceof AST_LambdaDefinition) {
if (node instanceof AST_DefClass) {
var save_exported = exported;
exported = tw.parent() instanceof AST_ExportDeclaration;
node.name.walk(tw);
exported = save_exported;
walk_scope(function() {
if (node.extends) node.extends.walk(tw);
node.properties.forEach(function(prop) {
prop.walk(tw);
});
});
return true;
}
if (node instanceof AST_Definitions) {
var save_exported = exported;
exported = tw.parent() instanceof AST_ExportDeclaration;
descend();
exported = save_exported;
return true;
}
if (node instanceof AST_LambdaDefinition) {
var save_exported = exported;
exported = tw.parent() instanceof AST_ExportDeclaration;
node.name.walk(tw);
exported = save_exported;
walk_scope(function() {
node.argnames.forEach(function(argname) {
argname.walk(tw);
@@ -169,9 +198,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
} else if (node instanceof AST_SymbolConst) {
scope.def_variable(node).defun = defun;
var def = scope.def_variable(node);
def.defun = defun;
if (exported) def.exported = true;
} else if (node instanceof AST_SymbolDefun) {
defun.def_function(node, tw.parent());
var def = defun.def_function(node, tw.parent());
if (exported) def.exported = true;
entangle(defun, scope);
} else if (node instanceof AST_SymbolFunarg) {
defun.def_variable(node);
@@ -180,9 +212,11 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
var def = defun.def_function(node, node.name == "arguments" ? undefined : defun);
if (options.ie8) def.defun = defun.parent_scope.resolve();
} else if (node instanceof AST_SymbolLet) {
scope.def_variable(node);
var def = scope.def_variable(node);
if (exported) def.exported = true;
} else if (node instanceof AST_SymbolVar) {
defun.def_variable(node, null);
var def = defun.def_variable(node, node instanceof AST_SymbolImport ? undefined : null);
if (exported) def.exported = true;
entangle(defun, scope);
}
@@ -239,16 +273,18 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
return true;
}
if (node instanceof AST_SymbolDeclaration) {
var def = node.definition();
def.preinit = def.references.length;
if (node instanceof AST_SymbolCatch) {
// ensure mangling works if `catch` reuses a scope variable
var def = node.definition().redefined();
if (def) for (var s = node.scope; s; s = s.parent_scope) {
push_uniq(s.enclosed, def);
if (s === def.scope) break;
var redef = def.redefined();
if (redef) for (var s = node.scope; s; s = s.parent_scope) {
push_uniq(s.enclosed, redef);
if (s === redef.scope) break;
}
} else if (node instanceof AST_SymbolConst) {
// ensure compression works if `const` reuses a scope variable
var redef = node.definition().redefined();
var redef = def.redefined();
if (redef) redef.const_redefs = true;
}
if (node.name != "arguments") return true;
@@ -299,6 +335,13 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
self.uses_eval = true;
}
}
if (sym.init instanceof AST_LambdaDefinition && sym.scope !== sym.init.name.scope) {
var scope = node.scope;
do {
if (scope === sym.init.name.scope) break;
} while (scope = scope.parent_scope);
if (!scope) sym.init = undefined;
}
node.thedef = sym;
node.reference(options);
return true;
@@ -399,6 +442,7 @@ AST_Scope.DEFMETHOD("init_vars", function(parent_scope) {
});
AST_Arrow.DEFMETHOD("init_vars", function(parent_scope) {
init_scope_vars(this, parent_scope);
return this;
});
AST_AsyncArrow.DEFMETHOD("init_vars", function(parent_scope) {
init_scope_vars(this, parent_scope);
@@ -418,7 +462,9 @@ AST_Symbol.DEFMETHOD("mark_enclosed", function(options) {
var def = this.definition();
for (var s = this.scope; s; s = s.parent_scope) {
push_uniq(s.enclosed, def);
if (options.keep_fnames) {
if (!options) {
delete s._var_names;
} else if (options.keep_fnames) {
s.functions.each(function(d) {
push_uniq(def.scope.enclosed, d);
});

View File

@@ -147,6 +147,15 @@ TreeTransformer.prototype = new TreeWalker;
}
DEF(AST_Arrow, transform_arrow);
DEF(AST_AsyncArrow, transform_arrow);
DEF(AST_Class, function(self, tw) {
if (self.name) self.name = self.name.transform(tw);
if (self.extends) self.extends = self.extends.transform(tw);
self.properties = do_list(self.properties, tw);
});
DEF(AST_ClassProperty, function(self, tw) {
if (self.key instanceof AST_Node) self.key = self.key.transform(tw);
if (self.value) self.value = self.value.transform(tw);
});
DEF(AST_Call, function(self, tw) {
self.expression = self.expression.transform(tw);
self.args = do_list(self.args, tw);
@@ -204,6 +213,20 @@ TreeTransformer.prototype = new TreeWalker;
if (self.key instanceof AST_Node) self.key = self.key.transform(tw);
self.value = self.value.transform(tw);
});
DEF(AST_ExportDeclaration, function(self, tw) {
self.body = self.body.transform(tw);
});
DEF(AST_ExportDefault, function(self, tw) {
self.body = self.body.transform(tw);
});
DEF(AST_ExportReferences, function(self, tw) {
self.properties = do_list(self.properties, tw);
});
DEF(AST_Import, function(self, tw) {
if (self.all) self.all = self.all.transform(tw);
if (self.default) self.default = self.default.transform(tw);
if (self.properties) self.properties = do_list(self.properties, tw);
});
DEF(AST_Template, function(self, tw) {
if (self.tag) self.tag = self.tag.transform(tw);
self.expressions = do_list(self.expressions, tw);

View File

@@ -238,7 +238,7 @@ function HOP(obj, prop) {
// return true if the node at the top of the stack (that means the
// innermost node in the current output) is lexically the first in
// a statement.
function first_in_statement(stack, arrow) {
function first_in_statement(stack, arrow, export_default) {
var node = stack.parent(-1);
for (var i = 0, p; p = stack.parent(i++); node = p) {
if (is_arrow(p)) {
@@ -249,12 +249,14 @@ function first_in_statement(stack, arrow) {
if (p.expression === node) continue;
} else if (p instanceof AST_Conditional) {
if (p.condition === node) continue;
} else if (p instanceof AST_ExportDefault) {
return export_default;
} else if (p instanceof AST_PropAccess) {
if (p.expression === node) continue;
} else if (p instanceof AST_Sequence) {
if (p.expressions[0] === node) continue;
} else if (p instanceof AST_Statement) {
return p.body === node;
} else if (p instanceof AST_SimpleStatement) {
return true;
} else if (p instanceof AST_Template) {
if (p.tag === node) continue;
} else if (p instanceof AST_UnaryPostfix) {

View File

@@ -3,7 +3,7 @@
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.12.8",
"version": "3.13.4",
"engines": {
"node": ">=0.8.0"
},

View File

@@ -10,33 +10,38 @@ var sandbox = require("./sandbox");
var semver = require("semver");
var U = require("./node");
var file = process.argv[2];
var batch = 50;
var dir = path.resolve(path.dirname(module.filename), "compress");
if (file) {
if (process.argv.length > 3) {
var file = process.argv[2];
var start = process.argv[3] | 0;
var minify_options = require("./ufuzz/options.json").map(JSON.stringify);
log("--- {file}", { file: file });
var tests = parse_test(path.resolve(dir, file));
process.exit(Object.keys(tests).filter(function(name) {
process.exit(Object.keys(tests).slice(start, start + batch).filter(function(name) {
return !test_case(tests[name]);
}).length);
} else {
var files = fs.readdirSync(dir).filter(function(name) {
var files = process.argv.length == 3 ? [ process.argv[2] ] : fs.readdirSync(dir).filter(function(name) {
return /\.js$/i.test(name);
});
var failures = 0;
var failed_files = Object.create(null);
(function next() {
var file = files.shift();
if (file) {
child_process.spawn(process.argv[0], [ process.argv[1], file ], {
(function next(file, start, length) {
if (start < length) {
child_process.spawn(process.argv[0], [ process.argv[1], file, start, batch ], {
stdio: [ "ignore", 1, 2 ]
}).on("exit", function(code) {
if (code) {
failures += code;
failed_files[file] = code;
failed_files[file] = true;
}
next();
next(file, start + batch, length);
});
} else if (file = files.shift()) {
log("--- {file}", { file: file });
start = 0;
length = Object.keys(parse_test(path.resolve(dir, file))).length;
next(file, start, length);
} else if (failures) {
console.error();
console.error("!!! Failed " + failures + " test case(s).");
@@ -257,6 +262,7 @@ function test_case(test) {
var input = to_toplevel(test.input, test.mangle);
var input_code = make_code(input);
var input_formatted = make_code(test.input, {
annotations: true,
beautify: true,
comments: "all",
keep_quoted_props: true,

View File

@@ -0,0 +1,473 @@
issue_2629_1: {
options = {
annotations: true,
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ a();
/*@__PURE__*/ (b());
(/*@__PURE__*/ c)();
(/*@__PURE__*/ d());
}
expect_exact: "c();"
}
issue_2629_2: {
options = {
annotations: true,
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ a(1)(2)(3);
/*@__PURE__*/ (b(1))(2)(3);
/*@__PURE__*/ (c(1)(2))(3);
/*@__PURE__*/ (d(1)(2)(3));
(/*@__PURE__*/ e)(1)(2)(3);
(/*@__PURE__*/ f(1))(2)(3);
(/*@__PURE__*/ g(1)(2))(3);
(/*@__PURE__*/ h(1)(2)(3));
}
expect_exact: [
"e(1)(2)(3);",
"f(1)(2)(3);",
"g(1)(2)(3);",
]
}
issue_2629_3: {
options = {
annotations: true,
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ a.x(1).y(2).z(3);
/*@__PURE__*/ (b.x)(1).y(2).z(3);
/*@__PURE__*/ (c.x(1)).y(2).z(3);
/*@__PURE__*/ (d.x(1).y)(2).z(3);
/*@__PURE__*/ (e.x(1).y(2)).z(3);
/*@__PURE__*/ (f.x(1).y(2).z)(3);
/*@__PURE__*/ (g.x(1).y(2).z(3));
(/*@__PURE__*/ h).x(1).y(2).z(3);
(/*@__PURE__*/ i.x)(1).y(2).z(3);
(/*@__PURE__*/ j.x(1)).y(2).z(3);
(/*@__PURE__*/ k.x(1).y)(2).z(3);
(/*@__PURE__*/ l.x(1).y(2)).z(3);
(/*@__PURE__*/ m.x(1).y(2).z)(3);
(/*@__PURE__*/ n.x(1).y(2).z(3));
}
expect_exact: [
"h.x(1).y(2).z(3);",
"i.x(1).y(2).z(3);",
"j.x(1).y(2).z(3);",
"k.x(1).y(2).z(3);",
"l.x(1).y(2).z(3);",
"m.x(1).y(2).z(3);",
]
}
issue_2629_4: {
options = {
annotations: true,
side_effects: true,
}
input: {
(/*@__PURE__*/ x(), y());
(w(), /*@__PURE__*/ x(), y());
}
expect: {
y();
w(), y();
}
}
issue_2629_5: {
options = {
annotations: true,
side_effects: true,
}
input: {
[ /*@__PURE__*/ x() ];
[ /*@__PURE__*/ x(), y() ];
[ w(), /*@__PURE__*/ x(), y() ];
}
expect: {
y();
w(), y();
}
}
issue_2638: {
options = {
annotations: true,
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/(g() || h())(x(), y());
(/*@__PURE__*/ (a() || b()))(c(), d());
}
expect_exact: [
"x(),y();",
"(a()||b())(c(),d());",
]
}
issue_2705_1: {
options = {
annotations: true,
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ new a();
/*@__PURE__*/ (new b());
new (/*@__PURE__*/ c)();
(/*@__PURE__*/ new d());
}
expect_exact: [
"new c;",
]
}
issue_2705_2: {
options = {
annotations: true,
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ new a(1)(2)(3);
/*@__PURE__*/ new (b(1))(2)(3);
/*@__PURE__*/ new (c(1)(2))(3);
/*@__PURE__*/ new (d(1)(2)(3));
new (/*@__PURE__*/ e)(1)(2)(3);
(/*@__PURE__*/ new f(1))(2)(3);
(/*@__PURE__*/ new g(1)(2))(3);
(/*@__PURE__*/ new h(1)(2)(3));
}
expect_exact: [
"new e(1)(2)(3);",
"new f(1)(2)(3);",
"new g(1)(2)(3);",
]
}
issue_2705_3: {
options = {
annotations: true,
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ new a.x(1).y(2).z(3);
/*@__PURE__*/ new (b.x)(1).y(2).z(3);
/*@__PURE__*/ new (c.x(1)).y(2).z(3);
/*@__PURE__*/ new (d.x(1).y)(2).z(3);
/*@__PURE__*/ new (e.x(1).y(2)).z(3);
/*@__PURE__*/ new (f.x(1).y(2).z)(3);
/*@__PURE__*/ new (g.x(1).y(2).z(3));
new (/*@__PURE__*/ h).x(1).y(2).z(3);
/* */ new (/*@__PURE__*/ i.x)(1).y(2).z(3);
(/*@__PURE__*/ new j.x(1)).y(2).z(3);
(/*@__PURE__*/ new k.x(1).y)(2).z(3);
(/*@__PURE__*/ new l.x(1).y(2)).z(3);
(/*@__PURE__*/ new m.x(1).y(2).z)(3);
(/*@__PURE__*/ new n.x(1).y(2).z(3));
}
expect_exact: [
"new h.x(1).y(2).z(3);",
"/* */new i.x(1).y(2).z(3);",
"new j.x(1).y(2).z(3);",
"new k.x(1).y(2).z(3);",
"new l.x(1).y(2).z(3);",
"new m.x(1).y(2).z(3);",
]
}
issue_2705_4: {
options = {
annotations: true,
side_effects: true,
}
input: {
(/*@__PURE__*/ new x(), y());
(w(), /*@__PURE__*/ new x(), y());
}
expect: {
y();
w(), y();
}
}
issue_2705_5: {
options = {
annotations: true,
side_effects: true,
}
input: {
[ /*@__PURE__*/ new x() ];
[ /*@__PURE__*/ new x(), y() ];
[ w(), /*@__PURE__*/ new x(), y() ];
}
expect: {
y();
w(), y();
}
}
issue_2705_6: {
options = {
annotations: true,
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/new (g() || h())(x(), y());
/* */ new (/*@__PURE__*/ (a() || b()))(c(), d());
}
expect_exact: [
"x(),y();",
"/* */new(a()||b())(c(),d());",
]
}
issue_3858: {
options = {
annotations: true,
collapse_vars: true,
inline: true,
keep_fargs: false,
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
f("PASS");
}
expect: {
var f = function(a) {
return function() {
console.log(a);
}();
};
f("PASS");
}
expect_stdout: "PASS"
}
inline_pure_call_1: {
options = {
annotations: true,
collapse_vars: true,
inline: true,
keep_fargs: false,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
f("PASS");
}
expect: {}
}
inline_pure_call_2: {
options = {
annotations: true,
collapse_vars: true,
inline: true,
keep_fargs: false,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
var a = f("PASS");
}
expect: {}
}
inline_pure_call_3: {
options = {
annotations: true,
collapse_vars: true,
evaluate: true,
inline: true,
keep_fargs: false,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
var a = f("PASS");
console.log(a);
}
expect: {
var a = function() {
console.log("PASS");
}();
console.log(a);
}
expect_stdout: [
"PASS",
"undefined",
]
}
inline_pure_call_4: {
options = {
annotations: true,
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = /*@__PURE__*/ function() {
return console.log("PASS"), 42;
}();
console.log(a);
}
expect: {
var a = function() {
return console.log("PASS"), 42;
}();
console.log(a);
}
expect_stdout: [
"PASS",
"42",
]
}
compress_and_output_annotations_enabled: {
options = {
annotations: true,
side_effects: true,
}
beautify = {
annotations: true,
beautify: true,
comments: false,
}
input: {
var top = /*@__PURE__*/ foo();
/*@__PURE__*/ a(1)(2)(3);
/*@__PURE__*/ (b(1))(2)(3);
/*@__PURE__*/ (c(1)(2))(3);
/*@__PURE__*/ (d(1)(2)(3));
(/*@__PURE__*/ e)(1)(2)(3);
(/*@__PURE__*/ f(1))(2)(3);
(/*@__PURE__*/ g(1)(2))(3);
(/*@__PURE__*/ h(1)(2)(3));
/*@__PURE__*/ l(1).p(2);
(/*@__PURE__*/ m(1)).p(2);
(/*@__PURE__*/ n(1).p)(2);
(/*@__PURE__*/ o(1).p(2));
}
expect_exact: [
"var top = /*@__PURE__*/foo();",
"",
"e(1)(2)(3);",
"",
"f(1)(2)(3);",
"",
"g(1)(2)(3);",
"",
"m(1).p(2);",
"",
"n(1).p(2);",
]
}
compress_annotations_disabled_output_annotations_enabled: {
options = {
annotations: false,
evaluate: true,
sequences: true,
side_effects: true,
}
beautify = {
annotations: true,
comments: true,
}
input: {
/*@__PURE__*/ a(1+2);
/*#__PURE__*/ (b(2+3));
(/*@__PURE__*/ c)(side_effect);
(/*#__PURE__*/ d(effect()));
}
expect_exact: [
"/*@__PURE__*/a(3),",
"/*#__PURE__*/b(5),",
"c(side_effect),",
"/*#__PURE__*/d(effect());",
]
}
compress_and_output_annotations_disabled: {
options = {
annotations: false,
evaluate: true,
sequences: true,
side_effects: true,
}
beautify = {
annotations: false,
comments: true,
}
input: {
/*@__PURE__*/ a(1+2);
/*@__PURE__*/ (b(2+3));
(/*@__PURE__*/ c)(side_effect);
(/*@__PURE__*/ d(effect()));
}
expect_exact: [
"a(3),",
"b(5),",
"c(side_effect),",
"d(effect());",
]
}

View File

@@ -84,6 +84,7 @@ replace_index_drop_fargs_1: {
evaluate: true,
keep_fargs: false,
properties: true,
reduce_vars: true,
}
input: {
var arguments = [];
@@ -119,7 +120,7 @@ replace_index_drop_fargs_1: {
console.log(b, b, arguments.foo);
})("bar", 42);
(function(arguments) {
console.log(arguments[1], arguments[1], arguments.foo);
console.log("bar"[1], "bar"[1], "bar".foo);
})("bar", 42);
(function(argument_0, argument_1) {
var arguments;
@@ -649,6 +650,7 @@ issue_3420_1: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
console.log(function() {
@@ -671,6 +673,7 @@ issue_3420_2: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
var foo = function() {
@@ -691,6 +694,7 @@ issue_3420_3: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
"use strict";
@@ -713,6 +717,7 @@ issue_3420_4: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
!function() {
@@ -738,6 +743,7 @@ issue_3420_5: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
"use strict";
@@ -765,6 +771,7 @@ issue_3420_6: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
console.log(function() {
@@ -783,6 +790,7 @@ issue_3420_7: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
"use strict";
@@ -827,6 +835,7 @@ issue_4291_1: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
console.log(function() {
@@ -847,6 +856,7 @@ issue_4291_2: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
var a = function() {
@@ -857,8 +867,8 @@ issue_4291_2: {
console.log(a[1], a[0], a.length);
}
expect: {
var a = function(argument_0) {
if (argument_0)
var a = function() {
if (arguments[0])
arguments[1] = "PASS";
return arguments;
}(42);
@@ -871,6 +881,7 @@ issue_4397: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
console.log(typeof function() {
@@ -977,3 +988,48 @@ issue_4432: {
}
expect_stdout: "PASS"
}
issue_4696: {
options = {
arguments: true,
keep_fargs: false,
}
input: {
console.log(function() {
for (arguments in [ 42 ]);
for (var a in arguments[0])
return "PASS";
}());
}
expect: {
console.log(function() {
for (arguments in [ 42 ]);
for (var a in arguments[0])
return "PASS";
}());
}
expect_stdout: "PASS"
}
issue_4809: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
A = 0;
(function() {
arguments[A] = "PASS";
console.log(arguments[0]);
})();
}
expect: {
A = 0;
(function() {
arguments[A] = "PASS";
console.log(arguments[0]);
})();
}
expect_stdout: "PASS"
}

View File

@@ -355,3 +355,72 @@ constructor_good: {
expect_stdout: true
expect_warnings: []
}
unsafe_evaluate_modified_binary: {
options = {
evaluate: true,
reduce_vars: true,
unsafe: true,
}
input: {
(function(a) {
(console && a).push(1);
if (a.length)
console.log("PASS");
})([]);
}
expect: {
(function(a) {
(console && a).push(1);
if (a.length)
console.log("PASS");
})([]);
}
expect_stdout: "PASS"
}
unsafe_evaluate_modified_conditional: {
options = {
evaluate: true,
reduce_vars: true,
unsafe: true,
}
input: {
(function(a) {
(console ? a : []).push(1);
if (a.length)
console.log("PASS");
})([]);
}
expect: {
(function(a) {
(console ? a : []).push(1);
if (a.length)
console.log("PASS");
})([]);
}
expect_stdout: "PASS"
}
unsafe_evaluate_modified_sequence: {
options = {
evaluate: true,
reduce_vars: true,
unsafe: true,
}
input: {
(function(a) {
(0, a).push(1);
if (a.length)
console.log("PASS");
})([]);
}
expect: {
(function(a) {
(0, a).push(1);
if (a.length)
console.log("PASS");
})([]);
}
expect_stdout: "PASS"
}

View File

@@ -34,7 +34,7 @@ destructured_funarg: {
node_version: ">=6"
}
await_parenthesis: {
await_parentheses: {
input: {
async function f() {
await (a => a);
@@ -43,7 +43,7 @@ await_parenthesis: {
expect_exact: "async function f(){await(a=>a)}"
}
for_parenthesis_init: {
for_parentheses_init: {
input: {
for (a => (a in a); console.log(42););
}
@@ -52,7 +52,7 @@ for_parenthesis_init: {
node_version: ">=4"
}
for_parenthesis_condition: {
for_parentheses_condition: {
input: {
for (console.log(42); a => (a in a);)
break;
@@ -62,7 +62,7 @@ for_parenthesis_condition: {
node_version: ">=4"
}
for_parenthesis_step: {
for_parentheses_step: {
input: {
for (; console.log(42); a => (a in a));
}
@@ -71,7 +71,7 @@ for_parenthesis_step: {
node_version: ">=4"
}
for_assign_parenthesis_init: {
for_assign_parentheses_init: {
input: {
for (f = a => (a in a); console.log(42););
}
@@ -80,7 +80,7 @@ for_assign_parenthesis_init: {
node_version: ">=4"
}
for_assign_parenthesis_condition: {
for_assign_parentheses_condition: {
input: {
for (console.log(42); f = a => (a in a);)
break;
@@ -90,7 +90,7 @@ for_assign_parenthesis_condition: {
node_version: ">=4"
}
for_assign_parenthesis_step: {
for_assign_parentheses_step: {
input: {
for (; console.log(42); f = a => (a in a));
}
@@ -99,7 +99,7 @@ for_assign_parenthesis_step: {
node_version: ">=4"
}
for_declaration_parenthesis_init: {
for_declaration_parentheses_init: {
input: {
for (var f = a => (a in a); console.log(42););
}
@@ -108,7 +108,7 @@ for_declaration_parenthesis_init: {
node_version: ">=4"
}
for_statement_parenthesis_init: {
for_statement_parentheses_init: {
input: {
for (a => {
a in a;
@@ -276,6 +276,7 @@ drop_arguments: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
console.log(function() {
@@ -694,3 +695,122 @@ issue_4476: {
expect_stdout: "foo bar"
node_version: ">=4"
}
issue_4666: {
input: {
console.log((a => /[0-9]/.test(a))(42));
}
expect_exact: "console.log((a=>/[0-9]/.test(a))(42));"
expect_stdout: "true"
node_version: ">=4"
}
issue_4685_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
new function(f) {
if (f() !== this)
console.log("PASS");
}(() => this);
}
expect: {
new function(f) {
if (f() !== this)
console.log("PASS");
}(() => this);
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4685_2: {
options = {
reduce_vars: true,
unused: true,
}
input: {
new function(f) {
if (f() !== this)
console.log("PASS");
}(() => {
if (console)
return this;
});
}
expect: {
new function(f) {
if (f() !== this)
console.log("PASS");
}(() => {
if (console)
return this;
});
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4687_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
new function() {
console.log(function(f) {
return f() === this;
}(() => this) || "PASS");
}
}
expect: {
new function() {
console.log(function(f) {
return f() === this;
}(() => this) || "PASS");
}
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4687_2: {
options = {
reduce_vars: true,
unused: true,
}
input: {
new function() {
console.log(function(f) {
return f() === this;
}(() => {
if (console)
return this;
}) || "PASS");
}
}
expect: {
new function() {
console.log(function(f) {
return f() === this;
}(() => {
if (console)
return this;
}) || "PASS");
}
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4772: {
input: {
var f = a => (a)
/**/ console.log(f("PASS"));
}
expect_exact: 'var f=a=>a;console.log(f("PASS"));'
expect_stdout: "PASS"
node_version: ">=4"
}

View File

@@ -475,3 +475,200 @@ issue_4521: {
}
expect_stdout: "42"
}
logical_assignments: {
input: {
var a = 42, b = null, c;
a &&= "foo";
b ||= "bar";
c ??= "baz";
console.log(a, b, c);
}
expect_exact: 'var a=42,b=null,c;a&&="foo";b||="bar";c??="baz";console.log(a,b,c);'
expect_stdout: "foo bar baz"
node_version: ">=15"
}
logical_collapse_vars: {
options = {
collapse_vars: true,
}
input: {
var a = "FAIL", b = false;
a = "PASS";
b ??= a;
console.log(a);
}
expect: {
var a = "FAIL", b = false;
a = "PASS";
b ??= a;
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=15"
}
logical_reduce_vars: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = "PASS", b = 42;
b ??= a = "FAIL";
console.log(a);
}
expect: {
var a = "PASS", b = 42;
b ??= a = "FAIL";
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=15"
}
logical_side_effects: {
options = {
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = "PASS", b = 42;
b ??= a = "FAIL";
console.log(a);
}
expect: {
var a = "PASS", b = 42;
b ??= a = "FAIL";
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=15"
}
issue_4815_1: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = "PASS";
42..p &&= a = "FAIL";
console.log(a);
}
expect: {
var a = "PASS";
42..p &&= a = "FAIL";
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=15"
}
issue_4815_2: {
options = {
pure_getters: "strict",
side_effects: true,
}
input: {
var a = "PASS";
42..p &&= a = "FAIL";
console.log(a);
}
expect: {
var a = "PASS";
42..p &&= a = "FAIL";
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=15"
}
issue_4819: {
options = {
comparisons: true,
}
input: {
console.log(void 0 === ([].p &&= 42));
}
expect: {
console.log(void 0 === ([].p &&= 42));
}
expect_stdout: "true"
node_version: ">=15"
}
issue_4827_1: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
A = "FAIL";
var a = A, b = "PASS", c;
c &&= b = a, console.log(b);
}
expect: {
A = "FAIL";
var a = A, b = "PASS", c;
c &&= b = a, console.log(b);
}
expect_stdout: "PASS"
node_version: ">=15"
}
issue_4827_2: {
options = {
collapse_vars: true,
inline: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 0, b = "PASS";
function f(c) {
a++,
c &&= b = a;
}
f();
console.log(b);
}
expect: {
var a = 0, b = "PASS";
a++,
c &&= b = a;
var c;
console.log(b);
}
expect_stdout: "PASS"
node_version: ">=15"
}
issue_4827_3: {
options = {
merge_vars: true,
toplevel: true,
}
input: {
var a = 0, b, c;
a++;
c &&= b = a;
console.log(b);
}
expect: {
var a = 0, b, c;
a++;
c &&= b = a;
console.log(b);
}
expect_stdout: "undefined"
node_version: ">=15"
}

View File

@@ -23,16 +23,34 @@ async_label: {
}
await_await: {
options = {
awaits: true,
side_effects: true,
}
input: {
(async function() {
console.log("PASS");
await await 42;
await await {
then(resolve) {
resolve({
then() {
console.log("PASS");
},
});
},
};
})();
}
expect: {
(async function() {
console.log("PASS");
await await 42;
await {
then(resolve) {
resolve({
then() {
console.log("PASS");
},
});
},
};
})();
}
expect_stdout: "PASS"
@@ -546,7 +564,7 @@ drop_return: {
input: {
(async function(a) {
while (!console);
return console.log(a);
return !console.log(a);
})(42);
}
expect: {
@@ -596,14 +614,14 @@ functions: {
async function b() {
return !!b;
}
var c = async function(c) {
async function c(c) {
return c;
};
}
if (await c(await b(await a()))) {
async function d() {}
async function e() {
return typeof e;
}
var d = async function() {};
var e = async function y() {
return typeof y;
};
var f = async function(f) {
return f;
};
@@ -654,9 +672,9 @@ functions_use_strict: {
async function b() {
return !!b;
}
var c = async function(c) {
async function c(c) {
return c;
};
}
if (await c(await b(await a()))) {
var d = async function() {};
var e = async function y() {
@@ -673,6 +691,54 @@ functions_use_strict: {
node_version: ">=8"
}
functions_anonymous: {
options = {
functions: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var await = async function() {
console.log("PASS");
};
await(await);
}
expect: {
async function await() {
console.log("PASS");
}
await();
}
expect_stdout: "PASS"
node_version: ">=8"
}
functions_inner_var: {
options = {
functions: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var await = function a() {
var a;
console.log(a, a);
};
await(await);
}
expect: {
function await() {
var a;
console.log(a, a);
}
await();
}
expect_stdout: "undefined undefined"
node_version: ">=8"
}
issue_4335_1: {
options = {
inline: true,
@@ -1246,3 +1312,222 @@ issue_4618: {
expect_stdout: "function"
node_version: ">=8"
}
issue_4717: {
options = {
inline: true,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function() {
async function f() {
var a = function() {
await;
}();
return "FAIL";
}
return f();
})().then(console.log).catch(function() {
console.log("PASS");
});
}
expect: {
(async function() {
return function() {
await;
}(), "FAIL";
})().then(console.log).catch(function() {
console.log("PASS");
});
}
expect_stdout: "PASS"
node_version: ">=8"
}
issue_4738_1: {
options = {
awaits: true,
side_effects: true,
}
input: {
(async function() {
await {
then() {
console.log("PASS");
},
};
})();
}
expect: {
(async function() {
await {
then() {
console.log("PASS");
},
};
})();
}
expect_stdout: "PASS"
node_version: ">=8"
}
issue_4738_2: {
options = {
awaits: true,
side_effects: true,
}
input: {
(async function() {
await {
get then() {
console.log("PASS");
},
};
})();
}
expect: {
(async function() {
await {
get then() {
console.log("PASS");
},
};
})();
}
expect_stdout: "PASS"
node_version: ">=8"
}
issue_4738_3: {
options = {
awaits: true,
side_effects: true,
}
input: {
(async function() {
await {
then: function() {
console.log("PASS");
},
};
})();
}
expect: {
(async function() {
await {
then: function() {
console.log("PASS");
},
};
})();
}
expect_stdout: "PASS"
node_version: ">=8"
}
issue_4747: {
options = {
collapse_vars: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(function(a) {
async function f() {
a = "PASS";
null.p += "PASS";
}
f();
return a;
}("FAIL"));
}
expect: {
console.log(function(a) {
(async function() {
a = "PASS";
null.p += "PASS";
})();
return a;
}("FAIL"));
}
expect_stdout: "PASS"
node_version: ">=8"
}
issue_4764_1: {
options = {
side_effects: true,
}
input: {
(async function() {
return {
then() {
console.log("PASS");
},
};
})();
}
expect: {
(async function() {
return {
then() {
console.log("PASS");
},
};
})();
}
expect_stdout: "PASS"
node_version: ">=8"
}
issue_4764_2: {
options = {
arrows: true,
side_effects: true,
}
input: {
(async () => ({
get then() {
console.log("PASS");
},
}))();
}
expect: {
(async () => ({
get then() {
console.log("PASS");
},
}))();
}
expect_stdout: "PASS"
node_version: ">=8"
}
issue_4764_3: {
options = {
side_effects: true,
}
input: {
(async function(o) {
return o;
})({
then() {
console.log("PASS");
},
});
}
expect: {
(async function(o) {
return o;
})({
then() {
console.log("PASS");
},
});
}
expect_stdout: "PASS"
node_version: ">=8"
}

View File

@@ -60,3 +60,33 @@ issue_4590: {
expect_stdout: "PASS"
node_version: ">=10"
}
issue_4801: {
options = {
booleans: true,
collapse_vars: true,
reduce_vars: true,
unused: true,
}
input: {
try {
(function(a) {
A = 42;
a || A;
})(!(0 == 42 >> 0o644n));
} catch (e) {
console.log("PASS");
}
}
expect: {
try {
(function(a) {
0 != (A = 42) >> 0o644n || A;
})();
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
node_version: ">=10"
}

1543
test/compress/classes.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -958,8 +958,7 @@ collapse_vars_misc: {
}
expect: {
function f0(o, a, h) {
var b = 3 - a;
return o.run(b)[7] = h;
return o.run(3 - a)[7] = h;
}
function f1(x) { return 5 - x }
function f2(x) { return foo() / (5 - x) }
@@ -2276,8 +2275,8 @@ var_defs: {
}
expect: {
var f1 = function(x, y) {
var r = x + y, z = r * r - r, b = 7;
console.log(z + b);
var r = x + y;
console.log(r * r - r + 7);
};
f1("1", 0);
}
@@ -2869,7 +2868,7 @@ lvalues_def: {
expect_stdout: true
}
compound_assignment: {
compound_assignment_1: {
options = {
collapse_vars: true,
}
@@ -2888,6 +2887,23 @@ compound_assignment: {
expect_stdout: "4"
}
compound_assignment_2: {
options = {
collapse_vars: true,
}
input: {
var a;
a = 1;
for (a += a + 2; console.log(a););
}
expect: {
var a;
a = 1;
for (a += a + 2; console.log(a););
}
expect_stdout: "4"
}
issue_2187_1: {
options = {
collapse_vars: true,
@@ -2907,8 +2923,7 @@ issue_2187_1: {
var a = 1;
!function(foo) {
foo();
var a = 2;
console.log(a);
console.log(2);
}(function() {
console.log(a);
});
@@ -6961,8 +6976,7 @@ sequence_in_iife_2: {
}
expect: {
var a = "foo", b = 42;
b = a;
console.log(a, b);
console.log(a, b = a);
}
expect_stdout: "foo foo"
}
@@ -8756,3 +8770,165 @@ issue_4586_2: {
}
expect_stdout: "PASS"
}
issue_4732_1: {
options = {
booleans: true,
collapse_vars: true,
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
var a = 0;
(function(b) {
var b = a++;
var c = b ? b && console.log("PASS") : 0;
})(a++);
}
expect: {
var a = 0;
(function(b) {
(b = a++) && (b && console.log("PASS"));
})(a++);
}
expect_stdout: "PASS"
}
issue_4732_2: {
options = {
collapse_vars: true,
conditionals: true,
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
var a = 0;
(function(b) {
var b = a++;
var c = b ? b && console.log("PASS") : 0;
})(a++);
}
expect: {
var a = 0;
(function(b) {
(b = a++) && b && console.log("PASS");
})(a++);
}
expect_stdout: "PASS"
}
dot_in_try: {
options = {
collapse_vars: true,
}
input: {
var o, a = 6, b = 7, c;
try {
c = a * b;
o.p(c);
} catch (e) {
console.log(c);
}
}
expect: {
var o, a = 6, b = 7, c;
try {
c = a * b;
o.p(c);
} catch (e) {
console.log(c);
}
}
expect_stdout: "42"
}
dot_non_local: {
options = {
collapse_vars: true,
}
input: {
var o, a = 6, b = 7, c;
function f() {
c = a * b;
o.p(c);
}
try {
f();
} catch (e) {
console.log(c);
}
}
expect: {
var o, a = 6, b = 7, c;
function f() {
c = a * b;
o.p(c);
}
try {
f();
} catch (e) {
console.log(c);
}
}
expect_stdout: "42"
}
issue_4806: {
options = {
collapse_vars: true,
}
input: {
var a, o = {
f: function() {
console.log(this === o ? "FAIL" : "PASS");
},
};
(a = 42, o.f)(42);
}
expect: {
var a, o = {
f: function() {
console.log(this === o ? "FAIL" : "PASS");
},
};
(0, o.f)(a = 42);
}
expect_stdout: "PASS"
}
issue_4852: {
options = {
collapse_vars: true,
}
input: {
var a = "PASS";
(function(b) {
switch (b = a) {
case 42:
try {
console;
} catch (b) {
b.p;
}
case console.log(b):
}
})("FAIL");
}
expect: {
var a = "PASS";
(function(b) {
switch (a) {
case 42:
try {
console;
} catch (b) {
b.p;
}
case console.log(a):
}
})("FAIL");
}
expect_stdout: "PASS"
}

View File

@@ -1861,3 +1861,39 @@ issue_3808_2: {
}
expect_stdout: " PASS"
}
object_super: {
options = {
conditionals: true,
}
input: {
Object.setPrototypeOf({
f(a) {
a ? this.g("FAIL") : super.g("FAIL");
},
g(b) {
console.log(b);
},
}, {
g() {
console.log("PASS");
},
}).f();
}
expect: {
Object.setPrototypeOf({
f(a) {
a ? this.g("FAIL") : super.g("FAIL");
},
g(b) {
console.log(b);
},
}, {
g() {
console.log("PASS");
},
}).f();
}
expect_stdout: "PASS"
node_version: ">=4"
}

View File

@@ -1434,3 +1434,104 @@ issue_4527: {
}
expect_stdout: "aaaa"
}
issue_4689: {
options = {
sequences: true,
}
input: {
"use strict";
var a = "PASS";
console.log(a);
for (const a in 42);
}
expect: {
"use strict";
var a = "PASS";
console.log(a);
for (const a in 42);
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4691: {
options = {
if_return: true,
toplevel: true,
}
input: {
function A() {}
A.prototype.f = function() {
if (!this)
return;
const a = "PA";
function g(b) {
h(a + b);
}
[ "SS" ].forEach(function(c) {
g(c);
});
};
function h(d) {
console.log(d);
}
new A().f();
}
expect: {
function A() {}
A.prototype.f = function() {
if (this) {
const a = "PA";
[ "SS" ].forEach(function(c) {
g(c);
});
function g(b) {
h(a + b);
}
}
};
function h(d) {
console.log(d);
}
new A().f();
}
expect_stdout: "PASS"
}
issue_4848: {
options = {
if_return: true,
}
input: {
function f(a) {
a(function() {
console.log(b);
});
if (!console)
return;
const b = "PASS";
}
var g;
f(function(h) {
g = h;
});
g();
}
expect: {
function f(a) {
a(function() {
console.log(b);
});
if (!console)
return;
const b = "PASS";
}
var g;
f(function(h) {
g = h;
});
g();
}
expect_stdout: "PASS"
}

View File

@@ -53,8 +53,10 @@ dead_code_2_should_warn: {
g();
x = 10;
throw new Error("foo");
var x;
function g(){};
{
var x;
function g(){};
}
}
f();
}
@@ -62,7 +64,6 @@ dead_code_2_should_warn: {
expect_warnings: [
"WARN: Dropping unreachable code [test/compress/dead-code.js:8,12]",
]
node_version: "<=4"
}
dead_code_constant_boolean_should_warn_more: {
@@ -88,8 +89,10 @@ dead_code_constant_boolean_should_warn_more: {
bar();
}
expect: {
var foo;
function bar() {}
{
var foo;
function bar() {}
}
// nothing for the while
// as for the for, it should keep:
var x = 10, y;

View File

@@ -473,7 +473,7 @@ inline_side_effects_2: {
}
expect: {
var a = 42;
[ 0[0] = --a ] = [ console ];
[ [].e = --a ] = [ console ];
console.log(a);
}
expect_stdout: "42"
@@ -1477,7 +1477,7 @@ issue_4502_4: {
(function(a, b = console.log("FAIL")) {})(..."" + console.log(42));
}
expect: {
[ , 0[0] = console.log("FAIL") ] = [ ..."" + console.log(42) ];
[ , [].e = console.log("FAIL") ] = [ ..."" + console.log(42) ];
}
expect_stdout: "42"
node_version: ">=6"
@@ -1661,3 +1661,23 @@ issue_4588_2_evaluate: {
expect_stdout: "1"
node_version: ">=6"
}
issue_4817: {
options = {
ie8: true,
inline: true,
unused: true,
}
input: {
(function f(a = console.log(typeof f)) {
return 42;
})();
}
expect: {
(function f(a = console.log(typeof f)) {
return 42;
})();
}
expect_stdout: "function"
node_version: ">=6"
}

View File

@@ -3251,3 +3251,140 @@ issue_4558_2: {
}
expect_stdout: "PASS"
}
issue_4662: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 0;
function f(b, c) {
console.log(b, c);
}
f(++a, a = a, a);
}
expect: {
var a = 0;
(function(b, c) {
console.log(b, c);
})(++a, a = 1);
}
expect_stdout: "1 1"
}
issue_4806_1: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
O = {
f: function() {
console.log(this === O ? "FAIL" : "PASS");
},
};
var a;
(a = 42, O.f)();
a;
}
expect: {
O = {
f: function() {
console.log(this === O ? "FAIL" : "PASS");
},
};
(0, O.f)();
42;
}
expect_stdout: "PASS"
}
issue_4806_2: {
options = {
sequences: true,
toplevel: true,
unused: true,
}
input: {
O = {
f: function() {
console.log(this === O ? "FAIL" : "PASS");
},
};
var a;
(a = 42, O.f)();
a;
}
expect: {
O = {
f: function() {
console.log(this === O ? "FAIL" : "PASS");
},
},
(0, O.f)();
}
expect_stdout: "PASS"
}
issue_4806_3: {
options = {
side_effects: true,
toplevel: true,
unused: true,
}
input: {
O = {
f: function() {
console.log(this === O ? "FAIL" : "PASS");
},
};
var a;
(a = 42, O.f)();
a;
}
expect: {
O = {
f: function() {
console.log(this === O ? "FAIL" : "PASS");
},
};
(0, O.f)();
}
expect_stdout: "PASS"
}
issue_4834: {
options = {
inline: true,
keep_fargs: false,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
try {
new function(a, b) {
b;
b.p;
}(42);
} catch (e) {
console.log("PASS");
}
}
expect: {
try {
void b.p;
} catch (e) {
console.log("PASS");
}
var b;
}
expect_stdout: "PASS"
}

View File

@@ -399,18 +399,18 @@ unsafe_object_accessor: {
function f() {
var a = {
get b() {},
set b() {}
set b(v) {},
};
return {a:a};
return { a: a };
}
}
expect: {
function f() {
var a = {
get b() {},
set b() {}
set b(v) {},
};
return {a:a};
return { a: a };
}
}
}
@@ -847,6 +847,8 @@ unsafe_charAt_noop: {
unsafe: true,
}
input: {
s = "foo";
x = 42;
console.log(
s.charAt(0),
"string".charAt(x),
@@ -854,12 +856,15 @@ unsafe_charAt_noop: {
);
}
expect: {
s = "foo";
x = 42;
console.log(
s[0],
"string"[0 | x],
(typeof x)[0]
s[0] || "",
"string"[0 | x] || "",
(typeof x)[0] || ""
);
}
expect_stdout: "f n"
}
issue_1649: {

View File

@@ -43,6 +43,28 @@ await: {
node_version: ">=8"
}
assignment_1: {
input: {
var a = 2;
a **= 5;
console.log(a);
}
expect_exact: "var a=2;a**=5;console.log(a);"
expect_stdout: "32"
node_version: ">=8"
}
assignment_2: {
input: {
var a = 8n;
a **= a;
console.log(a);
}
expect_exact: "var a=8n;a**=a;console.log(a);"
expect_stdout: "16777216n"
node_version: ">=10"
}
evaluate: {
options = {
evaluate: true,
@@ -56,3 +78,70 @@ evaluate: {
expect_stdout: "5"
node_version: ">=8"
}
issue_4664: {
options = {
collapse_vars: true,
evaluate: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function f() {
new function(a) {
console.log(typeof f, a, typeof this);
}((A = 0, (NaN ^ 1) * 2 ** 30), 0);
}
f();
}
expect: {
(function f() {
new function(a) {
console.log(typeof f, 1073741824, typeof this);
}(A = 0);
})();
}
expect_stdout: "function 1073741824 object"
node_version: ">=8"
}
issue_4715: {
options = {
evaluate: true,
}
input: {
A = 1;
console.log((-0) ** A + 0);
console.log((-0) ** A - 0);
console.log((-0) ** A * 1);
console.log((-0) ** A / 1);
console.log(Math.pow(-0, A) + 0);
console.log(Math.pow(-0, A) - 0);
console.log(Math.pow(-0, A) * 1);
console.log(Math.pow(-0, A) / 1);
}
expect: {
A = 1;
console.log((-0) ** A + 0);
console.log((-0) ** A);
console.log((-0) ** A * 1);
console.log((-0) ** A);
console.log(Math.pow(-0, A) + 0);
console.log(+Math.pow(-0, A));
console.log(+Math.pow(-0, A));
console.log(+Math.pow(-0, A));
}
expect_stdout: [
"0",
"-0",
"-0",
"-0",
"0",
"-0",
"-0",
"-0",
]
node_version: ">=8"
}

491
test/compress/exports.js Normal file
View File

@@ -0,0 +1,491 @@
refs: {
input: {
export {};
export { a, b as B, c as case, d as default };
}
expect_exact: "export{};export{a,b as B,c as case,d as default};"
}
var_defs: {
input: {
export const a = 1;
export let b = 2, c = 3;
export var { d, e: [] } = f;
}
expect_exact: "export const a=1;export let b=2,c=3;export var{d,e:[]}=f;"
}
defuns: {
input: {
export class A {}
export function e() {}
export function* f(a) {}
export async function g(b, c) {}
export async function* h({}, ...[]) {}
}
expect_exact: "export class A{}export function e(){}export function*f(a){}export async function g(b,c){}export async function*h({},...[]){}"
}
defaults: {
input: {
export default 42;
export default async;
export default (x, y) => x * x;
export default class {};
export default function*(a, b) {};
export default async function f({ c }, ...[ d ]) {};
}
expect_exact: "export default 42;export default async;export default(x,y)=>x*x;export default class{}export default function*(a,b){}export default async function f({c},...[d]){}"
}
defaults_parentheses_1: {
input: {
export default function() {
console.log("FAIL");
}(console.log("PASS"));
}
expect_exact: 'export default function(){console.log("FAIL")}console.log("PASS");'
}
defaults_parentheses_2: {
input: {
export default (async function() {
console.log("PASS");
})();
}
expect_exact: 'export default(async function(){console.log("PASS")})();'
}
defaults_parentheses_3: {
input: {
export default (42, "PASS");
}
expect_exact: 'export default(42,"PASS");'
}
defaults_parentheses_4: {
input: {
export default (function f() {});
}
expect_exact: "export default(function f(){});"
}
defaults_parentheses_5: {
input: {
export default (function(a) {
console.log(a[0]);
}`PASS`);
}
expect_exact: "export default(function(a){console.log(a[0])})`PASS`;"
}
defaults_parentheses_6: {
options = {
conditionals: true,
}
input: {
export default !function() {
while (!console);
}() ? "PASS" : "FAIL";
}
expect_exact: 'export default(function(){while(!console);})()?"FAIL":"PASS";'
}
foreign: {
input: {
export * from "foo";
export {} from "bar";
export * as a from "baz";
export { default } from "moo";
export { b, c as case, default as delete, d } from "moz";
}
expect_exact: 'export*from"foo";export{}from"bar";export*as a from"baz";export{default}from"moo";export{b,c as case,default as delete,d}from"moz";'
}
same_quotes: {
beautify = {
beautify: true,
quote_style: 3,
}
input: {
export * from 'foo';
export {} from "bar";
}
expect_exact: [
"export * from 'foo';",
"",
'export {} from "bar";',
]
}
drop_unused: {
options = {
toplevel: true,
unused: true,
}
input: {
export default 42;
export default (x, y) => x * x;
export default class A extends B { get p() { h() } }
export default function*(a, b) {}
export default async function f({ c }, ...[ d ]) {}
export var e;
export function g(x, [ y ], ...z) {}
function h() {}
}
expect: {
export default 42;
export default (x, y) => x * x;
export default class extends B { get p() { h() } }
export default function*(a, b) {}
export default async function({}) {}
export var e;
export function g(x, []) {}
function h() {}
}
}
mangle: {
rename = false
mangle = {
toplevel: true,
}
input: {
const a = 42;
export let b, { foo: c } = a;
export function f(d, { [b]: e }) {
d(e, f);
}
export default a;
export default async function g(x, ...{ [c]: y }) {
(await x)(g, y);
}
}
expect: {
const t = 42;
export let b, { foo: c } = t;
export function f(t, { [b]: o }) {
t(o, f);
}
export default t;
export default async function e(t, ...{ [c]: o}) {
(await t)(e, o);
}
}
}
mangle_rename: {
rename = true
mangle = {
toplevel: true,
}
input: {
const a = 42;
export let b, { foo: c } = a;
export function f(d, { [b]: e }) {
d(e, f);
}
export default a;
export default async function g(x, ...{ [c]: y }) {
(await x)(g, y);
}
}
expect: {
const t = 42;
export let b, { foo: c } = t;
export function f(t, { [b]: o }) {
t(o, f);
}
export default t;
export default async function e(t, ...{ [c]: o}) {
(await t)(e, o);
}
}
}
hoist_exports_1: {
options = {
hoist_exports: true,
}
input: {
export { a };
export var b;
export function f() {}
}
expect: {
var b;
function f() {}
export { a, b, f };
}
}
hoist_exports_2: {
options = {
evaluate: true,
hoist_exports: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
mangle = {
toplevel: true,
}
input: {
const a = 42;
export let bbb, { foo: ccc } = a;
export function fff(d, { [bbb]: e }) {
d(e, fff);
}
export default a;
export default async function g(x, ...{ [ccc]: y }) {
(await x)(g, y);
}
}
expect: {
let e, { foo: a } = 42;
function f(t, { [e]: o }) {
t(o, f);
}
export default 42;
export default async function n(t, ...{ [a]: o }) {
(await t)(n, o);
};
export { e as bbb, a as ccc, f as fff };
}
}
hoist_vars: {
options = {
hoist_vars: true,
}
input: {
var a;
export var b = 42;
}
expect: {
var a;
export var b = 42;
}
}
keep_return_values: {
options = {
booleans: true,
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
export default function() {
return [];
}
export default function f() {
return null;
}
}
expect: {
export default function() {
return [];
}
export default function f() {
return null;
}
}
}
in_use: {
options = {
pure_getters: "strict",
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
export function f() {}
f.prototype.p = 42;
}
expect: {
export function f() {}
f.prototype.p = 42;
}
}
in_use_default: {
options = {
pure_getters: "strict",
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
export default function f() {}
f.prototype.p = 42;
}
expect: {
export default function f() {}
f.prototype.p = 42;
}
}
single_use: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
export function f() {
console.log("PASS");
}
f();
}
expect: {
export function f() {
console.log("PASS");
}
f();
}
}
single_use_default: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
export default function f() {
console.log("PASS");
}
f();
}
expect: {
export default function f() {
console.log("PASS");
}
f();
}
}
single_use_class: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
export class A {}
A.prototype.p = "PASS";
}
expect: {
export class A {}
A.prototype.p = "PASS";
}
}
single_use_class_default: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
export default class A {}
A.prototype.p = "PASS";
}
expect: {
export default class A {}
A.prototype.p = "PASS";
}
}
hoist_funs: {
options = {
hoist_funs: true,
}
input: {
export function f() {}
export default async function* g() {}
}
expect_exact: "export function f(){}export default async function*g(){}"
}
issue_4742_join_vars_1: {
options = {
join_vars: true,
}
input: {
var a = 42;
export var a;
}
expect: {
var a = 42;
export var a;
}
}
issue_4742_join_vars_2: {
options = {
join_vars: true,
}
input: {
export var a = "foo";
var b;
b = "bar";
}
expect: {
export var a = "foo";
var b, b = "bar";
}
}
issue_4742_unused_1: {
options = {
unused: true,
}
input: {
var a = 42;
export var a;
}
expect: {
var a = 42;
export var a;
}
}
issue_4742_unused_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
export var a = "foo";
var a = "bar";
}
expect: {
export var a = "foo";
a = "bar";
}
}
issue_4761: {
input: {
export default "function" == 42;
}
expect_exact: 'export default"function"==42;'
}
issue_4766: {
options = {
unused: true,
}
input: {
var a = "foo";
export var a = "bar";
}
expect: {
var a = "foo";
export var a = "bar";
}
}

View File

@@ -2053,7 +2053,7 @@ issue_2898: {
expect_stdout: "2"
}
deduplicate_parenthesis: {
deduplicate_parentheses: {
input: {
({}).a = b;
(({}).a = b)();
@@ -2492,14 +2492,14 @@ issue_3297_3: {
input: {
function function1(session) {
var public = {
processBulk: processBulk
processBulk: processBulk,
};
return public;
function processBulk(bulk) {
var subparam1 = session();
function processOne(param1) {
var param2 = {
subparam1: subparam1
subparam1: subparam1,
};
doProcessOne({
param1: param1,
@@ -2525,18 +2525,18 @@ issue_3297_3: {
return {
processBulk: function n(o) {
var r, t, u = c();
o && 0 < o.length && (r = {
param1: o.shift(),
param2: {
subparam1: u
}
},
o && 0 < o.length && (r = o.shift(),
t = function() {
n(o);
},
console.log(JSON.stringify(r)),
console.log(JSON.stringify({
param1: r,
param2: {
subparam1: u,
},
})),
t());
}
},
};
}
function1(function() {
@@ -2751,17 +2751,17 @@ functions: {
function b() {
return !!b;
}
var c = function(c) {
function c(c) {
return c;
};
}
if (c(b(a()))) {
function d() {}
function e() {
return typeof e;
}
var f = function(f) {
function f(f) {
return f;
};
}
console.log(a(d()), b(e()), c(f(42)), typeof d, e(), typeof f);
}
}();
@@ -2808,9 +2808,9 @@ functions_use_strict: {
function b() {
return !!b;
}
var c = function(c) {
function c(c) {
return c;
};
}
if (c(b(a()))) {
var d = function() {};
var e = function y() {
@@ -2826,6 +2826,30 @@ functions_use_strict: {
expect_stdout: "a true 42 function function function"
}
functions_inner_var: {
options = {
functions: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = function() {
var a;
console.log(a, a);
};
a(a);
}
expect: {
function a() {
var a;
console.log(a, a);
}
a();
}
expect_stdout: "undefined undefined"
}
issue_2437: {
options = {
collapse_vars: true,
@@ -3322,7 +3346,9 @@ issue_3506_1: {
}
expect: {
var a = "FAIL";
a && (a = "PASS");
!function(b) {
b && (a = "PASS");
}(a);
console.log(a);
}
expect_stdout: "PASS"
@@ -5016,9 +5042,12 @@ catch_no_argname: {
try {
throw a;
} catch {
console.log(a, a, a);
function g() {
return a;
}
console.log(a, a, g());
}
console.log(a, a, a);
console.log(a, a, g());
}
expect_stdout: [
"PASS PASS PASS",
@@ -5407,3 +5436,588 @@ issue_4612_4: {
}
expect_stdout: "undefined"
}
issue_4655: {
options = {
functions: true,
loops: true,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
(function f() {
while (console.log("PASS")) {
var g = function() {};
for (var a in g)
g();
}
})();
}
expect: {
(function() {
for (; console.log("PASS");) {
function g() {};
}
})();
}
expect_stdout: "PASS"
}
issue_4659_1: {
options = {
inline: true,
reduce_vars: true,
}
input: {
var a = 0;
(function() {
function f() {
return a++;
}
(function() {
f && f();
(function() {
var a = console && a;
})();
})();
})();
console.log(a);
}
expect: {
var a = 0;
(function() {
function f() {
return a++;
}
(function() {
f && a++;
(function() {
var a = console && a;
})();
})();
})();
console.log(a);
}
expect_stdout: "1"
}
issue_4659_2: {
options = {
inline: true,
reduce_vars: true,
}
input: {
var a = 0;
(function() {
function f() {
return a++;
}
(function() {
(function() {
f && f();
})();
(function() {
var a = console && a;
})();
})();
})();
console.log(a);
}
expect: {
var a = 0;
(function() {
function f() {
return a++;
}
(function() {
void (f && a++);
(function() {
var a = console && a;
})();
})();
})();
console.log(a);
}
expect_stdout: "1"
}
issue_4659_3: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
input: {
var a = 0;
(function() {
function f() {
return a++;
}
(function() {
function g() {
while (!console);
}
g(f && f());
(function() {
var a = console && a;
})();
})();
})();
console.log(a);
}
expect: {
var a = 0;
(function() {
function f() {
return a++;
}
(function() {
(function() {
while (!console);
})(f && a++);
(function() {
var a = console && a;
})();
})();
})();
console.log(a);
}
expect_stdout: "1"
}
block_scope_1: {
input: {
console.log(typeof f);
function f() {}
}
expect: {
console.log(typeof f);
function f() {}
}
expect_stdout: "function"
}
block_scope_1_compress: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
typeofs: true,
unused: true,
}
input: {
console.log(typeof f);
function f() {}
}
expect: {
console.log("function");
}
expect_stdout: "function"
}
block_scope_2: {
input: {
{
console.log(typeof f);
}
function f() {}
}
expect: {
console.log(typeof f);
function f() {}
}
expect_stdout: "function"
}
block_scope_2_compress: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
typeofs: true,
unused: true,
}
input: {
{
console.log(typeof f);
}
function f() {}
}
expect: {
console.log("function");
}
expect_stdout: "function"
}
block_scope_3: {
input: {
console.log(typeof f);
{
function f() {}
}
}
expect: {
console.log(typeof f);
{
function f() {}
}
}
expect_stdout: true
}
block_scope_3_compress: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
typeofs: true,
unused: true,
}
input: {
console.log(typeof f);
{
function f() {}
}
}
expect: {
console.log(typeof f);
{
function f() {}
}
}
expect_stdout: true
}
block_scope_4: {
input: {
{
console.log(typeof f);
function f() {}
}
}
expect: {
console.log(typeof f);
function f() {}
}
expect_stdout: "function"
}
block_scope_4_compress: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
typeofs: true,
unused: true,
}
input: {
{
console.log(typeof f);
function f() {}
}
}
expect: {
console.log("function");
}
expect_stdout: "function"
}
issue_4725_1: {
options = {
inline: true,
}
input: {
var o = {
f() {
return function g() {
return g;
}();
}
};
console.log(typeof o.f());
}
expect: {
var o = {
f() {
return function g() {
return g;
}();
}
};
console.log(typeof o.f());
}
expect_stdout: "function"
node_version: ">=4"
}
issue_4725_2: {
options = {
inline: true,
}
input: {
var o = {
f() {
return function() {
while (console.log("PASS"));
}();
}
};
o.f();
}
expect: {
var o = {
f() {
while (console.log("PASS"));
}
};
o.f();
}
expect_stdout: "PASS"
node_version: ">=4"
}
new_target_1: {
input: {
new function f() {
console.log(new.target === f);
}();
console.log(function() {
return new.target;
}());
}
expect: {
new function f() {
console.log(new.target === f);
}();
console.log(function() {
return new.target;
}());
}
expect_stdout: [
"true",
"undefined",
]
node_version: ">=6"
}
new_target_2: {
input: {
new function(a) {
if (!new.target)
console.log("FAIL");
else if (a)
console.log("PASS");
else
new new.target(new.target.length);
}();
}
expect: {
new function(a) {
if (!new.target)
console.log("FAIL");
else if (a)
console.log("PASS");
else
new new.target(new.target.length);
}();
}
expect_stdout: "PASS"
node_version: ">=6"
}
new_target_collapse_vars: {
options = {
collapse_vars: true,
unused: true,
}
input: {
new function(a) {
if (a)
console.log("PASS");
else
new new.target(new.target.length);
}(0);
}
expect: {
new function(a) {
if (a)
console.log("PASS");
else
new new.target(new.target.length);
}(0);
}
expect_stdout: "PASS"
node_version: ">=6"
}
new_target_delete: {
options = {
evaluate: true,
}
input: {
new function() {
console.log(delete new.target);
}();
}
expect: {
new function() {
console.log(delete new.target);
}();
}
expect_stdout: true
node_version: ">=6"
}
new_target_reduce_vars: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
new function(a) {
if (a)
console.log("PASS");
else
new new.target(new.target.length);
}(0);
}
expect: {
new function(a) {
if (a)
console.log("PASS");
else
new new.target(new.target.length);
}(0);
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_4753_1: {
options = {
inline: true,
toplevel: true,
}
input: {
for (var i in [ 1, 2 ])
(function() {
function f() {}
f && console.log(f.p ^= 42);
})();
}
expect: {
for (var i in [ 1, 2 ])
f = function() {},
void (f && console.log(f.p ^= 42));
var f;
}
expect_stdout: [
"42",
"42",
]
}
issue_4753_2: {
options = {
inline: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
do {
(function() {
var a = f();
function f() {
return "PASS";
}
f;
function g() {
console.log(a);
}
g();
})();
} while (0);
}
expect: {
do {
f = function() {
return "PASS";
},
a = void 0,
a = f(),
console.log(a);
} while (0);
var f, a;
}
expect_stdout: "PASS"
}
issue_4788: {
options = {
evaluate: true,
functions: true,
keep_fnames: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
var a = function g() {
if (0) {
var g = 42;
f();
}
g || console.log("PASS");
};
a(a);
}
f();
}
expect: {
(function f() {
function a() {
if (0) {
var g = 42;
f();
}
g || console.log("PASS");
}
a();
})();
}
expect_stdout: "PASS"
}
issue_4823: {
options = {
functions: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(typeof function() {
{
function f() {}
var arguments = f();
function g() {}
var arguments = g;
}
return f && arguments;
}());
}
expect: {
console.log(typeof function() {
{
function f() {}
arguments = f();
var arguments = function() {};
}
return f && arguments;
}());
}
expect_stdout: "function"
}

View File

@@ -1068,3 +1068,29 @@ issue_4023: {
}
expect_stdout: "true"
}
object_super: {
options = {
hoist_props: true,
reduce_vars: true,
toplevel: true,
}
input: {
var o = {
f(a) {
return a ? console.log("PASS") : super.log("PASS");
},
};
o.f(42);
}
expect: {
var o = {
f(a) {
return a ? console.log("PASS") : super.log("PASS");
},
};
o.f(42);
}
expect_stdout: "PASS"
node_version: ">=4"
}

View File

@@ -152,7 +152,7 @@ issue_4487: {
}
expect: {
function a() {
var a = console.log(typeof a);
var f = console.log(typeof f);
}
a();
}
@@ -207,3 +207,61 @@ issue_4517: {
}
expect_stdout: "2boolean"
}
issue_4736: {
options = {
collapse_vars: true,
evaluate: true,
hoist_vars: true,
merge_vars: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a;
function f() {
(function g() {
var b = (a = 0, 1 << 30);
var c = (a = 0, console.log(b));
var d = c;
})(f);
}
f();
}
expect: {
(function() {
(function() {
0,
console.log(1073741824);
})();
})();
}
expect_stdout: "1073741824"
}
issue_4839: {
options = {
evaluate: true,
hoist_vars: true,
keep_fargs: false,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = function(a, b) {
return b && b;
}("foo");
for (var k in o)
throw "FAIL";
console.log("PASS");
}
expect: {
var k, o = void 0;
for (k in o)
throw "FAIL";
console.log("PASS");
}
expect_stdout: "PASS"
}

View File

@@ -2919,3 +2919,29 @@ issue_4568: {
}
expect_stdout: "undefined 1"
}
issue_4729: {
options = {
ie8: true,
pure_getters: true,
toplevel: true,
unused: true,
}
input: {
try {
f;
} catch (e) {
var a = a && a[function f() {}];
console.log("PASS");
}
}
expect: {
try {
f;
} catch (e) {
(function f() {});
console.log("PASS");
}
}
expect_stdout: "PASS"
}

211
test/compress/imports.js Normal file
View File

@@ -0,0 +1,211 @@
nought: {
input: {
import "foo";
}
expect_exact: 'import"foo";'
}
default_only: {
input: {
import foo from "bar";
}
expect_exact: 'import foo from"bar";'
}
all_only: {
input: {
import * as foo from "bar";
}
expect_exact: 'import*as foo from"bar";'
}
keys_only: {
input: {
import { as as foo, bar, delete as baz } from "moo";
}
expect_exact: 'import{as as foo,bar,delete as baz}from"moo";'
}
default_all: {
input: {
import foo, * as bar from "baz";
}
expect_exact: 'import foo,*as bar from"baz";'
}
default_keys: {
input: {
import foo, { bar } from "baz";
}
expect_exact: 'import foo,{bar}from"baz";'
}
dynamic: {
input: {
(async a => await import(a))("foo").then(bar);
}
expect_exact: '(async a=>await import(a))("foo").then(bar);'
}
dynamic_nought: {
input: {
import(foo);
}
expect_exact: "import(foo);"
}
import_meta_1: {
input: {
console.log(import.meta, import.meta.url);
}
expect_exact: "console.log(import.meta,import.meta.url);"
}
import_meta_2: {
input: {
import.meta.url.split("/").forEach(function(part, index) {
console.log(index, part);
});
}
expect_exact: 'import.meta.url.split("/").forEach(function(part,index){console.log(index,part)});'
}
same_quotes: {
beautify = {
beautify: true,
quote_style: 3,
}
input: {
import 'foo';
import "bar";
}
expect_exact: [
"import 'foo';",
"",
'import "bar";',
]
}
drop_unused: {
options = {
imports: true,
toplevel: true,
unused: true,
}
input: {
import a, * as b from "foo";
import { c, bar as d } from "baz";
console.log(c);
}
expect: {
import "foo";
import { c as c } from "baz";
console.log(c);
}
}
mangle: {
rename = false
mangle = {
toplevel: true,
}
input: {
import foo, { bar } from "baz";
consoe.log(moo);
import * as moo from "moz";
}
expect: {
import o, { bar as m } from "baz";
consoe.log(r);
import * as r from "moz";
}
}
rename_mangle: {
rename = true
mangle = {
toplevel: true,
}
input: {
import foo, { bar } from "baz";
consoe.log(moo);
import * as moo from "moz";
}
expect: {
import o, { bar as m } from "baz";
consoe.log(r);
import * as r from "moz";
}
}
keep_ref: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
import foo from "bar";
foo();
}
expect: {
import foo from "bar";
foo();
}
}
forbid_merge: {
options = {
merge_vars: true,
toplevel: true,
}
input: {
import A from "foo";
export default class extends A {}
var f = () => () => {};
f();
f();
}
expect: {
import A from "foo";
export default class extends A {}
var f = () => () => {};
f();
f();
}
}
issue_4708_1: {
options = {
imports: true,
toplevel: true,
unused: true,
}
input: {
var a;
import a from "foo";
}
expect: {
var a;
import a from "foo";
}
}
issue_4708_2: {
options = {
imports: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a;
console.log(a);
import a from "foo";
}
expect: {
var a;
console.log(a);
import a from "foo";
}
}

View File

@@ -2,61 +2,77 @@ keep_name_of_getter: {
options = {
unused: true,
}
input: { a = { get foo () {} } }
expect: { a = { get foo () {} } }
input: {
a = {
get foo() {},
};
}
expect: {
a = {
get foo() {},
};
}
}
keep_name_of_setter: {
options = {
unused: true,
}
input: { a = { set foo () {} } }
expect: { a = { set foo () {} } }
input: {
a = {
set foo(v) {},
};
}
expect: {
a = {
set foo(v) {},
};
}
}
setter_with_operator_keys: {
input: {
var tokenCodes = {
get instanceof(){
var tokenCodes = {
get instanceof() {
return test0;
},
set instanceof(value){
set instanceof(value) {
test0 = value;
},
set typeof(value){
set typeof(value) {
test1 = value;
},
get typeof(){
get typeof() {
return test1;
},
set else(value){
set else(value) {
test2 = value;
},
get else(){
get else() {
return test2;
}
},
};
}
expect: {
var tokenCodes = {
get instanceof(){
var tokenCodes = {
get instanceof() {
return test0;
},
set instanceof(value){
set instanceof(value) {
test0 = value;
},
set typeof(value){
set typeof(value) {
test1 = value;
},
get typeof(){
get typeof() {
return test1;
},
set else(value){
set else(value) {
test2 = value;
},
get else(){
get else() {
return test2;
}
},
};
}
}
}

View File

@@ -4,7 +4,7 @@ mangle_keep_fnames_false: {
keep_fnames: true,
}
mangle = {
keep_fnames : false,
keep_fnames: false,
}
input: {
"use strict";
@@ -30,7 +30,7 @@ mangle_keep_fnames_true: {
keep_fnames: true,
}
mangle = {
keep_fnames : true,
keep_fnames: true,
}
input: {
"use strict";

View File

@@ -1,5 +1,6 @@
pure_function_calls: {
options = {
annotations: true,
booleans: true,
comparisons: true,
conditionals: true,
@@ -60,6 +61,7 @@ pure_function_calls: {
pure_function_calls_toplevel: {
options = {
annotations: true,
booleans: true,
comparisons: true,
conditionals: true,
@@ -126,6 +128,7 @@ pure_function_calls_toplevel: {
should_warn: {
options = {
annotations: true,
booleans: true,
conditionals: true,
evaluate: true,

View File

@@ -426,6 +426,7 @@ wrap_iife_in_return_call: {
pure_annotation_1: {
options = {
annotations: true,
inline: true,
side_effects: true,
}
@@ -439,6 +440,7 @@ pure_annotation_1: {
pure_annotation_2: {
options = {
annotations: true,
collapse_vars: true,
inline: true,
side_effects: true,
@@ -463,15 +465,19 @@ drop_fargs: {
var a = 1;
!function(a_1) {
a++;
}(a++ + (a && a.var));
}(a++ + (a && console.log(a)));
console.log(a);
}
expect: {
var a = 1;
++a && a.var, a++;
++a && console.log(a),
a++;
console.log(a);
}
expect_stdout: "3"
expect_stdout: [
"2",
"3",
]
}
keep_fargs: {
@@ -486,13 +492,17 @@ keep_fargs: {
var a = 1;
!function(a_1) {
a++;
}(a++ + (a && a.var));
}(a++ + (a && console.log(a)));
console.log(a);
}
expect: {
var a = 1;
++a && a.var, a++;
++a && console.log(a),
a++;
console.log(a);
}
expect_stdout: "3"
expect_stdout: [
"2",
"3",
]
}

View File

@@ -438,7 +438,7 @@ issue_2506: {
function f0(bar) {
(function() {
(function() {
if (false <= NaN & this >> 1 >= 0)
if (false <= 0/0 & this >> 1 >= 0)
c++;
})(c++);
})();

View File

@@ -359,7 +359,7 @@ reduce_block_2_toplevel: {
node_version: ">=4"
}
reduce_vars: {
reduce_vars_1: {
options = {
evaluate: true,
reduce_vars: true,
@@ -381,6 +381,86 @@ reduce_vars: {
node_version: ">=4"
}
reduce_vars_2: {
options = {
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
"use strict";
(function() {
function f() {
console.log(typeof a);
}
for (let a in [ 42 ])
f();
})();
}
expect: {
"use strict";
(function() {
function f() {
console.log(typeof a);
}
for (let a in [ 42 ])
f();
})();
}
expect_stdout: "undefined"
node_version: ">=4"
}
reduce_vars_3: {
options = {
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
"use strict";
(function(a) {
let i = 1;
function f() {
i = 0;
}
for (let i = 0, x = 0; i < a.length; i++, x++) {
if (x != i) {
console.log("FAIL");
break;
}
f();
console.log(a[i]);
}
console.log(i);
})([ 4, 2 ]);
}
expect: {
"use strict";
(function(a) {
let i = 1;
function f() {
i = 0;
}
for (let i = 0, x = 0; i < a.length; i++, x++) {
if (x != i) {
console.log("FAIL");
break;
}
f();
console.log(a[i]);
}
console.log(i);
})([ 4, 2 ]);
}
expect_stdout: [
"4",
"2",
"0",
]
node_version: ">=4"
}
hoist_props: {
options = {
hoist_props: true,
@@ -610,6 +690,28 @@ drop_unused: {
node_version: ">=4"
}
default_init: {
options = {
evaluate: true,
reduce_vars: true,
sequences: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
let a;
a = "PASS";
console.log(a);
}
expect: {
"use strict";
console.log("PASS");
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4191: {
options = {
functions: true,
@@ -1319,7 +1421,6 @@ issue_4531_2: {
toplevel: true,
}
input: {
"use strict";
var a = console;
console.log(typeof a, function a() {
let { [console]: a } = 0 && a;
@@ -1328,7 +1429,6 @@ issue_4531_2: {
}());
}
expect: {
"use strict";
var o = console;
console.log(typeof o, function o() {
let { [console]: o } = 0;
@@ -1339,3 +1439,110 @@ issue_4531_2: {
expect_stdout: "object undefined"
node_version: ">=6"
}
issue_4689: {
options = {
sequences: true,
}
input: {
"use strict";
var a = "PASS";
console.log(a);
for (let a in 42);
}
expect: {
"use strict";
var a = "PASS";
console.log(a);
for (let a in 42);
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4691: {
options = {
if_return: true,
toplevel: true,
}
input: {
"use strict";
function A() {}
A.prototype.f = function() {
if (!this)
return;
let a = "PA";
function g(b) {
h(a + b);
}
[ "SS" ].forEach(function(c) {
g(c);
});
};
function h(d) {
console.log(d);
}
new A().f();
}
expect: {
"use strict";
function A() {}
A.prototype.f = function() {
if (this) {
let a = "PA";
[ "SS" ].forEach(function(c) {
g(c);
});
function g(b) {
h(a + b);
}
}
};
function h(d) {
console.log(d);
}
new A().f();
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4848: {
options = {
if_return: true,
}
input: {
"use strict";
function f(a) {
a(function() {
console.log(b);
});
if (!console)
return;
let b = "PASS";
}
var g;
f(function(h) {
g = h;
});
g();
}
expect: {
"use strict";
function f(a) {
a(function() {
console.log(b);
});
if (!console)
return;
let b = "PASS";
}
var g;
f(function(h) {
g = h;
});
g();
}
expect_stdout: "PASS"
node_version: ">=4"
}

View File

@@ -501,14 +501,14 @@ do_switch: {
}
}
in_parenthesis_1: {
in_parentheses_1: {
input: {
for (("foo" in {});0;);
}
expect_exact: 'for(("foo"in{});0;);'
}
in_parenthesis_2: {
in_parentheses_2: {
input: {
for ((function(){ "foo" in {}; });0;);
}
@@ -836,10 +836,7 @@ for_of: {
console.log(async);
}
expect_exact: 'var async=["PASS",42];async.p="FAIL";for(async of(null,async))console.log(async);'
expect_stdout: [
"PASS",
"42",
]
expect_stdout: true
node_version: ">=0.12"
}
@@ -1067,7 +1064,6 @@ issue_4084: {
options = {
keep_fargs: false,
loops: true,
passes: 2,
reduce_vars: true,
unused: true,
}
@@ -1276,6 +1272,7 @@ issue_4355: {
dead_code: true,
evaluate: true,
loops: true,
passes: 2,
side_effects: true,
unused: true,
}

View File

@@ -2620,9 +2620,9 @@ issue_4126_1: {
try {
console.log("PASS");
} catch (e) {
var b = a;
var c = a;
} finally {
var c = b;
var c = c;
}
console.log(c);
}
@@ -3212,3 +3212,92 @@ issue_4628: {
}
expect_stdout: "undefined"
}
issue_4653: {
options = {
evaluate: true,
merge_vars: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 1, b;
function f(c, d) {
c || console.log(d);
}
f(a++ + (b = b), b |= console.log(a));
}
expect: {
var b = 1;
(function(c, d) {
c || console.log(d);
})(+b + (b = void 0), b |= console.log(2));
}
expect_stdout: [
"2",
"0",
]
}
issue_4759: {
options = {
merge_vars: true,
toplevel: true,
}
input: {
var i = 2, a = 1, b, c, d;
while (i--) {
try {
if (1 != b) {
d = [];
null.p;
c = d;
} else {
b = 0;
a = c;
}
} catch (e) {}
b = a;
}
console.log(a);
}
expect: {
var i = 2, a = 1, b, c, d;
while (i--) {
try {
if (1 != b) {
d = [];
null.p;
c = d;
} else {
b = 0;
a = c;
}
} catch (e) {}
b = a;
}
console.log(a);
}
expect_stdout: "undefined"
}
issue_4761: {
options = {
merge_vars: true,
toplevel: true,
}
input: {
var a = "FAIL", b;
try {
!a && --a && (b = 0)[console] || console.log(b);
} catch (e) {}
}
expect: {
var a = "FAIL", b;
try {
!a && --a && (b = 0)[console] || console.log(b);
} catch (e) {}
}
expect_stdout: "undefined"
}

View File

@@ -85,7 +85,7 @@ new_with_unary_prefix: {
expect_exact: 'var bar=(+new Date).toString(32);';
}
dot_parenthesis_1: {
dot_parentheses_1: {
input: {
console.log(new (Math.random().constructor) instanceof Number);
}
@@ -93,7 +93,7 @@ dot_parenthesis_1: {
expect_stdout: "true"
}
dot_parenthesis_2: {
dot_parentheses_2: {
input: {
console.log(typeof new function(){Math.random()}.constructor);
}

130
test/compress/nullish.js Normal file
View File

@@ -0,0 +1,130 @@
parentheses: {
input: {
(console.log("foo") || console.log("bar") ?? console.log("baz")) && console.log("moo");
}
expect_exact:'((console.log("foo")||console.log("bar"))??console.log("baz"))&&console.log("moo");'
expect_stdout: [
"foo",
"bar",
"baz",
]
node_version: ">=14"
}
evaluate: {
options = {
evaluate: true,
side_effects: true,
}
input: {
void console.log("foo" ?? "bar") ?? console.log("baz");
}
expect: {
console.log("foo"),
console.log("baz");
}
expect_stdout: [
"foo",
"baz",
]
node_version: ">=14"
}
conditional_assignment_1: {
options = {
collapse_vars: true,
}
input: {
console.log(function(a, b) {
b ?? (a = "FAIL");
return a;
}("PASS", !console));
}
expect: {
console.log(function(a, b) {
b ?? (a = "FAIL");
return a;
}("PASS", !console));
}
expect_stdout: "PASS"
node_version: ">=14"
}
conditional_assignment_2: {
options = {
conditionals: true,
}
input: {
var a, b = false;
a = "PASS",
b ?? (a = "FAIL"),
console.log(a);
}
expect: {
var a, b = false;
a = "PASS",
b ?? (a = "FAIL"),
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=14"
}
conditional_assignment_3: {
options = {
conditionals: true,
join_vars: true,
}
input: {
var a, b = false;
a = "PASS",
b ?? (a = "FAIL"),
console.log(a);
}
expect: {
var a, b = false, a = "PASS";
b ?? (a = "FAIL"),
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=14"
}
conditional_assignment_4: {
options = {
side_effects: true,
}
input: {
console.log(function(a) {
!console ?? (a = "FAIL");
return a;
}("PASS"));
}
expect: {
console.log(function(a) {
!console ?? (a = "FAIL");
return a;
}("PASS"));
}
expect_stdout: "PASS"
node_version: ">=14"
}
issue_4679: {
options = {
comparisons: true,
ie8: true,
}
input: {
var a;
if (void 0 === (undefined ?? a))
console.log("PASS");
}
expect: {
var a;
if (void 0 === (undefined ?? a))
console.log("PASS");
}
expect_stdout: "PASS"
node_version: ">=14"
}

View File

@@ -1,3 +1,10 @@
literal_infinity: {
input: {
console.log(2e308, -1e2345);
}
expect_exact: "console.log(1/0,-(1/0));"
}
parentheses_for_prototype_functions: {
beautify = {
beautify: true,
@@ -97,6 +104,40 @@ parentheses_for_prototype_functions_galio: {
expect_stdout: true
}
octal: {
beautify = {
beautify: true,
}
input: {
(function() {
console.log(052);
console.log(-052);
console.log(018);
console.log(-018);
console.log(052.toFixed(0));
console.log(-052.toFixed(0));
console.log(018..toFixed(0));
console.log(-018..toFixed(0));
})();
}
expect_exact: [
"(function() {",
" console.log(42);",
" console.log(-42);",
" console.log(18);",
" console.log(-18);",
" console.log(42..toFixed(0));",
" console.log(-42..toFixed(0));",
" console.log(18..toFixed(0));",
" console.log(-18..toFixed(0));",
"})();",
]
expect_stdout: true
}
comparisons: {
options = {
comparisons: true,
@@ -777,7 +818,7 @@ issue_1710: {
expect_stdout: true
}
unary_binary_parenthesis: {
unary_binary_parentheses: {
options = {
evaluate: true,
}

View File

@@ -280,6 +280,72 @@ shorthand_keywords: {
node_version: ">=6"
}
object_super: {
input: {
var o = {
f() {
return super.p;
},
p: "FAIL",
};
Object.setPrototypeOf(o, { p: "PASS" });
console.log(o.f());
}
expect_exact: 'var o={f(){return super.p},p:"FAIL"};Object.setPrototypeOf(o,{p:"PASS"});console.log(o.f());'
expect_stdout: "PASS"
node_version: ">=4"
}
object_super_async: {
input: {
var o = {
async f() {
return super.p;
},
p: "FAIL",
};
Object.setPrototypeOf(o, { p: "PASS" });
o.f().then(console.log);
}
expect_exact: 'var o={async f(){return super.p},p:"FAIL"};Object.setPrototypeOf(o,{p:"PASS"});o.f().then(console.log);'
expect_stdout: "PASS"
node_version: ">=8"
}
object_super_generator: {
input: {
var o = {
*f() {
yield super.p;
},
p: "FAIL",
};
Object.setPrototypeOf(o, { p: "PASS" });
console.log(o.f().next().value);
}
expect_exact: 'var o={*f(){yield super.p},p:"FAIL"};Object.setPrototypeOf(o,{p:"PASS"});console.log(o.f().next().value);'
expect_stdout: "PASS"
node_version: ">=4"
}
object_super_async_generator: {
input: {
var o = {
async *f() {
return super.p;
},
p: "FAIL",
};
Object.setPrototypeOf(o, { p: "PASS" });
o.f().next().then(function(v) {
console.log(v.value, v.done);
});
}
expect_exact: 'var o={async*f(){return super.p},p:"FAIL"};Object.setPrototypeOf(o,{p:"PASS"});o.f().next().then(function(v){console.log(v.value,v.done)});'
expect_stdout: "PASS true"
node_version: ">=10"
}
issue_4269_1: {
options = {
evaluate: true,

View File

@@ -1378,3 +1378,71 @@ issue_3389: {
}
expect_stdout: "PASS"
}
object_super: {
options = {
properties: true,
}
input: {
({
f(a) {
return a ? console.log("PASS") : super.log("PASS");
},
}).f(console);
}
expect: {
({
f(a) {
return a ? console.log("PASS") : super.log("PASS");
},
}).f(console);
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4831_1: {
options = {
properties: true,
}
input: {
console.log({
f() {
return arguments;
},
}.f("PASS")[0]);
}
expect: {
console.log([
function() {
return arguments;
},
][0]("PASS")[0]);
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4831_2: {
options = {
properties: true,
}
input: {
var f = {
f() {
return arguments;
},
}.f;
console.log(f("PASS")[0]);
}
expect: {
var f = {
f() {
return arguments;
},
}.f;
console.log(f("PASS")[0]);
}
expect_stdout: "PASS"
node_version: ">=4"
}

View File

@@ -294,248 +294,6 @@ unary: {
}
}
issue_2629_1: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ a();
/*@__PURE__*/ (b());
(/*@__PURE__*/ c)();
(/*@__PURE__*/ d());
}
expect_exact: [
"/* */c();",
]
}
issue_2629_2: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ a(1)(2)(3);
/*@__PURE__*/ (b(1))(2)(3);
/*@__PURE__*/ (c(1)(2))(3);
/*@__PURE__*/ (d(1)(2)(3));
(/*@__PURE__*/ e)(1)(2)(3);
(/*@__PURE__*/ f(1))(2)(3);
(/*@__PURE__*/ g(1)(2))(3);
(/*@__PURE__*/ h(1)(2)(3));
}
expect_exact: [
"/* */e(1)(2)(3);",
"/* */f(1)(2)(3);",
"/* */g(1)(2)(3);",
]
}
issue_2629_3: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ a.x(1).y(2).z(3);
/*@__PURE__*/ (b.x)(1).y(2).z(3);
/*@__PURE__*/ (c.x(1)).y(2).z(3);
/*@__PURE__*/ (d.x(1).y)(2).z(3);
/*@__PURE__*/ (e.x(1).y(2)).z(3);
/*@__PURE__*/ (f.x(1).y(2).z)(3);
/*@__PURE__*/ (g.x(1).y(2).z(3));
(/*@__PURE__*/ h).x(1).y(2).z(3);
(/*@__PURE__*/ i.x)(1).y(2).z(3);
(/*@__PURE__*/ j.x(1)).y(2).z(3);
(/*@__PURE__*/ k.x(1).y)(2).z(3);
(/*@__PURE__*/ l.x(1).y(2)).z(3);
(/*@__PURE__*/ m.x(1).y(2).z)(3);
(/*@__PURE__*/ n.x(1).y(2).z(3));
}
expect_exact: [
"/* */h.x(1).y(2).z(3);",
"/* */i.x(1).y(2).z(3);",
"/* */j.x(1).y(2).z(3);",
"/* */k.x(1).y(2).z(3);",
"/* */l.x(1).y(2).z(3);",
"/* */m.x(1).y(2).z(3);",
]
}
issue_2629_4: {
options = {
side_effects: true,
}
input: {
(/*@__PURE__*/ x(), y());
(w(), /*@__PURE__*/ x(), y());
}
expect: {
y();
w(), y();
}
}
issue_2629_5: {
options = {
side_effects: true,
}
input: {
[ /*@__PURE__*/ x() ];
[ /*@__PURE__*/ x(), y() ];
[ w(), /*@__PURE__*/ x(), y() ];
}
expect: {
y();
w(), y();
}
}
issue_2638: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/(g() || h())(x(), y());
(/*@__PURE__*/ (a() || b()))(c(), d());
}
expect_exact: [
"/* */x(),y();",
"/* */(a()||b())(c(),d());",
]
}
issue_2705_1: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ new a();
/*@__PURE__*/ (new b());
new (/*@__PURE__*/ c)();
(/*@__PURE__*/ new d());
}
expect_exact: [
"new/* */c;",
]
}
issue_2705_2: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ new a(1)(2)(3);
/*@__PURE__*/ new (b(1))(2)(3);
/*@__PURE__*/ new (c(1)(2))(3);
/*@__PURE__*/ new (d(1)(2)(3));
new (/*@__PURE__*/ e)(1)(2)(3);
(/*@__PURE__*/ new f(1))(2)(3);
(/*@__PURE__*/ new g(1)(2))(3);
(/*@__PURE__*/ new h(1)(2)(3));
}
expect_exact: [
"new/* */e(1)(2)(3);",
"/* */new f(1)(2)(3);",
"/* */new g(1)(2)(3);",
]
}
issue_2705_3: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ new a.x(1).y(2).z(3);
/*@__PURE__*/ new (b.x)(1).y(2).z(3);
/*@__PURE__*/ new (c.x(1)).y(2).z(3);
/*@__PURE__*/ new (d.x(1).y)(2).z(3);
/*@__PURE__*/ new (e.x(1).y(2)).z(3);
/*@__PURE__*/ new (f.x(1).y(2).z)(3);
/*@__PURE__*/ new (g.x(1).y(2).z(3));
new (/*@__PURE__*/ h).x(1).y(2).z(3);
/* */ new (/*@__PURE__*/ i.x)(1).y(2).z(3);
(/*@__PURE__*/ new j.x(1)).y(2).z(3);
(/*@__PURE__*/ new k.x(1).y)(2).z(3);
(/*@__PURE__*/ new l.x(1).y(2)).z(3);
(/*@__PURE__*/ new m.x(1).y(2).z)(3);
(/*@__PURE__*/ new n.x(1).y(2).z(3));
}
expect_exact: [
"new/* */h.x(1).y(2).z(3);",
"/* */new/* */i.x(1).y(2).z(3);",
"/* */new j.x(1).y(2).z(3);",
"/* */new k.x(1).y(2).z(3);",
"/* */new l.x(1).y(2).z(3);",
"/* */new m.x(1).y(2).z(3);",
]
}
issue_2705_4: {
options = {
side_effects: true,
}
input: {
(/*@__PURE__*/ new x(), y());
(w(), /*@__PURE__*/ new x(), y());
}
expect: {
y();
w(), y();
}
}
issue_2705_5: {
options = {
side_effects: true,
}
input: {
[ /*@__PURE__*/ new x() ];
[ /*@__PURE__*/ new x(), y() ];
[ w(), /*@__PURE__*/ new x(), y() ];
}
expect: {
y();
w(), y();
}
}
issue_2705_6: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/new (g() || h())(x(), y());
/* */ new (/*@__PURE__*/ (a() || b()))(c(), d());
}
expect_exact: [
"/* */x(),y();",
"/* */new(/* */a()||b())(c(),d());",
]
}
issue_3065_1: {
options = {
inline: true,
@@ -680,130 +438,3 @@ issue_3325_2: {
}
expect_stdout: "PASS"
}
issue_3858: {
options = {
collapse_vars: true,
inline: true,
keep_fargs: false,
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
f("PASS");
}
expect: {
var f = function(a) {
return function() {
console.log(a);
}();
};
f("PASS");
}
expect_stdout: "PASS"
}
inline_pure_call_1: {
options = {
collapse_vars: true,
inline: true,
keep_fargs: false,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
f("PASS");
}
expect: {}
}
inline_pure_call_2: {
options = {
collapse_vars: true,
inline: true,
keep_fargs: false,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
var a = f("PASS");
}
expect: {}
}
inline_pure_call_3: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
keep_fargs: false,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
var a = f("PASS");
console.log(a);
}
expect: {
var a = function() {
console.log("PASS");
}();
console.log(a);
}
expect_stdout: [
"PASS",
"undefined",
]
}
inline_pure_call_4: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = /*@__PURE__*/ function() {
return console.log("PASS"), 42;
}();
console.log(a);
}
expect: {
var a = function() {
return console.log("PASS"), 42;
}();
console.log(a);
}
expect_stdout: [
"PASS",
"42",
]
}

View File

@@ -1220,13 +1220,110 @@ drop_arguments: {
expect_stdout: "PASS"
}
lvalues_def: {
options = {
collapse_vars: true,
pure_getters: true,
side_effects: true,
unused: true,
}
input: {
var a = 0, b = 1;
var a = b++, b = +function() {}();
a && a[a++];
console.log(a, b);
}
expect: {
var a = 0, b = 1;
a = b++, b = +void 0;
a && a++;
console.log(a, b);
}
expect_stdout: true
}
side_effects_assign: {
options = {
evaluate: true,
pure_getters: true,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
}
input: {
var a = typeof void (a && a.in == 1, 0);
console.log(a);
}
expect: {
var a = "undefined";
console.log(a);
}
expect_stdout: "undefined"
}
issue_2062: {
options = {
booleans: true,
collapse_vars: true,
conditionals: true,
pure_getters: true,
side_effects: true,
}
input: {
var a = 1;
if ([ a || a++ + a--, a++ + a--, a && a.var ]);
console.log(a);
}
expect: {
var a = 1;
a || (a++, a--), a++, a--;
console.log(a);
}
expect_stdout: "1"
}
issue_2878: {
options = {
collapse_vars: true,
pure_getters: true,
sequences: true,
}
input: {
var c = 0;
(function(a, b) {
function f2() {
if (a) c++;
}
b = f2();
a = 1;
b && b.b;
f2();
})();
console.log(c);
}
expect: {
var c = 0;
(function(a, b) {
function f2() {
if (a) c++;
}
b = f2(),
a = 1,
f2();
})(),
console.log(c);
}
expect_stdout: "1"
}
issue_3427: {
options = {
assignments: true,
collapse_vars: true,
evaluate: true,
inline: true,
passes: 2,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
@@ -1242,6 +1339,74 @@ issue_3427: {
expect_stdout: true
}
issue_3490_1: {
options = {
conditionals: true,
dead_code: true,
inline: true,
pure_getters: true,
sequences: true,
side_effects: true,
toplevel: true,
}
input: {
var b = 42, c = "FAIL";
if ({
3: function() {
var a;
return (a && a.p) < this;
}(),
}) c = "PASS";
if (b) while ("" == typeof d);
console.log(c, b);
}
expect: {
var b = 42, c = "FAIL";
if (function() {
var a;
}(), c = "PASS", b) while ("" == typeof d);
console.log(c, b);
}
expect_stdout: "PASS 42"
}
issue_4135: {
options = {
evaluate: true,
inline: true,
merge_vars: true,
pure_getters: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 0, b = 0;
--b;
a++;
if (!a)
var c = function() {
var d = 0;
function f() {
d && d.p;
}
f();
this;
}(a++);
console.log(a, b, c);
}
expect: {
var a = 0;
0;
a++;
if (!a)
var c = void a++;
console.log(a, -1, c);
}
expect_stdout: "1 -1 undefined"
}
issue_4440: {
options = {
pure_getters: "strict",
@@ -1270,3 +1435,132 @@ issue_4440: {
}
expect_stdout: "PASS"
}
issue_4730_1: {
options = {
pure_getters: true,
side_effects: true,
}
input: {
var a;
console.log("PASS") + (a && a[a.p]);
}
expect: {
var a;
console.log("PASS");
}
expect_stdout: "PASS"
}
issue_4730_2: {
options = {
pure_getters: true,
side_effects: true,
}
input: {
var a;
!console.log("PASS") || a && a[a.p];
}
expect: {
var a;
console.log("PASS");
}
expect_stdout: "PASS"
}
issue_4751: {
options = {
pure_getters: true,
side_effects: true,
}
input: {
var o = {
get p() {
console.log("PASS");
},
};
o && o.p;
}
expect: {
var o = {
get p() {
console.log("PASS");
},
};
}
}
super_toString: {
options = {
pure_getters: true,
unsafe: true,
}
input: {
console.log({
f() {
return super.toString();
},
}.f());
}
expect: {
console.log({
f() {
return super.toString();
},
}.f());
}
expect_stdout: "[object Object]"
node_version: ">=4"
}
this_toString: {
options = {
pure_getters: true,
unsafe: true,
}
input: {
console.log({
f() {
return this.toString();
},
}.f());
}
expect: {
console.log({
f() {
return "" + this;
},
}.f());
}
expect_stdout: "[object Object]"
node_version: ">=4"
}
issue_4803: {
options = {
hoist_vars: true,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var o = {
get f() {
console.log("PASS");
},
} || 42;
for (var k in o)
o[k];
}
expect: {
var k, o = {
get f() {
console.log("PASS");
},
} || 42;
for (k in o)
o[k];
}
expect_stdout: "PASS"
}

View File

@@ -673,27 +673,21 @@ issue_4575: {
unused: true,
}
input: {
A = "PASS";
(function() {
var a = 0, b = a;
var c = function a(...b) {
A;
var d = A;
console.log(d, b.length);
(function(a) {
var b = a;
var c = function a(...d) {
console.log(d.length);
}();
})();
}
expect: {
A = "PASS";
(function() {
(function(b) {
A;
var d = A;
console.log(d, b.length);
})([]);
(function(a) {
(function a(...d) {
console.log(d.length);
})();
})();
}
expect_stdout: "PASS 0"
expect_stdout: "0"
node_version: ">=6"
}
@@ -763,3 +757,30 @@ issue_4644_2: {
expect_stdout: "PASS 0 undefined"
node_version: ">=6"
}
issue_4666: {
options = {
evaluate: true,
reduce_vars: true,
rests: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var a = 0, b = 0;
var o = ((...c) => a++ + c)(b);
for (var k in o)
b++;
console.log(a, b);
}
expect: {
var a = 0, b = 0;
var o = (c => +a + c)([ b ]);
for(var k in o)
b++;
console.log(1, b);
}
expect_stdout: "1 2"
node_version: ">=6"
}

View File

@@ -172,3 +172,32 @@ issue_4054: {
}
expect_stdout: "{ p: [Setter] }"
}
issue_4811_1: {
input: {
for (var PASS in this);
console.log(PASS, this, {} < this);
}
expect: {
for (var PASS in this);
console.log(PASS, this, {} < this);
}
expect_stdout: "PASS [object global] true"
}
issue_4811_2: {
options = {
side_effects: true,
}
input: {
(async function() {});
for (var PASS in this);
console.log(PASS, this, {} < this);
}
expect: {
for (var PASS in this);
console.log(PASS, this, {} < this);
}
expect_stdout: "PASS [object global] true"
node_version: ">=8"
}

View File

@@ -564,6 +564,34 @@ delete_seq_3: {
}
delete_seq_4: {
options = {
booleans: true,
evaluate: false,
sequences: true,
side_effects: true,
}
input: {
function f() {}
console.log(delete (f(), undefined));
console.log(delete (f(), void 0));
console.log(delete (f(), Infinity));
console.log(delete (f(), 1 / 0));
console.log(delete (f(), NaN));
console.log(delete (f(), 0 / 0));
}
expect: {
function f() {}
console.log(delete void f()),
console.log(delete void f()),
console.log((f(), delete (1 / 0))),
console.log((f(), delete (1 / 0))),
console.log(delete (f(), NaN)),
console.log((f(), delete(0 / 0)));
}
expect_stdout: true
}
delete_seq_4_evaluate: {
options = {
booleans: true,
evaluate: true,
@@ -592,6 +620,35 @@ delete_seq_4: {
}
delete_seq_5: {
options = {
booleans: true,
evaluate: false,
keep_infinity: true,
sequences: true,
side_effects: true,
}
input: {
function f() {}
console.log(delete (f(), undefined));
console.log(delete (f(), void 0));
console.log(delete (f(), Infinity));
console.log(delete (f(), 1 / 0));
console.log(delete (f(), NaN));
console.log(delete (f(), 0 / 0));
}
expect: {
function f() {}
console.log(delete void f()),
console.log(delete void f()),
console.log(delete (f(), Infinity)),
console.log((f(), delete (1 / 0))),
console.log(delete (f(), NaN)),
console.log((f(), delete (0 / 0)));
}
expect_stdout: true
}
delete_seq_5_evaluate: {
options = {
booleans: true,
evaluate: true,
@@ -663,12 +720,21 @@ side_effects_cascade_1: {
if (a < 0) a = 0;
b.a = a;
}
var m = {}, n = {};
f(13, m);
f("foo", n);
console.log(m.a, n.a);
}
expect: {
function f(a, b) {
(a -= 42) < 0 && (a = 0), b.a = a;
b.a = a = (a -= 42) < 0 ? 0 : a;
}
var m = {}, n = {};
f(13, m),
f("foo", n),
console.log(m.a, n.a);
}
expect_stdout: "0 NaN"
}
side_effects_cascade_2: {

View File

@@ -348,8 +348,6 @@ issue_3983_1: {
}
expect: {
var a = "PASS";
g();
function g() {}
console.log(a);
}
expect_stdout: "PASS"
@@ -458,14 +456,14 @@ issue_4325: {
}
expect: {
(function() {
(function() {
(function(c) {
try {
(void 0).p = 0;
c.p = 0;
} catch (e) {
console.log("PASS");
return;
}
})();
})(void 0);
})();
}
expect_stdout: "PASS"
@@ -506,3 +504,114 @@ issue_4366_2: {
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4668: {
options = {
conditionals: true,
keep_fargs: false,
keep_fnames: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function f(a) {
var b, c;
function g() {
return a = 0 + a, !d || (a = 0);
}
c = g();
}
console.log(f());
var d = 0;
}
expect: {
console.log(function f() {
(function g() {
0;
})();
}());
}
expect_stdout: "undefined"
}
drop_side_effect_free_call: {
options = {
inline: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
function f(a) {
return "PA" + a;
}
f(42);
console.log(f("SS"));
}
expect: {
function f(a) {
return "PA" + a;
}
console.log(f("SS"));
}
expect_stdout: "PASS"
}
issue_4730_1: {
options = {
pure_getters: "strict",
side_effects: true,
}
input: {
var a;
console.log("PASS") + (a && a[a.p]);
}
expect: {
var a;
console.log("PASS"),
a && a[a.p];
}
expect_stdout: "PASS"
}
issue_4730_2: {
options = {
pure_getters: "strict",
side_effects: true,
}
input: {
var a;
!console.log("PASS") || a && a[a.p];
}
expect: {
var a;
!console.log("PASS") || a && a[a.p];
}
expect_stdout: "PASS"
}
issue_4751: {
options = {
pure_getters: "strict",
side_effects: true,
}
input: {
var o = {
get p() {
console.log("PASS");
},
};
o && o.p;
}
expect: {
var o = {
get p() {
console.log("PASS");
},
};
o && o.p;
}
expect_stdout: "PASS"
}

View File

@@ -85,7 +85,7 @@ collapse_vars_4: {
node_version: ">=6"
}
conditionals_farg: {
conditionals_farg_1: {
options = {
conditionals: true,
}
@@ -107,6 +107,28 @@ conditionals_farg: {
node_version: ">=6"
}
conditionals_farg_2: {
options = {
conditionals: true,
pure_getters: "strict",
reduce_vars: true,
}
input: {
var log = console.log;
(function(a) {
return a.length ? log(...a) : log("FAIL");
})([ "PASS" ]);
}
expect: {
var log = console.log;
(function(a) {
return a.length ? log(...a) : log("FAIL");
})([ "PASS" ]);
}
expect_stdout: "PASS"
node_version: ">=6"
}
dont_inline: {
options = {
inline: true,
@@ -272,6 +294,31 @@ reduce_vars_2: {
node_version: ">=6"
}
reduce_vars_3: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {}
function g() {
return (a => a)(...[ f ]);
}
console.log(g() === g() ? "PASS" : "FAIL");
}
expect: {
function f() {}
function g() {
return (a => a)(...[ f ]);
}
console.log(g() === g() ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
node_version: ">=6"
}
convert_setter: {
options = {
objects: true,
@@ -667,6 +714,57 @@ unused_var_side_effects: {
node_version: ">=8"
}
unsafe_join_1: {
options = {
unsafe: true,
}
input: {
console.log([ ..."foo" ].join());
}
expect: {
console.log([ ..."foo" ].join());
}
expect_stdout: "f,o,o"
node_version: ">=6"
}
unsafe_join_2: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log([ "foo", ..."bar" ].join(""));
}
expect: {
console.log([ "foo", ..."bar" ].join(""));
}
expect_stdout: "foobar"
node_version: ">=6"
}
unsafe_join_3: {
options = {
unsafe: true,
}
input: {
try {
[].join(...console);
} catch (e) {
console.log("PASS");
}
}
expect: {
try {
[].join(...console);
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_4329: {
options = {
objects: true,
@@ -947,3 +1045,26 @@ issue_4614: {
expect_stdout: true
node_version: ">=6"
}
issue_4849: {
options = {
reduce_vars: true,
unused: true,
}
input: {
while (function() {
while (!console);
}(new function(a) {
console.log(typeof { ...a });
}(function() {})));
}
expect: {
while (function() {
while (!console);
}(function(a) {
console.log(typeof { ...function() {} });
}()));
}
expect_stdout: "object"
node_version: ">=8"
}

View File

@@ -53,7 +53,7 @@ tagged_chain: {
node_version: ">=4"
}
tag_parenthesis_arrow: {
tag_parentheses_arrow: {
input: {
console.log((s => s.raw[0])`\tPASS`.slice(2));
}
@@ -62,7 +62,7 @@ tag_parenthesis_arrow: {
node_version: ">=4"
}
tag_parenthesis_new: {
tag_parentheses_new: {
input: {
(new function() {
return console.log;
@@ -73,6 +73,20 @@ tag_parenthesis_new: {
node_version: ">=4"
}
tag_parentheses_sequence: {
input: {
var o = {
f() {
console.log(this === o ? "FAIL" : "PASS");
},
};
(42, o.f)``;
}
expect_exact: 'var o={f(){console.log(this===o?"FAIL":"PASS")}};(42,o.f)``;'
expect_stdout: "PASS"
node_version: ">=4"
}
malformed_escape: {
input: {
(function(s) {
@@ -211,7 +225,7 @@ unsafe_evaluate: {
node_version: ">=8"
}
side_effects: {
side_effects_1: {
options = {
side_effects: true,
}
@@ -228,6 +242,30 @@ side_effects: {
node_version: ">=4"
}
side_effects_2: {
options = {
side_effects: true,
}
input: {
var o = {
f() {
console.log(this === o ? "FAIL" : "PASS");
},
};
(42, o.f)``;
}
expect: {
var o = {
f() {
console.log(this === o ? "FAIL" : "PASS");
},
};
(0, o.f)``;
}
expect_stdout: "PASS"
node_version: ">=4"
}
unsafe_side_effects: {
options = {
side_effects: true,
@@ -298,3 +336,33 @@ issue_4630: {
expect_stdout: "/PASS/"
node_version: ">=4"
}
issue_4676: {
options = {
evaluate: true,
reduce_vars: true,
templates: true,
toplevel: true,
unsafe:true,
unused: true,
}
input: {
function f(a) {
var b = `foo${a = "PASS"}`;
for (var c in f && b)
b.p;
return a;
}
console.log(f("FAIL"));
}
expect: {
console.log(function f(a) {
var b = "fooPASS";
for (var c in f, b)
b.p;
return "PASS";
}());
}
expect_stdout: "PASS"
node_version: ">=4"
}

View File

@@ -50,7 +50,7 @@ unicode_parse_variables: {
}
}
unicode_escaped_identifier: {
unicode_escaped_identifier_1: {
input: {
var \u0061 = "\ud800\udc00";
console.log(a);
@@ -59,6 +59,17 @@ unicode_escaped_identifier: {
expect_stdout: "\ud800\udc00"
}
unicode_escaped_identifier_2: {
input: {
var \u{61} = "foo";
var \u{10000} = "bar";
console.log(a, \u{10000});
}
expect_exact: 'var a="foo";var \u{10000}="bar";console.log(a,\u{10000});'
expect_stdout: "foo bar"
node_version: ">=4"
}
unicode_identifier_ascii_only: {
beautify = {
ascii_only: true,

View File

@@ -354,6 +354,92 @@ forin_let_2: {
node_version: ">=6"
}
loop_scope_1: {
options = {
toplevel: true,
varify: true,
}
input: {
"use strict";
var o = { foo: 1, bar: 2 };
for (let i in o) {
console.log(i);
}
for (const j in o)
setTimeout(() => console.log(j), 0);
for (let k in o)
setTimeout(function() {
console.log(k);
}, 0);
}
expect: {
"use strict";
var o = { foo: 1, bar: 2 };
for (var i in o)
console.log(i);
for (const j in o)
setTimeout(() => console.log(j), 0);
for (let k in o)
setTimeout(function() {
console.log(k);
}, 0);
}
expect_stdout: [
"foo",
"bar",
"foo",
"bar",
"foo",
"bar",
]
node_version: ">=4"
}
loop_scope_2: {
options = {
reduce_vars: true,
toplevel: true,
varify: true,
}
input: {
"use strict";
var a = [ "foo", "bar" ];
for (var i = 0; i < a.length; i++) {
const x = a[i];
console.log(x);
let y = a[i];
setTimeout(() => console.log(y), 0);
const z = a[i];
setTimeout(function() {
console.log(z);
}, 0);
}
}
expect: {
"use strict";
var a = [ "foo", "bar" ];
for (var i = 0; i < a.length; i++) {
var x = a[i];
console.log(x);
let y = a[i];
setTimeout(() => console.log(y), 0);
const z = a[i];
setTimeout(function() {
console.log(z);
}, 0);
}
}
expect_stdout: [
"foo",
"bar",
"foo",
"foo",
"bar",
"bar",
]
node_version: ">=4"
}
issue_4290_1_const: {
options = {
reduce_vars: true,
@@ -409,3 +495,31 @@ drop_forin_let: {
expect_stdout: "PASS"
node_version: ">=4"
}
default_init: {
options = {
join_vars: true,
reduce_vars: true,
unused: true,
varify: true,
}
input: {
A = "PASS";
(function() {
"use strict";
let a;
a = A;
console.log(a);
})();
}
expect: {
A = "PASS";
(function() {
"use strict";
var a = A;
console.log(a);
})();
}
expect_stdout: "PASS"
node_version: ">=4"
}

View File

@@ -247,6 +247,30 @@ collapse_vars_4: {
node_version: ">=4"
}
collapse_vars_5: {
options = {
collapse_vars: true,
}
input: {
var a = function* f(b, c) {
b = yield c = b;
console.log(c);
}("PASS");
a.next();
a.next("FAIL");
}
expect: {
var a = function* f(b, c) {
b = yield c = b;
console.log(c);
}("PASS");
a.next();
a.next("FAIL");
}
expect_stdout: "PASS"
node_version: ">=4"
}
collapse_property_lambda: {
options = {
collapse_vars: true,
@@ -388,14 +412,14 @@ functions: {
function* b() {
return !!b;
}
var c = function*(c) {
function* c(c) {
return c;
};
}
if (yield* c(yield* b(yield* a()))) {
function* d() {}
function* e() {
return typeof e;
}
var d = function*() {};
var e = function* y() {
return typeof y;
};
var f = function*(f) {
return f;
};
@@ -446,9 +470,9 @@ functions_use_strict: {
function* b() {
return !!b;
}
var c = function*(c) {
function* c(c) {
return c;
};
}
if (yield* c(yield* b(yield* a()))) {
var d = function*() {};
var e = function* y() {
@@ -465,6 +489,54 @@ functions_use_strict: {
node_version: ">=4"
}
functions_anonymous: {
options = {
functions: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var yield = function*() {
return "PASS";
};
console.log(yield().next(yield).value);
}
expect: {
function* yield() {
return "PASS";
}
console.log(yield().next(yield).value);
}
expect_stdout: "PASS"
node_version: ">=4"
}
functions_inner_var: {
options = {
functions: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var yield = function* a() {
var a;
console.log(a, a);
};
yield().next(yield);
}
expect: {
function* yield() {
var a;
console.log(a, a);
}
yield().next(yield);
}
expect_stdout: "undefined undefined"
node_version: ">=4"
}
negate_iife: {
options = {
negate_iife: true,
@@ -690,7 +762,7 @@ drop_body: {
})([ console.log("baz") ]);
}
expect: {
[ [ , 0[0] = console.log("foo") ] ] = [ [ console.log("baz") ] ];
[ [ , [].e = console.log("foo") ] ] = [ [ console.log("baz") ] ];
}
expect_stdout: [
"baz",
@@ -950,3 +1022,43 @@ issue_4641_2: {
]
node_version: ">=10"
}
issue_4769_1: {
options = {
side_effects: true,
}
input: {
console.log(function*() {
(function({} = yield => {}) {})();
}().next().done);
}
expect: {
console.log(function*() {
(function({} = yield => {}) {})();
}().next().done);
}
expect_stdout: "true"
node_version: ">=6"
}
issue_4769_2: {
options = {
inline: true,
}
input: {
console.log(function*() {
return function({} = yield => {}) {
return "PASS";
}();
}().next().value);
}
expect: {
console.log(function*() {
return function({} = yield => {}) {
return "PASS";
}();
}().next().value);
}
expect_stdout: "PASS"
node_version: ">=6"
}

View File

@@ -1,5 +1,6 @@
exports["Compressor"] = Compressor;
exports["defaults"] = defaults;
exports["is_statement"] = is_statement;
exports["JS_Parse_Error"] = JS_Parse_Error;
exports["List"] = List;
exports["mangle_properties"] = mangle_properties;

View File

@@ -0,0 +1 @@
for await (; console.log(42););

View File

@@ -1,11 +1,11 @@
{
"version": 3,
"sources": [
"input.js"
],
"names": [],
"mappings": ";;;;;;;;;;;;;;eAAc,OAAO,CAAC,KAAD,C;IAAd,G,YAAA,G;;gBACS,OAAO,CAAC,OAAD,C;IAAhB,K,aAAA,K;;AAEP,GAAG,CAAC,CAAJ,OAAA,GAAG,qBAAM,GAAG,CAAC,CAAJ,CAAM,KAAK,CAAC,CAAZ,CAAN,EAAH",
"sourcesContent": [
"const {foo} = require(\"bar\");\nconst {hello} = require(\"world\");\n\nfoo.x(...foo.y(hello.z));\n"
]
}
{
"version": 3,
"sources": [
"input.js"
],
"names": [],
"mappings": ";;;;;;;;;;;;;;eAAc,OAAO,CAAC,KAAD,C;IAAd,G,YAAA,G;;gBACS,OAAO,CAAC,OAAD,C;IAAhB,K,aAAA,K;;AAEP,GAAG,CAAC,CAAJ,OAAA,GAAG,qBAAM,GAAG,CAAC,CAAJ,CAAM,KAAK,CAAC,CAAZ,CAAN,EAAH",
"sourcesContent": [
"const {foo} = require(\"bar\");\nconst {hello} = require(\"world\");\n\nfoo.x(...foo.y(hello.z));\n"
]
}

View File

@@ -1,8 +1,8 @@
{
"compress": false,
"mangle": {
"properties": {
"regex": "/^_/"
}
}
}
{
"compress": false,
"mangle": {
"properties": {
"regex": "/^_/"
}
}
}

View File

@@ -29,7 +29,7 @@ describe("bin/uglifyjs", function() {
var command = uglifyjscmd + ' test/input/comments/filter.js --comments all';
exec(command, function(err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "// foo\n/*@preserve*/\n// bar\n\n");
assert.strictEqual(stdout, "// foo\n/*@preserve*/\n// bar\n");
done();
});
});
@@ -37,7 +37,7 @@ describe("bin/uglifyjs", function() {
var command = uglifyjscmd + ' test/input/comments/filter.js --comments /r/';
exec(command, function(err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "/*@preserve*/\n// bar\n\n");
assert.strictEqual(stdout, "/*@preserve*/\n// bar\n");
done();
});
});
@@ -45,7 +45,7 @@ describe("bin/uglifyjs", function() {
var command = uglifyjscmd + ' test/input/comments/filter.js --comments';
exec(command, function(err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "/*@preserve*/\n\n");
assert.strictEqual(stdout, "/*@preserve*/\n");
done();
});
});
@@ -679,6 +679,20 @@ describe("bin/uglifyjs", function() {
done();
});
});
it("Should throw syntax error (for-await)", function(done) {
var command = uglifyjscmd + " test/input/invalid/for-await.js";
exec(command, function(err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/for-await.js:1,11",
"for await (; console.log(42););",
" ^",
"ERROR: Unexpected token: punc «;»",
].join("\n"));
done();
});
});
it("Should throw syntax error (switch defaults)", function(done) {
var command = uglifyjscmd + " test/input/invalid/switch.js";
exec(command, function(err, stdout, stderr) {

View File

@@ -259,7 +259,7 @@ describe("comments", function() {
assert.strictEqual(result.code, code);
});
it("Should handle comments around parenthesis correctly", function() {
it("Should handle comments around parentheses correctly", function() {
var code = [
"a();",
"/* foo */",
@@ -392,12 +392,12 @@ describe("comments", function() {
describe("comment filters", function() {
it("Should be able to filter comments by passing regexp", function() {
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
assert.strictEqual(ast.print_to_string({comments: /^!/}), "/*!test1*/\n//!test3\n//!test6\n//!test8\n");
assert.strictEqual(ast.print_to_string({comments: /^!/}), "/*!test1*/\n//!test3\n//!test6\n//!test8");
});
it("Should be able to filter comments with the 'all' option", function() {
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
assert.strictEqual(ast.print_to_string({comments: "all"}), "/*!test1*/\n/*test2*/\n//!test3\n//test4\n//test5\n//!test6\n//test7\n//!test8\n");
assert.strictEqual(ast.print_to_string({comments: "all"}), "/*!test1*/\n/*test2*/\n//!test3\n//test4\n//test5\n//!test6\n//test7\n//!test8");
});
it("Should be able to filter commments with the 'some' option", function() {
@@ -410,13 +410,12 @@ describe("comments", function() {
var f = function(node, comment) {
return comment.value.length === 8;
};
assert.strictEqual(ast.print_to_string({comments: f}), "/*TEST 123*/\n//8 chars.\n");
assert.strictEqual(ast.print_to_string({comments: f}), "/*TEST 123*/\n//8 chars.");
});
it("Should be able to filter comments by passing regex in string format", function() {
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
assert.strictEqual(ast.print_to_string({comments: "/^!/"}), "/*!test1*/\n//!test3\n//!test6\n//!test8\n");
assert.strictEqual(ast.print_to_string({comments: "/^!/"}), "/*!test1*/\n//!test3\n//!test6\n//!test8");
});
it("Should be able to get the comment and comment type when using a function", function() {
@@ -424,14 +423,12 @@ describe("comments", function() {
var f = function(node, comment) {
return comment.type == "comment1" || comment.type == "comment3";
};
assert.strictEqual(ast.print_to_string({comments: f}), "//!test3\n//test4\n//test5\n//!test6\n");
assert.strictEqual(ast.print_to_string({comments: f}), "//!test3\n//test4\n//test5\n//!test6");
});
it("Should be able to filter comments by passing a boolean", function() {
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
assert.strictEqual(ast.print_to_string({comments: true}), "/*!test1*/\n/*test2*/\n//!test3\n//test4\n//test5\n//!test6\n//test7\n//!test8\n");
assert.strictEqual(ast.print_to_string({comments: true}), "/*!test1*/\n/*test2*/\n//!test3\n//test4\n//test5\n//!test6\n//test7\n//!test8");
assert.strictEqual(ast.print_to_string({comments: false}), "");
});
@@ -439,10 +436,8 @@ describe("comments", function() {
var ast = UglifyJS.parse("#!Random comment\n//test1\n/*test2*/");
var f = function(node, comment) {
assert.strictEqual(comment.type === "comment5", false);
return true;
};
assert.strictEqual(ast.print_to_string({comments: f}), "#!Random comment\n//test1\n/*test2*/");
});
@@ -453,9 +448,8 @@ describe("comments", function() {
it("Should have no problem on multiple calls", function() {
const options = {
comments: /ok/
comments: /ok/,
};
assert.strictEqual(UglifyJS.parse("/* ok */ function a(){}").print_to_string(options), "/* ok */function a(){}");
assert.strictEqual(UglifyJS.parse("/* ok */ function a(){}").print_to_string(options), "/* ok */function a(){}");
assert.strictEqual(UglifyJS.parse("/* ok */ function a(){}").print_to_string(options), "/* ok */function a(){}");
@@ -463,14 +457,14 @@ describe("comments", function() {
it("Should handle shebang and preamble correctly", function() {
var code = UglifyJS.minify("#!/usr/bin/node\nvar x = 10;", {
output: { preamble: "/* Build */" }
output: { preamble: "/* Build */" },
}).code;
assert.strictEqual(code, "#!/usr/bin/node\n/* Build */\nvar x=10;");
});
it("Should handle preamble without shebang correctly", function() {
var code = UglifyJS.minify("var x = 10;", {
output: { preamble: "/* Build */" }
output: { preamble: "/* Build */" },
}).code;
assert.strictEqual(code, "/* Build */\nvar x=10;");
});

71
test/mocha/exports.js Normal file
View File

@@ -0,0 +1,71 @@
var assert = require("assert");
var UglifyJS = require("../node");
describe("export", function() {
it("Should reject invalid `export ...` statement syntax", function() {
[
"export *;",
"export A;",
"export 42;",
"export var;",
"export * as A;",
"export A as B;",
"export const A;",
"export function(){};",
].forEach(function(code) {
assert.throws(function() {
UglifyJS.parse(code);
}, function(e) {
return e instanceof UglifyJS.JS_Parse_Error;
}, code);
});
});
it("Should reject invalid `export { ... }` statement syntax", function() {
[
"export { * };",
"export { * as A };",
"export { 42 as A };",
"export { A as B-C };",
"export { default as A };",
].forEach(function(code) {
assert.throws(function() {
UglifyJS.parse(code);
}, function(e) {
return e instanceof UglifyJS.JS_Parse_Error;
}, code);
});
});
it("Should reject invalid `export default ...` statement syntax", function() {
[
"export default *;",
"export default var;",
"export default A as B;",
].forEach(function(code) {
assert.throws(function() {
UglifyJS.parse(code);
}, function(e) {
return e instanceof UglifyJS.JS_Parse_Error;
}, code);
});
});
it("Should reject invalid `export ... from ...` statement syntax", function() {
[
"export from 'path';",
"export * from `path`;",
"export A as B from 'path';",
"export default from 'path';",
"export { A }, B from 'path';",
"export * as A, B from 'path';",
"export * as A, {} from 'path';",
"export { * as A } from 'path';",
"export { 42 as A } from 'path';",
"export { A-B as C } from 'path';",
].forEach(function(code) {
assert.throws(function() {
UglifyJS.parse(code);
}, function(e) {
return e instanceof UglifyJS.JS_Parse_Error;
}, code);
});
});
});

28
test/mocha/imports.js Normal file
View File

@@ -0,0 +1,28 @@
var assert = require("assert");
var UglifyJS = require("../node");
describe("import", function() {
it("Should reject invalid `import` statement syntax", function() {
[
"import *;",
"import A;",
"import {};",
"import `path`;",
"import from 'path';",
"import * from 'path';",
"import A as B from 'path';",
"import { A }, B from 'path';",
"import * as A, B from 'path';",
"import * as A, {} from 'path';",
"import { * as A } from 'path';",
"import { 42 as A } from 'path';",
"import { A-B as C } from 'path';",
].forEach(function(code) {
assert.throws(function() {
UglifyJS.parse(code);
}, function(e) {
return e instanceof UglifyJS.JS_Parse_Error;
}, code);
});
});
});

View File

@@ -235,8 +235,8 @@ describe("minify", function() {
}
});
var code = result.code;
assert.strictEqual(code, "var a=/* */function(){foo()}();");
})
assert.strictEqual(code, "var a=function(){foo()}();");
});
});
describe("JS_Parse_Error", function() {

View File

@@ -84,7 +84,7 @@ describe("parentheses", function() {
}
});
it("Should compress leading parenthesis with reasonable performance", function() {
it("Should compress leading parentheses with reasonable performance", function() {
this.timeout(30000);
var code = [
"({}?0:1)&&x();",

View File

@@ -183,6 +183,24 @@ describe("test/reduce.js", function() {
"// }",
].join("\n"));
});
it("Should reduce `for (const ... in ...)` without invalid intermediate AST", function() {
if (semver.satisfies(process.version, "<4")) return;
var code = [
"var a = 0;",
"",
"for (const b in [ 1, 2, 3 ]) {",
" a = +a + 1 - .2;",
" console.log(a);",
"}",
].join("\n");
var result = reduce_test(code, {
compress: {
unsafe_math: true,
},
});
if (result.error) throw result.error;
assert.deepEqual(result.warnings, []);
});
it("Should reduce infinite loops with reasonable performance", function() {
if (semver.satisfies(process.version, "<=0.10")) return;
this.timeout(120000);
@@ -294,8 +312,9 @@ describe("test/reduce.js", function() {
"// }",
]).join("\n"));
});
it("Should maintain block-scope for const & let", function() {
it("Should maintain block-scope for const/let", function() {
if (semver.satisfies(process.version, "<4")) return;
this.timeout(120000);
var code = [
'"use strict";',
"",
@@ -360,4 +379,39 @@ describe("test/reduce.js", function() {
if (result.error) throw result.error;
assert.strictEqual(result.code, read("test/input/reduce/destructured_catch.reduced.js"));
});
it("Should not enumerate `toString` over global context", function() {
if (semver.satisfies(process.version, "<8")) return;
var code = [
"(async function() {});",
"for (var k in this);",
"console.log(k, 42 + this);",
].join("\n");
var result = reduce_test(code, {
mangle: false,
});
if (result.error) throw result.error;
assert.strictEqual(result.code, [
"// Can't reproduce test failure",
"// minify options: {",
'// "mangle": false',
"// }",
].join("\n"));
});
it("Should reduce object with method syntax without invalid intermediate AST", function() {
if (semver.satisfies(process.version, "<4")) return;
var code = [
"console.log({",
" f() {",
" return 1 - .8;",
" },",
"}.f());",
].join("\n");
var result = reduce_test(code, {
compress: {
unsafe_math: true,
},
});
if (result.error) throw result.error;
assert.deepEqual(result.warnings, []);
});
});

View File

@@ -132,6 +132,17 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
return;
}
if (parent instanceof U.AST_VarDef && parent.name === node) return;
// preserve exports
if (parent instanceof U.AST_ExportDeclaration) return;
if (parent instanceof U.AST_ExportDefault) return;
if (parent instanceof U.AST_ExportForeign) return;
if (parent instanceof U.AST_ExportReferences) return;
// preserve sole definition of an export statement
if (node instanceof U.AST_VarDef
&& parent.definitions.length == 1
&& tt.parent(1) instanceof U.AST_ExportDeclaration) {
return;
}
// preserve for (var xxx; ...)
if (parent instanceof U.AST_For && parent.init === node && node instanceof U.AST_Definitions) return node;
// preserve for (xxx in/of ...)
@@ -181,7 +192,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
}
else if (node instanceof U.AST_Call) {
var expr = [
node.expression,
!(node.expression instanceof U.AST_Super) && node.expression,
node.args[0],
null, // intentional
][ ((node.start._permute += step) * steps | 0) % 3 ];
@@ -197,12 +208,10 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
}
if (node.expression instanceof U.AST_Function) {
// hoist and return expressions from the IIFE function expression
var body = node.expression.body;
node.expression.body = [];
var seq = [];
body.forEach(function(node) {
node.expression.body.forEach(function(node) {
var expr = expr instanceof U.AST_Exit ? node.value : node.body;
if (expr instanceof U.AST_Node && !is_statement(expr)) {
if (expr instanceof U.AST_Node && !U.is_statement(expr) && can_hoist(expr)) {
// collect expressions from each statements' body
seq.push(expr);
}
@@ -253,7 +262,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
CHANGED = true;
return List.skip;
default:
if (!has_exit(node)) {
if (!has_exit(node) && can_hoist(node)) {
// hoist function declaration body
var body = node.body;
node.body = [];
@@ -307,10 +316,11 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
var expr;
switch ((node.start._permute * steps | 0) % 3) {
case 0:
if (!(node.init instanceof U.AST_Definitions
&& node.init.definitions[0].name instanceof U.AST_Destructured)) {
expr = node.init;
if (node.init instanceof U.AST_Definitions) {
if (node.init instanceof U.AST_Const) break;
if (node.init.definitions[0].name instanceof U.AST_Destructured) break;
}
expr = node.init;
break;
case 1:
expr = node.object;
@@ -358,7 +368,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
}
else if (node instanceof U.AST_PropAccess) {
var expr = [
node.expression,
!(node.expression instanceof U.AST_Super) && node.expression,
node.property instanceof U.AST_Node && !(parent instanceof U.AST_Destructured) && node.property,
][ node.start._permute++ % 2 ];
if (expr) {
@@ -370,11 +380,9 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
if (node.body instanceof U.AST_Call && node.body.expression instanceof U.AST_Function) {
// hoist simple statement IIFE function expression body
node.start._permute++;
if (!has_exit(node.body.expression)) {
var body = node.body.expression.body;
node.body.expression.body = [];
if (!has_exit(node.body.expression) && can_hoist(node.body.expression)) {
CHANGED = true;
return List.splice(body);
return List.splice(node.body.expression.body);
}
}
}
@@ -468,7 +476,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
}
// replace this node
var newNode = is_statement(node) ? new U.AST_EmptyStatement({
var newNode = U.is_statement(node) ? new U.AST_EmptyStatement({
start: {},
}) : U.parse(REPLACEMENTS[node.start._permute % REPLACEMENTS.length | 0], {
expression: true,
@@ -477,23 +485,27 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
CHANGED = true;
return newNode;
}, function(node, in_list) {
if (node instanceof U.AST_Sequence) {
if (node instanceof U.AST_Definitions) {
// remove empty var statement
if (node.definitions.length == 0) return in_list ? List.skip : new U.AST_EmptyStatement({
start: {},
});
} else if (node instanceof U.AST_ObjectMethod) {
if (!/Function$/.test(node.value.TYPE)) return new U.AST_ObjectKeyVal({
key: node.key,
value: node.value,
start: {},
});
} else if (node instanceof U.AST_Sequence) {
// expand single-element sequence
if (node.expressions.length == 1) return node.expressions[0];
}
else if (node instanceof U.AST_Try) {
} else if (node instanceof U.AST_Try) {
// expand orphaned try block
if (!node.bcatch && !node.bfinally) return new U.AST_BlockStatement({
body: node.body,
start: {},
});
}
else if (node instanceof U.AST_Definitions) {
// remove empty var statement
if (node.definitions.length == 0) return in_list ? List.skip : new U.AST_EmptyStatement({
start: {},
});
}
});
var diff_error_message;
@@ -662,12 +674,22 @@ function has_loopcontrol(body, loop, label) {
return found;
}
function is_timed_out(result) {
return sandbox.is_error(result) && /timed out/.test(result.message);
function can_hoist(body) {
var found = false;
body.walk(new U.TreeWalker(function(node) {
if (found) return true;
if (node instanceof U.AST_NewTarget) return found = true;
if (node instanceof U.AST_Scope) {
if (node === body) return;
return true;
}
if (node instanceof U.AST_Super) return found = true;
}));
return !found;
}
function is_statement(node) {
return node instanceof U.AST_Statement && !(node instanceof U.AST_LambdaExpression);
function is_timed_out(result) {
return sandbox.is_error(result) && /timed out/.test(result.message);
}
function merge_sequence(array, node) {
@@ -689,7 +711,7 @@ function to_sequence(expressions) {
}
function to_statement(node) {
return is_statement(node) ? node : new U.AST_SimpleStatement({
return U.is_statement(node) ? node : new U.AST_SimpleStatement({
body: node,
start: {},
});
@@ -724,7 +746,7 @@ function run_code(code, toplevel, result_cache, timeout) {
if (!value) {
var start = Date.now();
result_cache[key] = value = {
result: sandbox.run_code(code, toplevel, timeout),
result: sandbox.run_code(sandbox.patch_module_statements(code), toplevel, timeout),
elapsed: Date.now() - start,
};
}

98
test/release/acorn.sh Executable file
View File

@@ -0,0 +1,98 @@
#!/bin/sh
alias uglify-js=$PWD/bin/uglifyjs
UGLIFY_OPTIONS=$@
minify_in_situ() {
ARGS="$UGLIFY_OPTIONS --validate --in-situ"
DIRS="$1"
echo '> uglify-js' $DIRS $UGLIFY_OPTIONS
for i in `find $DIRS -type f -name '*.js'`
do
ARGS="$ARGS $i"
done
uglify-js $ARGS
}
rm -rf tmp/acorn \
&& git clone https://github.com/acornjs/acorn.git tmp/acorn \
&& cd tmp/acorn \
&& rm -rf .git/hooks \
&& git checkout 74b59384320ced82e09da2e8fdbed16810f7379a \
&& patch -l -p1 <<EOF
diff --git a/acorn-loose/rollup.config.js b/acorn-loose/rollup.config.js
index d2389b2..c37882b 100644
--- a/acorn-loose/rollup.config.js
+++ b/acorn-loose/rollup.config.js
@@ -1,2 +0,0 @@
-import buble from "rollup-plugin-buble"
-
@@ -23 +20,0 @@ export default {
- buble({transforms: {dangerousForOf: true}})
diff --git a/acorn-walk/rollup.config.js b/acorn-walk/rollup.config.js
index 67dd613..8c28807 100644
--- a/acorn-walk/rollup.config.js
+++ b/acorn-walk/rollup.config.js
@@ -1,2 +0,0 @@
-import buble from "rollup-plugin-buble"
-
@@ -19 +16,0 @@ export default {
- buble({transforms: {dangerousForOf: true}})
diff --git a/acorn/rollup.config.bin.js b/acorn/rollup.config.bin.js
index 8a082b0..b3eda60 100644
--- a/acorn/rollup.config.bin.js
+++ b/acorn/rollup.config.bin.js
@@ -1,2 +0,0 @@
-import buble from "rollup-plugin-buble"
-
@@ -11 +9 @@ export default {
- plugins: [buble()]
+ plugins: []
diff --git a/acorn/rollup.config.js b/acorn/rollup.config.js
index c775a0c..cfd4c68 100644
--- a/acorn/rollup.config.js
+++ b/acorn/rollup.config.js
@@ -1,2 +0,0 @@
-import buble from "rollup-plugin-buble"
-
@@ -19 +16,0 @@ export default {
- buble({transforms: {dangerousForOf: true}})
diff --git a/package.json b/package.json
index 382f59e..4612a75 100644
--- a/package.json
+++ b/package.json
@@ -24,4 +24 @@
- "prepare": "npm run test",
- "test": "node test/run.js && node test/lint.js",
- "pretest": "npm run build:main && npm run build:loose",
- "test:test262": "node bin/run_test262.js",
+ "test": "node test/run.js",
@@ -32,2 +29 @@
- "build:bin": "rollup -c acorn/rollup.config.bin.js",
- "lint": "eslint acorn/src/ acorn-walk/src/ acorn-loose/src/"
+ "build:bin": "rollup -c acorn/rollup.config.bin.js"
@@ -36,6 +31,0 @@
- "eslint": "^4.10.0",
- "eslint-config-standard": "^10.2.1",
- "eslint-plugin-import": "^2.2.0",
- "eslint-plugin-node": "^5.2.1",
- "eslint-plugin-promise": "^3.5.0",
- "eslint-plugin-standard": "^3.0.1",
@@ -43,4 +32,0 @@
- "rollup-plugin-buble": "^0.19.0",
- "test262": "git+https://github.com/tc39/test262.git#a6c819ad0f049f23f1a37af6b89dbb79fe3b9216",
- "test262-parser-runner": "^0.5.0",
- "test262-stream": "^1.2.1",
EOF
ERR=$?; if [ "$ERR" != "0" ]; then echo "Error: $ERR"; exit $ERR; fi
minify_in_situ "acorn/src" \
&& minify_in_situ "acorn-loose/src" \
&& minify_in_situ "acorn-walk/src" \
&& rm -rf node_modules \
&& npm install \
&& rm -rf acorn/dist acorn-loose/dist acorn-walk/dist \
&& npm run build \
&& minify_in_situ "acorn/dist" \
&& minify_in_situ "acorn-loose/dist" \
&& minify_in_situ "acorn-walk/dist" \
&& npm test

182
test/release/bootstrap.sh Executable file
View File

@@ -0,0 +1,182 @@
#!/bin/sh
alias uglify-js=$PWD/bin/uglifyjs
UGLIFY_OPTIONS=$@
minify_in_situ() {
ARGS="$UGLIFY_OPTIONS --validate --in-situ"
DIRS="$1"
echo '> uglify-js' $DIRS $UGLIFY_OPTIONS
for i in `find $DIRS -type f -name '*.js'`
do
ARGS="$ARGS $i"
done
uglify-js $ARGS
}
rm -rf tmp/bootstrap \
&& git clone --depth 1 --branch v5.0.0-beta2 https://github.com/twbs/bootstrap.git tmp/bootstrap \
&& cd tmp/bootstrap \
&& rm -rf .git/hooks \
&& patch -p1 <<EOF
--- a/.babelrc.js
+++ /dev/null
@@ -1,12 +0,0 @@
-module.exports = {
- presets: [
- [
- '@babel/preset-env',
- {
- loose: true,
- bugfixes: true,
- modules: false
- }
- ]
- ]
-};
--- a/.gitattributes
+++ b/.gitattributes
@@ -5,0 +6 @@
+*.png binary
--- a/build/build-plugins.js
+++ b/build/build-plugins.js
@@ -14 +13,0 @@ const rollup = require('rollup')
-const { babel } = require('@rollup/plugin-babel')
@@ -19,6 +17,0 @@ const plugins = [
- babel({
- // Only transpile our source code
- exclude: 'node_modules/**',
- // Include the helpers in each file, at most one copy of each
- babelHelpers: 'bundled'
- })
--- a/build/rollup.config.js
+++ b/build/rollup.config.js
@@ -4 +3,0 @@ const path = require('path')
-const { babel } = require('@rollup/plugin-babel')
@@ -15,6 +13,0 @@ const plugins = [
- babel({
- // Only transpile our source code
- exclude: 'node_modules/**',
- // Include the helpers in the bundle, at most one copy of each
- babelHelpers: 'bundled'
- })
--- a/js/tests/integration/rollup.bundle.js
+++ b/js/tests/integration/rollup.bundle.js
@@ -3 +2,0 @@
-const { babel } = require('@rollup/plugin-babel')
@@ -18,4 +16,0 @@ module.exports = {
- babel({
- exclude: 'node_modules/**',
- babelHelpers: 'bundled'
- })
--- a/js/tests/karma.conf.js
+++ b/js/tests/karma.conf.js
@@ -7,2 +6,0 @@ const ip = require('ip')
-const { babel } = require('@rollup/plugin-babel')
-const istanbul = require('rollup-plugin-istanbul')
@@ -84,13 +81,0 @@ const conf = {
- istanbul({
- exclude: [
- 'node_modules/**',
- 'js/tests/unit/**/*.spec.js',
- 'js/tests/helpers/**/*.js'
- ]
- }),
- babel({
- // Only transpile our source code
- exclude: 'node_modules/**',
- // Inline the required helpers in each file
- babelHelpers: 'inline'
- }),
@@ -142 +126,0 @@ if (BROWSERSTACK) {
- 'karma-coverage-istanbul-reporter'
@@ -144 +127,0 @@ if (BROWSERSTACK) {
- reporters.push('coverage-istanbul')
--- a/package.json
+++ b/package.json
@@ -23 +23 @@
- "start": "npm-run-all --parallel watch docs-serve",
+ "start": "npm-run-all --parallel watch",
@@ -28,3 +27,0 @@
- "css-lint": "npm-run-all --continue-on-error --parallel css-lint-*",
- "css-lint-stylelint": "stylelint \"**/*.{css,scss}\" --cache --cache-location .cache/.stylelintcache --rd",
- "css-lint-vars": "fusv scss/ site/assets/scss/",
@@ -44 +40,0 @@
- "js-lint": "eslint --cache --cache-location .cache/.eslintcache --report-unused-disable-directives .",
@@ -46,3 +42,3 @@
- "js-minify-standalone": "terser --compress passes=2 --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.js.map,includeSources,url=bootstrap.min.js.map\" --output dist/js/bootstrap.min.js dist/js/bootstrap.js",
- "js-minify-standalone-esm": "terser --compress passes=2 --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.esm.js.map,includeSources,url=bootstrap.esm.min.js.map\" --output dist/js/bootstrap.esm.min.js dist/js/bootstrap.esm.js",
- "js-minify-bundle": "terser --compress passes=2 --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.bundle.js.map,includeSources,url=bootstrap.bundle.min.js.map\" --output dist/js/bootstrap.bundle.min.js dist/js/bootstrap.bundle.js",
+ "js-minify-standalone": "../../bin/uglifyjs --compress passes=2 --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.js.map,includeSources,url=bootstrap.min.js.map\" --output dist/js/bootstrap.min.js dist/js/bootstrap.js",
+ "js-minify-standalone-esm": "../../bin/uglifyjs --compress passes=2 --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.esm.js.map,includeSources,url=bootstrap.esm.min.js.map\" --output dist/js/bootstrap.esm.min.js dist/js/bootstrap.esm.js",
+ "js-minify-bundle": "../../bin/uglifyjs --compress passes=2 --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.bundle.js.map,includeSources,url=bootstrap.bundle.min.js.map\" --output dist/js/bootstrap.bundle.min.js dist/js/bootstrap.bundle.js",
@@ -56,25 +52 @@
- "lint": "npm-run-all --parallel js-lint css-lint lockfile-lint",
- "docs": "npm-run-all docs-build docs-lint",
- "docs-build": "hugo --cleanDestinationDir",
- "docs-compile": "npm run docs-build",
- "docs-linkinator": "linkinator _gh_pages --recurse --silent --skip \"^(?!http://localhost)\"",
- "docs-vnu": "node build/vnu-jar.js",
- "docs-lint": "npm-run-all --parallel docs-vnu docs-linkinator",
- "docs-serve": "hugo server --port 9001 --disableFastRender",
- "docs-serve-only": "npx sirv-cli _gh_pages --port 9001",
- "lockfile-lint": "lockfile-lint --allowed-hosts npm --allowed-schemes https: --empty-hostname false --type npm --path package-lock.json",
- "update-deps": "ncu -u -x karma-browserstack-launcher,terser && npm update && echo Manually update site/assets/js/vendor",
- "release": "npm-run-all dist release-sri docs-build release-zip*",
- "release-sri": "node build/generate-sri.js",
- "release-version": "node build/change-version.js",
- "release-zip": "cross-env-shell \"rm -rf bootstrap-\$npm_package_version-dist && cp -r dist/ bootstrap-\$npm_package_version-dist && zip -r9 bootstrap-\$npm_package_version-dist.zip bootstrap-\$npm_package_version-dist && rm -rf bootstrap-\$npm_package_version-dist\"",
- "release-zip-examples": "node build/zip-examples.js",
- "dist": "npm-run-all --parallel css js",
- "test": "npm-run-all lint dist js-test docs-build docs-lint",
- "netlify": "cross-env-shell HUGO_BASEURL=\$DEPLOY_PRIME_URL npm-run-all dist release-sri docs-build",
- "watch": "npm-run-all --parallel watch-*",
- "watch-css-main": "nodemon --watch scss/ --ext scss --exec \"npm-run-all css-lint css-compile css-prefix\"",
- "watch-css-dist": "nodemon --watch dist/css/ --ext css --ignore \"dist/css/*.rtl.*\" --exec \"npm run css-rtl\"",
- "watch-css-docs": "nodemon --watch site/assets/scss/ --ext scss --exec \"npm run css-lint\"",
- "watch-js-main": "nodemon --watch js/src/ --ext js --exec \"npm-run-all js-lint js-compile\"",
- "watch-js-docs": "nodemon --watch site/assets/js/ --ext js --exec \"npm run js-lint\""
+ "dist": "npm run css && npm run js"
@@ -103,3 +74,0 @@
- "@babel/cli": "^7.12.13",
- "@babel/core": "^7.12.13",
- "@babel/preset-env": "^7.12.13",
@@ -107 +75,0 @@
- "@rollup/plugin-babel": "^5.2.3",
@@ -115,4 +82,0 @@
- "eslint": "^7.19.0",
- "eslint-config-xo": "^0.34.0",
- "eslint-plugin-import": "^2.22.1",
- "eslint-plugin-unicorn": "^27.0.0",
@@ -122 +85,0 @@
- "hugo-bin": "^0.68.0",
@@ -128 +90,0 @@
- "karma-coverage-istanbul-reporter": "^3.0.3",
@@ -134,2 +95,0 @@
- "linkinator": "^2.13.4",
- "lockfile-lint": "^4.3.7",
@@ -141 +100,0 @@
- "rollup-plugin-istanbul": "^3.0.0",
@@ -144,5 +103 @@
- "shelljs": "^0.8.4",
- "stylelint": "^13.9.0",
- "stylelint-config-twbs-bootstrap": "^2.1.0",
- "terser": "5.1.0",
- "vnu-jar": "21.2.5"
+ "shelljs": "^0.8.4"
@@ -155,3 +109,0 @@
- "hugo-bin": {
- "buildTags": "extended"
- },
EOF
ERR=$?; if [ "$ERR" != "0" ]; then echo "Error: $ERR"; exit $ERR; fi
rm -rf node_modules \
&& npm ci \
&& minify_in_situ "node_modules/@popperjs/core" \
&& rm -rf dist/js/* \
&& minify_in_situ "build" \
&& minify_in_situ "js" \
&& minify_in_situ "site" \
&& npm run dist \
&& minify_in_situ "dist" \
&& npm run js-test

45
test/release/buble.sh Executable file
View File

@@ -0,0 +1,45 @@
#!/bin/sh
alias uglify-js=$PWD/bin/uglifyjs
UGLIFY_OPTIONS=$@
minify_in_situ() {
ARGS="$UGLIFY_OPTIONS --validate --in-situ"
DIRS="$1"
echo '> uglify-js' $DIRS $UGLIFY_OPTIONS
for i in `find $DIRS -type f -name '*.js'`
do
ARGS="$ARGS $i"
done
uglify-js $ARGS
}
rm -rf tmp/buble \
&& git clone https://github.com/bublejs/buble.git tmp/buble \
&& cd tmp/buble \
&& rm -rf .git/hooks \
&& git checkout dcc5ab02c9af6ddaad94e587c4911677340ec100 \
&& patch -l -p1 <<EOF
--- a/package.json
+++ b/package.json
@@ -29 +28,0 @@
- "prepublish": "npm test",
@@ -67,3 +66 @@
- "source-map-support": "^0.5.16",
- "test262": "git+https://github.com/tc39/test262.git#4f1155c566a222238fd86f179c6635ecb4c289bb",
- "test262-stream": "^1.3.0"
+ "source-map-support": "^0.5.16"
--- a/src/program/BlockStatement.js
+++ b/src/program/BlockStatement.js
@@ -309 +309 @@ export default class BlockStatement extends Node {
- let cont = false; // TODO implement proper continue...
+ let cont = !declarations; // TODO implement proper continue...
EOF
ERR=$?; if [ "$ERR" != "0" ]; then echo "Error: $ERR"; exit $ERR; fi
minify_in_situ "src" \
&& rm -rf node_modules \
&& npm ci \
&& rm -rf dist \
&& npm run build \
&& minify_in_situ "dist" \
&& node_modules/.bin/mocha

45
test/release/butternut.sh Executable file
View File

@@ -0,0 +1,45 @@
#!/bin/sh
alias uglify-js=$PWD/bin/uglifyjs
UGLIFY_OPTIONS=$@
minify_in_situ() {
ARGS="$UGLIFY_OPTIONS --validate --in-situ"
DIRS="$1"
echo '> uglify-js' $DIRS $UGLIFY_OPTIONS
for i in `find $DIRS -type f -name '*.js'`
do
ARGS="$ARGS $i"
done
uglify-js $ARGS
}
rm -rf tmp/butternut \
&& git clone https://github.com/Rich-Harris/butternut.git tmp/butternut \
&& cd tmp/butternut \
&& rm -rf .git/hooks \
&& patch -l -p1 <<EOF
--- a/package.json
+++ b/package.json
@@ -25 +24,0 @@
- "prepublish": "npm run test:min",
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -1 +0,0 @@
-import buble from 'rollup-plugin-buble';
@@ -28,6 +26,0 @@ const config = {
- buble({
- include: ['src/**', 'node_modules/acorn/**'],
- transforms: {
- dangerousForOf: true
- }
- }),
EOF
ERR=$?; if [ "$ERR" != "0" ]; then echo "Error: $ERR"; exit $ERR; fi
minify_in_situ "src" \
&& rm -rf node_modules \
&& npm install \
&& rm -rf dist \
&& npm run build \
&& minify_in_situ "dist" \
&& node_modules/.bin/mocha test/test.js

54
test/release/install.sh Executable file
View File

@@ -0,0 +1,54 @@
echo "::group::GitHub Environment Variables"
echo "CI: $CI"
echo "GITHUB_WORKFLOW: $GITHUB_WORKFLOW"
echo "GITHUB_RUN_ID: $GITHUB_RUN_ID"
echo "GITHUB_RUN_NUMBER: $GITHUB_RUN_NUMBER"
echo "GITHUB_ACTION: $GITHUB_ACTION"
echo "GITHUB_ACTIONS: $GITHUB_ACTIONS"
echo "GITHUB_ACTOR: $GITHUB_ACTOR"
echo "GITHUB_REPOSITORY: $GITHUB_REPOSITORY"
echo "GITHUB_EVENT_NAME: $GITHUB_EVENT_NAME"
echo "GITHUB_EVENT_PATH: $GITHUB_EVENT_PATH"
echo "GITHUB_WORKSPACE: $GITHUB_WORKSPACE"
echo "GITHUB_SHA: $GITHUB_SHA"
echo "GITHUB_REF: $GITHUB_REF"
echo "GITHUB_HEAD_REF: $GITHUB_HEAD_REF"
echo "GITHUB_BASE_REF: $GITHUB_BASE_REF"
echo "GITHUB_SERVER_URL: $GITHUB_SERVER_URL"
echo "GITHUB_API_URL: $GITHUB_API_URL"
echo "GITHUB_GRAPHQL_URL: $GITHUB_GRAPHQL_URL"
echo "::endgroup::"
if command -v timeout &> /dev/null; then NATIVE=1; fi
timeout() {
T=$1
shift
shift
shift
expect <<EOF
set timeout $T
spawn -noecho sh -c "$@"
expect timeout { exit 124 } eof
catch wait ret
exit [lindex \$ret 3]
EOF
return $?
}
if [ $NATIVE ]; then unset -f timeout; fi
git clone --branch v1.6.0 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
while ! timeout 60 bash -c ". ~/.nvs/nvs.sh add $NODE && nvs use $NODE"; do
cd ~/.nvs
while !(git clean -xdf); do echo "'git clean' failed - retrying..."; done
cd -
done
. ~/.nvs/nvs.sh --version
nvs use $NODE
node --version
npm config set audit false
npm config set optional false
npm config set save false
npm config set strict-ssl false
npm config set update-notifier false
npm --version
while !(npm install); do echo "'npm install' failed - retrying..."; done

198
test/release/mathjs.sh Executable file
View File

@@ -0,0 +1,198 @@
#!/bin/sh
alias uglify-js=$PWD/bin/uglifyjs
UGLIFY_OPTIONS="--annotations $@"
minify_in_situ() {
ARGS="$UGLIFY_OPTIONS --validate --in-situ"
DIRS="$1"
echo '> uglify-js' $DIRS $UGLIFY_OPTIONS
for i in `find $DIRS -type f -name '*.js'`
do
ARGS="$ARGS $i"
done
for i in `find $DIRS -type f -name '*.mjs'`
do
ARGS="$ARGS $i"
done
uglify-js $ARGS
}
rm -rf tmp/mathjs \
&& git clone --depth 1 --branch v9.2.0 https://github.com/josdejong/mathjs.git tmp/mathjs \
&& cd tmp/mathjs \
&& rm -rf .git/hooks \
&& patch -l -p1 <<EOF
--- a/gulpfile.cjs
+++ b/gulpfile.cjs
@@ -74 +74 @@ const webpackConfig = {
- mode: 'production',
+ mode: 'development',
--- a/package.json
+++ b/package.json
@@ -132,2 +131,0 @@
- "prepublishOnly": "npm run test:all && npm run lint",
- "prepare": "npm run build",
--- a/src/utils/string.js
+++ b/src/utils/string.js
@@ -15,0 +16,7 @@ export function endsWith (text, search) {
+export function HACK (value) {
+ if (typeof value == "object") {
+ (value = Object.create(value)).valueOf = function() { return this }
+ }
+ return value
+}
+
@@ -68 +75 @@ export function format (value, options) {
- return value.toString()
+ return HACK(value).toString()
--- a/test/node-tests/cli/cli.test.js
+++ b/test/node-tests/cli/cli.test.js
@@ -36 +35,0 @@ describe('command line interface', function () {
- const path2 = path.join(__dirname, 'script2')
@@ -38,2 +37,2 @@ describe('command line interface', function () {
- run('"' + path1 + '" "' + path2 + '"', function (e, result) {
- assert.strictEqual(result, '2\n8\n')
+ run('"' + path1 + '"', function (e, result) {
+ assert.strictEqual(result, '2\n')
--- a/test/unit-tests/expression/node/Node.test.js
+++ b/test/unit-tests/expression/node/Node.test.js
@@ -157 +157 @@ describe('Node', function () {
- assert.throws(function () {
+ if (0) assert.throws(function () {
--- a/test/unit-tests/expression/parse.test.js
+++ b/test/unit-tests/expression/parse.test.js
@@ -0,0 +1 @@
+import { HACK } from '../../../src/utils/string.js'
@@ -333 +334 @@ describe('parse', function () {
- assert.strictEqual(fmath.parse('1/3').compile().evaluate().toString(), '0.(3)')
+ assert.strictEqual(HACK(fmath.parse('1/3').compile().evaluate()).toString(), '0.(3)')
--- a/test/unit-tests/function/arithmetic/abs.test.js
+++ b/test/unit-tests/function/arithmetic/abs.test.js
@@ -0,0 +1 @@
+import { HACK } from '../../../../src/utils/string.js'
@@ -35,3 +36,3 @@ describe('abs', function () {
- assert.strictEqual(abs(a).toString(), '0.(3)')
- assert.strictEqual(a.toString(), '-0.(3)')
- assert.strictEqual(abs(fraction('1/3')).toString(), '0.(3)')
+ assert.strictEqual(HACK(abs(a)).toString(), '0.(3)')
+ assert.strictEqual(HACK(a).toString(), '-0.(3)')
+ assert.strictEqual(HACK(abs(fraction('1/3'))).toString(), '0.(3)')
--- a/test/unit-tests/function/arithmetic/addScalar.test.js
+++ b/test/unit-tests/function/arithmetic/addScalar.test.js
@@ -0,0 +1 @@
+import { HACK } from '../../../../src/utils/string.js'
@@ -71 +72 @@ describe('addScalar', function () {
- assert.strictEqual(a.toString(), '0.(3)')
+ assert.strictEqual(HACK(a).toString(), '0.(3)')
@@ -73 +74 @@ describe('addScalar', function () {
- assert.strictEqual(add(math.fraction(1), math.fraction(1, 3)).toString(), '1.(3)')
+ assert.strictEqual(HACK(add(math.fraction(1), math.fraction(1, 3))).toString(), '1.(3)')
--- a/test/unit-tests/function/arithmetic/ceil.test.js
+++ b/test/unit-tests/function/arithmetic/ceil.test.js
@@ -0,0 +1 @@
+import { HACK } from '../../../../src/utils/string.js'
@@ -88 +89 @@ describe('ceil', function () {
- assert.strictEqual(a.toString(), '0.(6)')
+ assert.strictEqual(HACK(a).toString(), '0.(6)')
@@ -105 +106 @@ describe('ceil', function () {
- assert.strictEqual(a.toString(), '0.(6)')
+ assert.strictEqual(HACK(a).toString(), '0.(6)')
@@ -107 +108 @@ describe('ceil', function () {
- assert.strictEqual(a.toString(), '0.(6)')
+ assert.strictEqual(HACK(a).toString(), '0.(6)')
--- a/test/unit-tests/function/arithmetic/fix.test.js
+++ b/test/unit-tests/function/arithmetic/fix.test.js
@@ -0,0 +1 @@
+import { HACK } from '../../../../src/utils/string.js'
@@ -107 +108 @@ describe('fix', function () {
- assert.strictEqual(a.toString(), '0.(6)')
+ assert.strictEqual(HACK(a).toString(), '0.(6)')
@@ -124 +125 @@ describe('fix', function () {
- assert.strictEqual(a.toString(), '0.(6)')
+ assert.strictEqual(HACK(a).toString(), '0.(6)')
@@ -127 +128 @@ describe('fix', function () {
- assert.strictEqual(b.toString(), '-0.(6)')
+ assert.strictEqual(HACK(b).toString(), '-0.(6)')
--- a/test/unit-tests/function/arithmetic/floor.test.js
+++ b/test/unit-tests/function/arithmetic/floor.test.js
@@ -0,0 +1 @@
+import { HACK } from '../../../../src/utils/string.js'
@@ -96 +97 @@ describe('floor', function () {
- assert.strictEqual(a.toString(), '0.(6)')
+ assert.strictEqual(HACK(a).toString(), '0.(6)')
--- a/test/unit-tests/function/arithmetic/gcd.test.js
+++ b/test/unit-tests/function/arithmetic/gcd.test.js
@@ -0,0 +1 @@
+import { HACK } from '../../../../src/utils/string.js'
@@ -62 +63 @@ describe('gcd', function () {
- assert.strictEqual(gcd(a, math.fraction(3, 7)).toString(), '0.017(857142)')
+ assert.strictEqual(HACK(gcd(a, math.fraction(3, 7))).toString(), '0.017(857142)')
--- a/test/unit-tests/function/arithmetic/multiply.test.js
+++ b/test/unit-tests/function/arithmetic/multiply.test.js
@@ -0,0 +1 @@
+import { HACK } from '../../../../src/utils/string.js'
@@ -129 +130 @@ describe('multiply', function () {
- assert.strictEqual(multiply(math.fraction(2), math.fraction(1, 3)).toString(), '0.(6)')
+ assert.strictEqual(HACK(multiply(math.fraction(2), math.fraction(1, 3))).toString(), '0.(6)')
--- a/test/unit-tests/function/arithmetic/round.test.js
+++ b/test/unit-tests/function/arithmetic/round.test.js
@@ -0,0 +1 @@
+import { HACK } from '../../../../src/utils/string.js'
@@ -82 +83 @@ describe('round', function () {
- assert.strictEqual(a.toString(), '0.(6)')
+ assert.strictEqual(HACK(a).toString(), '0.(6)')
--- a/test/unit-tests/function/arithmetic/subtract.test.js
+++ b/test/unit-tests/function/arithmetic/subtract.test.js
@@ -0,0 +1 @@
+import { HACK } from '../../../../src/utils/string.js'
@@ -76,2 +77,2 @@ describe('subtract', function () {
- assert.strictEqual(subtract(a, math.fraction(1, 6)).toString(), '0.1(6)')
- assert.strictEqual(a.toString(), '0.(3)')
+ assert.strictEqual(HACK(subtract(a, math.fraction(1, 6))).toString(), '0.1(6)')
+ assert.strictEqual(HACK(a).toString(), '0.(3)')
@@ -80 +81 @@ describe('subtract', function () {
- assert.strictEqual(subtract(math.fraction(1), math.fraction(1, 3)).toString(), '0.(6)')
+ assert.strictEqual(HACK(subtract(math.fraction(1), math.fraction(1, 3))).toString(), '0.(6)')
--- a/test/unit-tests/function/arithmetic/unaryMinus.test.js
+++ b/test/unit-tests/function/arithmetic/unaryMinus.test.js
@@ -0,0 +1 @@
+import { HACK } from '../../../../src/utils/string.js'
@@ -31 +32 @@ describe('unaryMinus', function () {
- assert.deepStrictEqual(math.unaryMinus(bignumber(0)).toString(), '0')
+ assert.deepStrictEqual(HACK(math.unaryMinus(bignumber(0))).toString(), '0')
--- a/test/unit-tests/function/relational/compare.test.js
+++ b/test/unit-tests/function/relational/compare.test.js
@@ -0,0 +1 @@
+import { HACK } from '../../../../src/utils/string.js'
@@ -76,2 +77,2 @@ describe('compare', function () {
- assert.strictEqual(a.toString(), '0.(3)')
- assert.strictEqual(b.toString(), '0.1(6)')
+ assert.strictEqual(HACK(a).toString(), '0.(3)')
+ assert.strictEqual(HACK(b).toString(), '0.1(6)')
--- a/test/unit-tests/function/relational/compareNatural.test.js
+++ b/test/unit-tests/function/relational/compareNatural.test.js
@@ -0,0 +1 @@
+import { HACK } from '../../../../src/utils/string.js'
@@ -57,2 +58,2 @@ describe('compareNatural', function () {
- assert.strictEqual(a.toString(), '0.(3)')
- assert.strictEqual(b.toString(), '0.1(6)')
+ assert.strictEqual(HACK(a).toString(), '0.(3)')
+ assert.strictEqual(HACK(b).toString(), '0.1(6)')
--- a/test/unit-tests/type/matrix/Matrix.test.js
+++ b/test/unit-tests/type/matrix/Matrix.test.js
@@ -44 +44 @@ describe('matrix', function () {
- assert.throws(function () { m.toString() }, /Cannot invoke toString on a Matrix interface/)
+ if (0) assert.throws(function () { m.toString() }, /Cannot invoke toString on a Matrix interface/)
EOF
ERR=$?; if [ "$ERR" != "0" ]; then echo "Error: $ERR"; exit $ERR; fi
minify_in_situ "bin" \
&& minify_in_situ "src" \
&& minify_in_situ "test" \
&& minify_in_situ "tools" \
&& rm -rf node_modules \
&& npm ci \
&& rm -rf lib \
&& npm run build \
&& minify_in_situ "lib" \
&& npm run test:all

84
test/release/rollup-es.sh Executable file
View File

@@ -0,0 +1,84 @@
#!/bin/sh
alias uglify-js=$PWD/bin/uglifyjs
UGLIFY_OPTIONS=$@
minify_in_situ() {
ARGS="$UGLIFY_OPTIONS --validate --in-situ"
DIRS="$1"
echo '> uglify-js' $DIRS $UGLIFY_OPTIONS
for i in `find $DIRS -type f -name '*.js'`
do
ARGS="$ARGS $i"
done
uglify-js $ARGS
}
rm -rf tmp/rollup \
&& git clone https://github.com/rollup/rollup.git tmp/rollup \
&& cd tmp/rollup \
&& rm -rf .git/hooks \
&& git checkout 3d80c06f895eab41e648ee99786fa68c72458b80 \
&& patch -l -p1 <<EOF
--- a/package.json
+++ b/package.json
@@ -23 +22,0 @@
- "prepublishOnly": "npm run lint && npm run test:only && npm run test:leak",
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -1,5 +1,4 @@
import { readFileSync } from 'fs';
-import buble from 'rollup-plugin-buble';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import json from 'rollup-plugin-json';
@@ -25,12 +24,6 @@ export default [
input: 'src/node-entry.js',
plugins: [
json(),
- buble({
- include: ['src/**', 'node_modules/acorn/**'],
- target: {
- node: '4'
- }
- }),
resolve(),
commonjs()
],
@@ -48,12 +41,6 @@ export default [
input: 'src/browser-entry.js',
plugins: [
json(),
- buble({
- include: ['src/**', 'node_modules/acorn/**'],
- target: {
- node: '4'
- }
- }),
resolve(),
commonjs(),
{
@@ -80,7 +67,6 @@ export default [
plugins: [
string({ include: '**/*.md' }),
json(),
- buble({ target: { node: 4 } }),
commonjs({
include: 'node_modules/**'
}),
--- a/test/mocha.opts
+++ b/test/mocha.opts
@@ -1,2 +1 @@
---require buble/register
test/test.js
EOF
ERR=$?; if [ "$ERR" != "0" ]; then echo "Error: $ERR"; exit $ERR; fi
minify_in_situ "bin" \
&& minify_in_situ "browser" \
&& minify_in_situ "src" \
&& rm -rf node_modules \
&& npm ci \
&& rm -rf dist \
&& npm run build \
&& minify_in_situ "dist" \
&& node_modules/.bin/mocha

52
test/release/rollup-ts.sh Executable file
View File

@@ -0,0 +1,52 @@
#!/bin/sh
alias uglify-js=$PWD/bin/uglifyjs
UGLIFY_OPTIONS=$@
minify_in_situ() {
ARGS="$UGLIFY_OPTIONS --validate --in-situ"
DIRS="$1"
echo '> uglify-js' $DIRS $UGLIFY_OPTIONS
for i in `find $DIRS -type f -name '*.js'`
do
ARGS="$ARGS $i"
done
uglify-js $ARGS
for i in `find $DIRS -type f -name '*.ts' | grep -v '\.d\.ts'`
do
echo "$i"
node_modules/.bin/esbuild --loader=ts --target=node14 < "$i" \
| uglify-js $UGLIFY_OPTIONS -o "$i"
done
}
rm -rf tmp/rollup \
&& git clone --depth 1 --branch v2.39.1 https://github.com/rollup/rollup.git tmp/rollup \
&& cd tmp/rollup \
&& rm -rf .git/hooks \
&& patch -l -p1 <<EOF
--- a/package.json
+++ b/package.json
@@ -27,4 +26,0 @@
- "postinstall": "husky install",
- "postpublish": "pinst --enable",
- "prepare": "npm run build",
- "prepublishOnly": "pinst --disable && npm ci && npm run lint:nofix && npm run security && npm run build:bootstrap && npm run test:all",
--- a/test/cli/index.js
+++ b/test/cli/index.js
@@ -13,0 +14,3 @@ sander.rimrafSync(__dirname, 'node_modules');
+sander.rimrafSync(__dirname, 'samples', 'watch', 'bundle-error');
+sander.rimrafSync(__dirname, 'samples', 'watch', 'watch-config-error');
+sander.rimrafSync(__dirname, 'samples', 'watch', 'watch-config-initial-error');
EOF
ERR=$?; if [ "$ERR" != "0" ]; then echo "Error: $ERR"; exit $ERR; fi
npm install esbuild-wasm@0.8.56 \
&& minify_in_situ "cli" \
&& minify_in_situ "src" \
&& rm -rf node_modules \
&& npm ci \
&& rm -rf dist \
&& npm run build \
&& minify_in_situ "dist" \
&& node_modules/.bin/mocha test/test.js \
&& node_modules/.bin/mocha test/browser/index.js

90
test/release/sucrase.sh Executable file
View File

@@ -0,0 +1,90 @@
#!/bin/sh
alias uglify-js=$PWD/bin/uglifyjs
UGLIFY_OPTIONS=$@
minify_in_situ() {
ARGS="$UGLIFY_OPTIONS --validate --in-situ"
DIRS="$1"
echo '> uglify-js' $DIRS $UGLIFY_OPTIONS
for i in `find $DIRS -type f -name '*.js'`
do
ARGS="$ARGS $i"
done
for i in `find $DIRS -type f -name '*.mjs'`
do
ARGS="$ARGS $i"
done
uglify-js $ARGS
for i in `find $DIRS -type f -name '*.ts' | grep -v '\.d\.ts'`
do
echo "$i"
node_modules/.bin/esbuild --loader=ts --target=node14 < "$i" \
| uglify-js $UGLIFY_OPTIONS -o "$i"
done
}
rm -rf tmp/sucrase \
&& git clone https://github.com/alangpierce/sucrase.git tmp/sucrase \
&& cd tmp/sucrase \
&& rm -rf .git/hooks \
&& git checkout 38b66f3009feb76750a799deea211adcc83574f1 \
&& patch -l -p1 <<EOF
--- a/package.json
+++ b/package.json
@@ -25 +24,0 @@
- "prepublishOnly": "yarn clean && yarn build",
@@ -65 +63,0 @@
- "test262-harness": "^6.5.0",
--- a/script/build.ts
+++ b/script/build.ts
@@ -16 +15,0 @@ async function main(): Promise<void> {
- () => buildBenchmark(),
@@ -18,5 +16,0 @@ async function main(): Promise<void> {
- () => buildIntegration("./integrations/gulp-plugin"),
- () => buildIntegration("./integrations/jest-plugin"),
- () => buildIntegration("./integrations/webpack-loader"),
- () => buildIntegration("./integrations/webpack-object-rest-spread-plugin"),
- () => buildWebsite(),
@@ -66,3 +59,0 @@ async function buildSucrase(): Promise<void> {
- // Also add in .d.ts files from tsc, which only need to be compiled once.
- await run(\`\${TSC} --project ./src --outDir ./dist-types\`);
- await mergeDirectoryContents("./dist-types/src", "./dist");
@@ -70 +61 @@ async function buildSucrase(): Promise<void> {
- await run("yarn link");
+ await run("npm link");
--- a/src/identifyShadowedGlobals.ts
+++ b/src/identifyShadowedGlobals.ts
@@ -23,0 +24 @@ export default function identifyShadowedGlobals(
+export { identifyShadowedGlobals as HACK };
--- a/src/parser/tokenizer/state.ts
+++ b/src/parser/tokenizer/state.ts
@@ -100,0 +101 @@ export default class State {
+export { State as HACK };
--- a/src/transformers/JSXTransformer.ts
+++ b/src/transformers/JSXTransformer.ts
@@ -253,0 +254 @@ export default class JSXTransformer extends Transformer {
+export { JSXTransformer as HACK };
--- a/src/util/getClassInfo.ts
+++ b/src/util/getClassInfo.ts
@@ -164,0 +165 @@ export default function getClassInfo(
+export { getClassInfo as HACK };
--- a/src/util/getDeclarationInfo.ts
+++ b/src/util/getDeclarationInfo.ts
@@ -40,0 +41 @@ export default function getDeclarationInfo(tokens: TokenProcessor): DeclarationI
+export { getDeclarationInfo as HACK };
--- a/src/util/getJSXPragmaInfo.ts
+++ b/src/util/getJSXPragmaInfo.ts
@@ -14,0 +15 @@ export default function getJSXPragmaInfo(options: Options): JSXPragmaInfo {
+export { getJSXPragmaInfo as HACK };
EOF
ERR=$?; if [ "$ERR" != "0" ]; then echo "Error: $ERR"; exit $ERR; fi
npm install esbuild-wasm@0.8.56 \
&& minify_in_situ "src" \
&& rm -rf node_modules \
&& npm install \
&& npm run clean \
&& npm run build \
&& minify_in_situ "dist" \
&& minify_in_situ "dist-self-build" \
&& npm run test-only

View File

@@ -0,0 +1,47 @@
#!/bin/sh
alias uglify-js="node --max-old-space-size=4096 $PWD/bin/uglifyjs"
UGLIFY_OPTIONS=$@
minify_in_situ() {
ARGS="$UGLIFY_OPTIONS --validate --in-situ"
DIRS="$1"
echo '> uglify-js' $DIRS $UGLIFY_OPTIONS
for i in `find $DIRS -type f -name '*.js'`
do
ARGS="$ARGS $i"
done
uglify-js $ARGS
}
rm -rf tmp/web-tooling-benchmark \
&& git clone --depth 1 --branch v0.5.3 https://github.com/v8/web-tooling-benchmark.git tmp/web-tooling-benchmark \
&& cd tmp/web-tooling-benchmark \
&& rm -rf .git/hooks \
&& patch -l -p1 <<EOF
--- a/package.json
+++ b/package.json
@@ -12 +11,0 @@
- "postinstall": "npm run build:terser-bundled && npm run build:uglify-js-bundled && npm run build",
--- a/src/cli-flags-helper.js
+++ b/src/cli-flags-helper.js
@@ -7 +6,0 @@ const targetList = new Set([
- "chai",
--- a/src/cli.js
+++ b/src/cli.js
@@ -18,0 +19 @@ suite.on("error", event => {
+ global.process.exitCode = 42;
EOF
ERR=$?; if [ "$ERR" != "0" ]; then echo "Error: $ERR"; exit $ERR; fi
minify_in_situ "src" \
&& minify_in_situ "third_party" \
&& rm -rf node_modules \
&& npm ci \
&& rm -rf build/* \
&& npm run build:terser-bundled \
&& npm run build:uglify-js-bundled \
&& minify_in_situ "build" \
&& rm -rf dist \
&& npm run build \
&& minify_in_situ "dist" \
&& node dist/cli.js

View File

@@ -25,7 +25,13 @@ exports.run_code = semver.satisfies(process.version, "0.8") ? function(code, top
} while (prev !== stdout);
return stdout;
} : semver.satisfies(process.version, "<0.12") ? run_code_vm : function(code, toplevel, timeout) {
if (/\basync([ \t]+[^\s()[\]{},.&|!~=*%/+-]+|[ \t]*\([\s\S]*?\))[ \t]*=>|\b(async[ \t]+function|setInterval|setTimeout)\b/.test(code)) {
if ([
/\basync[ \t]*\([\s\S]*?\)[ \t]*=>/,
/\b(async[ \t]+function|setImmediate|setInterval|setTimeout)\b/,
/\basync([ \t]+|[ \t]*\*[ \t]*)[^\s()[\]{},.&|!~=*%/+-]+(\s*\(|[ \t]*=>)/,
].some(function(pattern) {
return pattern.test(code);
})) {
return run_code_exec(code, toplevel, timeout);
} else {
return run_code_vm(code, toplevel, timeout);
@@ -43,6 +49,30 @@ exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expec
} : function(expected, actual) {
return typeof expected == typeof actual && strip_func_ids(expected) == strip_func_ids(actual);
};
exports.patch_module_statements = function(code) {
var count = 0, imports = [];
code = code.replace(/\bexport(?:\s*\{[^}]*}\s*?(?:$|\n|;)|\s+default\b(?:\s*(\(|\{|class\s*\{|class\s+(?=extends\b)|(?:async\s+)?function\s*(?:\*\s*)?\())?|\b)/g, function(match, header) {
if (!header) return "";
if (header.length == 1) return "0, " + header;
return header.slice(0, -1) + " _" + ++count + header.slice(-1);
}).replace(/\bimport\.meta\b/g, function() {
return '({ url: "https://example.com/path/index.html" })';
}).replace(/\bimport\b(?:\s*([^('"]+)\bfrom\b)?\s*(['"]).*?\2(?:$|\n|;)/g, function(match, symbols) {
if (symbols) {
if (!/^[{*]/.test(symbols)) symbols = "default:" + symbols;
symbols = symbols.replace(/[{}]/g, "").trim().replace(/\s*,\s*/g, ",");
symbols = symbols.replace(/\*/, '"*"').replace(/\bas\s+(?!$|,|as\s)/g, ":");
imports.push([
"const {",
symbols,
"} = new Proxy(Object.create(null), { get(_, value) { return { value }; } });",
].join(""));
}
return "";
});
imports.push("");
return imports.join("\n") + code;
};
function is_error(result) {
return result && typeof result.name == "string" && typeof result.message == "string";
@@ -147,12 +177,14 @@ function setup(global, builtins, setup_log, setup_tty) {
[
// for Node.js v0.12
"Buffer",
"clearImmediate",
"clearInterval",
"clearTimeout",
// for Node.js v0.12
"DTRACE_NET_STREAM_END",
// for Node.js v8
"process",
"setImmediate",
"setInterval",
"setTimeout",
].forEach(function(name) {
@@ -170,9 +202,13 @@ function setup(global, builtins, setup_log, setup_tty) {
});
Object.defineProperties(global, props);
// for Node.js v8+
global.toString = function() {
return "[object global]";
};
if (global.toString !== Object.prototype.toString) {
global.__proto__ = Object.defineProperty(Object.create(global.__proto__), "toString", {
value: function() {
return "[object global]";
},
});
}
function self() {
return this;
@@ -186,7 +222,7 @@ function setup(global, builtins, setup_log, setup_tty) {
if (arg === global) return "[object global]";
if (/Error$/.test(arg.name)) return arg.toString();
if (typeof arg.then == "function") return "[object Promise]";
arg.constructor.toString();
if (arg.constructor) arg.constructor.toString();
var index = cache.original.indexOf(arg);
if (index >= 0) return cache.replaced[index];
if (--cache.level < 0) return "[object Object]";

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
exports["Dictionary"] = Dictionary;
exports["is_statement"] = is_statement;
exports["List"] = List;
exports["minify"] = minify;
exports["parse"] = parse;