From 71556d00b5ceab4304077a9898c7ed0264041a76 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 21 Jun 2017 23:15:39 +0800 Subject: [PATCH] correctly parse `export` of `function` & `class` (#2135) --- lib/parse.js | 96 ++++++++----------- test/compress/export.js | 13 +++ test/input/invalid/{export.js => export_1.js} | 0 test/input/invalid/export_2.js | 1 + test/input/invalid/export_3.js | 1 + test/mocha/cli.js | 34 ++++++- 6 files changed, 89 insertions(+), 56 deletions(-) rename test/input/invalid/{export.js => export_1.js} (100%) create mode 100644 test/input/invalid/export_2.js create mode 100644 test/input/invalid/export_3.js diff --git a/lib/parse.js b/lib/parse.js index 27ecba59..57710d15 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -2413,72 +2413,60 @@ function parse($TEXT, options) { 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(); - } else { - exported_names = map_names(false); + } else if (exported_names = map_names(false)) { + if (is("name", "from")) { + next(); - if (exported_names) { - if (is("name", "from")) { - next(); - - 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(), - }); - } else { - return new AST_Export({ - start: start, - is_default: is_default, - exported_names: exported_names, - end: prev(), - }); + 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(), + }); + } else { + return new AST_Export({ + start: start, + is_default: is_default, + exported_names: exported_names, + end: prev(), + }); } } - 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")) { - 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 { + var node; + var exported_value; + var exported_definition; + if (is("punc", "{") + || is_default + && (is("keyword", "class") || is("keyword", "function")) + && is_token(peek(), "punc")) { exported_value = expression(false); semicolon(); + } else if ((node = statement()) instanceof AST_Definitions && is_default) { + unexpected(node.start); + } else if (node instanceof AST_Definitions || node instanceof AST_Defun || node instanceof AST_DefClass) { + exported_definition = node; + } else if (node instanceof AST_SimpleStatement) { + exported_value = node.body; + } else { + unexpected(node.start); } return new AST_Export({ diff --git a/test/compress/export.js b/test/compress/export.js index 4b90bcff..c79a3b77 100644 --- a/test/compress/export.js +++ b/test/compress/export.js @@ -107,3 +107,16 @@ issue_2129: { export const { keys } = Object; } } + +async_func: { + options = { + keep_fargs: false, + unused: true, + } + input: { + export async function Foo(x){}; + } + expect: { + export async function Foo(){}; + } +} diff --git a/test/input/invalid/export.js b/test/input/invalid/export_1.js similarity index 100% rename from test/input/invalid/export.js rename to test/input/invalid/export_1.js diff --git a/test/input/invalid/export_2.js b/test/input/invalid/export_2.js new file mode 100644 index 00000000..08cb2f63 --- /dev/null +++ b/test/input/invalid/export_2.js @@ -0,0 +1 @@ +export class{}; diff --git a/test/input/invalid/export_3.js b/test/input/invalid/export_3.js new file mode 100644 index 00000000..8c288ea2 --- /dev/null +++ b/test/input/invalid/export_3.js @@ -0,0 +1 @@ +export function(){}; diff --git a/test/mocha/cli.js b/test/mocha/cli.js index c01c02dd..1e96fc0b 100644 --- a/test/mocha/cli.js +++ b/test/mocha/cli.js @@ -534,13 +534,13 @@ describe("bin/uglifyjs", function () { }); }); it("Should throw syntax error (block-level export)", function(done) { - var command = uglifyjscmd + ' test/input/invalid/export.js -m'; + var command = uglifyjscmd + ' test/input/invalid/export_1.js -m'; exec(command, function (err, stdout, stderr) { assert.ok(err); assert.strictEqual(stdout, ""); assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ - "Parse error at test/input/invalid/export.js:2,4", + "Parse error at test/input/invalid/export_1.js:2,4", " export var V = 1;", " ^", "ERROR: Export statement may only appear at top level" @@ -563,6 +563,36 @@ describe("bin/uglifyjs", function () { done(); }); }); + it("Should throw syntax error (anonymous class)", function(done) { + var command = uglifyjscmd + ' test/input/invalid/export_2.js'; + + exec(command, function (err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/export_2.js:1,12", + "export class{};", + " ^", + "ERROR: Unexpected token: punc ({)" + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (anonymous function)", function(done) { + var command = uglifyjscmd + ' test/input/invalid/export_3.js'; + + exec(command, function (err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/export_3.js:1,15", + "export function(){};", + " ^", + "ERROR: Unexpected token: punc (()" + ].join("\n")); + done(); + }); + }); it("Should handle literal string as source map input", function(done) { var command = [ uglifyjscmd,