use minify::MinifyOptions;
use regex::Regex;
use serde::{Deserialize, Serialize};
use swc_ecma_parser::{EsSyntax as EsConfig, TsSyntax as TsConfig};
use tree_shaking::TreeShakingConfig;
use self::{
bool_or_obj::BoolOrObj, comments::CommentsConfig, config_regex::ConfigRegex, html::HtmlConfig,
partial_bundling::PartialBundlingConfig, preset_env::PresetEnvConfig, script::ScriptConfig,
};
use crate::HashMap;
pub const FARM_MODULE_SYSTEM: &str = "m";
pub const FARM_DYNAMIC_REQUIRE: &str = "farmDynamicRequire";
pub const FARM_REQUIRE: &str = "farmRequire";
pub const FARM_MODULE: &str = "module";
pub const FARM_MODULE_EXPORT: &str = "exports";
pub mod asset;
pub mod bool_or_obj;
pub mod comments;
pub mod config_regex;
pub mod css;
pub mod custom;
pub mod external;
pub mod html;
pub mod minify;
mod output;
pub mod partial_bundling;
pub mod persistent_cache;
pub mod preset_env;
pub mod script;
pub mod tree_shaking;
use asset::AssetsConfig;
pub use css::*;
pub use output::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AliasItem {
pub find: StringOrRegex,
pub replacement: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum StringOrRegex {
String(String),
#[serde(with = "serde_regex")]
Regex(Regex),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", default)]
pub struct Config {
pub input: HashMap<String, String>,
pub output: Box<OutputConfig>,
pub root: String,
pub mode: Mode,
pub resolve: Box<ResolveConfig>,
pub external: Vec<ConfigRegex>,
pub define: HashMap<String, serde_json::Value>,
pub runtime: Box<RuntimeConfig>,
pub script: Box<ScriptConfig>,
pub assets: Box<AssetsConfig>,
pub css: Box<CssConfig>,
pub html: Box<HtmlConfig>,
pub sourcemap: Box<SourcemapConfig>,
pub partial_bundling: Box<PartialBundlingConfig>,
pub lazy_compilation: bool,
pub core_lib_path: Option<String>,
pub tree_shaking: Box<BoolOrObj<TreeShakingConfig>>,
pub minify: Box<BoolOrObj<MinifyOptions>>,
pub preset_env: Box<PresetEnvConfig>,
pub record: bool,
pub progress: bool,
pub persistent_cache: Box<persistent_cache::PersistentCacheConfig>,
pub concatenate_modules: bool,
pub comments: Box<CommentsConfig>,
pub custom: Box<HashMap<String, String>>,
}
impl Default for Config {
fn default() -> Self {
let root = std::env::current_dir()
.unwrap()
.to_string_lossy()
.to_string();
Self {
input: HashMap::from_iter([("index".to_string(), "./index.html".to_string())]),
root: root.clone(),
output: Default::default(),
mode: Mode::Development,
resolve: Default::default(),
define: HashMap::default(),
external: Default::default(),
runtime: Default::default(),
script: Default::default(),
css: Default::default(),
html: Box::default(),
assets: Default::default(),
sourcemap: Default::default(),
partial_bundling: Default::default(),
lazy_compilation: true,
core_lib_path: None,
tree_shaking: Box::new(BoolOrObj::Bool(true)),
minify: Box::new(BoolOrObj::Bool(true)),
preset_env: Box::<PresetEnvConfig>::default(),
record: false,
progress: true,
persistent_cache: Box::<persistent_cache::PersistentCacheConfig>::new(
persistent_cache::PersistentCacheConfig::get_default_config(&root),
),
concatenate_modules: false,
comments: Box::default(),
custom: Box::<HashMap<String, String>>::default(),
}
}
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub enum Mode {
#[serde(rename = "development")]
Development,
#[serde(rename = "production")]
Production,
}
impl Mode {
pub fn is_dev(&self) -> bool {
matches!(self, Mode::Development)
}
pub fn is_prod(&self) -> bool {
matches!(self, Mode::Production)
}
}
impl Default for Mode {
fn default() -> Self {
Self::Development
}
}
impl ToString for Mode {
fn to_string(&self) -> String {
match self {
Mode::Development => "development".to_string(),
Mode::Production => "production".to_string(),
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct ScriptParserConfig {
pub es_config: EsConfig,
pub ts_config: TsConfig,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase", default)]
pub struct ResolveConfig {
pub alias: Vec<AliasItem>,
pub main_fields: Vec<String>,
pub main_files: Vec<String>,
pub extensions: Vec<String>,
pub conditions: Vec<String>,
pub symlinks: bool,
pub strict_exports: bool,
pub auto_external_failed_resolve: bool,
pub dedupe: Vec<String>,
}
impl Default for ResolveConfig {
fn default() -> Self {
Self {
alias: vec![],
main_fields: vec![],
main_files: vec![String::from("index")],
extensions: vec![
String::from("tsx"),
String::from("ts"),
String::from("mts"),
String::from("cts"),
String::from("jsx"),
String::from("mjs"),
String::from("js"),
String::from("cjs"),
String::from("json"),
String::from("html"),
String::from("css"),
],
conditions: vec![
String::from("development"),
String::from("production"),
String::from("module"),
],
symlinks: true,
strict_exports: false,
auto_external_failed_resolve: false,
dedupe: vec![],
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", default)]
pub struct RuntimeConfig {
pub path: String,
pub plugins: Vec<String>,
pub swc_helpers_path: String,
pub namespace: String,
pub isolate: bool,
}
impl Default for RuntimeConfig {
fn default() -> Self {
Self {
path: String::from(""),
plugins: vec![],
swc_helpers_path: String::from(""),
namespace: String::from("__farm_default_namespace__"),
isolate: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SourcemapConfig {
#[serde(rename = "inline")]
Inline,
#[serde(rename = "all")]
All,
#[serde(rename = "all-inline")]
AllInline,
#[serde(untagged)]
Bool(bool),
}
impl Default for SourcemapConfig {
fn default() -> Self {
Self::Bool(true)
}
}
impl SourcemapConfig {
pub fn enabled(&self, immutable: bool) -> bool {
match self {
Self::Bool(b) => *b && !immutable,
Self::Inline => !immutable,
_ => true,
}
}
pub fn is_inline(&self) -> bool {
match self {
Self::Bool(_) => false,
Self::Inline => true,
Self::All => false,
Self::AllInline => true,
}
}
pub fn is_all(&self) -> bool {
match self {
Self::Bool(_) => false,
Self::Inline => false,
Self::All => true,
Self::AllInline => true,
}
}
pub fn is_false(&self) -> bool {
match self {
Self::Bool(b) => !*b,
_ => false,
}
}
}
mod tests {
#[test]
fn test_deserialize() {
use super::SourcemapConfig;
let config: SourcemapConfig = serde_json::from_str("true").expect("failed to parse");
assert!(matches!(config, SourcemapConfig::Bool(true)));
let config: SourcemapConfig = serde_json::from_str("false").expect("failed to parse");
assert!(matches!(config, SourcemapConfig::Bool(false)));
let config: SourcemapConfig = serde_json::from_str("\"all-inline\"").expect("failed to parse");
assert!(matches!(config, SourcemapConfig::AllInline));
let config: SourcemapConfig = serde_json::from_str("\"inline\"").expect("failed to parse");
assert!(matches!(config, SourcemapConfig::Inline));
let config: SourcemapConfig = serde_json::from_str("\"all\"").expect("failed to parse");
assert!(matches!(config, SourcemapConfig::All));
}
#[test]
fn target_env() {
use super::TargetEnv;
let env = TargetEnv::Browser;
assert!(env.is_browser());
assert!(!env.is_node());
assert!(!env.is_library());
let env = TargetEnv::Node;
assert!(env.is_node());
assert!(!env.is_browser());
assert!(!env.is_library());
let env = TargetEnv::Library;
assert!(env.is_library());
assert!(!env.is_node());
assert!(!env.is_browser());
}
}