Skip to content

Commit 2e02778

Browse files
author
Andy
authored
When loading a module from node_modules, get packageId even in the loadModuleFromFile case (#18185)
* When loading a module from node_modules, get packageId even in the `loadModuleFromFile` case * Support packageId for <reference types> too
1 parent ed4e2e6 commit 2e02778

File tree

42 files changed

+491
-121
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+491
-121
lines changed

src/compiler/moduleNameResolver.ts

+61-38
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,17 @@ namespace ts {
5151
DtsOnly /** Only '.d.ts' */
5252
}
5353

54+
interface PathAndPackageId {
55+
readonly fileName: string;
56+
readonly packageId: PackageId;
57+
}
5458
/** Used with `Extensions.DtsOnly` to extract the path from TypeScript results. */
55-
function resolvedTypeScriptOnly(resolved: Resolved | undefined): string | undefined {
59+
function resolvedTypeScriptOnly(resolved: Resolved | undefined): PathAndPackageId | undefined {
5660
if (!resolved) {
5761
return undefined;
5862
}
5963
Debug.assert(extensionIsTypeScript(resolved.extension));
60-
return resolved.path;
64+
return { fileName: resolved.path, packageId: resolved.packageId };
6165
}
6266

6367
function createResolvedModuleWithFailedLookupLocations(resolved: Resolved | undefined, isExternalLibraryImport: boolean, failedLookupLocations: string[]): ResolvedModuleWithFailedLookupLocations {
@@ -201,18 +205,18 @@ namespace ts {
201205
let resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined;
202206
if (resolved) {
203207
if (!options.preserveSymlinks) {
204-
resolved = realPath(resolved, host, traceEnabled);
208+
resolved = { ...resolved, fileName: realPath(resolved.fileName, host, traceEnabled) };
205209
}
206210

207211
if (traceEnabled) {
208-
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolved, primary);
212+
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolved.fileName, primary);
209213
}
210-
resolvedTypeReferenceDirective = { primary, resolvedFileName: resolved };
214+
resolvedTypeReferenceDirective = { primary, resolvedFileName: resolved.fileName, packageId: resolved.packageId };
211215
}
212216

213217
return { resolvedTypeReferenceDirective, failedLookupLocations };
214218

215-
function primaryLookup(): string | undefined {
219+
function primaryLookup(): PathAndPackageId | undefined {
216220
// Check primary library paths
217221
if (typeRoots && typeRoots.length) {
218222
if (traceEnabled) {
@@ -237,8 +241,8 @@ namespace ts {
237241
}
238242
}
239243

240-
function secondaryLookup(): string | undefined {
241-
let resolvedFile: string;
244+
function secondaryLookup(): PathAndPackageId | undefined {
245+
let resolvedFile: PathAndPackageId;
242246
const initialLocationForSecondaryLookup = containingFile && getDirectoryPath(containingFile);
243247

244248
if (initialLocationForSecondaryLookup !== undefined) {
@@ -675,7 +679,7 @@ namespace ts {
675679
if (extension !== undefined) {
676680
const path = tryFile(candidate, failedLookupLocations, /*onlyRecordFailures*/ false, state);
677681
if (path !== undefined) {
678-
return { path, extension, packageId: undefined };
682+
return noPackageId({ path, ext: extension });
679683
}
680684
}
681685

@@ -875,38 +879,49 @@ namespace ts {
875879
return undefined;
876880
}
877881

878-
function loadNodeModuleFromDirectory(extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState, considerPackageJson = true): Resolved | undefined {
879-
const directoryExists = !onlyRecordFailures && directoryProbablyExists(candidate, state.host);
880-
881-
let packageId: PackageId | undefined;
882-
883-
if (considerPackageJson) {
884-
const packageJsonPath = pathToPackageJson(candidate);
885-
if (directoryExists && state.host.fileExists(packageJsonPath)) {
886-
if (state.traceEnabled) {
887-
trace(state.host, Diagnostics.Found_package_json_at_0, packageJsonPath);
888-
}
889-
const jsonContent = readJson(packageJsonPath, state.host);
882+
function loadNodeModuleFromDirectory(extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState, considerPackageJson = true) {
883+
const { packageJsonContent, packageId } = considerPackageJson
884+
? getPackageJsonInfo(candidate, "", failedLookupLocations, onlyRecordFailures, state)
885+
: { packageJsonContent: undefined, packageId: undefined };
886+
return withPackageId(packageId, loadNodeModuleFromDirectoryWorker(extensions, candidate, failedLookupLocations, onlyRecordFailures, state, packageJsonContent));
887+
}
890888

891-
if (typeof jsonContent.name === "string" && typeof jsonContent.version === "string") {
892-
packageId = { name: jsonContent.name, version: jsonContent.version };
893-
}
889+
function loadNodeModuleFromDirectoryWorker(extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState, packageJsonContent: PackageJson | undefined): PathAndExtension | undefined {
890+
const fromPackageJson = packageJsonContent && loadModuleFromPackageJson(packageJsonContent, extensions, candidate, failedLookupLocations, state);
891+
if (fromPackageJson) {
892+
return fromPackageJson;
893+
}
894+
const directoryExists = !onlyRecordFailures && directoryProbablyExists(candidate, state.host);
895+
return loadModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocations, !directoryExists, state);
896+
}
894897

895-
const fromPackageJson = loadModuleFromPackageJson(jsonContent, extensions, candidate, failedLookupLocations, state);
896-
if (fromPackageJson) {
897-
return withPackageId(packageId, fromPackageJson);
898-
}
898+
function getPackageJsonInfo(
899+
nodeModuleDirectory: string,
900+
subModuleName: string,
901+
failedLookupLocations: Push<string>,
902+
onlyRecordFailures: boolean,
903+
{ host, traceEnabled }: ModuleResolutionState,
904+
): { packageJsonContent: PackageJson | undefined, packageId: PackageId | undefined } {
905+
const directoryExists = !onlyRecordFailures && directoryProbablyExists(nodeModuleDirectory, host);
906+
const packageJsonPath = pathToPackageJson(nodeModuleDirectory);
907+
if (directoryExists && host.fileExists(packageJsonPath)) {
908+
if (traceEnabled) {
909+
trace(host, Diagnostics.Found_package_json_at_0, packageJsonPath);
899910
}
900-
else {
901-
if (directoryExists && state.traceEnabled) {
902-
trace(state.host, Diagnostics.File_0_does_not_exist, packageJsonPath);
903-
}
904-
// record package json as one of failed lookup locations - in the future if this file will appear it will invalidate resolution results
905-
failedLookupLocations.push(packageJsonPath);
911+
const packageJsonContent = readJson(packageJsonPath, host);
912+
const packageId: PackageId = typeof packageJsonContent.name === "string" && typeof packageJsonContent.version === "string"
913+
? { name: packageJsonContent.name, subModuleName, version: packageJsonContent.version }
914+
: undefined;
915+
return { packageJsonContent, packageId };
916+
}
917+
else {
918+
if (directoryExists && traceEnabled) {
919+
trace(host, Diagnostics.File_0_does_not_exist, packageJsonPath);
906920
}
921+
// record package json as one of failed lookup locations - in the future if this file will appear it will invalidate resolution results
922+
failedLookupLocations.push(packageJsonPath);
923+
return { packageJsonContent: undefined, packageId: undefined };
907924
}
908-
909-
return withPackageId(packageId, loadModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocations, !directoryExists, state));
910925
}
911926

912927
function loadModuleFromPackageJson(jsonContent: PackageJson, extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, state: ModuleResolutionState): PathAndExtension | undefined {
@@ -961,10 +976,18 @@ namespace ts {
961976
}
962977

963978
function loadModuleFromNodeModulesFolder(extensions: Extensions, moduleName: string, nodeModulesFolder: string, nodeModulesFolderExists: boolean, failedLookupLocations: Push<string>, state: ModuleResolutionState): Resolved | undefined {
979+
const { top, rest } = getNameOfTopDirectory(moduleName);
980+
const packageRootPath = combinePaths(nodeModulesFolder, top);
981+
const { packageJsonContent, packageId } = getPackageJsonInfo(packageRootPath, rest, failedLookupLocations, !nodeModulesFolderExists, state);
964982
const candidate = normalizePath(combinePaths(nodeModulesFolder, moduleName));
983+
const pathAndExtension = loadModuleFromFile(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state) ||
984+
loadNodeModuleFromDirectoryWorker(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state, packageJsonContent);
985+
return withPackageId(packageId, pathAndExtension);
986+
}
965987

966-
return loadModuleFromFileNoPackageId(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state) ||
967-
loadNodeModuleFromDirectory(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state);
988+
function getNameOfTopDirectory(name: string): { top: string, rest: string } {
989+
const idx = name.indexOf(directorySeparator);
990+
return idx === -1 ? { top: name, rest: "" } : { top: name.slice(0, idx), rest: name.slice(idx + 1) };
968991
}
969992

970993
function loadModuleFromNodeModules(extensions: Extensions, moduleName: string, directory: string, failedLookupLocations: Push<string>, state: ModuleResolutionState, cache: NonRelativeModuleNameResolutionCache): SearchResult<Resolved> {

src/compiler/program.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -1427,7 +1427,7 @@ namespace ts {
14271427
}
14281428

14291429
function processRootFile(fileName: string, isDefaultLib: boolean) {
1430-
processSourceFile(normalizePath(fileName), isDefaultLib);
1430+
processSourceFile(normalizePath(fileName), isDefaultLib, /*packageId*/ undefined);
14311431
}
14321432

14331433
function fileReferenceIsEqualTo(a: FileReference, b: FileReference): boolean {
@@ -1591,9 +1591,9 @@ namespace ts {
15911591
}
15921592

15931593
/** This has side effects through `findSourceFile`. */
1594-
function processSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number): void {
1594+
function processSourceFile(fileName: string, isDefaultLib: boolean, packageId: PackageId | undefined, refFile?: SourceFile, refPos?: number, refEnd?: number): void {
15951595
getSourceFileFromReferenceWorker(fileName,
1596-
fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, refFile, refPos, refEnd, /*packageId*/ undefined),
1596+
fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, refFile, refPos, refEnd, packageId),
15971597
(diagnostic, ...args) => {
15981598
fileProcessingDiagnostics.add(refFile !== undefined && refEnd !== undefined && refPos !== undefined
15991599
? createFileDiagnostic(refFile, refPos, refEnd - refPos, diagnostic, ...args)
@@ -1675,7 +1675,7 @@ namespace ts {
16751675
});
16761676

16771677
if (packageId) {
1678-
const packageIdKey = `${packageId.name}@${packageId.version}`;
1678+
const packageIdKey = `${packageId.name}/${packageId.subModuleName}@${packageId.version}`;
16791679
const fileFromPackageId = packageIdToSourceFile.get(packageIdKey);
16801680
if (fileFromPackageId) {
16811681
// Some other SourceFile already exists with this package name and version.
@@ -1735,7 +1735,7 @@ namespace ts {
17351735
function processReferencedFiles(file: SourceFile, isDefaultLib: boolean) {
17361736
forEach(file.referencedFiles, ref => {
17371737
const referencedFileName = resolveTripleslashReference(ref.fileName, file.fileName);
1738-
processSourceFile(referencedFileName, isDefaultLib, file, ref.pos, ref.end);
1738+
processSourceFile(referencedFileName, isDefaultLib, /*packageId*/ undefined, file, ref.pos, ref.end);
17391739
});
17401740
}
17411741

@@ -1766,7 +1766,7 @@ namespace ts {
17661766
if (resolvedTypeReferenceDirective) {
17671767
if (resolvedTypeReferenceDirective.primary) {
17681768
// resolved from the primary path
1769-
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, refFile, refPos, refEnd);
1769+
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, refFile, refPos, refEnd);
17701770
}
17711771
else {
17721772
// If we already resolved to this file, it must have been a secondary reference. Check file contents
@@ -1789,7 +1789,7 @@ namespace ts {
17891789
}
17901790
else {
17911791
// First resolution of this library
1792-
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, refFile, refPos, refEnd);
1792+
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, refFile, refPos, refEnd);
17931793
}
17941794
}
17951795
}

src/compiler/types.ts

+6
Original file line numberDiff line numberDiff line change
@@ -4055,6 +4055,11 @@ namespace ts {
40554055
* If accessing a non-index file, this should include its name e.g. "foo/bar".
40564056
*/
40574057
name: string;
4058+
/**
4059+
* Name of a submodule within this package.
4060+
* May be "".
4061+
*/
4062+
subModuleName: string;
40584063
/** Version of the package, e.g. "1.2.3" */
40594064
version: string;
40604065
}
@@ -4078,6 +4083,7 @@ namespace ts {
40784083
primary: boolean;
40794084
// The location of the .d.ts file we located, or undefined if resolution failed
40804085
resolvedFileName?: string;
4086+
packageId?: PackageId;
40814087
}
40824088

40834089
export interface ResolvedTypeReferenceDirectiveWithFailedLookupLocations {

src/compiler/utilities.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ namespace ts {
104104
}
105105

106106
function packageIdIsEqual(a: PackageId | undefined, b: PackageId | undefined): boolean {
107-
return a === b || a && b && a.name === b.name && a.version === b.version;
107+
return a === b || a && b && a.name === b.name && a.subModuleName === b.subModuleName && a.version === b.version;
108108
}
109109

110110
export function typeDirectiveIsEqualTo(oldResolution: ResolvedTypeReferenceDirective, newResolution: ResolvedTypeReferenceDirective): boolean {

0 commit comments

Comments
 (0)