implement mangle.properties.domprops (#5686)

- support destructuring syntax
- fix corner case with comma operator
This commit is contained in:
Alex Lam S.L
2022-09-29 05:18:04 +01:00
committed by GitHub
parent a570c00251
commit bd5fc4cb1b
15 changed files with 94 additions and 38 deletions

View File

@@ -891,12 +891,15 @@ UglifyJS.minify(code, { mangle: { toplevel: true } }).code;
### Mangle properties options ### Mangle properties options
- `builtins` (default: `false`) — Use `true` to allow the mangling of builtin - `builtins` (default: `false`) — Use `true` to allow the mangling of built-in
DOM properties. Not recommended to override this setting. properties of JavaScript API. 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. Pass an empty string `""` to enable, or a non-empty string to set the debug suffix.
- `domprops` (default: `false`) — Use `true` to allow the mangling of properties
commonly found in Document Object Model. Not recommended to override this setting.
- `keep_fargs` (default: `false`) — Use `true` to prevent mangling of function - `keep_fargs` (default: `false`) — Use `true` to prevent mangling of function
arguments. arguments.

View File

@@ -238,17 +238,6 @@ if (specified["beautify"] && specified["output-opts"]) fatal("--beautify cannot
[ "compress", "mangle" ].forEach(function(name) { [ "compress", "mangle" ].forEach(function(name) {
if (!(name in options)) options[name] = false; if (!(name in options)) options[name] = false;
}); });
if (options.mangle && options.mangle.properties) {
if (options.mangle.properties.domprops) {
delete options.mangle.properties.domprops;
} else {
if (typeof options.mangle.properties != "object") options.mangle.properties = {};
if (!Array.isArray(options.mangle.properties.reserved)) options.mangle.properties.reserved = [];
require("../tools/domprops").forEach(function(name) {
UglifyJS.push_uniq(options.mangle.properties.reserved, name);
});
}
}
if (/^ast|spidermonkey$/.test(output)) { if (/^ast|spidermonkey$/.test(output)) {
if (typeof options.output != "object") options.output = {}; if (typeof options.output != "object") options.output = {};
options.output.ast = true; options.output.ast = true;

View File

@@ -124,7 +124,9 @@ function get_builtins() {
function reserve_quoted_keys(ast, reserved) { function reserve_quoted_keys(ast, reserved) {
ast.walk(new TreeWalker(function(node) { ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_ClassProperty || node instanceof AST_ObjectProperty) { if (node instanceof AST_ClassProperty
|| node instanceof AST_DestructuredKeyVal
|| node instanceof AST_ObjectProperty) {
if (node.key instanceof AST_Node) { if (node.key instanceof AST_Node) {
addStrings(node.key, add); addStrings(node.key, add);
} else if (node.start && node.start.quote) { } else if (node.start && node.start.quote) {
@@ -158,12 +160,16 @@ function mangle_properties(ast, options) {
builtins: false, builtins: false,
cache: null, cache: null,
debug: false, debug: false,
domprops: false,
keep_quoted: false, keep_quoted: false,
regex: null, regex: null,
reserved: null, reserved: null,
}, true); }, true);
var reserved = options.builtins ? new Dictionary() : get_builtins(); var reserved = options.builtins ? new Dictionary() : get_builtins();
if (!options.domprops && typeof domprops !== "undefined") domprops.forEach(function(name) {
reserved.set(name, true);
});
if (Array.isArray(options.reserved)) options.reserved.forEach(function(name) { if (Array.isArray(options.reserved)) options.reserved.forEach(function(name) {
reserved.set(name, true); reserved.set(name, true);
}); });
@@ -210,7 +216,9 @@ function mangle_properties(ast, options) {
addStrings(node.args[0], add); addStrings(node.args[0], add);
break; break;
} }
} else if (node instanceof AST_ClassProperty || node instanceof AST_ObjectProperty) { } else if (node instanceof AST_ClassProperty
|| node instanceof AST_DestructuredKeyVal
|| node instanceof AST_ObjectProperty) {
if (node.key instanceof AST_Node) { if (node.key instanceof AST_Node) {
addStrings(node.key, add); addStrings(node.key, add);
} else { } else {
@@ -244,7 +252,9 @@ function mangle_properties(ast, options) {
mangleStrings(node.args[0]); mangleStrings(node.args[0]);
break; break;
} }
} else if (node instanceof AST_ClassProperty || node instanceof AST_ObjectProperty) { } else if (node instanceof AST_ClassProperty
|| node instanceof AST_DestructuredKeyVal
|| node instanceof AST_ObjectProperty) {
if (node.key instanceof AST_Node) { if (node.key instanceof AST_Node) {
mangleStrings(node.key); mangleStrings(node.key);
} else { } else {
@@ -307,7 +317,7 @@ function mangle_properties(ast, options) {
function mangleStrings(node) { function mangleStrings(node) {
if (node instanceof AST_Sequence) { if (node instanceof AST_Sequence) {
mangleStrings(node.expressions.tail_node()); mangleStrings(node.tail_node());
} else if (node instanceof AST_String) { } else if (node instanceof AST_String) {
node.value = mangle(node.value); node.value = mangle(node.value);
} else if (node instanceof AST_Conditional) { } else if (node instanceof AST_Conditional) {

View File

@@ -271,7 +271,9 @@ function test_case(test) {
expect = test.expect_exact; expect = test.expect_exact;
} }
var input = to_toplevel(test.input, test.mangle, test.expression); var input = to_toplevel(test.input, test.mangle, test.expression);
var input_code = make_code(input, {}, test.expression); var input_code = make_code(input, {
keep_quoted_props: true,
}, test.expression);
var input_formatted = make_code(test.input, { var input_formatted = make_code(test.input, {
annotations: true, annotations: true,
beautify: true, beautify: true,

View File

@@ -2164,6 +2164,7 @@ issue_4829_2: {
mangle_properties: { mangle_properties: {
mangle = { mangle = {
properties: { properties: {
domprops: true,
keep_quoted: true, keep_quoted: true,
}, },
} }

View File

@@ -1737,6 +1737,23 @@ singleton_side_effects: {
node_version: ">=6" node_version: ">=6"
} }
mangle_properties: {
mangle = {
properties: {
domprops: true,
},
}
input: {
function f({ p: a }) {
return a;
}
console.log(f({ p: "PASS" }));
}
expect_exact: 'function f({n}){return n}console.log(f({n:"PASS"}));'
expect_stdout: "PASS"
node_version: ">=6"
}
issue_4280: { issue_4280: {
options = { options = {
evaluate: true, evaluate: true,

View File

@@ -1,6 +1,7 @@
issue_1321_no_debug: { issue_1321_no_debug: {
mangle = { mangle = {
properties: { properties: {
domprops: true,
keep_quoted: true, keep_quoted: true,
}, },
} }
@@ -23,6 +24,7 @@ issue_1321_debug: {
mangle = { mangle = {
properties: { properties: {
debug: "", debug: "",
domprops: true,
keep_quoted: true, keep_quoted: true,
}, },
} }
@@ -44,6 +46,7 @@ issue_1321_debug: {
issue_1321_with_quoted: { issue_1321_with_quoted: {
mangle = { mangle = {
properties: { properties: {
domprops: true,
keep_quoted: false, keep_quoted: false,
}, },
} }

View File

@@ -65,7 +65,9 @@ mangle_props: {
numeric_literal: { numeric_literal: {
mangle = { mangle = {
properties: true, properties: {
domprops: true,
},
} }
beautify = { beautify = {
beautify: true, beautify: true,
@@ -125,6 +127,7 @@ identifier: {
mangle = { mangle = {
properties: { properties: {
builtins: true, builtins: true,
domprops: true,
}, },
} }
input: { input: {

View File

@@ -1,6 +1,7 @@
dont_reuse_prop: { dont_reuse_prop: {
mangle = { mangle = {
properties: { properties: {
domprops: true,
regex: /asd/, regex: /asd/,
}, },
} }
@@ -29,6 +30,7 @@ dont_reuse_prop: {
unmangleable_props_should_always_be_reserved: { unmangleable_props_should_always_be_reserved: {
mangle = { mangle = {
properties: { properties: {
domprops: true,
regex: /asd/, regex: /asd/,
}, },
} }

View File

@@ -173,7 +173,9 @@ numeric_literal: {
side_effects: true, side_effects: true,
} }
mangle = { mangle = {
properties: true, properties: {
domprops: true,
},
} }
beautify = { beautify = {
beautify: true, beautify: true,

View File

@@ -133,6 +133,7 @@ evaluate_string_length: {
mangle_properties_1: { mangle_properties_1: {
mangle = { mangle = {
properties: { properties: {
domprops: true,
keep_quoted: false, keep_quoted: false,
}, },
} }
@@ -155,9 +156,10 @@ mangle_properties_1: {
mangle_properties_2: { mangle_properties_2: {
mangle = { mangle = {
properties: { properties: {
domprops: true,
reserved: [ reserved: [
"value", "value",
] ],
}, },
} }
input: { input: {
@@ -199,6 +201,24 @@ mangle_properties_2: {
] ]
} }
mangle_properties_3: {
mangle = {
properties: true,
}
input: {
console.log({
[(console, "foo")]: "PASS",
}.foo);
}
expect: {
console.log({
[(console, "o")]: "PASS",
}.o);
}
expect_stdout: "PASS"
node_version: ">=4"
}
mangle_unquoted_properties: { mangle_unquoted_properties: {
options = { options = {
evaluate: true, evaluate: true,
@@ -207,6 +227,7 @@ mangle_unquoted_properties: {
mangle = { mangle = {
properties: { properties: {
builtins: true, builtins: true,
domprops: true,
keep_quoted: true, keep_quoted: true,
}, },
} }
@@ -308,6 +329,7 @@ mangle_debug_suffix_keep_quoted: {
properties: { properties: {
builtins: true, builtins: true,
debug: "XYZ", debug: "XYZ",
domprops: true,
keep_quoted: true, keep_quoted: true,
reserved: [], reserved: [],
}, },

View File

@@ -42,7 +42,9 @@ describe("let", function() {
compress: false, compress: false,
ie: true, ie: true,
mangle: { mangle: {
properties: true, properties: {
domprops: true,
},
}, },
}); });
if (result.error) throw result.error; if (result.error) throw result.error;

View File

@@ -110,10 +110,12 @@ describe("minify", function() {
var result = UglifyJS.minify(code, { var result = UglifyJS.minify(code, {
compress: false, compress: false,
mangle: { mangle: {
properties: true, properties: {
toplevel: true domprops: true,
},
toplevel: true,
}, },
nameCache: cache nameCache: cache,
}); });
if (result.error) throw result.error; if (result.error) throw result.error;
original += code; original += code;
@@ -188,21 +190,19 @@ describe("minify", function() {
it("Shouldn't mangle quoted properties", function() { it("Shouldn't mangle quoted properties", function() {
var js = 'a["foo"] = "bar"; a.color = "red"; x = {"bar": 10};'; var js = 'a["foo"] = "bar"; a.color = "red"; x = {"bar": 10};';
var result = UglifyJS.minify(js, { var result = UglifyJS.minify(js, {
compress: { compress: true,
properties: false
},
mangle: { mangle: {
properties: { properties: {
keep_quoted: true domprops: true,
} keep_quoted: true,
},
}, },
output: { output: {
keep_quoted_props: true, keep_quoted_props: true,
quote_style: 3 quote_style: 3,
} },
}); });
assert.strictEqual(result.code, assert.strictEqual(result.code, 'a["foo"]="bar",a.a="red",x={"bar":10};');
'a["foo"]="bar",a.a="red",x={"bar":10};');
}); });
it("Should not mangle quoted property within dead code", function() { it("Should not mangle quoted property within dead code", function() {
var result = UglifyJS.minify('({ "keep": 1 }); g.keep = g.change = 42;', { var result = UglifyJS.minify('({ "keep": 1 }); g.keep = g.change = 42;', {

View File

@@ -1,6 +1,6 @@
var fs = require("fs"); var fs = require("fs");
new Function("exports", require("../tools/node").FILES.map(function(file) { new Function("domprops", "exports", require("../tools/node").FILES.map(function(file) {
if (/exports\.js$/.test(file)) file = require.resolve("./exports"); if (/exports\.js$/.test(file)) file = require.resolve("./exports");
return fs.readFileSync(file, "utf8"); return fs.readFileSync(file, "utf8");
}).join("\n\n"))(exports); }).join("\n\n"))(require("../tools/domprops.json"), exports);

View File

@@ -15,13 +15,13 @@ exports.FILES = [
require.resolve("./exports.js"), require.resolve("./exports.js"),
]; ];
new Function("exports", function() { new Function("domprops", "exports", function() {
var code = exports.FILES.map(function(file) { var code = exports.FILES.map(function(file) {
return fs.readFileSync(file, "utf8"); return fs.readFileSync(file, "utf8");
}); });
code.push("exports.describe_ast = " + describe_ast.toString()); code.push("exports.describe_ast = " + describe_ast.toString());
return code.join("\n\n"); return code.join("\n\n");
}())(exports); }())(require("./domprops.json"), exports);
function to_comment(value) { function to_comment(value) {
if (typeof value != "string") value = JSON.stringify(value, function(key, value) { if (typeof value != "string") value = JSON.stringify(value, function(key, value) {