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 if chunking_context
66 .unused_references()
67 .contains_key(&ResolvedVc::upcast(self.reference))
68 .await?
69 {
70 return Ok(CodeGeneration::empty());
71 }
72
73 let mut visitors = vec![];
74
75 let export = self.export.clone();
76 let imported_module = self.reference.get_referenced_asset().await?;
77
78 enum ImportedIdent {
79 Module(ReferencedAssetIdent),
80 None,
81 Unresolvable,
82 }
83
84 let imported_ident = match &imported_module {
85 ReferencedAsset::None => ImportedIdent::None,
86 imported_module => imported_module
87 .get_ident(chunking_context, export, scope_hoisting_context)
88 .await?
89 .map_or(ImportedIdent::Unresolvable, ImportedIdent::Module),
90 };
91
92 let mut ast_path = self.ast_path.0.clone();
93 loop {
94 match ast_path.last() {
95 Some(swc_core::ecma::visit::AstParentKind::Prop(PropField::Shorthand)) => {
98 ast_path.pop();
99 visitors.push(create_visitor!(
100 exact,
101 ast_path,
102 visit_mut_prop,
103 |prop: &mut Prop| {
104 if let Prop::Shorthand(ident) = prop {
105 match &imported_ident {
106 ImportedIdent::Module(imported_ident) => {
107 *prop = Prop::KeyValue(KeyValueProp {
108 key: PropName::Ident(ident.clone().into()),
109 value: Box::new(
110 imported_ident.as_expr(ident.span, false),
111 ),
112 });
113 }
114 ImportedIdent::None => {
115 *prop = Prop::KeyValue(KeyValueProp {
116 key: PropName::Ident(ident.clone().into()),
117 value: Expr::undefined(ident.span),
118 });
119 }
120 ImportedIdent::Unresolvable => {
121 }
123 }
124 }
125 }
126 ));
127 break;
128 }
129 Some(swc_core::ecma::visit::AstParentKind::Expr(_)) => {
131 ast_path.pop();
132 let in_call = !self.keep_this
133 && matches!(
134 ast_path.last(),
135 Some(swc_core::ecma::visit::AstParentKind::Callee(
136 CalleeField::Expr
137 ))
138 );
139
140 visitors.push(create_visitor!(
141 exact,
142 ast_path,
143 visit_mut_expr,
144 |expr: &mut Expr| {
145 use swc_core::common::Spanned;
146 match &imported_ident {
147 ImportedIdent::Module(imported_ident) => {
148 *expr = imported_ident.as_expr(expr.span(), in_call);
149 }
150 ImportedIdent::None => {
151 *expr = *Expr::undefined(expr.span());
152 }
153 ImportedIdent::Unresolvable => {
154 }
156 }
157 }
158 ));
159 break;
160 }
161 Some(swc_core::ecma::visit::AstParentKind::SimpleAssignTarget(_)) => {
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 Some(_) => {
194 ast_path.pop();
195 }
196 None => break,
197 }
198 }
199
200 Ok(CodeGeneration::visitors(visitors))
201 }
202}
203
204impl From<EsmBinding> for CodeGen {
205 fn from(val: EsmBinding) -> Self {
206 CodeGen::EsmBinding(val)
207 }
208}