1use std::{
2 collections::{HashMap, HashSet},
3 env,
4 path::{Path, PathBuf},
5 sync::Arc,
6};
7
8use anyhow::{bail, Context, Error};
9use dashmap::DashMap;
10use indexmap::IndexMap;
11use once_cell::sync::Lazy;
12use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
13use serde::{Deserialize, Serialize};
14use swc_atoms::Atom;
15use swc_cached::regex::CachedRegex;
16#[allow(unused)]
17use swc_common::plugin::metadata::TransformPluginMetadataContext;
18use swc_common::{
19 comments::{Comments, SingleThreadedComments},
20 errors::Handler,
21 FileName, Mark, SourceMap, SyntaxContext,
22};
23pub use swc_compiler_base::{IsModule, SourceMapsConfig};
24use swc_config::{
25 config_types::{BoolConfig, BoolOr, BoolOrDataConfig, MergingOption},
26 merge::Merge,
27};
28use swc_ecma_ast::{noop_pass, EsVersion, Expr, Pass, Program};
29use swc_ecma_ext_transforms::jest;
30use swc_ecma_lints::{
31 config::LintConfig,
32 rules::{lint_pass, LintParams},
33};
34use swc_ecma_loader::resolvers::{
35 lru::CachingResolver, node::NodeModulesResolver, tsc::TsConfigResolver,
36};
37pub use swc_ecma_minifier::js::*;
38use swc_ecma_minifier::option::terser::TerserTopLevelOptions;
39use swc_ecma_parser::{parse_file_as_expr, Syntax, TsSyntax};
40pub use swc_ecma_transforms::proposals::DecoratorVersion;
41use swc_ecma_transforms::{
42 feature::FeatureFlag,
43 hygiene,
44 modules::{
45 self,
46 path::{ImportResolver, NodeImportResolver, Resolver},
47 rewriter::import_rewriter,
48 util, EsModuleConfig,
49 },
50 optimization::{const_modules, json_parse, simplifier},
51 proposals::{
52 decorators, explicit_resource_management::explicit_resource_management,
53 export_default_from, import_attributes,
54 },
55 react::{self, default_pragma, default_pragma_frag},
56 resolver,
57 typescript::{self, TsImportExportAssignConfig},
58 Assumptions,
59};
60use swc_ecma_transforms_compat::es2015::regenerator;
61use swc_ecma_transforms_optimization::{
62 inline_globals2,
63 simplify::{dce::Config as DceConfig, Config as SimplifyConfig},
64 GlobalExprMap,
65};
66use swc_ecma_utils::NodeIgnoringSpan;
67use swc_ecma_visit::VisitMutWith;
68use swc_visit::Optional;
69
70pub use crate::plugin::PluginConfig;
71use crate::{
72 builder::PassBuilder, dropped_comments_preserver::dropped_comments_preserver, SwcImportResolver,
73};
74
75#[cfg(test)]
76mod tests;
77
78#[cfg(feature = "plugin")]
79pub static PLUGIN_MODULE_CACHE: Lazy<swc_plugin_runner::cache::PluginModuleCache> =
81 Lazy::new(Default::default);
82
83#[cfg(feature = "plugin")]
93pub fn init_plugin_module_cache_once(
94 enable_fs_cache_store: bool,
95 fs_cache_store_root: &Option<String>,
96) {
97 PLUGIN_MODULE_CACHE.inner.get_or_init(|| {
98 parking_lot::Mutex::new(swc_plugin_runner::cache::PluginModuleCache::create_inner(
99 enable_fs_cache_store,
100 fs_cache_store_root,
101 ))
102 });
103}
104
105#[derive(Default, Clone, Serialize, Deserialize)]
106#[serde(rename_all = "camelCase")]
107pub struct ParseOptions {
108 #[serde(default)]
109 pub comments: bool,
110 #[serde(flatten)]
111 pub syntax: Syntax,
112
113 #[serde(default)]
114 pub is_module: IsModule,
115
116 #[serde(default)]
117 pub target: EsVersion,
118}
119
120#[derive(Debug, Clone, Default, Deserialize)]
121#[serde(deny_unknown_fields, rename_all = "camelCase")]
122pub struct Options {
123 #[serde(flatten)]
124 pub config: Config,
125
126 #[serde(skip_deserializing, default)]
127 pub skip_helper_injection: bool,
128
129 #[serde(skip_deserializing, default)]
130 pub disable_hygiene: bool,
131
132 #[serde(skip_deserializing, default)]
133 pub disable_fixer: bool,
134
135 #[serde(skip_deserializing, default)]
136 pub top_level_mark: Option<Mark>,
137
138 #[serde(skip_deserializing, default)]
139 pub unresolved_mark: Option<Mark>,
140
141 #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))]
142 #[serde(default = "default_cwd")]
143 pub cwd: PathBuf,
144
145 #[serde(default)]
146 pub caller: Option<CallerOptions>,
147
148 #[serde(default)]
149 pub filename: String,
150
151 #[serde(default)]
152 pub config_file: Option<ConfigFile>,
153
154 #[serde(default)]
155 pub root: Option<PathBuf>,
156
157 #[serde(default)]
158 pub root_mode: RootMode,
159
160 #[serde(default = "default_swcrc")]
161 pub swcrc: bool,
162
163 #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))]
164 #[serde(default)]
165 pub swcrc_roots: Option<PathBuf>,
166
167 #[serde(default = "default_env_name")]
168 pub env_name: String,
169
170 #[serde(default)]
171 pub source_maps: Option<SourceMapsConfig>,
172
173 #[serde(default)]
174 pub source_file_name: Option<String>,
175
176 #[serde(default)]
177 pub source_root: Option<String>,
178
179 #[serde(default)]
180 pub output_path: Option<PathBuf>,
181
182 #[serde(default)]
183 pub experimental: ExperimentalOptions,
184}
185
186#[derive(Debug, Clone, Default, Deserialize, Merge)]
187#[serde(deny_unknown_fields, rename_all = "camelCase")]
188pub struct ExperimentalOptions {
189 #[serde(default)]
190 pub error_format: Option<ErrorFormat>,
191}
192
193impl Options {
194 pub fn codegen_target(&self) -> Option<EsVersion> {
195 self.config.jsc.target
196 }
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize)]
200#[serde(untagged)]
201pub enum InputSourceMap {
202 Bool(bool),
203 Str(String),
204}
205
206impl Default for InputSourceMap {
207 fn default() -> Self {
208 InputSourceMap::Bool(true)
209 }
210}
211
212impl Options {
213 #[allow(clippy::too_many_arguments)]
217 pub fn build_as_input<'a, P>(
218 &self,
219 cm: &Arc<SourceMap>,
220 base: &FileName,
221 parse: impl FnOnce(Syntax, EsVersion, IsModule) -> Result<Program, Error>,
222 output_path: Option<&Path>,
223 source_root: Option<String>,
224 source_file_name: Option<String>,
225 handler: &Handler,
226 config: Option<Config>,
227 comments: Option<&'a SingleThreadedComments>,
228 custom_before_pass: impl FnOnce(&Program) -> P,
229 ) -> Result<BuiltInput<Box<dyn 'a + Pass>>, Error>
230 where
231 P: 'a + Pass,
232 {
233 let mut cfg = self.config.clone();
234
235 cfg.merge(config.unwrap_or_default());
236
237 if let FileName::Real(base) = base {
238 cfg.adjust(base);
239 }
240
241 let is_module = cfg.is_module.unwrap_or_default();
242
243 let mut source_maps = self.source_maps.clone();
244 source_maps.merge(cfg.source_maps.clone());
245
246 let JscConfig {
247 assumptions,
248 transform,
249 syntax,
250 external_helpers,
251 target,
252 loose,
253 keep_class_names,
254 base_url,
255 paths,
256 minify: mut js_minify,
257 experimental,
258 lints,
259 preserve_all_comments,
260 ..
261 } = cfg.jsc;
262 let loose = loose.into_bool();
263 let preserve_all_comments = preserve_all_comments.into_bool();
264 let keep_class_names = keep_class_names.into_bool();
265 let external_helpers = external_helpers.into_bool();
266
267 let mut assumptions = assumptions.unwrap_or_else(|| {
268 if loose {
269 Assumptions::all()
270 } else {
271 Assumptions::default()
272 }
273 });
274
275 let unresolved_mark = self.unresolved_mark.unwrap_or_default();
276 let top_level_mark = self.top_level_mark.unwrap_or_default();
277
278 if target.is_some() && cfg.env.is_some() {
279 bail!("`env` and `jsc.target` cannot be used together");
280 }
281
282 let es_version = target.unwrap_or_default();
283
284 let syntax = syntax.unwrap_or_default();
285
286 let mut program = parse(syntax, es_version, is_module)?;
287
288 let mut transform = transform.into_inner().unwrap_or_default();
289
290 if syntax.typescript() {
295 assumptions.set_class_methods |= !transform.use_define_for_class_fields.into_bool();
296 }
297
298 assumptions.set_public_class_fields |= !transform.use_define_for_class_fields.into_bool();
299
300 program.visit_mut_with(&mut resolver(
301 unresolved_mark,
302 top_level_mark,
303 syntax.typescript(),
304 ));
305
306 let default_top_level = program.is_module();
307
308 js_minify = js_minify.map(|mut c| {
309 let compress = c
310 .compress
311 .unwrap_as_option(|default| match default {
312 Some(true) => Some(Default::default()),
313 _ => None,
314 })
315 .map(|mut c| {
316 if c.toplevel.is_none() {
317 c.toplevel = Some(TerserTopLevelOptions::Bool(default_top_level));
318 }
319
320 if matches!(
321 cfg.module,
322 None | Some(ModuleConfig::Es6(..) | ModuleConfig::NodeNext(..))
323 ) {
324 c.module = true;
325 }
326
327 c
328 })
329 .map(BoolOrDataConfig::from_obj)
330 .unwrap_or_else(|| BoolOrDataConfig::from_bool(false));
331
332 let mangle = c
333 .mangle
334 .unwrap_as_option(|default| match default {
335 Some(true) => Some(Default::default()),
336 _ => None,
337 })
338 .map(|mut c| {
339 if c.top_level.is_none() {
340 c.top_level = Some(default_top_level);
341 }
342
343 c
344 })
345 .map(BoolOrDataConfig::from_obj)
346 .unwrap_or_else(|| BoolOrDataConfig::from_bool(false));
347
348 if c.toplevel.is_none() {
349 c.toplevel = Some(default_top_level);
350 }
351
352 JsMinifyOptions {
353 compress,
354 mangle,
355 ..c
356 }
357 });
358
359 if js_minify.is_some() && js_minify.as_ref().unwrap().keep_fnames {
360 js_minify = js_minify.map(|c| {
361 let compress = c
362 .compress
363 .unwrap_as_option(|default| match default {
364 Some(true) => Some(Default::default()),
365 _ => None,
366 })
367 .map(|mut c| {
368 c.keep_fnames = true;
369 c
370 })
371 .map(BoolOrDataConfig::from_obj)
372 .unwrap_or_else(|| BoolOrDataConfig::from_bool(false));
373 let mangle = c
374 .mangle
375 .unwrap_as_option(|default| match default {
376 Some(true) => Some(Default::default()),
377 _ => None,
378 })
379 .map(|mut c| {
380 c.keep_fn_names = true;
381 c
382 })
383 .map(BoolOrDataConfig::from_obj)
384 .unwrap_or_else(|| BoolOrDataConfig::from_bool(false));
385 JsMinifyOptions {
386 compress,
387 mangle,
388 ..c
389 }
390 });
391 }
392
393 if js_minify.is_some() && js_minify.as_ref().unwrap().keep_classnames {
394 js_minify = js_minify.map(|c| {
395 let compress = c
396 .compress
397 .unwrap_as_option(|default| match default {
398 Some(true) => Some(Default::default()),
399 _ => None,
400 })
401 .map(|mut c| {
402 c.keep_classnames = true;
403 c
404 })
405 .map(BoolOrDataConfig::from_obj)
406 .unwrap_or_else(|| BoolOrDataConfig::from_bool(false));
407 let mangle = c
408 .mangle
409 .unwrap_as_option(|default| match default {
410 Some(true) => Some(Default::default()),
411 _ => None,
412 })
413 .map(|mut c| {
414 c.keep_class_names = true;
415 c
416 })
417 .map(BoolOrDataConfig::from_obj)
418 .unwrap_or_else(|| BoolOrDataConfig::from_bool(false));
419 JsMinifyOptions {
420 compress,
421 mangle,
422 ..c
423 }
424 });
425 }
426
427 let regenerator = transform.regenerator.clone();
428
429 let preserve_comments = if preserve_all_comments {
430 BoolOr::Bool(true)
431 } else {
432 js_minify
433 .as_ref()
434 .map(|v| match v.format.comments.clone().into_inner() {
435 Some(v) => v,
436 None => BoolOr::Bool(true),
437 })
438 .unwrap_or_else(|| {
439 BoolOr::Data(if cfg.minify.into_bool() {
440 JsMinifyCommentOption::PreserveSomeComments
441 } else {
442 JsMinifyCommentOption::PreserveAllComments
443 })
444 })
445 };
446
447 if syntax.typescript() {
448 transform.legacy_decorator = true.into();
449 }
450 let optimizer = transform.optimizer;
451
452 let const_modules = {
453 let enabled = transform.const_modules.is_some();
454 let config = transform.const_modules.unwrap_or_default();
455
456 let globals = config.globals;
457 Optional::new(const_modules(cm.clone(), globals), enabled)
458 };
459
460 let json_parse_pass = {
461 optimizer
462 .as_ref()
463 .and_then(|v| v.jsonify)
464 .as_ref()
465 .map(|cfg| json_parse(cfg.min_cost))
466 };
467
468 let simplifier_pass = {
469 if let Some(ref opts) = optimizer.as_ref().and_then(|o| o.simplify) {
470 match opts {
471 SimplifyOption::Bool(allow_simplify) => {
472 if *allow_simplify {
473 Some(simplifier(unresolved_mark, Default::default()))
474 } else {
475 None
476 }
477 }
478 SimplifyOption::Json(cfg) => Some(simplifier(
479 unresolved_mark,
480 SimplifyConfig {
481 dce: DceConfig {
482 preserve_imports_with_side_effects: cfg
483 .preserve_imports_with_side_effects,
484 ..Default::default()
485 },
486 ..Default::default()
487 },
488 )),
489 }
490 } else {
491 None
492 }
493 };
494
495 let optimization = {
496 optimizer
497 .and_then(|o| o.globals)
498 .map(|opts| opts.build(cm, handler))
499 };
500
501 let unresolved_ctxt = SyntaxContext::empty().apply_mark(unresolved_mark);
502 let top_level_ctxt = SyntaxContext::empty().apply_mark(top_level_mark);
503
504 let pass = (
505 const_modules,
506 optimization,
507 Optional::new(export_default_from(), syntax.export_default_from()),
508 simplifier_pass,
509 json_parse_pass,
510 );
511
512 let import_export_assign_config = match cfg.module {
513 Some(ModuleConfig::Es6(..)) => TsImportExportAssignConfig::EsNext,
514 Some(ModuleConfig::CommonJs(..))
515 | Some(ModuleConfig::Amd(..))
516 | Some(ModuleConfig::Umd(..)) => TsImportExportAssignConfig::Preserve,
517 Some(ModuleConfig::NodeNext(..)) => TsImportExportAssignConfig::NodeNext,
518 _ => TsImportExportAssignConfig::Classic,
520 };
521
522 let verbatim_module_syntax = transform.verbatim_module_syntax.into_bool();
523
524 let charset = cfg.jsc.output.charset.or_else(|| {
525 if js_minify.as_ref()?.format.ascii_only {
526 Some(OutputCharset::Ascii)
527 } else {
528 None
529 }
530 });
531
532 let codegen_inline_script = js_minify.as_ref().map_or(false, |v| v.format.inline_script);
537
538 let preamble = if !cfg.jsc.output.preamble.is_empty() {
539 cfg.jsc.output.preamble
540 } else {
541 js_minify
542 .as_ref()
543 .map(|v| v.format.preamble.clone())
544 .unwrap_or_default()
545 };
546
547 let paths = paths.into_iter().collect();
548 let resolver = ModuleConfig::get_resolver(&base_url, paths, base, cfg.module.as_ref());
549
550 let pass = PassBuilder::new(
551 cm,
552 handler,
553 loose,
554 assumptions,
555 top_level_mark,
556 unresolved_mark,
557 pass,
558 )
559 .target(es_version)
560 .skip_helper_injection(self.skip_helper_injection)
561 .minify(js_minify)
562 .hygiene(if self.disable_hygiene {
563 None
564 } else {
565 Some(hygiene::Config {
566 keep_class_names,
567 ..Default::default()
568 })
569 })
570 .fixer(!self.disable_fixer)
571 .preset_env(cfg.env)
572 .regenerator(regenerator)
573 .finalize(
574 syntax,
575 cfg.module,
576 comments.map(|v| v as _),
577 resolver.clone(),
578 );
579
580 let keep_import_attributes = experimental.keep_import_attributes.into_bool();
581 let disable_all_lints = experimental.disable_all_lints.into_bool();
582
583 #[cfg(feature = "plugin")]
584 let plugin_transforms: Box<dyn Pass> = {
585 let transform_filename = match base {
586 FileName::Real(path) => path.as_os_str().to_str().map(String::from),
587 FileName::Custom(filename) => Some(filename.to_owned()),
588 _ => None,
589 };
590 let transform_metadata_context = Arc::new(TransformPluginMetadataContext::new(
591 transform_filename,
592 self.env_name.to_owned(),
593 None,
594 ));
595
596 #[cfg(all(feature = "plugin", not(target_arch = "wasm32")))]
600 {
601 use swc_ecma_loader::resolve::Resolve;
602
603 let plugin_resolver = CachingResolver::new(
604 40,
605 NodeModulesResolver::new(
606 swc_ecma_loader::TargetEnv::Node,
607 Default::default(),
608 true,
609 ),
610 );
611
612 if let Some(plugins) = &experimental.plugins {
613 init_plugin_module_cache_once(true, &experimental.cache_root);
616
617 let mut inner_cache = PLUGIN_MODULE_CACHE
618 .inner
619 .get()
620 .expect("Cache should be available")
621 .lock();
622
623 for plugin_config in plugins.iter() {
625 let plugin_name = &plugin_config.0;
626
627 if !inner_cache.contains(&plugin_name) {
628 let resolved_path = plugin_resolver.resolve(
629 &FileName::Real(PathBuf::from(&plugin_name)),
630 &plugin_name,
631 )?;
632
633 let path = if let FileName::Real(value) = resolved_path.filename {
634 value
635 } else {
636 anyhow::bail!("Failed to resolve plugin path: {:?}", resolved_path);
637 };
638
639 inner_cache.store_bytes_from_path(&path, &plugin_name)?;
640 tracing::debug!("Initialized WASM plugin {plugin_name}");
641 }
642 }
643 }
644
645 Box::new(crate::plugin::plugins(
646 experimental.plugins,
647 transform_metadata_context,
648 comments.cloned(),
649 cm.clone(),
650 unresolved_mark,
651 ))
652 }
653
654 #[cfg(all(feature = "plugin", target_arch = "wasm32"))]
659 {
660 handler.warn(
661 "Currently @swc/wasm does not support plugins, plugin transform will be \
662 skipped. Refer https://github.com/swc-project/swc/issues/3934 for the details.",
663 );
664
665 Box::new(noop())
666 }
667 };
668
669 #[cfg(not(feature = "plugin"))]
670 let plugin_transforms: Box<dyn Pass> = {
671 if experimental.plugins.is_some() {
672 handler.warn(
673 "Plugin is not supported with current @swc/core. Plugin transform will be \
674 skipped.",
675 );
676 }
677 Box::new(noop_pass())
678 };
679
680 let mut plugin_transforms = Some(plugin_transforms);
681
682 let pass: Box<dyn Pass> = if experimental
683 .disable_builtin_transforms_for_internal_testing
684 .into_bool()
685 {
686 plugin_transforms.unwrap()
687 } else {
688 let decorator_pass: Box<dyn Pass> =
689 match transform.decorator_version.unwrap_or_default() {
690 DecoratorVersion::V202112 => Box::new(decorators(decorators::Config {
691 legacy: transform.legacy_decorator.into_bool(),
692 emit_metadata: transform.decorator_metadata.into_bool(),
693 use_define_for_class_fields: !assumptions.set_public_class_fields,
694 })),
695 DecoratorVersion::V202203 => Box::new(
696 swc_ecma_transforms::proposals::decorator_2022_03::decorator_2022_03(),
697 ),
698 DecoratorVersion::V202311 => todo!("2023-11 decorator"),
699 };
700
701 Box::new((
702 (
703 if experimental.run_plugin_first.into_bool() {
704 plugin_transforms.take()
705 } else {
706 None
707 },
708 Optional::new(
709 lint_pass(swc_ecma_lints::rules::all(LintParams {
710 program: &program,
711 lint_config: &lints,
712 top_level_ctxt,
713 unresolved_ctxt,
714 es_version,
715 source_map: cm.clone(),
716 })),
717 !disable_all_lints,
718 ),
719 Optional::new(decorator_pass, syntax.decorators()),
721 Optional::new(
722 explicit_resource_management(),
723 syntax.explicit_resource_management(),
724 ),
725 ),
726 (
729 Optional::new(import_attributes(), !keep_import_attributes),
730 Optional::new(
731 typescript::tsx::<Option<&dyn Comments>>(
732 cm.clone(),
733 typescript::Config {
734 import_export_assign_config,
735 verbatim_module_syntax,
736 ..Default::default()
737 },
738 typescript::TsxConfig {
739 pragma: Some(
740 transform
741 .react
742 .pragma
743 .clone()
744 .unwrap_or_else(default_pragma),
745 ),
746 pragma_frag: Some(
747 transform
748 .react
749 .pragma_frag
750 .clone()
751 .unwrap_or_else(default_pragma_frag),
752 ),
753 },
754 comments.map(|v| v as _),
755 unresolved_mark,
756 top_level_mark,
757 ),
758 syntax.typescript(),
759 ),
760 ),
761 (
762 plugin_transforms.take(),
763 custom_before_pass(&program),
764 Optional::new(
766 react::react::<&dyn Comments>(
767 cm.clone(),
768 comments.map(|v| v as _),
769 transform.react,
770 top_level_mark,
771 unresolved_mark,
772 ),
773 syntax.jsx(),
774 ),
775 pass,
776 Optional::new(jest::jest(), transform.hidden.jest.into_bool()),
777 Optional::new(
778 dropped_comments_preserver(comments.cloned()),
779 preserve_all_comments,
780 ),
781 ),
782 ))
783 };
784
785 Ok(BuiltInput {
786 program,
787 minify: cfg.minify.into_bool(),
788 pass,
789 external_helpers,
790 syntax,
791 target: es_version,
792 is_module,
793 source_maps: source_maps.unwrap_or(SourceMapsConfig::Bool(false)),
794 inline_sources_content: cfg.inline_sources_content.into_bool(),
795 input_source_map: cfg.input_source_map.clone().unwrap_or_default(),
796 output_path: output_path.map(|v| v.to_path_buf()),
797 source_root,
798 source_file_name,
799 comments: comments.cloned(),
800 preserve_comments,
801 emit_source_map_columns: cfg.emit_source_map_columns.into_bool(),
802 output: JscOutputConfig {
803 charset,
804 preamble,
805 preserve_annotations: cfg.jsc.output.preserve_annotations,
806 },
807 emit_assert_for_import_attributes: experimental
808 .emit_assert_for_import_attributes
809 .into_bool(),
810 codegen_inline_script,
811 emit_isolated_dts: experimental.emit_isolated_dts.into_bool(),
812 unresolved_mark,
813 resolver,
814 })
815 }
816}
817
818#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
819pub enum RootMode {
820 #[default]
821 #[serde(rename = "root")]
822 Root,
823 #[serde(rename = "upward")]
824 Upward,
825 #[serde(rename = "upward-optional")]
826 UpwardOptional,
827}
828
829const fn default_swcrc() -> bool {
830 true
831}
832
833#[derive(Debug, Clone, Serialize, Deserialize)]
834#[serde(untagged)]
835pub enum ConfigFile {
836 Bool(bool),
837 Str(String),
838}
839
840impl Default for ConfigFile {
841 fn default() -> Self {
842 ConfigFile::Bool(true)
843 }
844}
845
846#[derive(Debug, Default, Clone, Serialize, Deserialize)]
847#[serde(rename_all = "camelCase")]
848pub struct CallerOptions {
849 pub name: String,
850}
851
852#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))]
853fn default_cwd() -> PathBuf {
854 static CWD: Lazy<PathBuf> = Lazy::new(|| ::std::env::current_dir().unwrap());
855
856 CWD.clone()
857}
858
859#[derive(Debug, Clone, Deserialize)]
861#[serde(untagged, rename = "swcrc")]
862#[allow(clippy::large_enum_variant)]
863pub enum Rc {
864 Single(Config),
865 Multi(Vec<Config>),
866}
867
868impl Default for Rc {
869 fn default() -> Self {
870 Rc::Multi(vec![
871 Config {
872 env: None,
873 test: None,
874 exclude: Some(FileMatcher::Regex("\\.tsx?$".into())),
875 jsc: JscConfig {
876 syntax: Some(Default::default()),
877 ..Default::default()
878 },
879 ..Default::default()
880 },
881 Config {
882 env: None,
883 test: Some(FileMatcher::Regex("\\.tsx$".into())),
884 exclude: None,
885 jsc: JscConfig {
886 syntax: Some(Syntax::Typescript(TsSyntax {
887 tsx: true,
888 ..Default::default()
889 })),
890 ..Default::default()
891 },
892 ..Default::default()
893 },
894 Config {
895 env: None,
896 test: Some(FileMatcher::Regex("\\.(cts|mts)$".into())),
897 exclude: None,
898 jsc: JscConfig {
899 syntax: Some(Syntax::Typescript(TsSyntax {
900 tsx: false,
901 disallow_ambiguous_jsx_like: true,
902 ..Default::default()
903 })),
904 ..Default::default()
905 },
906 ..Default::default()
907 },
908 Config {
909 env: None,
910 test: Some(FileMatcher::Regex("\\.ts$".into())),
911 exclude: None,
912 jsc: JscConfig {
913 syntax: Some(Syntax::Typescript(TsSyntax {
914 tsx: false,
915 ..Default::default()
916 })),
917 ..Default::default()
918 },
919 ..Default::default()
920 },
921 ])
922 }
923}
924
925impl Rc {
926 pub fn into_config(self, filename: Option<&Path>) -> Result<Option<Config>, Error> {
928 let cs = match self {
929 Rc::Single(mut c) => match filename {
930 Some(filename) => {
931 if c.matches(filename)? {
932 c.adjust(filename);
933
934 return Ok(Some(c));
935 } else {
936 return Ok(None);
937 }
938 }
939 None => return Ok(Some(c)),
941 },
942 Rc::Multi(cs) => cs,
943 };
944
945 match filename {
946 Some(filename) => {
947 for mut c in cs {
948 if c.matches(filename)? {
949 c.adjust(filename);
950
951 return Ok(Some(c));
952 }
953 }
954 }
955 None => return Ok(Some(Config::default())),
956 }
957
958 bail!(".swcrc exists but not matched")
959 }
960}
961
962#[derive(Debug, Default, Clone, Deserialize, Merge)]
964#[serde(deny_unknown_fields, rename_all = "camelCase")]
965pub struct Config {
966 #[serde(default)]
967 pub env: Option<swc_ecma_preset_env::Config>,
968
969 #[serde(default)]
970 pub test: Option<FileMatcher>,
971
972 #[serde(default)]
973 pub exclude: Option<FileMatcher>,
974
975 #[serde(default)]
976 pub jsc: JscConfig,
977
978 #[serde(default)]
979 pub module: Option<ModuleConfig>,
980
981 #[serde(default)]
982 pub minify: BoolConfig<false>,
983
984 #[serde(default)]
985 pub input_source_map: Option<InputSourceMap>,
986
987 #[serde(default)]
989 pub source_maps: Option<SourceMapsConfig>,
990
991 #[serde(default)]
992 pub inline_sources_content: BoolConfig<true>,
993
994 #[serde(default)]
995 pub emit_source_map_columns: BoolConfig<true>,
996
997 #[serde(default)]
998 pub error: ErrorConfig,
999
1000 #[serde(default)]
1001 pub is_module: Option<IsModule>,
1002
1003 #[serde(rename = "$schema")]
1004 pub schema: Option<String>,
1005}
1006
1007impl Config {
1008 pub fn adjust(&mut self, file: &Path) {
1014 if let Some(Syntax::Typescript(TsSyntax { tsx, dts, .. })) = &mut self.jsc.syntax {
1015 let is_dts = file
1016 .file_name()
1017 .and_then(|f| f.to_str())
1018 .map(|s| s.ends_with(".d.ts"))
1019 .unwrap_or(false);
1020
1021 if is_dts {
1022 *dts = true;
1023 }
1024
1025 if file.extension() == Some("tsx".as_ref()) {
1026 *tsx = true;
1027 } else if file.extension() == Some("ts".as_ref()) {
1028 *tsx = false;
1029 }
1030 }
1031 }
1032}
1033
1034#[derive(Debug, Clone, Serialize, Deserialize)]
1035#[serde(untagged)]
1036pub enum FileMatcher {
1037 None,
1038 Regex(CachedRegex),
1039 Multi(Vec<FileMatcher>),
1040}
1041
1042impl Default for FileMatcher {
1043 fn default() -> Self {
1044 Self::None
1045 }
1046}
1047
1048impl FileMatcher {
1049 pub fn matches(&self, filename: &Path) -> Result<bool, Error> {
1050 match self {
1051 FileMatcher::None => Ok(false),
1052
1053 FileMatcher::Regex(re) => {
1054 let filename = if cfg!(target_os = "windows") {
1055 filename.to_string_lossy().replace('\\', "/")
1056 } else {
1057 filename.to_string_lossy().to_string()
1058 };
1059
1060 Ok(re.is_match(&filename))
1061 }
1062 FileMatcher::Multi(ref v) => {
1063 for m in v {
1065 if m.matches(filename)? {
1066 return Ok(true);
1067 }
1068 }
1069
1070 Ok(false)
1071 }
1072 }
1073 }
1074}
1075
1076impl Config {
1077 pub fn matches(&self, filename: &Path) -> Result<bool, Error> {
1078 if let Some(ref exclude) = self.exclude {
1079 if exclude.matches(filename)? {
1080 return Ok(false);
1081 }
1082 }
1083
1084 if let Some(ref include) = self.test {
1085 if include.matches(filename)? {
1086 return Ok(true);
1087 }
1088 return Ok(false);
1089 }
1090
1091 Ok(true)
1092 }
1093}
1094
1095#[non_exhaustive]
1097pub struct BuiltInput<P: Pass> {
1098 pub program: Program,
1099 pub pass: P,
1100 pub syntax: Syntax,
1101 pub target: EsVersion,
1102 pub minify: bool,
1105 pub external_helpers: bool,
1106 pub source_maps: SourceMapsConfig,
1107 pub input_source_map: InputSourceMap,
1108 pub is_module: IsModule,
1109 pub output_path: Option<PathBuf>,
1110
1111 pub source_root: Option<String>,
1112 pub source_file_name: Option<String>,
1113
1114 pub comments: Option<SingleThreadedComments>,
1115 pub preserve_comments: BoolOr<JsMinifyCommentOption>,
1116
1117 pub inline_sources_content: bool,
1118 pub emit_source_map_columns: bool,
1119
1120 pub output: JscOutputConfig,
1121 pub emit_assert_for_import_attributes: bool,
1122 pub codegen_inline_script: bool,
1123
1124 pub emit_isolated_dts: bool,
1125 pub unresolved_mark: Mark,
1126 pub resolver: Option<(FileName, Arc<dyn ImportResolver>)>,
1127}
1128
1129impl<P> BuiltInput<P>
1130where
1131 P: Pass,
1132{
1133 pub fn with_pass<N>(self, map: impl FnOnce(P) -> N) -> BuiltInput<N>
1134 where
1135 N: Pass,
1136 {
1137 BuiltInput {
1138 program: self.program,
1139 pass: map(self.pass),
1140 syntax: self.syntax,
1141 target: self.target,
1142 minify: self.minify,
1143 external_helpers: self.external_helpers,
1144 source_maps: self.source_maps,
1145 input_source_map: self.input_source_map,
1146 is_module: self.is_module,
1147 output_path: self.output_path,
1148 source_root: self.source_root,
1149 source_file_name: self.source_file_name,
1150 comments: self.comments,
1151 preserve_comments: self.preserve_comments,
1152 inline_sources_content: self.inline_sources_content,
1153 emit_source_map_columns: self.emit_source_map_columns,
1154 output: self.output,
1155 emit_assert_for_import_attributes: self.emit_assert_for_import_attributes,
1156 codegen_inline_script: self.codegen_inline_script,
1157 emit_isolated_dts: self.emit_isolated_dts,
1158 unresolved_mark: self.unresolved_mark,
1159 resolver: self.resolver,
1160 }
1161 }
1162}
1163
1164#[derive(Debug, Default, Clone, Serialize, Deserialize, Merge)]
1166#[serde(deny_unknown_fields, rename_all = "camelCase")]
1167pub struct JscConfig {
1168 #[serde(default)]
1169 pub assumptions: Option<Assumptions>,
1170
1171 #[serde(rename = "parser", default)]
1172 pub syntax: Option<Syntax>,
1173
1174 #[serde(default)]
1175 pub transform: MergingOption<TransformConfig>,
1176
1177 #[serde(default)]
1178 pub external_helpers: BoolConfig<false>,
1179
1180 #[serde(default)]
1181 pub target: Option<EsVersion>,
1182
1183 #[serde(default)]
1184 pub loose: BoolConfig<false>,
1185
1186 #[serde(default)]
1187 pub keep_class_names: BoolConfig<false>,
1188
1189 #[serde(default)]
1190 pub base_url: PathBuf,
1191
1192 #[serde(default)]
1193 pub paths: Paths,
1194
1195 #[serde(default)]
1196 pub minify: Option<JsMinifyOptions>,
1197
1198 #[serde(default)]
1199 pub experimental: JscExperimental,
1200
1201 #[serde(default)]
1202 pub lints: LintConfig,
1203
1204 #[serde(default)]
1205 pub preserve_all_comments: BoolConfig<false>,
1206
1207 #[serde(default)]
1208 pub output: JscOutputConfig,
1209}
1210
1211#[derive(Debug, Default, Clone, Serialize, Deserialize, Merge)]
1212#[serde(deny_unknown_fields, rename_all = "camelCase")]
1213pub struct JscOutputConfig {
1214 #[serde(default)]
1215 pub charset: Option<OutputCharset>,
1216
1217 #[serde(default)]
1218 pub preamble: String,
1219
1220 #[serde(default)]
1221 pub preserve_annotations: BoolConfig<false>,
1222}
1223
1224#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1225#[serde(deny_unknown_fields, rename_all = "camelCase")]
1226pub enum OutputCharset {
1227 #[default]
1228 #[serde(rename = "utf8")]
1229 Utf8,
1230 #[serde(rename = "ascii")]
1231 Ascii,
1232}
1233
1234#[derive(Debug, Default, Clone, Serialize, Deserialize, Merge)]
1236#[serde(deny_unknown_fields, rename_all = "camelCase")]
1237pub struct JscExperimental {
1238 #[serde(default)]
1240 pub plugins: Option<Vec<PluginConfig>>,
1241 #[serde(default, alias = "keepImportAssertions")]
1243 pub keep_import_attributes: BoolConfig<false>,
1244
1245 #[serde(default)]
1246 pub emit_assert_for_import_attributes: BoolConfig<false>,
1247 #[serde(default)]
1253 pub cache_root: Option<String>,
1254
1255 #[serde(default)]
1256 pub run_plugin_first: BoolConfig<false>,
1257
1258 #[serde(default)]
1259 pub disable_builtin_transforms_for_internal_testing: BoolConfig<false>,
1260
1261 #[serde(default)]
1265 pub emit_isolated_dts: BoolConfig<false>,
1266
1267 #[serde(default)]
1268 pub disable_all_lints: BoolConfig<true>,
1269}
1270
1271#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1272pub enum ErrorFormat {
1273 #[serde(rename = "json")]
1274 Json,
1275 #[serde(rename = "normal")]
1276 Normal,
1277}
1278
1279impl ErrorFormat {
1280 pub fn format(&self, err: &Error) -> String {
1281 match self {
1282 ErrorFormat::Normal => format!("{:?}", err),
1283 ErrorFormat::Json => {
1284 let mut map = serde_json::Map::new();
1285
1286 map.insert("message".into(), serde_json::Value::String(err.to_string()));
1287
1288 map.insert(
1289 "stack".into(),
1290 serde_json::Value::Array(
1291 err.chain()
1292 .skip(1)
1293 .map(|err| err.to_string())
1294 .map(serde_json::Value::String)
1295 .collect(),
1296 ),
1297 );
1298
1299 serde_json::to_string(&map).unwrap()
1300 }
1301 }
1302 }
1303}
1304
1305impl Default for ErrorFormat {
1306 fn default() -> Self {
1307 Self::Normal
1308 }
1309}
1310
1311pub type Paths = IndexMap<String, Vec<String>, FxBuildHasher>;
1313pub(crate) type CompiledPaths = Vec<(String, Vec<String>)>;
1314
1315#[derive(Debug, Clone, Serialize, Deserialize)]
1316#[serde(deny_unknown_fields, rename_all = "camelCase")]
1317#[serde(tag = "type")]
1318pub enum ModuleConfig {
1319 #[serde(rename = "commonjs")]
1320 CommonJs(modules::common_js::Config),
1321 #[serde(rename = "umd")]
1322 Umd(modules::umd::Config),
1323 #[serde(rename = "amd")]
1324 Amd(modules::amd::Config),
1325 #[serde(rename = "systemjs")]
1326 SystemJs(modules::system_js::Config),
1327 #[serde(rename = "es6")]
1328 Es6(EsModuleConfig),
1329 #[serde(rename = "nodenext")]
1330 NodeNext(EsModuleConfig),
1331}
1332
1333impl ModuleConfig {
1334 pub fn build<'cmt>(
1335 cm: Arc<SourceMap>,
1336 comments: Option<&'cmt dyn Comments>,
1337 config: Option<ModuleConfig>,
1338 unresolved_mark: Mark,
1339 available_features: FeatureFlag,
1340 resolver: Option<(FileName, Arc<dyn ImportResolver>)>,
1341 ) -> Box<dyn Pass + 'cmt> {
1342 let resolver = if let Some((base, resolver)) = resolver {
1343 Resolver::Real { base, resolver }
1344 } else {
1345 Resolver::Default
1346 };
1347
1348 match config {
1349 None | Some(ModuleConfig::Es6(..)) | Some(ModuleConfig::NodeNext(..)) => match resolver
1350 {
1351 Resolver::Default => Box::new(noop_pass()),
1352 Resolver::Real { base, resolver } => Box::new(import_rewriter(base, resolver)),
1353 },
1354 Some(ModuleConfig::CommonJs(config)) => Box::new(modules::common_js::common_js(
1355 resolver,
1356 unresolved_mark,
1357 config,
1358 available_features,
1359 )),
1360 Some(ModuleConfig::Umd(config)) => Box::new(modules::umd::umd(
1361 cm,
1362 resolver,
1363 unresolved_mark,
1364 config,
1365 available_features,
1366 )),
1367 Some(ModuleConfig::Amd(config)) => Box::new(modules::amd::amd(
1368 resolver,
1369 unresolved_mark,
1370 config,
1371 available_features,
1372 comments,
1373 )),
1374 Some(ModuleConfig::SystemJs(config)) => Box::new(modules::system_js::system_js(
1375 resolver,
1376 unresolved_mark,
1377 config,
1378 )),
1379 }
1380 }
1381
1382 pub fn get_resolver(
1383 base_url: &Path,
1384 paths: CompiledPaths,
1385 base: &FileName,
1386 config: Option<&ModuleConfig>,
1387 ) -> Option<(FileName, Arc<dyn ImportResolver>)> {
1388 let skip_resolver = base_url.as_os_str().is_empty() && paths.is_empty();
1389
1390 if skip_resolver {
1391 return None;
1392 }
1393
1394 let base = match base {
1395 FileName::Real(v) if !skip_resolver => {
1396 FileName::Real(v.canonicalize().unwrap_or_else(|_| v.to_path_buf()))
1397 }
1398 _ => base.clone(),
1399 };
1400
1401 let base_url = base_url.to_path_buf();
1402 let resolver = match config {
1403 None => build_resolver(base_url, paths, false, &util::Config::default_js_ext()),
1404 Some(ModuleConfig::Es6(config)) | Some(ModuleConfig::NodeNext(config)) => {
1405 build_resolver(
1406 base_url,
1407 paths,
1408 config.config.resolve_fully,
1409 &config.config.out_file_extension,
1410 )
1411 }
1412 Some(ModuleConfig::CommonJs(config)) => build_resolver(
1413 base_url,
1414 paths,
1415 config.resolve_fully,
1416 &config.out_file_extension,
1417 ),
1418 Some(ModuleConfig::Umd(config)) => build_resolver(
1419 base_url,
1420 paths,
1421 config.config.resolve_fully,
1422 &config.config.out_file_extension,
1423 ),
1424 Some(ModuleConfig::Amd(config)) => build_resolver(
1425 base_url,
1426 paths,
1427 config.config.resolve_fully,
1428 &config.config.out_file_extension,
1429 ),
1430 Some(ModuleConfig::SystemJs(config)) => build_resolver(
1431 base_url,
1432 paths,
1433 config.config.resolve_fully,
1434 &config.config.out_file_extension,
1435 ),
1436 };
1437
1438 Some((base, resolver))
1439 }
1440}
1441
1442#[derive(Debug, Default, Clone, Serialize, Deserialize, Merge)]
1443#[serde(deny_unknown_fields, rename_all = "camelCase")]
1444pub struct TransformConfig {
1445 #[serde(default)]
1446 pub react: react::Options,
1447
1448 #[serde(default)]
1449 pub const_modules: Option<ConstModulesConfig>,
1450
1451 #[serde(default)]
1452 pub optimizer: Option<OptimizerConfig>,
1453
1454 #[serde(default)]
1455 pub legacy_decorator: BoolConfig<false>,
1456
1457 #[serde(default)]
1458 pub decorator_metadata: BoolConfig<false>,
1459
1460 #[serde(default)]
1461 pub hidden: HiddenTransformConfig,
1462
1463 #[serde(default)]
1464 pub regenerator: regenerator::Config,
1465
1466 #[serde(default)]
1467 #[deprecated]
1468 pub treat_const_enum_as_enum: BoolConfig<false>,
1469
1470 #[serde(default)]
1472 pub use_define_for_class_fields: BoolConfig<true>,
1473
1474 #[serde(default)]
1476 pub verbatim_module_syntax: BoolConfig<false>,
1477
1478 #[serde(default)]
1479 pub decorator_version: Option<DecoratorVersion>,
1480}
1481
1482#[derive(Debug, Default, Clone, Serialize, Deserialize, Merge)]
1483#[serde(deny_unknown_fields, rename_all = "camelCase")]
1484pub struct HiddenTransformConfig {
1485 #[serde(default)]
1486 pub jest: BoolConfig<false>,
1487}
1488
1489#[derive(Debug, Default, Clone, Serialize, Deserialize, Merge)]
1490#[serde(deny_unknown_fields, rename_all = "camelCase")]
1491pub struct ConstModulesConfig {
1492 #[serde(default)]
1493 pub globals: FxHashMap<Atom, FxHashMap<Atom, String>>,
1494}
1495
1496#[derive(Debug, Default, Clone, Serialize, Deserialize, Merge)]
1497#[serde(deny_unknown_fields, rename_all = "camelCase")]
1498pub struct OptimizerConfig {
1499 #[serde(default)]
1500 pub globals: Option<GlobalPassOption>,
1501
1502 #[serde(default)]
1503 pub simplify: Option<SimplifyOption>,
1504
1505 #[serde(default)]
1506 pub jsonify: Option<JsonifyOption>,
1507}
1508
1509#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
1510#[serde(untagged)]
1511pub enum SimplifyOption {
1512 Bool(bool),
1513 Json(SimplifyJsonOption),
1514}
1515
1516impl Default for SimplifyOption {
1517 fn default() -> Self {
1518 SimplifyOption::Bool(true)
1519 }
1520}
1521
1522#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
1523#[serde(deny_unknown_fields, rename_all = "camelCase")]
1524pub struct SimplifyJsonOption {
1525 #[serde(default = "default_preserve_imports_with_side_effects")]
1526 pub preserve_imports_with_side_effects: bool,
1527}
1528
1529fn default_preserve_imports_with_side_effects() -> bool {
1530 true
1531}
1532
1533#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
1534#[serde(deny_unknown_fields, rename_all = "camelCase")]
1535pub struct JsonifyOption {
1536 #[serde(default = "default_jsonify_min_cost")]
1537 pub min_cost: usize,
1538}
1539
1540fn default_jsonify_min_cost() -> usize {
1541 1024
1542}
1543
1544#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, Merge)]
1545#[serde(deny_unknown_fields, rename_all = "camelCase")]
1546pub struct ErrorConfig {
1547 pub filename: BoolConfig<true>,
1548}
1549
1550#[derive(Debug, Default, Clone, Serialize, Deserialize)]
1551#[serde(deny_unknown_fields, rename_all = "camelCase")]
1552pub struct GlobalPassOption {
1553 #[serde(default)]
1554 pub vars: IndexMap<Atom, Atom, FxBuildHasher>,
1555 #[serde(default)]
1556 pub envs: GlobalInliningPassEnvs,
1557
1558 #[serde(default)]
1559 pub typeofs: FxHashMap<Atom, Atom>,
1560}
1561
1562#[derive(Debug, Clone, Serialize, Deserialize)]
1563#[serde(untagged)]
1564pub enum GlobalInliningPassEnvs {
1565 List(FxHashSet<String>),
1566 Map(FxHashMap<Atom, Atom>),
1567}
1568
1569impl Default for GlobalInliningPassEnvs {
1570 fn default() -> Self {
1571 let mut v = HashSet::default();
1572 v.insert(String::from("NODE_ENV"));
1573 v.insert(String::from("SWC_ENV"));
1574
1575 GlobalInliningPassEnvs::List(v)
1576 }
1577}
1578
1579impl GlobalPassOption {
1580 pub fn build(self, cm: &SourceMap, handler: &Handler) -> impl 'static + Pass {
1581 type ValuesMap = Arc<FxHashMap<Atom, Expr>>;
1582
1583 fn expr(cm: &SourceMap, handler: &Handler, src: String) -> Box<Expr> {
1584 let fm = cm.new_source_file(FileName::Anon.into(), src);
1585
1586 let mut errors = Vec::new();
1587 let expr = parse_file_as_expr(
1588 &fm,
1589 Syntax::Es(Default::default()),
1590 Default::default(),
1591 None,
1592 &mut errors,
1593 );
1594
1595 for e in errors {
1596 e.into_diagnostic(handler).emit()
1597 }
1598
1599 match expr {
1600 Ok(v) => v,
1601 _ => panic!("{} is not a valid expression", fm.src),
1602 }
1603 }
1604
1605 fn mk_map(
1606 cm: &SourceMap,
1607 handler: &Handler,
1608 values: impl Iterator<Item = (Atom, Atom)>,
1609 is_env: bool,
1610 ) -> ValuesMap {
1611 let mut m = HashMap::default();
1612
1613 for (k, v) in values {
1614 let v = if is_env {
1615 format!("'{}'", v)
1616 } else {
1617 (*v).into()
1618 };
1619 let v_str = v.clone();
1620
1621 let e = expr(cm, handler, v_str);
1622
1623 m.insert((*k).into(), *e);
1624 }
1625
1626 Arc::new(m)
1627 }
1628
1629 let env_map = if cfg!(target_arch = "wasm32") {
1630 Arc::new(Default::default())
1631 } else {
1632 match &self.envs {
1633 GlobalInliningPassEnvs::List(env_list) => {
1634 static CACHE: Lazy<DashMap<Vec<String>, ValuesMap, FxBuildHasher>> =
1635 Lazy::new(Default::default);
1636
1637 let cache_key = env_list.iter().cloned().collect::<Vec<_>>();
1638 if let Some(v) = CACHE.get(&cache_key).as_deref().cloned() {
1639 v
1640 } else {
1641 let map = mk_map(
1642 cm,
1643 handler,
1644 env::vars()
1645 .filter(|(k, _)| env_list.contains(k))
1646 .map(|(k, v)| (k.into(), v.into())),
1647 true,
1648 );
1649 CACHE.insert(cache_key, map.clone());
1650 map
1651 }
1652 }
1653
1654 GlobalInliningPassEnvs::Map(map) => {
1655 static CACHE: Lazy<DashMap<Vec<(Atom, Atom)>, ValuesMap, FxBuildHasher>> =
1656 Lazy::new(Default::default);
1657
1658 let cache_key = self
1659 .vars
1660 .iter()
1661 .map(|(k, v)| (k.clone(), v.clone()))
1662 .collect::<Vec<_>>();
1663 if let Some(v) = CACHE.get(&cache_key) {
1664 (*v).clone()
1665 } else {
1666 let map = mk_map(
1667 cm,
1668 handler,
1669 map.iter().map(|(k, v)| (k.clone(), v.clone())),
1670 false,
1671 );
1672 CACHE.insert(cache_key, map.clone());
1673 map
1674 }
1675 }
1676 }
1677 };
1678
1679 let global_exprs = {
1680 static CACHE: Lazy<DashMap<Vec<(Atom, Atom)>, GlobalExprMap, FxBuildHasher>> =
1681 Lazy::new(Default::default);
1682
1683 let cache_key = self
1684 .vars
1685 .iter()
1686 .filter(|(k, _)| k.contains('.'))
1687 .map(|(k, v)| (k.clone(), v.clone()))
1688 .collect::<Vec<_>>();
1689
1690 if let Some(v) = CACHE.get(&cache_key) {
1691 (*v).clone()
1692 } else {
1693 let map = self
1694 .vars
1695 .iter()
1696 .filter(|(k, _)| k.contains('.'))
1697 .map(|(k, v)| {
1698 (
1699 NodeIgnoringSpan::owned(*expr(cm, handler, k.to_string())),
1700 *expr(cm, handler, v.to_string()),
1701 )
1702 })
1703 .collect::<FxHashMap<_, _>>();
1704 let map = Arc::new(map);
1705 CACHE.insert(cache_key, map.clone());
1706 map
1707 }
1708 };
1709
1710 let global_map = {
1711 static CACHE: Lazy<DashMap<Vec<(Atom, Atom)>, ValuesMap, FxBuildHasher>> =
1712 Lazy::new(Default::default);
1713
1714 let cache_key = self
1715 .vars
1716 .iter()
1717 .filter(|(k, _)| !k.contains('.'))
1718 .map(|(k, v)| (k.clone(), v.clone()))
1719 .collect::<Vec<_>>();
1720 if let Some(v) = CACHE.get(&cache_key) {
1721 (*v).clone()
1722 } else {
1723 let map = mk_map(
1724 cm,
1725 handler,
1726 self.vars.into_iter().filter(|(k, _)| !k.contains('.')),
1727 false,
1728 );
1729 CACHE.insert(cache_key, map.clone());
1730 map
1731 }
1732 };
1733
1734 inline_globals2(env_map, global_map, global_exprs, Arc::new(self.typeofs))
1735 }
1736}
1737
1738fn default_env_name() -> String {
1739 if let Ok(v) = env::var("SWC_ENV") {
1740 return v;
1741 }
1742
1743 match env::var("NODE_ENV") {
1744 Ok(v) => v,
1745 Err(_) => "development".into(),
1746 }
1747}
1748
1749fn build_resolver(
1750 mut base_url: PathBuf,
1751 paths: CompiledPaths,
1752 resolve_fully: bool,
1753 file_extension: &str,
1754) -> SwcImportResolver {
1755 static CACHE: Lazy<DashMap<(PathBuf, CompiledPaths, bool), SwcImportResolver, FxBuildHasher>> =
1756 Lazy::new(Default::default);
1757
1758 if cfg!(target_os = "windows") {
1760 base_url = base_url
1761 .canonicalize()
1762 .with_context(|| {
1763 format!(
1764 "failed to canonicalize jsc.baseUrl(`{}`)\nThis is required on Windows \
1765 because of UNC path.",
1766 base_url.display()
1767 )
1768 })
1769 .unwrap();
1770 }
1771
1772 if let Some(cached) = CACHE.get(&(base_url.clone(), paths.clone(), resolve_fully)) {
1773 return cached.clone();
1774 }
1775
1776 let r = {
1777 let r = NodeModulesResolver::without_node_modules(
1778 swc_ecma_loader::TargetEnv::Node,
1779 Default::default(),
1780 true,
1781 );
1782
1783 let r = CachingResolver::new(1024, r);
1784
1785 let r = TsConfigResolver::new(r, base_url.clone(), paths.clone());
1786 let r = CachingResolver::new(256, r);
1787
1788 let r = NodeImportResolver::with_config(
1789 r,
1790 swc_ecma_transforms::modules::path::Config {
1791 base_dir: Some(base_url.clone()),
1792 resolve_fully,
1793 file_extension: file_extension.to_owned(),
1794 },
1795 );
1796 Arc::new(r)
1797 };
1798
1799 CACHE.insert((base_url, paths, resolve_fully), r.clone());
1800
1801 r
1802}