fix export default

- prohibit definition statements
- parse `AST_Defun` properly
- drop only unused class and function names
This commit is contained in:
alexlamsl 2017-05-26 05:59:33 +08:00
parent e5d93f1555
commit e793f9b159
4 changed files with 126 additions and 16 deletions

View File

@ -2021,7 +2021,7 @@ merge(Compressor.prototype, {
var tw = new TreeWalker(function(node, descend){ var tw = new TreeWalker(function(node, descend){
if (node !== self) { if (node !== self) {
if (node instanceof AST_Defun || node instanceof AST_DefClass) { if (node instanceof AST_Defun || node instanceof AST_DefClass) {
if ((!drop_funcs || tw.parent() instanceof AST_Export) && scope === self) { if (!drop_funcs && scope === self) {
var node_def = node.name.definition(); var node_def = node.name.definition();
if (node_def.global && !(node_def.id in in_use_ids)) { if (node_def.global && !(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true; in_use_ids[node_def.id] = true;
@ -2157,9 +2157,11 @@ merge(Compressor.prototype, {
// pass 3: we should drop declarations not in_use // pass 3: we should drop declarations not in_use
var tt = new TreeTransformer( var tt = new TreeTransformer(
function before(node, descend, in_list) { function before(node, descend, in_list) {
if (node instanceof AST_Function var parent = tt.parent();
&& node.name if (!compressor.option("keep_fnames")
&& !compressor.option("keep_fnames")) { && ((node instanceof AST_Function || node instanceof AST_ClassExpression) && node.name
|| (node instanceof AST_Defun || node instanceof AST_DefClass)
&& parent instanceof AST_Export && parent.is_default)) {
var def = node.name.definition(); var def = node.name.definition();
// any declarations with same name will overshadow // any declarations with same name will overshadow
// name of this anonymous function and can therefore // name of this anonymous function and can therefore
@ -2194,7 +2196,7 @@ merge(Compressor.prototype, {
} }
} }
} }
if ((node instanceof AST_Defun || node instanceof AST_DefClass) && node !== self) { if ((node instanceof AST_Defun || node instanceof AST_DefClass) && !(parent instanceof AST_Export) && node !== self) {
var keep = (node.name.definition().id in in_use_ids) || !drop_funcs && node.name.definition().global; var keep = (node.name.definition().id in in_use_ids) || !drop_funcs && node.name.definition().global;
if (!keep) { if (!keep) {
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name)); compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
@ -2202,7 +2204,7 @@ merge(Compressor.prototype, {
} }
return node; return node;
} }
if (node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn && tt.parent().init === node)) { if (node instanceof AST_Definitions && !(parent instanceof AST_ForIn && parent.init === node)) {
// place uninitialized names at the start // place uninitialized names at the start
var body = [], head = [], tail = []; var body = [], head = [], tail = [];
// for unused names whose initialization has // for unused names whose initialization has
@ -2298,7 +2300,7 @@ merge(Compressor.prototype, {
if (!(def.id in in_use_ids) if (!(def.id in in_use_ids)
&& (drop_vars || !def.global) && (drop_vars || !def.global)
&& self.variables.get(def.name) === def) { && self.variables.get(def.name) === def) {
return maintain_this_binding(tt.parent(), node, node.right.transform(tt)); return maintain_this_binding(parent, node, node.right.transform(tt));
} }
} }
// certain combination of unused name + side effect leads to: // certain combination of unused name + side effect leads to:

View File

@ -2417,14 +2417,26 @@ function parse($TEXT, options) {
} }
} }
var is_definition = is("keyword", "var") var is_definition = is("keyword", "var") || is("keyword", "let") || is("keyword", "const");
|| is("keyword", "let")
|| is("keyword", "const")
|| is("keyword", "function") && !is_default;
if (is_definition) { if (is_definition) {
if (is_default) unexpected();
exported_definition = statement(); exported_definition = statement();
} else if (is("keyword", "class")) {
var cls = expr_atom(false);
if (cls.name) {
cls.name = new AST_SymbolDefClass(cls.name);
exported_definition = new AST_DefClass(cls);
} else {
exported_value = cls;
}
} else if (is("keyword", "function")) { } else if (is("keyword", "function")) {
exported_value = expr_atom(false); var func = expr_atom(false);
if (func.name) {
func.name = new AST_SymbolDefun(func.name);
exported_definition = new AST_Defun(func);
} else {
exported_value = func;
}
} else { } else {
exported_value = expression(false); exported_value = expression(false);
semicolon(); semicolon();

View File

@ -230,7 +230,8 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
} }
function mark_export(def, level) { function mark_export(def, level) {
def.export = tw.parent(level) instanceof AST_Export; var node = tw.parent(level);
def.export = node instanceof AST_Export && !node.is_default;
} }
}); });
self.walk(tw); self.walk(tw);

View File

@ -1,6 +1,7 @@
export_func_1: { export_func_1: {
options = { options = {
hoist_funs: true, hoist_funs: true,
toplevel: true,
unused: true, unused: true,
} }
input: { input: {
@ -13,6 +14,7 @@ export_func_2: {
options = { options = {
hoist_funs: true, hoist_funs: true,
side_effects: false, side_effects: false,
toplevel: true,
unused: true, unused: true,
} }
input: { input: {
@ -25,6 +27,7 @@ export_func_3: {
options = { options = {
hoist_funs: true, hoist_funs: true,
side_effects: true, side_effects: true,
toplevel: true,
unused: true, unused: true,
} }
input: { input: {
@ -36,6 +39,7 @@ export_func_3: {
export_default_func_1: { export_default_func_1: {
options = { options = {
hoist_funs: true, hoist_funs: true,
toplevel: true,
unused: true, unused: true,
} }
input: { input: {
@ -48,6 +52,7 @@ export_default_func_2: {
options = { options = {
hoist_funs: true, hoist_funs: true,
side_effects: false, side_effects: false,
toplevel: true,
unused: true, unused: true,
} }
input: { input: {
@ -60,6 +65,7 @@ export_default_func_3: {
options = { options = {
hoist_funs: true, hoist_funs: true,
side_effects: true, side_effects: true,
toplevel: true,
unused: true, unused: true,
} }
input: { input: {
@ -68,6 +74,82 @@ export_default_func_3: {
expect_exact: "export default function(){};" expect_exact: "export default function(){};"
} }
export_class_1: {
options = {
hoist_funs: true,
toplevel: true,
unused: true,
}
input: {
export class C {};
}
expect_exact: "export class C{};"
}
export_class_2: {
options = {
hoist_funs: true,
side_effects: false,
toplevel: true,
unused: true,
}
input: {
export class C {}(1);
}
expect_exact: "export class C{};1;"
}
export_class_3: {
options = {
hoist_funs: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
export class C {}(1);
}
expect_exact: "export class C{};"
}
export_default_class_1: {
options = {
hoist_funs: true,
toplevel: true,
unused: true,
}
input: {
export default class C {};
}
expect_exact: "export default class{};"
}
export_default_class_2: {
options = {
hoist_funs: true,
side_effects: false,
toplevel: true,
unused: true,
}
input: {
export default class C {}(1);
}
expect_exact: "export default class{};1;"
}
export_default_class_3: {
options = {
hoist_funs: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
export default class C {}(1);
}
expect_exact: "export default class{};"
}
export_mangle_1: { export_mangle_1: {
mangle = { mangle = {
toplevel: true, toplevel: true,
@ -89,7 +171,7 @@ export_mangle_2: {
return one - two; return one - two;
}; };
} }
expect_exact: "export default function n(r,t){return r-t};" expect_exact: "export default function n(n,r){return n-r};"
} }
export_mangle_3: { export_mangle_3: {
@ -125,7 +207,7 @@ export_mangle_4: {
} }
}; };
} }
expect_exact: "export default class C{go(n,r){return n-r+n}};" expect_exact: "export default class n{go(n,r){return n-r+n}};"
} }
export_mangle_5: { export_mangle_5: {
@ -181,6 +263,19 @@ export_toplevel_2: {
} }
expect: { expect: {
export class B {}; export class B {};
export default class C {}; export default class {};
} }
} }
export_default_func_ref: {
options = {
hoist_funs: true,
toplevel: true,
unused: true,
}
input: {
export default function f(){};
f();
}
expect_exact: "export default function f(){};f();"
}