From a020d2ead342f95e6c577af8efa9b3b82c399312 Mon Sep 17 00:00:00 2001 From: kzc Date: Thu, 28 Sep 2017 06:43:09 -0400 Subject: [PATCH] support dynamic `import()`, trap invalid use of `export` (#2335) --- lib/parse.js | 25 ++++++++++++++----------- test/compress/export.js | 24 ++++++++++++++++++++++++ test/mocha/export.js | 20 +++++++++++++++++++- 3 files changed, 57 insertions(+), 12 deletions(-) diff --git a/lib/parse.js b/lib/parse.js index 0cd5671f..0fe9e0cf 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -44,9 +44,9 @@ "use strict"; -var KEYWORDS = 'break case catch class const continue debugger default delete do else export extends finally for function if in instanceof new return switch throw try typeof var let void while with import'; +var KEYWORDS = 'break case catch class const continue debugger default delete do else export extends finally for function if in instanceof let new return switch throw try typeof var void while with'; var KEYWORDS_ATOM = 'false null true'; -var RESERVED_WORDS = 'enum implements interface package private protected public static super this ' + KEYWORDS_ATOM + " " + KEYWORDS; +var RESERVED_WORDS = 'enum implements import interface package private protected public static super this ' + KEYWORDS_ATOM + " " + KEYWORDS; var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case yield await'; KEYWORDS = makePredicate(KEYWORDS); @@ -1013,6 +1013,12 @@ function parse($TEXT, options) { next(); return function_(AST_Defun, false, true); } + if (S.token.value == "import" && !is_token(peek(), "punc", "(")) { + next(); + var node = import_(); + semicolon(); + return node; + } return is_token(peek(), "punc", ":") ? labeled_statement() : simple_statement(); @@ -1149,15 +1155,11 @@ function parse($TEXT, options) { body : statement() }); - case "import": - next(); - var node = import_(); - semicolon(); - return node; - case "export": - next(); - return export_(); + if (!is_token(peek(), "punc", "(")) { + next(); + return export_(); + } } } unexpected(); @@ -1875,7 +1877,8 @@ function parse($TEXT, options) { : !no_in && kind === "const" && S.input.has_directive("use strict") ? croak("Missing initializer in const declaration") : null, end : prev() - }) + }); + if (def.name.name == "import") croak("Unexpected token: import"); } a.push(def); if (!is("punc", ",")) diff --git a/test/compress/export.js b/test/compress/export.js index 84c0e5b4..6b5c7478 100644 --- a/test/compress/export.js +++ b/test/compress/export.js @@ -226,3 +226,27 @@ keyword_valid_3: { export { default as default } from "module.js"; } } + +dynamic_import: { + mangle = { + toplevel: true, + } + input: { + import traditional from './traditional.js'; + function imp(x) { + return import(x); + } + import("module_for_side_effects.js"); + let dynamic = import("some/module.js"); + dynamic.foo(); + } + expect: { + import o from "./traditional.js"; + function t(o) { + return import(o); + } + import("module_for_side_effects.js"); + let r = import("some/module.js"); + r.foo(); + } +} diff --git a/test/mocha/export.js b/test/mocha/export.js index e9bb8503..187ca57e 100644 --- a/test/mocha/export.js +++ b/test/mocha/export.js @@ -1,7 +1,7 @@ var assert = require("assert"); var uglify = require("../node"); -describe("Export", function() { +describe("Export/Import", function() { it("Should parse export directives", function() { var inputs = [ ['export * from "a.js"', ['*'], "a.js"], @@ -36,4 +36,22 @@ describe("Export", function() { assert.equal(st.module_name.value, filename); } }); + + it("Should not parse invalid uses of export", function() { + assert.equal(uglify.minify("export").error.message, "Unexpected token: eof (undefined)"); + assert.equal(uglify.minify("export;").error.message, "Unexpected token: punc (;)"); + assert.equal(uglify.minify("export();").error.message, "Unexpected token: keyword (export)"); + assert.equal(uglify.minify("export(1);").error.message, "Unexpected token: keyword (export)"); + assert.equal(uglify.minify("var export;").error.message, "Name expected"); + assert.equal(uglify.minify("var export = 1;").error.message, "Name expected"); + assert.equal(uglify.minify("function f(export){}").error.message, "Invalid function parameter"); + }); + + it("Should not parse invalid uses of import", function() { + assert.equal(uglify.minify("import").error.message, "Unexpected token: eof (undefined)"); + assert.equal(uglify.minify("import;").error.message, "Unexpected token: punc (;)"); + assert.equal(uglify.minify("var import;").error.message, "Unexpected token: import"); + assert.equal(uglify.minify("var import = 1;").error.message, "Unexpected token: import"); + assert.equal(uglify.minify("function f(import){}").error.message, "Unexpected token: name (import)"); + }); });