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,
10    reference::{ModuleReference, ModuleReferences},
11    reference_type::{CommonJsReferenceSubType, ReferenceType},
12    resolve::{
13        ModuleResolveResult, ModuleResolveResultItem,
14        origin::{ResolveOrigin, ResolveOriginExt},
15        parse::Request,
16        resolve,
17    },
18    source::Source,
19};
20use turbopack_resolve::ecmascript::apply_cjs_specific_options;
21
22use self::{parse::WebpackRuntime, references::module_references};
23use crate::EcmascriptInputTransforms;
24
25pub mod parse;
26pub(crate) mod references;
27
28#[turbo_tasks::value]
29pub struct WebpackModuleAsset {
30    pub source: ResolvedVc<Box<dyn Source>>,
31    pub runtime: ResolvedVc<WebpackRuntime>,
32    pub transforms: ResolvedVc<EcmascriptInputTransforms>,
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    ) -> Vc<Self> {
43        Self::cell(WebpackModuleAsset {
44            source,
45            runtime,
46            transforms,
47        })
48    }
49}
50
51#[turbo_tasks::value_impl]
52impl Module for WebpackModuleAsset {
53    #[turbo_tasks::function]
54    fn ident(&self) -> Vc<AssetIdent> {
55        self.source.ident().with_modifier(rcstr!("webpack"))
56    }
57
58    #[turbo_tasks::function]
59    fn references(&self) -> Vc<ModuleReferences> {
60        module_references(*self.source, *self.runtime, *self.transforms)
61    }
62}
63
64#[turbo_tasks::value_impl]
65impl Asset for WebpackModuleAsset {
66    #[turbo_tasks::function]
67    fn content(&self) -> Vc<AssetContent> {
68        self.source.content()
69    }
70}
71
72#[turbo_tasks::value(shared)]
73pub struct WebpackChunkAssetReference {
74    #[turbo_tasks(trace_ignore)]
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(),
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(),
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 ty = ReferenceType::CommonJs(CommonJsReferenceSubType::Undefined);
163        let options = self.origin.resolve_options(ty.clone()).await?;
164
165        let options = apply_cjs_specific_options(options);
166
167        let resolved = resolve(
168            self.origin.origin_path().await?.parent(),
169            ReferenceType::CommonJs(CommonJsReferenceSubType::Undefined),
170            *self.request,
171            options,
172        );
173
174        Ok(resolved
175            .await?
176            .map_module(|source| async move {
177                Ok(ModuleResolveResultItem::Module(ResolvedVc::upcast(
178                    WebpackModuleAsset::new(*source, *self.runtime, *self.transforms)
179                        .to_resolved()
180                        .await?,
181                )))
182            })
183            .await?
184            .cell())
185    }
186}
187
188#[turbo_tasks::value_impl]
189impl ValueToString for WebpackRuntimeAssetReference {
190    #[turbo_tasks::function]
191    async fn to_string(&self) -> Result<Vc<RcStr>> {
192        Ok(Vc::cell(
193            format!("webpack {}", self.request.to_string().await?,).into(),
194        ))
195    }
196}