diff --git a/lib/ast.js b/lib/ast.js index e6867205..160b1cc2 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -745,6 +745,16 @@ var AST_Export = DEFNODE("Export", "exported_definition exported_value is_defaul exported_value: "[AST_Node?] An exported value", is_default: "[Boolean] Whether this is the default exported value of this module" }, + _walk: function (visitor) { + visitor._visit(this, function () { + if (this.exported_definition) { + this.exported_definition._walk(visitor); + } + if (this.exported_value) { + this.exported_value._walk(visitor); + } + }); + } }, AST_Statement); var AST_VarDef = DEFNODE("VarDef", "name value", { diff --git a/lib/scope.js b/lib/scope.js index 23f70ad1..f9241f2d 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -49,6 +49,7 @@ function SymbolDef(scope, index, orig) { this.scope = scope; this.references = []; this.global = false; + this.export = false; this.mangled_name = null; this.object_destructuring_arg = false; this.undeclared = false; @@ -61,6 +62,7 @@ SymbolDef.prototype = { if (!options) options = {}; return (this.global && !options.toplevel) + || this.export || this.object_destructuring_arg || this.undeclared || (!options.eval && (this.scope.uses_eval || this.scope.uses_with)) @@ -102,6 +104,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ var defun = null; var nesting = 0; var in_destructuring = null; + var in_export; var tw = new TreeWalker(function(node, descend){ if (options.screw_ie8 && node instanceof AST_Catch) { var save_scope = scope; @@ -131,6 +134,11 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ labels = save_labels; return true; // don't descend again in TreeWalker } + if (node instanceof AST_Export) { + in_export = true; + descend(); + in_export = false; + } if (node instanceof AST_LabeledStatement) { var l = node.label; if (labels.has(l.name)) { @@ -151,14 +159,14 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ } if (node instanceof AST_SymbolFunarg) { node.object_destructuring_arg = !!in_destructuring; - defun.def_variable(node); + defun.def_variable(node, in_export); } if (node instanceof AST_Label) { node.thedef = node; node.references = []; } if (node instanceof AST_SymbolLambda) { - defun.def_function(node); + defun.def_function(node, in_export); } else if (node instanceof AST_SymbolDefun) { // Careful here, the scope where this should be defined is @@ -166,22 +174,22 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ // scope when we encounter the AST_Defun node (which is // instanceof AST_Scope) but we get to the symbol a bit // later. - (node.scope = defun.parent_scope).def_function(node); + (node.scope = defun.parent_scope).def_function(node, in_export); } else if (node instanceof AST_SymbolClass) { - defun.def_variable(node); + defun.def_variable(node, in_export); } else if (node instanceof AST_SymbolImport) { - scope.def_variable(node); + scope.def_variable(node, in_export); } else if (node instanceof AST_SymbolDefClass) { // This deals with the name of the class being available // inside the class. - (node.scope = defun.parent_scope).def_function(node); + (node.scope = defun.parent_scope).def_function(node, in_export); } else if (node instanceof AST_SymbolVar || node instanceof AST_SymbolConst) { - var def = defun.def_variable(node); + var def = defun.def_variable(node, in_export); def.constant = node instanceof AST_SymbolConst; def.destructuring = in_destructuring; def.init = tw.parent().value; @@ -294,11 +302,11 @@ AST_Scope.DEFMETHOD("find_variable", function(name){ || (this.parent_scope && this.parent_scope.find_variable(name)); }); -AST_Scope.DEFMETHOD("def_function", function(symbol){ - this.functions.set(symbol.name, this.def_variable(symbol)); +AST_Scope.DEFMETHOD("def_function", function(symbol, in_export){ + this.functions.set(symbol.name, this.def_variable(symbol, in_export)); }); -AST_Scope.DEFMETHOD("def_variable", function(symbol){ +AST_Scope.DEFMETHOD("def_variable", function(symbol, in_export){ var def; if (!this.variables.has(symbol.name)) { def = new SymbolDef(this, this.variables.size(), symbol); @@ -311,6 +319,10 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol){ // TODO The real fix comes with block scoping being first class in uglifyJS, // enabling import definitions to behave like module-level let declarations } + if (!this.parent_scope && in_export) { + def.global = false; + def.export = true; + } } else { def = this.variables.get(symbol.name); def.orig.push(symbol); diff --git a/test/compress/harmony.js b/test/compress/harmony.js index d27d903b..fec3a835 100644 --- a/test/compress/harmony.js +++ b/test/compress/harmony.js @@ -350,6 +350,22 @@ import_statement_mangling: { } } +export_statement_mangling: { + mangle = { }; + input: { + export var foo = 6; + export function bar() { } + export class Baz { } + bar(foo, Baz) + } + expect: { + export var foo = 6; + export function bar() { } + export class Baz { } + bar(foo, Baz) + } +} + // Fabio: My patches accidentally caused a crash whenever // there's an extraneous set of parens around an object. regression_cannot_destructure: {