-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
lib.rs
163 lines (155 loc) 路 5.71 KB
/
lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
use crate::utils::create_return_with_default;
use swc_core::{
common::{util::take::Take, Spanned, DUMMY_SP},
ecma::{ast::*, transforms::testing::test_inline, visit::*},
plugin::{plugin_transform, proxies::TransformPluginProgramMetadata},
};
mod utils;
pub struct ReactDemoVisitor;
/**
* implement `VisitMut` trait for transform `export` to `return`, and transform `import` to `await import`
*/
impl VisitMut for ReactDemoVisitor {
fn visit_mut_module_item(&mut self, n: &mut ModuleItem) {
if let ModuleItem::ModuleDecl(module_decl) = n.clone() {
match module_decl {
ModuleDecl::Import(import_decl) => {
// transform import declaration to await import declaration
if !import_decl.type_only {
// skip non-type import
let name: Pat = if let Some(import) =
import_decl.specifiers.iter().find(|s| s.is_namespace())
{
// extract local name from `import * as x from'y'`
Pat::Ident(import.clone().namespace().unwrap().local.into())
} else {
// extract local name from `import x from 'y'` and `import { x } from 'y'`
// and transform to { default: x } or { x: x }
let props: Vec<ObjectPatProp> = import_decl
.specifiers
.iter()
.map::<ObjectPatProp, _>(|s| {
match s {
ImportSpecifier::Default(import_default) => {
// transform default import to { default: x }
ObjectPatProp::KeyValue(KeyValuePatProp {
key: PropName::Ident(Ident::new("default".into(), import_default.span)),
value: import_default.clone().local.into(),
})
}
ImportSpecifier::Named(import_named) => {
// transform non-default import, e.g. { y: x } or { 'y*y': x } or { x: x }
let key: PropName = match &import_named.imported {
Some(ModuleExportName::Ident(ident)) => PropName::Ident(ident.clone()),
Some(ModuleExportName::Str(str)) => PropName::Str(str.clone()),
None => PropName::Ident(import_named.local.clone()),
};
ObjectPatProp::KeyValue(KeyValuePatProp {
key,
value: import_named.clone().local.into(),
})
}
ImportSpecifier::Namespace(_) => unreachable!("already handle in prev if arm"),
}
})
.collect();
Pat::Object(ObjectPat {
span: import_decl.span,
props,
optional: false,
type_ann: None,
})
};
// replace import declaration to variable declaration with await import
*n = ModuleItem::Stmt(Stmt::Decl(Decl::Var(Box::new(VarDecl {
kind: VarDeclKind::Const,
declare: false,
span: n.span(),
decls: vec![VarDeclarator {
// variable name
name,
// await import expression
init: Some(Box::new(Expr::Await(AwaitExpr {
span: DUMMY_SP,
arg: CallExpr {
span: DUMMY_SP,
callee: Callee::Import(Import::dummy()),
args: vec![ExprOrSpread {
spread: None,
expr: Box::new(Expr::Lit(Lit::Str(*import_decl.src))),
}],
type_args: None,
}
.into(),
}))),
span: DUMMY_SP,
definite: false,
}],
}))));
}
}
ModuleDecl::ExportDefaultExpr(export_expr) => {
// transform export default expression to return statement
*n = ModuleItem::Stmt(create_return_with_default(*export_expr.expr, n.span()));
}
ModuleDecl::ExportDefaultDecl(export_decl) => {
// transform export default declaration to return statement
match export_decl.decl {
DefaultDecl::Class(c) => {
*n = ModuleItem::Stmt(create_return_with_default(Expr::Class(c), n.span()));
}
DefaultDecl::Fn(f) => {
*n = ModuleItem::Stmt(create_return_with_default(Expr::Fn(f), n.span()));
}
DefaultDecl::TsInterfaceDecl(_) => { /* omit interface declaration */ }
}
}
ModuleDecl::ExportNamed(_) => {
unreachable!("export named should be transform to export default")
}
_ => { /* omit other declarations */ }
}
}
}
}
#[plugin_transform]
pub fn process_transform(program: Program, _metadata: TransformPluginProgramMetadata) -> Program {
program.fold_with(&mut as_folder(ReactDemoVisitor))
}
test_inline!(
Default::default(),
|_| as_folder(ReactDemoVisitor),
imports,
// input
r#"import a from 'a';
import { b } from 'b';
import { c1 as c } from 'c';
import * as d from 'd';
import e, { e1, e2 as e3 } from 'e';"#,
// output
r#"const { default: a } = await import('a');
const { b: b } = await import('b');
const { c1: c } = await import('c');
const d = await import('d');
const { default: e , e1: e1 , e2: e3 } = await import('e');"#
);
test_inline!(
Default::default(),
|_| as_folder(ReactDemoVisitor),
exports,
// input
r#"export default a;
export default () => null;
export default class A {}"#,
// output
r#"return {
default: a
};
return {
default: ()=>null
};
return {
default: class A {
}
};"#
);