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 environment::ChunkLoading,
14 issue::IssueSource,
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 annotations: ImportAnnotations,
42 pub issue_source: IssueSource,
43 pub error_mode: ResolveErrorMode,
44 pub import_externals: bool,
45 pub export_usage: ExportUsage,
49}
50
51impl EsmAsyncAssetReference {
52 fn get_origin(&self) -> Vc<Box<dyn ResolveOrigin>> {
53 if let Some(transition) = self.annotations.transition() {
54 self.origin.with_transition(transition.into())
55 } else {
56 *self.origin
57 }
58 }
59}
60
61impl EsmAsyncAssetReference {
62 pub fn new(
63 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
64 request: ResolvedVc<Request>,
65 issue_source: IssueSource,
66 annotations: ImportAnnotations,
67 error_mode: ResolveErrorMode,
68 import_externals: bool,
69 export_usage: ExportUsage,
70 ) -> Self {
71 EsmAsyncAssetReference {
72 origin,
73 request,
74 issue_source,
75 annotations,
76 error_mode,
77 import_externals,
78 export_usage,
79 }
80 }
81}
82
83#[turbo_tasks::value_impl]
84impl ModuleReference for EsmAsyncAssetReference {
85 #[turbo_tasks::function]
86 async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
87 esm_resolve(
88 self.get_origin().resolve().await?,
89 *self.request,
90 EcmaScriptModulesReferenceSubType::DynamicImport,
91 self.error_mode,
92 Some(self.issue_source),
93 )
94 .await
95 }
96
97 fn chunking_type(&self) -> Option<ChunkingType> {
98 Some(ChunkingType::Async)
99 }
100
101 fn binding_usage(&self) -> BindingUsage {
102 BindingUsage {
103 import: Default::default(),
104 export: self.export_usage.clone(),
105 }
106 }
107}
108
109impl IntoCodeGenReference for EsmAsyncAssetReference {
110 fn into_code_gen_reference(
111 self,
112 path: AstPath,
113 ) -> (ResolvedVc<Box<dyn ModuleReference>>, CodeGen) {
114 let reference = self.resolved_cell();
115 (
116 ResolvedVc::upcast(reference),
117 CodeGen::EsmAsyncAssetReferenceCodeGen(EsmAsyncAssetReferenceCodeGen {
118 reference,
119 path,
120 }),
121 )
122 }
123}
124
125#[derive(
126 PartialEq, Eq, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, Encode, Decode,
127)]
128pub struct EsmAsyncAssetReferenceCodeGen {
129 path: AstPath,
130 reference: ResolvedVc<EsmAsyncAssetReference>,
131}
132
133impl EsmAsyncAssetReferenceCodeGen {
134 pub async fn code_generation(
135 &self,
136 chunking_context: Vc<Box<dyn ChunkingContext>>,
137 ) -> Result<CodeGeneration> {
138 let reference = self.reference.await?;
139
140 let pm = PatternMapping::resolve_request(
141 *reference.request,
142 *reference.origin,
143 chunking_context,
144 self.reference.resolve_reference(),
145 if matches!(
146 *chunking_context.environment().chunk_loading().await?,
147 ChunkLoading::Edge
148 ) {
149 ResolveType::ChunkItem
150 } else {
151 ResolveType::AsyncChunkLoader
152 },
153 )
154 .await?;
155
156 let import_externals = reference.import_externals;
157
158 let visitor = create_visitor!(self.path, visit_mut_expr, |expr: &mut Expr| {
159 let old_expr = expr.take();
160 let message = if let Expr::Call(CallExpr { args, .. }) = old_expr {
161 match args.into_iter().next() {
162 Some(ExprOrSpread {
163 spread: None,
164 expr: key_expr,
165 }) => {
166 *expr = pm.create_import(*key_expr, import_externals);
167 return;
168 }
169 Some(ExprOrSpread {
171 spread: Some(_),
172 expr: _,
173 }) => "spread operator is illegal in import() expressions.",
174 _ => "import() expressions require at least 1 argument",
175 }
176 } else {
177 "visitor must be executed on a CallExpr"
178 };
179 let error = quote_expr!(
180 "() => { throw new Error($message); }",
181 message: Expr = Expr::Lit(Lit::Str(message.into()))
182 );
183 *expr = Expr::Call(CallExpr {
184 callee: Callee::Expr(quote_expr!("Promise.resolve().then")),
185 args: vec![ExprOrSpread {
186 spread: None,
187 expr: error,
188 }],
189 span: DUMMY_SP,
190 ..Default::default()
191 });
192 });
193
194 Ok(CodeGeneration::visitors(vec![visitor]))
195 }
196}