unrspack_resolver/
tsconfig_serde.rs1use 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 #[serde(skip)]
19 pub root: bool,
20
21 #[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 #[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#[derive(Debug, Default, Deserialize)]
103#[serde(rename_all = "camelCase")]
104pub struct CompilerOptionsSerde {
105 pub base_url: Option<PathBuf>,
106
107 pub paths: Option<CompilerOptionsPathsMap>,
109
110 #[serde(skip)]
112 paths_base: PathBuf,
113
114 pub experimental_decorators: Option<bool>,
116
117 pub jsx: Option<String>,
119
120 pub jsx_factory: Option<String>,
122
123 pub jsx_fragment_factory: Option<String>,
125
126 pub jsx_import_source: Option<String>,
128
129 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#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
207#[serde(untagged)]
208pub enum ExtendsField {
209 Single(String),
210 Multiple(Vec<String>),
211}
212
213#[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 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}