turbopack_ecmascript/references/esm/
binding.rs1use anyhow::Result;
2use serde::{Deserialize, Serialize};
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, module_graph::ModuleGraph};
10
11use super::EsmAssetReference;
12use crate::{
13 ScopeHoistingContext,
14 code_gen::{CodeGen, CodeGeneration},
15 create_visitor,
16 references::{
17 AstPath,
18 esm::base::{ReferencedAsset, ReferencedAssetIdent},
19 },
20};
21
22#[derive(Hash, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, TraceRawVcs, NonLocalValue)]
23pub struct EsmBinding {
24 reference: ResolvedVc<EsmAssetReference>,
25 export: Option<RcStr>,
26 ast_path: AstPath,
27 keep_this: bool,
28}
29
30impl EsmBinding {
31 pub fn new(
32 reference: ResolvedVc<EsmAssetReference>,
33 export: Option<RcStr>,
34 ast_path: AstPath,
35 ) -> Self {
36 EsmBinding {
37 reference,
38 export,
39 ast_path,
40 keep_this: false,
41 }
42 }
43
44 pub fn new_keep_this(
46 reference: ResolvedVc<EsmAssetReference>,
47 export: Option<RcStr>,
48 ast_path: AstPath,
49 ) -> Self {
50 EsmBinding {
51 reference,
52 export,
53 ast_path,
54 keep_this: true,
55 }
56 }
57
58 pub async fn code_generation(
59 &self,
60 _module_graph: Vc<ModuleGraph>,
61 chunking_context: Vc<Box<dyn ChunkingContext>>,
62 scope_hoisting_context: ScopeHoistingContext<'_>,
63 ) -> Result<CodeGeneration> {
64 let mut visitors = vec![];
65
66 let export = self.export.clone();
67 let imported_module = self.reference.get_referenced_asset().await?;
68
69 enum ImportedIdent {
70 Module(ReferencedAssetIdent),
71 None,
72 Unresolvable,
73 }
74
75 let imported_ident = match &*imported_module {
76 ReferencedAsset::None => ImportedIdent::None,
77 imported_module => imported_module
78 .get_ident(chunking_context, export, scope_hoisting_context)
79 .await?
80 .map_or(ImportedIdent::Unresolvable, ImportedIdent::Module),
81 };
82
83 let mut ast_path = self.ast_path.0.clone();
84 loop {
85 match ast_path.last() {
86 Some(swc_core::ecma::visit::AstParentKind::Prop(PropField::Shorthand)) => {
89 ast_path.pop();
90 visitors.push(create_visitor!(
91 exact,
92 ast_path,
93 visit_mut_prop,
94 |prop: &mut Prop| {
95 if let Prop::Shorthand(ident) = prop {
96 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::BindingIdent(
154 swc_core::ecma::visit::fields::BindingIdentField::Id,
155 )) => {
156 ast_path.pop();
157
158 if let Some(swc_core::ecma::visit::AstParentKind::SimpleAssignTarget(
161 swc_core::ecma::visit::fields::SimpleAssignTargetField::Ident,
162 )) = ast_path.last()
163 {
164 ast_path.pop();
165
166 visitors.push(create_visitor!(
167 exact,
168 ast_path,
169 visit_mut_simple_assign_target,
170 |l: &mut SimpleAssignTarget| {
171 use swc_core::common::Spanned;
172 match &imported_ident {
173 ImportedIdent::Module(imported_ident) => {
174 *l = imported_ident
175 .as_expr_individual(l.span())
176 .map_either(
177 |i| SimpleAssignTarget::Ident(i.into()),
178 SimpleAssignTarget::Member,
179 )
180 .into_inner();
181 }
182 ImportedIdent::None => {
183 }
185 ImportedIdent::Unresolvable => {
186 }
188 }
189 }
190 ));
191 break;
192 }
193 }
194 Some(_) => {
195 ast_path.pop();
196 }
197 None => break,
198 }
199 }
200
201 Ok(CodeGeneration::visitors(visitors))
202 }
203}
204
205impl From<EsmBinding> for CodeGen {
206 fn from(val: EsmBinding) -> Self {
207 CodeGen::EsmBinding(val)
208 }
209}