rolldown_plugin_utils/
to_output_file_path.rs1use std::{path::Path, pin::Pin};
2
3use itertools::Either;
4use sugar_path::SugarPath as _;
5
6use super::{join_url_segments, uri::encode_uri_path};
7
8pub type RenderBuiltUrl = dyn Fn(
9 &str,
10 &RenderBuiltUrlConfig,
11 ) -> Pin<
12 Box<dyn Future<Output = anyhow::Result<Option<Either<String, RenderBuiltUrlRet>>>> + Send>,
13 > + Send
14 + Sync;
15
16pub struct RenderBuiltUrlConfig<'a> {
17 pub is_ssr: bool,
18 pub r#type: &'a str,
19 pub host_id: &'a str,
20 pub host_type: &'a str,
21}
22
23pub struct RenderBuiltUrlRet {
24 pub relative: Option<bool>,
25 pub runtime: Option<String>,
26}
27
28pub enum AssetUrlResult {
29 WithRuntime(String),
30 WithoutRuntime(String),
31}
32
33impl AssetUrlResult {
34 pub fn to_asset_url_in_js(self) -> anyhow::Result<String> {
35 match self {
36 AssetUrlResult::WithRuntime(v) => Ok(rolldown_utils::concat_string!("\"+", v, "+\"")),
37 AssetUrlResult::WithoutRuntime(v) => {
38 let string = serde_json::to_string(&encode_uri_path(v))?;
39 Ok(string[1..string.len() - 1].to_owned())
40 }
41 }
42 }
43
44 pub fn to_asset_url_in_css_or_html(self) -> String {
45 match self {
46 AssetUrlResult::WithRuntime(_) => unreachable!("The asset url should not have a runtime"),
47 AssetUrlResult::WithoutRuntime(v) => v,
48 }
49 }
50}
51
52pub struct ToOutputFilePathEnv<'a> {
53 pub is_ssr: bool,
54 pub host_id: &'a str,
55 pub url_base: &'a str,
56 pub decoded_base: &'a str,
57 pub render_built_url: Option<&'a RenderBuiltUrl>,
58}
59
60impl ToOutputFilePathEnv<'_> {
61 pub async fn to_output_file_path(
62 &self,
63 filename: &str,
64 host_type: &str,
65 is_public_asset: bool,
66 to_relative: impl Fn(&Path, &Path) -> AssetUrlResult,
67 ) -> anyhow::Result<AssetUrlResult> {
68 let mut relative = self.url_base.is_empty() || self.url_base == "./";
69 if let Some(render_built_url) = self.render_built_url {
70 if let Some(result) = render_built_url(
71 filename,
72 &RenderBuiltUrlConfig {
73 is_ssr: self.is_ssr,
74 host_id: self.host_id,
75 r#type: if is_public_asset { "public" } else { "asset" },
76 host_type,
77 },
78 )
79 .await?
80 {
81 match result {
82 Either::Left(result) => return Ok(AssetUrlResult::WithoutRuntime(result)),
83 Either::Right(result) => {
84 if let Some(runtime) = result.runtime {
85 if matches!(host_type, "css" | "html") {
86 return Err(anyhow::anyhow!(
87 "The `{{ runtime: '{runtime}' }}` is not supported for assets in {host_type} files: {filename}"
88 ));
89 }
90 return Ok(AssetUrlResult::WithRuntime(runtime));
91 }
92 if let Some(r) = result.relative {
93 relative = r;
94 }
95 }
96 }
97 }
98 }
99 Ok(if relative && !self.is_ssr {
100 to_relative(filename.as_path(), self.host_id.as_path())
101 } else {
102 AssetUrlResult::WithoutRuntime(join_url_segments(self.decoded_base, filename).into_owned())
103 })
104 }
105}