From 9b349c16c79aa955f3834d575f9c94ef24da1576 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Wed, 10 Dec 2025 21:20:15 -0800
Subject: [PATCH 01/29] Fix resolver misport
---
internal/module/resolver.go | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/internal/module/resolver.go b/internal/module/resolver.go
index f5ae94746fc..2114f3c5642 100644
--- a/internal/module/resolver.go
+++ b/internal/module/resolver.go
@@ -985,7 +985,7 @@ func (r *resolutionState) loadModuleFromSpecificNodeModulesDirectory(ext extensi
}
if fromDirectory := r.loadNodeModuleFromDirectoryWorker(ext, candidate, !nodeModulesDirectoryExists, packageInfo); !fromDirectory.shouldContinueSearching() {
- fromDirectory.packageId = r.getPackageId(packageDirectory, packageInfo)
+ fromDirectory.packageId = r.getPackageId(fromDirectory.path, packageInfo)
return fromDirectory
}
}
@@ -994,12 +994,12 @@ func (r *resolutionState) loadModuleFromSpecificNodeModulesDirectory(ext extensi
loader := func(extensions extensions, candidate string, onlyRecordFailures bool) *resolved {
if rest != "" || !r.esmMode {
if fromFile := r.loadModuleFromFile(extensions, candidate, onlyRecordFailures); !fromFile.shouldContinueSearching() {
- fromFile.packageId = r.getPackageId(packageDirectory, packageInfo)
+ fromFile.packageId = r.getPackageId(fromFile.path, packageInfo)
return fromFile
}
}
if fromDirectory := r.loadNodeModuleFromDirectoryWorker(extensions, candidate, onlyRecordFailures, packageInfo); !fromDirectory.shouldContinueSearching() {
- fromDirectory.packageId = r.getPackageId(packageDirectory, packageInfo)
+ fromDirectory.packageId = r.getPackageId(fromDirectory.path, packageInfo)
return fromDirectory
}
// !!! this is ported exactly, but checking for null seems wrong?
@@ -1009,7 +1009,7 @@ func (r *resolutionState) loadModuleFromSpecificNodeModulesDirectory(ext extensi
// EsmMode disables index lookup in `loadNodeModuleFromDirectoryWorker` generally, however non-relative package resolutions still assume
// a default `index.js` entrypoint if no `main` or `exports` are present
if indexResult := r.loadModuleFromFile(extensions, tspath.CombinePaths(candidate, "index.js"), onlyRecordFailures); !indexResult.shouldContinueSearching() {
- indexResult.packageId = r.getPackageId(packageDirectory, packageInfo)
+ indexResult.packageId = r.getPackageId(indexResult.path, packageInfo)
return indexResult
}
}
From dc9841e6e2df102911d590b9c7b0e005c85e5953 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Wed, 10 Dec 2025 21:22:14 -0800
Subject: [PATCH 02/29] Update baselines
---
...age_relativeImportWithinPackage.trace.json | 4 ++--
...ativeImportWithinPackage_scoped.trace.json | 4 ++--
...on_packageJson_yesAtPackageRoot.trace.json | 2 +-
...AtPackageRoot_fakeScopedPackage.trace.json | 2 +-
...ageRoot_mainFieldInSubDirectory.trace.json | 2 +-
.../reactJsxReactResolvedNodeNext.trace.json | 2 +-
...esolvepackagejsonexports=false).trace.json | 2 +-
.../typesVersions.ambientModules.trace.json | 2 +-
.../typesVersions.multiFile.trace.json | 4 ++--
...VersionsDeclarationEmit.ambient.trace.json | 2 +-
...rsionsDeclarationEmit.multiFile.trace.json | 4 ++--
...it.multiFileBackReferenceToSelf.trace.json | 4 ++--
...ultiFileBackReferenceToUnmapped.trace.json | 4 ++--
...project-correctly-with-preserveSymlinks.js | 2 +-
...-file-from-referenced-project-correctly.js | 2 +-
...for-changes-to-package-json-main-fields.js | 2 +-
...t-correctly-with-cts-and-mts-extensions.js | 4 ++--
...ibling-package-through-indirect-symlink.js | 4 ++--
...er-symlinked-package-with-indirect-link.js | 12 +++++-----
...gh-source-and-another-symlinked-package.js | 8 +++----
.../tsc/moduleResolution/pnpm-style-layout.js | 24 +++++++++----------
21 files changed, 48 insertions(+), 48 deletions(-)
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.trace.json b/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.trace.json
index dc6e3a92cf3..8de27591a16 100644
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.trace.json
+++ b/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.trace.json
@@ -11,7 +11,7 @@ File '/node_modules/foo/use.tsx' does not exist.
File '/node_modules/foo/use.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/node_modules/foo/use.d.ts', result '/node_modules/foo/use.d.ts'.
-======== Module name 'foo/use' was successfully resolved to '/node_modules/foo/use.d.ts' with Package ID 'foo@1.2.3'. ========
+======== Module name 'foo/use' was successfully resolved to '/node_modules/foo/use.d.ts' with Package ID 'foo/use.d.ts@1.2.3'. ========
======== Resolving module 'a' from '/index.ts'. ========
Module resolution kind is not specified, using 'Bundler'.
Resolving in CJS mode with conditions 'require', 'types'.
@@ -58,4 +58,4 @@ File '/node_modules/a/node_modules/foo/index.tsx' does not exist.
File '/node_modules/a/node_modules/foo/index.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/node_modules/a/node_modules/foo/index.d.ts', result '/node_modules/a/node_modules/foo/index.d.ts'.
-======== Module name 'foo' was successfully resolved to '/node_modules/a/node_modules/foo/index.d.ts' with Package ID 'foo@1.2.3'. ========
+======== Module name 'foo' was successfully resolved to '/node_modules/a/node_modules/foo/index.d.ts' with Package ID 'foo/index.d.ts@1.2.3'. ========
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage_scoped.trace.json b/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage_scoped.trace.json
index 9e9981afde1..39f2d2b98bc 100644
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage_scoped.trace.json
+++ b/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage_scoped.trace.json
@@ -11,7 +11,7 @@ File '/node_modules/@foo/bar/use.tsx' does not exist.
File '/node_modules/@foo/bar/use.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/node_modules/@foo/bar/use.d.ts', result '/node_modules/@foo/bar/use.d.ts'.
-======== Module name '@foo/bar/use' was successfully resolved to '/node_modules/@foo/bar/use.d.ts' with Package ID '@foo/bar@1.2.3'. ========
+======== Module name '@foo/bar/use' was successfully resolved to '/node_modules/@foo/bar/use.d.ts' with Package ID '@foo/bar/use.d.ts@1.2.3'. ========
======== Resolving module 'a' from '/index.ts'. ========
Module resolution kind is not specified, using 'Bundler'.
Resolving in CJS mode with conditions 'require', 'types'.
@@ -58,4 +58,4 @@ File '/node_modules/a/node_modules/@foo/bar/index.tsx' does not exist.
File '/node_modules/a/node_modules/@foo/bar/index.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/node_modules/a/node_modules/@foo/bar/index.d.ts', result '/node_modules/a/node_modules/@foo/bar/index.d.ts'.
-======== Module name '@foo/bar' was successfully resolved to '/node_modules/a/node_modules/@foo/bar/index.d.ts' with Package ID '@foo/bar@1.2.3'. ========
+======== Module name '@foo/bar' was successfully resolved to '/node_modules/a/node_modules/@foo/bar/index.d.ts' with Package ID '@foo/bar/index.d.ts@1.2.3'. ========
diff --git a/testdata/baselines/reference/submodule/compiler/moduleResolution_packageJson_yesAtPackageRoot.trace.json b/testdata/baselines/reference/submodule/compiler/moduleResolution_packageJson_yesAtPackageRoot.trace.json
index f47943dc9ed..642813b1a23 100644
--- a/testdata/baselines/reference/submodule/compiler/moduleResolution_packageJson_yesAtPackageRoot.trace.json
+++ b/testdata/baselines/reference/submodule/compiler/moduleResolution_packageJson_yesAtPackageRoot.trace.json
@@ -25,4 +25,4 @@ File '/node_modules/foo/bar.jsx' does not exist.
File '/node_modules/foo/bar/index.js' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/node_modules/foo/bar/index.js', result '/node_modules/foo/bar/index.js'.
-======== Module name 'foo/bar' was successfully resolved to '/node_modules/foo/bar/index.js' with Package ID 'foo@1.2.3'. ========
+======== Module name 'foo/bar' was successfully resolved to '/node_modules/foo/bar/index.js' with Package ID 'foo/bar/index.js@1.2.3'. ========
diff --git a/testdata/baselines/reference/submodule/compiler/moduleResolution_packageJson_yesAtPackageRoot_fakeScopedPackage.trace.json b/testdata/baselines/reference/submodule/compiler/moduleResolution_packageJson_yesAtPackageRoot_fakeScopedPackage.trace.json
index 6dd76efa8d5..c75b64190d1 100644
--- a/testdata/baselines/reference/submodule/compiler/moduleResolution_packageJson_yesAtPackageRoot_fakeScopedPackage.trace.json
+++ b/testdata/baselines/reference/submodule/compiler/moduleResolution_packageJson_yesAtPackageRoot_fakeScopedPackage.trace.json
@@ -25,4 +25,4 @@ File '/node_modules/foo/@bar.jsx' does not exist.
File '/node_modules/foo/@bar/index.js' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/node_modules/foo/@bar/index.js', result '/node_modules/foo/@bar/index.js'.
-======== Module name 'foo/@bar' was successfully resolved to '/node_modules/foo/@bar/index.js' with Package ID 'foo@1.2.3'. ========
+======== Module name 'foo/@bar' was successfully resolved to '/node_modules/foo/@bar/index.js' with Package ID 'foo/@bar/index.js@1.2.3'. ========
diff --git a/testdata/baselines/reference/submodule/compiler/moduleResolution_packageJson_yesAtPackageRoot_mainFieldInSubDirectory.trace.json b/testdata/baselines/reference/submodule/compiler/moduleResolution_packageJson_yesAtPackageRoot_mainFieldInSubDirectory.trace.json
index eeba5d48f4c..9ab3791dde2 100644
--- a/testdata/baselines/reference/submodule/compiler/moduleResolution_packageJson_yesAtPackageRoot_mainFieldInSubDirectory.trace.json
+++ b/testdata/baselines/reference/submodule/compiler/moduleResolution_packageJson_yesAtPackageRoot_mainFieldInSubDirectory.trace.json
@@ -18,4 +18,4 @@ File '/node_modules/foo/src/index.tsx' does not exist.
File '/node_modules/foo/src/index.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/node_modules/foo/src/index.d.ts', result '/node_modules/foo/src/index.d.ts'.
-======== Module name 'foo' was successfully resolved to '/node_modules/foo/src/index.d.ts' with Package ID 'foo@1.2.3'. ========
+======== Module name 'foo' was successfully resolved to '/node_modules/foo/src/index.d.ts' with Package ID 'foo/src/index.d.ts@1.2.3'. ========
diff --git a/testdata/baselines/reference/submodule/compiler/reactJsxReactResolvedNodeNext.trace.json b/testdata/baselines/reference/submodule/compiler/reactJsxReactResolvedNodeNext.trace.json
index 590f425b3a5..028a3d5f62b 100644
--- a/testdata/baselines/reference/submodule/compiler/reactJsxReactResolvedNodeNext.trace.json
+++ b/testdata/baselines/reference/submodule/compiler/reactJsxReactResolvedNodeNext.trace.json
@@ -10,7 +10,7 @@ Found 'package.json' at '/.src/node_modules/@types/react/package.json'.
File '/.src/node_modules/@types/react/jsx-runtime.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/.src/node_modules/@types/react/jsx-runtime.d.ts', result '/.src/node_modules/@types/react/jsx-runtime.d.ts'.
-======== Module name 'react/jsx-runtime' was successfully resolved to '/.src/node_modules/@types/react/jsx-runtime.d.ts' with Package ID '@types/react@0.0.1'. ========
+======== Module name 'react/jsx-runtime' was successfully resolved to '/.src/node_modules/@types/react/jsx-runtime.d.ts' with Package ID '@types/react/jsx-runtime.d.ts@0.0.1'. ========
======== Resolving module './' from '/.src/node_modules/@types/react/jsx-runtime.d.ts'. ========
Module resolution kind is not specified, using 'NodeNext'.
Resolving in CJS mode with conditions 'require', 'types', 'node'.
diff --git a/testdata/baselines/reference/submodule/conformance/customConditions(resolvepackagejsonexports=false).trace.json b/testdata/baselines/reference/submodule/conformance/customConditions(resolvepackagejsonexports=false).trace.json
index 2f3ee4888b7..96a621363af 100644
--- a/testdata/baselines/reference/submodule/conformance/customConditions(resolvepackagejsonexports=false).trace.json
+++ b/testdata/baselines/reference/submodule/conformance/customConditions(resolvepackagejsonexports=false).trace.json
@@ -18,4 +18,4 @@ File '/node_modules/lodash/index.tsx' does not exist.
File '/node_modules/lodash/index.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/node_modules/lodash/index.d.ts', result '/node_modules/lodash/index.d.ts'.
-======== Module name 'lodash' was successfully resolved to '/node_modules/lodash/index.d.ts' with Package ID 'lodash@1.0.0'. ========
+======== Module name 'lodash' was successfully resolved to '/node_modules/lodash/index.d.ts' with Package ID 'lodash/index.d.ts@1.0.0'. ========
diff --git a/testdata/baselines/reference/submodule/conformance/typesVersions.ambientModules.trace.json b/testdata/baselines/reference/submodule/conformance/typesVersions.ambientModules.trace.json
index 3880078ee4f..3cd5db8e87e 100644
--- a/testdata/baselines/reference/submodule/conformance/typesVersions.ambientModules.trace.json
+++ b/testdata/baselines/reference/submodule/conformance/typesVersions.ambientModules.trace.json
@@ -21,7 +21,7 @@ File '/.src/node_modules/ext/ts3.1/index.tsx' does not exist.
File '/.src/node_modules/ext/ts3.1/index.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/.src/node_modules/ext/ts3.1/index.d.ts', result '/.src/node_modules/ext/ts3.1/index.d.ts'.
-======== Module name 'ext' was successfully resolved to '/.src/node_modules/ext/ts3.1/index.d.ts' with Package ID 'ext@1.0.0'. ========
+======== Module name 'ext' was successfully resolved to '/.src/node_modules/ext/ts3.1/index.d.ts' with Package ID 'ext/ts3.1/index.d.ts@1.0.0'. ========
======== Resolving module 'ext/other' from '/.src/main.ts'. ========
Module resolution kind is not specified, using 'Bundler'.
Resolving in CJS mode with conditions 'require', 'types'.
diff --git a/testdata/baselines/reference/submodule/conformance/typesVersions.multiFile.trace.json b/testdata/baselines/reference/submodule/conformance/typesVersions.multiFile.trace.json
index 27fef0f5833..dd86ae732a2 100644
--- a/testdata/baselines/reference/submodule/conformance/typesVersions.multiFile.trace.json
+++ b/testdata/baselines/reference/submodule/conformance/typesVersions.multiFile.trace.json
@@ -21,7 +21,7 @@ File '/.src/node_modules/ext/ts3.1/index.tsx' does not exist.
File '/.src/node_modules/ext/ts3.1/index.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/.src/node_modules/ext/ts3.1/index.d.ts', result '/.src/node_modules/ext/ts3.1/index.d.ts'.
-======== Module name 'ext' was successfully resolved to '/.src/node_modules/ext/ts3.1/index.d.ts' with Package ID 'ext@1.0.0'. ========
+======== Module name 'ext' was successfully resolved to '/.src/node_modules/ext/ts3.1/index.d.ts' with Package ID 'ext/ts3.1/index.d.ts@1.0.0'. ========
======== Resolving module 'ext/other' from '/.src/main.ts'. ========
Module resolution kind is not specified, using 'Bundler'.
Resolving in CJS mode with conditions 'require', 'types'.
@@ -39,4 +39,4 @@ File '/.src/node_modules/ext/ts3.1/other.tsx' does not exist.
File '/.src/node_modules/ext/ts3.1/other.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/.src/node_modules/ext/ts3.1/other.d.ts', result '/.src/node_modules/ext/ts3.1/other.d.ts'.
-======== Module name 'ext/other' was successfully resolved to '/.src/node_modules/ext/ts3.1/other.d.ts' with Package ID 'ext@1.0.0'. ========
+======== Module name 'ext/other' was successfully resolved to '/.src/node_modules/ext/ts3.1/other.d.ts' with Package ID 'ext/ts3.1/other.d.ts@1.0.0'. ========
diff --git a/testdata/baselines/reference/submodule/conformance/typesVersionsDeclarationEmit.ambient.trace.json b/testdata/baselines/reference/submodule/conformance/typesVersionsDeclarationEmit.ambient.trace.json
index 3880078ee4f..3cd5db8e87e 100644
--- a/testdata/baselines/reference/submodule/conformance/typesVersionsDeclarationEmit.ambient.trace.json
+++ b/testdata/baselines/reference/submodule/conformance/typesVersionsDeclarationEmit.ambient.trace.json
@@ -21,7 +21,7 @@ File '/.src/node_modules/ext/ts3.1/index.tsx' does not exist.
File '/.src/node_modules/ext/ts3.1/index.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/.src/node_modules/ext/ts3.1/index.d.ts', result '/.src/node_modules/ext/ts3.1/index.d.ts'.
-======== Module name 'ext' was successfully resolved to '/.src/node_modules/ext/ts3.1/index.d.ts' with Package ID 'ext@1.0.0'. ========
+======== Module name 'ext' was successfully resolved to '/.src/node_modules/ext/ts3.1/index.d.ts' with Package ID 'ext/ts3.1/index.d.ts@1.0.0'. ========
======== Resolving module 'ext/other' from '/.src/main.ts'. ========
Module resolution kind is not specified, using 'Bundler'.
Resolving in CJS mode with conditions 'require', 'types'.
diff --git a/testdata/baselines/reference/submodule/conformance/typesVersionsDeclarationEmit.multiFile.trace.json b/testdata/baselines/reference/submodule/conformance/typesVersionsDeclarationEmit.multiFile.trace.json
index 27fef0f5833..dd86ae732a2 100644
--- a/testdata/baselines/reference/submodule/conformance/typesVersionsDeclarationEmit.multiFile.trace.json
+++ b/testdata/baselines/reference/submodule/conformance/typesVersionsDeclarationEmit.multiFile.trace.json
@@ -21,7 +21,7 @@ File '/.src/node_modules/ext/ts3.1/index.tsx' does not exist.
File '/.src/node_modules/ext/ts3.1/index.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/.src/node_modules/ext/ts3.1/index.d.ts', result '/.src/node_modules/ext/ts3.1/index.d.ts'.
-======== Module name 'ext' was successfully resolved to '/.src/node_modules/ext/ts3.1/index.d.ts' with Package ID 'ext@1.0.0'. ========
+======== Module name 'ext' was successfully resolved to '/.src/node_modules/ext/ts3.1/index.d.ts' with Package ID 'ext/ts3.1/index.d.ts@1.0.0'. ========
======== Resolving module 'ext/other' from '/.src/main.ts'. ========
Module resolution kind is not specified, using 'Bundler'.
Resolving in CJS mode with conditions 'require', 'types'.
@@ -39,4 +39,4 @@ File '/.src/node_modules/ext/ts3.1/other.tsx' does not exist.
File '/.src/node_modules/ext/ts3.1/other.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/.src/node_modules/ext/ts3.1/other.d.ts', result '/.src/node_modules/ext/ts3.1/other.d.ts'.
-======== Module name 'ext/other' was successfully resolved to '/.src/node_modules/ext/ts3.1/other.d.ts' with Package ID 'ext@1.0.0'. ========
+======== Module name 'ext/other' was successfully resolved to '/.src/node_modules/ext/ts3.1/other.d.ts' with Package ID 'ext/ts3.1/other.d.ts@1.0.0'. ========
diff --git a/testdata/baselines/reference/submodule/conformance/typesVersionsDeclarationEmit.multiFileBackReferenceToSelf.trace.json b/testdata/baselines/reference/submodule/conformance/typesVersionsDeclarationEmit.multiFileBackReferenceToSelf.trace.json
index e5390b7825d..539fad4d830 100644
--- a/testdata/baselines/reference/submodule/conformance/typesVersionsDeclarationEmit.multiFileBackReferenceToSelf.trace.json
+++ b/testdata/baselines/reference/submodule/conformance/typesVersionsDeclarationEmit.multiFileBackReferenceToSelf.trace.json
@@ -21,7 +21,7 @@ File '/.src/node_modules/ext/ts3.1/index.tsx' does not exist.
File '/.src/node_modules/ext/ts3.1/index.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/.src/node_modules/ext/ts3.1/index.d.ts', result '/.src/node_modules/ext/ts3.1/index.d.ts'.
-======== Module name 'ext' was successfully resolved to '/.src/node_modules/ext/ts3.1/index.d.ts' with Package ID 'ext@1.0.0'. ========
+======== Module name 'ext' was successfully resolved to '/.src/node_modules/ext/ts3.1/index.d.ts' with Package ID 'ext/ts3.1/index.d.ts@1.0.0'. ========
======== Resolving module 'ext/other' from '/.src/main.ts'. ========
Module resolution kind is not specified, using 'Bundler'.
Resolving in CJS mode with conditions 'require', 'types'.
@@ -39,7 +39,7 @@ File '/.src/node_modules/ext/ts3.1/other.tsx' does not exist.
File '/.src/node_modules/ext/ts3.1/other.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/.src/node_modules/ext/ts3.1/other.d.ts', result '/.src/node_modules/ext/ts3.1/other.d.ts'.
-======== Module name 'ext/other' was successfully resolved to '/.src/node_modules/ext/ts3.1/other.d.ts' with Package ID 'ext@1.0.0'. ========
+======== Module name 'ext/other' was successfully resolved to '/.src/node_modules/ext/ts3.1/other.d.ts' with Package ID 'ext/ts3.1/other.d.ts@1.0.0'. ========
======== Resolving module '../' from '/.src/node_modules/ext/ts3.1/index.d.ts'. ========
Module resolution kind is not specified, using 'Bundler'.
Resolving in CJS mode with conditions 'require', 'types'.
diff --git a/testdata/baselines/reference/submodule/conformance/typesVersionsDeclarationEmit.multiFileBackReferenceToUnmapped.trace.json b/testdata/baselines/reference/submodule/conformance/typesVersionsDeclarationEmit.multiFileBackReferenceToUnmapped.trace.json
index 59839558cfa..1147e4d6c5e 100644
--- a/testdata/baselines/reference/submodule/conformance/typesVersionsDeclarationEmit.multiFileBackReferenceToUnmapped.trace.json
+++ b/testdata/baselines/reference/submodule/conformance/typesVersionsDeclarationEmit.multiFileBackReferenceToUnmapped.trace.json
@@ -21,7 +21,7 @@ File '/.src/node_modules/ext/ts3.1/index.tsx' does not exist.
File '/.src/node_modules/ext/ts3.1/index.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/.src/node_modules/ext/ts3.1/index.d.ts', result '/.src/node_modules/ext/ts3.1/index.d.ts'.
-======== Module name 'ext' was successfully resolved to '/.src/node_modules/ext/ts3.1/index.d.ts' with Package ID 'ext@1.0.0'. ========
+======== Module name 'ext' was successfully resolved to '/.src/node_modules/ext/ts3.1/index.d.ts' with Package ID 'ext/ts3.1/index.d.ts@1.0.0'. ========
======== Resolving module 'ext/other' from '/.src/main.ts'. ========
Module resolution kind is not specified, using 'Bundler'.
Resolving in CJS mode with conditions 'require', 'types'.
@@ -37,7 +37,7 @@ File '/.src/node_modules/ext/other.tsx' does not exist.
File '/.src/node_modules/ext/other.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/.src/node_modules/ext/other.d.ts', result '/.src/node_modules/ext/other.d.ts'.
-======== Module name 'ext/other' was successfully resolved to '/.src/node_modules/ext/other.d.ts' with Package ID 'ext@1.0.0'. ========
+======== Module name 'ext/other' was successfully resolved to '/.src/node_modules/ext/other.d.ts' with Package ID 'ext/other.d.ts@1.0.0'. ========
======== Resolving module '../other' from '/.src/node_modules/ext/ts3.1/index.d.ts'. ========
Module resolution kind is not specified, using 'Bundler'.
Resolving in CJS mode with conditions 'require', 'types'.
diff --git a/testdata/baselines/reference/tsbuild/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly-with-preserveSymlinks.js b/testdata/baselines/reference/tsbuild/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly-with-preserveSymlinks.js
index 45b4bc5898a..034091b1994 100644
--- a/testdata/baselines/reference/tsbuild/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly-with-preserveSymlinks.js
+++ b/testdata/baselines/reference/tsbuild/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly-with-preserveSymlinks.js
@@ -88,7 +88,7 @@ File '/user/username/projects/myproject/node_modules/pkg2/build/index.ts' does n
File '/user/username/projects/myproject/node_modules/pkg2/build/index.tsx' does not exist.
File '/user/username/projects/myproject/node_modules/pkg2/build/index.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
-======== Module name 'pkg2' was successfully resolved to '/user/username/projects/myproject/node_modules/pkg2/build/index.d.ts' with Package ID 'pkg2@1.0.0'. ========
+======== Module name 'pkg2' was successfully resolved to '/user/username/projects/myproject/node_modules/pkg2/build/index.d.ts' with Package ID 'pkg2/build/index.d.ts@1.0.0'. ========
======== Resolving module 'const' from '/user/username/projects/myproject/packages/pkg2/index.ts'. ========
Using compiler options of project reference redirect '/user/username/projects/myproject/packages/pkg2/tsconfig.json'.
Module resolution kind is not specified, using 'Bundler'.
diff --git a/testdata/baselines/reference/tsbuild/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly.js b/testdata/baselines/reference/tsbuild/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly.js
index 3744082d9dd..e121e2932d8 100644
--- a/testdata/baselines/reference/tsbuild/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly.js
+++ b/testdata/baselines/reference/tsbuild/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly.js
@@ -89,7 +89,7 @@ File '/user/username/projects/myproject/node_modules/pkg2/build/index.tsx' does
File '/user/username/projects/myproject/node_modules/pkg2/build/index.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/user/username/projects/myproject/node_modules/pkg2/build/index.d.ts', result '/user/username/projects/myproject/packages/pkg2/build/index.d.ts'.
-======== Module name 'pkg2' was successfully resolved to '/user/username/projects/myproject/packages/pkg2/build/index.d.ts' with Package ID 'pkg2@1.0.0'. ========
+======== Module name 'pkg2' was successfully resolved to '/user/username/projects/myproject/packages/pkg2/build/index.d.ts' with Package ID 'pkg2/build/index.d.ts@1.0.0'. ========
======== Resolving module 'const' from '/user/username/projects/myproject/packages/pkg2/index.ts'. ========
Using compiler options of project reference redirect '/user/username/projects/myproject/packages/pkg2/tsconfig.json'.
Module resolution kind is not specified, using 'Bundler'.
diff --git a/testdata/baselines/reference/tsbuildWatch/moduleResolution/build-mode-watches-for-changes-to-package-json-main-fields.js b/testdata/baselines/reference/tsbuildWatch/moduleResolution/build-mode-watches-for-changes-to-package-json-main-fields.js
index e9cdbb7c507..098eebd9bc4 100644
--- a/testdata/baselines/reference/tsbuildWatch/moduleResolution/build-mode-watches-for-changes-to-package-json-main-fields.js
+++ b/testdata/baselines/reference/tsbuildWatch/moduleResolution/build-mode-watches-for-changes-to-package-json-main-fields.js
@@ -86,7 +86,7 @@ File '/user/username/projects/myproject/node_modules/pkg2/build/index.tsx' does
File '/user/username/projects/myproject/node_modules/pkg2/build/index.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/user/username/projects/myproject/node_modules/pkg2/build/index.d.ts', result '/user/username/projects/myproject/packages/pkg2/build/index.d.ts'.
-======== Module name 'pkg2' was successfully resolved to '/user/username/projects/myproject/packages/pkg2/build/index.d.ts' with Package ID 'pkg2@1.0.0'. ========
+======== Module name 'pkg2' was successfully resolved to '/user/username/projects/myproject/packages/pkg2/build/index.d.ts' with Package ID 'pkg2/build/index.d.ts@1.0.0'. ========
======== Resolving module './const.js' from '/user/username/projects/myproject/packages/pkg2/index.ts'. ========
Using compiler options of project reference redirect '/user/username/projects/myproject/packages/pkg2/tsconfig.json'.
Module resolution kind is not specified, using 'Bundler'.
diff --git a/testdata/baselines/reference/tsbuildWatch/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly-with-cts-and-mts-extensions.js b/testdata/baselines/reference/tsbuildWatch/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly-with-cts-and-mts-extensions.js
index 44005f78156..c9827a6f337 100644
--- a/testdata/baselines/reference/tsbuildWatch/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly-with-cts-and-mts-extensions.js
+++ b/testdata/baselines/reference/tsbuildWatch/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly-with-cts-and-mts-extensions.js
@@ -85,7 +85,7 @@ File '/user/username/projects/myproject/node_modules/pkg2/build/index.tsx' does
File '/user/username/projects/myproject/node_modules/pkg2/build/index.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/user/username/projects/myproject/node_modules/pkg2/build/index.d.ts', result '/user/username/projects/myproject/packages/pkg2/build/index.d.ts'.
-======== Module name 'pkg2' was successfully resolved to '/user/username/projects/myproject/packages/pkg2/build/index.d.ts' with Package ID 'pkg2@1.0.0'. ========
+======== Module name 'pkg2' was successfully resolved to '/user/username/projects/myproject/packages/pkg2/build/index.d.ts' with Package ID 'pkg2/build/index.d.ts@1.0.0'. ========
======== Resolving module './const.cjs' from '/user/username/projects/myproject/packages/pkg2/index.ts'. ========
Using compiler options of project reference redirect '/user/username/projects/myproject/packages/pkg2/tsconfig.json'.
Module resolution kind is not specified, using 'Node16'.
@@ -383,7 +383,7 @@ File '/user/username/projects/myproject/node_modules/pkg2/build/index.cts' does
File '/user/username/projects/myproject/node_modules/pkg2/build/index.d.cts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/user/username/projects/myproject/node_modules/pkg2/build/index.d.cts', result '/user/username/projects/myproject/packages/pkg2/build/index.d.cts'.
-======== Module name 'pkg2' was successfully resolved to '/user/username/projects/myproject/packages/pkg2/build/index.d.cts' with Package ID 'pkg2@1.0.0'. ========
+======== Module name 'pkg2' was successfully resolved to '/user/username/projects/myproject/packages/pkg2/build/index.d.cts' with Package ID 'pkg2/build/index.d.cts@1.0.0'. ========
======== Resolving module './const.cjs' from '/user/username/projects/myproject/packages/pkg2/index.cts'. ========
Using compiler options of project reference redirect '/user/username/projects/myproject/packages/pkg2/tsconfig.json'.
Module resolution kind is not specified, using 'Node16'.
diff --git a/testdata/baselines/reference/tsc/declarationEmit/when-pkg-references-sibling-package-through-indirect-symlink.js b/testdata/baselines/reference/tsc/declarationEmit/when-pkg-references-sibling-package-through-indirect-symlink.js
index 710a6e7b26d..914182b9489 100644
--- a/testdata/baselines/reference/tsc/declarationEmit/when-pkg-references-sibling-package-through-indirect-symlink.js
+++ b/testdata/baselines/reference/tsc/declarationEmit/when-pkg-references-sibling-package-through-indirect-symlink.js
@@ -68,11 +68,11 @@ Output::
pkg1/dist/types.d.ts
Imported via './types' from file 'pkg1/dist/index.d.ts'
pkg1/dist/index.d.ts
- Imported via '@raymondfeng/pkg1' from file 'pkg2/dist/types.d.ts' with packageId '@raymondfeng/pkg1@1.0.0'
+ Imported via '@raymondfeng/pkg1' from file 'pkg2/dist/types.d.ts' with packageId '@raymondfeng/pkg1/dist/index.d.ts@1.0.0'
pkg2/dist/types.d.ts
Imported via './types' from file 'pkg2/dist/index.d.ts'
pkg2/dist/index.d.ts
- Imported via "@raymondfeng/pkg2" from file 'pkg3/src/keys.ts' with packageId '@raymondfeng/pkg2@1.0.0'
+ Imported via "@raymondfeng/pkg2" from file 'pkg3/src/keys.ts' with packageId '@raymondfeng/pkg2/dist/index.d.ts@1.0.0'
pkg3/src/keys.ts
Imported via './keys' from file 'pkg3/src/index.ts'
Matched by default include pattern '**/*'
diff --git a/testdata/baselines/reference/tsc/declarationEmit/when-same-version-is-referenced-through-source-and-another-symlinked-package-with-indirect-link.js b/testdata/baselines/reference/tsc/declarationEmit/when-same-version-is-referenced-through-source-and-another-symlinked-package-with-indirect-link.js
index 5cfa0d4d571..cef83d3a2d3 100644
--- a/testdata/baselines/reference/tsc/declarationEmit/when-same-version-is-referenced-through-source-and-another-symlinked-package-with-indirect-link.js
+++ b/testdata/baselines/reference/tsc/declarationEmit/when-same-version-is-referenced-through-source-and-another-symlinked-package-with-indirect-link.js
@@ -112,7 +112,7 @@ File '/user/username/projects/myproject/plugin-one/node_modules/plugin-two/dist/
File '/user/username/projects/myproject/plugin-one/node_modules/plugin-two/dist/commonjs/index.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/user/username/projects/myproject/plugin-one/node_modules/plugin-two/dist/commonjs/index.d.ts', result '/user/username/projects/myproject/plugin-two/dist/commonjs/index.d.ts'.
-======== Module name 'plugin-two' was successfully resolved to '/user/username/projects/myproject/plugin-two/dist/commonjs/index.d.ts' with Package ID 'plugin-two@0.1.3'. ========
+======== Module name 'plugin-two' was successfully resolved to '/user/username/projects/myproject/plugin-two/dist/commonjs/index.d.ts' with Package ID 'plugin-two/dist/commonjs/index.d.ts@0.1.3'. ========
======== Resolving module 'typescript-fsa' from '/user/username/projects/myproject/plugin-one/index.ts'. ========
Module resolution kind is not specified, using 'Bundler'.
Resolving in CJS mode with conditions 'require', 'types'.
@@ -137,7 +137,7 @@ File '/user/username/projects/myproject/plugin-one/node_modules/typescript-fsa/i
File '/user/username/projects/myproject/plugin-one/node_modules/typescript-fsa/index.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/user/username/projects/myproject/plugin-one/node_modules/typescript-fsa/index.d.ts', result '/user/username/projects/myproject/plugin-one/node_modules/typescript-fsa/index.d.ts'.
-======== Module name 'typescript-fsa' was successfully resolved to '/user/username/projects/myproject/plugin-one/node_modules/typescript-fsa/index.d.ts' with Package ID 'typescript-fsa@3.0.0-beta-2'. ========
+======== Module name 'typescript-fsa' was successfully resolved to '/user/username/projects/myproject/plugin-one/node_modules/typescript-fsa/index.d.ts' with Package ID 'typescript-fsa/index.d.ts@3.0.0-beta-2'. ========
======== Resolving module 'typescript-fsa' from '/user/username/projects/myproject/plugin-two/dist/commonjs/index.d.ts'. ========
Module resolution kind is not specified, using 'Bundler'.
Resolving in CJS mode with conditions 'require', 'types'.
@@ -163,15 +163,15 @@ File '/user/username/projects/myproject/plugin-two/node_modules/typescript-fsa/i
File '/user/username/projects/myproject/plugin-two/node_modules/typescript-fsa/index.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/user/username/projects/myproject/plugin-two/node_modules/typescript-fsa/index.d.ts', result '/user/username/projects/myproject/plugin-two/node_modules/typescript-fsa/index.d.ts'.
-======== Module name 'typescript-fsa' was successfully resolved to '/user/username/projects/myproject/plugin-two/node_modules/typescript-fsa/index.d.ts' with Package ID 'typescript-fsa@3.0.0-beta-2'. ========
+======== Module name 'typescript-fsa' was successfully resolved to '/user/username/projects/myproject/plugin-two/node_modules/typescript-fsa/index.d.ts' with Package ID 'typescript-fsa/index.d.ts@3.0.0-beta-2'. ========
../../../../home/src/tslibs/TS/Lib/lib.d.ts
Default library for target 'ES5'
plugin-two/node_modules/typescript-fsa/index.d.ts
- Imported via "typescript-fsa" from file 'plugin-two/dist/commonjs/index.d.ts' with packageId 'typescript-fsa@3.0.0-beta-2'
+ Imported via "typescript-fsa" from file 'plugin-two/dist/commonjs/index.d.ts' with packageId 'typescript-fsa/index.d.ts@3.0.0-beta-2'
plugin-two/dist/commonjs/index.d.ts
- Imported via "plugin-two" from file 'plugin-one/index.ts' with packageId 'plugin-two@0.1.3'
+ Imported via "plugin-two" from file 'plugin-one/index.ts' with packageId 'plugin-two/dist/commonjs/index.d.ts@0.1.3'
plugin-one/node_modules/typescript-fsa/index.d.ts
- Imported via "typescript-fsa" from file 'plugin-one/index.ts' with packageId 'typescript-fsa@3.0.0-beta-2'
+ Imported via "typescript-fsa" from file 'plugin-one/index.ts' with packageId 'typescript-fsa/index.d.ts@3.0.0-beta-2'
plugin-one/index.ts
Matched by default include pattern '**/*'
//// [/home/src/tslibs/TS/Lib/lib.d.ts] *Lib*
diff --git a/testdata/baselines/reference/tsc/declarationEmit/when-same-version-is-referenced-through-source-and-another-symlinked-package.js b/testdata/baselines/reference/tsc/declarationEmit/when-same-version-is-referenced-through-source-and-another-symlinked-package.js
index 83b1575d13b..983c7cf3912 100644
--- a/testdata/baselines/reference/tsc/declarationEmit/when-same-version-is-referenced-through-source-and-another-symlinked-package.js
+++ b/testdata/baselines/reference/tsc/declarationEmit/when-same-version-is-referenced-through-source-and-another-symlinked-package.js
@@ -105,7 +105,7 @@ File '/user/username/projects/myproject/plugin-one/node_modules/typescript-fsa/i
File '/user/username/projects/myproject/plugin-one/node_modules/typescript-fsa/index.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/user/username/projects/myproject/plugin-one/node_modules/typescript-fsa/index.d.ts', result '/user/username/projects/myproject/plugin-one/node_modules/typescript-fsa/index.d.ts'.
-======== Module name 'typescript-fsa' was successfully resolved to '/user/username/projects/myproject/plugin-one/node_modules/typescript-fsa/index.d.ts' with Package ID 'typescript-fsa@3.0.0-beta-2'. ========
+======== Module name 'typescript-fsa' was successfully resolved to '/user/username/projects/myproject/plugin-one/node_modules/typescript-fsa/index.d.ts' with Package ID 'typescript-fsa/index.d.ts@3.0.0-beta-2'. ========
======== Resolving module 'plugin-two' from '/user/username/projects/myproject/plugin-one/index.ts'. ========
Module resolution kind is not specified, using 'Bundler'.
Resolving in CJS mode with conditions 'require', 'types'.
@@ -150,15 +150,15 @@ File '/user/username/projects/myproject/plugin-two/node_modules/typescript-fsa/i
File '/user/username/projects/myproject/plugin-two/node_modules/typescript-fsa/index.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/user/username/projects/myproject/plugin-two/node_modules/typescript-fsa/index.d.ts', result '/user/username/projects/myproject/plugin-two/node_modules/typescript-fsa/index.d.ts'.
-======== Module name 'typescript-fsa' was successfully resolved to '/user/username/projects/myproject/plugin-two/node_modules/typescript-fsa/index.d.ts' with Package ID 'typescript-fsa@3.0.0-beta-2'. ========
+======== Module name 'typescript-fsa' was successfully resolved to '/user/username/projects/myproject/plugin-two/node_modules/typescript-fsa/index.d.ts' with Package ID 'typescript-fsa/index.d.ts@3.0.0-beta-2'. ========
../../../../home/src/tslibs/TS/Lib/lib.d.ts
Default library for target 'ES5'
plugin-one/node_modules/typescript-fsa/index.d.ts
- Imported via "typescript-fsa" from file 'plugin-one/action.ts' with packageId 'typescript-fsa@3.0.0-beta-2'
+ Imported via "typescript-fsa" from file 'plugin-one/action.ts' with packageId 'typescript-fsa/index.d.ts@3.0.0-beta-2'
plugin-one/action.ts
Matched by default include pattern '**/*'
plugin-two/node_modules/typescript-fsa/index.d.ts
- Imported via "typescript-fsa" from file 'plugin-two/index.d.ts' with packageId 'typescript-fsa@3.0.0-beta-2'
+ Imported via "typescript-fsa" from file 'plugin-two/index.d.ts' with packageId 'typescript-fsa/index.d.ts@3.0.0-beta-2'
plugin-two/index.d.ts
Imported via "plugin-two" from file 'plugin-one/index.ts'
plugin-one/index.ts
diff --git a/testdata/baselines/reference/tsc/moduleResolution/pnpm-style-layout.js b/testdata/baselines/reference/tsc/moduleResolution/pnpm-style-layout.js
index 01e668625cb..3c2149a1b27 100644
--- a/testdata/baselines/reference/tsc/moduleResolution/pnpm-style-layout.js
+++ b/testdata/baselines/reference/tsc/moduleResolution/pnpm-style-layout.js
@@ -134,7 +134,7 @@ File '/home/src/projects/component-type-checker/packages/app/node_modules/@compo
File '/home/src/projects/component-type-checker/packages/app/node_modules/@component-type-checker/sdk/src/index.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/home/src/projects/component-type-checker/packages/app/node_modules/@component-type-checker/sdk/src/index.ts', result '/home/src/projects/component-type-checker/packages/sdk/src/index.ts'.
-======== Module name '@component-type-checker/sdk' was successfully resolved to '/home/src/projects/component-type-checker/packages/sdk/src/index.ts' with Package ID '@component-type-checker/sdk1@0.0.2'. ========
+======== Module name '@component-type-checker/sdk' was successfully resolved to '/home/src/projects/component-type-checker/packages/sdk/src/index.ts' with Package ID '@component-type-checker/sdk1/src/index.ts@0.0.2'. ========
======== Resolving module '@component-type-checker/components' from '/home/src/projects/component-type-checker/packages/app/src/app.tsx'. ========
Module resolution kind is not specified, using 'Bundler'.
Resolving in CJS mode with conditions 'import', 'types'.
@@ -159,7 +159,7 @@ Resolving real path for '/home/src/projects/component-type-checker/packages/app/
Found 'package.json' at '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+components@0.0.1_@component-type-checker+button@0.0.2/node_modules/@component-type-checker/button/package.json'.
Found peerDependency '@component-type-checker/button' with '0.0.2' version.
Resolving real path for '/home/src/projects/component-type-checker/packages/app/node_modules/@component-type-checker/components/src/index.ts', result '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+components@0.0.1_@component-type-checker+button@0.0.2/node_modules/@component-type-checker/components/src/index.ts'.
-======== Module name '@component-type-checker/components' was successfully resolved to '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+components@0.0.1_@component-type-checker+button@0.0.2/node_modules/@component-type-checker/components/src/index.ts' with Package ID '@component-type-checker/components@0.0.1+@component-type-checker/button@0.0.2'. ========
+======== Module name '@component-type-checker/components' was successfully resolved to '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+components@0.0.1_@component-type-checker+button@0.0.2/node_modules/@component-type-checker/components/src/index.ts' with Package ID '@component-type-checker/components/src/index.ts@0.0.1+@component-type-checker/button@0.0.2'. ========
======== Resolving module '@component-type-checker/button' from '/home/src/projects/component-type-checker/packages/app/src/app.tsx'. ========
Module resolution kind is not specified, using 'Bundler'.
Resolving in CJS mode with conditions 'import', 'types'.
@@ -181,7 +181,7 @@ File '/home/src/projects/component-type-checker/packages/app/node_modules/@compo
File '/home/src/projects/component-type-checker/packages/app/node_modules/@component-type-checker/button/src/index.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/home/src/projects/component-type-checker/packages/app/node_modules/@component-type-checker/button/src/index.ts', result '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+button@0.0.2/node_modules/@component-type-checker/button/src/index.ts'.
-======== Module name '@component-type-checker/button' was successfully resolved to '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+button@0.0.2/node_modules/@component-type-checker/button/src/index.ts' with Package ID '@component-type-checker/button@0.0.2'. ========
+======== Module name '@component-type-checker/button' was successfully resolved to '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+button@0.0.2/node_modules/@component-type-checker/button/src/index.ts' with Package ID '@component-type-checker/button/src/index.ts@0.0.2'. ========
======== Resolving module '@component-type-checker/components' from '/home/src/projects/component-type-checker/packages/sdk/src/index.ts'. ========
Module resolution kind is not specified, using 'Bundler'.
Resolving in CJS mode with conditions 'import', 'types'.
@@ -206,7 +206,7 @@ Resolving real path for '/home/src/projects/component-type-checker/packages/sdk/
Found 'package.json' at '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+components@0.0.1_@component-type-checker+button@0.0.1/node_modules/@component-type-checker/button/package.json'.
Found peerDependency '@component-type-checker/button' with '0.0.1' version.
Resolving real path for '/home/src/projects/component-type-checker/packages/sdk/node_modules/@component-type-checker/components/src/index.ts', result '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+components@0.0.1_@component-type-checker+button@0.0.1/node_modules/@component-type-checker/components/src/index.ts'.
-======== Module name '@component-type-checker/components' was successfully resolved to '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+components@0.0.1_@component-type-checker+button@0.0.1/node_modules/@component-type-checker/components/src/index.ts' with Package ID '@component-type-checker/components@0.0.1+@component-type-checker/button@0.0.1'. ========
+======== Module name '@component-type-checker/components' was successfully resolved to '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+components@0.0.1_@component-type-checker+button@0.0.1/node_modules/@component-type-checker/components/src/index.ts' with Package ID '@component-type-checker/components/src/index.ts@0.0.1+@component-type-checker/button@0.0.1'. ========
======== Resolving module '@component-type-checker/button' from '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+components@0.0.1_@component-type-checker+button@0.0.1/node_modules/@component-type-checker/components/src/index.ts'. ========
Module resolution kind is not specified, using 'Bundler'.
Resolving in CJS mode with conditions 'import', 'types'.
@@ -234,7 +234,7 @@ File '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-ty
File '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+components@0.0.1_@component-type-checker+button@0.0.1/node_modules/@component-type-checker/button/src/index.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+components@0.0.1_@component-type-checker+button@0.0.1/node_modules/@component-type-checker/button/src/index.ts', result '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+button@0.0.1/node_modules/@component-type-checker/button/src/index.ts'.
-======== Module name '@component-type-checker/button' was successfully resolved to '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+button@0.0.1/node_modules/@component-type-checker/button/src/index.ts' with Package ID '@component-type-checker/button@0.0.1'. ========
+======== Module name '@component-type-checker/button' was successfully resolved to '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+button@0.0.1/node_modules/@component-type-checker/button/src/index.ts' with Package ID '@component-type-checker/button/src/index.ts@0.0.1'. ========
======== Resolving module '@component-type-checker/button' from '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+components@0.0.1_@component-type-checker+button@0.0.2/node_modules/@component-type-checker/components/src/index.ts'. ========
Module resolution kind is not specified, using 'Bundler'.
Resolving in CJS mode with conditions 'import', 'types'.
@@ -262,20 +262,20 @@ File '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-ty
File '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+components@0.0.1_@component-type-checker+button@0.0.2/node_modules/@component-type-checker/button/src/index.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolving real path for '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+components@0.0.1_@component-type-checker+button@0.0.2/node_modules/@component-type-checker/button/src/index.ts', result '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+button@0.0.2/node_modules/@component-type-checker/button/src/index.ts'.
-======== Module name '@component-type-checker/button' was successfully resolved to '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+button@0.0.2/node_modules/@component-type-checker/button/src/index.ts' with Package ID '@component-type-checker/button@0.0.2'. ========
+======== Module name '@component-type-checker/button' was successfully resolved to '/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+button@0.0.2/node_modules/@component-type-checker/button/src/index.ts' with Package ID '@component-type-checker/button/src/index.ts@0.0.2'. ========
../../../../tslibs/TS/Lib/lib.es5.d.ts
Library 'lib.es5.d.ts' specified in compilerOptions
../../node_modules/.pnpm/@component-type-checker+button@0.0.1/node_modules/@component-type-checker/button/src/index.ts
- Imported via "@component-type-checker/button" from file '../../node_modules/.pnpm/@component-type-checker+components@0.0.1_@component-type-checker+button@0.0.1/node_modules/@component-type-checker/components/src/index.ts' with packageId '@component-type-checker/button@0.0.1'
+ Imported via "@component-type-checker/button" from file '../../node_modules/.pnpm/@component-type-checker+components@0.0.1_@component-type-checker+button@0.0.1/node_modules/@component-type-checker/components/src/index.ts' with packageId '@component-type-checker/button/src/index.ts@0.0.1'
../../node_modules/.pnpm/@component-type-checker+components@0.0.1_@component-type-checker+button@0.0.1/node_modules/@component-type-checker/components/src/index.ts
- Imported via "@component-type-checker/components" from file '../sdk/src/index.ts' with packageId '@component-type-checker/components@0.0.1+@component-type-checker/button@0.0.1'
+ Imported via "@component-type-checker/components" from file '../sdk/src/index.ts' with packageId '@component-type-checker/components/src/index.ts@0.0.1+@component-type-checker/button@0.0.1'
../sdk/src/index.ts
- Imported via "@component-type-checker/sdk" from file 'src/app.tsx' with packageId '@component-type-checker/sdk1@0.0.2'
+ Imported via "@component-type-checker/sdk" from file 'src/app.tsx' with packageId '@component-type-checker/sdk1/src/index.ts@0.0.2'
../../node_modules/.pnpm/@component-type-checker+button@0.0.2/node_modules/@component-type-checker/button/src/index.ts
- Imported via "@component-type-checker/button" from file '../../node_modules/.pnpm/@component-type-checker+components@0.0.1_@component-type-checker+button@0.0.2/node_modules/@component-type-checker/components/src/index.ts' with packageId '@component-type-checker/button@0.0.2'
- Imported via "@component-type-checker/button" from file 'src/app.tsx' with packageId '@component-type-checker/button@0.0.2'
+ Imported via "@component-type-checker/button" from file '../../node_modules/.pnpm/@component-type-checker+components@0.0.1_@component-type-checker+button@0.0.2/node_modules/@component-type-checker/components/src/index.ts' with packageId '@component-type-checker/button/src/index.ts@0.0.2'
+ Imported via "@component-type-checker/button" from file 'src/app.tsx' with packageId '@component-type-checker/button/src/index.ts@0.0.2'
../../node_modules/.pnpm/@component-type-checker+components@0.0.1_@component-type-checker+button@0.0.2/node_modules/@component-type-checker/components/src/index.ts
- Imported via "@component-type-checker/components" from file 'src/app.tsx' with packageId '@component-type-checker/components@0.0.1+@component-type-checker/button@0.0.2'
+ Imported via "@component-type-checker/components" from file 'src/app.tsx' with packageId '@component-type-checker/components/src/index.ts@0.0.1+@component-type-checker/button@0.0.2'
src/app.tsx
Matched by include pattern 'src' in 'tsconfig.json'
//// [/home/src/projects/component-type-checker/packages/app/dist/app.js] *new*
From d81c18e116734716c0fcedecd322011a62058535 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Wed, 10 Dec 2025 22:05:25 -0800
Subject: [PATCH 03/29] Implement package deduping kinda
---
internal/checker/checker.go | 42 ++++++-
internal/checker/relater.go | 2 +-
internal/compiler/fileloader.go | 6 +-
internal/compiler/filesparser.go | 106 ++++++++++++++++++
internal/compiler/program.go | 40 ++++++-
internal/core/compileroptions.go | 1 +
internal/diagnostics/diagnostics_generated.go | 4 +
.../diagnostics/extraDiagnosticMessages.json | 4 +
.../tstransforms/importelision_test.go | 4 +
internal/tsoptions/declscompiler.go | 9 ++
internal/tsoptions/parsinghelpers.go | 2 +
...duplicatePackage_referenceTypes.errors.txt | 30 -----
...catePackage_referenceTypes.errors.txt.diff | 34 ------
...age_relativeImportWithinPackage.errors.txt | 43 -------
...elativeImportWithinPackage.errors.txt.diff | 47 --------
...ativeImportWithinPackage_scoped.errors.txt | 43 -------
...ImportWithinPackage_scoped.errors.txt.diff | 47 --------
.../reference/tsbuild/commandLine/help.js | 5 +
.../reference/tsbuild/commandLine/locale.js | 5 +
.../reference/tsc/commandLine/help-all.js | 5 +
20 files changed, 229 insertions(+), 250 deletions(-)
delete mode 100644 testdata/baselines/reference/submodule/compiler/duplicatePackage_referenceTypes.errors.txt
delete mode 100644 testdata/baselines/reference/submodule/compiler/duplicatePackage_referenceTypes.errors.txt.diff
delete mode 100644 testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.errors.txt
delete mode 100644 testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.errors.txt.diff
delete mode 100644 testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage_scoped.errors.txt
delete mode 100644 testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage_scoped.errors.txt.diff
diff --git a/internal/checker/checker.go b/internal/checker/checker.go
index 8f5065c9be7..02d7af92f12 100644
--- a/internal/checker/checker.go
+++ b/internal/checker/checker.go
@@ -544,6 +544,10 @@ type Program interface {
GetProjectReferenceFromOutputDts(path tspath.Path) *tsoptions.SourceOutputAndProjectReference
GetRedirectForResolution(file ast.HasFileName) *tsoptions.ParsedCommandLine
CommonSourceDirectory() string
+ // GetFileCanonicalPath returns the canonical path for a file that may be from a duplicate package.
+ // Used to determine if two files from the same package (name@version) installed in different locations
+ // should be treated as the same for type compatibility purposes.
+ GetFileCanonicalPath(path tspath.Path) tspath.Path
}
type Host interface {
@@ -26975,8 +26979,14 @@ func (c *Checker) compareProperties(sourceProp *ast.Symbol, targetProp *ast.Symb
return TernaryFalse
}
if sourcePropAccessibility != ast.ModifierFlagsNone {
- if c.getTargetSymbol(sourceProp) != c.getTargetSymbol(targetProp) {
- return TernaryFalse
+ sourceTarget := c.getTargetSymbol(sourceProp)
+ targetTarget := c.getTargetSymbol(targetProp)
+ if sourceTarget != targetTarget {
+ // Check if these symbols are from duplicate package instances (same package installed
+ // in different locations). If they map to the same canonical file, treat them as identical.
+ if !c.areSymbolsFromSamePackageFile(sourceTarget, targetTarget) {
+ return TernaryFalse
+ }
}
} else {
if (sourceProp.Flags & ast.SymbolFlagsOptional) != (targetProp.Flags & ast.SymbolFlagsOptional) {
@@ -26989,6 +26999,34 @@ func (c *Checker) compareProperties(sourceProp *ast.Symbol, targetProp *ast.Symb
return compareTypes(c.getTypeOfSymbol(sourceProp), c.getTypeOfSymbol(targetProp))
}
+// areSymbolsFromSamePackageFile checks if two symbols come from files that are duplicates
+// of the same package file (same package name@version installed in different locations).
+func (c *Checker) areSymbolsFromSamePackageFile(source *ast.Symbol, target *ast.Symbol) bool {
+ // If package deduplication is disabled, don't treat any files as duplicates
+ if c.compilerOptions.DisablePackageDeduplication.IsTrue() {
+ return false
+ }
+ sourceDecl := source.ValueDeclaration
+ targetDecl := target.ValueDeclaration
+ if sourceDecl == nil || targetDecl == nil {
+ return false
+ }
+ sourceFile := ast.GetSourceFileOfNode(sourceDecl)
+ targetFile := ast.GetSourceFileOfNode(targetDecl)
+ if sourceFile == nil || targetFile == nil {
+ return false
+ }
+ // If they're from the same file, this is not a package deduplication scenario
+ if sourceFile.Path() == targetFile.Path() {
+ return false
+ }
+ // Get the canonical paths for both files
+ sourceCanonical := c.program.GetFileCanonicalPath(sourceFile.Path())
+ targetCanonical := c.program.GetFileCanonicalPath(targetFile.Path())
+ // If they map to the same canonical path, they're from the same package file
+ return sourceCanonical == targetCanonical
+}
+
func compareTypesEqual(s *Type, t *Type) Ternary {
if s == t {
return TernaryTrue
diff --git a/internal/checker/relater.go b/internal/checker/relater.go
index db83af07457..b55d891e545 100644
--- a/internal/checker/relater.go
+++ b/internal/checker/relater.go
@@ -4203,7 +4203,7 @@ func (r *Relater) propertyRelatedTo(source *Type, target *Type, sourceProp *ast.
targetPropFlags := getDeclarationModifierFlagsFromSymbol(targetProp)
switch {
case sourcePropFlags&ast.ModifierFlagsPrivate != 0 || targetPropFlags&ast.ModifierFlagsPrivate != 0:
- if sourceProp.ValueDeclaration != targetProp.ValueDeclaration {
+ if sourceProp.ValueDeclaration != targetProp.ValueDeclaration && !r.c.areSymbolsFromSamePackageFile(sourceProp, targetProp) {
if reportErrors {
if sourcePropFlags&ast.ModifierFlagsPrivate != 0 && targetPropFlags&ast.ModifierFlagsPrivate != 0 {
r.reportError(diagnostics.Types_have_separate_declarations_of_a_private_property_0, r.c.symbolToString(targetProp))
diff --git a/internal/compiler/fileloader.go b/internal/compiler/fileloader.go
index 6fdbf008ea0..bb9bb1f8ae4 100644
--- a/internal/compiler/fileloader.go
+++ b/internal/compiler/fileloader.go
@@ -68,7 +68,11 @@ type processedFiles struct {
// if file was included using source file and its output is actually part of program
// this contains mapping from output to source file
outputFileToProjectReferenceSource map[tspath.Path]string
- finishedProcessing bool
+ // Maps a source file path to the name of the package it was imported with
+ sourceFileToPackageName map[tspath.Path]string
+ // Key is a file path. Value is the list of files that redirect to it (same package, different install location)
+ redirectTargetsMap map[tspath.Path][]string
+ finishedProcessing bool
}
type jsxRuntimeImportSpecifier struct {
diff --git a/internal/compiler/filesparser.go b/internal/compiler/filesparser.go
index 307e6ccd65b..7a84b652b22 100644
--- a/internal/compiler/filesparser.go
+++ b/internal/compiler/filesparser.go
@@ -408,6 +408,15 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
}
}
+ // Build sourceFileToPackageName and redirectTargetsMap by scanning all resolved modules.
+ // This is done after loading is complete to ensure determinism regardless of load order.
+ // Skip this if package deduplication is disabled.
+ var sourceFileToPackageName map[tspath.Path]string
+ var redirectTargetsMap map[tspath.Path][]string
+ if !loader.opts.Config.CompilerOptions().DisablePackageDeduplication.IsTrue() {
+ sourceFileToPackageName, redirectTargetsMap = computePackageRedirects(resolvedModules, loader.toPath)
+ }
+
return processedFiles{
finishedProcessing: true,
resolver: loader.resolver,
@@ -424,7 +433,104 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
missingFiles: missingFiles,
includeProcessor: includeProcessor,
outputFileToProjectReferenceSource: outputFileToProjectReferenceSource,
+ sourceFileToPackageName: sourceFileToPackageName,
+ redirectTargetsMap: redirectTargetsMap,
+ }
+}
+
+// computePackageRedirects builds the sourceFileToPackageName and redirectTargetsMap by scanning
+// all resolved modules. Files from the same package (same name@version) are deduplicated:
+// the lexicographically first path becomes the "canonical" one and others redirect to it.
+// This is done after loading completes to ensure determinism regardless of concurrent load order.
+func computePackageRedirects(
+ resolvedModules map[tspath.Path]module.ModeAwareCache[*module.ResolvedModule],
+ toPath func(string) tspath.Path,
+) (sourceFileToPackageName map[tspath.Path]string, redirectTargetsMap map[tspath.Path][]string) {
+ // Collect all resolved files with package IDs
+ // packageIdKey -> list of (resolvedPath, packageName)
+ type fileInfo struct {
+ path tspath.Path
+ packageName string
}
+ packageIdToFiles := make(map[string][]fileInfo)
+
+ // Iterate through resolvedModules in sorted order for determinism
+ containingFilePaths := make([]tspath.Path, 0, len(resolvedModules))
+ for containingPath := range resolvedModules {
+ containingFilePaths = append(containingFilePaths, containingPath)
+ }
+ slices.Sort(containingFilePaths)
+
+ for _, containingPath := range containingFilePaths {
+ resolutions := resolvedModules[containingPath]
+ for _, resolution := range resolutions {
+ if resolution == nil || !resolution.IsResolved() {
+ continue
+ }
+ pkgId := resolution.PackageId
+ if pkgId.Name == "" {
+ continue
+ }
+ // packageIdKey is "name@version" (excluding peerDependencies for redirect grouping)
+ packageIdKey := pkgId.Name + "@" + pkgId.Version
+ if pkgId.SubModuleName != "" {
+ packageIdKey = pkgId.Name + "/" + pkgId.SubModuleName + "@" + pkgId.Version
+ }
+ resolvedPath := toPath(resolution.ResolvedFileName)
+ packageName := pkgId.PackageName()
+
+ // Check if we've already recorded this path for this package
+ files := packageIdToFiles[packageIdKey]
+ found := false
+ for _, f := range files {
+ if f.path == resolvedPath {
+ found = true
+ break
+ }
+ }
+ if !found {
+ packageIdToFiles[packageIdKey] = append(files, fileInfo{path: resolvedPath, packageName: packageName})
+ }
+ }
+ }
+
+ // Now for each packageIdKey with multiple files, pick the canonical one (lexicographically first)
+ // and build the redirect map
+ sourceFileToPackageName = make(map[tspath.Path]string)
+ redirectTargetsMap = make(map[tspath.Path][]string)
+
+ for _, files := range packageIdToFiles {
+ if len(files) == 0 {
+ continue
+ }
+
+ // Sort files by path for determinism - first one becomes canonical
+ slices.SortFunc(files, func(a, b fileInfo) int {
+ if a.path < b.path {
+ return -1
+ } else if a.path > b.path {
+ return 1
+ }
+ return 0
+ })
+
+ canonicalPath := files[0].path
+ packageName := files[0].packageName
+
+ // Record package name for all files from this package
+ for _, f := range files {
+ sourceFileToPackageName[f.path] = packageName
+ }
+
+ // If there are multiple files, the others redirect to the canonical one
+ if len(files) > 1 {
+ for _, f := range files[1:] {
+ redirectTargetsMap[canonicalPath] = append(redirectTargetsMap[canonicalPath], string(f.path))
+ }
+ }
+ }
+
+ return sourceFileToPackageName, redirectTargetsMap
}
func (w *filesParser) addIncludeReason(includeProcessor *includeProcessor, task *parseTask, reason *FileIncludeReason) {
diff --git a/internal/compiler/program.go b/internal/compiler/program.go
index a9a6b47f3e2..e643f9ab693 100644
--- a/internal/compiler/program.go
+++ b/internal/compiler/program.go
@@ -110,9 +110,30 @@ func (p *Program) GetPackageJsonInfo(pkgJsonPath string) *packagejson.InfoCacheE
return nil
}
-// GetRedirectTargets implements checker.Program.
+// GetRedirectTargets returns the list of file paths that redirect to the given path.
+// These are files from the same package (same name@version) installed in different locations.
func (p *Program) GetRedirectTargets(path tspath.Path) []string {
- return nil // !!! TODO: project references support
+ return p.redirectTargetsMap[path]
+}
+
+// GetFileCanonicalPath returns the canonical path for a file that may be a duplicate package.
+// If the file is a redirect target (i.e., it redirects to a canonical file), returns the canonical path.
+// Otherwise, returns the path unchanged.
+func (p *Program) GetFileCanonicalPath(path tspath.Path) tspath.Path {
+ // If this path has redirect targets, it's already canonical
+ if _, ok := p.redirectTargetsMap[path]; ok {
+ return path
+ }
+ // Check if this path is a redirect target of some canonical path
+ for canonicalPath, targets := range p.redirectTargetsMap {
+ for _, t := range targets {
+ if tspath.Path(t) == path {
+ return canonicalPath
+ }
+ }
+ }
+ // Not part of any redirect group, return as-is
+ return path
}
// gets the original file that was included in program
@@ -241,6 +262,21 @@ func (p *Program) UpdateProgram(changedFilePath tspath.Path, newHost CompilerHos
if !canReplaceFileInProgram(oldFile, newFile) {
return NewProgram(newOpts), false
}
+ // TODO: CHECK THIS
+ // If this file is part of a package redirect group (same package installed in multiple
+ // node_modules locations), we need to rebuild the program because the redirect targets
+ // might need recalculation. A file is in a redirect group if it's either a canonical
+ // file that others redirect to, or if it redirects to another file.
+ // if _, isCanonical := p.redirectTargetsMap[changedFilePath]; isCanonical {
+ // return NewProgram(newOpts), false
+ // }
+ // for _, targets := range p.redirectTargetsMap {
+ // for _, target := range targets {
+ // if tspath.Path(target) == changedFilePath {
+ // return NewProgram(newOpts), false
+ // }
+ // }
+ // }
// TODO: reverify compiler options when config has changed?
result := &Program{
opts: newOpts,
diff --git a/internal/core/compileroptions.go b/internal/core/compileroptions.go
index ad6a4ceecac..0ba810403cf 100644
--- a/internal/core/compileroptions.go
+++ b/internal/core/compileroptions.go
@@ -40,6 +40,7 @@ type CompilerOptions struct {
DisableSourceOfProjectReferenceRedirect Tristate `json:"disableSourceOfProjectReferenceRedirect,omitzero"`
DisableSolutionSearching Tristate `json:"disableSolutionSearching,omitzero"`
DisableReferencedProjectLoad Tristate `json:"disableReferencedProjectLoad,omitzero"`
+ DisablePackageDeduplication Tristate `json:"disablePackageDeduplication,omitzero"`
ErasableSyntaxOnly Tristate `json:"erasableSyntaxOnly,omitzero"`
ESModuleInterop Tristate `json:"esModuleInterop,omitzero"`
ExactOptionalPropertyTypes Tristate `json:"exactOptionalPropertyTypes,omitzero"`
diff --git a/internal/diagnostics/diagnostics_generated.go b/internal/diagnostics/diagnostics_generated.go
index 14c2a9b0f48..a84cb592483 100644
--- a/internal/diagnostics/diagnostics_generated.go
+++ b/internal/diagnostics/diagnostics_generated.go
@@ -4276,6 +4276,8 @@ var Set_the_number_of_projects_to_build_concurrently = &Message{code: 100009, ca
var X_all_unless_singleThreaded_is_passed = &Message{code: 100010, category: CategoryMessage, key: "all_unless_singleThreaded_is_passed_100010", text: "all, unless --singleThreaded is passed."}
+var Disable_deduplication_of_packages_with_the_same_name_and_version = &Message{code: 100011, category: CategoryMessage, key: "Disable_deduplication_of_packages_with_the_same_name_and_version_100011", text: "Disable deduplication of packages with the same name and version."}
+
func keyToMessage(key Key) *Message {
switch key {
case "Unterminated_string_literal_1002":
@@ -8552,6 +8554,8 @@ func keyToMessage(key Key) *Message {
return Set_the_number_of_projects_to_build_concurrently
case "all_unless_singleThreaded_is_passed_100010":
return X_all_unless_singleThreaded_is_passed
+ case "Disable_deduplication_of_packages_with_the_same_name_and_version_100011":
+ return Disable_deduplication_of_packages_with_the_same_name_and_version
default:
return nil
}
diff --git a/internal/diagnostics/extraDiagnosticMessages.json b/internal/diagnostics/extraDiagnosticMessages.json
index 2566d6ff532..4c51b0c450b 100644
--- a/internal/diagnostics/extraDiagnosticMessages.json
+++ b/internal/diagnostics/extraDiagnosticMessages.json
@@ -86,5 +86,9 @@
"Option '{0}' requires value to be greater than '{1}'.": {
"category": "Error",
"code": 5002
+ },
+ "Disable deduplication of packages with the same name and version.": {
+ "category": "Message",
+ "code": 100011
}
}
diff --git a/internal/transformers/tstransforms/importelision_test.go b/internal/transformers/tstransforms/importelision_test.go
index 68c18aa71f4..753c8e2e083 100644
--- a/internal/transformers/tstransforms/importelision_test.go
+++ b/internal/transformers/tstransforms/importelision_test.go
@@ -86,6 +86,10 @@ func (p *fakeProgram) GetRedirectTargets(path tspath.Path) []string {
return nil
}
+func (p *fakeProgram) GetFileCanonicalPath(path tspath.Path) tspath.Path {
+ return path
+}
+
func (p *fakeProgram) GetSourceOfProjectReferenceIfOutputIncluded(file ast.HasFileName) string {
return ""
}
diff --git a/internal/tsoptions/declscompiler.go b/internal/tsoptions/declscompiler.go
index 1550e1680d0..a6bb2733d07 100644
--- a/internal/tsoptions/declscompiler.go
+++ b/internal/tsoptions/declscompiler.go
@@ -185,6 +185,15 @@ var commonOptionsWithBuild = []*CommandLineOption{
DefaultValueDescription: false,
// Not setting affectsSemanticDiagnostics or affectsBuildInfo because we dont want all diagnostics to go away, its handled in builder
},
+ {
+ Name: "disablePackageDeduplication",
+ Kind: CommandLineOptionTypeBoolean,
+ Category: diagnostics.Type_Checking,
+ Description: diagnostics.Disable_deduplication_of_packages_with_the_same_name_and_version,
+ DefaultValueDescription: false,
+ AffectsSemanticDiagnostics: true,
+ AffectsBuildInfo: true,
+ },
{
Name: "noEmit",
Kind: CommandLineOptionTypeBoolean,
diff --git a/internal/tsoptions/parsinghelpers.go b/internal/tsoptions/parsinghelpers.go
index 4a3763168a1..3006411d8ce 100644
--- a/internal/tsoptions/parsinghelpers.go
+++ b/internal/tsoptions/parsinghelpers.go
@@ -227,6 +227,8 @@ func parseCompilerOptions(key string, value any, allOptions *core.CompilerOption
allOptions.DisableSolutionSearching = ParseTristate(value)
case "disableReferencedProjectLoad":
allOptions.DisableReferencedProjectLoad = ParseTristate(value)
+ case "disablePackageDeduplication":
+ allOptions.DisablePackageDeduplication = ParseTristate(value)
case "declarationMap":
allOptions.DeclarationMap = ParseTristate(value)
case "declaration":
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_referenceTypes.errors.txt b/testdata/baselines/reference/submodule/compiler/duplicatePackage_referenceTypes.errors.txt
deleted file mode 100644
index 954f20ec44d..00000000000
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_referenceTypes.errors.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-/index.ts(4,5): error TS2322: Type 'import("/node_modules/a/node_modules/foo/index").Foo' is not assignable to type 'import("/node_modules/@types/foo/index").Foo'.
- Types have separate declarations of a private property 'x'.
-
-
-==== /index.ts (1 errors) ====
- import * as a from "a";
- import { Foo } from "foo";
-
- let foo: Foo = a.foo;
- ~~~
-!!! error TS2322: Type 'import("/node_modules/a/node_modules/foo/index").Foo' is not assignable to type 'import("/node_modules/@types/foo/index").Foo'.
-!!! error TS2322: Types have separate declarations of a private property 'x'.
-
-==== /node_modules/a/index.d.ts (0 errors) ====
- ///
- import { Foo } from "foo";
- export const foo: Foo;
-
-==== /node_modules/a/node_modules/foo/index.d.ts (0 errors) ====
- export class Foo { private x; }
-
-==== /node_modules/a/node_modules/foo/package.json (0 errors) ====
- { "name": "foo", "version": "1.2.3" }
-
-==== /node_modules/@types/foo/index.d.ts (0 errors) ====
- export class Foo { private x; }
-
-==== /node_modules/@types/foo/package.json (0 errors) ====
- { "name": "foo", "version": "1.2.3" }
-
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_referenceTypes.errors.txt.diff b/testdata/baselines/reference/submodule/compiler/duplicatePackage_referenceTypes.errors.txt.diff
deleted file mode 100644
index 58e5b61220a..00000000000
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_referenceTypes.errors.txt.diff
+++ /dev/null
@@ -1,34 +0,0 @@
---- old.duplicatePackage_referenceTypes.errors.txt
-+++ new.duplicatePackage_referenceTypes.errors.txt
-@@= skipped -0, +0 lines =@@
--
-+/index.ts(4,5): error TS2322: Type 'import("/node_modules/a/node_modules/foo/index").Foo' is not assignable to type 'import("/node_modules/@types/foo/index").Foo'.
-+ Types have separate declarations of a private property 'x'.
-+
-+
-+==== /index.ts (1 errors) ====
-+ import * as a from "a";
-+ import { Foo } from "foo";
-+
-+ let foo: Foo = a.foo;
-+ ~~~
-+!!! error TS2322: Type 'import("/node_modules/a/node_modules/foo/index").Foo' is not assignable to type 'import("/node_modules/@types/foo/index").Foo'.
-+!!! error TS2322: Types have separate declarations of a private property 'x'.
-+
-+==== /node_modules/a/index.d.ts (0 errors) ====
-+ ///
-+ import { Foo } from "foo";
-+ export const foo: Foo;
-+
-+==== /node_modules/a/node_modules/foo/index.d.ts (0 errors) ====
-+ export class Foo { private x; }
-+
-+==== /node_modules/a/node_modules/foo/package.json (0 errors) ====
-+ { "name": "foo", "version": "1.2.3" }
-+
-+==== /node_modules/@types/foo/index.d.ts (0 errors) ====
-+ export class Foo { private x; }
-+
-+==== /node_modules/@types/foo/package.json (0 errors) ====
-+ { "name": "foo", "version": "1.2.3" }
-+
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.errors.txt b/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.errors.txt
deleted file mode 100644
index 9f4f6d7f9bb..00000000000
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.errors.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-/index.ts(4,5): error TS2345: Argument of type 'import("/node_modules/a/node_modules/foo/index").C' is not assignable to parameter of type 'import("/node_modules/foo/index").C'.
- Types have separate declarations of a private property 'x'.
-
-
-==== /index.ts (1 errors) ====
- import { use } from "foo/use";
- import { o } from "a";
-
- use(o);
- ~
-!!! error TS2345: Argument of type 'import("/node_modules/a/node_modules/foo/index").C' is not assignable to parameter of type 'import("/node_modules/foo/index").C'.
-!!! error TS2345: Types have separate declarations of a private property 'x'.
-
-==== /node_modules/a/node_modules/foo/package.json (0 errors) ====
- {
- "name": "foo",
- "version": "1.2.3"
- }
-
-==== /node_modules/a/node_modules/foo/index.d.ts (0 errors) ====
- export class C {
- private x: number;
- }
-
-==== /node_modules/a/index.d.ts (0 errors) ====
- import { C } from "foo";
- export const o: C;
-
-==== /node_modules/foo/use.d.ts (0 errors) ====
- import { C } from "./index";
- export function use(o: C): void;
-
-==== /node_modules/foo/index.d.ts (0 errors) ====
- export class C {
- private x: number;
- }
-
-==== /node_modules/foo/package.json (0 errors) ====
- {
- "name": "foo",
- "version": "1.2.3"
- }
-
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.errors.txt.diff b/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.errors.txt.diff
deleted file mode 100644
index 6be7c60fc74..00000000000
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.errors.txt.diff
+++ /dev/null
@@ -1,47 +0,0 @@
---- old.duplicatePackage_relativeImportWithinPackage.errors.txt
-+++ new.duplicatePackage_relativeImportWithinPackage.errors.txt
-@@= skipped -0, +0 lines =@@
--
-+/index.ts(4,5): error TS2345: Argument of type 'import("/node_modules/a/node_modules/foo/index").C' is not assignable to parameter of type 'import("/node_modules/foo/index").C'.
-+ Types have separate declarations of a private property 'x'.
-+
-+
-+==== /index.ts (1 errors) ====
-+ import { use } from "foo/use";
-+ import { o } from "a";
-+
-+ use(o);
-+ ~
-+!!! error TS2345: Argument of type 'import("/node_modules/a/node_modules/foo/index").C' is not assignable to parameter of type 'import("/node_modules/foo/index").C'.
-+!!! error TS2345: Types have separate declarations of a private property 'x'.
-+
-+==== /node_modules/a/node_modules/foo/package.json (0 errors) ====
-+ {
-+ "name": "foo",
-+ "version": "1.2.3"
-+ }
-+
-+==== /node_modules/a/node_modules/foo/index.d.ts (0 errors) ====
-+ export class C {
-+ private x: number;
-+ }
-+
-+==== /node_modules/a/index.d.ts (0 errors) ====
-+ import { C } from "foo";
-+ export const o: C;
-+
-+==== /node_modules/foo/use.d.ts (0 errors) ====
-+ import { C } from "./index";
-+ export function use(o: C): void;
-+
-+==== /node_modules/foo/index.d.ts (0 errors) ====
-+ export class C {
-+ private x: number;
-+ }
-+
-+==== /node_modules/foo/package.json (0 errors) ====
-+ {
-+ "name": "foo",
-+ "version": "1.2.3"
-+ }
-+
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage_scoped.errors.txt b/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage_scoped.errors.txt
deleted file mode 100644
index 19026366acc..00000000000
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage_scoped.errors.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-/index.ts(4,5): error TS2345: Argument of type 'import("/node_modules/a/node_modules/@foo/bar/index").C' is not assignable to parameter of type 'import("/node_modules/@foo/bar/index").C'.
- Types have separate declarations of a private property 'x'.
-
-
-==== /index.ts (1 errors) ====
- import { use } from "@foo/bar/use";
- import { o } from "a";
-
- use(o);
- ~
-!!! error TS2345: Argument of type 'import("/node_modules/a/node_modules/@foo/bar/index").C' is not assignable to parameter of type 'import("/node_modules/@foo/bar/index").C'.
-!!! error TS2345: Types have separate declarations of a private property 'x'.
-
-==== /node_modules/a/node_modules/@foo/bar/package.json (0 errors) ====
- {
- "name": "@foo/bar",
- "version": "1.2.3"
- }
-
-==== /node_modules/a/node_modules/@foo/bar/index.d.ts (0 errors) ====
- export class C {
- private x: number;
- }
-
-==== /node_modules/a/index.d.ts (0 errors) ====
- import { C } from "@foo/bar";
- export const o: C;
-
-==== /node_modules/@foo/bar/use.d.ts (0 errors) ====
- import { C } from "./index";
- export function use(o: C): void;
-
-==== /node_modules/@foo/bar/index.d.ts (0 errors) ====
- export class C {
- private x: number;
- }
-
-==== /node_modules/@foo/bar/package.json (0 errors) ====
- {
- "name": "@foo/bar",
- "version": "1.2.3"
- }
-
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage_scoped.errors.txt.diff b/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage_scoped.errors.txt.diff
deleted file mode 100644
index 5fd8e03f0b8..00000000000
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage_scoped.errors.txt.diff
+++ /dev/null
@@ -1,47 +0,0 @@
---- old.duplicatePackage_relativeImportWithinPackage_scoped.errors.txt
-+++ new.duplicatePackage_relativeImportWithinPackage_scoped.errors.txt
-@@= skipped -0, +0 lines =@@
--
-+/index.ts(4,5): error TS2345: Argument of type 'import("/node_modules/a/node_modules/@foo/bar/index").C' is not assignable to parameter of type 'import("/node_modules/@foo/bar/index").C'.
-+ Types have separate declarations of a private property 'x'.
-+
-+
-+==== /index.ts (1 errors) ====
-+ import { use } from "@foo/bar/use";
-+ import { o } from "a";
-+
-+ use(o);
-+ ~
-+!!! error TS2345: Argument of type 'import("/node_modules/a/node_modules/@foo/bar/index").C' is not assignable to parameter of type 'import("/node_modules/@foo/bar/index").C'.
-+!!! error TS2345: Types have separate declarations of a private property 'x'.
-+
-+==== /node_modules/a/node_modules/@foo/bar/package.json (0 errors) ====
-+ {
-+ "name": "@foo/bar",
-+ "version": "1.2.3"
-+ }
-+
-+==== /node_modules/a/node_modules/@foo/bar/index.d.ts (0 errors) ====
-+ export class C {
-+ private x: number;
-+ }
-+
-+==== /node_modules/a/index.d.ts (0 errors) ====
-+ import { C } from "@foo/bar";
-+ export const o: C;
-+
-+==== /node_modules/@foo/bar/use.d.ts (0 errors) ====
-+ import { C } from "./index";
-+ export function use(o: C): void;
-+
-+==== /node_modules/@foo/bar/index.d.ts (0 errors) ====
-+ export class C {
-+ private x: number;
-+ }
-+
-+==== /node_modules/@foo/bar/package.json (0 errors) ====
-+ {
-+ "name": "@foo/bar",
-+ "version": "1.2.3"
-+ }
-+
\ No newline at end of file
diff --git a/testdata/baselines/reference/tsbuild/commandLine/help.js b/testdata/baselines/reference/tsbuild/commandLine/help.js
index 856a112bfcb..e265cee7a4b 100644
--- a/testdata/baselines/reference/tsbuild/commandLine/help.js
+++ b/testdata/baselines/reference/tsbuild/commandLine/help.js
@@ -104,6 +104,11 @@ Disable full type checking (only critical parse and emit errors will be reported
type: boolean
default: false
+[94m--disablePackageDeduplication[39m
+Disable deduplication of packages with the same name and version.
+type: boolean
+default: false
+
[94m--noEmit[39m
Disable emitting files from a compilation.
type: boolean
diff --git a/testdata/baselines/reference/tsbuild/commandLine/locale.js b/testdata/baselines/reference/tsbuild/commandLine/locale.js
index 26271d5f01b..002c429519d 100644
--- a/testdata/baselines/reference/tsbuild/commandLine/locale.js
+++ b/testdata/baselines/reference/tsbuild/commandLine/locale.js
@@ -104,6 +104,11 @@ Disable full type checking (only critical parse and emit errors will be reported
type: boolean
default: false
+[94m--disablePackageDeduplication[39m
+Disable deduplication of packages with the same name and version.
+type: boolean
+default: false
+
[94m--noEmit[39m
Disable emitting files from a compilation.
type: boolean
diff --git a/testdata/baselines/reference/tsc/commandLine/help-all.js b/testdata/baselines/reference/tsc/commandLine/help-all.js
index 5e561d8833e..426564f638d 100644
--- a/testdata/baselines/reference/tsc/commandLine/help-all.js
+++ b/testdata/baselines/reference/tsc/commandLine/help-all.js
@@ -221,6 +221,11 @@ Ensure 'use strict' is always emitted.
type: boolean
default: `false`, unless `strict` is set
+[94m--disablePackageDeduplication[39m
+Disable deduplication of packages with the same name and version.
+type: boolean
+default: false
+
[94m--exactOptionalPropertyTypes[39m
Interpret optional property types as written, rather than adding 'undefined'.
type: boolean
From 358bd7158b2934e85e2e0ee3b4abf010c7a3ff74 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Thu, 11 Dec 2025 20:12:25 -0800
Subject: [PATCH 04/29] Update duplicate package test, sort of intentional
behavior
---
internal/fourslash/_scripts/failingTests.txt | 1 -
.../duplicatePackageServices.baseline.jsonc | 33 +++++++++++++++++++
.../duplicatePackageServices.baseline.jsonc | 21 ++++++++++++
...plicatePackageServices.baseline.jsonc.diff | 11 +++++++
4 files changed, 65 insertions(+), 1 deletion(-)
create mode 100644 testdata/baselines/reference/fourslash/findAllReferences/duplicatePackageServices.baseline.jsonc
create mode 100644 testdata/baselines/reference/submodule/fourslash/goToDefinition/duplicatePackageServices.baseline.jsonc
create mode 100644 testdata/baselines/reference/submodule/fourslash/goToDefinition/duplicatePackageServices.baseline.jsonc.diff
diff --git a/internal/fourslash/_scripts/failingTests.txt b/internal/fourslash/_scripts/failingTests.txt
index be20094672f..290e2e66c7e 100644
--- a/internal/fourslash/_scripts/failingTests.txt
+++ b/internal/fourslash/_scripts/failingTests.txt
@@ -246,7 +246,6 @@ TestContextuallyTypedFunctionExpressionGeneric1
TestContextualTypingOfGenericCallSignatures2
TestCrossFileQuickInfoExportedTypeDoesNotUseImportType
TestDoubleUnderscoreCompletions
-TestDuplicatePackageServices
TestEditJsdocType
TestErrorsAfterResolvingVariableDeclOfMergedVariableAndClassDecl
TestExportDefaultClass
diff --git a/testdata/baselines/reference/fourslash/findAllReferences/duplicatePackageServices.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllReferences/duplicatePackageServices.baseline.jsonc
new file mode 100644
index 00000000000..a2324c92698
--- /dev/null
+++ b/testdata/baselines/reference/fourslash/findAllReferences/duplicatePackageServices.baseline.jsonc
@@ -0,0 +1,33 @@
+// === findAllReferences ===
+// === /node_modules/a/index.d.ts ===
+// import [|X|]/*FIND ALL REFS*/ from "x";
+// export function a(x: [|X|]): void;
+
+// === /node_modules/a/node_modules/x/index.d.ts ===
+// export default class [|X|] {
+// private x: number;
+// }
+
+
+
+// === findAllReferences ===
+// === /node_modules/a/index.d.ts ===
+// import [|X|] from "x";
+// export function a(x: [|X|]): void;
+
+// === /node_modules/a/node_modules/x/index.d.ts ===
+// export default class /*FIND ALL REFS*/[|X|] {
+// private x: number;
+// }
+
+
+
+// === findAllReferences ===
+// === /node_modules/b/index.d.ts ===
+// import [|X|]/*FIND ALL REFS*/ from "x";
+// export const b: [|X|];
+
+// === /node_modules/b/node_modules/x/index.d.ts ===
+// export default class [|X|] {
+// private x: number;
+// }
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/fourslash/goToDefinition/duplicatePackageServices.baseline.jsonc b/testdata/baselines/reference/submodule/fourslash/goToDefinition/duplicatePackageServices.baseline.jsonc
new file mode 100644
index 00000000000..f2710c3963e
--- /dev/null
+++ b/testdata/baselines/reference/submodule/fourslash/goToDefinition/duplicatePackageServices.baseline.jsonc
@@ -0,0 +1,21 @@
+// === goToDefinition ===
+// === /node_modules/a/node_modules/x/index.d.ts ===
+// <|export default class [|X|] {
+// private x: number;
+// }|>
+
+// === /node_modules/a/index.d.ts ===
+// import [|X|]/*GOTO DEF*/ from "x";
+// export function a(x: X): void;
+
+
+
+// === goToDefinition ===
+// === /node_modules/b/node_modules/x/index.d.ts ===
+// <|export default class [|X|] {
+// private x: number;
+// }|>
+
+// === /node_modules/b/index.d.ts ===
+// import [|X|]/*GOTO DEF*/ from "x";
+// export const b: X;
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/fourslash/goToDefinition/duplicatePackageServices.baseline.jsonc.diff b/testdata/baselines/reference/submodule/fourslash/goToDefinition/duplicatePackageServices.baseline.jsonc.diff
new file mode 100644
index 00000000000..15df7ad52ad
--- /dev/null
+++ b/testdata/baselines/reference/submodule/fourslash/goToDefinition/duplicatePackageServices.baseline.jsonc.diff
@@ -0,0 +1,11 @@
+--- old.duplicatePackageServices.baseline.jsonc
++++ new.duplicatePackageServices.baseline.jsonc
+@@= skipped -10, +10 lines =@@
+
+
+ // === goToDefinition ===
+-// === /node_modules/a/node_modules/x/index.d.ts ===
++// === /node_modules/b/node_modules/x/index.d.ts ===
+ // <|export default class [|X|] {
+ // private x: number;
+ // }|>
\ No newline at end of file
From ed7017cf74f1c28f5c0b731e96cd5773d07cd1be Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Thu, 11 Dec 2025 20:56:00 -0800
Subject: [PATCH 05/29] equality
---
internal/checker/checker.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/internal/checker/checker.go b/internal/checker/checker.go
index 02d7af92f12..b4bb178457a 100644
--- a/internal/checker/checker.go
+++ b/internal/checker/checker.go
@@ -27017,7 +27017,7 @@ func (c *Checker) areSymbolsFromSamePackageFile(source *ast.Symbol, target *ast.
return false
}
// If they're from the same file, this is not a package deduplication scenario
- if sourceFile.Path() == targetFile.Path() {
+ if sourceFile == targetFile {
return false
}
// Get the canonical paths for both files
From f98b3771f8e62684258bc58a9514c0e44636eba8 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Thu, 11 Dec 2025 20:58:08 -0800
Subject: [PATCH 06/29] rename
---
internal/checker/checker.go | 13 +++----------
internal/compiler/program.go | 4 ++--
.../transformers/tstransforms/importelision_test.go | 2 +-
3 files changed, 6 insertions(+), 13 deletions(-)
diff --git a/internal/checker/checker.go b/internal/checker/checker.go
index b4bb178457a..713acaec412 100644
--- a/internal/checker/checker.go
+++ b/internal/checker/checker.go
@@ -544,10 +544,7 @@ type Program interface {
GetProjectReferenceFromOutputDts(path tspath.Path) *tsoptions.SourceOutputAndProjectReference
GetRedirectForResolution(file ast.HasFileName) *tsoptions.ParsedCommandLine
CommonSourceDirectory() string
- // GetFileCanonicalPath returns the canonical path for a file that may be from a duplicate package.
- // Used to determine if two files from the same package (name@version) installed in different locations
- // should be treated as the same for type compatibility purposes.
- GetFileCanonicalPath(path tspath.Path) tspath.Path
+ GetDeduplicatedPackagePath(path tspath.Path) tspath.Path
}
type Host interface {
@@ -27016,15 +27013,11 @@ func (c *Checker) areSymbolsFromSamePackageFile(source *ast.Symbol, target *ast.
if sourceFile == nil || targetFile == nil {
return false
}
- // If they're from the same file, this is not a package deduplication scenario
+ // If they're from the same file, they can't have been deduplicated.
if sourceFile == targetFile {
return false
}
- // Get the canonical paths for both files
- sourceCanonical := c.program.GetFileCanonicalPath(sourceFile.Path())
- targetCanonical := c.program.GetFileCanonicalPath(targetFile.Path())
- // If they map to the same canonical path, they're from the same package file
- return sourceCanonical == targetCanonical
+ return c.program.GetDeduplicatedPackagePath(sourceFile.Path()) == c.program.GetDeduplicatedPackagePath(targetFile.Path())
}
func compareTypesEqual(s *Type, t *Type) Ternary {
diff --git a/internal/compiler/program.go b/internal/compiler/program.go
index e643f9ab693..0eb91890598 100644
--- a/internal/compiler/program.go
+++ b/internal/compiler/program.go
@@ -116,10 +116,10 @@ func (p *Program) GetRedirectTargets(path tspath.Path) []string {
return p.redirectTargetsMap[path]
}
-// GetFileCanonicalPath returns the canonical path for a file that may be a duplicate package.
+// GetDeduplicatedPackagePath returns the canonical path for a file that may be a duplicate package.
// If the file is a redirect target (i.e., it redirects to a canonical file), returns the canonical path.
// Otherwise, returns the path unchanged.
-func (p *Program) GetFileCanonicalPath(path tspath.Path) tspath.Path {
+func (p *Program) GetDeduplicatedPackagePath(path tspath.Path) tspath.Path {
// If this path has redirect targets, it's already canonical
if _, ok := p.redirectTargetsMap[path]; ok {
return path
diff --git a/internal/transformers/tstransforms/importelision_test.go b/internal/transformers/tstransforms/importelision_test.go
index 753c8e2e083..d17c058f78f 100644
--- a/internal/transformers/tstransforms/importelision_test.go
+++ b/internal/transformers/tstransforms/importelision_test.go
@@ -86,7 +86,7 @@ func (p *fakeProgram) GetRedirectTargets(path tspath.Path) []string {
return nil
}
-func (p *fakeProgram) GetFileCanonicalPath(path tspath.Path) tspath.Path {
+func (p *fakeProgram) GetDeduplicatedPackagePath(path tspath.Path) tspath.Path {
return path
}
From ac7cfc56477b38465bc0b360eb5e8e5ad80e2695 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Thu, 11 Dec 2025 21:07:48 -0800
Subject: [PATCH 07/29] better comparison
---
internal/compiler/fileloader.go | 5 ++++-
internal/compiler/filesparser.go | 13 ++++++++++---
internal/compiler/program.go | 13 ++-----------
3 files changed, 16 insertions(+), 15 deletions(-)
diff --git a/internal/compiler/fileloader.go b/internal/compiler/fileloader.go
index bb9bb1f8ae4..0b69107b012 100644
--- a/internal/compiler/fileloader.go
+++ b/internal/compiler/fileloader.go
@@ -72,7 +72,10 @@ type processedFiles struct {
sourceFileToPackageName map[tspath.Path]string
// Key is a file path. Value is the list of files that redirect to it (same package, different install location)
redirectTargetsMap map[tspath.Path][]string
- finishedProcessing bool
+ // Maps any path (canonical or redirect target) to its canonical path.
+ // Canonical paths map to themselves; redirect targets map to their canonical path.
+ deduplicatedPathMap map[tspath.Path]tspath.Path
+ finishedProcessing bool
}
type jsxRuntimeImportSpecifier struct {
diff --git a/internal/compiler/filesparser.go b/internal/compiler/filesparser.go
index 7a84b652b22..92cda6e1c4c 100644
--- a/internal/compiler/filesparser.go
+++ b/internal/compiler/filesparser.go
@@ -413,8 +413,9 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
// Skip this if package deduplication is disabled.
var sourceFileToPackageName map[tspath.Path]string
var redirectTargetsMap map[tspath.Path][]string
+ var deduplicatedPathMap map[tspath.Path]tspath.Path
if !loader.opts.Config.CompilerOptions().DisablePackageDeduplication.IsTrue() {
- sourceFileToPackageName, redirectTargetsMap = computePackageRedirects(resolvedModules, loader.toPath)
+ sourceFileToPackageName, redirectTargetsMap, deduplicatedPathMap = computePackageRedirects(resolvedModules, loader.toPath)
}
return processedFiles{
@@ -435,6 +436,7 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
outputFileToProjectReferenceSource: outputFileToProjectReferenceSource,
sourceFileToPackageName: sourceFileToPackageName,
redirectTargetsMap: redirectTargetsMap,
+ deduplicatedPathMap: deduplicatedPathMap,
}
}
@@ -445,7 +447,7 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
func computePackageRedirects(
resolvedModules map[tspath.Path]module.ModeAwareCache[*module.ResolvedModule],
toPath func(string) tspath.Path,
-) (sourceFileToPackageName map[tspath.Path]string, redirectTargetsMap map[tspath.Path][]string) {
+) (sourceFileToPackageName map[tspath.Path]string, redirectTargetsMap map[tspath.Path][]string, deduplicatedPathMap map[tspath.Path]tspath.Path) {
// Collect all resolved files with package IDs
// packageIdKey -> list of (resolvedPath, packageName)
type fileInfo struct {
@@ -498,6 +500,7 @@ func computePackageRedirects(
// and build the redirect map
sourceFileToPackageName = make(map[tspath.Path]string)
redirectTargetsMap = make(map[tspath.Path][]string)
+ deduplicatedPathMap = make(map[tspath.Path]tspath.Path)
for _, files := range packageIdToFiles {
if len(files) == 0 {
@@ -524,13 +527,17 @@ func computePackageRedirects(
// If there are multiple files, the others redirect to the canonical one
if len(files) > 1 {
+ // Canonical path maps to itself
+ deduplicatedPathMap[canonicalPath] = canonicalPath
for _, f := range files[1:] {
redirectTargetsMap[canonicalPath] = append(redirectTargetsMap[canonicalPath], string(f.path))
+ // Redirect target maps to canonical
+ deduplicatedPathMap[f.path] = canonicalPath
}
}
}
- return sourceFileToPackageName, redirectTargetsMap
+ return sourceFileToPackageName, redirectTargetsMap, deduplicatedPathMap
}
func (w *filesParser) addIncludeReason(includeProcessor *includeProcessor, task *parseTask, reason *FileIncludeReason) {
diff --git a/internal/compiler/program.go b/internal/compiler/program.go
index 0eb91890598..97ecee2dee1 100644
--- a/internal/compiler/program.go
+++ b/internal/compiler/program.go
@@ -120,17 +120,8 @@ func (p *Program) GetRedirectTargets(path tspath.Path) []string {
// If the file is a redirect target (i.e., it redirects to a canonical file), returns the canonical path.
// Otherwise, returns the path unchanged.
func (p *Program) GetDeduplicatedPackagePath(path tspath.Path) tspath.Path {
- // If this path has redirect targets, it's already canonical
- if _, ok := p.redirectTargetsMap[path]; ok {
- return path
- }
- // Check if this path is a redirect target of some canonical path
- for canonicalPath, targets := range p.redirectTargetsMap {
- for _, t := range targets {
- if tspath.Path(t) == path {
- return canonicalPath
- }
- }
+ if canonicalPath, ok := p.deduplicatedPathMap[path]; ok {
+ return canonicalPath
}
// Not part of any redirect group, return as-is
return path
From 43f6e083e74ce0db24b9ebb6a23a4a98805a7838 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Thu, 11 Dec 2025 21:11:57 -0800
Subject: [PATCH 08/29] Fix paths
---
internal/compiler/filesparser.go | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/internal/compiler/filesparser.go b/internal/compiler/filesparser.go
index 92cda6e1c4c..9b25a8262df 100644
--- a/internal/compiler/filesparser.go
+++ b/internal/compiler/filesparser.go
@@ -452,6 +452,7 @@ func computePackageRedirects(
// packageIdKey -> list of (resolvedPath, packageName)
type fileInfo struct {
path tspath.Path
+ fileName string
packageName string
}
packageIdToFiles := make(map[string][]fileInfo)
@@ -478,7 +479,8 @@ func computePackageRedirects(
if pkgId.SubModuleName != "" {
packageIdKey = pkgId.Name + "/" + pkgId.SubModuleName + "@" + pkgId.Version
}
- resolvedPath := toPath(resolution.ResolvedFileName)
+ resolvedFileName := resolution.ResolvedFileName
+ resolvedPath := toPath(resolvedFileName)
packageName := pkgId.PackageName()
// Check if we've already recorded this path for this package
@@ -491,7 +493,7 @@ func computePackageRedirects(
}
}
if !found {
- packageIdToFiles[packageIdKey] = append(files, fileInfo{path: resolvedPath, packageName: packageName})
+ packageIdToFiles[packageIdKey] = append(files, fileInfo{path: resolvedPath, fileName: resolvedFileName, packageName: packageName})
}
}
}
@@ -530,7 +532,7 @@ func computePackageRedirects(
// Canonical path maps to itself
deduplicatedPathMap[canonicalPath] = canonicalPath
for _, f := range files[1:] {
- redirectTargetsMap[canonicalPath] = append(redirectTargetsMap[canonicalPath], string(f.path))
+ redirectTargetsMap[canonicalPath] = append(redirectTargetsMap[canonicalPath], f.fileName)
// Redirect target maps to canonical
deduplicatedPathMap[f.path] = canonicalPath
}
From b99c0ff4d779caec409a5818a92d0fb5ed1877b6 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Thu, 11 Dec 2025 21:18:07 -0800
Subject: [PATCH 09/29] Cleanup
---
internal/compiler/filesparser.go | 19 ++++---------------
1 file changed, 4 insertions(+), 15 deletions(-)
diff --git a/internal/compiler/filesparser.go b/internal/compiler/filesparser.go
index 9b25a8262df..99d1c464a09 100644
--- a/internal/compiler/filesparser.go
+++ b/internal/compiler/filesparser.go
@@ -1,6 +1,8 @@
package compiler
import (
+ "cmp"
+ "maps"
"math"
"slices"
"sync"
@@ -457,12 +459,7 @@ func computePackageRedirects(
}
packageIdToFiles := make(map[string][]fileInfo)
- // Iterate through resolvedModules in sorted order for determinism
- containingFilePaths := make([]tspath.Path, 0, len(resolvedModules))
- for containingPath := range resolvedModules {
- containingFilePaths = append(containingFilePaths, containingPath)
- }
- slices.Sort(containingFilePaths)
+ containingFilePaths := slices.AppendSeq(make([]tspath.Path, 0, len(resolvedModules)), maps.Keys(resolvedModules))
for _, containingPath := range containingFilePaths {
resolutions := resolvedModules[containingPath]
@@ -509,15 +506,7 @@ func computePackageRedirects(
continue
}
- // Sort files by path for determinism - first one becomes canonical
- slices.SortFunc(files, func(a, b fileInfo) int {
- if a.path < b.path {
- return -1
- } else if a.path > b.path {
- return 1
- }
- return 0
- })
+ slices.SortFunc(files, func(a, b fileInfo) int { return cmp.Compare(a.path, b.path) })
canonicalPath := files[0].path
packageName := files[0].packageName
From e25f2bc3a1c1a50dc3292dc192b1c9dd559831ae Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Thu, 11 Dec 2025 21:23:39 -0800
Subject: [PATCH 10/29] Cleanup
---
internal/compiler/filesparser.go | 23 ++++++-----------------
1 file changed, 6 insertions(+), 17 deletions(-)
diff --git a/internal/compiler/filesparser.go b/internal/compiler/filesparser.go
index 99d1c464a09..904774560ff 100644
--- a/internal/compiler/filesparser.go
+++ b/internal/compiler/filesparser.go
@@ -457,40 +457,29 @@ func computePackageRedirects(
fileName string
packageName string
}
- packageIdToFiles := make(map[string][]fileInfo)
+ packageIdToFiles := make(map[module.PackageId][]fileInfo)
containingFilePaths := slices.AppendSeq(make([]tspath.Path, 0, len(resolvedModules)), maps.Keys(resolvedModules))
+ slices.Sort(containingFilePaths)
for _, containingPath := range containingFilePaths {
resolutions := resolvedModules[containingPath]
for _, resolution := range resolutions {
- if resolution == nil || !resolution.IsResolved() {
+ if !resolution.IsResolved() {
continue
}
pkgId := resolution.PackageId
if pkgId.Name == "" {
continue
}
- // packageIdKey is "name@version" (excluding peerDependencies for redirect grouping)
- packageIdKey := pkgId.Name + "@" + pkgId.Version
- if pkgId.SubModuleName != "" {
- packageIdKey = pkgId.Name + "/" + pkgId.SubModuleName + "@" + pkgId.Version
- }
resolvedFileName := resolution.ResolvedFileName
resolvedPath := toPath(resolvedFileName)
packageName := pkgId.PackageName()
// Check if we've already recorded this path for this package
- files := packageIdToFiles[packageIdKey]
- found := false
- for _, f := range files {
- if f.path == resolvedPath {
- found = true
- break
- }
- }
- if !found {
- packageIdToFiles[packageIdKey] = append(files, fileInfo{path: resolvedPath, fileName: resolvedFileName, packageName: packageName})
+ files := packageIdToFiles[pkgId]
+ if !slices.ContainsFunc(files, func(f fileInfo) bool { return f.path == resolvedPath }) {
+ packageIdToFiles[pkgId] = append(files, fileInfo{path: resolvedPath, fileName: resolvedFileName, packageName: packageName})
}
}
}
From 044eee238be3975756324cd33b8e5d41c89e644e Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Thu, 11 Dec 2025 21:33:00 -0800
Subject: [PATCH 11/29] Manual test
---
internal/fourslash/_scripts/manualTests.txt | 3 +-
...plicatePackageServices_fileChanges_test.go | 68 +++++++++++++++++++
2 files changed, 70 insertions(+), 1 deletion(-)
create mode 100644 internal/fourslash/tests/manual/duplicatePackageServices_fileChanges_test.go
diff --git a/internal/fourslash/_scripts/manualTests.txt b/internal/fourslash/_scripts/manualTests.txt
index 5373890ae08..cbbe91801b0 100644
--- a/internal/fourslash/_scripts/manualTests.txt
+++ b/internal/fourslash/_scripts/manualTests.txt
@@ -2,6 +2,7 @@ completionListInClosedFunction05
completionsAtIncompleteObjectLiteralProperty
completionsSelfDeclaring1
completionsWithDeprecatedTag4
+duplicatePackageServices_fileChanges
navigationBarFunctionPrototype
navigationBarFunctionPrototype2
navigationBarFunctionPrototype3
@@ -26,4 +27,4 @@ jsDocFunctionSignatures12
outliningHintSpansForFunction
getOutliningSpans
outliningForNonCompleteInterfaceDeclaration
-incrementalParsingWithJsDoc
\ No newline at end of file
+incrementalParsingWithJsDoc
diff --git a/internal/fourslash/tests/manual/duplicatePackageServices_fileChanges_test.go b/internal/fourslash/tests/manual/duplicatePackageServices_fileChanges_test.go
new file mode 100644
index 00000000000..a3aed4e54a1
--- /dev/null
+++ b/internal/fourslash/tests/manual/duplicatePackageServices_fileChanges_test.go
@@ -0,0 +1,68 @@
+package fourslash_test
+
+import (
+ "testing"
+
+ "github.com/microsoft/typescript-go/internal/fourslash"
+ "github.com/microsoft/typescript-go/internal/testutil"
+)
+
+func TestDuplicatePackageServices_fileChanges(t *testing.T) {
+ t.Parallel()
+
+ defer testutil.RecoverAndFail(t, "Panic on fourslash test")
+ const content = `// @noImplicitReferences: true
+// @Filename: /node_modules/a/index.d.ts
+import X from "x";
+export function a(x: X): void;
+// @Filename: /node_modules/a/node_modules/x/index.d.ts
+export default class /*defAX*/X {
+ private x: number;
+}
+// @Filename: /node_modules/a/node_modules/x/package.json
+{ "name": "x", "version": "1.2./*aVersionPatch*/3" }
+// @Filename: /node_modules/b/index.d.ts
+import X from "x";
+export const b: X;
+// @Filename: /node_modules/b/node_modules/x/index.d.ts
+export default class /*defBX*/X {
+ private x: number;
+}
+// @Filename: /node_modules/b/node_modules/x/package.json
+{ "name": "x", "version": "1.2./*bVersionPatch*/3" }
+// @Filename: /src/a.ts
+import { a } from "a";
+import { b } from "b";
+a(/*error*/b);`
+ f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
+ defer done()
+
+ f.GoToFile(t, "/src/a.ts")
+ f.VerifyNumberOfErrorsInCurrentFile(t, 0)
+
+ testChangeAndChangeBack := func(versionPatch string, def string) {
+ // Insert "4" after the version patch marker, changing version from 1.2.3 to 1.2.43
+ f.GoToMarker(t, versionPatch)
+ f.Insert(t, "4")
+
+ // Insert a space after the definition marker to trigger a recheck
+ f.GoToMarker(t, def)
+ f.Insert(t, " ")
+
+ // No longer have identical packageId, so we get errors.
+ f.VerifyErrorExistsAfterMarker(t, "error")
+
+ // Undo the changes
+ f.GoToMarker(t, versionPatch)
+ f.DeleteAtCaret(t, 1)
+ f.GoToMarker(t, def)
+ f.DeleteAtCaret(t, 1)
+
+ // Back to being identical.
+ f.GoToFile(t, "/src/a.ts")
+ f.VerifyNumberOfErrorsInCurrentFile(t, 0)
+ }
+
+ testChangeAndChangeBack("aVersionPatch", "defAX")
+ testChangeAndChangeBack("bVersionPatch", "defBX")
+}
From d47b3a7f6d05e0f39e8c4376366e7c6830c0dc88 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Thu, 11 Dec 2025 21:39:32 -0800
Subject: [PATCH 12/29] files are not nil
---
internal/checker/checker.go | 3 ---
1 file changed, 3 deletions(-)
diff --git a/internal/checker/checker.go b/internal/checker/checker.go
index 713acaec412..f594f9de683 100644
--- a/internal/checker/checker.go
+++ b/internal/checker/checker.go
@@ -27010,9 +27010,6 @@ func (c *Checker) areSymbolsFromSamePackageFile(source *ast.Symbol, target *ast.
}
sourceFile := ast.GetSourceFileOfNode(sourceDecl)
targetFile := ast.GetSourceFileOfNode(targetDecl)
- if sourceFile == nil || targetFile == nil {
- return false
- }
// If they're from the same file, they can't have been deduplicated.
if sourceFile == targetFile {
return false
From d7e82327bc1d853a0447e441f2ff9906cde7445a Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Mon, 15 Dec 2025 10:40:45 -0800
Subject: [PATCH 13/29] Go to AST replacing version
---
internal/checker/checker.go | 32 +--------------
internal/checker/relater.go | 2 +-
internal/compiler/filesparser.go | 11 +++++
internal/compiler/program.go | 27 +++---------
.../tstransforms/importelision_test.go | 4 --
internal/tsoptions/declscompiler.go | 3 +-
.../duplicatePackageServices.baseline.jsonc | 22 +++++++---
...tionEmitForGlobalishSpecifierSymlink.types | 2 +-
...mitForGlobalishSpecifierSymlink.types.diff | 9 ++++
.../compiler/duplicatePackage.errors.txt | 5 +--
.../compiler/duplicatePackage.errors.txt.diff | 10 +----
.../compiler/duplicatePackage.symbols | 4 +-
.../compiler/duplicatePackage.symbols.diff | 14 -------
.../submodule/compiler/duplicatePackage.types | 12 +++---
.../compiler/duplicatePackage.types.diff | 41 -------------------
.../duplicatePackage_globalMerge.errors.txt | 4 +-
...plicatePackage_globalMerge.errors.txt.diff | 19 +++++++++
.../duplicatePackage_globalMerge.symbols | 2 +
.../duplicatePackage_globalMerge.symbols.diff | 11 -----
.../duplicatePackage_globalMerge.types | 2 +
.../duplicatePackage_globalMerge.types.diff | 11 -----
.../duplicatePackage_referenceTypes.types | 4 +-
...duplicatePackage_referenceTypes.types.diff | 14 -------
...ePackage_relativeImportWithinPackage.types | 4 +-
...age_relativeImportWithinPackage.types.diff | 11 +++--
...e_relativeImportWithinPackage_scoped.types | 4 +-
...ativeImportWithinPackage_scoped.types.diff | 17 --------
.../duplicatePackage_subModule.errors.txt | 33 ---------------
...duplicatePackage_subModule.errors.txt.diff | 37 -----------------
.../compiler/duplicatePackage_subModule.types | 4 +-
.../duplicatePackage_subModule.types.diff | 14 -------
.../duplicatePackage_withErrors.errors.txt | 5 +--
...uplicatePackage_withErrors.errors.txt.diff | 11 +----
.../duplicatePackage_withErrors.symbols | 2 +-
.../duplicatePackage_withErrors.symbols.diff | 9 ----
.../duplicatePackage_withErrors.types | 13 +++---
.../duplicatePackage_withErrors.types.diff | 29 -------------
.../duplicatePackageServices.baseline.jsonc | 2 +-
...plicatePackageServices.baseline.jsonc.diff | 11 -----
39 files changed, 111 insertions(+), 360 deletions(-)
create mode 100644 testdata/baselines/reference/submodule/compiler/declarationEmitForGlobalishSpecifierSymlink.types.diff
delete mode 100644 testdata/baselines/reference/submodule/compiler/duplicatePackage.symbols.diff
delete mode 100644 testdata/baselines/reference/submodule/compiler/duplicatePackage.types.diff
create mode 100644 testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.errors.txt.diff
delete mode 100644 testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.symbols.diff
delete mode 100644 testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.types.diff
delete mode 100644 testdata/baselines/reference/submodule/compiler/duplicatePackage_referenceTypes.types.diff
delete mode 100644 testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage_scoped.types.diff
delete mode 100644 testdata/baselines/reference/submodule/compiler/duplicatePackage_subModule.errors.txt
delete mode 100644 testdata/baselines/reference/submodule/compiler/duplicatePackage_subModule.errors.txt.diff
delete mode 100644 testdata/baselines/reference/submodule/compiler/duplicatePackage_subModule.types.diff
delete mode 100644 testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.symbols.diff
delete mode 100644 testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.types.diff
delete mode 100644 testdata/baselines/reference/submodule/fourslash/goToDefinition/duplicatePackageServices.baseline.jsonc.diff
diff --git a/internal/checker/checker.go b/internal/checker/checker.go
index f594f9de683..8f5065c9be7 100644
--- a/internal/checker/checker.go
+++ b/internal/checker/checker.go
@@ -544,7 +544,6 @@ type Program interface {
GetProjectReferenceFromOutputDts(path tspath.Path) *tsoptions.SourceOutputAndProjectReference
GetRedirectForResolution(file ast.HasFileName) *tsoptions.ParsedCommandLine
CommonSourceDirectory() string
- GetDeduplicatedPackagePath(path tspath.Path) tspath.Path
}
type Host interface {
@@ -26976,14 +26975,8 @@ func (c *Checker) compareProperties(sourceProp *ast.Symbol, targetProp *ast.Symb
return TernaryFalse
}
if sourcePropAccessibility != ast.ModifierFlagsNone {
- sourceTarget := c.getTargetSymbol(sourceProp)
- targetTarget := c.getTargetSymbol(targetProp)
- if sourceTarget != targetTarget {
- // Check if these symbols are from duplicate package instances (same package installed
- // in different locations). If they map to the same canonical file, treat them as identical.
- if !c.areSymbolsFromSamePackageFile(sourceTarget, targetTarget) {
- return TernaryFalse
- }
+ if c.getTargetSymbol(sourceProp) != c.getTargetSymbol(targetProp) {
+ return TernaryFalse
}
} else {
if (sourceProp.Flags & ast.SymbolFlagsOptional) != (targetProp.Flags & ast.SymbolFlagsOptional) {
@@ -26996,27 +26989,6 @@ func (c *Checker) compareProperties(sourceProp *ast.Symbol, targetProp *ast.Symb
return compareTypes(c.getTypeOfSymbol(sourceProp), c.getTypeOfSymbol(targetProp))
}
-// areSymbolsFromSamePackageFile checks if two symbols come from files that are duplicates
-// of the same package file (same package name@version installed in different locations).
-func (c *Checker) areSymbolsFromSamePackageFile(source *ast.Symbol, target *ast.Symbol) bool {
- // If package deduplication is disabled, don't treat any files as duplicates
- if c.compilerOptions.DisablePackageDeduplication.IsTrue() {
- return false
- }
- sourceDecl := source.ValueDeclaration
- targetDecl := target.ValueDeclaration
- if sourceDecl == nil || targetDecl == nil {
- return false
- }
- sourceFile := ast.GetSourceFileOfNode(sourceDecl)
- targetFile := ast.GetSourceFileOfNode(targetDecl)
- // If they're from the same file, they can't have been deduplicated.
- if sourceFile == targetFile {
- return false
- }
- return c.program.GetDeduplicatedPackagePath(sourceFile.Path()) == c.program.GetDeduplicatedPackagePath(targetFile.Path())
-}
-
func compareTypesEqual(s *Type, t *Type) Ternary {
if s == t {
return TernaryTrue
diff --git a/internal/checker/relater.go b/internal/checker/relater.go
index b55d891e545..db83af07457 100644
--- a/internal/checker/relater.go
+++ b/internal/checker/relater.go
@@ -4203,7 +4203,7 @@ func (r *Relater) propertyRelatedTo(source *Type, target *Type, sourceProp *ast.
targetPropFlags := getDeclarationModifierFlagsFromSymbol(targetProp)
switch {
case sourcePropFlags&ast.ModifierFlagsPrivate != 0 || targetPropFlags&ast.ModifierFlagsPrivate != 0:
- if sourceProp.ValueDeclaration != targetProp.ValueDeclaration && !r.c.areSymbolsFromSamePackageFile(sourceProp, targetProp) {
+ if sourceProp.ValueDeclaration != targetProp.ValueDeclaration {
if reportErrors {
if sourcePropFlags&ast.ModifierFlagsPrivate != 0 && targetPropFlags&ast.ModifierFlagsPrivate != 0 {
r.reportError(diagnostics.Types_have_separate_declarations_of_a_private_property_0, r.c.symbolToString(targetProp))
diff --git a/internal/compiler/filesparser.go b/internal/compiler/filesparser.go
index 904774560ff..e7a610e1abc 100644
--- a/internal/compiler/filesparser.go
+++ b/internal/compiler/filesparser.go
@@ -418,6 +418,17 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
var deduplicatedPathMap map[tspath.Path]tspath.Path
if !loader.opts.Config.CompilerOptions().DisablePackageDeduplication.IsTrue() {
sourceFileToPackageName, redirectTargetsMap, deduplicatedPathMap = computePackageRedirects(resolvedModules, loader.toPath)
+ // Physically replace duplicate source files with canonical ones.
+ // This ensures that when the checker encounters files from the same package
+ // installed in different locations, they're literally the same AST pointer,
+ // so symbol identity comparisons work correctly.
+ for duplicatePath, canonicalPath := range deduplicatedPathMap {
+ if duplicatePath != canonicalPath {
+ if canonicalFile, ok := filesByPath[canonicalPath]; ok {
+ filesByPath[duplicatePath] = canonicalFile
+ }
+ }
+ }
}
return processedFiles{
diff --git a/internal/compiler/program.go b/internal/compiler/program.go
index 97ecee2dee1..7986e1a19ad 100644
--- a/internal/compiler/program.go
+++ b/internal/compiler/program.go
@@ -116,17 +116,6 @@ func (p *Program) GetRedirectTargets(path tspath.Path) []string {
return p.redirectTargetsMap[path]
}
-// GetDeduplicatedPackagePath returns the canonical path for a file that may be a duplicate package.
-// If the file is a redirect target (i.e., it redirects to a canonical file), returns the canonical path.
-// Otherwise, returns the path unchanged.
-func (p *Program) GetDeduplicatedPackagePath(path tspath.Path) tspath.Path {
- if canonicalPath, ok := p.deduplicatedPathMap[path]; ok {
- return canonicalPath
- }
- // Not part of any redirect group, return as-is
- return path
-}
-
// gets the original file that was included in program
// this returns original source file name when including output of project reference
// otherwise same name
@@ -253,21 +242,15 @@ func (p *Program) UpdateProgram(changedFilePath tspath.Path, newHost CompilerHos
if !canReplaceFileInProgram(oldFile, newFile) {
return NewProgram(newOpts), false
}
- // TODO: CHECK THIS
// If this file is part of a package redirect group (same package installed in multiple
// node_modules locations), we need to rebuild the program because the redirect targets
// might need recalculation. A file is in a redirect group if it's either a canonical
// file that others redirect to, or if it redirects to another file.
- // if _, isCanonical := p.redirectTargetsMap[changedFilePath]; isCanonical {
- // return NewProgram(newOpts), false
- // }
- // for _, targets := range p.redirectTargetsMap {
- // for _, target := range targets {
- // if tspath.Path(target) == changedFilePath {
- // return NewProgram(newOpts), false
- // }
- // }
- // }
+ if canonicalPath, ok := p.deduplicatedPathMap[changedFilePath]; ok {
+ // File is either a canonical file or a redirect target; either way, need full rebuild
+ _ = canonicalPath
+ return NewProgram(newOpts), false
+ }
// TODO: reverify compiler options when config has changed?
result := &Program{
opts: newOpts,
diff --git a/internal/transformers/tstransforms/importelision_test.go b/internal/transformers/tstransforms/importelision_test.go
index d17c058f78f..68c18aa71f4 100644
--- a/internal/transformers/tstransforms/importelision_test.go
+++ b/internal/transformers/tstransforms/importelision_test.go
@@ -86,10 +86,6 @@ func (p *fakeProgram) GetRedirectTargets(path tspath.Path) []string {
return nil
}
-func (p *fakeProgram) GetDeduplicatedPackagePath(path tspath.Path) tspath.Path {
- return path
-}
-
func (p *fakeProgram) GetSourceOfProjectReferenceIfOutputIncluded(file ast.HasFileName) string {
return ""
}
diff --git a/internal/tsoptions/declscompiler.go b/internal/tsoptions/declscompiler.go
index a6bb2733d07..ecbc9f7ad59 100644
--- a/internal/tsoptions/declscompiler.go
+++ b/internal/tsoptions/declscompiler.go
@@ -191,8 +191,7 @@ var commonOptionsWithBuild = []*CommandLineOption{
Category: diagnostics.Type_Checking,
Description: diagnostics.Disable_deduplication_of_packages_with_the_same_name_and_version,
DefaultValueDescription: false,
- AffectsSemanticDiagnostics: true,
- AffectsBuildInfo: true,
+ AffectsProgramStructure: true,
},
{
Name: "noEmit",
diff --git a/testdata/baselines/reference/fourslash/findAllReferences/duplicatePackageServices.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllReferences/duplicatePackageServices.baseline.jsonc
index a2324c92698..3ca6e48ddb7 100644
--- a/testdata/baselines/reference/fourslash/findAllReferences/duplicatePackageServices.baseline.jsonc
+++ b/testdata/baselines/reference/fourslash/findAllReferences/duplicatePackageServices.baseline.jsonc
@@ -8,6 +8,10 @@
// private x: number;
// }
+// === /node_modules/b/index.d.ts ===
+// import [|X|] from "x";
+// export const b: [|X|];
+
// === findAllReferences ===
@@ -20,14 +24,22 @@
// private x: number;
// }
+// === /node_modules/b/index.d.ts ===
+// import [|X|] from "x";
+// export const b: [|X|];
+
// === findAllReferences ===
-// === /node_modules/b/index.d.ts ===
-// import [|X|]/*FIND ALL REFS*/ from "x";
-// export const b: [|X|];
+// === /node_modules/a/index.d.ts ===
+// import [|X|] from "x";
+// export function a(x: [|X|]): void;
-// === /node_modules/b/node_modules/x/index.d.ts ===
+// === /node_modules/a/node_modules/x/index.d.ts ===
// export default class [|X|] {
// private x: number;
-// }
\ No newline at end of file
+// }
+
+// === /node_modules/b/index.d.ts ===
+// import [|X|]/*FIND ALL REFS*/ from "x";
+// export const b: [|X|];
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/compiler/declarationEmitForGlobalishSpecifierSymlink.types b/testdata/baselines/reference/submodule/compiler/declarationEmitForGlobalishSpecifierSymlink.types
index 78a25058020..3539b7c1032 100644
--- a/testdata/baselines/reference/submodule/compiler/declarationEmitForGlobalishSpecifierSymlink.types
+++ b/testdata/baselines/reference/submodule/compiler/declarationEmitForGlobalishSpecifierSymlink.types
@@ -40,6 +40,6 @@ export const a = getA();
=== /p2/index.d.ts ===
export const a: import("typescript-fsa").A;
->a : import("/p2/node_modules/typescript-fsa/index").A
+>a : import("/p1/node_modules/typescript-fsa/index").A
diff --git a/testdata/baselines/reference/submodule/compiler/declarationEmitForGlobalishSpecifierSymlink.types.diff b/testdata/baselines/reference/submodule/compiler/declarationEmitForGlobalishSpecifierSymlink.types.diff
new file mode 100644
index 00000000000..045997d7f18
--- /dev/null
+++ b/testdata/baselines/reference/submodule/compiler/declarationEmitForGlobalishSpecifierSymlink.types.diff
@@ -0,0 +1,9 @@
+--- old.declarationEmitForGlobalishSpecifierSymlink.types
++++ new.declarationEmitForGlobalishSpecifierSymlink.types
+@@= skipped -39, +39 lines =@@
+
+ === /p2/index.d.ts ===
+ export const a: import("typescript-fsa").A;
+->a : import("/p2/node_modules/typescript-fsa/index").A
++>a : import("/p1/node_modules/typescript-fsa/index").A
+
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage.errors.txt b/testdata/baselines/reference/submodule/compiler/duplicatePackage.errors.txt
index 4e9b3720b6a..7402b0d3e6a 100644
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage.errors.txt
+++ b/testdata/baselines/reference/submodule/compiler/duplicatePackage.errors.txt
@@ -1,4 +1,3 @@
-/node_modules/b/index.d.ts(1,15): error TS2306: File '/node_modules/b/node_modules/x/index.d.ts' is not a module.
/node_modules/b/node_modules/x/index.d.ts(1,1): error TS1434: Unexpected keyword or identifier.
/node_modules/b/node_modules/x/index.d.ts(1,1): error TS2304: Cannot find name 'content'.
/node_modules/b/node_modules/x/index.d.ts(1,9): error TS1434: Unexpected keyword or identifier.
@@ -30,10 +29,8 @@
==== /node_modules/a/node_modules/x/package.json (0 errors) ====
{ "name": "x", "version": "1.2.3" }
-==== /node_modules/b/index.d.ts (1 errors) ====
+==== /node_modules/b/index.d.ts (0 errors) ====
import X from "x";
- ~~~
-!!! error TS2306: File '/node_modules/b/node_modules/x/index.d.ts' is not a module.
export const b: X;
==== /node_modules/b/node_modules/x/index.d.ts (5 errors) ====
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage.errors.txt.diff b/testdata/baselines/reference/submodule/compiler/duplicatePackage.errors.txt.diff
index ff69e90fced..84aba2edf1c 100644
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage.errors.txt.diff
+++ b/testdata/baselines/reference/submodule/compiler/duplicatePackage.errors.txt.diff
@@ -1,7 +1,6 @@
--- old.duplicatePackage.errors.txt
+++ new.duplicatePackage.errors.txt
@@= skipped -0, +0 lines =@@
-+/node_modules/b/index.d.ts(1,15): error TS2306: File '/node_modules/b/node_modules/x/index.d.ts' is not a module.
+/node_modules/b/node_modules/x/index.d.ts(1,1): error TS1434: Unexpected keyword or identifier.
+/node_modules/b/node_modules/x/index.d.ts(1,1): error TS2304: Cannot find name 'content'.
+/node_modules/b/node_modules/x/index.d.ts(1,9): error TS1434: Unexpected keyword or identifier.
@@ -10,15 +9,8 @@
/src/a.ts(5,3): error TS2345: Argument of type 'import("/node_modules/c/node_modules/x/index").default' is not assignable to parameter of type 'import("/node_modules/a/node_modules/x/index").default'.
Types have separate declarations of a private property 'x'.
-@@= skipped -23, +29 lines =@@
- ==== /node_modules/a/node_modules/x/package.json (0 errors) ====
- { "name": "x", "version": "1.2.3" }
-
--==== /node_modules/b/index.d.ts (0 errors) ====
-+==== /node_modules/b/index.d.ts (1 errors) ====
+@@= skipped -27, +32 lines =@@
import X from "x";
-+ ~~~
-+!!! error TS2306: File '/node_modules/b/node_modules/x/index.d.ts' is not a module.
export const b: X;
-==== /node_modules/b/node_modules/x/index.d.ts (0 errors) ====
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage.symbols b/testdata/baselines/reference/submodule/compiler/duplicatePackage.symbols
index b5fd703b49a..0872054c303 100644
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage.symbols
+++ b/testdata/baselines/reference/submodule/compiler/duplicatePackage.symbols
@@ -44,8 +44,10 @@ export const b: X;
>X : Symbol(X, Decl(index.d.ts, 0, 6))
=== /node_modules/b/node_modules/x/index.d.ts ===
-
content not parsed
+>X : Symbol(X, Decl(index.d.ts, 0, 0))
+
+>x : Symbol(X.x, Decl(index.d.ts, 0, 24))
=== /node_modules/c/index.d.ts ===
import X from "x";
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage.symbols.diff b/testdata/baselines/reference/submodule/compiler/duplicatePackage.symbols.diff
deleted file mode 100644
index a7dc526dc80..00000000000
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage.symbols.diff
+++ /dev/null
@@ -1,14 +0,0 @@
---- old.duplicatePackage.symbols
-+++ new.duplicatePackage.symbols
-@@= skipped -43, +43 lines =@@
- >X : Symbol(X, Decl(index.d.ts, 0, 6))
-
- === /node_modules/b/node_modules/x/index.d.ts ===
--content not parsed
-->X : Symbol(X, Decl(index.d.ts, 0, 0))
-
-->x : Symbol(X.x, Decl(index.d.ts, 0, 24))
-+content not parsed
-
- === /node_modules/c/index.d.ts ===
- import X from "x";
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage.types b/testdata/baselines/reference/submodule/compiler/duplicatePackage.types
index 77b1ce24d12..30efd9bc9e5 100644
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage.types
+++ b/testdata/baselines/reference/submodule/compiler/duplicatePackage.types
@@ -5,7 +5,7 @@ import { a } from "a";
>a : (x: import("/node_modules/a/node_modules/x/index").default) => void
import { b } from "b";
->b : X
+>b : import("/node_modules/a/node_modules/x/index").default
import { c } from "c";
>c : import("/node_modules/c/node_modules/x/index").default
@@ -13,7 +13,7 @@ import { c } from "c";
a(b); // Works
>a(b) : void
>a : (x: import("/node_modules/a/node_modules/x/index").default) => void
->b : X
+>b : import("/node_modules/a/node_modules/x/index").default
a(c); // Error, these are from different versions of the library.
>a(c) : void
@@ -38,16 +38,16 @@ export default class X {
=== /node_modules/b/index.d.ts ===
import X from "x";
->X : any
+>X : typeof X
export const b: X;
>b : X
=== /node_modules/b/node_modules/x/index.d.ts ===
content not parsed
->content : any
->not : any
->parsed : any
+>X : X
+
+>x : number
=== /node_modules/c/index.d.ts ===
import X from "x";
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage.types.diff b/testdata/baselines/reference/submodule/compiler/duplicatePackage.types.diff
deleted file mode 100644
index 6dcafc5f6d9..00000000000
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage.types.diff
+++ /dev/null
@@ -1,41 +0,0 @@
---- old.duplicatePackage.types
-+++ new.duplicatePackage.types
-@@= skipped -4, +4 lines =@@
- >a : (x: import("/node_modules/a/node_modules/x/index").default) => void
-
- import { b } from "b";
-->b : import("/node_modules/a/node_modules/x/index").default
-+>b : X
-
- import { c } from "c";
- >c : import("/node_modules/c/node_modules/x/index").default
-@@= skipped -8, +8 lines =@@
- a(b); // Works
- >a(b) : void
- >a : (x: import("/node_modules/a/node_modules/x/index").default) => void
-->b : import("/node_modules/a/node_modules/x/index").default
-+>b : X
-
- a(c); // Error, these are from different versions of the library.
- >a(c) : void
-@@= skipped -25, +25 lines =@@
-
- === /node_modules/b/index.d.ts ===
- import X from "x";
-->X : typeof X
-+>X : any
-
- export const b: X;
- >b : X
-
- === /node_modules/b/node_modules/x/index.d.ts ===
- content not parsed
-->X : X
--
-->x : number
-+>content : any
-+>not : any
-+>parsed : any
-
- === /node_modules/c/index.d.ts ===
- import X from "x";
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.errors.txt b/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.errors.txt
index d7e77340ac1..d0be79b1b5e 100644
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.errors.txt
+++ b/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.errors.txt
@@ -1,6 +1,6 @@
/node_modules/@types/react/index.d.ts(1,9): error TS2669: Augmentations for the global scope can only be directly nested in external modules or ambient module declarations.
/src/index.ts(1,24): error TS2306: File '/node_modules/@types/react/index.d.ts' is not a module.
-/tests/index.ts(1,24): error TS2306: File '/tests/node_modules/@types/react/index.d.ts' is not a module.
+/tests/index.ts(1,24): error TS2306: File '/node_modules/@types/react/index.d.ts' is not a module.
==== /src/bug25410.ts (0 errors) ====
@@ -15,7 +15,7 @@
==== /tests/index.ts (1 errors) ====
import * as React from 'react';
~~~~~~~
-!!! error TS2306: File '/tests/node_modules/@types/react/index.d.ts' is not a module.
+!!! error TS2306: File '/node_modules/@types/react/index.d.ts' is not a module.
export var y = 2
==== /tests/node_modules/@types/react/package.json (0 errors) ====
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.errors.txt.diff b/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.errors.txt.diff
new file mode 100644
index 00000000000..1f44baa7bff
--- /dev/null
+++ b/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.errors.txt.diff
@@ -0,0 +1,19 @@
+--- old.duplicatePackage_globalMerge.errors.txt
++++ new.duplicatePackage_globalMerge.errors.txt
+@@= skipped -0, +0 lines =@@
+ /node_modules/@types/react/index.d.ts(1,9): error TS2669: Augmentations for the global scope can only be directly nested in external modules or ambient module declarations.
+ /src/index.ts(1,24): error TS2306: File '/node_modules/@types/react/index.d.ts' is not a module.
+-/tests/index.ts(1,24): error TS2306: File '/tests/node_modules/@types/react/index.d.ts' is not a module.
++/tests/index.ts(1,24): error TS2306: File '/node_modules/@types/react/index.d.ts' is not a module.
+
+
+ ==== /src/bug25410.ts (0 errors) ====
+@@= skipped -14, +14 lines =@@
+ ==== /tests/index.ts (1 errors) ====
+ import * as React from 'react';
+ ~~~~~~~
+-!!! error TS2306: File '/tests/node_modules/@types/react/index.d.ts' is not a module.
++!!! error TS2306: File '/node_modules/@types/react/index.d.ts' is not a module.
+ export var y = 2
+
+ ==== /tests/node_modules/@types/react/package.json (0 errors) ====
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.symbols b/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.symbols
index abc070a6090..0b3ba9f8996 100644
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.symbols
+++ b/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.symbols
@@ -23,6 +23,8 @@ export var y = 2
=== /tests/node_modules/@types/react/index.d.ts ===
+>global : Symbol(global, Decl(index.d.ts, 0, 0))
+
=== /node_modules/@types/react/index.d.ts ===
declare global { }
>global : Symbol(global, Decl(index.d.ts, 0, 0))
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.symbols.diff b/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.symbols.diff
deleted file mode 100644
index 3de2625386f..00000000000
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.symbols.diff
+++ /dev/null
@@ -1,11 +0,0 @@
---- old.duplicatePackage_globalMerge.symbols
-+++ new.duplicatePackage_globalMerge.symbols
-@@= skipped -22, +22 lines =@@
-
- === /tests/node_modules/@types/react/index.d.ts ===
-
-->global : Symbol(global, Decl(index.d.ts, 0, 0))
--
- === /node_modules/@types/react/index.d.ts ===
- declare global { }
- >global : Symbol(global, Decl(index.d.ts, 0, 0))
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.types b/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.types
index ecba91848d4..920d6906dc1 100644
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.types
+++ b/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.types
@@ -25,6 +25,8 @@ export var y = 2
=== /tests/node_modules/@types/react/index.d.ts ===
+>global : typeof global
+
=== /node_modules/@types/react/index.d.ts ===
declare global { }
>global : typeof global
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.types.diff b/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.types.diff
deleted file mode 100644
index a280e89724d..00000000000
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.types.diff
+++ /dev/null
@@ -1,11 +0,0 @@
---- old.duplicatePackage_globalMerge.types
-+++ new.duplicatePackage_globalMerge.types
-@@= skipped -24, +24 lines =@@
-
- === /tests/node_modules/@types/react/index.d.ts ===
-
-->global : typeof global
--
- === /node_modules/@types/react/index.d.ts ===
- declare global { }
- >global : typeof global
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_referenceTypes.types b/testdata/baselines/reference/submodule/compiler/duplicatePackage_referenceTypes.types
index ac9be730820..c9c06c50d9d 100644
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_referenceTypes.types
+++ b/testdata/baselines/reference/submodule/compiler/duplicatePackage_referenceTypes.types
@@ -9,9 +9,9 @@ import { Foo } from "foo";
let foo: Foo = a.foo;
>foo : Foo
->a.foo : import("/node_modules/a/node_modules/foo/index").Foo
+>a.foo : Foo
>a : typeof a
->foo : import("/node_modules/a/node_modules/foo/index").Foo
+>foo : Foo
=== /node_modules/a/index.d.ts ===
///
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_referenceTypes.types.diff b/testdata/baselines/reference/submodule/compiler/duplicatePackage_referenceTypes.types.diff
deleted file mode 100644
index 80ed49bf274..00000000000
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_referenceTypes.types.diff
+++ /dev/null
@@ -1,14 +0,0 @@
---- old.duplicatePackage_referenceTypes.types
-+++ new.duplicatePackage_referenceTypes.types
-@@= skipped -8, +8 lines =@@
-
- let foo: Foo = a.foo;
- >foo : Foo
-->a.foo : Foo
-+>a.foo : import("/node_modules/a/node_modules/foo/index").Foo
- >a : typeof a
-->foo : Foo
-+>foo : import("/node_modules/a/node_modules/foo/index").Foo
-
- === /node_modules/a/index.d.ts ===
- ///
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.types b/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.types
index 75d6b524f41..20691ab09a4 100644
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.types
+++ b/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.types
@@ -2,14 +2,14 @@
=== /index.ts ===
import { use } from "foo/use";
->use : (o: import("/node_modules/foo/index").C) => void
+>use : (o: import("/node_modules/a/node_modules/foo/index").C) => void
import { o } from "a";
>o : import("/node_modules/a/node_modules/foo/index").C
use(o);
>use(o) : void
->use : (o: import("/node_modules/foo/index").C) => void
+>use : (o: import("/node_modules/a/node_modules/foo/index").C) => void
>o : import("/node_modules/a/node_modules/foo/index").C
=== /node_modules/a/node_modules/foo/index.d.ts ===
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.types.diff b/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.types.diff
index bd4b5c7d10c..2df6ebfad59 100644
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.types.diff
+++ b/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.types.diff
@@ -1,7 +1,11 @@
--- old.duplicatePackage_relativeImportWithinPackage.types
+++ new.duplicatePackage_relativeImportWithinPackage.types
-@@= skipped -4, +4 lines =@@
- >use : (o: import("/node_modules/foo/index").C) => void
+@@= skipped -1, +1 lines =@@
+
+ === /index.ts ===
+ import { use } from "foo/use";
+->use : (o: import("/node_modules/foo/index").C) => void
++>use : (o: import("/node_modules/a/node_modules/foo/index").C) => void
import { o } from "a";
->o : import("/node_modules/foo/index").C
@@ -9,8 +13,9 @@
use(o);
>use(o) : void
- >use : (o: import("/node_modules/foo/index").C) => void
+->use : (o: import("/node_modules/foo/index").C) => void
->o : import("/node_modules/foo/index").C
++>use : (o: import("/node_modules/a/node_modules/foo/index").C) => void
+>o : import("/node_modules/a/node_modules/foo/index").C
=== /node_modules/a/node_modules/foo/index.d.ts ===
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage_scoped.types b/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage_scoped.types
index 8fa6f0c8796..927fdfa7482 100644
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage_scoped.types
+++ b/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage_scoped.types
@@ -5,12 +5,12 @@ import { use } from "@foo/bar/use";
>use : (o: import("/node_modules/@foo/bar/index").C) => void
import { o } from "a";
->o : import("/node_modules/a/node_modules/@foo/bar/index").C
+>o : import("/node_modules/@foo/bar/index").C
use(o);
>use(o) : void
>use : (o: import("/node_modules/@foo/bar/index").C) => void
->o : import("/node_modules/a/node_modules/@foo/bar/index").C
+>o : import("/node_modules/@foo/bar/index").C
=== /node_modules/a/node_modules/@foo/bar/index.d.ts ===
export class C {
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage_scoped.types.diff b/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage_scoped.types.diff
deleted file mode 100644
index 61dace3bcc9..00000000000
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage_scoped.types.diff
+++ /dev/null
@@ -1,17 +0,0 @@
---- old.duplicatePackage_relativeImportWithinPackage_scoped.types
-+++ new.duplicatePackage_relativeImportWithinPackage_scoped.types
-@@= skipped -4, +4 lines =@@
- >use : (o: import("/node_modules/@foo/bar/index").C) => void
-
- import { o } from "a";
-->o : import("/node_modules/@foo/bar/index").C
-+>o : import("/node_modules/a/node_modules/@foo/bar/index").C
-
- use(o);
- >use(o) : void
- >use : (o: import("/node_modules/@foo/bar/index").C) => void
-->o : import("/node_modules/@foo/bar/index").C
-+>o : import("/node_modules/a/node_modules/@foo/bar/index").C
-
- === /node_modules/a/node_modules/@foo/bar/index.d.ts ===
- export class C {
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_subModule.errors.txt b/testdata/baselines/reference/submodule/compiler/duplicatePackage_subModule.errors.txt
deleted file mode 100644
index a7412affa67..00000000000
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_subModule.errors.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-/index.ts(4,7): error TS2322: Type 'import("/node_modules/a/node_modules/foo/Foo").default' is not assignable to type 'import("/node_modules/foo/Foo").default'.
- Property 'source' is protected but type 'Foo' is not a class derived from 'Foo'.
-
-
-==== /index.ts (1 errors) ====
- import Foo from "foo/Foo";
- import * as a from "a";
-
- const o: Foo = a.o;
- ~
-!!! error TS2322: Type 'import("/node_modules/a/node_modules/foo/Foo").default' is not assignable to type 'import("/node_modules/foo/Foo").default'.
-!!! error TS2322: Property 'source' is protected but type 'Foo' is not a class derived from 'Foo'.
-
-==== /node_modules/a/index.d.ts (0 errors) ====
- import Foo from "foo/Foo";
- export const o: Foo;
-
-==== /node_modules/a/node_modules/foo/Foo.d.ts (0 errors) ====
- export default class Foo {
- protected source: boolean;
- }
-
-==== /node_modules/a/node_modules/foo/package.json (0 errors) ====
- { "name": "foo", "version": "1.2.3" }
-
-==== /node_modules/foo/Foo.d.ts (0 errors) ====
- export default class Foo {
- protected source: boolean;
- }
-
-==== /node_modules/foo/package.json (0 errors) ====
- { "name": "foo", "version": "1.2.3" }
-
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_subModule.errors.txt.diff b/testdata/baselines/reference/submodule/compiler/duplicatePackage_subModule.errors.txt.diff
deleted file mode 100644
index 7fb6c1ad0b5..00000000000
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_subModule.errors.txt.diff
+++ /dev/null
@@ -1,37 +0,0 @@
---- old.duplicatePackage_subModule.errors.txt
-+++ new.duplicatePackage_subModule.errors.txt
-@@= skipped -0, +0 lines =@@
--
-+/index.ts(4,7): error TS2322: Type 'import("/node_modules/a/node_modules/foo/Foo").default' is not assignable to type 'import("/node_modules/foo/Foo").default'.
-+ Property 'source' is protected but type 'Foo' is not a class derived from 'Foo'.
-+
-+
-+==== /index.ts (1 errors) ====
-+ import Foo from "foo/Foo";
-+ import * as a from "a";
-+
-+ const o: Foo = a.o;
-+ ~
-+!!! error TS2322: Type 'import("/node_modules/a/node_modules/foo/Foo").default' is not assignable to type 'import("/node_modules/foo/Foo").default'.
-+!!! error TS2322: Property 'source' is protected but type 'Foo' is not a class derived from 'Foo'.
-+
-+==== /node_modules/a/index.d.ts (0 errors) ====
-+ import Foo from "foo/Foo";
-+ export const o: Foo;
-+
-+==== /node_modules/a/node_modules/foo/Foo.d.ts (0 errors) ====
-+ export default class Foo {
-+ protected source: boolean;
-+ }
-+
-+==== /node_modules/a/node_modules/foo/package.json (0 errors) ====
-+ { "name": "foo", "version": "1.2.3" }
-+
-+==== /node_modules/foo/Foo.d.ts (0 errors) ====
-+ export default class Foo {
-+ protected source: boolean;
-+ }
-+
-+==== /node_modules/foo/package.json (0 errors) ====
-+ { "name": "foo", "version": "1.2.3" }
-+
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_subModule.types b/testdata/baselines/reference/submodule/compiler/duplicatePackage_subModule.types
index 7767de1d948..976b785722d 100644
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_subModule.types
+++ b/testdata/baselines/reference/submodule/compiler/duplicatePackage_subModule.types
@@ -9,9 +9,9 @@ import * as a from "a";
const o: Foo = a.o;
>o : Foo
->a.o : import("/node_modules/a/node_modules/foo/Foo").default
+>a.o : Foo
>a : typeof a
->o : import("/node_modules/a/node_modules/foo/Foo").default
+>o : Foo
=== /node_modules/a/index.d.ts ===
import Foo from "foo/Foo";
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_subModule.types.diff b/testdata/baselines/reference/submodule/compiler/duplicatePackage_subModule.types.diff
deleted file mode 100644
index 25738a74af6..00000000000
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_subModule.types.diff
+++ /dev/null
@@ -1,14 +0,0 @@
---- old.duplicatePackage_subModule.types
-+++ new.duplicatePackage_subModule.types
-@@= skipped -8, +8 lines =@@
-
- const o: Foo = a.o;
- >o : Foo
-->a.o : Foo
-+>a.o : import("/node_modules/a/node_modules/foo/Foo").default
- >a : typeof a
-->o : Foo
-+>o : import("/node_modules/a/node_modules/foo/Foo").default
-
- === /node_modules/a/index.d.ts ===
- import Foo from "foo/Foo";
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.errors.txt b/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.errors.txt
index 090ca51dd33..f5c457bb9f7 100644
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.errors.txt
+++ b/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.errors.txt
@@ -1,5 +1,4 @@
/node_modules/a/node_modules/x/index.d.ts(1,18): error TS1254: A 'const' initializer in an ambient context must be a string or numeric literal or literal enum reference.
-/node_modules/b/index.d.ts(1,19): error TS2306: File '/node_modules/b/node_modules/x/index.d.ts' is not a module.
/node_modules/b/node_modules/x/index.d.ts(1,1): error TS1434: Unexpected keyword or identifier.
/node_modules/b/node_modules/x/index.d.ts(1,1): error TS2304: Cannot find name 'content'.
/node_modules/b/node_modules/x/index.d.ts(1,9): error TS1434: Unexpected keyword or identifier.
@@ -22,10 +21,8 @@
==== /node_modules/a/node_modules/x/package.json (0 errors) ====
{ "name": "x", "version": "1.2.3" }
-==== /node_modules/b/index.d.ts (1 errors) ====
+==== /node_modules/b/index.d.ts (0 errors) ====
export { x } from "x";
- ~~~
-!!! error TS2306: File '/node_modules/b/node_modules/x/index.d.ts' is not a module.
==== /node_modules/b/node_modules/x/index.d.ts (5 errors) ====
content not parsed
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.errors.txt.diff b/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.errors.txt.diff
index 8885fbc2f74..30b0beb10eb 100644
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.errors.txt.diff
+++ b/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.errors.txt.diff
@@ -2,7 +2,6 @@
+++ new.duplicatePackage_withErrors.errors.txt
@@= skipped -0, +0 lines =@@
/node_modules/a/node_modules/x/index.d.ts(1,18): error TS1254: A 'const' initializer in an ambient context must be a string or numeric literal or literal enum reference.
-+/node_modules/b/index.d.ts(1,19): error TS2306: File '/node_modules/b/node_modules/x/index.d.ts' is not a module.
+/node_modules/b/node_modules/x/index.d.ts(1,1): error TS1434: Unexpected keyword or identifier.
+/node_modules/b/node_modules/x/index.d.ts(1,1): error TS2304: Cannot find name 'content'.
+/node_modules/b/node_modules/x/index.d.ts(1,9): error TS1434: Unexpected keyword or identifier.
@@ -11,15 +10,9 @@
==== /src/a.ts (0 errors) ====
-@@= skipped -15, +21 lines =@@
- ==== /node_modules/a/node_modules/x/package.json (0 errors) ====
- { "name": "x", "version": "1.2.3" }
-
--==== /node_modules/b/index.d.ts (0 errors) ====
-+==== /node_modules/b/index.d.ts (1 errors) ====
+@@= skipped -18, +23 lines =@@
+ ==== /node_modules/b/index.d.ts (0 errors) ====
export { x } from "x";
-+ ~~~
-+!!! error TS2306: File '/node_modules/b/node_modules/x/index.d.ts' is not a module.
-==== /node_modules/b/node_modules/x/index.d.ts (0 errors) ====
+==== /node_modules/b/node_modules/x/index.d.ts (5 errors) ====
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.symbols b/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.symbols
index a8df9c4b128..76a9124077b 100644
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.symbols
+++ b/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.symbols
@@ -22,6 +22,6 @@ export { x } from "x";
>x : Symbol(x, Decl(index.d.ts, 0, 8))
=== /node_modules/b/node_modules/x/index.d.ts ===
-
content not parsed
+>x : Symbol(x, Decl(index.d.ts, 0, 12))
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.symbols.diff b/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.symbols.diff
deleted file mode 100644
index 666f32f2adf..00000000000
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.symbols.diff
+++ /dev/null
@@ -1,9 +0,0 @@
---- old.duplicatePackage_withErrors.symbols
-+++ new.duplicatePackage_withErrors.symbols
-@@= skipped -21, +21 lines =@@
- >x : Symbol(x, Decl(index.d.ts, 0, 8))
-
- === /node_modules/b/node_modules/x/index.d.ts ===
-+
- content not parsed
-->x : Symbol(x, Decl(index.d.ts, 0, 12))
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.types b/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.types
index f1e33a4617b..e96c81fd634 100644
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.types
+++ b/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.types
@@ -6,8 +6,8 @@ import { x as xa } from "a";
>xa : number
import { x as xb } from "b";
->x : any
->xb : any
+>x : number
+>xb : number
=== /node_modules/a/index.d.ts ===
export { x } from "x";
@@ -22,11 +22,12 @@ export const x = 1 + 1;
=== /node_modules/b/index.d.ts ===
export { x } from "x";
->x : any
+>x : number
=== /node_modules/b/node_modules/x/index.d.ts ===
content not parsed
->content : any
->not : any
->parsed : any
+>x : number
+>1 + 1 : number
+>1 : 1
+>1 : 1
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.types.diff b/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.types.diff
deleted file mode 100644
index 6f962c0e976..00000000000
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.types.diff
+++ /dev/null
@@ -1,29 +0,0 @@
---- old.duplicatePackage_withErrors.types
-+++ new.duplicatePackage_withErrors.types
-@@= skipped -5, +5 lines =@@
- >xa : number
-
- import { x as xb } from "b";
-->x : number
-->xb : number
-+>x : any
-+>xb : any
-
- === /node_modules/a/index.d.ts ===
- export { x } from "x";
-@@= skipped -16, +16 lines =@@
-
- === /node_modules/b/index.d.ts ===
- export { x } from "x";
-->x : number
-+>x : any
-
- === /node_modules/b/node_modules/x/index.d.ts ===
- content not parsed
-->x : number
-->1 + 1 : number
-->1 : 1
-->1 : 1
-+>content : any
-+>not : any
-+>parsed : any
diff --git a/testdata/baselines/reference/submodule/fourslash/goToDefinition/duplicatePackageServices.baseline.jsonc b/testdata/baselines/reference/submodule/fourslash/goToDefinition/duplicatePackageServices.baseline.jsonc
index f2710c3963e..9e286c69af5 100644
--- a/testdata/baselines/reference/submodule/fourslash/goToDefinition/duplicatePackageServices.baseline.jsonc
+++ b/testdata/baselines/reference/submodule/fourslash/goToDefinition/duplicatePackageServices.baseline.jsonc
@@ -11,7 +11,7 @@
// === goToDefinition ===
-// === /node_modules/b/node_modules/x/index.d.ts ===
+// === /node_modules/a/node_modules/x/index.d.ts ===
// <|export default class [|X|] {
// private x: number;
// }|>
diff --git a/testdata/baselines/reference/submodule/fourslash/goToDefinition/duplicatePackageServices.baseline.jsonc.diff b/testdata/baselines/reference/submodule/fourslash/goToDefinition/duplicatePackageServices.baseline.jsonc.diff
deleted file mode 100644
index 15df7ad52ad..00000000000
--- a/testdata/baselines/reference/submodule/fourslash/goToDefinition/duplicatePackageServices.baseline.jsonc.diff
+++ /dev/null
@@ -1,11 +0,0 @@
---- old.duplicatePackageServices.baseline.jsonc
-+++ new.duplicatePackageServices.baseline.jsonc
-@@= skipped -10, +10 lines =@@
-
-
- // === goToDefinition ===
--// === /node_modules/a/node_modules/x/index.d.ts ===
-+// === /node_modules/b/node_modules/x/index.d.ts ===
- // <|export default class [|X|] {
- // private x: number;
- // }|>
\ No newline at end of file
From af6f313738ddbe83bb849729a397af02614c59d2 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Mon, 15 Dec 2025 10:51:42 -0800
Subject: [PATCH 14/29] fmt
---
internal/tsoptions/declscompiler.go | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/internal/tsoptions/declscompiler.go b/internal/tsoptions/declscompiler.go
index ecbc9f7ad59..b1a0d3c4a34 100644
--- a/internal/tsoptions/declscompiler.go
+++ b/internal/tsoptions/declscompiler.go
@@ -186,12 +186,12 @@ var commonOptionsWithBuild = []*CommandLineOption{
// Not setting affectsSemanticDiagnostics or affectsBuildInfo because we dont want all diagnostics to go away, its handled in builder
},
{
- Name: "disablePackageDeduplication",
- Kind: CommandLineOptionTypeBoolean,
- Category: diagnostics.Type_Checking,
- Description: diagnostics.Disable_deduplication_of_packages_with_the_same_name_and_version,
- DefaultValueDescription: false,
- AffectsProgramStructure: true,
+ Name: "disablePackageDeduplication",
+ Kind: CommandLineOptionTypeBoolean,
+ Category: diagnostics.Type_Checking,
+ Description: diagnostics.Disable_deduplication_of_packages_with_the_same_name_and_version,
+ DefaultValueDescription: false,
+ AffectsProgramStructure: true,
},
{
Name: "noEmit",
From 03a3d8fe152d88dfffb13de8eda5206b24686480 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Mon, 15 Dec 2025 10:57:00 -0800
Subject: [PATCH 15/29] Drop unused
---
internal/compiler/fileloader.go | 4 +---
internal/compiler/filesparser.go | 15 +++------------
2 files changed, 4 insertions(+), 15 deletions(-)
diff --git a/internal/compiler/fileloader.go b/internal/compiler/fileloader.go
index 0b69107b012..c788922d89f 100644
--- a/internal/compiler/fileloader.go
+++ b/internal/compiler/fileloader.go
@@ -51,6 +51,7 @@ type fileLoader struct {
}
type processedFiles struct {
+ finishedProcessing bool
resolver *module.Resolver
files []*ast.SourceFile
filesByPath map[tspath.Path]*ast.SourceFile
@@ -68,14 +69,11 @@ type processedFiles struct {
// if file was included using source file and its output is actually part of program
// this contains mapping from output to source file
outputFileToProjectReferenceSource map[tspath.Path]string
- // Maps a source file path to the name of the package it was imported with
- sourceFileToPackageName map[tspath.Path]string
// Key is a file path. Value is the list of files that redirect to it (same package, different install location)
redirectTargetsMap map[tspath.Path][]string
// Maps any path (canonical or redirect target) to its canonical path.
// Canonical paths map to themselves; redirect targets map to their canonical path.
deduplicatedPathMap map[tspath.Path]tspath.Path
- finishedProcessing bool
}
type jsxRuntimeImportSpecifier struct {
diff --git a/internal/compiler/filesparser.go b/internal/compiler/filesparser.go
index e7a610e1abc..4c74e0457b8 100644
--- a/internal/compiler/filesparser.go
+++ b/internal/compiler/filesparser.go
@@ -413,11 +413,10 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
// Build sourceFileToPackageName and redirectTargetsMap by scanning all resolved modules.
// This is done after loading is complete to ensure determinism regardless of load order.
// Skip this if package deduplication is disabled.
- var sourceFileToPackageName map[tspath.Path]string
var redirectTargetsMap map[tspath.Path][]string
var deduplicatedPathMap map[tspath.Path]tspath.Path
if !loader.opts.Config.CompilerOptions().DisablePackageDeduplication.IsTrue() {
- sourceFileToPackageName, redirectTargetsMap, deduplicatedPathMap = computePackageRedirects(resolvedModules, loader.toPath)
+ redirectTargetsMap, deduplicatedPathMap = computePackageRedirects(resolvedModules, loader.toPath)
// Physically replace duplicate source files with canonical ones.
// This ensures that when the checker encounters files from the same package
// installed in different locations, they're literally the same AST pointer,
@@ -447,7 +446,6 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
missingFiles: missingFiles,
includeProcessor: includeProcessor,
outputFileToProjectReferenceSource: outputFileToProjectReferenceSource,
- sourceFileToPackageName: sourceFileToPackageName,
redirectTargetsMap: redirectTargetsMap,
deduplicatedPathMap: deduplicatedPathMap,
}
@@ -460,7 +458,7 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
func computePackageRedirects(
resolvedModules map[tspath.Path]module.ModeAwareCache[*module.ResolvedModule],
toPath func(string) tspath.Path,
-) (sourceFileToPackageName map[tspath.Path]string, redirectTargetsMap map[tspath.Path][]string, deduplicatedPathMap map[tspath.Path]tspath.Path) {
+) (redirectTargetsMap map[tspath.Path][]string, deduplicatedPathMap map[tspath.Path]tspath.Path) {
// Collect all resolved files with package IDs
// packageIdKey -> list of (resolvedPath, packageName)
type fileInfo struct {
@@ -497,7 +495,6 @@ func computePackageRedirects(
// Now for each packageIdKey with multiple files, pick the canonical one (lexicographically first)
// and build the redirect map
- sourceFileToPackageName = make(map[tspath.Path]string)
redirectTargetsMap = make(map[tspath.Path][]string)
deduplicatedPathMap = make(map[tspath.Path]tspath.Path)
@@ -509,12 +506,6 @@ func computePackageRedirects(
slices.SortFunc(files, func(a, b fileInfo) int { return cmp.Compare(a.path, b.path) })
canonicalPath := files[0].path
- packageName := files[0].packageName
-
- // Record package name for all files from this package
- for _, f := range files {
- sourceFileToPackageName[f.path] = packageName
- }
// If there are multiple files, the others redirect to the canonical one
if len(files) > 1 {
@@ -528,7 +519,7 @@ func computePackageRedirects(
}
}
- return sourceFileToPackageName, redirectTargetsMap, deduplicatedPathMap
+ return redirectTargetsMap, deduplicatedPathMap
}
func (w *filesParser) addIncludeReason(includeProcessor *includeProcessor, task *parseTask, reason *FileIncludeReason) {
From 27d78aff0a31cb813a72407d7a85a55389cda062 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Mon, 15 Dec 2025 11:14:06 -0800
Subject: [PATCH 16/29] Revert "Drop unused"
This reverts commit 03a3d8fe152d88dfffb13de8eda5206b24686480.
---
internal/compiler/fileloader.go | 4 +++-
internal/compiler/filesparser.go | 15 ++++++++++++---
2 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/internal/compiler/fileloader.go b/internal/compiler/fileloader.go
index c788922d89f..0b69107b012 100644
--- a/internal/compiler/fileloader.go
+++ b/internal/compiler/fileloader.go
@@ -51,7 +51,6 @@ type fileLoader struct {
}
type processedFiles struct {
- finishedProcessing bool
resolver *module.Resolver
files []*ast.SourceFile
filesByPath map[tspath.Path]*ast.SourceFile
@@ -69,11 +68,14 @@ type processedFiles struct {
// if file was included using source file and its output is actually part of program
// this contains mapping from output to source file
outputFileToProjectReferenceSource map[tspath.Path]string
+ // Maps a source file path to the name of the package it was imported with
+ sourceFileToPackageName map[tspath.Path]string
// Key is a file path. Value is the list of files that redirect to it (same package, different install location)
redirectTargetsMap map[tspath.Path][]string
// Maps any path (canonical or redirect target) to its canonical path.
// Canonical paths map to themselves; redirect targets map to their canonical path.
deduplicatedPathMap map[tspath.Path]tspath.Path
+ finishedProcessing bool
}
type jsxRuntimeImportSpecifier struct {
diff --git a/internal/compiler/filesparser.go b/internal/compiler/filesparser.go
index 4c74e0457b8..e7a610e1abc 100644
--- a/internal/compiler/filesparser.go
+++ b/internal/compiler/filesparser.go
@@ -413,10 +413,11 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
// Build sourceFileToPackageName and redirectTargetsMap by scanning all resolved modules.
// This is done after loading is complete to ensure determinism regardless of load order.
// Skip this if package deduplication is disabled.
+ var sourceFileToPackageName map[tspath.Path]string
var redirectTargetsMap map[tspath.Path][]string
var deduplicatedPathMap map[tspath.Path]tspath.Path
if !loader.opts.Config.CompilerOptions().DisablePackageDeduplication.IsTrue() {
- redirectTargetsMap, deduplicatedPathMap = computePackageRedirects(resolvedModules, loader.toPath)
+ sourceFileToPackageName, redirectTargetsMap, deduplicatedPathMap = computePackageRedirects(resolvedModules, loader.toPath)
// Physically replace duplicate source files with canonical ones.
// This ensures that when the checker encounters files from the same package
// installed in different locations, they're literally the same AST pointer,
@@ -446,6 +447,7 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
missingFiles: missingFiles,
includeProcessor: includeProcessor,
outputFileToProjectReferenceSource: outputFileToProjectReferenceSource,
+ sourceFileToPackageName: sourceFileToPackageName,
redirectTargetsMap: redirectTargetsMap,
deduplicatedPathMap: deduplicatedPathMap,
}
@@ -458,7 +460,7 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
func computePackageRedirects(
resolvedModules map[tspath.Path]module.ModeAwareCache[*module.ResolvedModule],
toPath func(string) tspath.Path,
-) (redirectTargetsMap map[tspath.Path][]string, deduplicatedPathMap map[tspath.Path]tspath.Path) {
+) (sourceFileToPackageName map[tspath.Path]string, redirectTargetsMap map[tspath.Path][]string, deduplicatedPathMap map[tspath.Path]tspath.Path) {
// Collect all resolved files with package IDs
// packageIdKey -> list of (resolvedPath, packageName)
type fileInfo struct {
@@ -495,6 +497,7 @@ func computePackageRedirects(
// Now for each packageIdKey with multiple files, pick the canonical one (lexicographically first)
// and build the redirect map
+ sourceFileToPackageName = make(map[tspath.Path]string)
redirectTargetsMap = make(map[tspath.Path][]string)
deduplicatedPathMap = make(map[tspath.Path]tspath.Path)
@@ -506,6 +509,12 @@ func computePackageRedirects(
slices.SortFunc(files, func(a, b fileInfo) int { return cmp.Compare(a.path, b.path) })
canonicalPath := files[0].path
+ packageName := files[0].packageName
+
+ // Record package name for all files from this package
+ for _, f := range files {
+ sourceFileToPackageName[f.path] = packageName
+ }
// If there are multiple files, the others redirect to the canonical one
if len(files) > 1 {
@@ -519,7 +528,7 @@ func computePackageRedirects(
}
}
- return redirectTargetsMap, deduplicatedPathMap
+ return sourceFileToPackageName, redirectTargetsMap, deduplicatedPathMap
}
func (w *filesParser) addIncludeReason(includeProcessor *includeProcessor, task *parseTask, reason *FileIncludeReason) {
From cf0bc1894d268c1300e62262f873cfa9160904da Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Mon, 15 Dec 2025 11:36:14 -0800
Subject: [PATCH 17/29] Delete files that have been deduped away
---
internal/compiler/filesparser.go | 8 +++++
.../compiler/duplicatePackage.errors.txt | 17 +---------
.../compiler/duplicatePackage.errors.txt.diff | 31 ------------------
.../duplicatePackage_withErrors.errors.txt | 17 +---------
...uplicatePackage_withErrors.errors.txt.diff | 32 -------------------
...er-symlinked-package-with-indirect-link.js | 2 --
...gh-source-and-another-symlinked-package.js | 2 --
7 files changed, 10 insertions(+), 99 deletions(-)
delete mode 100644 testdata/baselines/reference/submodule/compiler/duplicatePackage.errors.txt.diff
delete mode 100644 testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.errors.txt.diff
diff --git a/internal/compiler/filesparser.go b/internal/compiler/filesparser.go
index e7a610e1abc..a1d5fafdd85 100644
--- a/internal/compiler/filesparser.go
+++ b/internal/compiler/filesparser.go
@@ -429,6 +429,14 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
}
}
}
+ // Also filter allFiles to remove duplicates - we only want to check canonical files.
+ // This prevents duplicate checking and avoids processing dependencies of duplicate files.
+ allFiles = slices.DeleteFunc(allFiles, func(f *ast.SourceFile) bool {
+ if canonicalPath, ok := deduplicatedPathMap[f.Path()]; ok {
+ return f.Path() != canonicalPath
+ }
+ return false
+ })
}
return processedFiles{
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage.errors.txt b/testdata/baselines/reference/submodule/compiler/duplicatePackage.errors.txt
index 7402b0d3e6a..99d5a3f29cc 100644
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage.errors.txt
+++ b/testdata/baselines/reference/submodule/compiler/duplicatePackage.errors.txt
@@ -1,8 +1,3 @@
-/node_modules/b/node_modules/x/index.d.ts(1,1): error TS1434: Unexpected keyword or identifier.
-/node_modules/b/node_modules/x/index.d.ts(1,1): error TS2304: Cannot find name 'content'.
-/node_modules/b/node_modules/x/index.d.ts(1,9): error TS1434: Unexpected keyword or identifier.
-/node_modules/b/node_modules/x/index.d.ts(1,9): error TS2304: Cannot find name 'not'.
-/node_modules/b/node_modules/x/index.d.ts(1,13): error TS2304: Cannot find name 'parsed'.
/src/a.ts(5,3): error TS2345: Argument of type 'import("/node_modules/c/node_modules/x/index").default' is not assignable to parameter of type 'import("/node_modules/a/node_modules/x/index").default'.
Types have separate declarations of a private property 'x'.
@@ -33,18 +28,8 @@
import X from "x";
export const b: X;
-==== /node_modules/b/node_modules/x/index.d.ts (5 errors) ====
+==== /node_modules/b/node_modules/x/index.d.ts (0 errors) ====
content not parsed
- ~~~~~~~
-!!! error TS1434: Unexpected keyword or identifier.
- ~~~~~~~
-!!! error TS2304: Cannot find name 'content'.
- ~~~
-!!! error TS1434: Unexpected keyword or identifier.
- ~~~
-!!! error TS2304: Cannot find name 'not'.
- ~~~~~~
-!!! error TS2304: Cannot find name 'parsed'.
==== /node_modules/b/node_modules/x/package.json (0 errors) ====
{ "name": "x", "version": "1.2.3" }
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage.errors.txt.diff b/testdata/baselines/reference/submodule/compiler/duplicatePackage.errors.txt.diff
deleted file mode 100644
index 84aba2edf1c..00000000000
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage.errors.txt.diff
+++ /dev/null
@@ -1,31 +0,0 @@
---- old.duplicatePackage.errors.txt
-+++ new.duplicatePackage.errors.txt
-@@= skipped -0, +0 lines =@@
-+/node_modules/b/node_modules/x/index.d.ts(1,1): error TS1434: Unexpected keyword or identifier.
-+/node_modules/b/node_modules/x/index.d.ts(1,1): error TS2304: Cannot find name 'content'.
-+/node_modules/b/node_modules/x/index.d.ts(1,9): error TS1434: Unexpected keyword or identifier.
-+/node_modules/b/node_modules/x/index.d.ts(1,9): error TS2304: Cannot find name 'not'.
-+/node_modules/b/node_modules/x/index.d.ts(1,13): error TS2304: Cannot find name 'parsed'.
- /src/a.ts(5,3): error TS2345: Argument of type 'import("/node_modules/c/node_modules/x/index").default' is not assignable to parameter of type 'import("/node_modules/a/node_modules/x/index").default'.
- Types have separate declarations of a private property 'x'.
-
-@@= skipped -27, +32 lines =@@
- import X from "x";
- export const b: X;
-
--==== /node_modules/b/node_modules/x/index.d.ts (0 errors) ====
-+==== /node_modules/b/node_modules/x/index.d.ts (5 errors) ====
- content not parsed
-+ ~~~~~~~
-+!!! error TS1434: Unexpected keyword or identifier.
-+ ~~~~~~~
-+!!! error TS2304: Cannot find name 'content'.
-+ ~~~
-+!!! error TS1434: Unexpected keyword or identifier.
-+ ~~~
-+!!! error TS2304: Cannot find name 'not'.
-+ ~~~~~~
-+!!! error TS2304: Cannot find name 'parsed'.
-
- ==== /node_modules/b/node_modules/x/package.json (0 errors) ====
- { "name": "x", "version": "1.2.3" }
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.errors.txt b/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.errors.txt
index f5c457bb9f7..89cc598606f 100644
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.errors.txt
+++ b/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.errors.txt
@@ -1,9 +1,4 @@
/node_modules/a/node_modules/x/index.d.ts(1,18): error TS1254: A 'const' initializer in an ambient context must be a string or numeric literal or literal enum reference.
-/node_modules/b/node_modules/x/index.d.ts(1,1): error TS1434: Unexpected keyword or identifier.
-/node_modules/b/node_modules/x/index.d.ts(1,1): error TS2304: Cannot find name 'content'.
-/node_modules/b/node_modules/x/index.d.ts(1,9): error TS1434: Unexpected keyword or identifier.
-/node_modules/b/node_modules/x/index.d.ts(1,9): error TS2304: Cannot find name 'not'.
-/node_modules/b/node_modules/x/index.d.ts(1,13): error TS2304: Cannot find name 'parsed'.
==== /src/a.ts (0 errors) ====
@@ -24,18 +19,8 @@
==== /node_modules/b/index.d.ts (0 errors) ====
export { x } from "x";
-==== /node_modules/b/node_modules/x/index.d.ts (5 errors) ====
+==== /node_modules/b/node_modules/x/index.d.ts (0 errors) ====
content not parsed
- ~~~~~~~
-!!! error TS1434: Unexpected keyword or identifier.
- ~~~~~~~
-!!! error TS2304: Cannot find name 'content'.
- ~~~
-!!! error TS1434: Unexpected keyword or identifier.
- ~~~
-!!! error TS2304: Cannot find name 'not'.
- ~~~~~~
-!!! error TS2304: Cannot find name 'parsed'.
==== /node_modules/b/node_modules/x/package.json (0 errors) ====
{ "name": "x", "version": "1.2.3" }
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.errors.txt.diff b/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.errors.txt.diff
deleted file mode 100644
index 30b0beb10eb..00000000000
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_withErrors.errors.txt.diff
+++ /dev/null
@@ -1,32 +0,0 @@
---- old.duplicatePackage_withErrors.errors.txt
-+++ new.duplicatePackage_withErrors.errors.txt
-@@= skipped -0, +0 lines =@@
- /node_modules/a/node_modules/x/index.d.ts(1,18): error TS1254: A 'const' initializer in an ambient context must be a string or numeric literal or literal enum reference.
-+/node_modules/b/node_modules/x/index.d.ts(1,1): error TS1434: Unexpected keyword or identifier.
-+/node_modules/b/node_modules/x/index.d.ts(1,1): error TS2304: Cannot find name 'content'.
-+/node_modules/b/node_modules/x/index.d.ts(1,9): error TS1434: Unexpected keyword or identifier.
-+/node_modules/b/node_modules/x/index.d.ts(1,9): error TS2304: Cannot find name 'not'.
-+/node_modules/b/node_modules/x/index.d.ts(1,13): error TS2304: Cannot find name 'parsed'.
-
-
- ==== /src/a.ts (0 errors) ====
-@@= skipped -18, +23 lines =@@
- ==== /node_modules/b/index.d.ts (0 errors) ====
- export { x } from "x";
-
--==== /node_modules/b/node_modules/x/index.d.ts (0 errors) ====
-+==== /node_modules/b/node_modules/x/index.d.ts (5 errors) ====
- content not parsed
-+ ~~~~~~~
-+!!! error TS1434: Unexpected keyword or identifier.
-+ ~~~~~~~
-+!!! error TS2304: Cannot find name 'content'.
-+ ~~~
-+!!! error TS1434: Unexpected keyword or identifier.
-+ ~~~
-+!!! error TS2304: Cannot find name 'not'.
-+ ~~~~~~
-+!!! error TS2304: Cannot find name 'parsed'.
-
- ==== /node_modules/b/node_modules/x/package.json (0 errors) ====
- { "name": "x", "version": "1.2.3" }
\ No newline at end of file
diff --git a/testdata/baselines/reference/tsc/declarationEmit/when-same-version-is-referenced-through-source-and-another-symlinked-package-with-indirect-link.js b/testdata/baselines/reference/tsc/declarationEmit/when-same-version-is-referenced-through-source-and-another-symlinked-package-with-indirect-link.js
index cef83d3a2d3..70e094d00d3 100644
--- a/testdata/baselines/reference/tsc/declarationEmit/when-same-version-is-referenced-through-source-and-another-symlinked-package-with-indirect-link.js
+++ b/testdata/baselines/reference/tsc/declarationEmit/when-same-version-is-referenced-through-source-and-another-symlinked-package-with-indirect-link.js
@@ -166,8 +166,6 @@ Resolving real path for '/user/username/projects/myproject/plugin-two/node_modul
======== Module name 'typescript-fsa' was successfully resolved to '/user/username/projects/myproject/plugin-two/node_modules/typescript-fsa/index.d.ts' with Package ID 'typescript-fsa/index.d.ts@3.0.0-beta-2'. ========
../../../../home/src/tslibs/TS/Lib/lib.d.ts
Default library for target 'ES5'
-plugin-two/node_modules/typescript-fsa/index.d.ts
- Imported via "typescript-fsa" from file 'plugin-two/dist/commonjs/index.d.ts' with packageId 'typescript-fsa/index.d.ts@3.0.0-beta-2'
plugin-two/dist/commonjs/index.d.ts
Imported via "plugin-two" from file 'plugin-one/index.ts' with packageId 'plugin-two/dist/commonjs/index.d.ts@0.1.3'
plugin-one/node_modules/typescript-fsa/index.d.ts
diff --git a/testdata/baselines/reference/tsc/declarationEmit/when-same-version-is-referenced-through-source-and-another-symlinked-package.js b/testdata/baselines/reference/tsc/declarationEmit/when-same-version-is-referenced-through-source-and-another-symlinked-package.js
index 983c7cf3912..fc9f6e3c5d8 100644
--- a/testdata/baselines/reference/tsc/declarationEmit/when-same-version-is-referenced-through-source-and-another-symlinked-package.js
+++ b/testdata/baselines/reference/tsc/declarationEmit/when-same-version-is-referenced-through-source-and-another-symlinked-package.js
@@ -157,8 +157,6 @@ plugin-one/node_modules/typescript-fsa/index.d.ts
Imported via "typescript-fsa" from file 'plugin-one/action.ts' with packageId 'typescript-fsa/index.d.ts@3.0.0-beta-2'
plugin-one/action.ts
Matched by default include pattern '**/*'
-plugin-two/node_modules/typescript-fsa/index.d.ts
- Imported via "typescript-fsa" from file 'plugin-two/index.d.ts' with packageId 'typescript-fsa/index.d.ts@3.0.0-beta-2'
plugin-two/index.d.ts
Imported via "plugin-two" from file 'plugin-one/index.ts'
plugin-one/index.ts
From fb1e84f28ff339fab2368d7039d146b7255096ca Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Mon, 15 Dec 2025 12:57:22 -0800
Subject: [PATCH 18/29] Do this during collectFiles
---
internal/compiler/filesparser.go | 136 ++++++------------
...ePackage_relativeImportWithinPackage.types | 8 +-
...age_relativeImportWithinPackage.types.diff | 22 ---
3 files changed, 46 insertions(+), 120 deletions(-)
delete mode 100644 testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.types.diff
diff --git a/internal/compiler/filesparser.go b/internal/compiler/filesparser.go
index a1d5fafdd85..ab6a98ee5c5 100644
--- a/internal/compiler/filesparser.go
+++ b/internal/compiler/filesparser.go
@@ -1,8 +1,6 @@
package compiler
import (
- "cmp"
- "maps"
"math"
"slices"
"sync"
@@ -285,6 +283,17 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
var sourceFilesFoundSearchingNodeModules collections.Set[tspath.Path]
libFilesMap := make(map[tspath.Path]*LibFile, libFileCount)
+ var sourceFileToPackageName map[tspath.Path]string
+ var redirectTargetsMap map[tspath.Path][]string
+ var deduplicatedPathMap map[tspath.Path]tspath.Path
+ var packageIdToCanonicalPath map[module.PackageId]tspath.Path
+ if !loader.opts.Config.CompilerOptions().DisablePackageDeduplication.IsTrue() {
+ sourceFileToPackageName = make(map[tspath.Path]string, totalFileCount)
+ redirectTargetsMap = make(map[tspath.Path][]string)
+ deduplicatedPathMap = make(map[tspath.Path]tspath.Path)
+ packageIdToCanonicalPath = make(map[module.PackageId]tspath.Path)
+ }
+
var collectFiles func(tasks []*parseTask, seen map[*parseTaskData]string)
collectFiles = func(tasks []*parseTask, seen map[*parseTaskData]string) {
for _, task := range tasks {
@@ -333,6 +342,36 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
for _, trace := range task.resolutionsTrace {
loader.opts.Host.Trace(trace.Message, trace.Args...)
}
+
+ if packageIdToCanonicalPath != nil {
+ for _, resolution := range task.resolutionsInFile {
+ if !resolution.IsResolved() {
+ continue
+ }
+ pkgId := resolution.PackageId
+ if pkgId.Name == "" {
+ continue
+ }
+ resolvedPath := loader.toPath(resolution.ResolvedFileName)
+ packageName := pkgId.PackageName()
+
+ if canonical, exists := packageIdToCanonicalPath[pkgId]; exists {
+ if _, alreadyRecorded := sourceFileToPackageName[resolvedPath]; !alreadyRecorded {
+ sourceFileToPackageName[resolvedPath] = packageName
+ if resolvedPath != canonical {
+
+ deduplicatedPathMap[resolvedPath] = canonical
+ redirectTargetsMap[canonical] = append(redirectTargetsMap[canonical], resolution.ResolvedFileName)
+ }
+ }
+ } else {
+ packageIdToCanonicalPath[pkgId] = resolvedPath
+ sourceFileToPackageName[resolvedPath] = packageName
+ deduplicatedPathMap[resolvedPath] = resolvedPath
+ }
+ }
+ }
+
if subTasks := task.subTasks; len(subTasks) > 0 {
collectFiles(subTasks, seen)
}
@@ -410,18 +449,7 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
}
}
- // Build sourceFileToPackageName and redirectTargetsMap by scanning all resolved modules.
- // This is done after loading is complete to ensure determinism regardless of load order.
- // Skip this if package deduplication is disabled.
- var sourceFileToPackageName map[tspath.Path]string
- var redirectTargetsMap map[tspath.Path][]string
- var deduplicatedPathMap map[tspath.Path]tspath.Path
- if !loader.opts.Config.CompilerOptions().DisablePackageDeduplication.IsTrue() {
- sourceFileToPackageName, redirectTargetsMap, deduplicatedPathMap = computePackageRedirects(resolvedModules, loader.toPath)
- // Physically replace duplicate source files with canonical ones.
- // This ensures that when the checker encounters files from the same package
- // installed in different locations, they're literally the same AST pointer,
- // so symbol identity comparisons work correctly.
+ if deduplicatedPathMap != nil {
for duplicatePath, canonicalPath := range deduplicatedPathMap {
if duplicatePath != canonicalPath {
if canonicalFile, ok := filesByPath[canonicalPath]; ok {
@@ -429,8 +457,6 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
}
}
}
- // Also filter allFiles to remove duplicates - we only want to check canonical files.
- // This prevents duplicate checking and avoids processing dependencies of duplicate files.
allFiles = slices.DeleteFunc(allFiles, func(f *ast.SourceFile) bool {
if canonicalPath, ok := deduplicatedPathMap[f.Path()]; ok {
return f.Path() != canonicalPath
@@ -461,84 +487,6 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
}
}
-// computePackageRedirects builds the sourceFileToPackageName and redirectTargetsMap by scanning
-// all resolved modules. Files from the same package (same name@version) are deduplicated:
-// the lexicographically first path becomes the "canonical" one and others redirect to it.
-// This is done after loading completes to ensure determinism regardless of concurrent load order.
-func computePackageRedirects(
- resolvedModules map[tspath.Path]module.ModeAwareCache[*module.ResolvedModule],
- toPath func(string) tspath.Path,
-) (sourceFileToPackageName map[tspath.Path]string, redirectTargetsMap map[tspath.Path][]string, deduplicatedPathMap map[tspath.Path]tspath.Path) {
- // Collect all resolved files with package IDs
- // packageIdKey -> list of (resolvedPath, packageName)
- type fileInfo struct {
- path tspath.Path
- fileName string
- packageName string
- }
- packageIdToFiles := make(map[module.PackageId][]fileInfo)
-
- containingFilePaths := slices.AppendSeq(make([]tspath.Path, 0, len(resolvedModules)), maps.Keys(resolvedModules))
- slices.Sort(containingFilePaths)
-
- for _, containingPath := range containingFilePaths {
- resolutions := resolvedModules[containingPath]
- for _, resolution := range resolutions {
- if !resolution.IsResolved() {
- continue
- }
- pkgId := resolution.PackageId
- if pkgId.Name == "" {
- continue
- }
- resolvedFileName := resolution.ResolvedFileName
- resolvedPath := toPath(resolvedFileName)
- packageName := pkgId.PackageName()
-
- // Check if we've already recorded this path for this package
- files := packageIdToFiles[pkgId]
- if !slices.ContainsFunc(files, func(f fileInfo) bool { return f.path == resolvedPath }) {
- packageIdToFiles[pkgId] = append(files, fileInfo{path: resolvedPath, fileName: resolvedFileName, packageName: packageName})
- }
- }
- }
-
- // Now for each packageIdKey with multiple files, pick the canonical one (lexicographically first)
- // and build the redirect map
- sourceFileToPackageName = make(map[tspath.Path]string)
- redirectTargetsMap = make(map[tspath.Path][]string)
- deduplicatedPathMap = make(map[tspath.Path]tspath.Path)
-
- for _, files := range packageIdToFiles {
- if len(files) == 0 {
- continue
- }
-
- slices.SortFunc(files, func(a, b fileInfo) int { return cmp.Compare(a.path, b.path) })
-
- canonicalPath := files[0].path
- packageName := files[0].packageName
-
- // Record package name for all files from this package
- for _, f := range files {
- sourceFileToPackageName[f.path] = packageName
- }
-
- // If there are multiple files, the others redirect to the canonical one
- if len(files) > 1 {
- // Canonical path maps to itself
- deduplicatedPathMap[canonicalPath] = canonicalPath
- for _, f := range files[1:] {
- redirectTargetsMap[canonicalPath] = append(redirectTargetsMap[canonicalPath], f.fileName)
- // Redirect target maps to canonical
- deduplicatedPathMap[f.path] = canonicalPath
- }
- }
- }
-
- return sourceFileToPackageName, redirectTargetsMap, deduplicatedPathMap
-}
-
func (w *filesParser) addIncludeReason(includeProcessor *includeProcessor, task *parseTask, reason *FileIncludeReason) {
if task.redirectedParseTask != nil {
w.addIncludeReason(includeProcessor, task.redirectedParseTask, reason)
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.types b/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.types
index 20691ab09a4..0993442b2de 100644
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.types
+++ b/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.types
@@ -2,15 +2,15 @@
=== /index.ts ===
import { use } from "foo/use";
->use : (o: import("/node_modules/a/node_modules/foo/index").C) => void
+>use : (o: import("/node_modules/foo/index").C) => void
import { o } from "a";
->o : import("/node_modules/a/node_modules/foo/index").C
+>o : import("/node_modules/foo/index").C
use(o);
>use(o) : void
->use : (o: import("/node_modules/a/node_modules/foo/index").C) => void
->o : import("/node_modules/a/node_modules/foo/index").C
+>use : (o: import("/node_modules/foo/index").C) => void
+>o : import("/node_modules/foo/index").C
=== /node_modules/a/node_modules/foo/index.d.ts ===
export class C {
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.types.diff b/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.types.diff
deleted file mode 100644
index 2df6ebfad59..00000000000
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_relativeImportWithinPackage.types.diff
+++ /dev/null
@@ -1,22 +0,0 @@
---- old.duplicatePackage_relativeImportWithinPackage.types
-+++ new.duplicatePackage_relativeImportWithinPackage.types
-@@= skipped -1, +1 lines =@@
-
- === /index.ts ===
- import { use } from "foo/use";
-->use : (o: import("/node_modules/foo/index").C) => void
-+>use : (o: import("/node_modules/a/node_modules/foo/index").C) => void
-
- import { o } from "a";
-->o : import("/node_modules/foo/index").C
-+>o : import("/node_modules/a/node_modules/foo/index").C
-
- use(o);
- >use(o) : void
-->use : (o: import("/node_modules/foo/index").C) => void
-->o : import("/node_modules/foo/index").C
-+>use : (o: import("/node_modules/a/node_modules/foo/index").C) => void
-+>o : import("/node_modules/a/node_modules/foo/index").C
-
- === /node_modules/a/node_modules/foo/index.d.ts ===
- export class C {
\ No newline at end of file
From 34155c945d471284dc4da4da2a6edb9a659e46a8 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Tue, 16 Dec 2025 19:19:31 -0800
Subject: [PATCH 19/29] Remove _
---
internal/compiler/program.go | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/internal/compiler/program.go b/internal/compiler/program.go
index 09c0f007c81..4b8f55004fc 100644
--- a/internal/compiler/program.go
+++ b/internal/compiler/program.go
@@ -246,9 +246,8 @@ func (p *Program) UpdateProgram(changedFilePath tspath.Path, newHost CompilerHos
// node_modules locations), we need to rebuild the program because the redirect targets
// might need recalculation. A file is in a redirect group if it's either a canonical
// file that others redirect to, or if it redirects to another file.
- if canonicalPath, ok := p.deduplicatedPathMap[changedFilePath]; ok {
+ if _, ok := p.deduplicatedPathMap[changedFilePath]; ok {
// File is either a canonical file or a redirect target; either way, need full rebuild
- _ = canonicalPath
return NewProgram(newOpts), false
}
// TODO: reverify compiler options when config has changed?
From 68627e3a55688c40a6ec828509e978a0f48903fc Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Tue, 16 Dec 2025 19:39:39 -0800
Subject: [PATCH 20/29] Add test
---
...ablepackagededuplication=false).errors.txt | 48 +++++++++++++
...tion(disablepackagededuplication=false).js | 52 ++++++++++++++
...disablepackagededuplication=false).symbols | 67 +++++++++++++++++++
...n(disablepackagededuplication=false).types | 66 ++++++++++++++++++
...sablepackagededuplication=true).errors.txt | 66 ++++++++++++++++++
...ation(disablepackagededuplication=true).js | 52 ++++++++++++++
...(disablepackagededuplication=true).symbols | 65 ++++++++++++++++++
...on(disablepackagededuplication=true).types | 66 ++++++++++++++++++
.../compiler/disablePackageDeduplication.ts | 43 ++++++++++++
9 files changed, 525 insertions(+)
create mode 100644 testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).errors.txt
create mode 100644 testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).js
create mode 100644 testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).symbols
create mode 100644 testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).types
create mode 100644 testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).errors.txt
create mode 100644 testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).js
create mode 100644 testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).symbols
create mode 100644 testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).types
create mode 100644 testdata/tests/cases/compiler/disablePackageDeduplication.ts
diff --git a/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).errors.txt b/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).errors.txt
new file mode 100644
index 00000000000..99d5a3f29cc
--- /dev/null
+++ b/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).errors.txt
@@ -0,0 +1,48 @@
+/src/a.ts(5,3): error TS2345: Argument of type 'import("/node_modules/c/node_modules/x/index").default' is not assignable to parameter of type 'import("/node_modules/a/node_modules/x/index").default'.
+ Types have separate declarations of a private property 'x'.
+
+
+==== /src/a.ts (1 errors) ====
+ import { a } from "a";
+ import { b } from "b";
+ import { c } from "c";
+ a(b); // Works
+ a(c); // Error, these are from different versions of the library.
+ ~
+!!! error TS2345: Argument of type 'import("/node_modules/c/node_modules/x/index").default' is not assignable to parameter of type 'import("/node_modules/a/node_modules/x/index").default'.
+!!! error TS2345: Types have separate declarations of a private property 'x'.
+
+==== /node_modules/a/index.d.ts (0 errors) ====
+ import X from "x";
+ export function a(x: X): void;
+
+==== /node_modules/a/node_modules/x/index.d.ts (0 errors) ====
+ export default class X {
+ private x: number;
+ }
+
+==== /node_modules/a/node_modules/x/package.json (0 errors) ====
+ { "name": "x", "version": "1.2.3" }
+
+==== /node_modules/b/index.d.ts (0 errors) ====
+ import X from "x";
+ export const b: X;
+
+==== /node_modules/b/node_modules/x/index.d.ts (0 errors) ====
+ content not parsed
+
+==== /node_modules/b/node_modules/x/package.json (0 errors) ====
+ { "name": "x", "version": "1.2.3" }
+
+==== /node_modules/c/index.d.ts (0 errors) ====
+ import X from "x";
+ export const c: X;
+
+==== /node_modules/c/node_modules/x/index.d.ts (0 errors) ====
+ export default class X {
+ private x: number;
+ }
+
+==== /node_modules/c/node_modules/x/package.json (0 errors) ====
+ { "name": "x", "version": "1.2.4" }
+
\ No newline at end of file
diff --git a/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).js b/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).js
new file mode 100644
index 00000000000..f56347541c5
--- /dev/null
+++ b/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).js
@@ -0,0 +1,52 @@
+//// [tests/cases/compiler/disablePackageDeduplication.ts] ////
+
+//// [index.d.ts]
+import X from "x";
+export function a(x: X): void;
+
+//// [index.d.ts]
+export default class X {
+ private x: number;
+}
+
+//// [package.json]
+{ "name": "x", "version": "1.2.3" }
+
+//// [index.d.ts]
+import X from "x";
+export const b: X;
+
+//// [index.d.ts]
+content not parsed
+
+//// [package.json]
+{ "name": "x", "version": "1.2.3" }
+
+//// [index.d.ts]
+import X from "x";
+export const c: X;
+
+//// [index.d.ts]
+export default class X {
+ private x: number;
+}
+
+//// [package.json]
+{ "name": "x", "version": "1.2.4" }
+
+//// [a.ts]
+import { a } from "a";
+import { b } from "b";
+import { c } from "c";
+a(b); // Works
+a(c); // Error, these are from different versions of the library.
+
+
+//// [a.js]
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const a_1 = require("a");
+const b_1 = require("b");
+const c_1 = require("c");
+(0, a_1.a)(b_1.b); // Works
+(0, a_1.a)(c_1.c); // Error, these are from different versions of the library.
diff --git a/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).symbols b/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).symbols
new file mode 100644
index 00000000000..e75f610301f
--- /dev/null
+++ b/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).symbols
@@ -0,0 +1,67 @@
+//// [tests/cases/compiler/disablePackageDeduplication.ts] ////
+
+=== /src/a.ts ===
+import { a } from "a";
+>a : Symbol(a, Decl(a.ts, 0, 8))
+
+import { b } from "b";
+>b : Symbol(b, Decl(a.ts, 1, 8))
+
+import { c } from "c";
+>c : Symbol(c, Decl(a.ts, 2, 8))
+
+a(b); // Works
+>a : Symbol(a, Decl(a.ts, 0, 8))
+>b : Symbol(b, Decl(a.ts, 1, 8))
+
+a(c); // Error, these are from different versions of the library.
+>a : Symbol(a, Decl(a.ts, 0, 8))
+>c : Symbol(c, Decl(a.ts, 2, 8))
+
+=== /node_modules/a/index.d.ts ===
+import X from "x";
+>X : Symbol(X, Decl(index.d.ts, 0, 6))
+
+export function a(x: X): void;
+>a : Symbol(a, Decl(index.d.ts, 0, 18))
+>x : Symbol(x, Decl(index.d.ts, 1, 18))
+>X : Symbol(X, Decl(index.d.ts, 0, 6))
+
+=== /node_modules/a/node_modules/x/index.d.ts ===
+export default class X {
+>X : Symbol(X, Decl(index.d.ts, 0, 0))
+
+ private x: number;
+>x : Symbol(X.x, Decl(index.d.ts, 0, 24))
+}
+
+=== /node_modules/b/index.d.ts ===
+import X from "x";
+>X : Symbol(X, Decl(index.d.ts, 0, 6))
+
+export const b: X;
+>b : Symbol(b, Decl(index.d.ts, 1, 12))
+>X : Symbol(X, Decl(index.d.ts, 0, 6))
+
+=== /node_modules/b/node_modules/x/index.d.ts ===
+content not parsed
+>X : Symbol(X, Decl(index.d.ts, 0, 0))
+
+>x : Symbol(X.x, Decl(index.d.ts, 0, 24))
+
+=== /node_modules/c/index.d.ts ===
+import X from "x";
+>X : Symbol(X, Decl(index.d.ts, 0, 6))
+
+export const c: X;
+>c : Symbol(c, Decl(index.d.ts, 1, 12))
+>X : Symbol(X, Decl(index.d.ts, 0, 6))
+
+=== /node_modules/c/node_modules/x/index.d.ts ===
+export default class X {
+>X : Symbol(X, Decl(index.d.ts, 0, 0))
+
+ private x: number;
+>x : Symbol(X.x, Decl(index.d.ts, 0, 24))
+}
+
diff --git a/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).types b/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).types
new file mode 100644
index 00000000000..46b6a2e4e5c
--- /dev/null
+++ b/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).types
@@ -0,0 +1,66 @@
+//// [tests/cases/compiler/disablePackageDeduplication.ts] ////
+
+=== /src/a.ts ===
+import { a } from "a";
+>a : (x: import("/node_modules/a/node_modules/x/index").default) => void
+
+import { b } from "b";
+>b : import("/node_modules/a/node_modules/x/index").default
+
+import { c } from "c";
+>c : import("/node_modules/c/node_modules/x/index").default
+
+a(b); // Works
+>a(b) : void
+>a : (x: import("/node_modules/a/node_modules/x/index").default) => void
+>b : import("/node_modules/a/node_modules/x/index").default
+
+a(c); // Error, these are from different versions of the library.
+>a(c) : void
+>a : (x: import("/node_modules/a/node_modules/x/index").default) => void
+>c : import("/node_modules/c/node_modules/x/index").default
+
+=== /node_modules/a/index.d.ts ===
+import X from "x";
+>X : typeof X
+
+export function a(x: X): void;
+>a : (x: X) => void
+>x : X
+
+=== /node_modules/a/node_modules/x/index.d.ts ===
+export default class X {
+>X : X
+
+ private x: number;
+>x : number
+}
+
+=== /node_modules/b/index.d.ts ===
+import X from "x";
+>X : typeof X
+
+export const b: X;
+>b : X
+
+=== /node_modules/b/node_modules/x/index.d.ts ===
+content not parsed
+>X : X
+
+>x : number
+
+=== /node_modules/c/index.d.ts ===
+import X from "x";
+>X : typeof X
+
+export const c: X;
+>c : X
+
+=== /node_modules/c/node_modules/x/index.d.ts ===
+export default class X {
+>X : X
+
+ private x: number;
+>x : number
+}
+
diff --git a/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).errors.txt b/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).errors.txt
new file mode 100644
index 00000000000..4e9b3720b6a
--- /dev/null
+++ b/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).errors.txt
@@ -0,0 +1,66 @@
+/node_modules/b/index.d.ts(1,15): error TS2306: File '/node_modules/b/node_modules/x/index.d.ts' is not a module.
+/node_modules/b/node_modules/x/index.d.ts(1,1): error TS1434: Unexpected keyword or identifier.
+/node_modules/b/node_modules/x/index.d.ts(1,1): error TS2304: Cannot find name 'content'.
+/node_modules/b/node_modules/x/index.d.ts(1,9): error TS1434: Unexpected keyword or identifier.
+/node_modules/b/node_modules/x/index.d.ts(1,9): error TS2304: Cannot find name 'not'.
+/node_modules/b/node_modules/x/index.d.ts(1,13): error TS2304: Cannot find name 'parsed'.
+/src/a.ts(5,3): error TS2345: Argument of type 'import("/node_modules/c/node_modules/x/index").default' is not assignable to parameter of type 'import("/node_modules/a/node_modules/x/index").default'.
+ Types have separate declarations of a private property 'x'.
+
+
+==== /src/a.ts (1 errors) ====
+ import { a } from "a";
+ import { b } from "b";
+ import { c } from "c";
+ a(b); // Works
+ a(c); // Error, these are from different versions of the library.
+ ~
+!!! error TS2345: Argument of type 'import("/node_modules/c/node_modules/x/index").default' is not assignable to parameter of type 'import("/node_modules/a/node_modules/x/index").default'.
+!!! error TS2345: Types have separate declarations of a private property 'x'.
+
+==== /node_modules/a/index.d.ts (0 errors) ====
+ import X from "x";
+ export function a(x: X): void;
+
+==== /node_modules/a/node_modules/x/index.d.ts (0 errors) ====
+ export default class X {
+ private x: number;
+ }
+
+==== /node_modules/a/node_modules/x/package.json (0 errors) ====
+ { "name": "x", "version": "1.2.3" }
+
+==== /node_modules/b/index.d.ts (1 errors) ====
+ import X from "x";
+ ~~~
+!!! error TS2306: File '/node_modules/b/node_modules/x/index.d.ts' is not a module.
+ export const b: X;
+
+==== /node_modules/b/node_modules/x/index.d.ts (5 errors) ====
+ content not parsed
+ ~~~~~~~
+!!! error TS1434: Unexpected keyword or identifier.
+ ~~~~~~~
+!!! error TS2304: Cannot find name 'content'.
+ ~~~
+!!! error TS1434: Unexpected keyword or identifier.
+ ~~~
+!!! error TS2304: Cannot find name 'not'.
+ ~~~~~~
+!!! error TS2304: Cannot find name 'parsed'.
+
+==== /node_modules/b/node_modules/x/package.json (0 errors) ====
+ { "name": "x", "version": "1.2.3" }
+
+==== /node_modules/c/index.d.ts (0 errors) ====
+ import X from "x";
+ export const c: X;
+
+==== /node_modules/c/node_modules/x/index.d.ts (0 errors) ====
+ export default class X {
+ private x: number;
+ }
+
+==== /node_modules/c/node_modules/x/package.json (0 errors) ====
+ { "name": "x", "version": "1.2.4" }
+
\ No newline at end of file
diff --git a/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).js b/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).js
new file mode 100644
index 00000000000..f56347541c5
--- /dev/null
+++ b/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).js
@@ -0,0 +1,52 @@
+//// [tests/cases/compiler/disablePackageDeduplication.ts] ////
+
+//// [index.d.ts]
+import X from "x";
+export function a(x: X): void;
+
+//// [index.d.ts]
+export default class X {
+ private x: number;
+}
+
+//// [package.json]
+{ "name": "x", "version": "1.2.3" }
+
+//// [index.d.ts]
+import X from "x";
+export const b: X;
+
+//// [index.d.ts]
+content not parsed
+
+//// [package.json]
+{ "name": "x", "version": "1.2.3" }
+
+//// [index.d.ts]
+import X from "x";
+export const c: X;
+
+//// [index.d.ts]
+export default class X {
+ private x: number;
+}
+
+//// [package.json]
+{ "name": "x", "version": "1.2.4" }
+
+//// [a.ts]
+import { a } from "a";
+import { b } from "b";
+import { c } from "c";
+a(b); // Works
+a(c); // Error, these are from different versions of the library.
+
+
+//// [a.js]
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const a_1 = require("a");
+const b_1 = require("b");
+const c_1 = require("c");
+(0, a_1.a)(b_1.b); // Works
+(0, a_1.a)(c_1.c); // Error, these are from different versions of the library.
diff --git a/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).symbols b/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).symbols
new file mode 100644
index 00000000000..2450a099f00
--- /dev/null
+++ b/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).symbols
@@ -0,0 +1,65 @@
+//// [tests/cases/compiler/disablePackageDeduplication.ts] ////
+
+=== /src/a.ts ===
+import { a } from "a";
+>a : Symbol(a, Decl(a.ts, 0, 8))
+
+import { b } from "b";
+>b : Symbol(b, Decl(a.ts, 1, 8))
+
+import { c } from "c";
+>c : Symbol(c, Decl(a.ts, 2, 8))
+
+a(b); // Works
+>a : Symbol(a, Decl(a.ts, 0, 8))
+>b : Symbol(b, Decl(a.ts, 1, 8))
+
+a(c); // Error, these are from different versions of the library.
+>a : Symbol(a, Decl(a.ts, 0, 8))
+>c : Symbol(c, Decl(a.ts, 2, 8))
+
+=== /node_modules/a/index.d.ts ===
+import X from "x";
+>X : Symbol(X, Decl(index.d.ts, 0, 6))
+
+export function a(x: X): void;
+>a : Symbol(a, Decl(index.d.ts, 0, 18))
+>x : Symbol(x, Decl(index.d.ts, 1, 18))
+>X : Symbol(X, Decl(index.d.ts, 0, 6))
+
+=== /node_modules/a/node_modules/x/index.d.ts ===
+export default class X {
+>X : Symbol(X, Decl(index.d.ts, 0, 0))
+
+ private x: number;
+>x : Symbol(X.x, Decl(index.d.ts, 0, 24))
+}
+
+=== /node_modules/b/index.d.ts ===
+import X from "x";
+>X : Symbol(X, Decl(index.d.ts, 0, 6))
+
+export const b: X;
+>b : Symbol(b, Decl(index.d.ts, 1, 12))
+>X : Symbol(X, Decl(index.d.ts, 0, 6))
+
+=== /node_modules/b/node_modules/x/index.d.ts ===
+
+content not parsed
+
+=== /node_modules/c/index.d.ts ===
+import X from "x";
+>X : Symbol(X, Decl(index.d.ts, 0, 6))
+
+export const c: X;
+>c : Symbol(c, Decl(index.d.ts, 1, 12))
+>X : Symbol(X, Decl(index.d.ts, 0, 6))
+
+=== /node_modules/c/node_modules/x/index.d.ts ===
+export default class X {
+>X : Symbol(X, Decl(index.d.ts, 0, 0))
+
+ private x: number;
+>x : Symbol(X.x, Decl(index.d.ts, 0, 24))
+}
+
diff --git a/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).types b/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).types
new file mode 100644
index 00000000000..e445464c768
--- /dev/null
+++ b/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).types
@@ -0,0 +1,66 @@
+//// [tests/cases/compiler/disablePackageDeduplication.ts] ////
+
+=== /src/a.ts ===
+import { a } from "a";
+>a : (x: import("/node_modules/a/node_modules/x/index").default) => void
+
+import { b } from "b";
+>b : X
+
+import { c } from "c";
+>c : import("/node_modules/c/node_modules/x/index").default
+
+a(b); // Works
+>a(b) : void
+>a : (x: import("/node_modules/a/node_modules/x/index").default) => void
+>b : X
+
+a(c); // Error, these are from different versions of the library.
+>a(c) : void
+>a : (x: import("/node_modules/a/node_modules/x/index").default) => void
+>c : import("/node_modules/c/node_modules/x/index").default
+
+=== /node_modules/a/index.d.ts ===
+import X from "x";
+>X : typeof X
+
+export function a(x: X): void;
+>a : (x: X) => void
+>x : X
+
+=== /node_modules/a/node_modules/x/index.d.ts ===
+export default class X {
+>X : X
+
+ private x: number;
+>x : number
+}
+
+=== /node_modules/b/index.d.ts ===
+import X from "x";
+>X : any
+
+export const b: X;
+>b : X
+
+=== /node_modules/b/node_modules/x/index.d.ts ===
+content not parsed
+>content : any
+>not : any
+>parsed : any
+
+=== /node_modules/c/index.d.ts ===
+import X from "x";
+>X : typeof X
+
+export const c: X;
+>c : X
+
+=== /node_modules/c/node_modules/x/index.d.ts ===
+export default class X {
+>X : X
+
+ private x: number;
+>x : number
+}
+
diff --git a/testdata/tests/cases/compiler/disablePackageDeduplication.ts b/testdata/tests/cases/compiler/disablePackageDeduplication.ts
new file mode 100644
index 00000000000..11aaf510a01
--- /dev/null
+++ b/testdata/tests/cases/compiler/disablePackageDeduplication.ts
@@ -0,0 +1,43 @@
+// @noImplicitReferences: true
+// @disablePackageDeduplication: true,false
+
+// @Filename: /node_modules/a/index.d.ts
+import X from "x";
+export function a(x: X): void;
+
+// @Filename: /node_modules/a/node_modules/x/index.d.ts
+export default class X {
+ private x: number;
+}
+
+// @Filename: /node_modules/a/node_modules/x/package.json
+{ "name": "x", "version": "1.2.3" }
+
+// @Filename: /node_modules/b/index.d.ts
+import X from "x";
+export const b: X;
+
+// @Filename: /node_modules/b/node_modules/x/index.d.ts
+content not parsed
+
+// @Filename: /node_modules/b/node_modules/x/package.json
+{ "name": "x", "version": "1.2.3" }
+
+// @Filename: /node_modules/c/index.d.ts
+import X from "x";
+export const c: X;
+
+// @Filename: /node_modules/c/node_modules/x/index.d.ts
+export default class X {
+ private x: number;
+}
+
+// @Filename: /node_modules/c/node_modules/x/package.json
+{ "name": "x", "version": "1.2.4" }
+
+// @Filename: /src/a.ts
+import { a } from "a";
+import { b } from "b";
+import { c } from "c";
+a(b); // Works
+a(c); // Error, these are from different versions of the library.
From 2bb7300b49ca42d87bb5f2bd92007c3d3f08e7f1 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Tue, 16 Dec 2025 19:46:49 -0800
Subject: [PATCH 21/29] Use correct path for module error
---
internal/checker/checker.go | 2 +-
.../duplicatePackage_globalMerge.errors.txt | 4 ++--
...plicatePackage_globalMerge.errors.txt.diff | 19 -------------------
3 files changed, 3 insertions(+), 22 deletions(-)
delete mode 100644 testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.errors.txt.diff
diff --git a/internal/checker/checker.go b/internal/checker/checker.go
index b15adea0958..99716dea977 100644
--- a/internal/checker/checker.go
+++ b/internal/checker/checker.go
@@ -14867,7 +14867,7 @@ func (c *Checker) resolveExternalModule(location *ast.Node, moduleReference stri
return c.getMergedSymbol(sourceFile.Symbol)
}
if errorNode != nil && moduleNotFoundError != nil && !isSideEffectImport(errorNode) {
- c.error(errorNode, diagnostics.File_0_is_not_a_module, sourceFile.FileName())
+ c.error(errorNode, diagnostics.File_0_is_not_a_module, resolvedModule.ResolvedFileName)
}
return nil
}
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.errors.txt b/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.errors.txt
index d0be79b1b5e..d7e77340ac1 100644
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.errors.txt
+++ b/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.errors.txt
@@ -1,6 +1,6 @@
/node_modules/@types/react/index.d.ts(1,9): error TS2669: Augmentations for the global scope can only be directly nested in external modules or ambient module declarations.
/src/index.ts(1,24): error TS2306: File '/node_modules/@types/react/index.d.ts' is not a module.
-/tests/index.ts(1,24): error TS2306: File '/node_modules/@types/react/index.d.ts' is not a module.
+/tests/index.ts(1,24): error TS2306: File '/tests/node_modules/@types/react/index.d.ts' is not a module.
==== /src/bug25410.ts (0 errors) ====
@@ -15,7 +15,7 @@
==== /tests/index.ts (1 errors) ====
import * as React from 'react';
~~~~~~~
-!!! error TS2306: File '/node_modules/@types/react/index.d.ts' is not a module.
+!!! error TS2306: File '/tests/node_modules/@types/react/index.d.ts' is not a module.
export var y = 2
==== /tests/node_modules/@types/react/package.json (0 errors) ====
diff --git a/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.errors.txt.diff b/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.errors.txt.diff
deleted file mode 100644
index 1f44baa7bff..00000000000
--- a/testdata/baselines/reference/submodule/compiler/duplicatePackage_globalMerge.errors.txt.diff
+++ /dev/null
@@ -1,19 +0,0 @@
---- old.duplicatePackage_globalMerge.errors.txt
-+++ new.duplicatePackage_globalMerge.errors.txt
-@@= skipped -0, +0 lines =@@
- /node_modules/@types/react/index.d.ts(1,9): error TS2669: Augmentations for the global scope can only be directly nested in external modules or ambient module declarations.
- /src/index.ts(1,24): error TS2306: File '/node_modules/@types/react/index.d.ts' is not a module.
--/tests/index.ts(1,24): error TS2306: File '/tests/node_modules/@types/react/index.d.ts' is not a module.
-+/tests/index.ts(1,24): error TS2306: File '/node_modules/@types/react/index.d.ts' is not a module.
-
-
- ==== /src/bug25410.ts (0 errors) ====
-@@= skipped -14, +14 lines =@@
- ==== /tests/index.ts (1 errors) ====
- import * as React from 'react';
- ~~~~~~~
--!!! error TS2306: File '/tests/node_modules/@types/react/index.d.ts' is not a module.
-+!!! error TS2306: File '/node_modules/@types/react/index.d.ts' is not a module.
- export var y = 2
-
- ==== /tests/node_modules/@types/react/package.json (0 errors) ====
\ No newline at end of file
From 9d0f1d25a2b162f768475e0783978bfc6aff897c Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Thu, 8 Jan 2026 11:34:05 -0800
Subject: [PATCH 22/29] Add new failing test that strada passes
---
...geDeduplicationDuplicateGlobals.errors.txt | 58 +++++++++++++++++
.../packageDeduplicationDuplicateGlobals.js | 63 +++++++++++++++++++
...ckageDeduplicationDuplicateGlobals.symbols | 62 ++++++++++++++++++
...packageDeduplicationDuplicateGlobals.types | 62 ++++++++++++++++++
.../packageDeduplicationDuplicateGlobals.ts | 60 ++++++++++++++++++
5 files changed, 305 insertions(+)
create mode 100644 testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.errors.txt
create mode 100644 testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.js
create mode 100644 testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.symbols
create mode 100644 testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.types
create mode 100644 testdata/tests/cases/compiler/packageDeduplicationDuplicateGlobals.ts
diff --git a/testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.errors.txt b/testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.errors.txt
new file mode 100644
index 00000000000..4af2522c358
--- /dev/null
+++ b/testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.errors.txt
@@ -0,0 +1,58 @@
+/node_modules/.pnpm/foo@1.0.0+globals@2.0.0/node_modules/globals/index.d.ts(1,13): error TS2403: Subsequent variable declarations must have the same type. Variable 'myGlobal' must be of type 'string', but here has type 'number'.
+
+
+==== /src/index.ts (0 errors) ====
+ import { useBar } from "bar";
+ import { useBaz } from "baz";
+
+ const barResult = useBar();
+ const bazResult = useBaz();
+
+ const x: string = myGlobal;
+
+==== /node_modules/.pnpm/foo@1.0.0+globals@1.0.0/node_modules/foo/package.json (0 errors) ====
+ { "name": "foo", "version": "1.0.0" }
+
+==== /node_modules/.pnpm/foo@1.0.0+globals@1.0.0/node_modules/foo/index.d.ts (0 errors) ====
+ import "globals";
+ export declare function useFoo(): typeof myGlobal;
+
+==== /node_modules/.pnpm/foo@1.0.0+globals@1.0.0/node_modules/globals/package.json (0 errors) ====
+ { "name": "globals", "version": "1.0.0" }
+
+==== /node_modules/.pnpm/foo@1.0.0+globals@1.0.0/node_modules/globals/index.d.ts (0 errors) ====
+ declare var myGlobal: string;
+
+==== /node_modules/.pnpm/foo@1.0.0+globals@2.0.0/node_modules/foo/package.json (0 errors) ====
+ { "name": "foo", "version": "1.0.0" }
+
+==== /node_modules/.pnpm/foo@1.0.0+globals@2.0.0/node_modules/foo/index.d.ts (0 errors) ====
+ import "globals";
+ export declare function useFoo(): typeof myGlobal;
+
+==== /node_modules/.pnpm/foo@1.0.0+globals@2.0.0/node_modules/globals/package.json (0 errors) ====
+ { "name": "globals", "version": "2.0.0" }
+
+==== /node_modules/.pnpm/foo@1.0.0+globals@2.0.0/node_modules/globals/index.d.ts (1 errors) ====
+ declare var myGlobal: number;
+ ~~~~~~~~
+!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'myGlobal' must be of type 'string', but here has type 'number'.
+!!! related TS6203 /node_modules/.pnpm/foo@1.0.0+globals@1.0.0/node_modules/globals/index.d.ts:1:13: 'myGlobal' was also declared here.
+
+==== /node_modules/.pnpm/bar@1.0.0/node_modules/bar/package.json (0 errors) ====
+ { "name": "bar", "version": "1.0.0" }
+
+==== /node_modules/.pnpm/bar@1.0.0/node_modules/bar/index.d.ts (0 errors) ====
+ import { useFoo } from "foo";
+ export declare function useBar(): ReturnType;
+
+==== /node_modules/.pnpm/baz@1.0.0/node_modules/baz/package.json (0 errors) ====
+ { "name": "baz", "version": "1.0.0" }
+
+==== /node_modules/.pnpm/baz@1.0.0/node_modules/baz/index.d.ts (0 errors) ====
+ import { useFoo } from "foo";
+ export declare function useBaz(): ReturnType;
+
+
+
+
\ No newline at end of file
diff --git a/testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.js b/testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.js
new file mode 100644
index 00000000000..574b3c20517
--- /dev/null
+++ b/testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.js
@@ -0,0 +1,63 @@
+//// [tests/cases/compiler/packageDeduplicationDuplicateGlobals.ts] ////
+
+//// [package.json]
+{ "name": "foo", "version": "1.0.0" }
+
+//// [index.d.ts]
+import "globals";
+export declare function useFoo(): typeof myGlobal;
+
+//// [package.json]
+{ "name": "globals", "version": "1.0.0" }
+
+//// [index.d.ts]
+declare var myGlobal: string;
+
+//// [package.json]
+{ "name": "foo", "version": "1.0.0" }
+
+//// [index.d.ts]
+import "globals";
+export declare function useFoo(): typeof myGlobal;
+
+//// [package.json]
+{ "name": "globals", "version": "2.0.0" }
+
+//// [index.d.ts]
+declare var myGlobal: number;
+
+//// [package.json]
+{ "name": "bar", "version": "1.0.0" }
+
+//// [index.d.ts]
+import { useFoo } from "foo";
+export declare function useBar(): ReturnType;
+
+//// [package.json]
+{ "name": "baz", "version": "1.0.0" }
+
+//// [index.d.ts]
+import { useFoo } from "foo";
+export declare function useBaz(): ReturnType;
+
+
+
+
+//// [index.ts]
+import { useBar } from "bar";
+import { useBaz } from "baz";
+
+const barResult = useBar();
+const bazResult = useBaz();
+
+const x: string = myGlobal;
+
+
+//// [index.js]
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const bar_1 = require("bar");
+const baz_1 = require("baz");
+const barResult = (0, bar_1.useBar)();
+const bazResult = (0, baz_1.useBaz)();
+const x = myGlobal;
diff --git a/testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.symbols b/testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.symbols
new file mode 100644
index 00000000000..22c2acbefcd
--- /dev/null
+++ b/testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.symbols
@@ -0,0 +1,62 @@
+//// [tests/cases/compiler/packageDeduplicationDuplicateGlobals.ts] ////
+
+=== /src/index.ts ===
+import { useBar } from "bar";
+>useBar : Symbol(useBar, Decl(index.ts, 0, 8))
+
+import { useBaz } from "baz";
+>useBaz : Symbol(useBaz, Decl(index.ts, 1, 8))
+
+const barResult = useBar();
+>barResult : Symbol(barResult, Decl(index.ts, 3, 5))
+>useBar : Symbol(useBar, Decl(index.ts, 0, 8))
+
+const bazResult = useBaz();
+>bazResult : Symbol(bazResult, Decl(index.ts, 4, 5))
+>useBaz : Symbol(useBaz, Decl(index.ts, 1, 8))
+
+const x: string = myGlobal;
+>x : Symbol(x, Decl(index.ts, 6, 5))
+>myGlobal : Symbol(myGlobal, Decl(index.d.ts, 0, 11), Decl(index.d.ts, 0, 11))
+
+=== /node_modules/.pnpm/foo@1.0.0+globals@1.0.0/node_modules/foo/index.d.ts ===
+import "globals";
+export declare function useFoo(): typeof myGlobal;
+>useFoo : Symbol(useFoo, Decl(index.d.ts, 0, 17))
+>myGlobal : Symbol(myGlobal, Decl(index.d.ts, 0, 11), Decl(index.d.ts, 0, 11))
+
+=== /node_modules/.pnpm/foo@1.0.0+globals@1.0.0/node_modules/globals/index.d.ts ===
+declare var myGlobal: string;
+>myGlobal : Symbol(myGlobal, Decl(index.d.ts, 0, 11), Decl(index.d.ts, 0, 11))
+
+=== /node_modules/.pnpm/foo@1.0.0+globals@2.0.0/node_modules/foo/index.d.ts ===
+import "globals";
+export declare function useFoo(): typeof myGlobal;
+>useFoo : Symbol(useFoo, Decl(index.d.ts, 0, 17))
+>myGlobal : Symbol(myGlobal, Decl(index.d.ts, 0, 11), Decl(index.d.ts, 0, 11))
+
+=== /node_modules/.pnpm/foo@1.0.0+globals@2.0.0/node_modules/globals/index.d.ts ===
+declare var myGlobal: number;
+>myGlobal : Symbol(myGlobal, Decl(index.d.ts, 0, 11), Decl(index.d.ts, 0, 11))
+
+=== /node_modules/.pnpm/bar@1.0.0/node_modules/bar/index.d.ts ===
+import { useFoo } from "foo";
+>useFoo : Symbol(useFoo, Decl(index.d.ts, 0, 8))
+
+export declare function useBar(): ReturnType;
+>useBar : Symbol(useBar, Decl(index.d.ts, 0, 29))
+>ReturnType : Symbol(ReturnType, Decl(lib.es5.d.ts, --, --))
+>useFoo : Symbol(useFoo, Decl(index.d.ts, 0, 8))
+
+=== /node_modules/.pnpm/baz@1.0.0/node_modules/baz/index.d.ts ===
+import { useFoo } from "foo";
+>useFoo : Symbol(useFoo, Decl(index.d.ts, 0, 8))
+
+export declare function useBaz(): ReturnType;
+>useBaz : Symbol(useBaz, Decl(index.d.ts, 0, 29))
+>ReturnType : Symbol(ReturnType, Decl(lib.es5.d.ts, --, --))
+>useFoo : Symbol(useFoo, Decl(index.d.ts, 0, 8))
+
+
+
+
diff --git a/testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.types b/testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.types
new file mode 100644
index 00000000000..33b2ead9a9f
--- /dev/null
+++ b/testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.types
@@ -0,0 +1,62 @@
+//// [tests/cases/compiler/packageDeduplicationDuplicateGlobals.ts] ////
+
+=== /src/index.ts ===
+import { useBar } from "bar";
+>useBar : () => string
+
+import { useBaz } from "baz";
+>useBaz : () => string
+
+const barResult = useBar();
+>barResult : string
+>useBar() : string
+>useBar : () => string
+
+const bazResult = useBaz();
+>bazResult : string
+>useBaz() : string
+>useBaz : () => string
+
+const x: string = myGlobal;
+>x : string
+>myGlobal : string
+
+=== /node_modules/.pnpm/foo@1.0.0+globals@1.0.0/node_modules/foo/index.d.ts ===
+import "globals";
+export declare function useFoo(): typeof myGlobal;
+>useFoo : () => string
+>myGlobal : string
+
+=== /node_modules/.pnpm/foo@1.0.0+globals@1.0.0/node_modules/globals/index.d.ts ===
+declare var myGlobal: string;
+>myGlobal : string
+
+=== /node_modules/.pnpm/foo@1.0.0+globals@2.0.0/node_modules/foo/index.d.ts ===
+import "globals";
+export declare function useFoo(): typeof myGlobal;
+>useFoo : () => string
+>myGlobal : string
+
+=== /node_modules/.pnpm/foo@1.0.0+globals@2.0.0/node_modules/globals/index.d.ts ===
+declare var myGlobal: number;
+>myGlobal : string
+
+=== /node_modules/.pnpm/bar@1.0.0/node_modules/bar/index.d.ts ===
+import { useFoo } from "foo";
+>useFoo : () => string
+
+export declare function useBar(): ReturnType;
+>useBar : () => string
+>useFoo : () => string
+
+=== /node_modules/.pnpm/baz@1.0.0/node_modules/baz/index.d.ts ===
+import { useFoo } from "foo";
+>useFoo : () => string
+
+export declare function useBaz(): ReturnType;
+>useBaz : () => string
+>useFoo : () => string
+
+
+
+
diff --git a/testdata/tests/cases/compiler/packageDeduplicationDuplicateGlobals.ts b/testdata/tests/cases/compiler/packageDeduplicationDuplicateGlobals.ts
new file mode 100644
index 00000000000..2209322a157
--- /dev/null
+++ b/testdata/tests/cases/compiler/packageDeduplicationDuplicateGlobals.ts
@@ -0,0 +1,60 @@
+// @strict: true
+// @noImplicitReferences: true
+
+// @filename: /node_modules/.pnpm/foo@1.0.0+globals@1.0.0/node_modules/foo/package.json
+{ "name": "foo", "version": "1.0.0" }
+
+// @filename: /node_modules/.pnpm/foo@1.0.0+globals@1.0.0/node_modules/foo/index.d.ts
+import "globals";
+export declare function useFoo(): typeof myGlobal;
+
+// @filename: /node_modules/.pnpm/foo@1.0.0+globals@1.0.0/node_modules/globals/package.json
+{ "name": "globals", "version": "1.0.0" }
+
+// @filename: /node_modules/.pnpm/foo@1.0.0+globals@1.0.0/node_modules/globals/index.d.ts
+declare var myGlobal: string;
+
+// @filename: /node_modules/.pnpm/foo@1.0.0+globals@2.0.0/node_modules/foo/package.json
+{ "name": "foo", "version": "1.0.0" }
+
+// @filename: /node_modules/.pnpm/foo@1.0.0+globals@2.0.0/node_modules/foo/index.d.ts
+import "globals";
+export declare function useFoo(): typeof myGlobal;
+
+// @filename: /node_modules/.pnpm/foo@1.0.0+globals@2.0.0/node_modules/globals/package.json
+{ "name": "globals", "version": "2.0.0" }
+
+// @filename: /node_modules/.pnpm/foo@1.0.0+globals@2.0.0/node_modules/globals/index.d.ts
+declare var myGlobal: number;
+
+// @filename: /node_modules/.pnpm/bar@1.0.0/node_modules/bar/package.json
+{ "name": "bar", "version": "1.0.0" }
+
+// @filename: /node_modules/.pnpm/bar@1.0.0/node_modules/bar/index.d.ts
+import { useFoo } from "foo";
+export declare function useBar(): ReturnType;
+
+// @filename: /node_modules/.pnpm/baz@1.0.0/node_modules/baz/package.json
+{ "name": "baz", "version": "1.0.0" }
+
+// @filename: /node_modules/.pnpm/baz@1.0.0/node_modules/baz/index.d.ts
+import { useFoo } from "foo";
+export declare function useBaz(): ReturnType;
+
+// @link: /node_modules/.pnpm/foo@1.0.0+globals@1.0.0/node_modules/foo -> /node_modules/.pnpm/bar@1.0.0/node_modules/foo
+// @link: /node_modules/.pnpm/foo@1.0.0+globals@1.0.0/node_modules/globals -> /node_modules/.pnpm/bar@1.0.0/node_modules/globals
+
+// @link: /node_modules/.pnpm/foo@1.0.0+globals@2.0.0/node_modules/foo -> /node_modules/.pnpm/baz@1.0.0/node_modules/foo
+// @link: /node_modules/.pnpm/foo@1.0.0+globals@2.0.0/node_modules/globals -> /node_modules/.pnpm/baz@1.0.0/node_modules/globals
+
+// @link: /node_modules/.pnpm/bar@1.0.0/node_modules/bar -> /node_modules/bar
+// @link: /node_modules/.pnpm/baz@1.0.0/node_modules/baz -> /node_modules/baz
+
+// @filename: /src/index.ts
+import { useBar } from "bar";
+import { useBaz } from "baz";
+
+const barResult = useBar();
+const bazResult = useBaz();
+
+const x: string = myGlobal;
From a6956efef42342721d3bbd3b78053518397c6b09 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Thu, 8 Jan 2026 11:49:15 -0800
Subject: [PATCH 23/29] Do not extra load files
---
internal/compiler/filesparser.go | 16 ++++-
...geDeduplicationDuplicateGlobals.errors.txt | 58 -------------------
...ckageDeduplicationDuplicateGlobals.symbols | 12 ++--
...packageDeduplicationDuplicateGlobals.types | 4 --
4 files changed, 18 insertions(+), 72 deletions(-)
delete mode 100644 testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.errors.txt
diff --git a/internal/compiler/filesparser.go b/internal/compiler/filesparser.go
index ab6a98ee5c5..46a18b27e04 100644
--- a/internal/compiler/filesparser.go
+++ b/internal/compiler/filesparser.go
@@ -372,8 +372,20 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
}
}
- if subTasks := task.subTasks; len(subTasks) > 0 {
- collectFiles(subTasks, seen)
+ // Check if this file is from a deduplicated package.
+ // If so, skip walking its subtasks - the canonical package's
+ // dependencies will be used instead.
+ skipSubtasks := false
+ if deduplicatedPathMap != nil {
+ if canonical, ok := deduplicatedPathMap[task.path]; ok && task.path != canonical {
+ skipSubtasks = true
+ }
+ }
+
+ if !skipSubtasks {
+ if subTasks := task.subTasks; len(subTasks) > 0 {
+ collectFiles(subTasks, seen)
+ }
}
// Exclude automatic type directive tasks from include reason processing,
diff --git a/testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.errors.txt b/testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.errors.txt
deleted file mode 100644
index 4af2522c358..00000000000
--- a/testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.errors.txt
+++ /dev/null
@@ -1,58 +0,0 @@
-/node_modules/.pnpm/foo@1.0.0+globals@2.0.0/node_modules/globals/index.d.ts(1,13): error TS2403: Subsequent variable declarations must have the same type. Variable 'myGlobal' must be of type 'string', but here has type 'number'.
-
-
-==== /src/index.ts (0 errors) ====
- import { useBar } from "bar";
- import { useBaz } from "baz";
-
- const barResult = useBar();
- const bazResult = useBaz();
-
- const x: string = myGlobal;
-
-==== /node_modules/.pnpm/foo@1.0.0+globals@1.0.0/node_modules/foo/package.json (0 errors) ====
- { "name": "foo", "version": "1.0.0" }
-
-==== /node_modules/.pnpm/foo@1.0.0+globals@1.0.0/node_modules/foo/index.d.ts (0 errors) ====
- import "globals";
- export declare function useFoo(): typeof myGlobal;
-
-==== /node_modules/.pnpm/foo@1.0.0+globals@1.0.0/node_modules/globals/package.json (0 errors) ====
- { "name": "globals", "version": "1.0.0" }
-
-==== /node_modules/.pnpm/foo@1.0.0+globals@1.0.0/node_modules/globals/index.d.ts (0 errors) ====
- declare var myGlobal: string;
-
-==== /node_modules/.pnpm/foo@1.0.0+globals@2.0.0/node_modules/foo/package.json (0 errors) ====
- { "name": "foo", "version": "1.0.0" }
-
-==== /node_modules/.pnpm/foo@1.0.0+globals@2.0.0/node_modules/foo/index.d.ts (0 errors) ====
- import "globals";
- export declare function useFoo(): typeof myGlobal;
-
-==== /node_modules/.pnpm/foo@1.0.0+globals@2.0.0/node_modules/globals/package.json (0 errors) ====
- { "name": "globals", "version": "2.0.0" }
-
-==== /node_modules/.pnpm/foo@1.0.0+globals@2.0.0/node_modules/globals/index.d.ts (1 errors) ====
- declare var myGlobal: number;
- ~~~~~~~~
-!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'myGlobal' must be of type 'string', but here has type 'number'.
-!!! related TS6203 /node_modules/.pnpm/foo@1.0.0+globals@1.0.0/node_modules/globals/index.d.ts:1:13: 'myGlobal' was also declared here.
-
-==== /node_modules/.pnpm/bar@1.0.0/node_modules/bar/package.json (0 errors) ====
- { "name": "bar", "version": "1.0.0" }
-
-==== /node_modules/.pnpm/bar@1.0.0/node_modules/bar/index.d.ts (0 errors) ====
- import { useFoo } from "foo";
- export declare function useBar(): ReturnType;
-
-==== /node_modules/.pnpm/baz@1.0.0/node_modules/baz/package.json (0 errors) ====
- { "name": "baz", "version": "1.0.0" }
-
-==== /node_modules/.pnpm/baz@1.0.0/node_modules/baz/index.d.ts (0 errors) ====
- import { useFoo } from "foo";
- export declare function useBaz(): ReturnType;
-
-
-
-
\ No newline at end of file
diff --git a/testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.symbols b/testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.symbols
index 22c2acbefcd..69ea54c1932 100644
--- a/testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.symbols
+++ b/testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.symbols
@@ -17,27 +17,23 @@ const bazResult = useBaz();
const x: string = myGlobal;
>x : Symbol(x, Decl(index.ts, 6, 5))
->myGlobal : Symbol(myGlobal, Decl(index.d.ts, 0, 11), Decl(index.d.ts, 0, 11))
+>myGlobal : Symbol(myGlobal, Decl(index.d.ts, 0, 11))
=== /node_modules/.pnpm/foo@1.0.0+globals@1.0.0/node_modules/foo/index.d.ts ===
import "globals";
export declare function useFoo(): typeof myGlobal;
>useFoo : Symbol(useFoo, Decl(index.d.ts, 0, 17))
->myGlobal : Symbol(myGlobal, Decl(index.d.ts, 0, 11), Decl(index.d.ts, 0, 11))
+>myGlobal : Symbol(myGlobal, Decl(index.d.ts, 0, 11))
=== /node_modules/.pnpm/foo@1.0.0+globals@1.0.0/node_modules/globals/index.d.ts ===
declare var myGlobal: string;
->myGlobal : Symbol(myGlobal, Decl(index.d.ts, 0, 11), Decl(index.d.ts, 0, 11))
+>myGlobal : Symbol(myGlobal, Decl(index.d.ts, 0, 11))
=== /node_modules/.pnpm/foo@1.0.0+globals@2.0.0/node_modules/foo/index.d.ts ===
import "globals";
export declare function useFoo(): typeof myGlobal;
>useFoo : Symbol(useFoo, Decl(index.d.ts, 0, 17))
->myGlobal : Symbol(myGlobal, Decl(index.d.ts, 0, 11), Decl(index.d.ts, 0, 11))
-
-=== /node_modules/.pnpm/foo@1.0.0+globals@2.0.0/node_modules/globals/index.d.ts ===
-declare var myGlobal: number;
->myGlobal : Symbol(myGlobal, Decl(index.d.ts, 0, 11), Decl(index.d.ts, 0, 11))
+>myGlobal : Symbol(myGlobal, Decl(index.d.ts, 0, 11))
=== /node_modules/.pnpm/bar@1.0.0/node_modules/bar/index.d.ts ===
import { useFoo } from "foo";
diff --git a/testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.types b/testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.types
index 33b2ead9a9f..11ce01d5f35 100644
--- a/testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.types
+++ b/testdata/baselines/reference/compiler/packageDeduplicationDuplicateGlobals.types
@@ -37,10 +37,6 @@ export declare function useFoo(): typeof myGlobal;
>useFoo : () => string
>myGlobal : string
-=== /node_modules/.pnpm/foo@1.0.0+globals@2.0.0/node_modules/globals/index.d.ts ===
-declare var myGlobal: number;
->myGlobal : string
-
=== /node_modules/.pnpm/bar@1.0.0/node_modules/bar/index.d.ts ===
import { useFoo } from "foo";
>useFoo : () => string
From 15e0d64d5be41a34917819adb752265628d12fb9 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Thu, 8 Jan 2026 12:08:52 -0800
Subject: [PATCH 24/29] Rando newline
---
internal/compiler/filesparser.go | 1 -
1 file changed, 1 deletion(-)
diff --git a/internal/compiler/filesparser.go b/internal/compiler/filesparser.go
index 46a18b27e04..6e54db6f337 100644
--- a/internal/compiler/filesparser.go
+++ b/internal/compiler/filesparser.go
@@ -359,7 +359,6 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
if _, alreadyRecorded := sourceFileToPackageName[resolvedPath]; !alreadyRecorded {
sourceFileToPackageName[resolvedPath] = packageName
if resolvedPath != canonical {
-
deduplicatedPathMap[resolvedPath] = canonical
redirectTargetsMap[canonical] = append(redirectTargetsMap[canonical], resolution.ResolvedFileName)
}
From 8cbc494a4679ce89afa66c72c2542c8baecfb511 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Thu, 8 Jan 2026 14:43:44 -0800
Subject: [PATCH 25/29] Greatly simplify
---
internal/compiler/fileloader.go | 3 +
internal/compiler/filesparser.go | 78 ++++++-------------
...er-symlinked-package-with-indirect-link.js | 4 +-
3 files changed, 30 insertions(+), 55 deletions(-)
diff --git a/internal/compiler/fileloader.go b/internal/compiler/fileloader.go
index 0b69107b012..d55c0343774 100644
--- a/internal/compiler/fileloader.go
+++ b/internal/compiler/fileloader.go
@@ -196,6 +196,7 @@ func (p *fileLoader) resolveAutomaticTypeDirectives(containingFileName string) (
kind: fileIncludeKindAutomaticTypeDirectiveFile,
data: &automaticTypeDirectiveFileData{name, resolved.PackageId},
},
+ packageId: resolved.PackageId,
})
}
}
@@ -366,6 +367,7 @@ func (p *fileLoader) resolveTypeReferenceDirectives(t *parseTask) {
increaseDepth: resolved.IsExternalLibraryImport,
elideOnDepth: false,
includeReason: includeReason,
+ packageId: resolved.PackageId,
}, nil)
} else {
t.processingDiagnostics = append(t.processingDiagnostics, &processingDiagnostic{
@@ -472,6 +474,7 @@ func (p *fileLoader) resolveImportsAndModuleAugmentations(t *parseTask) {
synthetic: core.IfElse(importIndex < 0, entry, nil),
},
},
+ packageId: resolvedModule.PackageId,
}, nil)
}
}
diff --git a/internal/compiler/filesparser.go b/internal/compiler/filesparser.go
index 6e54db6f337..99217398de7 100644
--- a/internal/compiler/filesparser.go
+++ b/internal/compiler/filesparser.go
@@ -25,6 +25,7 @@ type parseTask struct {
startedSubTasks bool
isForAutomaticTypeDirective bool
includeReason *FileIncludeReason
+ packageId module.PackageId
metadata ast.SourceFileMetaData
resolutionsInFile module.ModeAwareCache[*module.ResolvedModule]
@@ -164,6 +165,7 @@ type resolvedRef struct {
increaseDepth bool
elideOnDepth bool
includeReason *FileIncludeReason
+ packageId module.PackageId
}
func (t *parseTask) addSubTask(ref resolvedRef, libFile *LibFile) {
@@ -174,6 +176,7 @@ func (t *parseTask) addSubTask(ref resolvedRef, libFile *LibFile) {
increaseDepth: ref.increaseDepth,
elideOnDepth: ref.elideOnDepth,
includeReason: ref.includeReason,
+ packageId: ref.packageId,
}
t.subTasks = append(t.subTasks, subTask)
}
@@ -190,6 +193,7 @@ type parseTaskData struct {
mu sync.Mutex
lowestDepth int
startedSubTasks bool
+ packageId module.PackageId
}
func (w *filesParser) parse(loader *fileLoader, tasks []*parseTask) {
@@ -220,6 +224,11 @@ func (w *filesParser) start(loader *fileLoader, tasks []*parseTask, depth int) {
}
}
+ // Propagate packageId to data if we have one and data doesn't yet
+ if data.packageId.Name == "" && task.packageId.Name != "" {
+ data.packageId = task.packageId
+ }
+
currentDepth := core.IfElse(task.increaseDepth, depth+1, depth)
if currentDepth < data.lowestDepth {
// If we're seeing this task at a lower depth than before,
@@ -343,45 +352,20 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
loader.opts.Host.Trace(trace.Message, trace.Args...)
}
- if packageIdToCanonicalPath != nil {
- for _, resolution := range task.resolutionsInFile {
- if !resolution.IsResolved() {
- continue
- }
- pkgId := resolution.PackageId
- if pkgId.Name == "" {
- continue
- }
- resolvedPath := loader.toPath(resolution.ResolvedFileName)
- packageName := pkgId.PackageName()
-
- if canonical, exists := packageIdToCanonicalPath[pkgId]; exists {
- if _, alreadyRecorded := sourceFileToPackageName[resolvedPath]; !alreadyRecorded {
- sourceFileToPackageName[resolvedPath] = packageName
- if resolvedPath != canonical {
- deduplicatedPathMap[resolvedPath] = canonical
- redirectTargetsMap[canonical] = append(redirectTargetsMap[canonical], resolution.ResolvedFileName)
- }
- }
- } else {
- packageIdToCanonicalPath[pkgId] = resolvedPath
- sourceFileToPackageName[resolvedPath] = packageName
- deduplicatedPathMap[resolvedPath] = resolvedPath
- }
- }
- }
-
- // Check if this file is from a deduplicated package.
- // If so, skip walking its subtasks - the canonical package's
- // dependencies will be used instead.
- skipSubtasks := false
- if deduplicatedPathMap != nil {
- if canonical, ok := deduplicatedPathMap[task.path]; ok && task.path != canonical {
- skipSubtasks = true
+ var existingCanonicalPath tspath.Path
+ if packageIdToCanonicalPath != nil && data.packageId.Name != "" {
+ if canonical, exists := packageIdToCanonicalPath[data.packageId]; exists {
+ packageIdToCanonicalPath[data.packageId] = task.path
+ sourceFileToPackageName[task.path] = data.packageId.PackageName()
+ deduplicatedPathMap[task.path] = canonical
+ redirectTargetsMap[canonical] = append(redirectTargetsMap[canonical], task.normalizedFilePath)
+ existingCanonicalPath = canonical
+ } else {
+ packageIdToCanonicalPath[data.packageId] = task.path
}
}
- if !skipSubtasks {
+ if existingCanonicalPath == "" {
if subTasks := task.subTasks; len(subTasks) > 0 {
collectFiles(subTasks, seen)
}
@@ -402,6 +386,10 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
continue
}
file := task.file
+ if existingCanonicalPath != "" {
+ file = filesByPath[existingCanonicalPath]
+ }
+
path := task.path
if len(task.processingDiagnostics) > 0 {
@@ -417,7 +405,7 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
if task.libFile != nil {
libFiles = append(libFiles, file)
libFilesMap[path] = task.libFile
- } else {
+ } else if existingCanonicalPath == "" {
files = append(files, file)
}
filesByPath[path] = file
@@ -460,22 +448,6 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
}
}
- if deduplicatedPathMap != nil {
- for duplicatePath, canonicalPath := range deduplicatedPathMap {
- if duplicatePath != canonicalPath {
- if canonicalFile, ok := filesByPath[canonicalPath]; ok {
- filesByPath[duplicatePath] = canonicalFile
- }
- }
- }
- allFiles = slices.DeleteFunc(allFiles, func(f *ast.SourceFile) bool {
- if canonicalPath, ok := deduplicatedPathMap[f.Path()]; ok {
- return f.Path() != canonicalPath
- }
- return false
- })
- }
-
return processedFiles{
finishedProcessing: true,
resolver: loader.resolver,
diff --git a/testdata/baselines/reference/tsc/declarationEmit/when-same-version-is-referenced-through-source-and-another-symlinked-package-with-indirect-link.js b/testdata/baselines/reference/tsc/declarationEmit/when-same-version-is-referenced-through-source-and-another-symlinked-package-with-indirect-link.js
index 70e094d00d3..8a3a24113f2 100644
--- a/testdata/baselines/reference/tsc/declarationEmit/when-same-version-is-referenced-through-source-and-another-symlinked-package-with-indirect-link.js
+++ b/testdata/baselines/reference/tsc/declarationEmit/when-same-version-is-referenced-through-source-and-another-symlinked-package-with-indirect-link.js
@@ -166,10 +166,10 @@ Resolving real path for '/user/username/projects/myproject/plugin-two/node_modul
======== Module name 'typescript-fsa' was successfully resolved to '/user/username/projects/myproject/plugin-two/node_modules/typescript-fsa/index.d.ts' with Package ID 'typescript-fsa/index.d.ts@3.0.0-beta-2'. ========
../../../../home/src/tslibs/TS/Lib/lib.d.ts
Default library for target 'ES5'
+plugin-two/node_modules/typescript-fsa/index.d.ts
+ Imported via "typescript-fsa" from file 'plugin-two/dist/commonjs/index.d.ts' with packageId 'typescript-fsa/index.d.ts@3.0.0-beta-2'
plugin-two/dist/commonjs/index.d.ts
Imported via "plugin-two" from file 'plugin-one/index.ts' with packageId 'plugin-two/dist/commonjs/index.d.ts@0.1.3'
-plugin-one/node_modules/typescript-fsa/index.d.ts
- Imported via "typescript-fsa" from file 'plugin-one/index.ts' with packageId 'typescript-fsa/index.d.ts@3.0.0-beta-2'
plugin-one/index.ts
Matched by default include pattern '**/*'
//// [/home/src/tslibs/TS/Lib/lib.d.ts] *Lib*
From 2744db9044cc15515a126b912b3395f51e388002 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Thu, 8 Jan 2026 14:53:14 -0800
Subject: [PATCH 26/29] Further simplifications
---
internal/compiler/fileloader.go | 2 --
internal/compiler/filesparser.go | 8 ++------
2 files changed, 2 insertions(+), 8 deletions(-)
diff --git a/internal/compiler/fileloader.go b/internal/compiler/fileloader.go
index d55c0343774..3f97f101211 100644
--- a/internal/compiler/fileloader.go
+++ b/internal/compiler/fileloader.go
@@ -68,8 +68,6 @@ type processedFiles struct {
// if file was included using source file and its output is actually part of program
// this contains mapping from output to source file
outputFileToProjectReferenceSource map[tspath.Path]string
- // Maps a source file path to the name of the package it was imported with
- sourceFileToPackageName map[tspath.Path]string
// Key is a file path. Value is the list of files that redirect to it (same package, different install location)
redirectTargetsMap map[tspath.Path][]string
// Maps any path (canonical or redirect target) to its canonical path.
diff --git a/internal/compiler/filesparser.go b/internal/compiler/filesparser.go
index 99217398de7..82b0cc8eadf 100644
--- a/internal/compiler/filesparser.go
+++ b/internal/compiler/filesparser.go
@@ -292,12 +292,10 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
var sourceFilesFoundSearchingNodeModules collections.Set[tspath.Path]
libFilesMap := make(map[tspath.Path]*LibFile, libFileCount)
- var sourceFileToPackageName map[tspath.Path]string
var redirectTargetsMap map[tspath.Path][]string
var deduplicatedPathMap map[tspath.Path]tspath.Path
var packageIdToCanonicalPath map[module.PackageId]tspath.Path
if !loader.opts.Config.CompilerOptions().DisablePackageDeduplication.IsTrue() {
- sourceFileToPackageName = make(map[tspath.Path]string, totalFileCount)
redirectTargetsMap = make(map[tspath.Path][]string)
deduplicatedPathMap = make(map[tspath.Path]tspath.Path)
packageIdToCanonicalPath = make(map[module.PackageId]tspath.Path)
@@ -355,13 +353,12 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
var existingCanonicalPath tspath.Path
if packageIdToCanonicalPath != nil && data.packageId.Name != "" {
if canonical, exists := packageIdToCanonicalPath[data.packageId]; exists {
- packageIdToCanonicalPath[data.packageId] = task.path
- sourceFileToPackageName[task.path] = data.packageId.PackageName()
- deduplicatedPathMap[task.path] = canonical
redirectTargetsMap[canonical] = append(redirectTargetsMap[canonical], task.normalizedFilePath)
+ deduplicatedPathMap[task.path] = canonical
existingCanonicalPath = canonical
} else {
packageIdToCanonicalPath[data.packageId] = task.path
+ deduplicatedPathMap[task.path] = task.path
}
}
@@ -464,7 +461,6 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
missingFiles: missingFiles,
includeProcessor: includeProcessor,
outputFileToProjectReferenceSource: outputFileToProjectReferenceSource,
- sourceFileToPackageName: sourceFileToPackageName,
redirectTargetsMap: redirectTargetsMap,
deduplicatedPathMap: deduplicatedPathMap,
}
From 31b3a1e0c3fde43dafb914c8d25abb9be3ad4d17 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Fri, 9 Jan 2026 13:31:08 -0800
Subject: [PATCH 27/29] Use a set instead
---
internal/compiler/fileloader.go | 5 ++---
internal/compiler/filesparser.go | 9 ++++-----
internal/compiler/program.go | 5 ++---
3 files changed, 8 insertions(+), 11 deletions(-)
diff --git a/internal/compiler/fileloader.go b/internal/compiler/fileloader.go
index 3f97f101211..12bd88e2f57 100644
--- a/internal/compiler/fileloader.go
+++ b/internal/compiler/fileloader.go
@@ -70,9 +70,8 @@ type processedFiles struct {
outputFileToProjectReferenceSource map[tspath.Path]string
// Key is a file path. Value is the list of files that redirect to it (same package, different install location)
redirectTargetsMap map[tspath.Path][]string
- // Maps any path (canonical or redirect target) to its canonical path.
- // Canonical paths map to themselves; redirect targets map to their canonical path.
- deduplicatedPathMap map[tspath.Path]tspath.Path
+ // Any paths involved in deduplication, including canonical paths and redirected paths
+ deduplicatedPaths collections.Set[tspath.Path]
finishedProcessing bool
}
diff --git a/internal/compiler/filesparser.go b/internal/compiler/filesparser.go
index 82b0cc8eadf..350a4a0e3e8 100644
--- a/internal/compiler/filesparser.go
+++ b/internal/compiler/filesparser.go
@@ -293,11 +293,10 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
libFilesMap := make(map[tspath.Path]*LibFile, libFileCount)
var redirectTargetsMap map[tspath.Path][]string
- var deduplicatedPathMap map[tspath.Path]tspath.Path
+ var deduplicatedPaths collections.Set[tspath.Path]
var packageIdToCanonicalPath map[module.PackageId]tspath.Path
if !loader.opts.Config.CompilerOptions().DisablePackageDeduplication.IsTrue() {
redirectTargetsMap = make(map[tspath.Path][]string)
- deduplicatedPathMap = make(map[tspath.Path]tspath.Path)
packageIdToCanonicalPath = make(map[module.PackageId]tspath.Path)
}
@@ -354,11 +353,11 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
if packageIdToCanonicalPath != nil && data.packageId.Name != "" {
if canonical, exists := packageIdToCanonicalPath[data.packageId]; exists {
redirectTargetsMap[canonical] = append(redirectTargetsMap[canonical], task.normalizedFilePath)
- deduplicatedPathMap[task.path] = canonical
existingCanonicalPath = canonical
+ deduplicatedPaths.Add(task.path)
+ deduplicatedPaths.Add(canonical)
} else {
packageIdToCanonicalPath[data.packageId] = task.path
- deduplicatedPathMap[task.path] = task.path
}
}
@@ -462,7 +461,7 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
includeProcessor: includeProcessor,
outputFileToProjectReferenceSource: outputFileToProjectReferenceSource,
redirectTargetsMap: redirectTargetsMap,
- deduplicatedPathMap: deduplicatedPathMap,
+ deduplicatedPaths: deduplicatedPaths,
}
}
diff --git a/internal/compiler/program.go b/internal/compiler/program.go
index 90d13aaea85..7e9b1777f47 100644
--- a/internal/compiler/program.go
+++ b/internal/compiler/program.go
@@ -250,9 +250,8 @@ func (p *Program) UpdateProgram(changedFilePath tspath.Path, newHost CompilerHos
}
// If this file is part of a package redirect group (same package installed in multiple
// node_modules locations), we need to rebuild the program because the redirect targets
- // might need recalculation. A file is in a redirect group if it's either a canonical
- // file that others redirect to, or if it redirects to another file.
- if _, ok := p.deduplicatedPathMap[changedFilePath]; ok {
+ // might need recalculation.
+ if p.deduplicatedPaths.Has(changedFilePath) {
// File is either a canonical file or a redirect target; either way, need full rebuild
return NewProgram(newOpts), false
}
From a05a21e754e404e18830351c44d7588f1991e6e0 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Fri, 9 Jan 2026 13:44:04 -0800
Subject: [PATCH 28/29] oops
---
internal/compiler/fileloader.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/internal/compiler/fileloader.go b/internal/compiler/fileloader.go
index 12bd88e2f57..2d9e722bedd 100644
--- a/internal/compiler/fileloader.go
+++ b/internal/compiler/fileloader.go
@@ -71,8 +71,8 @@ type processedFiles struct {
// Key is a file path. Value is the list of files that redirect to it (same package, different install location)
redirectTargetsMap map[tspath.Path][]string
// Any paths involved in deduplication, including canonical paths and redirected paths
- deduplicatedPaths collections.Set[tspath.Path]
- finishedProcessing bool
+ deduplicatedPaths collections.Set[tspath.Path]
+ finishedProcessing bool
}
type jsxRuntimeImportSpecifier struct {
From ce80a21006e08016f88b606d7f4dd3f928d3e07b Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Fri, 9 Jan 2026 13:56:28 -0800
Subject: [PATCH 29/29] Switch to --deduplicatePackages, default true
---
internal/compiler/filesparser.go | 2 +-
internal/core/compileroptions.go | 2 +-
internal/diagnostics/diagnostics_generated.go | 6 +++---
internal/diagnostics/extraDiagnosticMessages.json | 2 +-
internal/tsoptions/declscompiler.go | 6 +++---
internal/tsoptions/parsinghelpers.go | 4 ++--
...duplicatePackages(deduplicatepackages=false).errors.txt} | 0
...js => deduplicatePackages(deduplicatepackages=false).js} | 2 +-
... deduplicatePackages(deduplicatepackages=false).symbols} | 2 +-
...=> deduplicatePackages(deduplicatepackages=false).types} | 2 +-
...eduplicatePackages(deduplicatepackages=true).errors.txt} | 0
....js => deduplicatePackages(deduplicatepackages=true).js} | 2 +-
...> deduplicatePackages(deduplicatepackages=true).symbols} | 2 +-
... => deduplicatePackages(deduplicatepackages=true).types} | 2 +-
testdata/baselines/reference/tsbuild/commandLine/help.js | 6 +++---
testdata/baselines/reference/tsbuild/commandLine/locale.js | 6 +++---
testdata/baselines/reference/tsc/commandLine/help-all.js | 6 +++---
...isablePackageDeduplication.ts => deduplicatePackages.ts} | 2 +-
18 files changed, 27 insertions(+), 27 deletions(-)
rename testdata/baselines/reference/compiler/{disablePackageDeduplication(disablepackagededuplication=true).errors.txt => deduplicatePackages(deduplicatepackages=false).errors.txt} (100%)
rename testdata/baselines/reference/compiler/{disablePackageDeduplication(disablepackagededuplication=false).js => deduplicatePackages(deduplicatepackages=false).js} (91%)
rename testdata/baselines/reference/compiler/{disablePackageDeduplication(disablepackagededuplication=true).symbols => deduplicatePackages(deduplicatepackages=false).symbols} (91%)
rename testdata/baselines/reference/compiler/{disablePackageDeduplication(disablepackagededuplication=true).types => deduplicatePackages(deduplicatepackages=false).types} (90%)
rename testdata/baselines/reference/compiler/{disablePackageDeduplication(disablepackagededuplication=false).errors.txt => deduplicatePackages(deduplicatepackages=true).errors.txt} (100%)
rename testdata/baselines/reference/compiler/{disablePackageDeduplication(disablepackagededuplication=true).js => deduplicatePackages(deduplicatepackages=true).js} (91%)
rename testdata/baselines/reference/compiler/{disablePackageDeduplication(disablepackagededuplication=false).symbols => deduplicatePackages(deduplicatepackages=true).symbols} (92%)
rename testdata/baselines/reference/compiler/{disablePackageDeduplication(disablepackagededuplication=false).types => deduplicatePackages(deduplicatepackages=true).types} (90%)
rename testdata/tests/cases/compiler/{disablePackageDeduplication.ts => deduplicatePackages.ts} (95%)
diff --git a/internal/compiler/filesparser.go b/internal/compiler/filesparser.go
index 350a4a0e3e8..4e963bd7d30 100644
--- a/internal/compiler/filesparser.go
+++ b/internal/compiler/filesparser.go
@@ -295,7 +295,7 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles {
var redirectTargetsMap map[tspath.Path][]string
var deduplicatedPaths collections.Set[tspath.Path]
var packageIdToCanonicalPath map[module.PackageId]tspath.Path
- if !loader.opts.Config.CompilerOptions().DisablePackageDeduplication.IsTrue() {
+ if !loader.opts.Config.CompilerOptions().DeduplicatePackages.IsFalse() {
redirectTargetsMap = make(map[tspath.Path][]string)
packageIdToCanonicalPath = make(map[module.PackageId]tspath.Path)
}
diff --git a/internal/core/compileroptions.go b/internal/core/compileroptions.go
index eef9e3bd8e6..b2abf707048 100644
--- a/internal/core/compileroptions.go
+++ b/internal/core/compileroptions.go
@@ -36,11 +36,11 @@ type CompilerOptions struct {
Declaration Tristate `json:"declaration,omitzero"`
DeclarationDir string `json:"declarationDir,omitzero"`
DeclarationMap Tristate `json:"declarationMap,omitzero"`
+ DeduplicatePackages Tristate `json:"deduplicatePackages,omitzero"`
DisableSizeLimit Tristate `json:"disableSizeLimit,omitzero"`
DisableSourceOfProjectReferenceRedirect Tristate `json:"disableSourceOfProjectReferenceRedirect,omitzero"`
DisableSolutionSearching Tristate `json:"disableSolutionSearching,omitzero"`
DisableReferencedProjectLoad Tristate `json:"disableReferencedProjectLoad,omitzero"`
- DisablePackageDeduplication Tristate `json:"disablePackageDeduplication,omitzero"`
ErasableSyntaxOnly Tristate `json:"erasableSyntaxOnly,omitzero"`
ESModuleInterop Tristate `json:"esModuleInterop,omitzero"`
ExactOptionalPropertyTypes Tristate `json:"exactOptionalPropertyTypes,omitzero"`
diff --git a/internal/diagnostics/diagnostics_generated.go b/internal/diagnostics/diagnostics_generated.go
index a84cb592483..26789f741c9 100644
--- a/internal/diagnostics/diagnostics_generated.go
+++ b/internal/diagnostics/diagnostics_generated.go
@@ -4276,7 +4276,7 @@ var Set_the_number_of_projects_to_build_concurrently = &Message{code: 100009, ca
var X_all_unless_singleThreaded_is_passed = &Message{code: 100010, category: CategoryMessage, key: "all_unless_singleThreaded_is_passed_100010", text: "all, unless --singleThreaded is passed."}
-var Disable_deduplication_of_packages_with_the_same_name_and_version = &Message{code: 100011, category: CategoryMessage, key: "Disable_deduplication_of_packages_with_the_same_name_and_version_100011", text: "Disable deduplication of packages with the same name and version."}
+var Deduplicate_packages_with_the_same_name_and_version = &Message{code: 100011, category: CategoryMessage, key: "Deduplicate_packages_with_the_same_name_and_version_100011", text: "Deduplicate packages with the same name and version."}
func keyToMessage(key Key) *Message {
switch key {
@@ -8554,8 +8554,8 @@ func keyToMessage(key Key) *Message {
return Set_the_number_of_projects_to_build_concurrently
case "all_unless_singleThreaded_is_passed_100010":
return X_all_unless_singleThreaded_is_passed
- case "Disable_deduplication_of_packages_with_the_same_name_and_version_100011":
- return Disable_deduplication_of_packages_with_the_same_name_and_version
+ case "Deduplicate_packages_with_the_same_name_and_version_100011":
+ return Deduplicate_packages_with_the_same_name_and_version
default:
return nil
}
diff --git a/internal/diagnostics/extraDiagnosticMessages.json b/internal/diagnostics/extraDiagnosticMessages.json
index 4c51b0c450b..2bd4640ff26 100644
--- a/internal/diagnostics/extraDiagnosticMessages.json
+++ b/internal/diagnostics/extraDiagnosticMessages.json
@@ -87,7 +87,7 @@
"category": "Error",
"code": 5002
},
- "Disable deduplication of packages with the same name and version.": {
+ "Deduplicate packages with the same name and version.": {
"category": "Message",
"code": 100011
}
diff --git a/internal/tsoptions/declscompiler.go b/internal/tsoptions/declscompiler.go
index b1a0d3c4a34..ac16be0b7b5 100644
--- a/internal/tsoptions/declscompiler.go
+++ b/internal/tsoptions/declscompiler.go
@@ -186,11 +186,11 @@ var commonOptionsWithBuild = []*CommandLineOption{
// Not setting affectsSemanticDiagnostics or affectsBuildInfo because we dont want all diagnostics to go away, its handled in builder
},
{
- Name: "disablePackageDeduplication",
+ Name: "deduplicatePackages",
Kind: CommandLineOptionTypeBoolean,
Category: diagnostics.Type_Checking,
- Description: diagnostics.Disable_deduplication_of_packages_with_the_same_name_and_version,
- DefaultValueDescription: false,
+ Description: diagnostics.Deduplicate_packages_with_the_same_name_and_version,
+ DefaultValueDescription: true,
AffectsProgramStructure: true,
},
{
diff --git a/internal/tsoptions/parsinghelpers.go b/internal/tsoptions/parsinghelpers.go
index 3006411d8ce..a631b28d1ed 100644
--- a/internal/tsoptions/parsinghelpers.go
+++ b/internal/tsoptions/parsinghelpers.go
@@ -217,6 +217,8 @@ func parseCompilerOptions(key string, value any, allOptions *core.CompilerOption
allOptions.Composite = ParseTristate(value)
case "declarationDir":
allOptions.DeclarationDir = ParseString(value)
+ case "deduplicatePackages":
+ allOptions.DeduplicatePackages = ParseTristate(value)
case "diagnostics":
allOptions.Diagnostics = ParseTristate(value)
case "disableSizeLimit":
@@ -227,8 +229,6 @@ func parseCompilerOptions(key string, value any, allOptions *core.CompilerOption
allOptions.DisableSolutionSearching = ParseTristate(value)
case "disableReferencedProjectLoad":
allOptions.DisableReferencedProjectLoad = ParseTristate(value)
- case "disablePackageDeduplication":
- allOptions.DisablePackageDeduplication = ParseTristate(value)
case "declarationMap":
allOptions.DeclarationMap = ParseTristate(value)
case "declaration":
diff --git a/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).errors.txt b/testdata/baselines/reference/compiler/deduplicatePackages(deduplicatepackages=false).errors.txt
similarity index 100%
rename from testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).errors.txt
rename to testdata/baselines/reference/compiler/deduplicatePackages(deduplicatepackages=false).errors.txt
diff --git a/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).js b/testdata/baselines/reference/compiler/deduplicatePackages(deduplicatepackages=false).js
similarity index 91%
rename from testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).js
rename to testdata/baselines/reference/compiler/deduplicatePackages(deduplicatepackages=false).js
index f56347541c5..4f0edc06126 100644
--- a/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).js
+++ b/testdata/baselines/reference/compiler/deduplicatePackages(deduplicatepackages=false).js
@@ -1,4 +1,4 @@
-//// [tests/cases/compiler/disablePackageDeduplication.ts] ////
+//// [tests/cases/compiler/deduplicatePackages.ts] ////
//// [index.d.ts]
import X from "x";
diff --git a/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).symbols b/testdata/baselines/reference/compiler/deduplicatePackages(deduplicatepackages=false).symbols
similarity index 91%
rename from testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).symbols
rename to testdata/baselines/reference/compiler/deduplicatePackages(deduplicatepackages=false).symbols
index 2450a099f00..e339c9b4602 100644
--- a/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).symbols
+++ b/testdata/baselines/reference/compiler/deduplicatePackages(deduplicatepackages=false).symbols
@@ -1,4 +1,4 @@
-//// [tests/cases/compiler/disablePackageDeduplication.ts] ////
+//// [tests/cases/compiler/deduplicatePackages.ts] ////
=== /src/a.ts ===
import { a } from "a";
diff --git a/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).types b/testdata/baselines/reference/compiler/deduplicatePackages(deduplicatepackages=false).types
similarity index 90%
rename from testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).types
rename to testdata/baselines/reference/compiler/deduplicatePackages(deduplicatepackages=false).types
index e445464c768..8d0db6b7064 100644
--- a/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).types
+++ b/testdata/baselines/reference/compiler/deduplicatePackages(deduplicatepackages=false).types
@@ -1,4 +1,4 @@
-//// [tests/cases/compiler/disablePackageDeduplication.ts] ////
+//// [tests/cases/compiler/deduplicatePackages.ts] ////
=== /src/a.ts ===
import { a } from "a";
diff --git a/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).errors.txt b/testdata/baselines/reference/compiler/deduplicatePackages(deduplicatepackages=true).errors.txt
similarity index 100%
rename from testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).errors.txt
rename to testdata/baselines/reference/compiler/deduplicatePackages(deduplicatepackages=true).errors.txt
diff --git a/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).js b/testdata/baselines/reference/compiler/deduplicatePackages(deduplicatepackages=true).js
similarity index 91%
rename from testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).js
rename to testdata/baselines/reference/compiler/deduplicatePackages(deduplicatepackages=true).js
index f56347541c5..4f0edc06126 100644
--- a/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=true).js
+++ b/testdata/baselines/reference/compiler/deduplicatePackages(deduplicatepackages=true).js
@@ -1,4 +1,4 @@
-//// [tests/cases/compiler/disablePackageDeduplication.ts] ////
+//// [tests/cases/compiler/deduplicatePackages.ts] ////
//// [index.d.ts]
import X from "x";
diff --git a/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).symbols b/testdata/baselines/reference/compiler/deduplicatePackages(deduplicatepackages=true).symbols
similarity index 92%
rename from testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).symbols
rename to testdata/baselines/reference/compiler/deduplicatePackages(deduplicatepackages=true).symbols
index e75f610301f..6b82e45fce8 100644
--- a/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).symbols
+++ b/testdata/baselines/reference/compiler/deduplicatePackages(deduplicatepackages=true).symbols
@@ -1,4 +1,4 @@
-//// [tests/cases/compiler/disablePackageDeduplication.ts] ////
+//// [tests/cases/compiler/deduplicatePackages.ts] ////
=== /src/a.ts ===
import { a } from "a";
diff --git a/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).types b/testdata/baselines/reference/compiler/deduplicatePackages(deduplicatepackages=true).types
similarity index 90%
rename from testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).types
rename to testdata/baselines/reference/compiler/deduplicatePackages(deduplicatepackages=true).types
index 46b6a2e4e5c..35169543ee0 100644
--- a/testdata/baselines/reference/compiler/disablePackageDeduplication(disablepackagededuplication=false).types
+++ b/testdata/baselines/reference/compiler/deduplicatePackages(deduplicatepackages=true).types
@@ -1,4 +1,4 @@
-//// [tests/cases/compiler/disablePackageDeduplication.ts] ////
+//// [tests/cases/compiler/deduplicatePackages.ts] ////
=== /src/a.ts ===
import { a } from "a";
diff --git a/testdata/baselines/reference/tsbuild/commandLine/help.js b/testdata/baselines/reference/tsbuild/commandLine/help.js
index e265cee7a4b..37d3b89b958 100644
--- a/testdata/baselines/reference/tsbuild/commandLine/help.js
+++ b/testdata/baselines/reference/tsbuild/commandLine/help.js
@@ -104,10 +104,10 @@ Disable full type checking (only critical parse and emit errors will be reported
type: boolean
default: false
-[94m--disablePackageDeduplication[39m
-Disable deduplication of packages with the same name and version.
+[94m--deduplicatePackages[39m
+Deduplicate packages with the same name and version.
type: boolean
-default: false
+default: true
[94m--noEmit[39m
Disable emitting files from a compilation.
diff --git a/testdata/baselines/reference/tsbuild/commandLine/locale.js b/testdata/baselines/reference/tsbuild/commandLine/locale.js
index 002c429519d..bbdd99f5836 100644
--- a/testdata/baselines/reference/tsbuild/commandLine/locale.js
+++ b/testdata/baselines/reference/tsbuild/commandLine/locale.js
@@ -104,10 +104,10 @@ Disable full type checking (only critical parse and emit errors will be reported
type: boolean
default: false
-[94m--disablePackageDeduplication[39m
-Disable deduplication of packages with the same name and version.
+[94m--deduplicatePackages[39m
+Deduplicate packages with the same name and version.
type: boolean
-default: false
+default: true
[94m--noEmit[39m
Disable emitting files from a compilation.
diff --git a/testdata/baselines/reference/tsc/commandLine/help-all.js b/testdata/baselines/reference/tsc/commandLine/help-all.js
index 426564f638d..bd4ff598e3b 100644
--- a/testdata/baselines/reference/tsc/commandLine/help-all.js
+++ b/testdata/baselines/reference/tsc/commandLine/help-all.js
@@ -221,10 +221,10 @@ Ensure 'use strict' is always emitted.
type: boolean
default: `false`, unless `strict` is set
-[94m--disablePackageDeduplication[39m
-Disable deduplication of packages with the same name and version.
+[94m--deduplicatePackages[39m
+Deduplicate packages with the same name and version.
type: boolean
-default: false
+default: true
[94m--exactOptionalPropertyTypes[39m
Interpret optional property types as written, rather than adding 'undefined'.
diff --git a/testdata/tests/cases/compiler/disablePackageDeduplication.ts b/testdata/tests/cases/compiler/deduplicatePackages.ts
similarity index 95%
rename from testdata/tests/cases/compiler/disablePackageDeduplication.ts
rename to testdata/tests/cases/compiler/deduplicatePackages.ts
index 11aaf510a01..684e9283e36 100644
--- a/testdata/tests/cases/compiler/disablePackageDeduplication.ts
+++ b/testdata/tests/cases/compiler/deduplicatePackages.ts
@@ -1,5 +1,5 @@
// @noImplicitReferences: true
-// @disablePackageDeduplication: true,false
+// @deduplicatePackages: true,false
// @Filename: /node_modules/a/index.d.ts
import X from "x";