rspack_plugin_css/dependency/
url.rs1use cow_utils::CowUtils;
2use rspack_cacheable::{cacheable, cacheable_dyn};
3use rspack_core::{
4 AsContextDependency, CodeGenerationDataFilename, CodeGenerationDataUrl, Compilation, Dependency,
5 DependencyCategory, DependencyCodeGeneration, DependencyId, DependencyRange, DependencyTemplate,
6 DependencyTemplateType, DependencyType, FactorizeInfo, ModuleDependency, ModuleIdentifier,
7 TemplateContext, TemplateReplaceSource,
8};
9
10use crate::utils::{AUTO_PUBLIC_PATH_PLACEHOLDER, css_escape_string};
11
12#[cacheable]
13#[derive(Debug, Clone)]
14pub struct CssUrlDependency {
15 id: DependencyId,
16 request: String,
17 range: DependencyRange,
18 replace_function: bool,
19 factorize_info: FactorizeInfo,
20}
21
22impl CssUrlDependency {
23 pub fn new(request: String, range: DependencyRange, replace_function: bool) -> Self {
24 Self {
25 request,
26 range,
27 id: DependencyId::new(),
28 replace_function,
29 factorize_info: Default::default(),
30 }
31 }
32
33 fn get_target_url(
34 &self,
35 identifier: &ModuleIdentifier,
36 compilation: &Compilation,
37 ) -> Option<String> {
38 let code_gen_result = compilation.code_generation_results.get_one(identifier);
40
41 if let Some(url) = code_gen_result.data.get::<CodeGenerationDataUrl>() {
42 Some(url.inner().to_string())
43 } else if let Some(data) = code_gen_result.data.get::<CodeGenerationDataFilename>() {
44 let filename = data.filename();
45 let public_path = data.public_path().cow_replace(
46 "__RSPACK_PLUGIN_ASSET_AUTO_PUBLIC_PATH__",
47 AUTO_PUBLIC_PATH_PLACEHOLDER,
48 );
49 Some(format!("{public_path}{filename}"))
50 } else {
51 None
52 }
53 }
54}
55
56#[cacheable_dyn]
57impl Dependency for CssUrlDependency {
58 fn id(&self) -> &DependencyId {
59 &self.id
60 }
61
62 fn category(&self) -> &DependencyCategory {
63 &DependencyCategory::Url
64 }
65
66 fn dependency_type(&self) -> &DependencyType {
67 &DependencyType::CssUrl
68 }
69
70 fn range(&self) -> Option<DependencyRange> {
71 Some(self.range)
72 }
73
74 fn could_affect_referencing_module(&self) -> rspack_core::AffectType {
75 rspack_core::AffectType::True
76 }
77}
78
79#[cacheable_dyn]
80impl ModuleDependency for CssUrlDependency {
81 fn request(&self) -> &str {
82 &self.request
83 }
84
85 fn user_request(&self) -> &str {
86 &self.request
87 }
88
89 fn factorize_info(&self) -> &FactorizeInfo {
90 &self.factorize_info
91 }
92
93 fn factorize_info_mut(&mut self) -> &mut FactorizeInfo {
94 &mut self.factorize_info
95 }
96}
97
98#[cacheable_dyn]
99impl DependencyCodeGeneration for CssUrlDependency {
100 fn dependency_template(&self) -> Option<DependencyTemplateType> {
101 Some(CssUrlDependencyTemplate::template_type())
102 }
103}
104
105impl AsContextDependency for CssUrlDependency {}
106
107#[cacheable]
108#[derive(Debug, Clone, Default)]
109pub struct CssUrlDependencyTemplate;
110
111impl CssUrlDependencyTemplate {
112 pub fn template_type() -> DependencyTemplateType {
113 DependencyTemplateType::Dependency(DependencyType::CssUrl)
114 }
115}
116
117impl DependencyTemplate for CssUrlDependencyTemplate {
118 fn render(
119 &self,
120 dep: &dyn DependencyCodeGeneration,
121 source: &mut TemplateReplaceSource,
122 code_generatable_context: &mut TemplateContext,
123 ) {
124 let dep = dep
125 .as_any()
126 .downcast_ref::<CssUrlDependency>()
127 .expect("CssUrlDependencyTemplate should be used for CssUrlDependency");
128
129 let TemplateContext { compilation, .. } = code_generatable_context;
130 if let Some(mgm) = compilation
131 .get_module_graph()
132 .module_graph_module_by_dependency_id(dep.id())
133 && let Some(target_url) = dep.get_target_url(&mgm.module_identifier, compilation)
134 {
135 let target_url = css_escape_string(&target_url);
136 let content = if dep.replace_function {
137 format!("url({target_url})")
138 } else {
139 target_url
140 };
141 source.replace(dep.range.start, dep.range.end, &content, None);
142 }
143 }
144}