Skip to main content

turbopack_ecmascript/webpack/
mod.rs

1use anyhow::Result;
2use swc_core::ecma::ast::Lit;
3use turbo_rcstr::{RcStr, rcstr};
4use turbo_tasks::{ResolvedVc, ValueToString, Vc};
5use turbopack_core::{
6    chunk::{ChunkingType, TracedMode},
7    compile_time_info::CompileTimeInfo,
8    file_source::FileSource,
9    ident::AssetIdent,
10    module::{Module, ModuleSideEffects},
11    reference::{ModuleReference, ModuleReferences},
12    reference_type::{CommonJsReferenceSubType, ReferenceType},
13    resolve::{
14        ModuleResolveResult, ModuleResolveResultItem, origin::ResolveOrigin, parse::Request,
15        resolve,
16    },
17    source::Source,
18};
19use turbopack_resolve::ecmascript::apply_cjs_specific_options;
20
21use self::{parse::WebpackRuntime, references::module_references};
22use crate::EcmascriptInputTransforms;
23
24pub mod parse;
25pub(crate) mod references;
26
27#[turbo_tasks::value]
28pub struct WebpackModuleAsset {
29    pub source: ResolvedVc<Box<dyn Source>>,
30    pub runtime: ResolvedVc<WebpackRuntime>,
31    pub transforms: ResolvedVc<EcmascriptInputTransforms>,
32    pub compile_time_info: ResolvedVc<CompileTimeInfo>,
33}
34
35#[turbo_tasks::value_impl]
36impl WebpackModuleAsset {
37    #[turbo_tasks::function]
38    pub fn new(
39        source: ResolvedVc<Box<dyn Source>>,
40        runtime: ResolvedVc<WebpackRuntime>,
41        transforms: ResolvedVc<EcmascriptInputTransforms>,
42        compile_time_info: ResolvedVc<CompileTimeInfo>,
43    ) -> Vc<Self> {
44        Self::cell(WebpackModuleAsset {
45            source,
46            runtime,
47            transforms,
48            compile_time_info,
49        })
50    }
51}
52
53#[turbo_tasks::value_impl]
54impl Module for WebpackModuleAsset {
55    #[turbo_tasks::function]
56    async fn ident(&self) -> Result<Vc<AssetIdent>> {
57        Ok(self
58            .source
59            .ident()
60            .owned()
61            .await?
62            .with_modifier(rcstr!("webpack"))
63            .into_vc())
64    }
65
66    #[turbo_tasks::function]
67    fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
68        Vc::cell(Some(self.source))
69    }
70
71    #[turbo_tasks::function]
72    fn references(&self) -> Vc<ModuleReferences> {
73        module_references(
74            *self.source,
75            *self.runtime,
76            *self.transforms,
77            *self.compile_time_info,
78        )
79    }
80
81    #[turbo_tasks::function]
82    fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
83        ModuleSideEffects::SideEffectful.cell()
84    }
85}
86
87#[turbo_tasks::value(shared)]
88#[derive(ValueToString)]
89#[value_to_string("webpack chunk {}", self.chunk_id())]
90pub struct WebpackChunkAssetReference {
91    #[turbo_tasks(trace_ignore)]
92    #[bincode(with_serde)]
93    pub chunk_id: Lit,
94    pub runtime: ResolvedVc<WebpackRuntime>,
95    pub transforms: ResolvedVc<EcmascriptInputTransforms>,
96    pub compile_time_info: ResolvedVc<CompileTimeInfo>,
97}
98
99impl WebpackChunkAssetReference {
100    fn chunk_id(&self) -> RcStr {
101        match &self.chunk_id {
102            Lit::Str(s) => RcStr::from(s.value.to_string_lossy().to_string()),
103            Lit::Num(n) => RcStr::from(n.to_string()),
104            _ => panic!("Unexpected literal type"),
105        }
106    }
107}
108
109#[turbo_tasks::value_impl]
110impl ModuleReference for WebpackChunkAssetReference {
111    #[turbo_tasks::function]
112    async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
113        let runtime = self.runtime.await?;
114        Ok(match &*runtime {
115            WebpackRuntime::Webpack5 { context_path } => {
116                // TODO: Determine the filename from the chunk filename in `webpack_runtime()`,
117                // refer to `is_webpack_runtime()` in https://github.com/vercel/next.js/commit/f6d8529af54b78e913f0f743ab6cace851b32e4f for a partial implentation
118                let chunk_id = match &self.chunk_id {
119                    Lit::Str(str) => str.value.to_string_lossy().into_owned(),
120                    Lit::Num(num) => format!("{num}"),
121                    _ => todo!(),
122                };
123                let filename = format!("./chunks/{chunk_id}.js");
124                let source = Vc::upcast(FileSource::new(context_path.join(&filename)?));
125
126                *ModuleResolveResult::module(ResolvedVc::upcast(
127                    WebpackModuleAsset::new(
128                        source,
129                        *self.runtime,
130                        *self.transforms,
131                        *self.compile_time_info,
132                    )
133                    .to_resolved()
134                    .await?,
135                ))
136            }
137            WebpackRuntime::None => *ModuleResolveResult::unresolvable(),
138        })
139    }
140
141    fn chunking_type(&self) -> Option<ChunkingType> {
142        Some(ChunkingType::Traced {
143            mode: TracedMode::Transitive,
144        })
145    }
146}
147
148#[turbo_tasks::value(shared)]
149#[derive(ValueToString)]
150#[value_to_string("webpack entry")]
151pub struct WebpackEntryAssetReference {
152    pub source: ResolvedVc<Box<dyn Source>>,
153    pub runtime: ResolvedVc<WebpackRuntime>,
154    pub transforms: ResolvedVc<EcmascriptInputTransforms>,
155    pub compile_time_info: ResolvedVc<CompileTimeInfo>,
156}
157
158#[turbo_tasks::value_impl]
159impl ModuleReference for WebpackEntryAssetReference {
160    #[turbo_tasks::function]
161    async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
162        Ok(*ModuleResolveResult::module(ResolvedVc::upcast(
163            WebpackModuleAsset::new(
164                *self.source,
165                *self.runtime,
166                *self.transforms,
167                *self.compile_time_info,
168            )
169            .to_resolved()
170            .await?,
171        )))
172    }
173
174    fn chunking_type(&self) -> Option<ChunkingType> {
175        Some(ChunkingType::Traced {
176            mode: TracedMode::Transitive,
177        })
178    }
179}
180
181#[turbo_tasks::value(shared)]
182#[derive(ValueToString)]
183#[value_to_string("webpack {request}")]
184pub struct WebpackRuntimeAssetReference {
185    pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
186    pub request: ResolvedVc<Request>,
187    pub runtime: ResolvedVc<WebpackRuntime>,
188    pub transforms: ResolvedVc<EcmascriptInputTransforms>,
189    pub compile_time_info: ResolvedVc<CompileTimeInfo>,
190}
191
192#[turbo_tasks::value_impl]
193impl ModuleReference for WebpackRuntimeAssetReference {
194    #[turbo_tasks::function]
195    async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
196        let origin = self.origin.into_trait_ref().await?;
197        let options = origin.resolve_options();
198
199        let options = apply_cjs_specific_options(options);
200
201        let resolved = resolve(
202            origin.origin_path().parent(),
203            ReferenceType::CommonJs(CommonJsReferenceSubType::Undefined),
204            *self.request,
205            options,
206        );
207
208        Ok(resolved
209            .await?
210            .map_module(|source| async move {
211                Ok(ModuleResolveResultItem::Module(ResolvedVc::upcast(
212                    WebpackModuleAsset::new(
213                        *source,
214                        *self.runtime,
215                        *self.transforms,
216                        *self.compile_time_info,
217                    )
218                    .to_resolved()
219                    .await?,
220                )))
221            })
222            .await?
223            .cell())
224    }
225
226    fn chunking_type(&self) -> Option<ChunkingType> {
227        Some(ChunkingType::Traced {
228            mode: TracedMode::Transitive,
229        })
230    }
231}