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, EcmascriptChunkItemOptions, EcmascriptChunkPlaceable,
15        EcmascriptExports, 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    async fn ident(&self) -> Result<Vc<AssetIdent>> {
50        let mut ident = self
51            .source
52            .ident()
53            .owned()
54            .await?
55            .with_modifier(rcstr!("static in ecmascript"));
56        if let Some(tag) = &self.tag {
57            ident = ident.with_modifier(format!("tag {}", tag).into());
58        }
59        Ok(ident.into_vc())
60    }
61
62    #[turbo_tasks::function]
63    fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
64        Vc::cell(Some(self.source))
65    }
66
67    #[turbo_tasks::function]
68    fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
69        ModuleSideEffects::SideEffectFree.cell()
70    }
71}
72
73#[turbo_tasks::value_impl]
74impl ChunkableModule for StaticUrlJsModule {
75    #[turbo_tasks::function]
76    fn as_chunk_item(
77        self: ResolvedVc<Self>,
78        module_graph: ResolvedVc<ModuleGraph>,
79        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
80    ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
81        ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
82    }
83}
84
85#[turbo_tasks::value_impl]
86impl EcmascriptChunkPlaceable for StaticUrlJsModule {
87    #[turbo_tasks::function]
88    fn get_exports(&self) -> Vc<EcmascriptExports> {
89        EcmascriptExports::Value.cell()
90    }
91
92    #[turbo_tasks::function]
93    async fn chunk_item_content(
94        self: Vc<Self>,
95        chunking_context: Vc<Box<dyn ChunkingContext>>,
96        _module_graph: Vc<ModuleGraph>,
97        _async_module_info: Option<Vc<AsyncModuleInfo>>,
98        _estimated: bool,
99    ) -> Result<Vc<EcmascriptChunkItemContent>> {
100        let this = self.await?;
101        let static_asset = self.static_output_asset(chunking_context);
102        let url = chunking_context
103            .asset_url(static_asset.path().owned().await?, this.tag.clone())
104            .await?;
105
106        let url_behavior = chunking_context.url_behavior(this.tag.clone()).await?;
107
108        let inner_code = match &url_behavior.suffix {
109            AssetSuffix::None => {
110                // No suffix, export as-is
111                format!(
112                    "{TURBOPACK_EXPORT_VALUE}({path});",
113                    path = StringifyJs(&url)
114                )
115            }
116            AssetSuffix::Constant(suffix) => {
117                // Append constant suffix
118                format!(
119                    "{TURBOPACK_EXPORT_VALUE}({path} + {suffix});",
120                    path = StringifyJs(&url),
121                    suffix = StringifyJs(suffix.as_str())
122                )
123            }
124            AssetSuffix::Inferred => {
125                // The runtime logic will infer the suffix
126                format!("{TURBOPACK_EXPORT_URL}({path});", path = StringifyJs(&url))
127            }
128            AssetSuffix::FromGlobal(global_name) => {
129                // Read suffix from global at runtime
130                format!(
131                    "{TURBOPACK_EXPORT_VALUE}({path} + (globalThis[{global}] || ''));",
132                    path = StringifyJs(&url),
133                    global = StringifyJs(global_name)
134                )
135            }
136        };
137
138        Ok(EcmascriptChunkItemContent {
139            inner_code: inner_code.into(),
140            options: EcmascriptChunkItemOptions {
141                supports_arrow_functions: *chunking_context
142                    .environment()
143                    .runtime_versions()
144                    .supports_arrow_functions()
145                    .await?,
146                ..Default::default()
147            },
148            ..Default::default()
149        }
150        .cell())
151    }
152
153    #[turbo_tasks::function]
154    fn chunk_item_output_assets(
155        self: Vc<Self>,
156        chunking_context: Vc<Box<dyn ChunkingContext>>,
157        _module_graph: Vc<ModuleGraph>,
158    ) -> Vc<OutputAssetsWithReferenced> {
159        static_url_js_output_assets(self, chunking_context)
160    }
161}
162
163#[turbo_tasks::function]
164async fn static_url_js_output_assets(
165    module: Vc<StaticUrlJsModule>,
166    chunking_context: Vc<Box<dyn ChunkingContext>>,
167) -> Result<Vc<OutputAssetsWithReferenced>> {
168    let static_asset = module
169        .static_output_asset(chunking_context)
170        .to_resolved()
171        .await?;
172    Ok(OutputAssetsWithReferenced::from_assets(Vc::cell(vec![
173        ResolvedVc::upcast(static_asset),
174    ])))
175}