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;
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 chunking_context: Vc<Box<dyn ChunkingContext>>,
61 scope_hoisting_context: ScopeHoistingContext<'_>,
62 ) -> Result<CodeGeneration> {
63 let mut visitors = vec![];
64
65 let export = self.export.clone();
66 let imported_module = self.reference.get_referenced_asset().await?;
67
68 enum ImportedIdent {
69 Module(ReferencedAssetIdent),
70 None,
71 Unresolvable,
72 }
73
74 let imported_ident = match &*imported_module {
75 ReferencedAsset::None => ImportedIdent::None,
76 imported_module => imported_module
77 .get_ident(chunking_context, export, scope_hoisting_context)
78 .await?
79 .map_or(ImportedIdent::Unresolvable, ImportedIdent::Module),
80 };
81
82 let mut ast_path = self.ast_path.0.clone();
83 loop {
84 match ast_path.last() {
85 Some(swc_core::ecma::visit::AstParentKind::Prop(PropField::Shorthand)) => {
88 ast_path.pop();
89 visitors.push(create_visitor!(
90 exact,
91 ast_path,
92 visit_mut_prop,
93 |prop: &mut Prop| {
94 if let Prop::Shorthand(ident) = prop {
95 match &imported_ident {
96 ImportedIdent::Module(imported_ident) => {
97 *prop = Prop::KeyValue(KeyValueProp {
98 key: PropName::Ident(ident.clone().into()),
99 value: Box::new(
100 imported_ident.as_expr(ident.span, false),
101 ),
102 });
103 }
104 ImportedIdent::None => {
105 *prop = Prop::KeyValue(KeyValueProp {
106 key: PropName::Ident(ident.clone().into()),
107 value: Expr::undefined(ident.span),
108 });
109 }
110 ImportedIdent::Unresolvable => {
111 }
113 }
114 }
115 }
116 ));
117 break;
118 }
119 Some(swc_core::ecma::visit::AstParentKind::Expr(_)) => {
121 ast_path.pop();
122 let in_call = !self.keep_this
123 && matches!(
124 ast_path.last(),
125 Some(swc_core::ecma::visit::AstParentKind::Callee(
126 CalleeField::Expr
127 ))
128 );
129
130 visitors.push(create_visitor!(
131 exact,
132 ast_path,
133 visit_mut_expr,
134 |expr: &mut Expr| {
135 use swc_core::common::Spanned;
136 match &imported_ident {
137 ImportedIdent::Module(imported_ident) => {
138 *expr = imported_ident.as_expr(expr.span(), in_call);
139 }
140 ImportedIdent::None => {
141 *expr = *Expr::undefined(expr.span());
142 }
143 ImportedIdent::Unresolvable => {
144 }
146 }
147 }
148 ));
149 break;
150 }
151 Some(swc_core::ecma::visit::AstParentKind::SimpleAssignTarget(_)) => {
154 ast_path.pop();
155
156 visitors.push(create_visitor!(
157 exact,
158 ast_path,
159 visit_mut_simple_assign_target,
160 |l: &mut SimpleAssignTarget| {
161 use swc_core::common::Spanned;
162 match &imported_ident {
163 ImportedIdent::Module(imported_ident) => {
164 *l = imported_ident
165 .as_expr_individual(l.span())
166 .map_either(
167 |i| SimpleAssignTarget::Ident(i.into()),
168 SimpleAssignTarget::Member,
169 )
170 .into_inner();
171 }
172 ImportedIdent::None => {
173 }
175 ImportedIdent::Unresolvable => {
176 }
178 }
179 }
180 ));
181 break;
182 }
183 Some(_) => {
184 ast_path.pop();
185 }
186 None => break,
187 }
188 }
189
190 Ok(CodeGeneration::visitors(visitors))
191 }
192}
193
194impl From<EsmBinding> for CodeGen {
195 fn from(val: EsmBinding) -> Self {
196 CodeGen::EsmBinding(val)
197 }
198}