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 format!(
110 "{TURBOPACK_EXPORT_VALUE}({path});",
111 path = StringifyJs(&url)
112 )
113 }
114 AssetSuffix::Constant(suffix) => {
115 format!(
117 "{TURBOPACK_EXPORT_VALUE}({path} + {suffix});",
118 path = StringifyJs(&url),
119 suffix = StringifyJs(suffix.as_str())
120 )
121 }
122 AssetSuffix::Inferred => {
123 format!("{TURBOPACK_EXPORT_URL}({path});", path = StringifyJs(&url))
125 }
126 AssetSuffix::FromGlobal(global_name) => {
127 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}