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)]
72#[derive(ValueToString)]
73#[value_to_string("webpack chunk {}", self.chunk_id())]
74pub struct WebpackChunkAssetReference {
75    #[turbo_tasks(trace_ignore)]
76    #[bincode(with_serde)]
77    pub chunk_id: Lit,
78    pub runtime: ResolvedVc<WebpackRuntime>,
79    pub transforms: ResolvedVc<EcmascriptInputTransforms>,
80}
81
82impl WebpackChunkAssetReference {
83    fn chunk_id(&self) -> RcStr {
84        match &self.chunk_id {
85            Lit::Str(s) => RcStr::from(s.value.to_string_lossy().to_string()),
86            Lit::Num(n) => RcStr::from(n.to_string()),
87            _ => panic!("Unexpected literal type"),
88        }
89    }
90}
91
92#[turbo_tasks::value_impl]
93impl ModuleReference for WebpackChunkAssetReference {
94    #[turbo_tasks::function]
95    async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
96        let runtime = self.runtime.await?;
97        Ok(match &*runtime {
98            WebpackRuntime::Webpack5 {
99                chunk_request_expr: _,
100                context_path,
101            } => {
102                // TODO determine filename from chunk_request_expr
103                let chunk_id = match &self.chunk_id {
104                    Lit::Str(str) => str.value.to_string_lossy().into_owned(),
105                    Lit::Num(num) => format!("{num}"),
106                    _ => todo!(),
107                };
108                let filename = format!("./chunks/{chunk_id}.js");
109                let source = Vc::upcast(FileSource::new(context_path.join(&filename)?));
110
111                *ModuleResolveResult::module(ResolvedVc::upcast(
112                    WebpackModuleAsset::new(source, *self.runtime, *self.transforms)
113                        .to_resolved()
114                        .await?,
115                ))
116            }
117            WebpackRuntime::None => *ModuleResolveResult::unresolvable(),
118        })
119    }
120}
121
122#[turbo_tasks::value(shared)]
123#[derive(ValueToString)]
124#[value_to_string("webpack entry")]
125pub struct WebpackEntryAssetReference {
126    pub source: ResolvedVc<Box<dyn Source>>,
127    pub runtime: ResolvedVc<WebpackRuntime>,
128    pub transforms: ResolvedVc<EcmascriptInputTransforms>,
129}
130
131#[turbo_tasks::value_impl]
132impl ModuleReference for WebpackEntryAssetReference {
133    #[turbo_tasks::function]
134    async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
135        Ok(*ModuleResolveResult::module(ResolvedVc::upcast(
136            WebpackModuleAsset::new(*self.source, *self.runtime, *self.transforms)
137                .to_resolved()
138                .await?,
139        )))
140    }
141}
142
143#[turbo_tasks::value(shared)]
144#[derive(ValueToString)]
145#[value_to_string("webpack {request}")]
146pub struct WebpackRuntimeAssetReference {
147    pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
148    pub request: ResolvedVc<Request>,
149    pub runtime: ResolvedVc<WebpackRuntime>,
150    pub transforms: ResolvedVc<EcmascriptInputTransforms>,
151}
152
153#[turbo_tasks::value_impl]
154impl ModuleReference for WebpackRuntimeAssetReference {
155    #[turbo_tasks::function]
156    async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
157        let options = self.origin.resolve_options();
158
159        let options = apply_cjs_specific_options(options);
160
161        let resolved = resolve(
162            self.origin.origin_path().await?.parent(),
163            ReferenceType::CommonJs(CommonJsReferenceSubType::Undefined),
164            *self.request,
165            options,
166        );
167
168        Ok(resolved
169            .await?
170            .map_module(|source| async move {
171                Ok(ModuleResolveResultItem::Module(ResolvedVc::upcast(
172                    WebpackModuleAsset::new(*source, *self.runtime, *self.transforms)
173                        .to_resolved()
174                        .await?,
175                )))
176            })
177            .await?
178            .cell())
179    }
180}