From 203ca2586a759ac2fce057e80ae9195d28ef247e Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Mon, 15 Feb 2021 01:01:18 +0000 Subject: [PATCH] introduce `hoist_exports` (#4651) --- README.md | 5 ++++- lib/compress.js | 35 +++++++++++++++++++++++++++++++++++ test/compress/exports.js | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 09da3aeb..ca2ddee1 100644 --- a/README.md +++ b/README.md @@ -667,11 +667,14 @@ to be `false` and all symbol names will be omitted. - `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) +- `hoist_exports` (default: `true`) -- hoist `export` statements to facilitate + various `compress` and `mangle` optimizations. + - `hoist_funs` (default: `false`) -- hoist function declarations - `hoist_props` (default: `true`) -- hoist properties from constant object and diff --git a/lib/compress.js b/lib/compress.js index 8bdc4b90..5adeb566 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -65,6 +65,7 @@ function Compressor(options, false_by_default) { expression : false, functions : !false_by_default, global_defs : false, + hoist_exports : !false_by_default, hoist_funs : false, hoist_props : !false_by_default, hoist_vars : false, @@ -183,6 +184,7 @@ merge(Compressor.prototype, { }, compress: function(node) { node = node.resolve_defines(this); + node.hoist_exports(this); if (this.option("expression")) { node.process_expression(true); } @@ -260,6 +262,35 @@ merge(Compressor.prototype, { return this.TYPE == node.TYPE && this.print_to_string() == node.print_to_string(); }); + AST_Toplevel.DEFMETHOD("hoist_exports", function(compressor) { + if (!compressor.option("hoist_exports")) return; + var body = this.body, props = []; + for (var i = 0; i < body.length; i++) { + var stat = body[i]; + if (stat instanceof AST_ExportDeclaration) { + body[i] = stat = stat.body; + if (stat instanceof AST_Definitions) { + stat.definitions.forEach(function(defn) { + defn.name.match_symbol(export_symbol, true); + }); + } else { + export_symbol(stat.name); + } + } else if (stat instanceof AST_ExportReferences) { + body.splice(i, 1); + [].push.apply(props, stat.properties); + } + } + if (props.length) body.push(make_node(AST_ExportReferences, this, { properties: props })); + + function export_symbol(sym) { + if (!(sym instanceof AST_SymbolDeclaration)) return; + var node = make_node(AST_SymbolExport, sym, sym); + node.alias = node.name; + props.push(node); + } + }); + AST_Scope.DEFMETHOD("process_expression", function(insert, transform) { var self = this; var tt = new TreeTransformer(function(node) { @@ -9871,6 +9902,10 @@ merge(Compressor.prototype, { } }); + OPT(AST_SymbolExport, function(self) { + return self; + }); + function recursive_ref(compressor, def) { var level = 0, node = compressor.self(); do { diff --git a/test/compress/exports.js b/test/compress/exports.js index afa546ec..75ce5a43 100644 --- a/test/compress/exports.js +++ b/test/compress/exports.js @@ -142,3 +142,38 @@ mangle_rename: { } } } + +hoist_exports: { + 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 f, { foo: o } = 42; + function c(t, { [f]: a }) { + t(a, c); + } + export default 42; + export default async function t(a, ...{ [o]: f }) { + (await a)(t, f); + }; + export { f as bbb, o as ccc, c as fff }; + } +} -- 2.34.1