summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2021-08-06 09:37:24 -0400
committerGitHub <noreply@github.com>2021-08-06 09:37:24 -0400
commite9ddc7a41ad7ea02172d88e95c82d007d8d5df63 (patch)
tree206a35cfec47b85aa7ead532c3e1143900cecb06
parent15b0e61de523c1a0157e754379f0ec7decf0c23e (diff)
feat(repl): support exports in the REPL (#11592)
-rw-r--r--cli/ast/mod.rs1
-rw-r--r--cli/ast/transforms.rs205
-rw-r--r--cli/tests/integration/repl_tests.rs13
3 files changed, 195 insertions, 24 deletions
diff --git a/cli/ast/mod.rs b/cli/ast/mod.rs
index 7b49bbedb..428fe126d 100644
--- a/cli/ast/mod.rs
+++ b/cli/ast/mod.rs
@@ -314,6 +314,7 @@ impl ParsedModule {
let mut passes = chain!(
Optional::new(jsx_pass, options.transform_jsx),
Optional::new(transforms::DownlevelImportsFolder, options.repl_imports),
+ Optional::new(transforms::StripExportsFolder, options.repl_imports),
proposals::decorators::decorators(proposals::decorators::Config {
legacy: true,
emit_metadata: options.emit_metadata
diff --git a/cli/ast/transforms.rs b/cli/ast/transforms.rs
index 570a0bf5b..461a12ff4 100644
--- a/cli/ast/transforms.rs
+++ b/cli/ast/transforms.rs
@@ -21,33 +21,12 @@ impl Fold for DownlevelImportsFolder {
ModuleItem::ModuleDecl(ModuleDecl::Import(import_decl)) => {
// Handle type only imports
if import_decl.type_only {
- return ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP }));
+ // should have no side effects
+ return create_empty_stmt();
}
// The initializer (ex. `await import('./mod.ts')`)
- let initializer = Box::new(Expr::Await(AwaitExpr {
- span: DUMMY_SP,
- arg: Box::new(Expr::Call(CallExpr {
- span: DUMMY_SP,
- callee: ExprOrSuper::Expr(Box::new(Expr::Ident(Ident {
- span: DUMMY_SP,
- sym: "import".into(),
- optional: false,
- }))),
- args: vec![ExprOrSpread {
- spread: None,
- expr: Box::new(Expr::Lit(Lit::Str(Str {
- span: DUMMY_SP,
- has_escape: false,
- kind: StrKind::Normal {
- contains_quote: false,
- },
- value: import_decl.src.value.clone(),
- }))),
- }],
- type_args: None,
- })),
- }));
+ let initializer = create_await_import_expr(&import_decl.src.value);
// Handle imports for the side effects
// ex. `import "module.ts"` -> `await import("module.ts");`
@@ -128,6 +107,78 @@ impl Fold for DownlevelImportsFolder {
}
}
+/// Strips export declarations and exports on named exports for the REPL.
+pub struct StripExportsFolder;
+
+impl Fold for StripExportsFolder {
+ noop_fold_type!(); // skip typescript specific nodes
+
+ fn fold_module_item(
+ &mut self,
+ module_item: swc_ast::ModuleItem,
+ ) -> swc_ast::ModuleItem {
+ use swc_ecmascript::ast::*;
+
+ match module_item {
+ ModuleItem::ModuleDecl(ModuleDecl::ExportAll(export_all)) => {
+ ModuleItem::Stmt(Stmt::Expr(ExprStmt {
+ span: DUMMY_SP,
+ expr: create_await_import_expr(&export_all.src.value),
+ }))
+ }
+ ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(export_named)) => {
+ if let Some(src) = export_named.src {
+ ModuleItem::Stmt(Stmt::Expr(ExprStmt {
+ span: DUMMY_SP,
+ expr: create_await_import_expr(&src.value),
+ }))
+ } else {
+ create_empty_stmt()
+ }
+ }
+ ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultExpr(default_expr)) => {
+ // transform a default export expression to its expression
+ ModuleItem::Stmt(Stmt::Expr(ExprStmt {
+ span: DUMMY_SP,
+ expr: default_expr.expr,
+ }))
+ }
+ ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(export_decl)) => {
+ // strip the export keyword on an exported declaration
+ ModuleItem::Stmt(Stmt::Decl(export_decl.decl))
+ }
+ ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(default_decl)) => {
+ // only keep named default exports
+ match default_decl.decl {
+ DefaultDecl::Fn(FnExpr {
+ ident: Some(ident),
+ function,
+ }) => ModuleItem::Stmt(Stmt::Decl(Decl::Fn(FnDecl {
+ declare: false,
+ ident,
+ function,
+ }))),
+ DefaultDecl::Class(ClassExpr {
+ ident: Some(ident),
+ class,
+ }) => ModuleItem::Stmt(Stmt::Decl(Decl::Class(ClassDecl {
+ declare: false,
+ ident,
+ class,
+ }))),
+ _ => create_empty_stmt(),
+ }
+ }
+ _ => module_item,
+ }
+ }
+}
+
+fn create_empty_stmt() -> swc_ast::ModuleItem {
+ use swc_ast::*;
+ ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP }))
+}
+
fn create_binding_ident(name: String) -> swc_ast::BindingIdent {
swc_ast::BindingIdent {
id: create_ident(name),
@@ -161,6 +212,33 @@ fn create_key_value(key: String, value: String) -> swc_ast::ObjectPatProp {
})
}
+fn create_await_import_expr(module_specifier: &str) -> Box<swc_ast::Expr> {
+ use swc_ast::*;
+ Box::new(Expr::Await(AwaitExpr {
+ span: DUMMY_SP,
+ arg: Box::new(Expr::Call(CallExpr {
+ span: DUMMY_SP,
+ callee: ExprOrSuper::Expr(Box::new(Expr::Ident(Ident {
+ span: DUMMY_SP,
+ sym: "import".into(),
+ optional: false,
+ }))),
+ args: vec![ExprOrSpread {
+ spread: None,
+ expr: Box::new(Expr::Lit(Lit::Str(Str {
+ span: DUMMY_SP,
+ has_escape: false,
+ kind: StrKind::Normal {
+ contains_quote: false,
+ },
+ value: module_specifier.into(),
+ }))),
+ }],
+ type_args: None,
+ })),
+ }))
+}
+
fn create_assignment(key: String) -> swc_ast::ObjectPatProp {
swc_ast::ObjectPatProp::Assign(swc_ast::AssignPatProp {
span: DUMMY_SP,
@@ -264,6 +342,85 @@ mod test {
);
}
+ #[test]
+ fn test_strip_exports_export_all() {
+ test_transform(
+ StripExportsFolder,
+ r#"export * from "./test.ts";"#,
+ r#"await import("./test.ts");"#,
+ );
+ }
+
+ #[test]
+ fn test_strip_exports_export_named() {
+ test_transform(
+ StripExportsFolder,
+ r#"export { test } from "./test.ts";"#,
+ r#"await import("./test.ts");"#,
+ );
+
+ test_transform(StripExportsFolder, r#"export { test };"#, ";");
+ }
+
+ #[test]
+ fn test_strip_exports_export_default_expr() {
+ test_transform(StripExportsFolder, "export default 5;", "5;");
+ }
+
+ #[test]
+ fn test_strip_exports_export_default_decl_name() {
+ test_transform(
+ StripExportsFolder,
+ "export default class Test {}",
+ "class Test {\n}",
+ );
+
+ test_transform(
+ StripExportsFolder,
+ "export default function test() {}",
+ "function test() {\n}",
+ );
+ }
+
+ #[test]
+ fn test_strip_exports_export_default_decl_no_name() {
+ test_transform(StripExportsFolder, "export default class {}", ";");
+
+ test_transform(StripExportsFolder, "export default function() {}", ";");
+ }
+
+ #[test]
+ fn test_strip_exports_export_named_decls() {
+ test_transform(
+ StripExportsFolder,
+ "export class Test {}",
+ "class Test {\n}",
+ );
+
+ test_transform(
+ StripExportsFolder,
+ "export function test() {}",
+ "function test() {\n}",
+ );
+
+ test_transform(StripExportsFolder, "export enum Test {}", "enum Test {\n}");
+
+ test_transform(
+ StripExportsFolder,
+ "export namespace Test {}",
+ "module Test {\n}",
+ );
+ }
+
+ #[test]
+ fn test_strip_exports_not_in_namespace() {
+ test_transform(
+ StripExportsFolder,
+ "namespace Test { export class Test {} }",
+ "module Test {\n export class Test {\n }\n}",
+ );
+ }
+
fn test_transform(
mut transform: impl Fold,
src: &str,
diff --git a/cli/tests/integration/repl_tests.rs b/cli/tests/integration/repl_tests.rs
index 955c27069..b96b398ae 100644
--- a/cli/tests/integration/repl_tests.rs
+++ b/cli/tests/integration/repl_tests.rs
@@ -423,6 +423,19 @@ fn import_declarations() {
}
#[test]
+fn exports_stripped() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["export default 5;", "export class Test {}"]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert!(out.contains("5\n"));
+ assert!(err.is_empty());
+}
+
+#[test]
fn eval_unterminated() {
let (out, err) = util::run_and_collect_output(
true,