unrspack_resolver/
tsconfig_serde.rs

1use std::{
2    path::{Path, PathBuf},
3    sync::Arc,
4};
5
6use serde::Deserialize;
7
8use crate::{
9    CompilerOptions, CompilerOptionsPathsMap, PathUtil, ProjectReference, TsConfig,
10    TsconfigReferences,
11};
12
13#[derive(Debug, Deserialize)]
14#[serde(rename_all = "camelCase")]
15pub struct TsConfigSerde {
16    /// Whether this is the caller tsconfig.
17    /// Used for final template variable substitution when all configs are extended and merged.
18    #[serde(skip)]
19    pub root: bool,
20
21    /// Path to `tsconfig.json`. Contains the `tsconfig.json` filename.
22    #[serde(skip)]
23    pub path: PathBuf,
24
25    #[serde(default)]
26    pub extends: Option<ExtendsField>,
27
28    #[serde(default)]
29    pub compiler_options: CompilerOptionsSerde,
30
31    /// Bubbled up project references with a reference to their tsconfig.
32    #[serde(default)]
33    pub references: Vec<ProjectReferenceSerde>,
34}
35
36impl TsConfig for TsConfigSerde {
37    type Co = CompilerOptionsSerde;
38
39    fn root(&self) -> bool {
40        self.root
41    }
42
43    fn path(&self) -> &Path {
44        &self.path
45    }
46
47    fn directory(&self) -> &Path {
48        debug_assert!(self.path.file_name().is_some());
49        self.path.parent().unwrap()
50    }
51
52    fn compiler_options(&self) -> &Self::Co {
53        &self.compiler_options
54    }
55
56    fn compiler_options_mut(&mut self) -> &mut Self::Co {
57        &mut self.compiler_options
58    }
59
60    fn extends(&self) -> impl Iterator<Item = &str> {
61        let specifiers = match &self.extends {
62            Some(ExtendsField::Single(specifier)) => {
63                vec![specifier.as_str()]
64            }
65            Some(ExtendsField::Multiple(specifiers)) => {
66                specifiers.iter().map(String::as_str).collect()
67            }
68            None => Vec::new(),
69        };
70        specifiers.into_iter()
71    }
72
73    fn load_references(&mut self, references: &TsconfigReferences) -> bool {
74        match references {
75            TsconfigReferences::Disabled => {
76                self.references.drain(..);
77            }
78            TsconfigReferences::Auto => {}
79            TsconfigReferences::Paths(paths) => {
80                self.references = paths
81                    .iter()
82                    .map(|path| ProjectReferenceSerde { path: path.clone(), tsconfig: None })
83                    .collect();
84            }
85        }
86
87        !self.references.is_empty()
88    }
89
90    fn references(&self) -> impl Iterator<Item = &impl ProjectReference<Tc = Self>> {
91        self.references.iter()
92    }
93
94    fn references_mut(&mut self) -> impl Iterator<Item = &mut impl ProjectReference<Tc = Self>> {
95        self.references.iter_mut()
96    }
97}
98
99/// Compiler Options
100///
101/// <https://www.typescriptlang.org/tsconfig#compilerOptions>
102#[derive(Debug, Default, Deserialize)]
103#[serde(rename_all = "camelCase")]
104pub struct CompilerOptionsSerde {
105    pub base_url: Option<PathBuf>,
106
107    /// Path aliases.
108    pub paths: Option<CompilerOptionsPathsMap>,
109
110    /// The actual base from where path aliases are resolved.
111    #[serde(skip)]
112    paths_base: PathBuf,
113
114    /// <https://www.typescriptlang.org/tsconfig/#experimentalDecorators>
115    pub experimental_decorators: Option<bool>,
116
117    /// <https://www.typescriptlang.org/tsconfig/#jsx>
118    pub jsx: Option<String>,
119
120    /// <https://www.typescriptlang.org/tsconfig/#jsxFactory>
121    pub jsx_factory: Option<String>,
122
123    /// <https://www.typescriptlang.org/tsconfig/#jsxFragmentFactory>
124    pub jsx_fragment_factory: Option<String>,
125
126    /// <https://www.typescriptlang.org/tsconfig/#jsxImportSource>
127    pub jsx_import_source: Option<String>,
128
129    /// <https://www.typescriptlang.org/tsconfig/#verbatimModuleSyntax>
130    pub verbatim_module_syntax: Option<bool>,
131}
132
133impl CompilerOptions for CompilerOptionsSerde {
134    fn base_url(&self) -> Option<&Path> {
135        self.base_url.as_deref()
136    }
137
138    fn set_base_url(&mut self, base_url: PathBuf) {
139        self.base_url = Some(base_url);
140    }
141
142    fn paths(&self) -> Option<&CompilerOptionsPathsMap> {
143        self.paths.as_ref()
144    }
145
146    fn paths_mut(&mut self) -> Option<&mut CompilerOptionsPathsMap> {
147        self.paths.as_mut()
148    }
149
150    fn set_paths(&mut self, paths: Option<CompilerOptionsPathsMap>) {
151        self.paths = paths;
152    }
153
154    fn paths_base(&self) -> &Path {
155        &self.paths_base
156    }
157
158    fn set_paths_base(&mut self, paths_base: PathBuf) {
159        self.paths_base = paths_base;
160    }
161
162    fn experimental_decorators(&self) -> Option<&bool> {
163        self.experimental_decorators.as_ref()
164    }
165
166    fn set_experimental_decorators(&mut self, experimental_decorators: bool) {
167        self.experimental_decorators = Some(experimental_decorators);
168    }
169
170    fn jsx(&self) -> Option<&str> {
171        self.jsx.as_deref()
172    }
173
174    fn set_jsx(&mut self, jsx: String) {
175        self.jsx = Some(jsx);
176    }
177
178    fn jsx_factory(&self) -> Option<&str> {
179        self.jsx_factory.as_deref()
180    }
181
182    fn set_jsx_factory(&mut self, jsx_factory: String) {
183        self.jsx_factory = Some(jsx_factory);
184    }
185
186    fn jsx_fragment_factory(&self) -> Option<&str> {
187        self.jsx_fragment_factory.as_deref()
188    }
189
190    fn set_jsx_fragment_factory(&mut self, jsx_fragment_factory: String) {
191        self.jsx_fragment_factory = Some(jsx_fragment_factory);
192    }
193
194    fn jsx_import_source(&self) -> Option<&str> {
195        self.jsx_import_source.as_deref()
196    }
197
198    fn set_jsx_import_source(&mut self, jsx_import_source: String) {
199        self.jsx_import_source = Some(jsx_import_source);
200    }
201}
202
203/// Value for the "extends" field.
204///
205/// <https://www.typescriptlang.org/tsconfig/#extends>
206#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
207#[serde(untagged)]
208pub enum ExtendsField {
209    Single(String),
210    Multiple(Vec<String>),
211}
212
213/// Project Reference
214///
215/// <https://www.typescriptlang.org/docs/handbook/project-references.html>
216#[derive(Debug, Deserialize)]
217pub struct ProjectReferenceSerde {
218    pub path: PathBuf,
219
220    #[serde(skip)]
221    pub tsconfig: Option<Arc<TsConfigSerde>>,
222}
223
224impl ProjectReference for ProjectReferenceSerde {
225    type Tc = TsConfigSerde;
226
227    fn path(&self) -> &Path {
228        &self.path
229    }
230
231    fn tsconfig(&self) -> Option<Arc<Self::Tc>> {
232        self.tsconfig.clone()
233    }
234
235    fn set_tsconfig(&mut self, tsconfig: Arc<Self::Tc>) {
236        self.tsconfig.replace(tsconfig);
237    }
238}
239
240impl TsConfigSerde {
241    /// Parses the tsconfig from a JSON string.
242    ///
243    /// # Errors
244    ///
245    /// * Any error that can be returned by `serde_json::from_str()`.
246    pub fn parse(root: bool, path: &Path, json: &mut str) -> Result<Self, serde_json::Error> {
247        _ = json_strip_comments::strip(json);
248        let mut tsconfig: Self = serde_json::from_str(json)?;
249        tsconfig.root = root;
250        tsconfig.path = path.to_path_buf();
251        let directory = tsconfig.directory().to_path_buf();
252        if let Some(base_url) = tsconfig.compiler_options.base_url {
253            tsconfig.compiler_options.base_url = Some(directory.normalize_with(base_url));
254        }
255        if tsconfig.compiler_options.paths.is_some() {
256            tsconfig.compiler_options.paths_base =
257                tsconfig.compiler_options.base_url.as_ref().map_or(directory, Clone::clone);
258        }
259        Ok(tsconfig)
260    }
261}