1use std::{any::Any, collections::HashMap, hash::Hash, sync::Arc};
2
3use farmfe_macro_cache_item::cache_item;
4use serde::{Deserialize, Serialize};
5
6use crate::{
7 config::Config,
8 context::CompilationContext,
9 error::Result,
10 module::{
11 module_graph::ModuleGraph, module_group::ModuleGroupGraph, Module, ModuleId, ModuleMetaData,
12 ModuleType,
13 },
14 resource::{
15 resource_pot::{ResourcePot, ResourcePotInfo, ResourcePotMetaData},
16 Resource, ResourceType,
17 },
18 stats::Stats,
19};
20
21pub mod constants;
22pub mod plugin_driver;
23
24pub const DEFAULT_PRIORITY: i32 = 100;
25
26pub trait Plugin: Any + Send + Sync {
27 fn name(&self) -> &str;
28
29 fn priority(&self) -> i32 {
30 DEFAULT_PRIORITY
31 }
32
33 fn config(&self, _config: &mut Config) -> Result<Option<()>> {
34 Ok(None)
35 }
36
37 fn plugin_cache_loaded(
38 &self,
39 _cache: &Vec<u8>,
40 _context: &Arc<CompilationContext>,
41 ) -> Result<Option<()>> {
42 Ok(None)
43 }
44
45 fn build_start(&self, _context: &Arc<CompilationContext>) -> Result<Option<()>> {
46 Ok(None)
47 }
48
49 fn resolve(
50 &self,
51 _param: &PluginResolveHookParam,
52 _context: &Arc<CompilationContext>,
53 _hook_context: &PluginHookContext,
54 ) -> Result<Option<PluginResolveHookResult>> {
55 Ok(None)
56 }
57
58 fn load(
59 &self,
60 _param: &PluginLoadHookParam,
61 _context: &Arc<CompilationContext>,
62 _hook_context: &PluginHookContext,
63 ) -> Result<Option<PluginLoadHookResult>> {
64 Ok(None)
65 }
66
67 fn transform(
68 &self,
69 _param: &PluginTransformHookParam,
70 _context: &Arc<CompilationContext>,
71 ) -> Result<Option<PluginTransformHookResult>> {
72 Ok(None)
73 }
74
75 fn parse(
76 &self,
77 _param: &PluginParseHookParam,
78 _context: &Arc<CompilationContext>,
79 _hook_context: &PluginHookContext,
80 ) -> Result<Option<ModuleMetaData>> {
81 Ok(None)
82 }
83
84 fn process_module(
85 &self,
86 _param: &mut PluginProcessModuleHookParam,
87 _context: &Arc<CompilationContext>,
88 ) -> Result<Option<()>> {
89 Ok(None)
90 }
91
92 fn analyze_deps(
93 &self,
94 _param: &mut PluginAnalyzeDepsHookParam,
95 _context: &Arc<CompilationContext>,
96 ) -> Result<Option<()>> {
97 Ok(None)
98 }
99
100 fn finalize_module(
101 &self,
102 _param: &mut PluginFinalizeModuleHookParam,
103 _context: &Arc<CompilationContext>,
104 ) -> Result<Option<()>> {
105 Ok(None)
106 }
107
108 fn build_end(&self, _context: &Arc<CompilationContext>) -> Result<Option<()>> {
110 Ok(None)
111 }
112
113 fn generate_start(&self, _context: &Arc<CompilationContext>) -> Result<Option<()>> {
114 Ok(None)
115 }
116
117 fn optimize_module_graph(
119 &self,
120 _module_graph: &mut ModuleGraph,
121 _context: &Arc<CompilationContext>,
122 ) -> Result<Option<()>> {
123 Ok(None)
124 }
125
126 fn analyze_module_graph(
128 &self,
129 _module_graph: &mut ModuleGraph,
130 _context: &Arc<CompilationContext>,
131 _hook_context: &PluginHookContext,
132 ) -> Result<Option<ModuleGroupGraph>> {
133 Ok(None)
134 }
135
136 fn partial_bundling(
138 &self,
139 _modules: &Vec<ModuleId>,
140 _context: &Arc<CompilationContext>,
141 _hook_context: &PluginHookContext,
142 ) -> Result<Option<Vec<ResourcePot>>> {
143 Ok(None)
144 }
145
146 fn process_resource_pots(
148 &self,
149 _resource_pots: &mut Vec<&mut ResourcePot>,
150 _context: &Arc<CompilationContext>,
151 ) -> Result<Option<()>> {
152 Ok(None)
153 }
154
155 fn render_start(
156 &self,
157 _config: &Config,
158 _context: &Arc<CompilationContext>,
159 ) -> Result<Option<()>> {
160 Ok(None)
161 }
162
163 fn render_resource_pot_modules(
164 &self,
165 _resource_pot: &ResourcePot,
166 _context: &Arc<CompilationContext>,
167 _hook_context: &PluginHookContext,
168 ) -> Result<Option<ResourcePotMetaData>> {
169 Ok(None)
170 }
171
172 fn render_resource_pot(
174 &self,
175 _resource_pot: &PluginRenderResourcePotHookParam,
176 _context: &Arc<CompilationContext>,
177 ) -> Result<Option<PluginRenderResourcePotHookResult>> {
178 Ok(None)
179 }
180
181 fn augment_resource_hash(
182 &self,
183 _render_pot_info: &ResourcePotInfo,
184 _context: &Arc<CompilationContext>,
185 ) -> Result<Option<String>> {
186 Ok(None)
187 }
188
189 fn optimize_resource_pot(
191 &self,
192 _resource: &mut ResourcePot,
193 _context: &Arc<CompilationContext>,
194 ) -> Result<Option<()>> {
195 Ok(None)
196 }
197
198 fn generate_resources(
200 &self,
201 _resource_pot: &mut ResourcePot,
202 _context: &Arc<CompilationContext>,
203 _hook_context: &PluginHookContext,
204 ) -> Result<Option<PluginGenerateResourcesHookResult>> {
205 Ok(None)
206 }
207
208 fn process_generated_resources(
210 &self,
211 _resources: &mut PluginGenerateResourcesHookResult,
212 _context: &Arc<CompilationContext>,
213 ) -> Result<Option<()>> {
214 Ok(None)
215 }
216
217 fn handle_entry_resource(
220 &self,
221 _resource: &mut PluginHandleEntryResourceHookParams,
222 _context: &Arc<CompilationContext>,
223 ) -> Result<Option<()>> {
224 Ok(None)
225 }
226
227 fn finalize_resources(
230 &self,
231 _param: &mut PluginFinalizeResourcesHookParams,
232 _context: &Arc<CompilationContext>,
233 ) -> Result<Option<()>> {
234 Ok(None)
235 }
236
237 fn generate_end(&self, _context: &Arc<CompilationContext>) -> Result<Option<()>> {
238 Ok(None)
239 }
240
241 fn finish(&self, _stat: &Stats, _context: &Arc<CompilationContext>) -> Result<Option<()>> {
242 Ok(None)
243 }
244
245 fn update_modules(
248 &self,
249 _params: &mut PluginUpdateModulesHookParams,
250 _context: &Arc<CompilationContext>,
251 ) -> Result<Option<()>> {
252 Ok(None)
253 }
254
255 fn module_graph_updated(
258 &self,
259 _param: &PluginModuleGraphUpdatedHookParams,
260 _context: &Arc<CompilationContext>,
261 ) -> Result<Option<()>> {
262 Ok(None)
263 }
264
265 fn update_finished(&self, _context: &Arc<CompilationContext>) -> Result<Option<()>> {
268 Ok(None)
269 }
270
271 fn handle_persistent_cached_module(
273 &self,
274 _module: &Module,
275 _context: &Arc<CompilationContext>,
276 ) -> Result<Option<bool>> {
277 Ok(None)
278 }
279
280 fn write_plugin_cache(&self, _context: &Arc<CompilationContext>) -> Result<Option<Vec<u8>>> {
281 Ok(None)
282 }
283}
284
285#[derive(Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize, Default)]
286#[serde(rename_all = "camelCase")]
287#[cache_item]
288pub enum ResolveKind {
289 Entry(String),
291 #[default]
293 Import,
294 ExportFrom,
296 DynamicImport,
298 Require,
300 CssAtImport,
302 CssUrl,
304 ScriptSrc,
306 LinkHref,
308 HmrUpdate,
310 Custom(String),
312}
313
314impl ResolveKind {
315 pub fn is_dynamic(&self) -> bool {
318 matches!(self, ResolveKind::DynamicImport)
319 || matches!(self, ResolveKind::Custom(c) if c.starts_with("dynamic:"))
320 }
321
322 pub fn is_export_from(&self) -> bool {
323 matches!(self, ResolveKind::ExportFrom)
324 }
325
326 pub fn is_require(&self) -> bool {
327 matches!(self, ResolveKind::Require)
328 }
329}
330
331impl From<&str> for ResolveKind {
332 fn from(value: &str) -> Self {
333 serde_json::from_str(value).unwrap()
334 }
335}
336
337impl From<ResolveKind> for String {
338 fn from(value: ResolveKind) -> Self {
339 serde_json::to_string(&value).unwrap()
340 }
341}
342
343#[derive(Debug, Clone, Serialize, Deserialize, Default)]
345pub struct PluginHookContext {
346 pub caller: Option<String>,
349 pub meta: HashMap<String, String>,
351}
352
353impl PluginHookContext {
354 fn caller_format<T: AsRef<str>>(name: T) -> String {
355 format!("[{}]", name.as_ref())
356 }
357
358 pub fn add_caller<T: AsRef<str>>(&self, name: T) -> Option<String> {
359 match self.caller.as_ref() {
360 Some(c) => Some(format!("{}{}", c, Self::caller_format(name))),
361 None => Some(Self::caller_format(name)),
362 }
363 }
364 pub fn contain_caller<T: AsRef<str>>(&self, name: T) -> bool {
365 if let Some(ref s) = self.caller {
366 s.contains(&Self::caller_format(name))
367 } else {
368 false
369 }
370 }
371}
372
373#[derive(Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq)]
375#[serde(rename_all = "camelCase")]
376pub struct PluginResolveHookParam {
377 pub source: String,
379 pub importer: Option<ModuleId>,
381 pub kind: ResolveKind,
383}
384
385#[derive(Debug, Serialize, Deserialize, Clone)]
386#[serde(rename_all = "camelCase", default)]
387pub struct PluginResolveHookResult {
388 pub resolved_path: String,
390 pub external: bool,
392 pub side_effects: bool,
395 pub query: Vec<(String, String)>,
399 pub meta: HashMap<String, String>,
401}
402
403impl Default for PluginResolveHookResult {
404 fn default() -> Self {
405 Self {
406 side_effects: true,
407 resolved_path: "unknown".to_string(),
408 external: false,
409 query: vec![],
410 meta: Default::default(),
411 }
412 }
413}
414
415#[derive(Debug, Clone, Serialize, Deserialize)]
416#[serde(rename_all = "camelCase")]
417pub struct PluginLoadHookParam<'a> {
418 pub module_id: String,
420 pub resolved_path: &'a str,
422 pub query: Vec<(String, String)>,
424 pub meta: HashMap<String, String>,
426}
427
428#[derive(Debug, Serialize, Deserialize)]
429#[serde(rename_all = "camelCase")]
430pub struct PluginLoadHookResult {
431 pub content: String,
433 pub module_type: ModuleType,
436 pub source_map: Option<String>,
438}
439
440#[derive(Debug, Clone, Serialize, Deserialize)]
441#[serde(rename_all = "camelCase")]
442pub struct PluginTransformHookParam<'a> {
443 pub module_id: String,
445 pub content: String,
447 pub module_type: ModuleType,
449 pub resolved_path: &'a str,
451 pub query: Vec<(String, String)>,
453 pub meta: HashMap<String, String>,
455 pub source_map_chain: Vec<Arc<String>>,
457}
458
459#[derive(Debug, Default, Serialize, Deserialize)]
460#[serde(rename_all = "camelCase", default)]
461pub struct PluginTransformHookResult {
462 pub content: String,
464 pub module_type: Option<ModuleType>,
466 pub source_map: Option<String>,
468 pub ignore_previous_source_map: bool,
470}
471
472#[derive(Debug, Serialize, Deserialize)]
473pub struct PluginParseHookParam {
474 pub module_id: ModuleId,
476 pub resolved_path: String,
478 pub query: Vec<(String, String)>,
480 pub module_type: ModuleType,
481 pub content: Arc<String>,
483}
484
485pub struct PluginProcessModuleHookParam<'a> {
486 pub module_id: &'a ModuleId,
487 pub module_type: &'a ModuleType,
488 pub content: Arc<String>,
489 pub meta: &'a mut ModuleMetaData,
490}
491
492#[derive(Clone)]
493pub struct PluginAnalyzeDepsHookParam<'a> {
494 pub module: &'a Module,
495 pub deps: Vec<PluginAnalyzeDepsHookResultEntry>,
497}
498
499#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize, serde::Deserialize)]
500#[cache_item]
501pub struct PluginAnalyzeDepsHookResultEntry {
502 pub source: String,
503 pub kind: ResolveKind,
504}
505
506pub struct PluginFinalizeModuleHookParam<'a> {
507 pub module: &'a mut Module,
508 pub deps: &'a Vec<PluginAnalyzeDepsHookResultEntry>,
509}
510
511#[derive(Default, Debug, serde::Serialize, serde::Deserialize, Clone)]
512pub struct WatchDiffResult {
513 pub add: Vec<String>,
514 pub remove: Vec<String>,
515}
516
517#[derive(Debug, Clone, Default, Serialize, Deserialize)]
519#[serde(rename_all = "camelCase", default)]
520pub struct UpdateResult {
521 pub added_module_ids: Vec<ModuleId>,
522 pub updated_module_ids: Vec<ModuleId>,
523 pub removed_module_ids: Vec<ModuleId>,
524 pub immutable_resources: String,
527 pub mutable_resources: String,
528 pub boundaries: HashMap<String, Vec<Vec<String>>>,
529 pub dynamic_resources_map: Option<HashMap<ModuleId, Vec<(String, ResourceType)>>>,
530 pub extra_watch_result: WatchDiffResult,
531}
532#[derive(Debug, Clone, Serialize, Deserialize)]
533pub enum UpdateType {
534 Added,
536 Updated,
538 Removed,
540}
541
542#[derive(Debug, Clone, Serialize, Deserialize)]
543#[serde(rename_all = "camelCase", default)]
544pub struct PluginUpdateModulesHookParams {
545 pub paths: Vec<(String, UpdateType)>,
546}
547
548#[derive(Debug, Clone, Serialize, Deserialize)]
549#[serde(rename_all = "camelCase", default)]
550pub struct PluginModuleGraphUpdatedHookParams {
551 pub added_modules_ids: Vec<ModuleId>,
552 pub removed_modules_ids: Vec<ModuleId>,
553 pub updated_modules_ids: Vec<ModuleId>,
554}
555
556#[derive(Debug, Serialize, Deserialize)]
557pub struct EmptyPluginHookParam {}
558
559#[derive(Debug, Serialize, Deserialize)]
560pub struct EmptyPluginHookResult {}
561
562#[cache_item]
563#[derive(Debug, Clone, Serialize, Deserialize)]
564#[serde(rename_all = "camelCase")]
565pub struct PluginGenerateResourcesHookResult {
566 pub resource: Resource,
567 pub source_map: Option<Resource>,
568}
569
570#[derive(Debug, Clone, Serialize, Deserialize)]
571#[serde(rename_all = "camelCase")]
572pub struct PluginRenderResourcePotHookParam {
573 pub content: Arc<String>,
574 pub source_map_chain: Vec<Arc<String>>,
575 pub resource_pot_info: ResourcePotInfo,
576}
577
578#[derive(Debug, Serialize, Deserialize)]
579pub struct PluginRenderResourcePotHookResult {
580 pub content: String,
581 pub source_map: Option<String>,
582}
583
584pub struct PluginDriverRenderResourcePotHookResult {
585 pub content: Arc<String>,
586 pub source_map_chain: Vec<Arc<String>>,
587}
588
589pub struct PluginFinalizeResourcesHookParams<'a> {
590 pub resources_map: &'a mut HashMap<String, Resource>,
591 pub config: &'a Config,
592}
593
594pub struct PluginHandleEntryResourceHookParams<'a> {
595 pub resource: &'a mut Resource,
596 pub module_graph: &'a ModuleGraph,
597 pub module_group_graph: &'a ModuleGroupGraph,
598 pub entry_module_id: &'a ModuleId,
599}