support export statements (#4650)
This commit is contained in:
95
lib/ast.js
95
lib/ast.js
@@ -207,6 +207,7 @@ var AST_Directive = DEFNODE("Directive", "quote value", {
|
|||||||
_validate: function() {
|
_validate: function() {
|
||||||
if (this.quote != null) {
|
if (this.quote != null) {
|
||||||
if (typeof this.quote != "string") throw new Error("quote must be string");
|
if (typeof this.quote != "string") throw new Error("quote must be string");
|
||||||
|
if (!/^["']$/.test(this.quote)) throw new Error("invalid quote: " + this.quote);
|
||||||
}
|
}
|
||||||
if (typeof this.value != "string") throw new Error("value must be string");
|
if (typeof this.value != "string") throw new Error("value must be string");
|
||||||
},
|
},
|
||||||
@@ -238,7 +239,7 @@ function must_be_expression(node, prop) {
|
|||||||
var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
|
var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
|
||||||
$documentation: "A statement consisting of an expression, i.e. a = 1 + 2",
|
$documentation: "A statement consisting of an expression, i.e. a = 1 + 2",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
body: "[AST_Node] an expression node (should not be instanceof AST_Statement)"
|
body: "[AST_Node] an expression node (should not be instanceof AST_Statement)",
|
||||||
},
|
},
|
||||||
walk: function(visitor) {
|
walk: function(visitor) {
|
||||||
var node = this;
|
var node = this;
|
||||||
@@ -1038,6 +1039,86 @@ var AST_VarDef = DEFNODE("VarDef", "name value", {
|
|||||||
|
|
||||||
/* -----[ OTHER ]----- */
|
/* -----[ OTHER ]----- */
|
||||||
|
|
||||||
|
var AST_ExportDeclaration = DEFNODE("ExportDeclaration", "body", {
|
||||||
|
$documentation: "An `export` statement",
|
||||||
|
$propdoc: {
|
||||||
|
body: "[AST_Definitions|AST_LambdaDefinition] the statement to export",
|
||||||
|
},
|
||||||
|
walk: function(visitor) {
|
||||||
|
var node = this;
|
||||||
|
visitor.visit(node, function() {
|
||||||
|
node.body.walk(visitor);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
_validate: function() {
|
||||||
|
if (!(this.body instanceof AST_Definitions || this.body instanceof AST_LambdaDefinition)) {
|
||||||
|
throw new Error("body must be AST_Definitions or AST_LambdaDefinition");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}, AST_Statement);
|
||||||
|
|
||||||
|
var AST_ExportDefault = DEFNODE("ExportDefault", "body", {
|
||||||
|
$documentation: "An `export default` statement",
|
||||||
|
$propdoc: {
|
||||||
|
body: "[AST_Node] an expression node (should not be instanceof AST_Statement)",
|
||||||
|
},
|
||||||
|
walk: function(visitor) {
|
||||||
|
var node = this;
|
||||||
|
visitor.visit(node, function() {
|
||||||
|
node.body.walk(visitor);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
_validate: function() {
|
||||||
|
must_be_expression(this, "body");
|
||||||
|
},
|
||||||
|
}, AST_Statement);
|
||||||
|
|
||||||
|
var AST_ExportForeign = DEFNODE("ExportForeign", "aliases keys path quote", {
|
||||||
|
$documentation: "An `export ... from '...'` statement",
|
||||||
|
$propdoc: {
|
||||||
|
aliases: "[string*] array of aliases to export",
|
||||||
|
keys: "[string*] array of keys to import",
|
||||||
|
path: "[string] the path to import module",
|
||||||
|
quote: "[string?] the original quote character",
|
||||||
|
},
|
||||||
|
_validate: function() {
|
||||||
|
if (this.aliases.length != this.keys.length) {
|
||||||
|
throw new Error("aliases:key length mismatch: " + this.aliases.length + " != " + this.keys.length);
|
||||||
|
}
|
||||||
|
this.aliases.forEach(function(name) {
|
||||||
|
if (typeof name != "string") throw new Error("aliases must contain string");
|
||||||
|
});
|
||||||
|
this.keys.forEach(function(name) {
|
||||||
|
if (typeof name != "string") throw new Error("keys must contain string");
|
||||||
|
});
|
||||||
|
if (typeof this.path != "string") throw new Error("path must be string");
|
||||||
|
if (this.quote != null) {
|
||||||
|
if (typeof this.quote != "string") throw new Error("quote must be string");
|
||||||
|
if (!/^["']$/.test(this.quote)) throw new Error("invalid quote: " + this.quote);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}, AST_Statement);
|
||||||
|
|
||||||
|
var AST_ExportReferences = DEFNODE("ExportReferences", "properties", {
|
||||||
|
$documentation: "An `export { ... }` statement",
|
||||||
|
$propdoc: {
|
||||||
|
properties: "[AST_SymbolExport*] array of aliases to export",
|
||||||
|
},
|
||||||
|
walk: function(visitor) {
|
||||||
|
var node = this;
|
||||||
|
visitor.visit(node, function() {
|
||||||
|
node.properties.forEach(function(prop) {
|
||||||
|
prop.walk(visitor);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
_validate: function() {
|
||||||
|
this.properties.forEach(function(prop) {
|
||||||
|
if (!(prop instanceof AST_SymbolExport)) throw new Error("properties must contain AST_SymbolExport");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}, AST_Statement);
|
||||||
|
|
||||||
var AST_Import = DEFNODE("Import", "all default path properties quote", {
|
var AST_Import = DEFNODE("Import", "all default path properties quote", {
|
||||||
$documentation: "An `import` statement",
|
$documentation: "An `import` statement",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
@@ -1072,6 +1153,7 @@ var AST_Import = DEFNODE("Import", "all default path properties quote", {
|
|||||||
});
|
});
|
||||||
if (this.quote != null) {
|
if (this.quote != null) {
|
||||||
if (typeof this.quote != "string") throw new Error("quote must be string");
|
if (typeof this.quote != "string") throw new Error("quote must be string");
|
||||||
|
if (!/^["']$/.test(this.quote)) throw new Error("invalid quote: " + this.quote);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}, AST_Statement);
|
}, AST_Statement);
|
||||||
@@ -1572,6 +1654,16 @@ var AST_SymbolRef = DEFNODE("SymbolRef", "fixed in_arg redef", {
|
|||||||
$documentation: "Reference to some symbol (not definition/declaration)",
|
$documentation: "Reference to some symbol (not definition/declaration)",
|
||||||
}, AST_Symbol);
|
}, AST_Symbol);
|
||||||
|
|
||||||
|
var AST_SymbolExport = DEFNODE("SymbolExport", "alias", {
|
||||||
|
$documentation: "Reference in an `export` statement",
|
||||||
|
$propdoc: {
|
||||||
|
alias: "[string] the `export` alias",
|
||||||
|
},
|
||||||
|
_validate: function() {
|
||||||
|
if (typeof this.alias != "string") throw new Error("alias must be string");
|
||||||
|
},
|
||||||
|
}, AST_SymbolRef);
|
||||||
|
|
||||||
var AST_LabelRef = DEFNODE("LabelRef", null, {
|
var AST_LabelRef = DEFNODE("LabelRef", null, {
|
||||||
$documentation: "Reference to a label symbol",
|
$documentation: "Reference to a label symbol",
|
||||||
}, AST_Symbol);
|
}, AST_Symbol);
|
||||||
@@ -1627,6 +1719,7 @@ var AST_String = DEFNODE("String", "quote value", {
|
|||||||
_validate: function() {
|
_validate: function() {
|
||||||
if (this.quote != null) {
|
if (this.quote != null) {
|
||||||
if (typeof this.quote != "string") throw new Error("quote must be string");
|
if (typeof this.quote != "string") throw new Error("quote must be string");
|
||||||
|
if (!/^["']$/.test(this.quote)) throw new Error("invalid quote: " + this.quote);
|
||||||
}
|
}
|
||||||
if (typeof this.value != "string") throw new Error("value must be string");
|
if (typeof this.value != "string") throw new Error("value must be string");
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ Compressor.prototype = new TreeTransformer;
|
|||||||
merge(Compressor.prototype, {
|
merge(Compressor.prototype, {
|
||||||
option: function(key) { return this.options[key] },
|
option: function(key) { return this.options[key] },
|
||||||
exposed: function(def) {
|
exposed: function(def) {
|
||||||
|
if (def.exported) return true;
|
||||||
if (def.undeclared) return true;
|
if (def.undeclared) return true;
|
||||||
if (!(def.global || def.scope.resolve() instanceof AST_Toplevel)) return false;
|
if (!(def.global || def.scope.resolve() instanceof AST_Toplevel)) return false;
|
||||||
var toplevel = this.toplevel;
|
var toplevel = this.toplevel;
|
||||||
@@ -5583,7 +5584,7 @@ merge(Compressor.prototype, {
|
|||||||
if (scope === self) {
|
if (scope === self) {
|
||||||
if (node instanceof AST_LambdaDefinition) {
|
if (node instanceof AST_LambdaDefinition) {
|
||||||
var def = node.name.definition();
|
var def = node.name.definition();
|
||||||
if (!drop_funcs && !(def.id in in_use_ids)) {
|
if ((!drop_funcs || def.exported) && !(def.id in in_use_ids)) {
|
||||||
in_use_ids[def.id] = true;
|
in_use_ids[def.id] = true;
|
||||||
in_use.push(def);
|
in_use.push(def);
|
||||||
}
|
}
|
||||||
@@ -5602,7 +5603,7 @@ merge(Compressor.prototype, {
|
|||||||
var redef = def.redefined();
|
var redef = def.redefined();
|
||||||
if (redef) var_defs[redef.id] = (var_defs[redef.id] || 0) + 1;
|
if (redef) var_defs[redef.id] = (var_defs[redef.id] || 0) + 1;
|
||||||
}
|
}
|
||||||
if (!(def.id in in_use_ids) && (!drop_vars
|
if (!(def.id in in_use_ids) && (!drop_vars || def.exported
|
||||||
|| (node instanceof AST_Const ? def.redefined() : def.const_redefs)
|
|| (node instanceof AST_Const ? def.redefined() : def.const_redefs)
|
||||||
|| !(node instanceof AST_Var || is_safe_lexical(def)))) {
|
|| !(node instanceof AST_Var || is_safe_lexical(def)))) {
|
||||||
in_use_ids[def.id] = true;
|
in_use_ids[def.id] = true;
|
||||||
|
|||||||
@@ -892,10 +892,6 @@ function OutputStream(options) {
|
|||||||
use_asm = was_asm;
|
use_asm = was_asm;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFPRINT(AST_Statement, function(output) {
|
|
||||||
this.body.print(output);
|
|
||||||
output.semicolon();
|
|
||||||
});
|
|
||||||
DEFPRINT(AST_Toplevel, function(output) {
|
DEFPRINT(AST_Toplevel, function(output) {
|
||||||
display_body(this.body, true, output, true);
|
display_body(this.body, true, output, true);
|
||||||
output.print("");
|
output.print("");
|
||||||
@@ -1011,6 +1007,64 @@ function OutputStream(options) {
|
|||||||
output.space();
|
output.space();
|
||||||
force_statement(self.body, output);
|
force_statement(self.body, output);
|
||||||
});
|
});
|
||||||
|
DEFPRINT(AST_ExportDeclaration, function(output) {
|
||||||
|
output.print("export");
|
||||||
|
output.space();
|
||||||
|
this.body.print(output);
|
||||||
|
});
|
||||||
|
DEFPRINT(AST_ExportDefault, function(output) {
|
||||||
|
output.print("export");
|
||||||
|
output.space();
|
||||||
|
output.print("default");
|
||||||
|
output.space();
|
||||||
|
this.body.print(output);
|
||||||
|
output.semicolon();
|
||||||
|
});
|
||||||
|
DEFPRINT(AST_ExportForeign, function(output) {
|
||||||
|
var self = this;
|
||||||
|
output.print("export");
|
||||||
|
output.space();
|
||||||
|
var len = self.keys.length;
|
||||||
|
if (len == 0) {
|
||||||
|
print_braced_empty(self, output);
|
||||||
|
} else if (self.keys[0] == "*") {
|
||||||
|
print_entry(0);
|
||||||
|
} else output.with_block(function() {
|
||||||
|
output.indent();
|
||||||
|
print_entry(0);
|
||||||
|
for (var i = 1; i < len; i++) {
|
||||||
|
output.print(",");
|
||||||
|
output.newline();
|
||||||
|
output.indent();
|
||||||
|
print_entry(i);
|
||||||
|
}
|
||||||
|
output.newline();
|
||||||
|
});
|
||||||
|
output.space();
|
||||||
|
output.print("from");
|
||||||
|
output.space();
|
||||||
|
output.print_string(self.path, self.quote);
|
||||||
|
output.semicolon();
|
||||||
|
|
||||||
|
function print_entry(index) {
|
||||||
|
var alias = self.aliases[index];
|
||||||
|
var key = self.keys[index];
|
||||||
|
output.print_name(key);
|
||||||
|
if (alias != key) {
|
||||||
|
output.space();
|
||||||
|
output.print("as");
|
||||||
|
output.space();
|
||||||
|
output.print_name(alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
DEFPRINT(AST_ExportReferences, function(output) {
|
||||||
|
var self = this;
|
||||||
|
output.print("export");
|
||||||
|
output.space();
|
||||||
|
print_properties(self, output);
|
||||||
|
output.semicolon();
|
||||||
|
});
|
||||||
DEFPRINT(AST_Import, function(output) {
|
DEFPRINT(AST_Import, function(output) {
|
||||||
var self = this;
|
var self = this;
|
||||||
output.print("import");
|
output.print("import");
|
||||||
@@ -1543,6 +1597,16 @@ function OutputStream(options) {
|
|||||||
DEFPRINT(AST_Symbol, function(output) {
|
DEFPRINT(AST_Symbol, function(output) {
|
||||||
print_symbol(this, output);
|
print_symbol(this, output);
|
||||||
});
|
});
|
||||||
|
DEFPRINT(AST_SymbolExport, function(output) {
|
||||||
|
var self = this;
|
||||||
|
print_symbol(self, output);
|
||||||
|
if (self.alias) {
|
||||||
|
output.space();
|
||||||
|
output.print("as");
|
||||||
|
output.space();
|
||||||
|
output.print_name(self.alias);
|
||||||
|
}
|
||||||
|
});
|
||||||
DEFPRINT(AST_SymbolImport, function(output) {
|
DEFPRINT(AST_SymbolImport, function(output) {
|
||||||
var self = this;
|
var self = this;
|
||||||
if (self.key) {
|
if (self.key) {
|
||||||
|
|||||||
117
lib/parse.js
117
lib/parse.js
@@ -844,6 +844,9 @@ function parse($TEXT, options) {
|
|||||||
case "await":
|
case "await":
|
||||||
if (S.in_async) return simple_statement();
|
if (S.in_async) return simple_statement();
|
||||||
break;
|
break;
|
||||||
|
case "export":
|
||||||
|
next();
|
||||||
|
return export_();
|
||||||
case "import":
|
case "import":
|
||||||
next();
|
next();
|
||||||
return import_();
|
return import_();
|
||||||
@@ -1275,6 +1278,115 @@ function parse($TEXT, options) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function is_alias() {
|
||||||
|
return is("name") || is_identifier_string(S.token.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function export_() {
|
||||||
|
if (is("operator", "*")) {
|
||||||
|
next();
|
||||||
|
var alias = "*";
|
||||||
|
if (is("name", "as")) {
|
||||||
|
next();
|
||||||
|
if (!is_alias()) expect_token("name");
|
||||||
|
alias = S.token.value;
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
expect_token("name", "from");
|
||||||
|
var path = S.token;
|
||||||
|
expect_token("string");
|
||||||
|
semicolon();
|
||||||
|
return new AST_ExportForeign({
|
||||||
|
aliases: [ alias ],
|
||||||
|
keys: [ "*" ],
|
||||||
|
path: path.value,
|
||||||
|
quote: path.quote,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (is("punc", "{")) {
|
||||||
|
next();
|
||||||
|
var aliases = [];
|
||||||
|
var keys = [];
|
||||||
|
while (is_alias()) {
|
||||||
|
var key = S.token;
|
||||||
|
next();
|
||||||
|
keys.push(key);
|
||||||
|
if (is("name", "as")) {
|
||||||
|
next();
|
||||||
|
if (!is_alias()) expect_token("name");
|
||||||
|
aliases.push(S.token.value);
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
aliases.push(key.value);
|
||||||
|
}
|
||||||
|
if (!is("punc", "}")) expect(",");
|
||||||
|
}
|
||||||
|
expect("}");
|
||||||
|
if (is("name", "from")) {
|
||||||
|
next();
|
||||||
|
var path = S.token;
|
||||||
|
expect_token("string");
|
||||||
|
semicolon();
|
||||||
|
return new AST_ExportForeign({
|
||||||
|
aliases: aliases,
|
||||||
|
keys: keys.map(function(token) {
|
||||||
|
return token.value;
|
||||||
|
}),
|
||||||
|
path: path.value,
|
||||||
|
quote: path.quote,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
semicolon();
|
||||||
|
return new AST_ExportReferences({
|
||||||
|
properties: keys.map(function(token, index) {
|
||||||
|
if (!is_token(token, "name")) token_error(token, "Name expected");
|
||||||
|
var sym = _make_symbol(AST_SymbolExport, token);
|
||||||
|
sym.alias = aliases[index];
|
||||||
|
return sym;
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (is("keyword", "default")) {
|
||||||
|
next();
|
||||||
|
var body = expression();
|
||||||
|
semicolon();
|
||||||
|
return new AST_ExportDefault({ body: body });
|
||||||
|
}
|
||||||
|
return new AST_ExportDeclaration({ body: export_decl() });
|
||||||
|
}
|
||||||
|
|
||||||
|
var export_decl = embed_tokens(function() {
|
||||||
|
switch (S.token.value) {
|
||||||
|
case "async":
|
||||||
|
next();
|
||||||
|
expect_token("keyword", "function");
|
||||||
|
if (!is("operator", "*")) return function_(AST_AsyncDefun);
|
||||||
|
next();
|
||||||
|
return function_(AST_AsyncGeneratorDefun);
|
||||||
|
case "const":
|
||||||
|
next();
|
||||||
|
var node = const_();
|
||||||
|
semicolon();
|
||||||
|
return node;
|
||||||
|
case "function":
|
||||||
|
next();
|
||||||
|
if (!is("operator", "*")) return function_(AST_Defun);
|
||||||
|
next();
|
||||||
|
return function_(AST_GeneratorDefun);
|
||||||
|
case "let":
|
||||||
|
next();
|
||||||
|
var node = let_();
|
||||||
|
semicolon();
|
||||||
|
return node;
|
||||||
|
case "var":
|
||||||
|
next();
|
||||||
|
var node = var_();
|
||||||
|
semicolon();
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
unexpected();
|
||||||
|
});
|
||||||
|
|
||||||
function import_() {
|
function import_() {
|
||||||
var all = null;
|
var all = null;
|
||||||
var def = as_symbol(AST_SymbolImport, true);
|
var def = as_symbol(AST_SymbolImport, true);
|
||||||
@@ -1288,7 +1400,7 @@ function parse($TEXT, options) {
|
|||||||
} else {
|
} else {
|
||||||
expect("{");
|
expect("{");
|
||||||
props = [];
|
props = [];
|
||||||
while (is("name") || is_identifier_string(S.token.value)) {
|
while (is_alias()) {
|
||||||
var alias;
|
var alias;
|
||||||
if (is_token(peek(), "name", "as")) {
|
if (is_token(peek(), "name", "as")) {
|
||||||
var key = S.token.value;
|
var key = S.token.value;
|
||||||
@@ -1307,9 +1419,8 @@ function parse($TEXT, options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (all || def || props) expect_token("name", "from");
|
if (all || def || props) expect_token("name", "from");
|
||||||
if (!is("string")) unexpected();
|
|
||||||
var path = S.token;
|
var path = S.token;
|
||||||
next();
|
expect_token("string");
|
||||||
semicolon();
|
semicolon();
|
||||||
return new AST_Import({
|
return new AST_Import({
|
||||||
all: all,
|
all: all,
|
||||||
|
|||||||
23
lib/scope.js
23
lib/scope.js
@@ -45,6 +45,7 @@
|
|||||||
|
|
||||||
function SymbolDef(id, scope, orig, init) {
|
function SymbolDef(id, scope, orig, init) {
|
||||||
this.eliminated = 0;
|
this.eliminated = 0;
|
||||||
|
this.exported = false;
|
||||||
this.global = false;
|
this.global = false;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.init = init;
|
this.init = init;
|
||||||
@@ -91,6 +92,7 @@ SymbolDef.prototype = {
|
|||||||
},
|
},
|
||||||
unmangleable: function(options) {
|
unmangleable: function(options) {
|
||||||
return this.global && !options.toplevel
|
return this.global && !options.toplevel
|
||||||
|
|| this.exported
|
||||||
|| this.undeclared
|
|| this.undeclared
|
||||||
|| !options.eval && this.scope.pinned()
|
|| !options.eval && this.scope.pinned()
|
||||||
|| options.keep_fnames
|
|| options.keep_fnames
|
||||||
@@ -118,11 +120,22 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
|||||||
// pass 1: setup scope chaining and handle definitions
|
// pass 1: setup scope chaining and handle definitions
|
||||||
var self = this;
|
var self = this;
|
||||||
var defun = null;
|
var defun = null;
|
||||||
|
var exported = false;
|
||||||
var next_def_id = 0;
|
var next_def_id = 0;
|
||||||
var scope = self.parent_scope = null;
|
var scope = self.parent_scope = null;
|
||||||
var tw = new TreeWalker(function(node, descend) {
|
var tw = new TreeWalker(function(node, descend) {
|
||||||
|
if (node instanceof AST_Definitions) {
|
||||||
|
var save_exported = exported;
|
||||||
|
exported = tw.parent() instanceof AST_ExportDeclaration;
|
||||||
|
descend();
|
||||||
|
exported = save_exported;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (node instanceof AST_LambdaDefinition) {
|
if (node instanceof AST_LambdaDefinition) {
|
||||||
|
var save_exported = exported;
|
||||||
|
exported = tw.parent() instanceof AST_ExportDeclaration;
|
||||||
node.name.walk(tw);
|
node.name.walk(tw);
|
||||||
|
exported = save_exported;
|
||||||
walk_scope(function() {
|
walk_scope(function() {
|
||||||
node.argnames.forEach(function(argname) {
|
node.argnames.forEach(function(argname) {
|
||||||
argname.walk(tw);
|
argname.walk(tw);
|
||||||
@@ -169,9 +182,11 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
|||||||
if (node instanceof AST_SymbolCatch) {
|
if (node instanceof AST_SymbolCatch) {
|
||||||
scope.def_variable(node).defun = defun;
|
scope.def_variable(node).defun = defun;
|
||||||
} else if (node instanceof AST_SymbolConst) {
|
} else if (node instanceof AST_SymbolConst) {
|
||||||
scope.def_variable(node).defun = defun;
|
var def = scope.def_variable(node);
|
||||||
|
def.defun = defun;
|
||||||
|
def.exported = exported;
|
||||||
} else if (node instanceof AST_SymbolDefun) {
|
} else if (node instanceof AST_SymbolDefun) {
|
||||||
defun.def_function(node, tw.parent());
|
defun.def_function(node, tw.parent()).exported = exported;
|
||||||
entangle(defun, scope);
|
entangle(defun, scope);
|
||||||
} else if (node instanceof AST_SymbolFunarg) {
|
} else if (node instanceof AST_SymbolFunarg) {
|
||||||
defun.def_variable(node);
|
defun.def_variable(node);
|
||||||
@@ -180,9 +195,9 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
|||||||
var def = defun.def_function(node, node.name == "arguments" ? undefined : defun);
|
var def = defun.def_function(node, node.name == "arguments" ? undefined : defun);
|
||||||
if (options.ie8) def.defun = defun.parent_scope.resolve();
|
if (options.ie8) def.defun = defun.parent_scope.resolve();
|
||||||
} else if (node instanceof AST_SymbolLet) {
|
} else if (node instanceof AST_SymbolLet) {
|
||||||
scope.def_variable(node);
|
scope.def_variable(node).exported = exported;
|
||||||
} else if (node instanceof AST_SymbolVar) {
|
} else if (node instanceof AST_SymbolVar) {
|
||||||
defun.def_variable(node, null);
|
defun.def_variable(node, null).exported = exported;
|
||||||
entangle(defun, scope);
|
entangle(defun, scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -204,6 +204,15 @@ TreeTransformer.prototype = new TreeWalker;
|
|||||||
if (self.key instanceof AST_Node) self.key = self.key.transform(tw);
|
if (self.key instanceof AST_Node) self.key = self.key.transform(tw);
|
||||||
self.value = self.value.transform(tw);
|
self.value = self.value.transform(tw);
|
||||||
});
|
});
|
||||||
|
DEF(AST_ExportDeclaration, function(self, tw) {
|
||||||
|
self.body = self.body.transform(tw);
|
||||||
|
});
|
||||||
|
DEF(AST_ExportDefault, function(self, tw) {
|
||||||
|
self.body = self.body.transform(tw);
|
||||||
|
});
|
||||||
|
DEF(AST_ExportReferences, function(self, tw) {
|
||||||
|
self.properties = do_list(self.properties, tw);
|
||||||
|
});
|
||||||
DEF(AST_Import, function(self, tw) {
|
DEF(AST_Import, function(self, tw) {
|
||||||
if (self.all) self.all = self.all.transform(tw);
|
if (self.all) self.all = self.all.transform(tw);
|
||||||
if (self.default) self.default = self.default.transform(tw);
|
if (self.default) self.default = self.default.transform(tw);
|
||||||
|
|||||||
@@ -249,12 +249,14 @@ function first_in_statement(stack, arrow) {
|
|||||||
if (p.expression === node) continue;
|
if (p.expression === node) continue;
|
||||||
} else if (p instanceof AST_Conditional) {
|
} else if (p instanceof AST_Conditional) {
|
||||||
if (p.condition === node) continue;
|
if (p.condition === node) continue;
|
||||||
|
} else if (p instanceof AST_ExportDefault) {
|
||||||
|
return false;
|
||||||
} else if (p instanceof AST_PropAccess) {
|
} else if (p instanceof AST_PropAccess) {
|
||||||
if (p.expression === node) continue;
|
if (p.expression === node) continue;
|
||||||
} else if (p instanceof AST_Sequence) {
|
} else if (p instanceof AST_Sequence) {
|
||||||
if (p.expressions[0] === node) continue;
|
if (p.expressions[0] === node) continue;
|
||||||
} else if (p instanceof AST_Statement) {
|
} else if (p instanceof AST_SimpleStatement) {
|
||||||
return p.body === node;
|
return true;
|
||||||
} else if (p instanceof AST_Template) {
|
} else if (p instanceof AST_Template) {
|
||||||
if (p.tag === node) continue;
|
if (p.tag === node) continue;
|
||||||
} else if (p instanceof AST_UnaryPostfix) {
|
} else if (p instanceof AST_UnaryPostfix) {
|
||||||
|
|||||||
144
test/compress/exports.js
Normal file
144
test/compress/exports.js
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
refs: {
|
||||||
|
input: {
|
||||||
|
export {};
|
||||||
|
export { a, b as B, c as case, d as default };
|
||||||
|
}
|
||||||
|
expect_exact: "export{};export{a as a,b as B,c as case,d as default};"
|
||||||
|
}
|
||||||
|
|
||||||
|
var_defs: {
|
||||||
|
input: {
|
||||||
|
export const a = 1;
|
||||||
|
export let b = 2, c = 3;
|
||||||
|
export var { d, e: [] } = f;
|
||||||
|
}
|
||||||
|
expect_exact: "export const a=1;export let b=2,c=3;export var{d:d,e:[]}=f;"
|
||||||
|
}
|
||||||
|
|
||||||
|
defuns: {
|
||||||
|
input: {
|
||||||
|
export function e() {}
|
||||||
|
export function* f(a) {}
|
||||||
|
export async function g(b, c) {}
|
||||||
|
export async function* h({}, ...[]) {}
|
||||||
|
}
|
||||||
|
expect_exact: "export function e(){}export function*f(a){}export async function g(b,c){}export async function*h({},...[]){}"
|
||||||
|
}
|
||||||
|
|
||||||
|
defaults: {
|
||||||
|
input: {
|
||||||
|
export default 42;
|
||||||
|
export default (x, y) => x * x;
|
||||||
|
export default function*(a, b) {};
|
||||||
|
export default async function f({ c }, ...[ d ]) {};
|
||||||
|
}
|
||||||
|
expect_exact: "export default 42;export default(x,y)=>x*x;export default function*(a,b){};export default async function f({c:c},...[d]){};"
|
||||||
|
}
|
||||||
|
|
||||||
|
foreign: {
|
||||||
|
input: {
|
||||||
|
export * from "foo";
|
||||||
|
export {} from "bar";
|
||||||
|
export * as a from "baz";
|
||||||
|
export { default } from "moo";
|
||||||
|
export { b, c as case, default as delete, d } from "moz";
|
||||||
|
}
|
||||||
|
expect_exact: 'export*from"foo";export{}from"bar";export*as a from"baz";export{default}from"moo";export{b,c as case,default as delete,d}from"moz";'
|
||||||
|
}
|
||||||
|
|
||||||
|
same_quotes: {
|
||||||
|
beautify = {
|
||||||
|
beautify: true,
|
||||||
|
quote_style: 3,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
export * from 'foo';
|
||||||
|
export {} from "bar";
|
||||||
|
}
|
||||||
|
expect_exact: [
|
||||||
|
"export * from 'foo';",
|
||||||
|
"",
|
||||||
|
'export {} from "bar";',
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_unused: {
|
||||||
|
options = {
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
export default 42;
|
||||||
|
export default (x, y) => x * x;
|
||||||
|
export default function*(a, b) {};
|
||||||
|
export default async function f({ c }, ...[ d ]) {};
|
||||||
|
export var e;
|
||||||
|
export function g(x, [ y ], ...z) {}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
export default 42;
|
||||||
|
export default (x, y) => x * x;
|
||||||
|
export default function*(a, b) {};
|
||||||
|
export default async function({}) {};
|
||||||
|
export var e;
|
||||||
|
export function g(x, []) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mangle: {
|
||||||
|
rename = false
|
||||||
|
mangle = {
|
||||||
|
toplevel: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
const a = 42;
|
||||||
|
export let b, { foo: c } = a;
|
||||||
|
export function f(d, { [b]: e }) {
|
||||||
|
d(e, f);
|
||||||
|
}
|
||||||
|
export default a;
|
||||||
|
export default async function g(x, ...{ [c]: y }) {
|
||||||
|
(await x)(g, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
const t = 42;
|
||||||
|
export let b, { foo: c } = t;
|
||||||
|
export function f(t, { [b]: o }) {
|
||||||
|
t(o, f);
|
||||||
|
}
|
||||||
|
export default t;
|
||||||
|
export default async function t(o, ...{ [c]: e}) {
|
||||||
|
(await o)(t, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mangle_rename: {
|
||||||
|
rename = true
|
||||||
|
mangle = {
|
||||||
|
toplevel: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
const a = 42;
|
||||||
|
export let b, { foo: c } = a;
|
||||||
|
export function f(d, { [b]: e }) {
|
||||||
|
d(e, f);
|
||||||
|
}
|
||||||
|
export default a;
|
||||||
|
export default async function g(x, ...{ [c]: y }) {
|
||||||
|
(await x)(g, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
const t = 42;
|
||||||
|
export let b, { foo: c } = t;
|
||||||
|
export function f(t, { [b]: o }) {
|
||||||
|
t(o, f);
|
||||||
|
}
|
||||||
|
export default t;
|
||||||
|
export default async function t(o, ...{ [c]: e}) {
|
||||||
|
(await o)(t, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
71
test/mocha/exports.js
Normal file
71
test/mocha/exports.js
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
var assert = require("assert");
|
||||||
|
var UglifyJS = require("../node");
|
||||||
|
|
||||||
|
describe("export", function() {
|
||||||
|
it("Should reject invalid `export ...` statement syntax", function() {
|
||||||
|
[
|
||||||
|
"export *;",
|
||||||
|
"export A;",
|
||||||
|
"export 42;",
|
||||||
|
"export var;",
|
||||||
|
"export * as A;",
|
||||||
|
"export A as B;",
|
||||||
|
"export const A;",
|
||||||
|
"export function(){};",
|
||||||
|
].forEach(function(code) {
|
||||||
|
assert.throws(function() {
|
||||||
|
UglifyJS.parse(code);
|
||||||
|
}, function(e) {
|
||||||
|
return e instanceof UglifyJS.JS_Parse_Error;
|
||||||
|
}, code);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("Should reject invalid `export { ... }` statement syntax", function() {
|
||||||
|
[
|
||||||
|
"export { * };",
|
||||||
|
"export { * as A };",
|
||||||
|
"export { 42 as A };",
|
||||||
|
"export { A as B-C };",
|
||||||
|
"export { default as A };",
|
||||||
|
].forEach(function(code) {
|
||||||
|
assert.throws(function() {
|
||||||
|
UglifyJS.parse(code);
|
||||||
|
}, function(e) {
|
||||||
|
return e instanceof UglifyJS.JS_Parse_Error;
|
||||||
|
}, code);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("Should reject invalid `export default ...` statement syntax", function() {
|
||||||
|
[
|
||||||
|
"export default *;",
|
||||||
|
"export default var;",
|
||||||
|
"export default A as B;",
|
||||||
|
].forEach(function(code) {
|
||||||
|
assert.throws(function() {
|
||||||
|
UglifyJS.parse(code);
|
||||||
|
}, function(e) {
|
||||||
|
return e instanceof UglifyJS.JS_Parse_Error;
|
||||||
|
}, code);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("Should reject invalid `export ... from ...` statement syntax", function() {
|
||||||
|
[
|
||||||
|
"export from 'path';",
|
||||||
|
"export * from `path`;",
|
||||||
|
"export A as B from 'path';",
|
||||||
|
"export default from 'path';",
|
||||||
|
"export { A }, B from 'path';",
|
||||||
|
"export * as A, B from 'path';",
|
||||||
|
"export * as A, {} from 'path';",
|
||||||
|
"export { * as A } from 'path';",
|
||||||
|
"export { 42 as A } from 'path';",
|
||||||
|
"export { A-B as C } from 'path';",
|
||||||
|
].forEach(function(code) {
|
||||||
|
assert.throws(function() {
|
||||||
|
UglifyJS.parse(code);
|
||||||
|
}, function(e) {
|
||||||
|
return e instanceof UglifyJS.JS_Parse_Error;
|
||||||
|
}, code);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user