Skip to content

Commit 2070b35

Browse files
authored
feat: add Resolver::resolve_tsconfig API (#312)
closes #289
1 parent 1419af5 commit 2070b35

File tree

4 files changed

+73
-43
lines changed

4 files changed

+73
-43
lines changed

src/lib.rs

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,22 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
177177
self.resolve_tracing(directory.as_ref(), specifier, &mut ctx)
178178
}
179179

180+
/// Resolve `tsconfig`.
181+
///
182+
/// The path can be:
183+
///
184+
/// * Path to a file with `.json` extension.
185+
/// * Path to a file without `.json` extension, `.json` will be appended to filename.
186+
/// * Path to a directory, where the filename is defaulted to `tsconfig.json`
187+
///
188+
/// # Errors
189+
///
190+
/// * See [ResolveError]
191+
pub fn resolve_tsconfig<P: AsRef<Path>>(&self, path: P) -> Result<Arc<TsConfig>, ResolveError> {
192+
let path = path.as_ref();
193+
self.load_tsconfig(true, path, &TsconfigReferences::Auto)
194+
}
195+
180196
/// Resolve `specifier` at absolute `path` with [ResolveContext]
181197
///
182198
/// # Errors
@@ -1114,30 +1130,6 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
11141130
None
11151131
}
11161132

1117-
fn load_tsconfig_paths(
1118-
&self,
1119-
cached_path: &CachedPath,
1120-
specifier: &str,
1121-
ctx: &mut Ctx,
1122-
) -> ResolveResult {
1123-
let Some(tsconfig_options) = &self.options.tsconfig else {
1124-
return Ok(None);
1125-
};
1126-
let tsconfig = self.load_tsconfig(
1127-
/* root */ true,
1128-
&tsconfig_options.config_file,
1129-
&tsconfig_options.references,
1130-
)?;
1131-
let paths = tsconfig.resolve(cached_path.path(), specifier);
1132-
for path in paths {
1133-
let cached_path = self.cache.value(&path);
1134-
if let Ok(path) = self.require_relative(&cached_path, ".", ctx) {
1135-
return Ok(Some(path));
1136-
}
1137-
}
1138-
Ok(None)
1139-
}
1140-
11411133
fn load_tsconfig(
11421134
&self,
11431135
root: bool,
@@ -1205,6 +1197,30 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
12051197
})
12061198
}
12071199

1200+
fn load_tsconfig_paths(
1201+
&self,
1202+
cached_path: &CachedPath,
1203+
specifier: &str,
1204+
ctx: &mut Ctx,
1205+
) -> ResolveResult {
1206+
let Some(tsconfig_options) = &self.options.tsconfig else {
1207+
return Ok(None);
1208+
};
1209+
let tsconfig = self.load_tsconfig(
1210+
/* root */ true,
1211+
&tsconfig_options.config_file,
1212+
&tsconfig_options.references,
1213+
)?;
1214+
let paths = tsconfig.resolve(cached_path.path(), specifier);
1215+
for path in paths {
1216+
let cached_path = self.cache.value(&path);
1217+
if let Ok(path) = self.require_relative(&cached_path, ".", ctx) {
1218+
return Ok(Some(path));
1219+
}
1220+
}
1221+
Ok(None)
1222+
}
1223+
12081224
fn get_extended_tsconfig_path(
12091225
&self,
12101226
directory: &CachedPath,

src/tsconfig.rs

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ pub struct TsConfig {
2525
/// Whether this is the caller tsconfig.
2626
/// Used for final template variable substitution when all configs are extended and merged.
2727
#[serde(skip)]
28-
root: bool,
28+
pub root: bool,
2929

3030
/// Path to `tsconfig.json`. Contains the `tsconfig.json` filename.
3131
#[serde(skip)]
32-
pub(crate) path: PathBuf,
32+
pub path: PathBuf,
3333

3434
#[serde(default)]
3535
pub extends: Option<ExtendsField>,
@@ -48,10 +48,10 @@ pub struct TsConfig {
4848
#[derive(Debug, Default, Deserialize)]
4949
#[serde(rename_all = "camelCase")]
5050
pub struct CompilerOptions {
51-
base_url: Option<PathBuf>,
51+
pub base_url: Option<PathBuf>,
5252

5353
/// Path aliases
54-
paths: Option<CompilerOptionsPathsMap>,
54+
pub paths: Option<CompilerOptionsPathsMap>,
5555

5656
/// The actual base for where path aliases are resolved from.
5757
#[serde(skip)]
@@ -73,7 +73,21 @@ pub struct ProjectReference {
7373
}
7474

7575
impl TsConfig {
76-
pub fn parse(root: bool, path: &Path, json: &mut str) -> Result<Self, serde_json::Error> {
76+
/// Directory to `tsconfig.json`
77+
///
78+
/// # Panics
79+
///
80+
/// * When the `tsconfig.json` path is misconfigured.
81+
pub fn directory(&self) -> &Path {
82+
debug_assert!(self.path.file_name().is_some());
83+
self.path.parent().unwrap()
84+
}
85+
86+
pub(crate) fn parse(
87+
root: bool,
88+
path: &Path,
89+
json: &mut str,
90+
) -> Result<Self, serde_json::Error> {
7791
_ = json_strip_comments::strip(json);
7892
let mut tsconfig: Self = serde_json::from_str(json)?;
7993
tsconfig.root = root;
@@ -89,7 +103,7 @@ impl TsConfig {
89103
Ok(tsconfig)
90104
}
91105

92-
pub fn build(mut self) -> Self {
106+
pub(crate) fn build(mut self) -> Self {
93107
if self.root {
94108
let dir = self.directory().to_path_buf();
95109
// Substitute template variable in `tsconfig.compilerOptions.paths`
@@ -104,17 +118,7 @@ impl TsConfig {
104118
self
105119
}
106120

107-
/// Directory to `tsconfig.json`
108-
///
109-
/// # Panics
110-
///
111-
/// * When the `tsconfig.json` path is misconfigured.
112-
pub fn directory(&self) -> &Path {
113-
debug_assert!(self.path.file_name().is_some());
114-
self.path.parent().unwrap()
115-
}
116-
117-
pub fn extend_tsconfig(&mut self, tsconfig: &Self) {
121+
pub(crate) fn extend_tsconfig(&mut self, tsconfig: &Self) {
118122
let compiler_options = &mut self.compiler_options;
119123
if compiler_options.paths.is_none() {
120124
compiler_options.paths_base = compiler_options
@@ -128,7 +132,7 @@ impl TsConfig {
128132
}
129133
}
130134

131-
pub fn resolve(&self, path: &Path, specifier: &str) -> Vec<PathBuf> {
135+
pub(crate) fn resolve(&self, path: &Path, specifier: &str) -> Vec<PathBuf> {
132136
if path.starts_with(self.base_path()) {
133137
let paths = self.resolve_path_alias(specifier);
134138
if !paths.is_empty() {
@@ -145,7 +149,7 @@ impl TsConfig {
145149

146150
// Copied from parcel
147151
// <https://github.com/parcel-bundler/parcel/blob/b6224fd519f95e68d8b93ba90376fd94c8b76e69/packages/utils/node-resolver-rs/src/tsconfig.rs#L93>
148-
pub fn resolve_path_alias(&self, specifier: &str) -> Vec<PathBuf> {
152+
pub(crate) fn resolve_path_alias(&self, specifier: &str) -> Vec<PathBuf> {
149153
if specifier.starts_with(['/', '.']) {
150154
return vec![];
151155
}

tests/integration_test.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@ fn package_json() {
4141
assert!(package_json.side_effects.as_ref().unwrap().is_object());
4242
}
4343

44+
#[test]
45+
fn tsconfig() {
46+
let resolver = Resolver::new(ResolveOptions::default());
47+
let tsconfig = resolver.resolve_tsconfig("./tests").unwrap();
48+
assert!(tsconfig.root);
49+
assert_eq!(tsconfig.path, PathBuf::from("./tests/tsconfig.json"));
50+
}
51+
4452
#[cfg(feature = "package_json_raw_json_api")]
4553
#[test]
4654
fn package_json_raw_json_api() {

tests/tsconfig.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
{
2+
}

0 commit comments

Comments
 (0)