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){
if (node !== self) {
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();
if (node_def.global && !(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
@ -2157,9 +2157,11 @@ merge(Compressor.prototype, {
// pass 3: we should drop declarations not in_use
var tt = new TreeTransformer(
function before(node, descend, in_list) {
if (node instanceof AST_Function
&& node.name
&& !compressor.option("keep_fnames")) {
var parent = tt.parent();
if (!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();
// any declarations with same name will overshadow
// 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;
if (!keep) {
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;
}
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
var body = [], head = [], tail = [];
// for unused names whose initialization has
@ -2298,7 +2300,7 @@ merge(Compressor.prototype, {
if (!(def.id in in_use_ids)
&& (drop_vars || !def.global)
&& 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:

View File

@ -2417,14 +2417,26 @@ function parse($TEXT, options) {
}
}
var is_definition = is("keyword", "var")
|| is("keyword", "let")
|| is("keyword", "const")
|| is("keyword", "function") && !is_default;
var is_definition = is("keyword", "var") || is("keyword", "let") || is("keyword", "const");
if (is_definition) {
if (is_default) unexpected();
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")) {
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 {
exported_value = expression(false);
semicolon();

View File

@ -230,7 +230,8 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
}
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);

View File

@ -1,6 +1,7 @@
export_func_1: {
options = {
hoist_funs: true,
toplevel: true,
unused: true,
}
input: {
@ -13,6 +14,7 @@ export_func_2: {
options = {
hoist_funs: true,
side_effects: false,
toplevel: true,
unused: true,
}
input: {
@ -25,6 +27,7 @@ export_func_3: {
options = {
hoist_funs: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
@ -36,6 +39,7 @@ export_func_3: {
export_default_func_1: {
options = {
hoist_funs: true,
toplevel: true,
unused: true,
}
input: {
@ -48,6 +52,7 @@ export_default_func_2: {
options = {
hoist_funs: true,
side_effects: false,
toplevel: true,
unused: true,
}
input: {
@ -60,6 +65,7 @@ export_default_func_3: {
options = {
hoist_funs: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
@ -68,6 +74,82 @@ export_default_func_3: {
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: {
mangle = {
toplevel: true,
@ -89,7 +171,7 @@ export_mangle_2: {
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: {
@ -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: {
@ -181,6 +263,19 @@ export_toplevel_2: {
}
expect: {
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();"
}