Skip to main content

turbopack_static/
ecma.rs

1use anyhow::Result;
2use turbo_rcstr::{RcStr, rcstr};
3use turbo_tasks::{ResolvedVc, Vc};
4use turbopack_core::{
5    chunk::{AssetSuffix, AsyncModuleInfo, ChunkableModule, ChunkingContext},
6    ident::AssetIdent,
7    module::{Module, ModuleSideEffects},
8    module_graph::ModuleGraph,
9    output::{OutputAsset, OutputAssetsWithReferenced},
10    source::Source,
11};
12use turbopack_ecmascript::{
13    chunk::{
14        EcmascriptChunkItemContent, EcmascriptChunkPlaceable, EcmascriptExports,
15        ecmascript_chunk_item,
16    },
17    runtime_functions::{TURBOPACK_EXPORT_URL, TURBOPACK_EXPORT_VALUE},
18    utils::StringifyJs,
19};
20
21use crate::output_asset::StaticOutputAsset;
22
23#[turbo_tasks::value]
24#[derive(Clone)]
25pub struct StaticUrlJsModule {
26    pub source: ResolvedVc<Box<dyn Source>>,
27    pub tag: Option<RcStr>,
28}
29
30#[turbo_tasks::value_impl]
31impl StaticUrlJsModule {
32    #[turbo_tasks::function]
33    pub fn new(source: ResolvedVc<Box<dyn Source>>, tag: Option<RcStr>) -> Vc<Self> {
34        Self::cell(StaticUrlJsModule { source, tag })
35    }
36
37    #[turbo_tasks::function]
38    fn static_output_asset(
39        &self,
40        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
41    ) -> Vc<StaticOutputAsset> {
42        StaticOutputAsset::new(*chunking_context, *self.source, self.tag.clone())
43    }
44}
45
46#[turbo_tasks::value_impl]
47impl Module for StaticUrlJsModule {
48    #[turbo_tasks::function]
49    fn ident(&self) -> Vc<AssetIdent> {
50        let mut ident = self
51            .source
52            .ident()
53            .with_modifier(rcstr!("static in ecmascript"));
54        if let Some(tag) = &self.tag {
55            ident = ident.with_modifier(format!("tag {}", tag).into());
56        }
57        ident
58    }
59
60    #[turbo_tasks::function]
61    fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
62        Vc::cell(Some(self.source))
63    }
64
65    #[turbo_tasks::function]
66    fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
67        ModuleSideEffects::SideEffectFree.cell()
68    }
69}
70
71#[turbo_tasks::value_impl]
72impl ChunkableModule for StaticUrlJsModule {
73    #[turbo_tasks::function]
74    fn as_chunk_item(
75        self: ResolvedVc<Self>,
76        module_graph: ResolvedVc<ModuleGraph>,
77        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
78    ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
79        ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
80    }
81}
82
83#[turbo_tasks::value_impl]
84impl EcmascriptChunkPlaceable for StaticUrlJsModule {
85    #[turbo_tasks::function]
86    fn get_exports(&self) -> Vc<EcmascriptExports> {
87        EcmascriptExports::Value.cell()
88    }
89
90    #[turbo_tasks::function]
91    async fn chunk_item_content(
92        self: Vc<Self>,
93        chunking_context: Vc<Box<dyn ChunkingContext>>,
94        _module_graph: Vc<ModuleGraph>,
95        _async_module_info: Option<Vc<AsyncModuleInfo>>,
96        _estimated: bool,
97    ) -> Result<Vc<EcmascriptChunkItemContent>> {
98        let this = self.await?;
99        let static_asset = self.static_output_asset(chunking_context);
100        let url = chunking_context
101            .asset_url(static_asset.path().owned().await?, this.tag.clone())
102            .await?;
103
104        let url_behavior = chunking_context.url_behavior(this.tag.clone()).await?;
105
106        let inner_code = match &url_behavior.suffix {
107            AssetSuffix::None => {
108                // No suffix, export as-is
109                format!(
110                    "{TURBOPACK_EXPORT_VALUE}({path});",
111                    path = StringifyJs(&url)
112                )
113            }
114            AssetSuffix::Constant(suffix) => {
115                // Append constant suffix
116                format!(
117                    "{TURBOPACK_EXPORT_VALUE}({path} + {suffix});",
118                    path = StringifyJs(&url),
119                    suffix = StringifyJs(suffix.as_str())
120                )
121            }
122            AssetSuffix::Inferred => {
123                // The runtime logic will infer the suffix
124                format!("{TURBOPACK_EXPORT_URL}({path});", path = StringifyJs(&url))
125            }
126            AssetSuffix::FromGlobal(global_name) => {
127                // Read suffix from global at runtime
128                format!(
129                    "{TURBOPACK_EXPORT_VALUE}({path} + (globalThis[{global}] || ''));",
130                    path = StringifyJs(&url),
131                    global = StringifyJs(global_name)
132                )
133            }
134        };
135
136        Ok(EcmascriptChunkItemContent {
137            inner_code: inner_code.into(),
138            ..Default::default()
139        }
140        .cell())
141    }
142
143    #[turbo_tasks::function]
144    fn chunk_item_output_assets(
145        self: Vc<Self>,
146        chunking_context: Vc<Box<dyn ChunkingContext>>,
147        _module_graph: Vc<ModuleGraph>,
148    ) -> Vc<OutputAssetsWithReferenced> {
149        static_url_js_output_assets(self, chunking_context)
150    }
151}
152
153#[turbo_tasks::function]
154async fn static_url_js_output_assets(
155    module: Vc<StaticUrlJsModule>,
156    chunking_context: Vc<Box<dyn ChunkingContext>>,
157) -> Result<Vc<OutputAssetsWithReferenced>> {
158    let static_asset = module
159        .static_output_asset(chunking_context)
160        .to_resolved()
161        .await?;
162    Ok(OutputAssetsWithReferenced::from_assets(Vc::cell(vec![
163        ResolvedVc::upcast(static_asset),
164    ])))
165}