swc_ecmascript_parser:
 - fix parsing of `export default from 'src';`

swc_ecmascript:
 - add a pass which allows injecting helpers before the module pass
This commit is contained in:
강동윤 2019-02-16 14:18:22 +09:00 committed by GitHub
parent 2a094e42db
commit 1a1889f437
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 177 additions and 11 deletions

View File

@ -1062,6 +1062,42 @@ export default App"#;
);
}
#[test]
fn export_default_3() {
let src = "export default from 'bar';";
test_parser(
src,
Syntax::Es(EsConfig {
export_default_from: true,
..Default::default()
}),
|p| {
p.parse_module().map_err(|mut e| {
e.emit();
()
})
},
);
}
#[test]
fn export_default_4() {
let src = "export default, {foo} from 'bar';";
test_parser(
src,
Syntax::Es(EsConfig {
export_default_from: true,
..Default::default()
}),
|p| {
p.parse_module().map_err(|mut e| {
e.emit();
()
})
},
);
}
#[test]
fn shebang_01() {
let src = "#!/usr/bin/env node";

View File

@ -200,6 +200,9 @@ impl<'a, I: Input> Parser<'a, I> {
}));
}
// Some("default") if default is exported from 'src'
let mut export_default = None;
if eat!("default") {
if self.input.syntax().typescript() {
if is!("abstract") && peeked_is!("class") {
@ -221,22 +224,27 @@ impl<'a, I: Input> Parser<'a, I> {
}
}
let decl = if is!("class") {
self.parse_default_class(decorators)?
if is!("class") {
let decl = self.parse_default_class(decorators)?;
return Ok(ModuleDecl::ExportDefaultDecl(decl));
} else if is!("async")
&& peeked_is!("function")
&& !self.input.has_linebreak_between_cur_and_peeked()
{
self.parse_default_async_fn(decorators)?
let decl = self.parse_default_async_fn(decorators)?;
return Ok(ModuleDecl::ExportDefaultDecl(decl));
} else if is!("function") {
self.parse_default_fn(decorators)?
let decl = self.parse_default_fn(decorators)?;
return Ok(ModuleDecl::ExportDefaultDecl(decl));
} else if self.input.syntax().export_default_from()
&& (is!("from") || (is!(',') && peeked_is!('{')))
{
export_default = Some(Ident::new("default".into(), self.input.prev_span()))
} else {
let expr = self.include_in_expr(true).parse_assignment_expr()?;
expect!(';');
return Ok(ModuleDecl::ExportDefaultExpr(expr));
};
return Ok(ModuleDecl::ExportDefaultDecl(decl));
}
}
let decl = if is!("class") {
@ -271,10 +279,15 @@ impl<'a, I: Input> Parser<'a, I> {
// export {};
// export {} from '';
let default = if self.input.syntax().export_default_from() && is!(IdentRef) {
Some(self.parse_ident(false, false)?)
} else {
None
let default = match export_default {
Some(default) => Some(default),
None => {
if self.input.syntax().export_default_from() && is!(IdentName) {
Some(self.parse_ident(false, false)?)
} else {
None
}
}
};
if is!("from") {

View File

@ -0,0 +1,116 @@
use super::util::Scope;
use crate::util::State;
use ast::*;
use swc_common::{Fold, Visit, VisitWith};
/// Inject required helpers methods **for** module transform passes.
#[derive(Default, Clone)]
pub struct ImportAnalyzer {
scope: State<Scope>,
}
impl Fold<Module> for ImportAnalyzer {
fn fold(&mut self, module: Module) -> Module {
module.visit_with(self);
for (_, ty) in self.scope.value.import_types.drain() {
match ty {
true => {
helper!(interop_require_wildcard);
}
false => {
helper!(interop_require_default);
}
}
}
module
}
}
impl Visit<ExportAll> for ImportAnalyzer {
fn visit(&mut self, export: &ExportAll) {
self.scope
.value
.import_types
.entry(export.src.value.clone())
.and_modify(|v| *v = true);
}
}
impl Visit<NamedExport> for ImportAnalyzer {
fn visit(&mut self, export: &NamedExport) {
for &NamedExportSpecifier {
ref orig,
ref exported,
..
} in export.specifiers.iter().map(|e| match *e {
ExportSpecifier::Named(ref e) => e,
_ => unreachable!("export default from 'foo'; should be removed by previous pass"),
}) {
let is_import_default = orig.sym == js_word!("default");
if let Some(ref src) = export.src {
if is_import_default {
self.scope
.import_types
.entry(src.value.clone())
.or_insert(false);
}
}
}
}
}
impl Visit<ImportDecl> for ImportAnalyzer {
fn visit(&mut self, import: &ImportDecl) {
if import.specifiers.is_empty() {
// import 'foo';
// -> require('foo');
} else if import.specifiers.len() == 1
&& match import.specifiers[0] {
ImportSpecifier::Namespace(..) => true,
_ => false,
}
{
} else {
for s in &import.specifiers {
match *s {
ImportSpecifier::Namespace(..) => unreachable!(
"import * as foo cannot be used with other type of import specifiers"
),
ImportSpecifier::Default(ref i) => {
self.scope
.import_types
.entry(import.src.value.clone())
.or_insert(false);
}
ImportSpecifier::Specific(ref i) => {
let ImportSpecific {
ref local,
ref imported,
..
} = *i;
let name = imported
.as_ref()
.map(|i| i.sym.clone())
.unwrap_or_else(|| local.sym.clone());
let is_default = name == js_word!("default");
if is_default {
self.scope
.import_types
.entry(import.src.value.clone())
.or_insert(false);
} else {
self.scope
.import_types
.entry(import.src.value.clone())
.and_modify(|v| *v = true);
}
}
}
}
}
}
}

View File

@ -1,4 +1,5 @@
pub mod amd;
pub mod common_js;
pub mod import_analysis;
pub mod umd;
mod util;