Repository: sverweij/dependency-cruiser
Pull request #886 (2023-12-13 09:54:53)

fix(extract): prevents classifying core modules as aliased-tsconfig-base-url
## Description, Motivation and Context

- fixes the bug described in the title

## How Has This Been Tested?

- [x] green ci
- [x] additional automated non-regression tests

## Types of changes

- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] Documentation only change
- [ ] Refactor (non-breaking change which fixes an issue without changing functionality)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)

## Checklist

- [x] :book:

  - My change doesn't require a documentation update, or ...
  - it _does_ and I have updated it

- [x] :balance_scale:
  - The contribution will be subject to [The MIT license](https://github.com/sverweij/dependency-cruiser/blob/main/LICENSE), and I'm OK with that.
  - The contribution is my own original work.
  - I am ok with the stuff in [**CONTRIBUTING.md**](https://github.com/sverweij/dependency-cruiser/blob/main/.github/CONTRIBUTING.md).


codeclimate[bot]: Code Climate has analyzed commit c3b26a20 and detected **0 issues** on this pull request.

View more on [Code Climate](https://codeclimate.com/github/sverweij/dependency-cruiser/pull/886).


Patch:
diff --git a/src/extract/resolve/module-classifiers.mjs b/src/extract/resolve/module-classifiers.mjs
index c2aee380..1c52450a 100644
--- a/src/extract/resolve/module-classifiers.mjs
+++ b/src/extract/resolve/module-classifiers.mjs
@@ -1,3 +1,4 @@
+/* eslint-disable max-lines */
 import { isAbsolute, resolve as path_resolve } from "node:path";
 import { join as posix_join } from "node:path/posix";
 import { isMatch } from "picomatch";
@@ -219,6 +220,8 @@ function stripIndex(pModulePath) {
  *
  * https://www.typescriptlang.org/docs/handbook/modules/reference.html#baseurl
  *
+ * Assumes the pModuleName is not relative.
+ *
  * @param {string} pModuleName
  * @param {string} pResolvedModuleName
  * @param {string} pTSConfigBaseURL
@@ -228,7 +231,13 @@ function matchesTSConfigBaseURL(
   pResolvedModuleName,
   pTSConfigBaseURL,
 ) {
-  if (!pTSConfigBaseURL) {
+  // the pModuleName === pResolvedModuleName check is there to prevent
+  // false positives for core modules ('fs' resolved === 'fs') and modules that
+  // we couldn't resolve at all (e.g. 'this/does/not/exist' => 'this/does/not/exist')
+  //
+  // we could also check whether the moduleName is relative, but that's
+  // not efficient as that was already done before this function was called.
+  if (!pTSConfigBaseURL || pModuleName === pResolvedModuleName) {
     return false;
   }
   // "If baseUrl is set, TypeScript will resolve non-relative module names


Test patch:
diff --git a/test/extract/resolve/module-classifiers.get-alias-types.spec.mjs b/test/extract/resolve/module-classifiers.get-alias-types.spec.mjs
index 449f577f..09dea134 100644
--- a/test/extract/resolve/module-classifiers.get-alias-types.spec.mjs
+++ b/test/extract/resolve/module-classifiers.get-alias-types.spec.mjs
@@ -286,7 +286,7 @@ describe("[I] extract/resolve/module-classifiers - getAliasTypes", () => {
     );
   });
 
-  it("should return aliased and aliased-tsconfig for tsconfig alias", () => {
+  it("should return aliased, aliased-tsconfig and aliased-tsconfig-paths for tsconfig paths", () => {
     const lManifest = {
       name: "test",
       version: "1.0.0",
@@ -312,4 +312,51 @@ describe("[I] extract/resolve/module-classifiers - getAliasTypes", () => {
       ["aliased", "aliased-tsconfig", "aliased-tsconfig-paths"],
     );
   });
+
+  it("should return aliased, aliased-tsconfig and aliased-tsconfig-base-url when it matches tsconfig base urls", () => {
+    const lManifest = {
+      name: "test",
+      version: "1.0.0",
+      dependencies: {},
+    };
+    const lTranspileOptions = {
+      tsConfig: {
+        options: {
+          baseUrl: "./src",
+          paths: {
+            "@tsconfig/*": ["./something-else/*"],
+          },
+        },
+      },
+    };
+    deepEqual(
+      getAliasTypes(
+        "package",
+        "src/package/index.js",
+        {},
+        lManifest,
+        lTranspileOptions,
+      ),
+      ["aliased", "aliased-tsconfig", "aliased-tsconfig-base-url"],
+    );
+  });
+
+  it("should NOT return aliased, aliased-tsconfig and aliased-tsconfig-base-url when it's a core module", () => {
+    const lManifest = {
+      name: "test",
+      version: "1.0.0",
+      dependencies: {},
+    };
+    const lTranspileOptions = {
+      tsConfig: {
+        options: {
+          baseUrl: "./",
+          paths: {
+            "@tsconfig/*": ["./something-else/*"],
+          },
+        },
+      },
+    };
+    deepEqual(getAliasTypes("fs", "fs", {}, lManifest, lTranspileOptions), []);
+  });
 });