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    asset::{Asset, AssetContent},
7    file_source::FileSource,
8    ident::AssetIdent,
9    module::{Module, ModuleSideEffects},
10    reference::{ModuleReference, ModuleReferences},
11    reference_type::{CommonJsReferenceSubType, ReferenceType},
12    resolve::{
13        ModuleResolveResult, ModuleResolveResultItem, origin::ResolveOrigin, parse::Request,
14        resolve,
15    },
16    source::Source,
17};
18use turbopack_resolve::ecmascript::apply_cjs_specific_options;
19
20use self::{parse::WebpackRuntime, references::module_references};
21use crate::EcmascriptInputTransforms;
22
23pub mod parse;
24pub(crate) mod references;
25
26#[turbo_tasks::value]
27pub struct WebpackModuleAsset {
28    pub source: ResolvedVc<Box<dyn Source>>,
29    pub runtime: ResolvedVc<WebpackRuntime>,
30    pub transforms: ResolvedVc<EcmascriptInputTransforms>,
31}
32
33#[turbo_tasks::value_impl]
34impl WebpackModuleAsset {
35    #[turbo_tasks::function]
36    pub fn new(
37        source: ResolvedVc<Box<dyn Source>>,
38        runtime: ResolvedVc<WebpackRuntime>,
39        transforms: ResolvedVc<EcmascriptInputTransforms>,
40    ) -> Vc<Self> {
41        Self::cell(WebpackModuleAsset {
42            source,
43            runtime,
44            transforms,
45        })
46    }
47}
48
49#[turbo_tasks::value_impl]
50impl Module for WebpackModuleAsset {
51    #[turbo_tasks::function]
52    fn ident(&self) -> Vc<AssetIdent> {
53        self.source.ident().with_modifier(rcstr!("webpack"))
54    }
55
56    #[turbo_tasks::function]
57    fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
58        Vc::cell(Some(self.source))
59    }
60
61    #[turbo_tasks::function]
62    fn references(&self) -> Vc<ModuleReferences> {
63        module_references(*self.source, *self.runtime, *self.transforms)
64    }
65
66    #[turbo_tasks::function]
67    fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
68        ModuleSideEffects::SideEffectful.cell()
69    }
70}
71
72#[turbo_tasks::value_impl]
73impl Asset for WebpackModuleAsset {
74    #[turbo_tasks::function]
75    fn content(&self) -> Vc<AssetContent> {
76        self.source.content()
77    }
78}
79
80#[turbo_tasks::value(shared)]
81pub struct WebpackChunkAssetReference {
82    #[turbo_tasks(trace_ignore)]
83    #[bincode(with_serde)]
84    pub chunk_id: Lit,
85    pub runtime: ResolvedVc<WebpackRuntime>,
86    pub transforms: ResolvedVc<EcmascriptInputTransforms>,
87}
88
89#[turbo_tasks::value_impl]
90impl ModuleReference for WebpackChunkAssetReference {
91    #[turbo_tasks::function]
92    async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
93        let runtime = self.runtime.await?;
94        Ok(match &*runtime {
95            WebpackRuntime::Webpack5 {
96                chunk_request_expr: _,
97                context_path,
98            } => {
99                // TODO determine filename from chunk_request_expr
100                let chunk_id = match &self.chunk_id {
101                    Lit::Str(str) => str.value.to_string_lossy().into_owned(),
102                    Lit::Num(num) => format!("{num}"),
103                    _ => todo!(),
104                };
105                let filename = format!("./chunks/{chunk_id}.js");
106                let source = Vc::upcast(FileSource::new(context_path.join(&filename)?));
107
108                *ModuleResolveResult::module(ResolvedVc::upcast(
109                    WebpackModuleAsset::new(source, *self.runtime, *self.transforms)
110                        .to_resolved()
111                        .await?,
112                ))
113            }
114            WebpackRuntime::None => *ModuleResolveResult::unresolvable(),
115        })
116    }
117}
118
119#[turbo_tasks::value_impl]
120impl ValueToString for WebpackChunkAssetReference {
121    #[turbo_tasks::function]
122    fn to_string(&self) -> Vc<RcStr> {
123        let chunk_id = match &self.chunk_id {
124            Lit::Str(str) => str.value.to_string_lossy().into_owned(),
125            Lit::Num(num) => format!("{num}"),
126            _ => todo!(),
127        };
128        Vc::cell(format!("webpack chunk {chunk_id}").into())
129    }
130}
131
132#[turbo_tasks::value(shared)]
133pub struct WebpackEntryAssetReference {
134    pub source: ResolvedVc<Box<dyn Source>>,
135    pub runtime: ResolvedVc<WebpackRuntime>,
136    pub transforms: ResolvedVc<EcmascriptInputTransforms>,
137}
138
139#[turbo_tasks::value_impl]
140impl ModuleReference for WebpackEntryAssetReference {
141    #[turbo_tasks::function]
142    async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
143        Ok(*ModuleResolveResult::module(ResolvedVc::upcast(
144            WebpackModuleAsset::new(*self.source, *self.runtime, *self.transforms)
145                .to_resolved()
146                .await?,
147        )))
148    }
149}
150
151#[turbo_tasks::value_impl]
152impl ValueToString for WebpackEntryAssetReference {
153    #[turbo_tasks::function]
154    fn to_string(&self) -> Vc<RcStr> {
155        Vc::cell(rcstr!("webpack entry"))
156    }
157}
158
159#[turbo_tasks::value(shared)]
160pub struct WebpackRuntimeAssetReference {
161    pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
162    pub request: ResolvedVc<Request>,
163    pub runtime: ResolvedVc<WebpackRuntime>,
164    pub transforms: ResolvedVc<EcmascriptInputTransforms>,
165}
166
167#[turbo_tasks::value_impl]
168impl ModuleReference for WebpackRuntimeAssetReference {
169    #[turbo_tasks::function]
170    async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
171        let ty = ReferenceType::CommonJs(CommonJsReferenceSubType::Undefined);
172        let options = self.origin.resolve_options(ty.clone());
173
174        let options = apply_cjs_specific_options(options);
175
176        let resolved = resolve(
177            self.origin.origin_path().await?.parent(),
178            ReferenceType::CommonJs(CommonJsReferenceSubType::Undefined),
179            *self.request,
180            options,
181        );
182
183        Ok(resolved
184            .await?
185            .map_module(|source| async move {
186                Ok(ModuleResolveResultItem::Module(ResolvedVc::upcast(
187                    WebpackModuleAsset::new(*source, *self.runtime, *self.transforms)
188                        .to_resolved()
189                        .await?,
190                )))
191            })
192            .await?
193            .cell())
194    }
195}
196
197#[turbo_tasks::value_impl]
198impl ValueToString for WebpackRuntimeAssetReference {
199    #[turbo_tasks::function]
200    async fn to_string(&self) -> Result<Vc<RcStr>> {
201        Ok(Vc::cell(
202            format!("webpack {}", self.request.to_string().await?,).into(),
203        ))
204    }
205}