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/12] 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 f5ae94746f..2114f3c564 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/12] 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 dc6e3a92cf..8de27591a1 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 9e9981afde..39f2d2b98b 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 f47943dc9e..642813b1a2 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 6dd76efa8d..c75b64190d 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 eeba5d48f4..9ab3791dde 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 590f425b3a..028a3d5f62 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 2f3ee4888b..96a621363a 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 3880078ee4..3cd5db8e87 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 27fef0f583..dd86ae732a 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 3880078ee4..3cd5db8e87 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 27fef0f583..dd86ae732a 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 e5390b7825..539fad4d83 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 59839558cf..1147e4d6c5 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 45b4bc5898..034091b199 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 3744082d9d..e121e2932d 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 e9cdbb7c50..098eebd9bc 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 44005f7815..c9827a6f33 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 710a6e7b26..914182b948 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 5cfa0d4d57..cef83d3a2d 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 83b1575d13..983c7cf391 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 01e668625c..3c2149a1b2 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/12] 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 8f5065c9be..02d7af92f1 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 db83af0745..b55d891e54 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 6fdbf008ea..bb9bb1f8ae 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 307e6ccd65..7a84b652b2 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 a9a6b47f3e..e643f9ab69 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 ad6a4ceeca..0ba810403c 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 14c2a9b0f4..a84cb59248 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 2566d6ff53..4c51b0c450 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 68c18aa71f..753c8e2e08 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 1550e1680d..a6bb2733d0 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 4a3763168a..3006411d8c 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 954f20ec44..0000000000 --- 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 58e5b61220..0000000000 --- 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 9f4f6d7f9b..0000000000 --- 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 6be7c60fc7..0000000000 --- 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 19026366ac..0000000000 --- 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 5fd8e03f0b..0000000000 --- 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 856a112bfc..e265cee7a4 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 +--disablePackageDeduplication +Disable deduplication of packages with the same name and version. +type: boolean +default: false + --noEmit 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 26271d5f01..002c429519 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 +--disablePackageDeduplication +Disable deduplication of packages with the same name and version. +type: boolean +default: false + --noEmit 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 5e561d8833..426564f638 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 +--disablePackageDeduplication +Disable deduplication of packages with the same name and version. +type: boolean +default: false + --exactOptionalPropertyTypes 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/12] 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 be20094672..290e2e66c7 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 0000000000..a2324c9269 --- /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 0000000000..f2710c3963 --- /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 0000000000..15df7ad52a --- /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/12] 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 02d7af92f1..b4bb178457 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/12] 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 b4bb178457..713acaec41 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 e643f9ab69..0eb9189059 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 753c8e2e08..d17c058f78 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/12] 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 bb9bb1f8ae..0b69107b01 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 7a84b652b2..92cda6e1c4 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 0eb9189059..97ecee2dee 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/12] 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 92cda6e1c4..9b25a8262d 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/12] 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 9b25a8262d..99d1c464a0 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/12] 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 99d1c464a0..904774560f 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/12] 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 5373890ae0..cbbe91801b 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 0000000000..a3aed4e54a --- /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/12] 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 713acaec41..f594f9de68 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