diff options
author | Alex Lam S.L <alexlamsl@gmail.com> | 2019-10-29 16:53:48 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-10-29 16:53:48 +0800 |
commit | bad664c6322538f1a5eec7f76dc44f81fa05c9e0 (patch) | |
tree | c8bb72ced239cd86cdb36f2efdf668f7df8a56df | |
parent | 8a191c0a8486bfd2a51562f0c072e00507abea6e (diff) | |
download | tracifyjs-bad664c6322538f1a5eec7f76dc44f81fa05c9e0.tar.gz tracifyjs-bad664c6322538f1a5eec7f76dc44f81fa05c9e0.zip |
compress object literals (#3546)
-rw-r--r-- | README.md | 40 | ||||
-rw-r--r-- | lib/compress.js | 36 | ||||
-rw-r--r-- | test/compress/objects.js | 223 |
3 files changed, 280 insertions, 19 deletions
@@ -478,42 +478,42 @@ if (result.error) throw result.error; ## Minify options -- `warnings` (default `false`) — pass `true` to return compressor warnings - in `result.warnings`. Use the value `"verbose"` for more detailed warnings. - -- `parse` (default `{}`) — pass an object if you wish to specify some - additional [parse options](#parse-options). - - `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. + +- `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 an object to specify [mangle options](#mangle-options) (see below). - `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 + 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 additional [output options](#output-options). The defaults are optimized for best compression. -- `sourceMap` (default `false`) - pass an object if you wish to specify +- `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 [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. -- `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. - -- `ie8` (default `false`) - set to `true` to support IE8. - -- `keep_fnames` (default: `false`) - pass `true` to prevent discarding or mangling - of function names. Useful for code relying on `Function.prototype.name`. +- `warnings` (default `false`) — pass `true` to return compressor warnings + in `result.warnings`. Use the value `"verbose"` for more detailed warnings. ## Minify options structure @@ -682,6 +682,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u 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. + - `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. diff --git a/lib/compress.js b/lib/compress.js index 10161337..3058b678 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -74,6 +74,7 @@ function Compressor(options, false_by_default) { keep_infinity : false, loops : !false_by_default, negate_iife : !false_by_default, + objects : !false_by_default, passes : 1, properties : !false_by_default, pure_getters : !false_by_default && "strict", @@ -6981,6 +6982,41 @@ merge(Compressor.prototype, { return self; }); + OPT(AST_Object, function(self, compressor) { + if (!compressor.option("objects") || compressor.has_directive("use strict")) return self; + var props = self.properties; + var keys = new Dictionary(); + var values = []; + self.properties.forEach(function(prop) { + if (typeof prop.key != "string") { + flush(); + values.push(prop); + return; + } + if (prop.value.has_side_effects(compressor)) { + flush(); + } + keys.add(prop.key, prop.value); + }); + flush(); + if (self.properties.length != values.length) { + return make_node(AST_Object, self, { + properties: values + }); + } + return self; + + function flush() { + keys.each(function(expressions, key) { + values.push(make_node(AST_ObjectKeyVal, self, { + key: key, + value: make_sequence(self, expressions) + })); + }); + keys = new Dictionary(); + } + }); + OPT(AST_Return, function(self, compressor) { if (self.value && is_undefined(self.value, compressor)) { self.value = null; diff --git a/test/compress/objects.js b/test/compress/objects.js new file mode 100644 index 00000000..fb1e5865 --- /dev/null +++ b/test/compress/objects.js @@ -0,0 +1,223 @@ +duplicate_key: { + options = { + objects: true, + side_effects: true, + } + input: { + var o = { + a: 1, + b: 2, + a: 3, + }; + for (var k in o) + console.log(k, o[k]); + } + expect: { + var o = { + a: 3, + b: 2, + }; + for (var k in o) + console.log(k, o[k]); + } + expect_stdout: [ + "a 3", + "b 2", + ] +} + +duplicate_key_strict: { + options = { + objects: true, + side_effects: true, + } + input: { + "use strict"; + var o = { + a: 1, + b: 2, + a: 3, + }; + for (var k in o) + console.log(k, o[k]); + } + expect: { + "use strict"; + var o = { + a: 1, + b: 2, + a: 3, + }; + for (var k in o) + console.log(k, o[k]); + } + expect_stdout: true +} + +duplicate_key_side_effect: { + options = { + objects: true, + side_effects: true, + } + input: { + var o = { + a: 1, + b: o = 2, + a: 3, + }; + for (var k in o) + console.log(k, o[k]); + } + expect: { + var o = { + a: 1, + b: o = 2, + a: 3, + }; + for (var k in o) + console.log(k, o[k]); + } + expect_stdout: [ + "a 3", + "b 2", + ] +} + +duplicate_key_with_accessor: { + options = { + objects: true, + side_effects: true, + } + input: { + [ + { + a: 0, + b: 1, + a: 2, + set b(v) {}, + }, + { + a: 3, + b: 4, + get a() { + return 5; + }, + a: 6, + b: 7, + a: 8, + b: 9, + }, + ].forEach(function(o) { + for (var k in o) + console.log(k, o[k]); + }); + } + expect: { + [ + { + a: 2, + b: 1, + set b(v) {}, + }, + { + a: 3, + b: 4, + get a() { + return 5; + }, + a: 8, + b: 9, + }, + ].forEach(function(o) { + for (var k in o) + console.log(k, o[k]); + }); + } + expect_stdout: true +} + +unsafe_object_repeated: { + options = { + evaluate: true, + objects: true, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unsafe: true, + } + input: { + var o = { a: { b: 1 }, a: 1 }; + console.log( + o + 1, + o.a + 1, + o.b + 1, + o.a.b + 1 + ); + } + expect: { + var o = { a: 1 }; + console.log( + o + 1, + 2, + o.b + 1, + NaN + ); + } + expect_stdout: true +} + +numeric_literal: { + options = { + objects: true, + side_effects: true, + } + mangle = { + properties: true, + } + beautify = { + beautify: true, + } + input: { + var obj = { + 0: 0, + "-0": 1, + 42: 2, + "42": 3, + 0x25: 4, + "0x25": 5, + 1E42: 6, + "1E42": 7, + "1e+42": 8, + }; + console.log(obj[-0], obj[-""], obj["-0"]); + console.log(obj[42], obj["42"]); + console.log(obj[0x25], obj["0x25"], obj[37], obj["37"]); + console.log(obj[1E42], obj["1E42"], obj["1e+42"]); + } + expect_exact: [ + 'var obj = {', + ' 0: 0,', + ' "-0": 1,', + ' 42: 3,', + ' 37: 4,', + ' o: 5,', + ' 1e42: 8,', + ' b: 7', + '};', + '', + 'console.log(obj[-0], obj[-""], obj["-0"]);', + '', + 'console.log(obj[42], obj["42"]);', + '', + 'console.log(obj[37], obj["o"], obj[37], obj["37"]);', + '', + 'console.log(obj[1e42], obj["b"], obj["1e+42"]);', + ] + expect_stdout: [ + "0 0 1", + "3 3", + "4 5 4 4", + "8 7 8", + ] +} |