0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-03-03 09:31:22 -05:00

feat(repl): support exports in the REPL (#11592)

This commit is contained in:
David Sherret 2021-08-06 09:37:24 -04:00 committed by GitHub
parent 15b0e61de5
commit e9ddc7a41a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 195 additions and 24 deletions

View file

@ -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

View file

@ -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,

View file

@ -422,6 +422,19 @@ fn import_declarations() {
assert!(out.contains("hello!\n"));
}
#[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(