turbopack_ecmascript/references/esm/
binding.rs1use anyhow::Result;
2use bincode::{Decode, Encode};
3use swc_core::ecma::{
4 ast::{Expr, KeyValueProp, Prop, PropName, SimpleAssignTarget},
5 visit::fields::{CalleeField, PropField},
6};
7use turbo_rcstr::RcStr;
8use turbo_tasks::{NonLocalValue, ResolvedVc, Vc, trace::TraceRawVcs};
9use turbopack_core::chunk::ChunkingContext;
10
11use crate::{
12 ScopeHoistingContext,
13 code_gen::{CodeGen, CodeGeneration},
14 create_visitor,
15 references::{
16 AstPath,
17 esm::{
18 EsmAssetReference,
19 base::{ReferencedAsset, ReferencedAssetIdent},
20 },
21 },
22};
23
24#[derive(Hash, Clone, Debug, PartialEq, Eq, TraceRawVcs, NonLocalValue, Encode, Decode)]
25pub struct EsmBinding {
26 reference: ResolvedVc<EsmAssetReference>,
27 export: Option<RcStr>,
28 ast_path: AstPath,
29 keep_this: bool,
30}
31
32impl EsmBinding {
33 pub fn new(
34 reference: ResolvedVc<EsmAssetReference>,
35 export: Option<RcStr>,
36 ast_path: AstPath,
37 ) -> Self {
38 EsmBinding {
39 reference,
40 export,
41 ast_path,
42 keep_this: false,
43 }
44 }
45
46 pub fn new_keep_this(
48 reference: ResolvedVc<EsmAssetReference>,
49 export: Option<RcStr>,
50 ast_path: AstPath,
51 ) -> Self {
52 EsmBinding {
53 reference,
54 export,
55 ast_path,
56 keep_this: true,
57 }
58 }
59
60 pub async fn code_generation(
61 &self,
62 chunking_context: Vc<Box<dyn ChunkingContext>>,
63 scope_hoisting_context: ScopeHoistingContext<'_>,
64 ) -> Result<CodeGeneration> {
65 let mut visitors = vec![];
66
67 let export = self.export.clone();
68 let imported_module = self.reference.get_referenced_asset().await?;
69
70 enum ImportedIdent {
71 Module(ReferencedAssetIdent),
72 None,
73 Unresolvable,
74 }
75
76 let imported_ident = match &*imported_module {
77 ReferencedAsset::None => ImportedIdent::None,
78 imported_module => imported_module
79 .get_ident(chunking_context, export, scope_hoisting_context)
80 .await?
81 .map_or(ImportedIdent::Unresolvable, ImportedIdent::Module),
82 };
83
84 let mut ast_path = self.ast_path.0.clone();
85 loop {
86 match ast_path.last() {
87 Some(swc_core::ecma::visit::AstParentKind::Prop(PropField::Shorthand)) => {
90 ast_path.pop();
91 visitors.push(create_visitor!(
92 exact,
93 ast_path,
94 visit_mut_prop,
95 |prop: &mut Prop| {
96 if let Prop::Shorthand(ident) = prop {
97 match &imported_ident {
98 ImportedIdent::Module(imported_ident) => {
99 *prop = Prop::KeyValue(KeyValueProp {
100 key: PropName::Ident(ident.clone().into()),
101 value: Box::new(
102 imported_ident.as_expr(ident.span, false),
103 ),
104 });
105 }
106 ImportedIdent::None => {
107 *prop = Prop::KeyValue(KeyValueProp {
108 key: PropName::Ident(ident.clone().into()),
109 value: Expr::undefined(ident.span),
110 });
111 }
112 ImportedIdent::Unresolvable => {
113 }
115 }
116 }
117 }
118 ));
119 break;
120 }
121 Some(swc_core::ecma::visit::AstParentKind::Expr(_)) => {
123 ast_path.pop();
124 let in_call = !self.keep_this
125 && matches!(
126 ast_path.last(),
127 Some(swc_core::ecma::visit::AstParentKind::Callee(
128 CalleeField::Expr
129 ))
130 );
131
132 visitors.push(create_visitor!(
133 exact,
134 ast_path,
135 visit_mut_expr,
136 |expr: &mut Expr| {
137 use swc_core::common::Spanned;
138 match &imported_ident {
139 ImportedIdent::Module(imported_ident) => {
140 *expr = imported_ident.as_expr(expr.span(), in_call);
141 }
142 ImportedIdent::None => {
143 *expr = *Expr::undefined(expr.span());
144 }
145 ImportedIdent::Unresolvable => {
146 }
148 }
149 }
150 ));
151 break;
152 }
153 Some(swc_core::ecma::visit::AstParentKind::SimpleAssignTarget(_)) => {
156 ast_path.pop();
157
158 visitors.push(create_visitor!(
159 exact,
160 ast_path,
161 visit_mut_simple_assign_target,
162 |l: &mut SimpleAssignTarget| {
163 use swc_core::common::Spanned;
164 match &imported_ident {
165 ImportedIdent::Module(imported_ident) => {
166 *l = imported_ident
167 .as_expr_individual(l.span())
168 .map_either(
169 |i| SimpleAssignTarget::Ident(i.into()),
170 SimpleAssignTarget::Member,
171 )
172 .into_inner();
173 }
174 ImportedIdent::None => {
175 }
177 ImportedIdent::Unresolvable => {
178 }
180 }
181 }
182 ));
183 break;
184 }
185 Some(_) => {
186 ast_path.pop();
187 }
188 None => break,
189 }
190 }
191
192 Ok(CodeGeneration::visitors(visitors))
193 }
194}
195
196impl From<EsmBinding> for CodeGen {
197 fn from(val: EsmBinding) -> Self {
198 CodeGen::EsmBinding(val)
199 }
200}