mirror of
https://github.com/swc-project/swc.git
synced 2024-12-24 22:22:34 +03:00
fix(es/parser): Fix parsing of export specifiers (#5190)
This commit is contained in:
parent
8ee3c8f5fb
commit
ec9378370d
@ -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()
|
||||
}),
|
||||
);
|
||||
|
@ -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(
|
||||
|
@ -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)?;
|
||||
|
@ -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';",
|
||||
|
Loading…
Reference in New Issue
Block a user