@ohos-ports/jest_workaround 0.79.19-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +99 -0
- package/jest_workaround.wasm +0 -0
- package/package.json +42 -0
- package/src/lib.rs +282 -0
- package/src/local_export_strip.rs +340 -0
- package/src/utils.rs +257 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) magic-akari
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# [SWC plugin] workaround for jest
|
|
2
|
+
|
|
3
|
+
[](https://crates.io/crates/jest_workaround)
|
|
4
|
+
[](https://www.npmjs.com/package/jest_workaround)
|
|
5
|
+
|
|
6
|
+
[](https://github.com/magic-akari/jest_workaround/actions/workflows/test.yml)
|
|
7
|
+
[](https://github.com/magic-akari/jest_workaround/actions/workflows/cron.yml)
|
|
8
|
+
|
|
9
|
+
This is a SWC plugin to handle jest compatibility issues.
|
|
10
|
+
|
|
11
|
+
This SWC plugin should be used with `@swc/jest`.
|
|
12
|
+
|
|
13
|
+
## plugin version
|
|
14
|
+
|
|
15
|
+
https://swc.rs/docs/plugin/selecting-swc-core
|
|
16
|
+
|
|
17
|
+
## usage
|
|
18
|
+
|
|
19
|
+
install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm i -D jest @swc/core @swc/jest jest_workaround
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
// jest.config.js
|
|
27
|
+
const fs = require("node:fs");
|
|
28
|
+
|
|
29
|
+
const swcrc = JSON.parse(fs.readFileSync(".swcrc", "utf8"));
|
|
30
|
+
|
|
31
|
+
// If you have other plugins, change this line.
|
|
32
|
+
((swcrc.jsc ??= {}).experimental ??= {}).plugins = [["jest_workaround", {}]];
|
|
33
|
+
|
|
34
|
+
module.exports = {
|
|
35
|
+
transform: {
|
|
36
|
+
"^.+\\.(t|j)sx?$": ["@swc/jest", swcrc],
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Make sure that `module.type` is `commonjs` in your `.swcrc` since this plugin
|
|
42
|
+
does not touch non-workaround parts, such as import statements.
|
|
43
|
+
|
|
44
|
+
## FAQ
|
|
45
|
+
|
|
46
|
+
#### 1. When do I need this?
|
|
47
|
+
|
|
48
|
+
If you're using the swc compiler to transform your code to comply with the ESM
|
|
49
|
+
specification, but you're also using Jest to test it in a CJS environment, you
|
|
50
|
+
may encounter issues due to the immutable issue of `exports`.
|
|
51
|
+
|
|
52
|
+
This plugin can help by transforming the `export` statements into mutable
|
|
53
|
+
`exports`.
|
|
54
|
+
|
|
55
|
+
#### 2. Do I have a better choice?
|
|
56
|
+
|
|
57
|
+
You may have other options depending on your specific needs:
|
|
58
|
+
|
|
59
|
+
- If you're able to run Jest in an ESM environment, you can use swc to transpile
|
|
60
|
+
TypeScript/JSX syntax or downgrade JavaScript syntax without module
|
|
61
|
+
conversion. Simply set the value of `module.type` to `es6` to achieve this.
|
|
62
|
+
|
|
63
|
+
- It's possible that some issues related to running Jest in an ESM environment
|
|
64
|
+
will be resolved over time. Keep an eye on
|
|
65
|
+
[facebook/jest#9430](https://github.com/facebook/jest/issues/9430) for
|
|
66
|
+
updates.
|
|
67
|
+
|
|
68
|
+
- If you don't need the behavior of ESM specifically, you can stick with the CJS
|
|
69
|
+
syntax to get the CJS behavior of `exports`.
|
|
70
|
+
|
|
71
|
+
These options may be worth considering before using this plugin.
|
|
72
|
+
|
|
73
|
+
CJS syntax
|
|
74
|
+
|
|
75
|
+
```JavaScript
|
|
76
|
+
exports.foo = function foo() {
|
|
77
|
+
return 42;
|
|
78
|
+
};
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
CTS(CJS in TypeScript) syntax
|
|
82
|
+
|
|
83
|
+
```TypeScript
|
|
84
|
+
export = {
|
|
85
|
+
foo: function foo() {
|
|
86
|
+
return 42;
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Notes:
|
|
92
|
+
|
|
93
|
+
- ESM style export means immutable exports when transformed into CJS
|
|
94
|
+
- ESM style import means hoisted require when transformed into CJS
|
|
95
|
+
|
|
96
|
+
#### 3. After upgrading the plugin version, the changes have not taken effect.
|
|
97
|
+
|
|
98
|
+
This is a known issue. You could remove the Jest cache by running
|
|
99
|
+
[`jest --clearCache`](https://jestjs.io/docs/cli#--clearcache) as a workaround.
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ohos-ports/jest_workaround",
|
|
3
|
+
"version": "0.79.19-beta.0",
|
|
4
|
+
"description": "[SWC plugin] workaround for jest",
|
|
5
|
+
"author": "magic-akari <akari.ccino@gmail.com>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"swc-plugin",
|
|
9
|
+
"jest"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"test:debug": "NODE_ENV=development pnpm build:debug && NODE_ENV=development jest",
|
|
13
|
+
"build:debug": "cargo build --target wasm32-wasi && cp -rf target/wasm32-wasi/debug/jest_workaround.wasm ./debug",
|
|
14
|
+
"test": "pnpm build && jest",
|
|
15
|
+
"build": "cargo prepublish --release && cp -rf target/wasm32-wasi/release/jest_workaround.wasm ."
|
|
16
|
+
},
|
|
17
|
+
"packageManager": "pnpm@8.6.8",
|
|
18
|
+
"main": "jest_workaround.wasm",
|
|
19
|
+
"files": [
|
|
20
|
+
"src",
|
|
21
|
+
"jest_workaround.wasm"
|
|
22
|
+
],
|
|
23
|
+
"homepage": "https://github.com/magic-akari/jest_workaround#readme",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "https://github.com/ohos-ports/ohos-ports.git",
|
|
27
|
+
"directory": "ports/jest_workaround/0.79.19"
|
|
28
|
+
},
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/ohos-ports/ohos-ports/issues"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@ohos-ports/swc-core": "^1.16.1-beta.1",
|
|
34
|
+
"@swc/jest": "^0.2.26",
|
|
35
|
+
"@types/jest": "^29.5.1",
|
|
36
|
+
"jest": "^29.5.0"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"@ohos-ports/swc-core": "^1.16.1-beta.1",
|
|
40
|
+
"@swc/jest": "^0.2.26"
|
|
41
|
+
}
|
|
42
|
+
}
|
package/src/lib.rs
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
mod local_export_strip;
|
|
2
|
+
mod utils;
|
|
3
|
+
|
|
4
|
+
use local_export_strip::LocalExportStrip;
|
|
5
|
+
use std::collections::HashSet;
|
|
6
|
+
|
|
7
|
+
use swc_core::{
|
|
8
|
+
common::{util::take::Take, Mark, SyntaxContext, DUMMY_SP},
|
|
9
|
+
ecma::{
|
|
10
|
+
ast::*,
|
|
11
|
+
utils::{
|
|
12
|
+
member_expr, private_ident, quote_ident, quote_str, ExprFactory, IntoIndirectCall,
|
|
13
|
+
},
|
|
14
|
+
visit::{noop_visit_mut_type, VisitMut, VisitMutWith},
|
|
15
|
+
},
|
|
16
|
+
plugin::{plugin_transform, proxies::TransformPluginProgramMetadata},
|
|
17
|
+
};
|
|
18
|
+
use utils::emit_export_stmts;
|
|
19
|
+
|
|
20
|
+
#[derive(Debug)]
|
|
21
|
+
pub struct TransformVisitor {
|
|
22
|
+
unresolved_mark: Mark,
|
|
23
|
+
|
|
24
|
+
export_decl_id: HashSet<Id>,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
impl VisitMut for TransformVisitor {
|
|
28
|
+
noop_visit_mut_type!();
|
|
29
|
+
|
|
30
|
+
fn visit_mut_script(&mut self, _: &mut Script) {
|
|
31
|
+
// skip
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
fn visit_mut_module(&mut self, n: &mut Module) {
|
|
35
|
+
let mut strip = LocalExportStrip::default();
|
|
36
|
+
n.visit_mut_with(&mut strip);
|
|
37
|
+
|
|
38
|
+
let LocalExportStrip {
|
|
39
|
+
has_export_assign,
|
|
40
|
+
export,
|
|
41
|
+
export_all,
|
|
42
|
+
export_decl_id,
|
|
43
|
+
..
|
|
44
|
+
} = strip;
|
|
45
|
+
|
|
46
|
+
self.export_decl_id = export_decl_id;
|
|
47
|
+
|
|
48
|
+
let mut stmts: Vec<ModuleItem> = Vec::with_capacity(n.body.len() + 1);
|
|
49
|
+
|
|
50
|
+
if !has_export_assign && !export.is_empty() {
|
|
51
|
+
// keep module env
|
|
52
|
+
stmts.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(
|
|
53
|
+
NamedExport::dummy(),
|
|
54
|
+
)));
|
|
55
|
+
|
|
56
|
+
let exports = self.exports();
|
|
57
|
+
|
|
58
|
+
let export_obj_prop_list = export.into_iter().map(Into::into).collect();
|
|
59
|
+
|
|
60
|
+
stmts.extend(
|
|
61
|
+
emit_export_stmts(exports, export_obj_prop_list)
|
|
62
|
+
.into_iter()
|
|
63
|
+
.map(Into::into),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
if !self.export_decl_id.is_empty() {
|
|
67
|
+
n.visit_mut_children_with(self);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
stmts.extend(export_all.into_iter().map(|id| self.export_all(id)));
|
|
72
|
+
|
|
73
|
+
stmts.extend(n.body.take());
|
|
74
|
+
|
|
75
|
+
n.body = stmts;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
fn visit_mut_prop(&mut self, n: &mut Prop) {
|
|
79
|
+
match n {
|
|
80
|
+
Prop::Shorthand(ref_ident) => {
|
|
81
|
+
if self.export_decl_id.contains(&ref_ident.to_id()) {
|
|
82
|
+
*n = KeyValueProp {
|
|
83
|
+
key: ref_ident.clone().into(),
|
|
84
|
+
value: Box::new(Expr::from(
|
|
85
|
+
self.exports().make_member(ref_ident.take().into()),
|
|
86
|
+
)),
|
|
87
|
+
}
|
|
88
|
+
.into()
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
_ => n.visit_mut_children_with(self),
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
fn visit_mut_expr(&mut self, n: &mut Expr) {
|
|
96
|
+
match n {
|
|
97
|
+
Expr::Ident(ref_ident) => {
|
|
98
|
+
if self.export_decl_id.contains(&ref_ident.to_id()) {
|
|
99
|
+
*n = Expr::from(self.exports().make_member(ref_ident.take().into()));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
_ => n.visit_mut_children_with(self),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
fn visit_mut_tagged_tpl(&mut self, n: &mut TaggedTpl) {
|
|
108
|
+
let is_indirect = n
|
|
109
|
+
.tag
|
|
110
|
+
.as_ident()
|
|
111
|
+
.map(|ident| self.export_decl_id.contains(&ident.to_id()))
|
|
112
|
+
.unwrap_or_default();
|
|
113
|
+
|
|
114
|
+
n.visit_mut_children_with(self);
|
|
115
|
+
|
|
116
|
+
if is_indirect {
|
|
117
|
+
*n = n.take().into_indirect()
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
fn visit_mut_callee(&mut self, n: &mut Callee) {
|
|
122
|
+
match n {
|
|
123
|
+
Callee::Expr(e) if e.is_ident() => {
|
|
124
|
+
let is_indirect_callee = e
|
|
125
|
+
.as_ident()
|
|
126
|
+
.map(|ident| self.export_decl_id.contains(&ident.to_id()))
|
|
127
|
+
.unwrap_or_default();
|
|
128
|
+
|
|
129
|
+
e.visit_mut_with(self);
|
|
130
|
+
|
|
131
|
+
if is_indirect_callee {
|
|
132
|
+
*n = n.take().into_indirect()
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
_ => n.visit_mut_children_with(self),
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
impl TransformVisitor {
|
|
142
|
+
pub fn new(unresolved_mark: Mark) -> Self {
|
|
143
|
+
Self {
|
|
144
|
+
unresolved_mark,
|
|
145
|
+
export_decl_id: Default::default(),
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
fn exports(&self) -> Ident {
|
|
150
|
+
quote_ident!(SyntaxContext::empty().apply_mark(self.unresolved_mark), "exports")
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/// ```JavaScript
|
|
154
|
+
/// Object.keys(_mod).forEach(function (key) {
|
|
155
|
+
/// if (key === "default" || key === "__esModule") return;
|
|
156
|
+
/// if (key in exports && exports[key] === _mod[key]) return;
|
|
157
|
+
/// exports[key] = _mod[key];
|
|
158
|
+
/// });
|
|
159
|
+
/// ```
|
|
160
|
+
fn export_all(&self, id: Id) -> ModuleItem {
|
|
161
|
+
let mod_name = Ident::from(id);
|
|
162
|
+
let key = private_ident!("key");
|
|
163
|
+
|
|
164
|
+
member_expr!(Default::default(), DUMMY_SP, Object.keys)
|
|
165
|
+
.as_call(DUMMY_SP, vec![mod_name.clone().as_arg()])
|
|
166
|
+
.make_member(quote_ident!("forEach"))
|
|
167
|
+
.as_call(
|
|
168
|
+
DUMMY_SP,
|
|
169
|
+
vec![Function {
|
|
170
|
+
params: vec![key.clone().into()],
|
|
171
|
+
decorators: vec![],
|
|
172
|
+
span: DUMMY_SP,
|
|
173
|
+
ctxt: SyntaxContext::empty(),
|
|
174
|
+
this_param: None,
|
|
175
|
+
body: Some(FunctionBody {
|
|
176
|
+
span: DUMMY_SP,
|
|
177
|
+
stmts: vec![
|
|
178
|
+
// if (key === "default" || key === "__esModule") return;
|
|
179
|
+
IfStmt {
|
|
180
|
+
span: DUMMY_SP,
|
|
181
|
+
test: BinExpr {
|
|
182
|
+
span: DUMMY_SP,
|
|
183
|
+
op: op!("||"),
|
|
184
|
+
left: BinExpr {
|
|
185
|
+
span: DUMMY_SP,
|
|
186
|
+
op: op!("==="),
|
|
187
|
+
left: key.clone().into(),
|
|
188
|
+
right: quote_str!("default").into(),
|
|
189
|
+
}
|
|
190
|
+
.into(),
|
|
191
|
+
right: BinExpr {
|
|
192
|
+
span: DUMMY_SP,
|
|
193
|
+
op: op!("==="),
|
|
194
|
+
left: key.clone().into(),
|
|
195
|
+
right: quote_str!("__esModule").into(),
|
|
196
|
+
}
|
|
197
|
+
.into(),
|
|
198
|
+
}
|
|
199
|
+
.into(),
|
|
200
|
+
cons: Box::new(
|
|
201
|
+
ReturnStmt {
|
|
202
|
+
span: DUMMY_SP,
|
|
203
|
+
arg: None,
|
|
204
|
+
}
|
|
205
|
+
.into(),
|
|
206
|
+
),
|
|
207
|
+
alt: None,
|
|
208
|
+
}
|
|
209
|
+
.into(),
|
|
210
|
+
// if (key in exports && exports[key] === _mod[key]) return;
|
|
211
|
+
IfStmt {
|
|
212
|
+
span: DUMMY_SP,
|
|
213
|
+
test: BinExpr {
|
|
214
|
+
span: DUMMY_SP,
|
|
215
|
+
op: op!("&&"),
|
|
216
|
+
left: BinExpr {
|
|
217
|
+
span: DUMMY_SP,
|
|
218
|
+
op: op!("in"),
|
|
219
|
+
left: key.clone().into(),
|
|
220
|
+
right: self.exports().into(),
|
|
221
|
+
}
|
|
222
|
+
.into(),
|
|
223
|
+
right: BinExpr {
|
|
224
|
+
span: DUMMY_SP,
|
|
225
|
+
op: op!("==="),
|
|
226
|
+
left: self
|
|
227
|
+
.exports()
|
|
228
|
+
.computed_member(quote_ident!("key"))
|
|
229
|
+
.into(),
|
|
230
|
+
right: mod_name
|
|
231
|
+
.clone()
|
|
232
|
+
.computed_member(quote_ident!("key"))
|
|
233
|
+
.into(),
|
|
234
|
+
}
|
|
235
|
+
.into(),
|
|
236
|
+
}
|
|
237
|
+
.into(),
|
|
238
|
+
cons: Box::new(
|
|
239
|
+
ReturnStmt {
|
|
240
|
+
span: DUMMY_SP,
|
|
241
|
+
arg: None,
|
|
242
|
+
}
|
|
243
|
+
.into(),
|
|
244
|
+
),
|
|
245
|
+
alt: None,
|
|
246
|
+
}
|
|
247
|
+
.into(),
|
|
248
|
+
// exports[key] = _mod[key];
|
|
249
|
+
mod_name
|
|
250
|
+
.clone()
|
|
251
|
+
.computed_member(quote_ident!("key"))
|
|
252
|
+
.make_assign_to(
|
|
253
|
+
op!("="),
|
|
254
|
+
self.exports()
|
|
255
|
+
.computed_member(quote_ident!("key"))
|
|
256
|
+
.into(),
|
|
257
|
+
)
|
|
258
|
+
.into_stmt(),
|
|
259
|
+
],
|
|
260
|
+
}),
|
|
261
|
+
is_generator: false,
|
|
262
|
+
is_async: false,
|
|
263
|
+
type_params: None,
|
|
264
|
+
return_type: None,
|
|
265
|
+
}
|
|
266
|
+
.as_arg()],
|
|
267
|
+
)
|
|
268
|
+
.into_stmt()
|
|
269
|
+
.into()
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
#[plugin_transform]
|
|
274
|
+
pub fn process_transform(
|
|
275
|
+
mut program: Program,
|
|
276
|
+
metadata: TransformPluginProgramMetadata,
|
|
277
|
+
) -> Program {
|
|
278
|
+
program.visit_mut_with(&mut TransformVisitor::new(
|
|
279
|
+
metadata.unresolved_mark,
|
|
280
|
+
));
|
|
281
|
+
program
|
|
282
|
+
}
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
use std::collections::HashSet;
|
|
2
|
+
|
|
3
|
+
use indexmap::IndexMap;
|
|
4
|
+
use swc_core::{
|
|
5
|
+
common::{util::take::Take, Span, DUMMY_SP},
|
|
6
|
+
ecma::{
|
|
7
|
+
ast::*,
|
|
8
|
+
atoms::{atom, Atom},
|
|
9
|
+
utils::{find_pat_ids, private_ident, ExprFactory},
|
|
10
|
+
visit::{noop_visit_mut_type, VisitMut, VisitMutWith},
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
use crate::utils::{key_from_export_name, local_ident_from_export_name};
|
|
15
|
+
|
|
16
|
+
type Export = IndexMap<(Atom, Span), Ident>;
|
|
17
|
+
|
|
18
|
+
#[derive(Debug, Default)]
|
|
19
|
+
pub(crate) struct LocalExportStrip {
|
|
20
|
+
pub(crate) has_export_assign: bool,
|
|
21
|
+
pub(crate) export: Export,
|
|
22
|
+
pub(crate) export_all: HashSet<Id>,
|
|
23
|
+
pub(crate) export_decl_id: HashSet<Id>,
|
|
24
|
+
export_default: Option<Stmt>,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
impl VisitMut for LocalExportStrip {
|
|
28
|
+
noop_visit_mut_type!();
|
|
29
|
+
|
|
30
|
+
fn visit_mut_script(&mut self, _: &mut Script) {
|
|
31
|
+
// skip
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
fn visit_mut_module(&mut self, n: &mut Module) {
|
|
35
|
+
let mut list = Vec::with_capacity(n.body.len());
|
|
36
|
+
|
|
37
|
+
for item in n.body.drain(..) {
|
|
38
|
+
match item {
|
|
39
|
+
ModuleItem::Stmt(stmt) => list.push(stmt.into()),
|
|
40
|
+
|
|
41
|
+
ModuleItem::ModuleDecl(mut module_decl) => {
|
|
42
|
+
// collect link meta
|
|
43
|
+
module_decl.visit_mut_with(self);
|
|
44
|
+
|
|
45
|
+
// emit stmt
|
|
46
|
+
match module_decl {
|
|
47
|
+
ModuleDecl::ExportDecl(ExportDecl { decl, .. }) => {
|
|
48
|
+
list.push(Stmt::Decl(decl).into());
|
|
49
|
+
}
|
|
50
|
+
ModuleDecl::ExportNamed(NamedExport { src: None, .. }) => continue,
|
|
51
|
+
ModuleDecl::ExportNamed(
|
|
52
|
+
item @ NamedExport {
|
|
53
|
+
src: Some(..),
|
|
54
|
+
type_only: false,
|
|
55
|
+
..
|
|
56
|
+
},
|
|
57
|
+
) => {
|
|
58
|
+
let decl: ModuleDecl = self.convert_export_decl(item).into();
|
|
59
|
+
list.push(decl.into());
|
|
60
|
+
}
|
|
61
|
+
ModuleDecl::ExportAll(
|
|
62
|
+
e @ ExportAll {
|
|
63
|
+
type_only: false, ..
|
|
64
|
+
},
|
|
65
|
+
) => {
|
|
66
|
+
let decl: ModuleDecl = self.convert_export_all(e).into();
|
|
67
|
+
list.push(decl.into());
|
|
68
|
+
}
|
|
69
|
+
ModuleDecl::ExportDefaultDecl(ExportDefaultDecl {
|
|
70
|
+
decl:
|
|
71
|
+
decl @ (DefaultDecl::Class(ClassExpr {
|
|
72
|
+
ident: Some(..), ..
|
|
73
|
+
})
|
|
74
|
+
| DefaultDecl::Fn(FnExpr {
|
|
75
|
+
ident: Some(..), ..
|
|
76
|
+
})),
|
|
77
|
+
..
|
|
78
|
+
}) => match decl {
|
|
79
|
+
DefaultDecl::Class(class_expr) => list.extend(
|
|
80
|
+
class_expr
|
|
81
|
+
.as_class_decl()
|
|
82
|
+
.map(|decl| Stmt::Decl(Decl::Class(decl)))
|
|
83
|
+
.map(Into::into),
|
|
84
|
+
),
|
|
85
|
+
DefaultDecl::Fn(fn_expr) => list.extend(
|
|
86
|
+
fn_expr
|
|
87
|
+
.as_fn_decl()
|
|
88
|
+
.map(|decl| Stmt::Decl(Decl::Fn(decl)))
|
|
89
|
+
.map(Into::into),
|
|
90
|
+
),
|
|
91
|
+
_ => unreachable!(),
|
|
92
|
+
},
|
|
93
|
+
ModuleDecl::ExportDefaultExpr(..) => {
|
|
94
|
+
list.extend(self.export_default.take().map(From::from))
|
|
95
|
+
}
|
|
96
|
+
ModuleDecl::TsExportAssignment(..) => {
|
|
97
|
+
self.has_export_assign = true;
|
|
98
|
+
list.push(module_decl.into());
|
|
99
|
+
}
|
|
100
|
+
_ => list.push(module_decl.into()),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
n.body = list;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/// ```javascript
|
|
110
|
+
/// export const foo = 1, bar = 2, { baz } = { baz: 3 };
|
|
111
|
+
/// export let a = 1, [b] = [2];
|
|
112
|
+
/// export function x() {}
|
|
113
|
+
/// export class y {}
|
|
114
|
+
/// ```
|
|
115
|
+
/// ->
|
|
116
|
+
/// ```javascript
|
|
117
|
+
/// const foo = 1, bar = 2, { baz } = { baz: 3 };
|
|
118
|
+
/// let a = 1, [b] = [2];
|
|
119
|
+
/// function x() {}
|
|
120
|
+
/// class y {}
|
|
121
|
+
/// ```
|
|
122
|
+
fn visit_mut_export_decl(&mut self, n: &mut ExportDecl) {
|
|
123
|
+
match &n.decl {
|
|
124
|
+
Decl::Class(ClassDecl { ident, .. }) | Decl::Fn(FnDecl { ident, .. }) => {
|
|
125
|
+
let ident = ident.clone();
|
|
126
|
+
|
|
127
|
+
self.export.insert((ident.sym.clone(), ident.span), ident);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
Decl::Var(v) => {
|
|
131
|
+
let ids = find_pat_ids::<_, Ident>(&v.decls);
|
|
132
|
+
|
|
133
|
+
self.export_decl_id.extend(ids.iter().map(Ident::to_id));
|
|
134
|
+
|
|
135
|
+
self.export.extend(ids.into_iter().map(|id| {
|
|
136
|
+
let ident = id.clone();
|
|
137
|
+
|
|
138
|
+
((id.sym, id.span), ident)
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
_ => {}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/// ```javascript
|
|
146
|
+
/// export { foo, foo as bar, foo as "baz" };
|
|
147
|
+
/// export { "foo", foo as bar, "foo" as "baz" } from "mod";
|
|
148
|
+
/// export * as foo from "mod";
|
|
149
|
+
/// export * as "bar" from "mod";
|
|
150
|
+
/// ```
|
|
151
|
+
fn visit_mut_named_export(&mut self, n: &mut NamedExport) {
|
|
152
|
+
if n.type_only || n.src.is_some() {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
let NamedExport { specifiers, .. } = n.take();
|
|
157
|
+
|
|
158
|
+
self.export.extend(specifiers.into_iter().map(|e| match e {
|
|
159
|
+
ExportSpecifier::Namespace(..) => {
|
|
160
|
+
unreachable!("`export *` without src is invalid")
|
|
161
|
+
}
|
|
162
|
+
ExportSpecifier::Default(..) => {
|
|
163
|
+
unreachable!("`export foo` without src is invalid")
|
|
164
|
+
}
|
|
165
|
+
ExportSpecifier::Named(ExportNamedSpecifier { orig, exported, .. }) => {
|
|
166
|
+
let orig = match orig {
|
|
167
|
+
ModuleExportName::Ident(id) => id,
|
|
168
|
+
ModuleExportName::Str(_) => {
|
|
169
|
+
unreachable!(r#"`export {{ "foo" }}` without src is invalid"#)
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
if let Some(exported) = exported {
|
|
174
|
+
let exported = match exported {
|
|
175
|
+
ModuleExportName::Ident(Ident { span, sym, .. }) => (sym, span),
|
|
176
|
+
ModuleExportName::Str(Str { span, value, .. }) => (value.to_atom_lossy().into_owned(), span),
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
(exported, orig)
|
|
180
|
+
} else {
|
|
181
|
+
let exported = orig.sym.clone();
|
|
182
|
+
((exported, orig.span), orig)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}))
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/// ```javascript
|
|
189
|
+
/// export default class foo {};
|
|
190
|
+
/// export default class {};
|
|
191
|
+
/// export default function bar () {};
|
|
192
|
+
/// export default function () {};
|
|
193
|
+
/// ```
|
|
194
|
+
/// ->
|
|
195
|
+
/// ```javascript
|
|
196
|
+
/// class foo {};
|
|
197
|
+
/// class _default {};
|
|
198
|
+
/// function bar () {};
|
|
199
|
+
/// function _default () {};
|
|
200
|
+
/// ```
|
|
201
|
+
fn visit_mut_export_default_decl(&mut self, n: &mut ExportDefaultDecl) {
|
|
202
|
+
match &mut n.decl {
|
|
203
|
+
DefaultDecl::Class(class_expr) => {
|
|
204
|
+
if let Some(ident) = class_expr.ident.clone() {
|
|
205
|
+
self.export.insert((atom!("default"), n.span), ident);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
DefaultDecl::Fn(fn_expr) => {
|
|
209
|
+
if let Some(ident) = fn_expr.ident.clone() {
|
|
210
|
+
self.export.insert((atom!("default"), n.span), ident);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
DefaultDecl::TsInterfaceDecl(_) => {}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/// ```javascript
|
|
218
|
+
/// export default foo;
|
|
219
|
+
/// export default 1
|
|
220
|
+
/// ```
|
|
221
|
+
/// ->
|
|
222
|
+
/// ```javascript
|
|
223
|
+
/// var _default = foo;
|
|
224
|
+
/// var _default = 1;
|
|
225
|
+
/// ```
|
|
226
|
+
fn visit_mut_export_default_expr(&mut self, n: &mut ExportDefaultExpr) {
|
|
227
|
+
let ident = private_ident!(n.span, "_default");
|
|
228
|
+
|
|
229
|
+
self.export
|
|
230
|
+
.insert((atom!("default"), n.span), ident.clone());
|
|
231
|
+
|
|
232
|
+
self.export_default = Some(Stmt::Decl(
|
|
233
|
+
n.expr
|
|
234
|
+
.take()
|
|
235
|
+
.into_var_decl(VarDeclKind::Const, ident.into())
|
|
236
|
+
.into(),
|
|
237
|
+
));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
impl LocalExportStrip {
|
|
242
|
+
fn convert_export_decl(&mut self, n: NamedExport) -> ImportDecl {
|
|
243
|
+
let NamedExport {
|
|
244
|
+
span,
|
|
245
|
+
specifiers,
|
|
246
|
+
src,
|
|
247
|
+
type_only,
|
|
248
|
+
with,
|
|
249
|
+
} = n;
|
|
250
|
+
|
|
251
|
+
let src = src.unwrap();
|
|
252
|
+
|
|
253
|
+
let specifiers = specifiers
|
|
254
|
+
.into_iter()
|
|
255
|
+
.flat_map(|s| self.convert_export_specifier(s))
|
|
256
|
+
.collect();
|
|
257
|
+
|
|
258
|
+
ImportDecl {
|
|
259
|
+
span,
|
|
260
|
+
specifiers,
|
|
261
|
+
src,
|
|
262
|
+
type_only,
|
|
263
|
+
with,
|
|
264
|
+
phase: ImportPhase::Evaluation,
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
fn convert_export_specifier(&mut self, s: ExportSpecifier) -> Option<ImportSpecifier> {
|
|
269
|
+
match s {
|
|
270
|
+
ExportSpecifier::Namespace(ExportNamespaceSpecifier { span, name }) => {
|
|
271
|
+
let key = key_from_export_name(&name);
|
|
272
|
+
let local = local_ident_from_export_name(name);
|
|
273
|
+
self.export.insert(key, local.clone());
|
|
274
|
+
|
|
275
|
+
Some(ImportSpecifier::Namespace(ImportStarAsSpecifier {
|
|
276
|
+
span,
|
|
277
|
+
local,
|
|
278
|
+
}))
|
|
279
|
+
}
|
|
280
|
+
ExportSpecifier::Default(ExportDefaultSpecifier { exported }) => {
|
|
281
|
+
let key = (exported.sym.clone(), exported.span);
|
|
282
|
+
let local = exported.into_private();
|
|
283
|
+
self.export.insert(key, local.clone());
|
|
284
|
+
|
|
285
|
+
Some(ImportSpecifier::Default(ImportDefaultSpecifier {
|
|
286
|
+
local,
|
|
287
|
+
span: DUMMY_SP,
|
|
288
|
+
}))
|
|
289
|
+
}
|
|
290
|
+
ExportSpecifier::Named(ExportNamedSpecifier {
|
|
291
|
+
span,
|
|
292
|
+
orig,
|
|
293
|
+
exported,
|
|
294
|
+
is_type_only: false,
|
|
295
|
+
}) => {
|
|
296
|
+
// export { "x-1" as "y-1" } from "foo"
|
|
297
|
+
// ->
|
|
298
|
+
// import { "x-1" as x1 } from "foo"
|
|
299
|
+
// export { x1 as "y-1" }
|
|
300
|
+
let name = exported.as_ref().unwrap_or(&orig);
|
|
301
|
+
|
|
302
|
+
let key = key_from_export_name(name);
|
|
303
|
+
let local = local_ident_from_export_name(orig.clone());
|
|
304
|
+
self.export.insert(key, local.clone());
|
|
305
|
+
|
|
306
|
+
Some(ImportSpecifier::Named(ImportNamedSpecifier {
|
|
307
|
+
span,
|
|
308
|
+
local,
|
|
309
|
+
imported: Some(orig),
|
|
310
|
+
is_type_only: false,
|
|
311
|
+
}))
|
|
312
|
+
}
|
|
313
|
+
_ => None,
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
fn convert_export_all(&mut self, e: ExportAll) -> ImportDecl {
|
|
318
|
+
let ExportAll {
|
|
319
|
+
span, src, with, ..
|
|
320
|
+
} = e;
|
|
321
|
+
|
|
322
|
+
let mod_name = private_ident!("mod");
|
|
323
|
+
|
|
324
|
+
self.export_all.insert(mod_name.to_id());
|
|
325
|
+
|
|
326
|
+
let star = ImportStarAsSpecifier {
|
|
327
|
+
span,
|
|
328
|
+
local: mod_name.clone(),
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
ImportDecl {
|
|
332
|
+
span,
|
|
333
|
+
specifiers: vec![star.into()],
|
|
334
|
+
src,
|
|
335
|
+
type_only: false,
|
|
336
|
+
with,
|
|
337
|
+
phase: ImportPhase::Evaluation,
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
package/src/utils.rs
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
use swc_core::{
|
|
2
|
+
common::{Span, Spanned, SyntaxContext, DUMMY_SP},
|
|
3
|
+
ecma::{
|
|
4
|
+
ast::*,
|
|
5
|
+
atoms::{atom, Atom},
|
|
6
|
+
utils::{
|
|
7
|
+
is_valid_prop_ident, member_expr, private_ident, quote_ident, quote_str, ExprFactory,
|
|
8
|
+
FunctionFactory,
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/// {
|
|
14
|
+
/// "key": ident,
|
|
15
|
+
/// }
|
|
16
|
+
pub(crate) struct ObjPropKeyIdent(Atom, Span, Ident);
|
|
17
|
+
|
|
18
|
+
impl From<((Atom, Span), Ident)> for ObjPropKeyIdent {
|
|
19
|
+
fn from(((key, span), ident): ((Atom, Span), Ident)) -> Self {
|
|
20
|
+
Self(key, span, ident)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
impl From<(Atom, Span, Ident)> for ObjPropKeyIdent {
|
|
25
|
+
fn from((key, span, ident): (Atom, Span, Ident)) -> Self {
|
|
26
|
+
Self(key, span, ident)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
impl Spanned for ObjPropKeyIdent {
|
|
31
|
+
fn span(&self) -> Span {
|
|
32
|
+
self.1
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
impl ObjPropKeyIdent {
|
|
37
|
+
pub fn key(&self) -> &Atom {
|
|
38
|
+
&self.0
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
pub fn into_expr(self) -> Expr {
|
|
42
|
+
self.2.into()
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// ```javascript
|
|
47
|
+
/// {
|
|
48
|
+
/// key: () => expr,
|
|
49
|
+
/// }
|
|
50
|
+
/// ```
|
|
51
|
+
pub(crate) fn prop_arrow(prop: ObjPropKeyIdent) -> Prop {
|
|
52
|
+
let key = prop_name(prop.key(), prop.span()).into();
|
|
53
|
+
|
|
54
|
+
KeyValueProp {
|
|
55
|
+
key,
|
|
56
|
+
value: Box::new(prop.into_expr().into_lazy_arrow(Default::default()).into()),
|
|
57
|
+
}
|
|
58
|
+
.into()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
pub(crate) fn prop_name(key: &str, span: Span) -> IdentOrStr {
|
|
62
|
+
if is_valid_prop_ident(key) {
|
|
63
|
+
IdentOrStr::Ident(quote_ident!(Default::default(), span, key))
|
|
64
|
+
} else {
|
|
65
|
+
IdentOrStr::Str(quote_str!(span, key))
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
pub(crate) enum IdentOrStr {
|
|
70
|
+
Ident(Ident),
|
|
71
|
+
Str(Str),
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
impl From<IdentOrStr> for PropName {
|
|
75
|
+
fn from(val: IdentOrStr) -> Self {
|
|
76
|
+
match val {
|
|
77
|
+
IdentOrStr::Ident(i) => Self::Ident(i.into()),
|
|
78
|
+
IdentOrStr::Str(s) => Self::Str(s),
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
impl From<IdentOrStr> for MemberProp {
|
|
84
|
+
fn from(val: IdentOrStr) -> Self {
|
|
85
|
+
match val {
|
|
86
|
+
IdentOrStr::Ident(i) => Self::Ident(i.into()),
|
|
87
|
+
IdentOrStr::Str(s) => Self::Computed(ComputedPropName {
|
|
88
|
+
span: DUMMY_SP,
|
|
89
|
+
expr: s.into(),
|
|
90
|
+
}),
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/// Creates
|
|
96
|
+
///
|
|
97
|
+
///```js
|
|
98
|
+
///
|
|
99
|
+
/// Object.defineProperty(target, prop_name, {
|
|
100
|
+
/// ...props
|
|
101
|
+
/// });
|
|
102
|
+
/// ```
|
|
103
|
+
pub(super) fn object_define_property(
|
|
104
|
+
target: ExprOrSpread,
|
|
105
|
+
prop_name: ExprOrSpread,
|
|
106
|
+
descriptor: ExprOrSpread,
|
|
107
|
+
) -> Expr {
|
|
108
|
+
member_expr!(Default::default(), DUMMY_SP, Object.defineProperty)
|
|
109
|
+
.as_call(DUMMY_SP, vec![target, prop_name, descriptor])
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
pub(crate) fn object_define_enumerable_configurable(
|
|
113
|
+
target: ExprOrSpread,
|
|
114
|
+
prop_name: ExprOrSpread,
|
|
115
|
+
prop: PropOrSpread,
|
|
116
|
+
) -> Expr {
|
|
117
|
+
object_define_property(
|
|
118
|
+
target,
|
|
119
|
+
prop_name,
|
|
120
|
+
ObjectLit {
|
|
121
|
+
span: DUMMY_SP,
|
|
122
|
+
props: vec![
|
|
123
|
+
PropOrSpread::Prop(Box::new(
|
|
124
|
+
KeyValueProp {
|
|
125
|
+
key: quote_ident!("enumerable").into(),
|
|
126
|
+
value: Box::new(true.into()),
|
|
127
|
+
}
|
|
128
|
+
.into(),
|
|
129
|
+
)),
|
|
130
|
+
prop,
|
|
131
|
+
PropOrSpread::Prop(Box::new(
|
|
132
|
+
KeyValueProp {
|
|
133
|
+
key: quote_ident!("configurable").into(),
|
|
134
|
+
value: Box::new(true.into()),
|
|
135
|
+
}
|
|
136
|
+
.into(),
|
|
137
|
+
)),
|
|
138
|
+
],
|
|
139
|
+
}
|
|
140
|
+
.as_arg(),
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
pub(crate) fn emit_export_stmts(exports: Ident, mut prop_list: Vec<ObjPropKeyIdent>) -> Vec<Stmt> {
|
|
145
|
+
match prop_list.len() {
|
|
146
|
+
0 | 1 => prop_list
|
|
147
|
+
.pop()
|
|
148
|
+
.map(|obj_prop| {
|
|
149
|
+
object_define_enumerable_configurable(
|
|
150
|
+
exports.as_arg(),
|
|
151
|
+
quote_str!(obj_prop.span(), obj_prop.key().clone()).as_arg(),
|
|
152
|
+
prop_arrow((atom!("get"), DUMMY_SP, obj_prop.2.clone()).into()).into(),
|
|
153
|
+
)
|
|
154
|
+
.into_stmt()
|
|
155
|
+
})
|
|
156
|
+
.into_iter()
|
|
157
|
+
.collect(),
|
|
158
|
+
_ => {
|
|
159
|
+
let props = prop_list
|
|
160
|
+
.into_iter()
|
|
161
|
+
.map(prop_arrow)
|
|
162
|
+
.map(Into::into)
|
|
163
|
+
.collect();
|
|
164
|
+
let obj_lit = ObjectLit {
|
|
165
|
+
span: DUMMY_SP,
|
|
166
|
+
props,
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
let esm_export_ident = private_ident!("_export");
|
|
170
|
+
|
|
171
|
+
vec![
|
|
172
|
+
Stmt::Decl(Decl::Fn(
|
|
173
|
+
esm_export().into_fn_decl(esm_export_ident.clone()),
|
|
174
|
+
)),
|
|
175
|
+
esm_export_ident
|
|
176
|
+
.as_call(DUMMY_SP, vec![exports.as_arg(), obj_lit.as_arg()])
|
|
177
|
+
.into_stmt(),
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/// ```javascript
|
|
184
|
+
/// function _esmExport(target, all) {
|
|
185
|
+
/// for (var name in all)Object.defineProperty(target, name, { get: all[name], enumerable: true, configurable: true });
|
|
186
|
+
/// }
|
|
187
|
+
/// ```
|
|
188
|
+
pub(crate) fn esm_export() -> Function {
|
|
189
|
+
let target = private_ident!("target");
|
|
190
|
+
let all = private_ident!("all");
|
|
191
|
+
let name = private_ident!("name");
|
|
192
|
+
|
|
193
|
+
let getter = KeyValueProp {
|
|
194
|
+
key: quote_ident!("get").into(),
|
|
195
|
+
value: Box::new(Expr::from(all.clone().computed_member(Expr::from(name.clone())))),
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
let body = object_define_enumerable_configurable(
|
|
199
|
+
target.clone().as_arg(),
|
|
200
|
+
name.clone().as_arg(),
|
|
201
|
+
PropOrSpread::Prop(Box::new(Prop::KeyValue(getter))),
|
|
202
|
+
)
|
|
203
|
+
.into_stmt();
|
|
204
|
+
|
|
205
|
+
let for_in_stmt: Stmt = ForInStmt {
|
|
206
|
+
span: DUMMY_SP,
|
|
207
|
+
left: VarDecl {
|
|
208
|
+
span: DUMMY_SP,
|
|
209
|
+
ctxt: SyntaxContext::empty(),
|
|
210
|
+
kind: VarDeclKind::Var,
|
|
211
|
+
declare: false,
|
|
212
|
+
decls: vec![VarDeclarator {
|
|
213
|
+
span: DUMMY_SP,
|
|
214
|
+
name: name.into(),
|
|
215
|
+
init: None,
|
|
216
|
+
definite: false,
|
|
217
|
+
}],
|
|
218
|
+
}
|
|
219
|
+
.into(),
|
|
220
|
+
right: Box::new(all.clone().into()),
|
|
221
|
+
body: Box::new(body),
|
|
222
|
+
}
|
|
223
|
+
.into();
|
|
224
|
+
|
|
225
|
+
Function {
|
|
226
|
+
params: vec![target.into(), all.into()],
|
|
227
|
+
decorators: Default::default(),
|
|
228
|
+
span: DUMMY_SP,
|
|
229
|
+
ctxt: SyntaxContext::empty(),
|
|
230
|
+
this_param: None,
|
|
231
|
+
body: Some(FunctionBody {
|
|
232
|
+
span: DUMMY_SP,
|
|
233
|
+
stmts: vec![for_in_stmt],
|
|
234
|
+
}),
|
|
235
|
+
is_generator: false,
|
|
236
|
+
is_async: false,
|
|
237
|
+
type_params: None,
|
|
238
|
+
return_type: None,
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
pub(crate) fn key_from_export_name(n: &ModuleExportName) -> (Atom, Span) {
|
|
243
|
+
match n {
|
|
244
|
+
ModuleExportName::Ident(ident) => (ident.sym.clone(), ident.span),
|
|
245
|
+
ModuleExportName::Str(str) => (str.value.to_atom_lossy().into_owned(), str.span),
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
pub(crate) fn local_ident_from_export_name(n: ModuleExportName) -> Ident {
|
|
250
|
+
match n {
|
|
251
|
+
ModuleExportName::Ident(ident) => private_ident!(ident.span, ident.sym.clone()),
|
|
252
|
+
ModuleExportName::Str(str) => match Ident::verify_symbol(&str.value.to_string_lossy()) {
|
|
253
|
+
Ok(_) => private_ident!(str.value.to_atom_lossy().into_owned()),
|
|
254
|
+
Err(s) => private_ident!(s),
|
|
255
|
+
},
|
|
256
|
+
}
|
|
257
|
+
}
|