summaryrefslogtreecommitdiff
path: root/cli/ast/transforms.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/ast/transforms.rs')
-rw-r--r--cli/ast/transforms.rs524
1 files changed, 0 insertions, 524 deletions
diff --git a/cli/ast/transforms.rs b/cli/ast/transforms.rs
deleted file mode 100644
index a89edc01d..000000000
--- a/cli/ast/transforms.rs
+++ /dev/null
@@ -1,524 +0,0 @@
-use deno_ast::swc::ast as swc_ast;
-use deno_ast::swc::common::DUMMY_SP;
-use deno_ast::swc::visit::noop_fold_type;
-use deno_ast::swc::visit::Fold;
-
-/// Transforms import declarations to variable declarations
-/// with a dynamic import. This is used to provide import
-/// declaration support in the REPL.
-pub struct DownlevelImportsFolder;
-
-impl Fold for DownlevelImportsFolder {
- noop_fold_type!(); // skip typescript specific nodes
-
- fn fold_module_item(
- &mut self,
- module_item: swc_ast::ModuleItem,
- ) -> swc_ast::ModuleItem {
- use deno_ast::swc::ast::*;
-
- match module_item {
- ModuleItem::ModuleDecl(ModuleDecl::Import(import_decl)) => {
- // Handle type only imports
- if import_decl.type_only {
- // should have no side effects
- return create_empty_stmt();
- }
-
- // The initializer (ex. `await import('./mod.ts')`)
- let initializer =
- create_await_import_expr(&import_decl.src.value, import_decl.asserts);
-
- // Handle imports for the side effects
- // ex. `import "module.ts"` -> `await import("module.ts");`
- if import_decl.specifiers.is_empty() {
- return ModuleItem::Stmt(Stmt::Expr(ExprStmt {
- span: DUMMY_SP,
- expr: initializer,
- }));
- }
-
- // Collect the specifiers and create the variable statement
- let named_import_props = import_decl
- .specifiers
- .iter()
- .filter_map(|specifier| match specifier {
- ImportSpecifier::Default(specifier) => Some(create_key_value(
- "default".to_string(),
- specifier.local.sym.to_string(),
- )),
- ImportSpecifier::Named(specifier) => {
- Some(match specifier.imported.as_ref() {
- Some(name) => create_key_value(
- match name {
- ModuleExportName::Ident(ident) => ident.sym.to_string(),
- ModuleExportName::Str(str) => str.value.to_string(),
- },
- specifier.local.sym.to_string(),
- ),
- None => create_assignment(specifier.local.sym.to_string()),
- })
- }
- ImportSpecifier::Namespace(_) => None,
- })
- .collect::<Vec<_>>();
- let namespace_import_name =
- import_decl
- .specifiers
- .iter()
- .find_map(|specifier| match specifier {
- ImportSpecifier::Namespace(specifier) => {
- Some(create_binding_ident(specifier.local.sym.to_string()))
- }
- _ => None,
- });
-
- ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl {
- span: DUMMY_SP,
- kind: VarDeclKind::Const,
- declare: false,
- decls: {
- let mut decls = Vec::new();
-
- if !named_import_props.is_empty() {
- decls.push(VarDeclarator {
- span: DUMMY_SP,
- name: Pat::Object(ObjectPat {
- span: DUMMY_SP,
- optional: false,
- props: named_import_props,
- type_ann: None,
- }),
- definite: false,
- init: Some(initializer.clone()),
- });
- }
- if let Some(namespace_import) = namespace_import_name {
- decls.push(VarDeclarator {
- span: DUMMY_SP,
- name: Pat::Ident(namespace_import),
- definite: false,
- init: Some(initializer),
- });
- }
-
- decls
- },
- })))
- }
- _ => module_item,
- }
- }
-}
-
-/// 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 deno_ast::swc::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,
- export_all.asserts,
- ),
- }))
- }
- 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, export_named.asserts),
- }))
- } 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),
- type_ann: None,
- }
-}
-
-fn create_ident(name: String) -> swc_ast::Ident {
- swc_ast::Ident {
- span: DUMMY_SP,
- sym: name.into(),
- optional: false,
- }
-}
-
-fn create_key_value(key: String, value: String) -> swc_ast::ObjectPatProp {
- swc_ast::ObjectPatProp::KeyValue(swc_ast::KeyValuePatProp {
- // use a string literal because it will work in more scenarios than an identifier
- key: swc_ast::PropName::Str(swc_ast::Str {
- span: DUMMY_SP,
- value: key.into(),
- has_escape: false,
- kind: swc_ast::StrKind::Synthesized,
- }),
- value: Box::new(swc_ast::Pat::Ident(swc_ast::BindingIdent {
- id: swc_ast::Ident {
- span: DUMMY_SP,
- sym: value.into(),
- optional: false,
- },
- type_ann: None,
- })),
- })
-}
-
-fn create_await_import_expr(
- module_specifier: &str,
- maybe_asserts: Option<swc_ast::ObjectLit>,
-) -> Box<swc_ast::Expr> {
- use swc_ast::*;
- let mut 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(),
- }))),
- }];
-
- // add assert object if it exists
- if let Some(asserts) = maybe_asserts {
- args.push(ExprOrSpread {
- spread: None,
- expr: Box::new(Expr::Object(ObjectLit {
- span: DUMMY_SP,
- props: vec![PropOrSpread::Prop(Box::new(Prop::KeyValue(
- KeyValueProp {
- key: PropName::Ident(create_ident("assert".to_string())),
- value: Box::new(Expr::Object(asserts)),
- },
- )))],
- })),
- })
- }
-
- 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,
- type_args: None,
- })),
- }))
-}
-
-fn create_assignment(key: String) -> swc_ast::ObjectPatProp {
- swc_ast::ObjectPatProp::Assign(swc_ast::AssignPatProp {
- span: DUMMY_SP,
- key: create_ident(key),
- value: None,
- })
-}
-
-#[cfg(test)]
-mod test {
- use deno_ast::swc::ast::Module;
- use deno_ast::swc::codegen::text_writer::JsWriter;
- use deno_ast::swc::codegen::Node;
- use deno_ast::swc::common::FileName;
- use deno_ast::swc::common::SourceMap;
- use deno_ast::swc::parser::Parser;
- use deno_ast::swc::parser::StringInput;
- use deno_ast::swc::parser::Syntax;
- use deno_ast::swc::parser::TsConfig;
- use deno_ast::swc::visit::Fold;
- use deno_ast::swc::visit::FoldWith;
- use deno_ast::ModuleSpecifier;
- use pretty_assertions::assert_eq;
- use std::rc::Rc;
-
- use super::*;
-
- #[test]
- fn test_downlevel_imports_type_only() {
- test_transform(
- DownlevelImportsFolder,
- r#"import type { test } from "./mod.ts";"#,
- ";",
- );
- }
-
- #[test]
- fn test_downlevel_imports_specifier_only() {
- test_transform(
- DownlevelImportsFolder,
- r#"import "./mod.ts";"#,
- r#"await import("./mod.ts");"#,
- );
-
- test_transform(
- DownlevelImportsFolder,
- r#"import {} from "./mod.ts";"#,
- r#"await import("./mod.ts");"#,
- );
- }
-
- #[test]
- fn test_downlevel_imports_default() {
- test_transform(
- DownlevelImportsFolder,
- r#"import mod from "./mod.ts";"#,
- r#"const { "default": mod } = await import("./mod.ts");"#,
- );
- }
-
- #[test]
- fn test_downlevel_imports_named() {
- test_transform(
- DownlevelImportsFolder,
- r#"import { A } from "./mod.ts";"#,
- r#"const { A } = await import("./mod.ts");"#,
- );
-
- test_transform(
- DownlevelImportsFolder,
- r#"import { A, B, C } from "./mod.ts";"#,
- r#"const { A , B , C } = await import("./mod.ts");"#,
- );
-
- test_transform(
- DownlevelImportsFolder,
- r#"import { A as LocalA, B, C as LocalC } from "./mod.ts";"#,
- r#"const { "A": LocalA , B , "C": LocalC } = await import("./mod.ts");"#,
- );
- }
-
- #[test]
- fn test_downlevel_imports_namespace() {
- test_transform(
- DownlevelImportsFolder,
- r#"import * as mod from "./mod.ts";"#,
- r#"const mod = await import("./mod.ts");"#,
- );
- }
-
- #[test]
- fn test_downlevel_imports_mixed() {
- test_transform(
- DownlevelImportsFolder,
- r#"import myDefault, { A, B as LocalB } from "./mod.ts";"#,
- r#"const { "default": myDefault , A , "B": LocalB } = await import("./mod.ts");"#,
- );
-
- test_transform(
- DownlevelImportsFolder,
- r#"import myDefault, * as mod from "./mod.ts";"#,
- r#"const { "default": myDefault } = await import("./mod.ts"), mod = await import("./mod.ts");"#,
- );
- }
-
- #[test]
- fn test_downlevel_imports_assertions() {
- test_transform(
- DownlevelImportsFolder,
- r#"import data from "./mod.json" assert { type: "json" };"#,
- "const { \"default\": data } = await import(\"./mod.json\", {\n assert: {\n type: \"json\"\n }\n});",
- );
- }
-
- #[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_assertions() {
- test_transform(
- StripExportsFolder,
- r#"export { default as data } from "./mod.json" assert { type: "json" };"#,
- "await import(\"./mod.json\", {\n assert: {\n type: \"json\"\n }\n});",
- );
- }
-
- #[test]
- fn test_strip_exports_export_all_assertions() {
- // even though this doesn't really make sense for someone to do
- test_transform(
- StripExportsFolder,
- r#"export * from "./mod.json" assert { type: "json" };"#,
- "await import(\"./mod.json\", {\n assert: {\n type: \"json\"\n }\n});",
- );
- }
-
- #[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() {}",
- );
- }
-
- #[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() {}",
- );
-
- 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,
- expected_output: &str,
- ) {
- let (source_map, module) = parse(src);
- let output = print(source_map, module.fold_with(&mut transform));
- assert_eq!(output, format!("{}\n", expected_output));
- }
-
- fn parse(src: &str) -> (Rc<SourceMap>, Module) {
- let source_map = Rc::new(SourceMap::default());
- let source_file = source_map.new_source_file(
- FileName::Url(ModuleSpecifier::parse("file:///test.ts").unwrap()),
- src.to_string(),
- );
- let input = StringInput::from(&*source_file);
- let syntax = Syntax::Typescript(TsConfig {
- ..Default::default()
- });
- let mut parser = Parser::new(syntax, input, None);
- (source_map, parser.parse_module().unwrap())
- }
-
- fn print(source_map: Rc<SourceMap>, module: Module) -> String {
- let mut buf = vec![];
- {
- let writer =
- Box::new(JsWriter::new(source_map.clone(), "\n", &mut buf, None));
- let config = deno_ast::swc::codegen::Config { minify: false };
- let mut emitter = deno_ast::swc::codegen::Emitter {
- cfg: config,
- comments: None,
- cm: source_map,
- wr: writer,
- };
- module.emit_with(&mut emitter).unwrap();
- }
- String::from_utf8(buf).unwrap()
- }
-}