diff --git a/lib/ast.js b/lib/ast.js index fabe7b40..11dd9250 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -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", $propdoc: { exported_definition: "[AST_Defun|AST_Definitions|AST_DefClass?] An exported definition", exported_value: "[AST_Node?] An exported value", + exported_names: "[string*] List of exported names", + module_name: "[string?] Name of the file to load exports from", is_default: "[Boolean] Whether this is the default exported value of this module" }, _walk: function (visitor) { @@ -1179,6 +1181,9 @@ var AST_SymbolImport = DEFNODE("SymbolImport", null, { var AST_SymbolImportForeign = DEFNODE("SymbolImportForeign", null, { $documentation: "A symbol imported from a module, but it is defined in the other module, and its real name is irrelevant for this module's purposes", }, AST_Symbol); +var AST_SymbolImportAsterisk = DEFNODE("SymbolImportAsterisk", null, { + $documentation: "All symbols imported / exported from a module", +}, AST_Symbol); var AST_Label = DEFNODE("Label", "references", { $documentation: "Symbol naming a label (declaration)", diff --git a/lib/parse.js b/lib/parse.js index 4c410040..d7de38af 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -2236,17 +2236,86 @@ function parse($TEXT, options) { }) } + function import_nameAsterisk() { + var start = S.token; + var foreign_name; + var name; + + next(); + + var end = prev(); + + name = new AST_Symbol({ + 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_() { var start = S.token; var is_default; var exported_value; var exported_definition; + var exported_names; if (is("keyword", "default")) { is_default = true; 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 = is("keyword", "var") || is("keyword", "let") || is("keyword", "const") || is("keyword", "class") || is("keyword", "function"); diff --git a/test/mocha/export.js b/test/mocha/export.js new file mode 100644 index 00000000..f8a26657 --- /dev/null +++ b/test/mocha/export.js @@ -0,0 +1,41 @@ +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, 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++) { + //console.log(inputs[i][0]); + 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) + } + + + }) +}); \ No newline at end of file