[ES6] Implemented parse for export Name from Module variants. (#1701)
- add `AST_Export` new variants output
- add tests to `test/compress/`
- update `$propdoc` of `AST_Export` ("exported_names" & "module_name")
- add tests for `export ... as ...` variants
This commit is contained in:
committed by
Alex Lam S.L
parent
fccefbeaca
commit
5dea52266b
@@ -789,11 +789,13 @@ var AST_Import = DEFNODE("Import", "imported_name imported_names module_name", {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var AST_Export = DEFNODE("Export", "exported_definition exported_value is_default", {
|
var AST_Export = DEFNODE("Export", "exported_definition exported_value is_default exported_names module_name", {
|
||||||
$documentation: "An `export` statement",
|
$documentation: "An `export` statement",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
exported_definition: "[AST_Defun|AST_Definitions|AST_DefClass?] An exported definition",
|
exported_definition: "[AST_Defun|AST_Definitions|AST_DefClass?] An exported definition",
|
||||||
exported_value: "[AST_Node?] An exported value",
|
exported_value: "[AST_Node?] An exported value",
|
||||||
|
exported_names: "[AST_NameImport*?] List of exported names",
|
||||||
|
module_name: "[AST_String?] Name of the file to load exports from",
|
||||||
is_default: "[Boolean] Whether this is the default exported value of this module"
|
is_default: "[Boolean] Whether this is the default exported value of this module"
|
||||||
},
|
},
|
||||||
_walk: function (visitor) {
|
_walk: function (visitor) {
|
||||||
|
|||||||
@@ -1277,11 +1277,37 @@ function OutputStream(options) {
|
|||||||
output.print("default");
|
output.print("default");
|
||||||
output.space();
|
output.space();
|
||||||
}
|
}
|
||||||
if (self.exported_value) {
|
if (self.exported_names) {
|
||||||
|
output.space();
|
||||||
|
|
||||||
|
if (self.exported_names.length === 1 && self.exported_names[0].name.name === "*") {
|
||||||
|
self.exported_names[0].print(output);
|
||||||
|
} else {
|
||||||
|
output.print("{");
|
||||||
|
self.exported_names.forEach(function (name_import, i) {
|
||||||
|
output.space();
|
||||||
|
name_import.print(output);
|
||||||
|
if (i < self.exported_names.length - 1) {
|
||||||
|
output.print(",");
|
||||||
|
output.space();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
output.space();
|
||||||
|
output.print("}");
|
||||||
|
}
|
||||||
|
output.space();
|
||||||
|
}
|
||||||
|
else if (self.exported_value) {
|
||||||
self.exported_value.print(output);
|
self.exported_value.print(output);
|
||||||
} else if (self.exported_definition) {
|
} else if (self.exported_definition) {
|
||||||
self.exported_definition.print(output);
|
self.exported_definition.print(output);
|
||||||
}
|
}
|
||||||
|
if (self.module_name) {
|
||||||
|
output.space();
|
||||||
|
output.print("from");
|
||||||
|
output.space();
|
||||||
|
self.module_name.print(output);
|
||||||
|
}
|
||||||
output.semicolon();
|
output.semicolon();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
68
lib/parse.js
68
lib/parse.js
@@ -2237,17 +2237,85 @@ function parse($TEXT, options) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function import_nameAsterisk() {
|
||||||
|
var start = S.token;
|
||||||
|
var foreign_name;
|
||||||
|
var name;
|
||||||
|
|
||||||
|
next();
|
||||||
|
|
||||||
|
var end = prev();
|
||||||
|
|
||||||
|
name = new AST_SymbolImport({
|
||||||
|
name: '*',
|
||||||
|
start: start,
|
||||||
|
end: end,
|
||||||
|
});
|
||||||
|
|
||||||
|
foreign_name = new AST_SymbolImportForeign({
|
||||||
|
name: '*',
|
||||||
|
start: start,
|
||||||
|
end: end,
|
||||||
|
});
|
||||||
|
|
||||||
|
return new AST_NameImport({
|
||||||
|
start: start,
|
||||||
|
foreign_name: foreign_name,
|
||||||
|
name: name,
|
||||||
|
end: end,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function export_() {
|
function export_() {
|
||||||
var start = S.token;
|
var start = S.token;
|
||||||
var is_default;
|
var is_default;
|
||||||
var exported_value;
|
var exported_value;
|
||||||
var exported_definition;
|
var exported_definition;
|
||||||
|
var exported_names;
|
||||||
|
|
||||||
if (is("keyword", "default")) {
|
if (is("keyword", "default")) {
|
||||||
is_default = true;
|
is_default = true;
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is("punc", "{")) {
|
||||||
|
next();
|
||||||
|
exported_names = [];
|
||||||
|
while (!is("punc", "}")) {
|
||||||
|
exported_names.push(import_name());
|
||||||
|
if (is("punc", ",")) {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
} else if (is("operator", "*")) {
|
||||||
|
var st = prev();
|
||||||
|
exported_names = [import_nameAsterisk()];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exported_names) {
|
||||||
|
expect_token("name", "from");
|
||||||
|
|
||||||
|
var mod_str = S.token;
|
||||||
|
if (mod_str.type !== 'string') {
|
||||||
|
unexpected();
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
|
||||||
|
return new AST_Export({
|
||||||
|
start: start,
|
||||||
|
is_default: is_default,
|
||||||
|
exported_names: exported_names,
|
||||||
|
module_name: new AST_String({
|
||||||
|
start: mod_str,
|
||||||
|
value: mod_str.value,
|
||||||
|
quote: mod_str.quote,
|
||||||
|
end: mod_str,
|
||||||
|
}),
|
||||||
|
end: prev(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var is_definition =
|
var is_definition =
|
||||||
is("keyword", "var") || is("keyword", "let") || is("keyword", "const") ||
|
is("keyword", "var") || is("keyword", "let") || is("keyword", "const") ||
|
||||||
is("keyword", "class") || is("keyword", "function");
|
is("keyword", "class") || is("keyword", "function");
|
||||||
|
|||||||
@@ -199,6 +199,15 @@ export_statement: {
|
|||||||
expect_exact: "export default 1;export var foo=4;export let foo=6;export const foo=6;export function foo(){};export class foo{};"
|
expect_exact: "export default 1;export var foo=4;export let foo=6;export const foo=6;export function foo(){};export class foo{};"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export_module_statement: {
|
||||||
|
input: {
|
||||||
|
export * from "a.js";
|
||||||
|
export {A} from "a.js";
|
||||||
|
export {A, B} from "a.js";
|
||||||
|
}
|
||||||
|
expect_exact: 'export*from"a.js";export{A}from"a.js";export{A,B}from"a.js";'
|
||||||
|
}
|
||||||
|
|
||||||
import_statement_mangling: {
|
import_statement_mangling: {
|
||||||
mangle = { toplevel: true };
|
mangle = { toplevel: true };
|
||||||
input: {
|
input: {
|
||||||
|
|||||||
40
test/mocha/export.js
Normal file
40
test/mocha/export.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
var assert = require("assert");
|
||||||
|
var uglify = require("../../");
|
||||||
|
|
||||||
|
describe("Export", function() {
|
||||||
|
it ("Should parse export directives", function() {
|
||||||
|
|
||||||
|
var inputs = [
|
||||||
|
['export * from "a.js"', ['*'], "a.js"],
|
||||||
|
['export {A} from "a.js"', ['A'], "a.js"],
|
||||||
|
['export {A as X} from "a.js"', ['X'], "a.js"],
|
||||||
|
['export {A as Foo, B} from "a.js"', ['Foo', 'B'], "a.js"],
|
||||||
|
['export {A, B} from "a.js"', ['A', 'B'], "a.js"],
|
||||||
|
];
|
||||||
|
|
||||||
|
var test = function(code) {
|
||||||
|
return uglify.parse(code, {fromString: true});
|
||||||
|
};
|
||||||
|
|
||||||
|
var extractNames = function(symbols) {
|
||||||
|
var ret = [];
|
||||||
|
for (var i = 0; i < symbols.length; i++) {
|
||||||
|
ret.push(symbols[i].name.name)
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var i = 0; i < inputs.length; i++) {
|
||||||
|
var ast = test(inputs[i][0]);
|
||||||
|
var names = inputs[i][1];
|
||||||
|
var filename = inputs[i][2];
|
||||||
|
assert(ast instanceof uglify.AST_Toplevel);
|
||||||
|
assert.equal(ast.body.length, 1);
|
||||||
|
var st = ast.body[0];
|
||||||
|
assert(st instanceof uglify.AST_Export);
|
||||||
|
var actualNames = extractNames(st.exported_names);
|
||||||
|
assert.deepEqual(actualNames, names);
|
||||||
|
assert.equal(st.module_name.value, filename)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
35
test/mocha/issue1702.js
Normal file
35
test/mocha/issue1702.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
var uglify = require('../../');
|
||||||
|
var assert = require("assert");
|
||||||
|
|
||||||
|
describe("For statement", function() {
|
||||||
|
it("For variable should list enclosing scope in its references (issue #17022)", function() {
|
||||||
|
var ast = uglify.parse("function f() { for (var a = 0; a < 10; a++) {} }");
|
||||||
|
ast.figure_out_scope();
|
||||||
|
|
||||||
|
var checkWalker = new uglify.TreeWalker(function(node, descend) {
|
||||||
|
if (node instanceof uglify.AST_VarDef) {
|
||||||
|
console.log("AST_VarDef");
|
||||||
|
// one reference should be in the AST_Defun scope - search for it
|
||||||
|
|
||||||
|
var walkNode = function (r) {
|
||||||
|
console.log(r.CTOR.name);
|
||||||
|
var walker = new uglify.TreeWalker(function(node, descend){
|
||||||
|
// do not walk into any other scope, it should be listed if needed
|
||||||
|
console.log(" " + node.CTOR.name);
|
||||||
|
if (node instanceof uglify.AST_Scope && node != r.scope) return true;
|
||||||
|
if (node instanceof uglify.AST_For) {
|
||||||
|
console.log("Great - we found the for statement referencing the variable")
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
r.scope.walk(walker);
|
||||||
|
r.walk(walker);
|
||||||
|
};
|
||||||
|
|
||||||
|
node.name.thedef.orig.forEach(walkNode);
|
||||||
|
node.name.thedef.references.forEach(walkNode);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ast.walk(checkWalker);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user