turbopack_ecmascript/references/esm/
dynamic.rs1use anyhow::Result;
2use bincode::{Decode, Encode};
3use swc_core::{
4 common::{DUMMY_SP, util::take::Take},
5 ecma::ast::{CallExpr, Callee, Expr, ExprOrSpread, Lit},
6 quote_expr,
7};
8use turbo_tasks::{
9 NonLocalValue, ResolvedVc, ValueToString, Vc, debug::ValueDebugFormat, trace::TraceRawVcs,
10};
11use turbopack_core::{
12 chunk::{ChunkingContext, ChunkingType},
13 issue::IssueSource,
14 module::Module,
15 reference::ModuleReference,
16 reference_type::EcmaScriptModulesReferenceSubType,
17 resolve::{
18 BindingUsage, ExportUsage, ModuleResolveResult, ResolveErrorMode,
19 origin::{ResolveOrigin, ResolveOriginExt},
20 parse::Request,
21 },
22};
23use turbopack_resolve::ecmascript::esm_resolve;
24
25use crate::{
26 analyzer::imports::ImportAnnotations,
27 code_gen::{CodeGen, CodeGeneration, IntoCodeGenReference},
28 create_visitor,
29 references::{
30 AstPath,
31 pattern_mapping::{PatternMapping, ResolveType},
32 },
33};
34
35#[turbo_tasks::value]
36#[derive(Hash, Debug, ValueToString)]
37#[value_to_string("dynamic import {request}")]
38pub struct EsmAsyncAssetReference {
39 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
40 pub request: ResolvedVc<Request>,
41 pub issue_source: IssueSource,
42 pub error_mode: ResolveErrorMode,
43 pub import_externals: bool,
44 pub export_usage: ExportUsage,
48 pub resolve_override: Option<ResolvedVc<Box<dyn Module>>>,
49}
50
51impl EsmAsyncAssetReference {
52 #[allow(clippy::too_many_arguments)]
53 pub async fn new(
54 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
55 request: ResolvedVc<Request>,
56 issue_source: IssueSource,
57 annotations: ImportAnnotations,
58 error_mode: ResolveErrorMode,
59 import_externals: bool,
60 export_usage: ExportUsage,
61 resolve_override: Option<ResolvedVc<Box<dyn Module>>>,
62 ) -> Result<Self> {
63 let origin = if let Some(transition) = annotations.transition() {
66 origin
67 .with_transition(transition.into())
68 .await?
69 .to_resolved()
70 .await?
71 } else {
72 origin
73 };
74 Ok(EsmAsyncAssetReference {
75 origin,
76 request,
77 issue_source,
78 error_mode,
79 import_externals,
80 export_usage,
81 resolve_override,
82 })
83 }
84}
85
86#[turbo_tasks::value_impl]
87impl ModuleReference for EsmAsyncAssetReference {
88 #[turbo_tasks::function]
89 async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
90 if let Some(resolved) = &self.resolve_override {
91 return Ok(*ModuleResolveResult::module(*resolved));
92 }
93
94 esm_resolve(
95 *self.origin,
96 *self.request,
97 EcmaScriptModulesReferenceSubType::DynamicImport,
98 self.error_mode,
99 Some(self.issue_source),
100 )
101 .await
102 }
103
104 fn chunking_type(&self) -> Option<ChunkingType> {
105 Some(ChunkingType::Async)
106 }
107
108 fn binding_usage(&self) -> BindingUsage {
109 BindingUsage {
110 import: Default::default(),
111 export: self.export_usage.clone(),
112 }
113 }
114
115 fn source(&self) -> Option<IssueSource> {
116 Some(self.issue_source)
117 }
118}
119
120impl IntoCodeGenReference for EsmAsyncAssetReference {
121 fn into_reference(self) -> ResolvedVc<Box<dyn ModuleReference>> {
122 ResolvedVc::upcast(self.resolved_cell())
123 }
124
125 fn into_code_gen_reference(
126 self,
127 path: AstPath,
128 ) -> (ResolvedVc<Box<dyn ModuleReference>>, CodeGen) {
129 let reference = self.resolved_cell();
130 (
131 ResolvedVc::upcast(reference),
132 CodeGen::EsmAsyncAssetReferenceCodeGen(EsmAsyncAssetReferenceCodeGen {
133 reference,
134 path,
135 }),
136 )
137 }
138}
139
140#[derive(
141 PartialEq, Eq, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, Encode, Decode,
142)]
143pub struct EsmAsyncAssetReferenceCodeGen {
144 path: AstPath,
145 reference: ResolvedVc<EsmAsyncAssetReference>,
146}
147
148impl EsmAsyncAssetReferenceCodeGen {
149 pub async fn code_generation(
150 &self,
151 chunking_context: Vc<Box<dyn ChunkingContext>>,
152 ) -> Result<CodeGeneration> {
153 let reference = self.reference.await?;
154
155 let pm = PatternMapping::resolve_request(
156 *reference.request,
157 *reference.origin,
158 chunking_context,
159 self.reference.resolve_reference(),
160 if chunking_context.chunk_loading().await?.can_split_async() {
161 ResolveType::AsyncChunkLoader
162 } else {
163 ResolveType::ChunkItem
164 },
165 Some(Vc::upcast(*self.reference)),
166 )
167 .await?;
168
169 let import_externals = reference.import_externals;
170
171 let visitor = create_visitor!(self.path, visit_mut_expr, |expr: &mut Expr| {
172 let old_expr = expr.take();
173 let message = if let Expr::Call(CallExpr { args, .. }) = old_expr {
174 match args.into_iter().next() {
175 Some(ExprOrSpread {
176 spread: None,
177 expr: key_expr,
178 }) => {
179 *expr = pm.create_import(*key_expr, import_externals);
180 return;
181 }
182 Some(ExprOrSpread {
184 spread: Some(_),
185 expr: _,
186 }) => "spread operator is illegal in import() expressions.",
187 _ => "import() expressions require at least 1 argument",
188 }
189 } else {
190 "visitor must be executed on a CallExpr"
191 };
192 let error = quote_expr!(
193 "() => { throw new Error($message); }",
194 message: Expr = Expr::Lit(Lit::Str(message.into()))
195 );
196 *expr = Expr::Call(CallExpr {
197 callee: Callee::Expr(quote_expr!("Promise.resolve().then")),
198 args: vec![ExprOrSpread {
199 spread: None,
200 expr: error,
201 }],
202 span: DUMMY_SP,
203 ..Default::default()
204 });
205 });
206
207 Ok(CodeGeneration::visitors(vec![visitor]))
208 }
209}