parse export & import statements correctly (#5550)

fixes #5548
This commit is contained in:
Alex Lam S.L
2022-07-07 21:04:56 +01:00
committed by GitHub
parent 80787ff7ef
commit b2bc2e1173
5 changed files with 93 additions and 19 deletions

View File

@@ -815,7 +815,7 @@ function parse($TEXT, options) {
} }
} }
var statement = embed_tokens(function() { var statement = embed_tokens(function(toplevel) {
handle_regexp(); handle_regexp();
switch (S.token.type) { switch (S.token.type) {
case "string": case "string":
@@ -854,9 +854,11 @@ function parse($TEXT, options) {
if (S.in_async) return simple_statement(); if (S.in_async) return simple_statement();
break; break;
case "export": case "export":
if (!toplevel && options.module !== "") unexpected();
next(); next();
return export_(); return export_();
case "import": case "import":
if (!toplevel && options.module !== "") unexpected();
var token = peek(); var token = peek();
if (!(token.type == "punc" && /^[(.]$/.test(token.value))) { if (!(token.type == "punc" && /^[(.]$/.test(token.value))) {
next(); next();
@@ -2563,7 +2565,7 @@ function parse($TEXT, options) {
} }
S.input.push_directives_stack(); S.input.push_directives_stack();
while (!is("eof")) while (!is("eof"))
body.push(statement()); body.push(statement(true));
S.input.pop_directives_stack(); S.input.pop_directives_stack();
var end = prev() || start; var end = prev() || start;
var toplevel = options.toplevel; var toplevel = options.toplevel;

View File

@@ -69,7 +69,7 @@ function make_code(ast, options) {
function parse_test(file) { function parse_test(file) {
var script = fs.readFileSync(file, "utf8"); var script = fs.readFileSync(file, "utf8");
try { try {
var ast = U.parse(script, { filename: file }); var ast = U.parse(script, { filename: file, module: "" });
} catch (e) { } catch (e) {
console.error("Caught error while parsing tests in " + file); console.error("Caught error while parsing tests in " + file);
console.error(e); console.error(e);

View File

@@ -193,22 +193,6 @@ forbid_merge: {
} }
} }
merge_tail: {
options = {
conditionals: true,
}
input: {
if (console)
import "foo";
else
import "foo";
}
expect: {
console;
import "foo";
}
}
issue_4708_1: { issue_4708_1: {
options = { options = {
imports: true, imports: true,

View File

@@ -68,4 +68,64 @@ describe("export", function() {
}, code); }, code);
}); });
}); });
it("Should reject `export` statement not under top-level scope", function() {
[
"{ export {}; }",
"if (0) export var A;",
"function f() { export default 42; }",
].forEach(function(code) {
assert.throws(function() {
UglifyJS.parse(code);
}, function(e) {
return e instanceof UglifyJS.JS_Parse_Error;
}, code);
});
});
it("Should compare `export` statements correctly", function() {
var stats = {
Declaration: [
"export let A;",
"export const A = 42;",
"export var { A, B: [] } = C;",
"export function A() { return B(A); }",
"export async function* A({}, ...[]) { return B(A); }",
],
Default: [
"export default 42;",
"export default A => A(B);",
"export default class A extends B {}",
"export default (class A extends B {});",
"export default class A { static C = 42; }",
"export default class A extends B { static C = 42; }",
],
Foreign: [
"export * from 'path';",
"export {} from 'path';",
"export * as A from 'path';",
"export { default } from 'path';",
"export { A, B as C } from 'path';",
"export { A, default as C } from 'path';",
],
References: [
"export {};",
"export { A };",
"export { A as B };",
"export { A, B as C };",
"export { A as default };",
],
};
for (var k in stats) stats[k].forEach(function(c, i) {
var s = UglifyJS.parse(c);
assert.ok(s instanceof UglifyJS.AST_Toplevel, c);
assert.strictEqual(s.body.length, 1, c);
assert.strictEqual(s.body[0].TYPE, "Export" + k, c);
for (var l in stats) stats[l].forEach(function(d, j) {
var t = UglifyJS.parse(d);
assert.ok(t instanceof UglifyJS.AST_Toplevel, d);
assert.strictEqual(t.body.length, 1, d);
assert.strictEqual(t.body[0].TYPE, "Export" + l, d);
assert.strictEqual(s.equals(t), k === l && i === j, c + "\n" + d);
});
});
});
}); });

View File

@@ -8,13 +8,16 @@ describe("import", function() {
"import A;", "import A;",
"import {};", "import {};",
"import `path`;", "import `path`;",
"{ import 'path'; }",
"import from 'path';", "import from 'path';",
"if (0) import 'path';",
"import * from 'path';", "import * from 'path';",
"import A as B from 'path';", "import A as B from 'path';",
"import { A }, B from 'path';", "import { A }, B from 'path';",
"import * as A, B from 'path';", "import * as A, B from 'path';",
"import * as A, {} from 'path';", "import * as A, {} from 'path';",
"import { * as A } from 'path';", "import { * as A } from 'path';",
"function f() { import 'path'; }",
"import { 42 as A } from 'path';", "import { 42 as A } from 'path';",
"import { A-B as C } from 'path';", "import { A-B as C } from 'path';",
].forEach(function(code) { ].forEach(function(code) {
@@ -25,4 +28,29 @@ describe("import", function() {
}, code); }, code);
}); });
}); });
it("Should compare `import` statements correctly", function() {
[
"import 'foo';",
"import 'path';",
"import A from 'path';",
"import { A } from 'path';",
"import * as A from 'path';",
"import A, { B } from 'path';",
"import A, * as B from 'path';",
"import { A as B } from 'path';",
"import A, { B, C as D } from 'path';",
].forEach(function(c, i, stats) {
var s = UglifyJS.parse(c);
assert.ok(s instanceof UglifyJS.AST_Toplevel, c);
assert.strictEqual(s.body.length, 1, c);
assert.ok(s.body[0] instanceof UglifyJS.AST_Import, c);
stats.forEach(function(d, j) {
var t = UglifyJS.parse(d);
assert.ok(t instanceof UglifyJS.AST_Toplevel, d);
assert.strictEqual(t.body.length, 1, d);
assert.ok(t.body[0] instanceof UglifyJS.AST_Import, d);
assert.strictEqual(s.equals(t), i === j, c + "\n" + d);
});
});
});
}); });