fix(es/parser): Fix parsing of export specifiers (#5190)

This commit is contained in:
magic-akari 2022-07-13 18:55:55 +08:00 committed by GitHub
parent 8ee3c8f5fb
commit ec9378370d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 136 additions and 72 deletions

View File

@ -326,6 +326,7 @@ fn named_and_namespace_export_from() {
"export * as Foo, { bar } from 'foo';",
Default::default(),
Syntax::Es(EsConfig {
export_default_from: true,
..EsConfig::default()
}),
);
@ -341,6 +342,7 @@ fn named_and_namespace_export_from_min() {
..Default::default()
},
Syntax::Es(EsConfig {
export_default_from: true,
..EsConfig::default()
}),
);

View File

@ -1644,6 +1644,67 @@ let x = 4";
);
}
#[test]
#[should_panic(expected = "Expected 'from', got ','")]
fn issue_4369_1() {
test_parser(
r#"export * as foo, { bar } from "mod""#,
Syntax::Es(EsConfig {
export_default_from: false,
..Default::default()
}),
|p| p.parse_module(),
);
}
#[test]
fn issue_4369_2() {
test_parser(
r#"export foo, * as bar, { baz } from "mod""#,
Syntax::Es(EsConfig {
export_default_from: true,
..Default::default()
}),
|p| p.parse_module(),
);
}
#[test]
fn issue_4369_3() {
test_parser(
r#"export foo, * as bar from "mod""#,
Syntax::Es(EsConfig {
export_default_from: true,
..Default::default()
}),
|p| p.parse_module(),
);
}
#[test]
fn issue_4369_4() {
test_parser(
r#"export * as bar, { baz } from "mod""#,
Syntax::Es(EsConfig {
export_default_from: true,
..Default::default()
}),
|p| p.parse_module(),
);
}
#[test]
fn issue_4369_5() {
test_parser(
r#"export foo, { baz } from "mod""#,
Syntax::Es(EsConfig {
export_default_from: true,
..Default::default()
}),
|p| p.parse_module(),
);
}
#[test]
fn issue_257_var() {
test_parser(

View File

@ -409,43 +409,14 @@ impl<I: Tokens> Parser<I> {
}
}
let mut has_star = false;
let mut export_ns = None;
let ns_export_specifier_start = cur_pos!(self);
let type_only = self.input.syntax().typescript() && eat!(self, "type");
if eat!(self, '*') {
has_star = true;
if self.input.syntax().typescript() && type_only {
// export type * from "mod";
// or
// export type * as foo from "mod";
self.emit_err(span!(self, start), SyntaxError::TS1383)
}
if is!(self, "from") {
let (src, asserts) = self.parse_from_clause_and_semi()?;
return Ok(ModuleDecl::ExportAll(ExportAll {
span: span!(self, start),
src,
asserts,
}));
}
if eat!(self, "as") {
let _ = cur!(self, false);
let name = self.parse_module_export_name()?;
export_ns = Some(ExportSpecifier::Namespace(ExportNamespaceSpecifier {
span: span!(self, ns_export_specifier_start),
name,
}));
}
}
// Some("default") if default is exported from 'src'
let mut export_default = None;
if !type_only && export_ns.is_none() && eat!(self, "default") {
if !type_only && eat!(self, "default") {
if self.input.syntax().typescript() {
if is!(self, "abstract")
&& peeked_is!(self, "class")
@ -492,7 +463,8 @@ impl<I: Tokens> Parser<I> {
let decl = self.parse_default_fn(start, decorators)?;
return Ok(ModuleDecl::ExportDefaultDecl(decl));
} else if self.input.syntax().export_default_from()
&& (is!(self, "from") || (is!(self, ',') && peeked_is!(self, '{')))
&& (is!(self, "from")
|| (is!(self, ',') && (peeked_is!(self, '{') || peeked_is!(self, '*'))))
{
export_default = Some(Ident::new("default".into(), self.input.prev_span()))
} else {
@ -547,22 +519,20 @@ impl<I: Tokens> Parser<I> {
{
self.parse_var_stmt(false).map(Decl::Var)?
} else {
// export {};
// export {} from '';
if is!(self, "from") {
if let Some(s) = export_ns {
let (src, asserts) = self.parse_from_clause_and_semi()?;
return Ok(ModuleDecl::ExportNamed(NamedExport {
span: span!(self, start),
specifiers: vec![s],
src: Some(src),
type_only,
asserts,
}));
}
}
// ```javascript
// export foo, * as bar, { baz } from "mod"; // *
// export * as bar, { baz } from "mod"; // *
// export foo, { baz } from "mod"; // *
// export foo, * as bar from "mod"; // *
// export foo from "mod"; // *
// export * as bar from "mod"; //
// export { baz } from "mod"; //
// export { baz } ; //
// export * from "mod"; //
// ```
// export default
// export foo
let default = match export_default {
Some(default) => Some(default),
None => {
@ -574,47 +544,78 @@ impl<I: Tokens> Parser<I> {
}
};
if is!(self, "from") {
if let Some(default) = default {
let (src, asserts) = self.parse_from_clause_and_semi()?;
return Ok(ModuleDecl::ExportNamed(NamedExport {
span: span!(self, start),
specifiers: vec![ExportSpecifier::Default(ExportDefaultSpecifier {
exported: default,
})],
src: Some(src),
type_only,
asserts,
}));
}
if self.input.syntax().typescript() && type_only && !is!(self, '{') {
self.emit_err(span!(self, start), SyntaxError::TS1383)
}
if has_star && export_ns.is_none() {
if default.is_none() && is!(self, '*') && !peeked_is!(self, "as") {
assert_and_bump!(self, '*');
// improve error message for `export * from foo`
let (src, asserts) = self.parse_from_clause_and_semi()?;
return Ok(ModuleDecl::ExportAll(ExportAll {
span: Span::new(start, src.span.hi(), Default::default()),
span: span!(self, start),
src,
asserts,
}));
}
let has_ns = export_ns.is_some();
let has_default = default.is_some();
if has_ns || has_default {
expect!(self, ',')
}
expect!(self, '{');
let mut specifiers = vec![];
if let Some(s) = export_ns {
specifiers.push(s)
}
let mut has_default = false;
let mut has_ns = false;
if let Some(default) = default {
has_default = true;
specifiers.push(ExportSpecifier::Default(ExportDefaultSpecifier {
exported: default,
}))
}
// export foo, * as bar
// ^
if !specifiers.is_empty() && is!(self, ',') && peeked_is!(self, '*') {
assert_and_bump!(self, ',');
has_ns = true;
}
// export * as bar
// ^
else if specifiers.is_empty() && is!(self, '*') {
has_ns = true;
}
if has_ns {
assert_and_bump!(self, '*');
expect!(self, "as");
let name = self.parse_module_export_name()?;
specifiers.push(ExportSpecifier::Namespace(ExportNamespaceSpecifier {
span: span!(self, ns_export_specifier_start),
name,
}));
}
if has_default || has_ns {
if is!(self, "from") {
let (src, asserts) = self.parse_from_clause_and_semi()?;
return Ok(ModuleDecl::ExportNamed(NamedExport {
span: span!(self, start),
specifiers,
src: Some(src),
type_only,
asserts,
}));
} else if !self.input.syntax().export_default_from() {
// emit error
expect!(self, "from");
}
expect!(self, ',');
}
expect!(self, '{');
let mut string_export_binding_span = None;
while !eof!(self) && !is!(self, '}') {
let specifier = self.parse_named_export_specifier(type_only)?;

View File

@ -45,7 +45,7 @@ export { x, y as w } from "mod";
);
test!(
syntax_namespace(),
syntax_default(),
|_| tr(),
namespace_compound_es6,
r"export * as foo, { bar } from 'bar';",