Skip to content

Commit

Permalink
Support export * as Foo from 'foo';
Browse files Browse the repository at this point in the history
  • Loading branch information
kdy1 committed Feb 18, 2019
1 parent 9497694 commit 5d4ca29
Show file tree
Hide file tree
Showing 13 changed files with 202 additions and 242 deletions.
6 changes: 3 additions & 3 deletions ecmascript/ast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ pub use self::{
lit::{Bool, Lit, Null, Number, Regex, RegexFlags, Str},
module::{Module, ModuleItem, Script},
module_decl::{
ExportAll, ExportAllAs, ExportDefaultDecl, ExportSpecifier, ImportDecl, ImportDefault,
ImportSpecific, ImportSpecifier, ImportStarAs, ModuleDecl, NamedExport,
NamedExportSpecifier,
ExportAll, ExportDefaultDecl, ExportSpecifier, ImportDecl, ImportDefault, ImportSpecific,
ImportSpecifier, ImportStarAs, ModuleDecl, NamedExport, NamedExportSpecifier,
NamespaceExportSpecifier,
},
operators::{AssignOp, BinaryOp, UnaryOp, UpdateOp},
pat::{
Expand Down
17 changes: 8 additions & 9 deletions ecmascript/ast/src/module_decl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ pub enum ModuleDecl {

ExportDefaultExpr(Box<Expr>),
ExportAll(ExportAll),
ExportAllAs(ExportAllAs),
TsImportEquals(TsImportEqualsDecl),
TsExportAssignment(TsExportAssignment),
TsNamespaceExport(TsNamespaceExportDecl),
Expand All @@ -38,14 +37,6 @@ pub struct ExportAll {
pub src: Str,
}

/// `export * as Foo from 'mod'`
#[ast_node]
pub struct ExportAllAs {
pub span: Span,
pub name: Ident,
pub src: Str,
}

/// `export { foo } from 'mod'`
/// `export { foo as bar } from 'mod'`
#[ast_node]
Expand Down Expand Up @@ -96,10 +87,18 @@ pub struct ImportSpecific {

#[ast_node]
pub enum ExportSpecifier {
Namespace(NamespaceExportSpecifier),
Default(Ident),
Named(NamedExportSpecifier),
}

/// `export * as foo from 'src';`
#[ast_node]
pub struct NamespaceExportSpecifier {
pub span: Span,
pub name: Ident,
}

#[ast_node]
pub struct NamedExportSpecifier {
pub span: Span,
Expand Down
24 changes: 4 additions & 20 deletions ecmascript/codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,6 @@ impl<'a> Emitter<'a> {
semi!();
}
ModuleDecl::ExportAll(ref d) => emit!(d),
ModuleDecl::ExportAllAs(ref d) => emit!(d),
ModuleDecl::TsExportAssignment(ref n) => emit!(n),
ModuleDecl::TsImportEquals(ref n) => emit!(n),
ModuleDecl::TsNamespaceExport(ref n) => emit!(n),
Expand Down Expand Up @@ -243,7 +242,10 @@ impl<'a> Emitter<'a> {
pub fn emit_export_specifier(&mut self, node: &ExportSpecifier) -> Result {
match node {
ExportSpecifier::Default(ref node) => {
unimplemented!("codegen of `export de fault from 'foo';`")
unimplemented!("codegen of `export default from 'foo';`")
}
ExportSpecifier::Namespace(ref node) => {
unimplemented!("codegen of `export foo as Foo from 'foo';`")
}
ExportSpecifier::Named(ref node) => emit!(node),
}
Expand Down Expand Up @@ -300,24 +302,6 @@ impl<'a> Emitter<'a> {
semi!();
}

#[emitter]
pub fn emit_export_all_as(&mut self, node: &ExportAllAs) -> Result {
self.emit_leading_comments_of_pos(node.span().lo())?;

keyword!("export");
space!();
punct!("*");
formatting_space!();
keyword!("as");
space!();
emit!(node.name);
space!();
keyword!("from");
space!();
emit!(node.src);
semi!();
}

#[emitter]
pub fn emit_lit(&mut self, node: &Lit) -> Result {
self.emit_leading_comments_of_pos(node.span().lo())?;
Expand Down
36 changes: 25 additions & 11 deletions ecmascript/parser/src/parser/stmt/module_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,16 @@ impl<'a, I: Input> Parser<'a, I> {
}
}

let mut export_star = None;

if eat!('*') {
if is!("from") {
let src = self.parse_from_clause_and_semi()?;
return Ok(ModuleDecl::ExportAll(ExportAll {
span: span!(start),
src,
}));
}
if is!("as") {
if !self.input.syntax().export_namespace_from() {
syntax_error!(span!(start), SyntaxError::ExportNamespaceFrom)
Expand All @@ -201,19 +210,11 @@ impl<'a, I: Input> Parser<'a, I> {
let _ = cur!(false);

let name = self.parse_ident_name()?;
let src = self.parse_from_clause_and_semi()?;
return Ok(ModuleDecl::ExportAllAs(ExportAllAs {
export_star = Some(ExportSpecifier::Namespace(NamespaceExportSpecifier {
span: span!(start),
name,
src,
}));
}

let src = self.parse_from_clause_and_semi()?;
return Ok(ModuleDecl::ExportAll(ExportAll {
span: span!(start),
src,
}));
}

// Some("default") if default is exported from 'src'
Expand Down Expand Up @@ -307,6 +308,15 @@ impl<'a, I: Input> Parser<'a, I> {
};

if is!("from") {
if let Some(s) = export_star {
let src = self.parse_from_clause_and_semi().map(Some)?;
return Ok(ModuleDecl::ExportNamed(NamedExport {
span: span!(start),
specifiers: vec![s],
src,
}));
}

if let Some(default) = default {
let src = self.parse_from_clause_and_semi().map(Some)?;
return Ok(ModuleDecl::ExportNamed(NamedExport {
Expand All @@ -317,13 +327,17 @@ impl<'a, I: Input> Parser<'a, I> {
}
}

let has_ns = export_star.is_some();
let has_default = default.is_some();
if has_default {
if has_ns || has_default {
expect!(',')
}

expect!('{');
let mut specifiers = vec![];
if let Some(s) = export_star {
specifiers.push(s)
}
if let Some(default) = default {
specifiers.push(ExportSpecifier::Default(default))
}
Expand All @@ -349,7 +363,7 @@ impl<'a, I: Input> Parser<'a, I> {
let src = if is!("from") {
Some(self.parse_from_clause_and_semi()?)
} else {
if has_default {
if has_default || has_ns {
syntax_error!(span!(start), SyntaxError::ExportDefaultWithOutFrom);
}
None
Expand Down
9 changes: 5 additions & 4 deletions ecmascript/transforms/src/modules/amd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,6 @@ impl Fold<Module> for Amd {
};

match decl {
ModuleDecl::ExportAllAs(..) => {
unreachable!("export * as Foo from 'foo'; should be removed by previous pass")
}
ModuleDecl::Import(import) => self.scope.insert_import(import),

ModuleDecl::ExportAll(..)
Expand Down Expand Up @@ -252,10 +249,14 @@ impl Fold<Module> for Amd {
for NamedExportSpecifier { orig, exported, .. } in
export.specifiers.into_iter().map(|e| match e {
ExportSpecifier::Named(e) => e,
_ => unreachable!(
ExportSpecifier::Default(..) => unreachable!(
"export default from 'foo'; should be removed by previous \
pass"
),
ExportSpecifier::Namespace(..) => unreachable!(
"export * as Foo from 'foo'; should be removed by \
previous pass"
),
})
{
let is_import_default = orig.sym == js_word!("default");
Expand Down
9 changes: 5 additions & 4 deletions ecmascript/transforms/src/modules/umd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,6 @@ impl Fold<Module> for Umd {
};

match decl {
ModuleDecl::ExportAllAs(..) => {
unreachable!("export * as Foo from 'foo'; should be removed by previous pass")
}
ModuleDecl::Import(import) => self.scope.insert_import(import),

ModuleDecl::ExportAll(..)
Expand Down Expand Up @@ -253,10 +250,14 @@ impl Fold<Module> for Umd {
for NamedExportSpecifier { orig, exported, .. } in
export.specifiers.into_iter().map(|e| match e {
ExportSpecifier::Named(e) => e,
_ => unreachable!(
ExportSpecifier::Default(..) => unreachable!(
"export default from 'foo'; should be removed by previous \
pass"
),
ExportSpecifier::Namespace(..) => unreachable!(
"export * as Foo from 'foo'; should be removed by \
previous pass"
),
})
{
let is_import_default = orig.sym == js_word!("default");
Expand Down
110 changes: 110 additions & 0 deletions ecmascript/transforms/src/proposals/export/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use crate::{pass::Pass, util::IdentExt};
use ast::*;
use swc_common::{Fold, DUMMY_SP};

#[cfg(test)]
mod tests;

/// `@babel/plugin-proposal-export-default-from` and
/// `@babel/plugin-proposal-export-namespace-from`
pub fn export() -> impl Pass + Clone {
ExportDefaultFrom
}

#[derive(Clone)]
struct ExportDefaultFrom;

impl Fold<Vec<ModuleItem>> for ExportDefaultFrom {
fn fold(&mut self, items: Vec<ModuleItem>) -> Vec<ModuleItem> {
// Imports
let mut stmts = Vec::with_capacity(items.len() + 4);
// Statements except import
let mut extra_stmts = Vec::with_capacity(items.len() + 4);

for item in items {
match item {
ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(mut export)) => {
// Skip if it does not have neither default export and namespace export
if export.specifiers.iter().all(|s| match *s {
ExportSpecifier::Named(..) => true,
_ => false,
}) {
extra_stmts.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(export)));
continue;
}

match export.specifiers.remove(0) {
ExportSpecifier::Default(default) => {
let local = default.prefix("_").private();

stmts.push(ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl {
span: DUMMY_SP,
specifiers: vec![ImportSpecifier::Default(ImportDefault {
span: DUMMY_SP,
local: local.clone(),
})],
src: export
.src
.clone()
.expect("`export default from` requires source"),
})));
extra_stmts.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(
NamedExport {
span: DUMMY_SP,
specifiers: vec![ExportSpecifier::Named(
NamedExportSpecifier {
span: DUMMY_SP,
orig: local,
exported: Some(default),
},
)],
src: None,
},
)));
}
ExportSpecifier::Namespace(ns) => {
let local = ns.name.prefix("_").private();

stmts.push(ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl {
span: DUMMY_SP,
specifiers: vec![ImportSpecifier::Namespace(ImportStarAs {
span: DUMMY_SP,
local: local.clone(),
})],
src: export
.src
.clone()
.expect("`export default from` requires source"),
})));
extra_stmts.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(
NamedExport {
span: DUMMY_SP,
specifiers: vec![ExportSpecifier::Named(
NamedExportSpecifier {
span: DUMMY_SP,
orig: local,
exported: Some(ns.name),
},
)],
src: None,
},
)));
}
_ => unreachable!(),
};
if !export.specifiers.is_empty() {
extra_stmts.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(export)));
}
}
ModuleItem::ModuleDecl(ModuleDecl::Import(..)) => stmts.push(item),
_ => extra_stmts.push(item),
}
}

stmts.append(&mut extra_stmts);

stmts.shrink_to_fit();

stmts
}
}
Loading

0 comments on commit 5d4ca29

Please sign in to comment.