fob_cli/config/
loading.rs1use crate::config::FobConfig;
2use crate::error::{ConfigError, Result};
3use figment::{
4 providers::{Env, Format as _, Json, Serialized},
5 Error as FigmentError, Figment,
6};
7use std::path::Path;
8
9impl FobConfig {
10 pub fn load(args: &crate::cli::BuildArgs, config_path: Option<&Path>) -> Result<Self> {
13 let defaults = Self::default_config();
14 let default_entry = defaults.entry.clone();
15 let mut figment = Figment::new().merge(Serialized::defaults(defaults));
16
17 let mut config_file = config_path
19 .map(|p| p.to_path_buf())
20 .or_else(|| {
21 args.cwd.as_ref().and_then(|cwd| {
22 let cwd_path = if cwd.is_absolute() {
23 cwd.clone()
24 } else {
25 std::env::current_dir().ok()?.join(cwd)
26 };
27 let candidate = cwd_path.join("fob.config.json");
28 candidate.exists().then_some(candidate)
29 })
30 })
31 .or_else(|| {
32 let default_path = Path::new("fob.config.json");
33 default_path.exists().then_some(default_path.to_path_buf())
34 });
35 let has_config_file = config_file.is_some();
36
37 if let Some(path) = config_file.take() {
38 figment = figment.merge(Json::file(path));
39 }
40
41 let env_provider = Env::prefixed("FOB_")
43 .map(|key| env_key_to_camel_case(key.as_str()).into())
44 .lowercase(false);
45 figment = figment.merge(env_provider);
46
47 let base_entry =
49 if args.entry.is_none() || args.entry.as_ref().is_some_and(|e| e.is_empty()) {
50 let base: Self = figment.clone().extract().map_err(convert_figment_error)?;
51 Some(base.entry)
52 } else {
53 None
54 };
55
56 let cli_config = Self::from_build_args(args);
57 figment = figment.merge(Serialized::defaults(cli_config));
58
59 let mut config: Self = figment.extract().map_err(convert_figment_error)?;
60
61 if let Some(entry) = base_entry {
62 config.entry = entry;
63 }
64
65 if args.entry.is_none()
66 && !has_config_file
67 && config.entry == default_entry
68 && !std::env::vars().any(|(key, _)| key == "FOB_ENTRY" || key.starts_with("FOB_ENTRY_"))
69 {
70 return Err(ConfigError::MissingField {
71 field: "entry".to_string(),
72 hint: "Specify at least one entry point with --entry or fob.config.json"
73 .to_string(),
74 }
75 .into());
76 }
77
78 Ok(config)
79 }
80
81 fn from_build_args(args: &crate::cli::BuildArgs) -> Self {
83 Self {
84 entry: args.entry.clone().unwrap_or_default(),
85 format: args.format.into(),
86 out_dir: args.out_dir.clone(),
87 dts: args.dts,
88 dts_bundle: if args.dts_bundle { Some(true) } else { None },
89 external: args.external.clone(),
90 platform: args.platform.into(),
91 sourcemap: args.sourcemap.map(Into::into),
92 minify: args.minify,
93 target: args.target,
94 global_name: args.global_name.clone(),
95 bundle: args.bundle,
96 splitting: args.splitting,
97 no_treeshake: args.no_treeshake,
98 clean: args.clean,
99 cwd: args.cwd.clone(),
100 }
101 }
102
103 pub(crate) fn default_config() -> Self {
105 use crate::config::types::*;
106 use std::path::PathBuf;
107
108 Self {
109 entry: vec!["src/index.ts".to_string()],
110 format: Format::Esm,
111 out_dir: PathBuf::from("dist"),
112 dts: false,
113 dts_bundle: None,
114 external: vec![],
115 platform: Platform::Browser,
116 sourcemap: None,
117 minify: false,
118 target: EsTarget::Es2020,
119 global_name: None,
120 bundle: true, splitting: false,
122 no_treeshake: false,
123 clean: false,
124 cwd: None,
125 }
126 }
127}
128
129fn convert_figment_error(e: FigmentError) -> crate::error::CliError {
130 ConfigError::InvalidValue {
131 field: "configuration".to_string(),
132 value: e.to_string(),
133 hint: "Check fob.config.json syntax and field types".to_string(),
134 }
135 .into()
136}
137
138fn env_key_to_camel_case(key: &str) -> String {
139 let mut parts = key.split('_').filter(|part| !part.is_empty());
140 let mut result = String::new();
141 if let Some(first) = parts.next() {
142 result.push_str(&first.to_ascii_lowercase());
143 }
144
145 for part in parts {
146 let mut chars = part.chars();
147 if let Some(first_char) = chars.next() {
148 result.push(first_char.to_ascii_uppercase());
149 for c in chars {
150 result.push(c.to_ascii_lowercase());
151 }
152 }
153 }
154
155 result
156}