use std::collections::BTreeMap;
use std::path::PathBuf;
use crate::configuration::{Builder, Configuration, DEFAULT_OUT_DIR, DEFAULT_SOURCE_ROOT};
use super::super::SwcCliOptions;
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct SourceCompilerOptions {
pub source_map: Option<bool>,
pub inline_source_map: Option<bool>,
pub out_dir: Option<String>,
pub root_dir: Option<String>,
pub base_url: Option<String>,
pub paths: BTreeMap<String, Vec<String>>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SwcDefaults {
pub swc_options: SwcOptions,
pub cli_options: SwcCliOptions,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SwcOptions {
pub source_maps: Option<SwcSourceMaps>,
pub module_type: String,
pub target: String,
pub parser_syntax: String,
pub decorators: bool,
pub dynamic_import: bool,
pub legacy_decorator: bool,
pub decorator_metadata: bool,
pub use_define_for_class_fields: bool,
pub keep_class_names: bool,
pub base_url: Option<String>,
pub paths: BTreeMap<String, Vec<String>>,
pub minify: bool,
pub swcrc: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SwcSourceMaps {
Bool(bool),
Inline,
}
pub fn swc_defaults_factory(
ts_options: Option<&SourceCompilerOptions>,
configuration: Option<&Configuration>,
) -> SwcDefaults {
let ts_options = ts_options.cloned().unwrap_or_default();
let configuration = configuration.cloned().unwrap_or_default();
let builder_options = match &configuration.compiler_options.builder {
Builder::Swc(options) => Some(options),
_ => None,
};
let default_out_dir = ts_options
.out_dir
.as_deref()
.map(convert_path)
.unwrap_or_else(|| DEFAULT_OUT_DIR.to_string());
SwcDefaults {
swc_options: SwcOptions {
source_maps: match (ts_options.source_map, ts_options.inline_source_map) {
(Some(true), _) => Some(SwcSourceMaps::Bool(true)),
(_, Some(true)) => Some(SwcSourceMaps::Inline),
_ => None,
},
module_type: "commonjs".to_string(),
target: "es2021".to_string(),
parser_syntax: "rust".to_string(),
decorators: true,
dynamic_import: true,
legacy_decorator: true,
decorator_metadata: true,
use_define_for_class_fields: false,
keep_class_names: true,
base_url: ts_options.base_url,
paths: ts_options.paths,
minify: false,
swcrc: true,
},
cli_options: SwcCliOptions {
out_dir: builder_options
.and_then(|options| options.out_dir.as_ref())
.map(PathBuf::from)
.unwrap_or_else(|| PathBuf::from(default_out_dir)),
filenames: non_empty_paths(
builder_options.map(|options| options.filenames.as_slice()),
vec![PathBuf::from(if configuration.source_root.is_empty() {
DEFAULT_SOURCE_ROOT
} else {
&configuration.source_root
})],
),
sync: builder_options
.and_then(|options| options.sync)
.unwrap_or(false),
extensions: non_empty_strings(
builder_options.map(|options| options.extensions.as_slice()),
vec![".js".to_string(), ".ts".to_string()],
),
copy_files: builder_options
.and_then(|options| options.copy_files)
.unwrap_or(false),
include_dotfiles: builder_options
.and_then(|options| options.include_dotfiles)
.unwrap_or(false),
quiet: builder_options
.and_then(|options| options.quiet)
.unwrap_or(false),
watch: false,
strip_leading_paths: ts_options.root_dir.is_none(),
},
}
}
pub fn convert_path(windows_path: &str) -> String {
let without_unc = windows_path.strip_prefix(r"\\?\").unwrap_or(windows_path);
let normalized = without_unc.replace('\\', "/");
let mut compact = String::new();
let mut previous_slash = false;
for ch in normalized.chars() {
if ch == '/' {
if !previous_slash {
compact.push(ch);
}
previous_slash = true;
} else {
compact.push(ch);
previous_slash = false;
}
}
compact
}
fn non_empty_paths(values: Option<&[String]>, default: Vec<PathBuf>) -> Vec<PathBuf> {
match values {
Some(values) if !values.is_empty() => values.iter().map(PathBuf::from).collect(),
_ => default,
}
}
fn non_empty_strings(values: Option<&[String]>, default: Vec<String>) -> Vec<String> {
match values {
Some(values) if !values.is_empty() => values.to_vec(),
_ => default,
}
}