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