farmfe_plugin_polyfill/
lib.rs

1use std::path::PathBuf;
2
3use farmfe_core::{
4  config::{
5    config_regex::ConfigRegex,
6    preset_env::{PresetEnvConfig, PresetEnvConfigObj},
7    Config,
8  },
9  plugin::Plugin,
10  serde_json,
11  swc_common::{comments::SingleThreadedComments, Mark},
12};
13use farmfe_toolkit::{
14  common::{create_swc_source_map, Source},
15  preset_env_base::query::Query,
16  script::swc_try_with::try_with,
17  swc_ecma_preset_env::{self, preset_env, Mode, Targets},
18  swc_ecma_transforms::Assumptions,
19  swc_ecma_transforms_base::{feature::FeatureFlag, helpers::inject_helpers},
20  swc_ecma_visit::{FoldWith, VisitMutWith},
21};
22
23pub struct FarmPluginPolyfill {
24  config: swc_ecma_preset_env::Config,
25  include: Vec<ConfigRegex>,
26  exclude: Vec<ConfigRegex>,
27  enforce_exclude: Vec<ConfigRegex>,
28  assumptions: Assumptions,
29}
30
31impl FarmPluginPolyfill {
32  pub fn new(config: &Config) -> Self {
33    let (config, include, exclude, assumptions) = match &*config.preset_env {
34      PresetEnvConfig::Bool(_) => {
35        let PresetEnvConfigObj {
36          include,
37          exclude,
38          options: _,
39          assumptions: _,
40        } = PresetEnvConfigObj::default();
41
42        (
43          swc_ecma_preset_env::Config {
44            mode: Some(Mode::Usage),
45            targets: Some(Targets::Query(Query::Single("ie >= 9".to_string()))),
46            ..Default::default()
47          },
48          include,
49          exclude,
50          Default::default(),
51        )
52      }
53      PresetEnvConfig::Obj(obj) => {
54        let options = &obj.options;
55        let mut user_config: swc_ecma_preset_env::Config =
56          serde_json::from_value(*options.clone()).unwrap();
57        user_config.mode = user_config.mode.or(Some(Mode::Usage));
58        user_config.targets = user_config
59          .targets
60          .or(Some(Targets::Query(Query::Single("ie >= 9".to_string()))));
61        let user_assumption: Assumptions =
62          serde_json::from_value(*obj.assumptions.clone()).unwrap();
63        (
64          user_config,
65          obj.include.clone(),
66          obj.exclude.clone(),
67          user_assumption,
68        )
69      }
70    };
71
72    Self {
73      config,
74      include,
75      exclude,
76      assumptions,
77      enforce_exclude: vec![ConfigRegex::new("node_modules/core-js")],
78    }
79  }
80}
81
82impl Plugin for FarmPluginPolyfill {
83  fn name(&self) -> &str {
84    "FarmPluginPolyfill"
85  }
86  /// The polyfill plugin should run after all other plugins
87  fn priority(&self) -> i32 {
88    i32::MIN
89  }
90
91  fn process_module(
92    &self,
93    param: &mut farmfe_core::plugin::PluginProcessModuleHookParam,
94    context: &std::sync::Arc<farmfe_core::context::CompilationContext>,
95  ) -> farmfe_core::error::Result<Option<()>> {
96    if !param.module_type.is_script() {
97      return Ok(None);
98    }
99
100    // ignore node_modules by default
101    let relative_path = param.module_id.relative_path();
102
103    if !self.include.iter().any(|r| r.is_match(relative_path))
104      && self.exclude.iter().any(|r| r.is_match(relative_path))
105    {
106      return Ok(None);
107    }
108
109    if self
110      .enforce_exclude
111      .iter()
112      .any(|r| r.is_match(relative_path))
113    {
114      return Ok(None);
115    }
116
117    let (cm, _) = create_swc_source_map(Source {
118      path: PathBuf::from(&param.module_id.to_string()),
119      content: param.content.clone(),
120    });
121    try_with(cm, &context.meta.script.globals, || {
122      let unresolved_mark = Mark::from_u32(param.meta.as_script().unresolved_mark);
123      let mut ast = param.meta.as_script_mut().take_ast();
124
125      let mut feature_flag = FeatureFlag::empty();
126      let comments: SingleThreadedComments = param.meta.as_script().comments.clone().into();
127      ast = ast.fold_with(&mut preset_env(
128        unresolved_mark,
129        Some(&comments),
130        self.config.clone(),
131        self.assumptions,
132        &mut feature_flag,
133      ));
134      ast.visit_mut_with(&mut inject_helpers(unresolved_mark));
135
136      param.meta.as_script_mut().set_ast(ast);
137    })?;
138
139    Ok(Some(()))
140  }
141}