Skip to content

Commit

Permalink
feat(resolver): implement tsconfig-paths (#750)
Browse files Browse the repository at this point in the history
This PR includes 3 core functinalies of tsconfig-paths:

* matching tsconfig.compilerOptions.paths
* use tsconfig.compilerOptions.baseUrl
* extend tsconfig with tsconfig.extends

This PR does not include tsconfig.references
  • Loading branch information
Boshen authored Aug 17, 2023
1 parent 9fa52fa commit 6ab4ce0
Show file tree
Hide file tree
Showing 49 changed files with 1,273 additions and 27 deletions.
3 changes: 3 additions & 0 deletions crates/oxc_resolver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ repository.workspace = true
rust-version.workspace = true
categories.workspace = true

[lib]
doctest = false

[dependencies]
tracing = { workspace = true }
dashmap = { workspace = true }
Expand Down
8 changes: 7 additions & 1 deletion crates/oxc_resolver/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Oxc Resolver

* [enhanced-resolve](https://github.com/webpack/enhanced-resolve) configurations
* built-in [tsconfig-paths-webpack-plugin](https://github.com/dividab/tsconfig-paths-webpack-plugin)

## TODO

- [ ] use `thiserror` for better error messages
Expand Down Expand Up @@ -36,7 +39,10 @@

## Test

Tests ported from [enhanced-resolve](https://github.com/webpack/enhanced-resolve).
Tests ported from
* [enhanced-resolve](https://github.com/webpack/enhanced-resolve/tree/main/test)
* [tsconfig-path](https://github.com/dividab/tsconfig-paths/blob/master/src/__tests__/data/match-path-data.ts) and [parcel-resolver](https://github.com/parcel-bundler/parcel/tree/v2/packages/utils/node-resolver-core/test/fixture/tsconfig) for tsconfig-paths

Test cases are located in `./src/tests`, fixtures are located in `./tests`

Crossed out test files are irrelevant.
Expand Down
44 changes: 37 additions & 7 deletions crates/oxc_resolver/src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ use std::{
use dashmap::DashMap;
use rustc_hash::FxHasher;

use crate::{package_json::PackageJson, FileMetadata, FileSystem, ResolveError, ResolveOptions};
use crate::{
package_json::PackageJson, FileMetadata, FileSystem, ResolveError, ResolveOptions, TsConfig,
};

#[derive(Default)]
pub struct Cache<Fs> {
pub(crate) fs: Fs,
// Using IdentityHasher to avoid double hashing in the `get` + `insert` case.
cache: DashMap<u64, CachedPath, BuildHasherDefault<IdentityHasher>>,
tsconfigs: DashMap<u64, Arc<TsConfig>, BuildHasherDefault<IdentityHasher>>,
}

#[derive(Default)]
Expand All @@ -33,19 +37,14 @@ impl Hasher for IdentityHasher {
}
}

impl<Fs: FileSystem> Default for Cache<Fs> {
fn default() -> Self {
Self { fs: Fs::default(), cache: DashMap::default() }
}
}

impl<Fs: FileSystem> Cache<Fs> {
pub fn new(fs: Fs) -> Self {
Self { fs, ..Self::default() }
}

pub fn clear(&self) {
self.cache.clear();
self.tsconfigs.clear();
}

pub fn value(&self, path: &Path) -> CachedPath {
Expand All @@ -63,6 +62,33 @@ impl<Fs: FileSystem> Cache<Fs> {
self.cache.insert(hash, data.clone());
data
}

pub fn tsconfig(
&self,
tsconfig_path: &Path,
callback: impl FnOnce(&mut TsConfig) -> Result<(), ResolveError>, // callback for modifying tsconfig with `extends`
) -> Result<Arc<TsConfig>, ResolveError> {
let hash = {
let mut hasher = FxHasher::default();
tsconfig_path.hash(&mut hasher);
hasher.finish()
};
self.tsconfigs
.entry(hash)
.or_try_insert_with(|| {
let mut tsconfig_string = self
.fs
.read_to_string(tsconfig_path)
.map_err(|_| ResolveError::NotFound(tsconfig_path.to_path_buf()))?;
let mut tsconfig =
TsConfig::parse(tsconfig_path, &mut tsconfig_string).map_err(|error| {
ResolveError::from_serde_json_error(tsconfig_path.to_path_buf(), &error)
})?;
callback(&mut tsconfig)?;
Ok(Arc::new(tsconfig))
})
.map(|r| Arc::clone(r.value()))
}
}

#[derive(Clone)]
Expand Down Expand Up @@ -109,6 +135,10 @@ impl CachedPathImpl {
self.path.to_path_buf()
}

pub fn parent(&self) -> Option<&CachedPath> {
self.parent.as_ref()
}

fn meta<Fs: FileSystem>(&self, fs: &Fs) -> Option<FileMetadata> {
*self.meta.get_or_init(|| fs.metadata(&self.path).ok())
}
Expand Down
Loading

0 comments on commit 6ab4ce0

Please sign in to comment.