From 1a1889f4373471bef21c92846412533f53cdee3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Sat, 16 Feb 2019 14:18:22 +0900 Subject: [PATCH] Fix bugs (#221) swc_ecmascript_parser: - fix parsing of `export default from 'src';` swc_ecmascript: - add a pass which allows injecting helpers before the module pass --- ecmascript/parser/src/parser/stmt/mod.rs | 36 ++++++ .../parser/src/parser/stmt/module_item.rs | 35 ++++-- .../transforms/src/modules/import_analysis.rs | 116 ++++++++++++++++++ ecmascript/transforms/src/modules/mod.rs | 1 + 4 files changed, 177 insertions(+), 11 deletions(-) create mode 100644 ecmascript/transforms/src/modules/import_analysis.rs diff --git a/ecmascript/parser/src/parser/stmt/mod.rs b/ecmascript/parser/src/parser/stmt/mod.rs index 4d26f22f470..8a93b781ebb 100644 --- a/ecmascript/parser/src/parser/stmt/mod.rs +++ b/ecmascript/parser/src/parser/stmt/mod.rs @@ -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"; diff --git a/ecmascript/parser/src/parser/stmt/module_item.rs b/ecmascript/parser/src/parser/stmt/module_item.rs index 0b00125c498..38149a085c6 100644 --- a/ecmascript/parser/src/parser/stmt/module_item.rs +++ b/ecmascript/parser/src/parser/stmt/module_item.rs @@ -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") { diff --git a/ecmascript/transforms/src/modules/import_analysis.rs b/ecmascript/transforms/src/modules/import_analysis.rs new file mode 100644 index 00000000000..d281ae7da13 --- /dev/null +++ b/ecmascript/transforms/src/modules/import_analysis.rs @@ -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, +} + +impl Fold 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 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 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 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); + } + } + } + } + } + } +} diff --git a/ecmascript/transforms/src/modules/mod.rs b/ecmascript/transforms/src/modules/mod.rs index 50eada84dc4..759ce2c9033 100644 --- a/ecmascript/transforms/src/modules/mod.rs +++ b/ecmascript/transforms/src/modules/mod.rs @@ -1,4 +1,5 @@ pub mod amd; pub mod common_js; +pub mod import_analysis; pub mod umd; mod util;