Skip to main content

cli/lib/compiler/defaults/
swc_defaults.rs

1//! Legacy SWC option planning kept only for projects that explicitly opt into SWC.
2
3use std::collections::BTreeMap;
4use std::path::PathBuf;
5
6use crate::configuration::{Builder, Configuration, DEFAULT_OUT_DIR, DEFAULT_SOURCE_ROOT};
7
8use super::super::SwcCliOptions;
9
10#[derive(Clone, Debug, Default, PartialEq, Eq)]
11pub struct SourceCompilerOptions {
12    pub source_map: Option<bool>,
13    pub inline_source_map: Option<bool>,
14    pub out_dir: Option<String>,
15    pub root_dir: Option<String>,
16    pub base_url: Option<String>,
17    pub paths: BTreeMap<String, Vec<String>>,
18}
19
20#[derive(Clone, Debug, PartialEq, Eq)]
21pub struct SwcDefaults {
22    pub swc_options: SwcOptions,
23    pub cli_options: SwcCliOptions,
24}
25
26#[derive(Clone, Debug, PartialEq, Eq)]
27pub struct SwcOptions {
28    pub source_maps: Option<SwcSourceMaps>,
29    pub module_type: String,
30    pub target: String,
31    pub parser_syntax: String,
32    pub decorators: bool,
33    pub dynamic_import: bool,
34    pub legacy_decorator: bool,
35    pub decorator_metadata: bool,
36    pub use_define_for_class_fields: bool,
37    pub keep_class_names: bool,
38    pub base_url: Option<String>,
39    pub paths: BTreeMap<String, Vec<String>>,
40    pub minify: bool,
41    pub swcrc: bool,
42}
43
44#[derive(Clone, Debug, PartialEq, Eq)]
45pub enum SwcSourceMaps {
46    Bool(bool),
47    Inline,
48}
49
50pub fn swc_defaults_factory(
51    ts_options: Option<&SourceCompilerOptions>,
52    configuration: Option<&Configuration>,
53) -> SwcDefaults {
54    let ts_options = ts_options.cloned().unwrap_or_default();
55    let configuration = configuration.cloned().unwrap_or_default();
56    let builder_options = match &configuration.compiler_options.builder {
57        Builder::Swc(options) => Some(options),
58        _ => None,
59    };
60    let default_out_dir = ts_options
61        .out_dir
62        .as_deref()
63        .map(convert_path)
64        .unwrap_or_else(|| DEFAULT_OUT_DIR.to_string());
65
66    SwcDefaults {
67        swc_options: SwcOptions {
68            source_maps: match (ts_options.source_map, ts_options.inline_source_map) {
69                (Some(true), _) => Some(SwcSourceMaps::Bool(true)),
70                (_, Some(true)) => Some(SwcSourceMaps::Inline),
71                _ => None,
72            },
73            module_type: "commonjs".to_string(),
74            target: "es2021".to_string(),
75            parser_syntax: "rust".to_string(),
76            decorators: true,
77            dynamic_import: true,
78            legacy_decorator: true,
79            decorator_metadata: true,
80            use_define_for_class_fields: false,
81            keep_class_names: true,
82            base_url: ts_options.base_url,
83            paths: ts_options.paths,
84            minify: false,
85            swcrc: true,
86        },
87        cli_options: SwcCliOptions {
88            out_dir: builder_options
89                .and_then(|options| options.out_dir.as_ref())
90                .map(PathBuf::from)
91                .unwrap_or_else(|| PathBuf::from(default_out_dir)),
92            filenames: non_empty_paths(
93                builder_options.map(|options| options.filenames.as_slice()),
94                vec![PathBuf::from(if configuration.source_root.is_empty() {
95                    DEFAULT_SOURCE_ROOT
96                } else {
97                    &configuration.source_root
98                })],
99            ),
100            sync: builder_options
101                .and_then(|options| options.sync)
102                .unwrap_or(false),
103            extensions: non_empty_strings(
104                builder_options.map(|options| options.extensions.as_slice()),
105                vec![".js".to_string(), ".ts".to_string()],
106            ),
107            copy_files: builder_options
108                .and_then(|options| options.copy_files)
109                .unwrap_or(false),
110            include_dotfiles: builder_options
111                .and_then(|options| options.include_dotfiles)
112                .unwrap_or(false),
113            quiet: builder_options
114                .and_then(|options| options.quiet)
115                .unwrap_or(false),
116            watch: false,
117            strip_leading_paths: ts_options.root_dir.is_none(),
118        },
119    }
120}
121
122pub fn convert_path(windows_path: &str) -> String {
123    let without_unc = windows_path.strip_prefix(r"\\?\").unwrap_or(windows_path);
124    let normalized = without_unc.replace('\\', "/");
125    let mut compact = String::new();
126    let mut previous_slash = false;
127    for ch in normalized.chars() {
128        if ch == '/' {
129            if !previous_slash {
130                compact.push(ch);
131            }
132            previous_slash = true;
133        } else {
134            compact.push(ch);
135            previous_slash = false;
136        }
137    }
138    compact
139}
140
141fn non_empty_paths(values: Option<&[String]>, default: Vec<PathBuf>) -> Vec<PathBuf> {
142    match values {
143        Some(values) if !values.is_empty() => values.iter().map(PathBuf::from).collect(),
144        _ => default,
145    }
146}
147
148fn non_empty_strings(values: Option<&[String]>, default: Vec<String>) -> Vec<String> {
149    match values {
150        Some(values) if !values.is_empty() => values.to_vec(),
151        _ => default,
152    }
153}