Repository: oxc-project/oxc-resolver
Pull request #211 (2024-07-02 05:25:18)

fix: tsconfig self-reference throw error
Self-reference in tsconfig will cause short-lived invaild cache, more info see https://github.com/oxc-project/oxc-resolver/pull/210.

This PR will throw a TsconfigSelfReference error.

codspeed-hq[bot]: ## [CodSpeed Performance Report](https://codspeed.io/oxc-project/oxc-resolver/branches/jerrykingxyz:tsconfig)

### Merging #211 will **not alter performance**

<sub>Comparing <code>jerrykingxyz:tsconfig</code> (0d442e0) with <code>main</code> (a662c98)</sub>



### Summary

`✅ 2` untouched benchmarks






Patch:
diff --git a/src/error.rs b/src/error.rs
index d8b190b..901e67f 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -28,6 +28,10 @@ pub enum ResolveError {
     #[error("Tsconfig not found {0}")]
     TsconfigNotFound(PathBuf),
 
+    /// Tsconfig's project reference path points to it self
+    #[error("Tsconfig's project reference path points to this tsconfig {0}")]
+    TsconfigSelfReference(PathBuf),
+
     #[error("{0}")]
     IOError(IOError),
 
diff --git a/src/lib.rs b/src/lib.rs
index ac95d63..cfe6c25 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1099,7 +1099,14 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
                     let tsconfig = self.cache.tsconfig(
                         /* root */ true,
                         &reference_tsconfig_path,
-                        |_| Ok(()),
+                        |reference_tsconfig| {
+                            if reference_tsconfig.path == tsconfig.path {
+                                return Err(ResolveError::TsconfigSelfReference(
+                                    reference_tsconfig.path.clone(),
+                                ));
+                            }
+                            Ok(())
+                        },
                     )?;
                     reference.tsconfig.replace(tsconfig);
                 }
diff --git a/src/tsconfig.rs b/src/tsconfig.rs
index 9ea23c9..0de8e8e 100644
--- a/src/tsconfig.rs
+++ b/src/tsconfig.rs
@@ -29,7 +29,7 @@ pub struct TsConfig {
 
     /// Path to `tsconfig.json`. Contains the `tsconfig.json` filename.
     #[serde(skip)]
-    path: PathBuf,
+    pub(crate) path: PathBuf,
 
     #[serde(default)]
     pub extends: Option<ExtendsField>,


Test patch:
diff --git a/src/tests/tsconfig_project_references.rs b/src/tests/tsconfig_project_references.rs
index b552fef..0d019d2 100644
--- a/src/tests/tsconfig_project_references.rs
+++ b/src/tests/tsconfig_project_references.rs
@@ -99,3 +99,34 @@ fn manual() {
         assert_eq!(resolved_path, expected, "{request} {path:?}");
     }
 }
+
+#[test]
+fn self_reference() {
+    let f = super::fixture_root().join("tsconfig/cases/project_references");
+
+    #[rustfmt::skip]
+    let pass = [
+        (f.join("app"), vec!["./tsconfig.json".into()]),
+        (f.join("app/tsconfig.json"), vec!["./tsconfig.json".into()]),
+        (f.join("app"), vec![f.join("app")]),
+        (f.join("app/tsconfig.json"), vec![f.join("app")]),
+        (f.join("app/tsconfig.json"), vec![f.join("project_b"), f.join("app")]),
+    ];
+
+    for (config_file, reference_paths) in pass {
+        let resolver = Resolver::new(ResolveOptions {
+            tsconfig: Some(TsconfigOptions {
+                config_file: config_file.clone(),
+                references: TsconfigReferences::Paths(reference_paths.clone()),
+            }),
+            ..ResolveOptions::default()
+        });
+        let path = f.join("app");
+        let resolved_path = resolver.resolve(&path, "@/index.ts").map(|f| f.full_path());
+        assert_eq!(
+            resolved_path,
+            Err(ResolveError::TsconfigSelfReference(f.join("app/tsconfig.json"))),
+            "{config_file:?} {reference_paths:?}"
+        );
+    }
+}