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