next_custom_transforms/transforms/fonts/
mod.rs1use rustc_hash::{FxHashMap, FxHashSet};
2use serde::Deserialize;
3use swc_core::{
4 atoms::Wtf8Atom,
5 common::{BytePos, Spanned},
6 ecma::{
7 ast::{Id, ModuleItem, Pass},
8 atoms::Atom,
9 visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitWith},
10 },
11};
12
13mod find_functions_outside_module_scope;
14mod font_functions_collector;
15mod font_imports_generator;
16
17#[derive(Clone, Debug, Deserialize)]
18#[serde(deny_unknown_fields, rename_all = "camelCase")]
19pub struct Config {
20 pub font_loaders: Vec<Wtf8Atom>,
21 pub relative_file_path_from_root: Atom,
22}
23
24pub fn next_font_loaders(config: Config) -> impl Pass + VisitMut {
25 visit_mut_pass(NextFontLoaders {
26 config,
27 state: State {
28 ..Default::default()
29 },
30 })
31}
32
33#[derive(Debug)]
34pub struct FontFunction {
35 loader: Wtf8Atom,
36 function_name: Option<Atom>,
37}
38#[derive(Debug, Default)]
39pub struct State {
40 font_functions: FxHashMap<Id, FontFunction>,
41 removable_module_items: FxHashSet<BytePos>,
42 font_imports: Vec<ModuleItem>,
43 font_exports: Vec<ModuleItem>,
44 font_functions_in_allowed_scope: FxHashSet<BytePos>,
45}
46
47struct NextFontLoaders {
48 config: Config,
49 state: State,
50}
51
52impl VisitMut for NextFontLoaders {
53 noop_visit_mut_type!();
54
55 fn visit_mut_module_items(&mut self, items: &mut Vec<ModuleItem>) {
56 let mut functions_collector = font_functions_collector::FontFunctionsCollector {
58 font_loaders: &self.config.font_loaders,
59 state: &mut self.state,
60 };
61 items.visit_with(&mut functions_collector);
62
63 if !self.state.removable_module_items.is_empty() {
64 let mut import_generator = font_imports_generator::FontImportsGenerator {
66 state: &mut self.state,
67 relative_path: &self.config.relative_file_path_from_root,
68 };
69 items.visit_with(&mut import_generator);
70
71 let mut wrong_scope =
73 find_functions_outside_module_scope::FindFunctionsOutsideModuleScope {
74 state: &self.state,
75 };
76 items.visit_with(&mut wrong_scope);
77
78 fn is_removable(ctx: &NextFontLoaders, item: &ModuleItem) -> bool {
79 ctx.state.removable_module_items.contains(&item.span_lo())
80 }
81
82 let first_removable_index = items
83 .iter()
84 .position(|item| is_removable(self, item))
85 .unwrap();
86
87 items.retain(|item| !is_removable(self, item));
89
90 items.splice(
92 first_removable_index..first_removable_index,
93 std::mem::take(&mut self.state.font_imports),
94 );
95 items.append(&mut self.state.font_exports);
96 }
97 }
98}