Skip to content

Commit 39ecfc0

Browse files
Andymhegazy
Andy
authored andcommitted
When loading a module from node_modules, get packageId even in the loadModuleFromFile case (#18185) (#18326)
* When loading a module from node_modules, get packageId even in the `loadModuleFromFile` case * Support packageId for <reference types> too
1 parent 14c860b commit 39ecfc0

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

@@ -874,38 +878,49 @@ namespace ts {
874878
return undefined;
875879
}
876880

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

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

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

911926
function loadModuleFromPackageJson(jsonContent: PackageJson, extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, state: ModuleResolutionState): PathAndExtension | undefined {
@@ -960,10 +975,18 @@ namespace ts {
960975
}
961976

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

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

969992
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
@@ -1423,7 +1423,7 @@ namespace ts {
14231423
}
14241424

14251425
function processRootFile(fileName: string, isDefaultLib: boolean) {
1426-
processSourceFile(normalizePath(fileName), isDefaultLib);
1426+
processSourceFile(normalizePath(fileName), isDefaultLib, /*packageId*/ undefined);
14271427
}
14281428

14291429
function fileReferenceIsEqualTo(a: FileReference, b: FileReference): boolean {
@@ -1589,9 +1589,9 @@ namespace ts {
15891589
}
15901590

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

16751675
if (packageId) {
1676-
const packageIdKey = `${packageId.name}@${packageId.version}`;
1676+
const packageIdKey = `${packageId.name}/${packageId.subModuleName}@${packageId.version}`;
16771677
const fileFromPackageId = packageIdToSourceFile.get(packageIdKey);
16781678
if (fileFromPackageId) {
16791679
// Some other SourceFile already exists with this package name and version.
@@ -1733,7 +1733,7 @@ namespace ts {
17331733
function processReferencedFiles(file: SourceFile, isDefaultLib: boolean) {
17341734
forEach(file.referencedFiles, ref => {
17351735
const referencedFileName = resolveTripleslashReference(ref.fileName, file.fileName);
1736-
processSourceFile(referencedFileName, isDefaultLib, file, ref.pos, ref.end);
1736+
processSourceFile(referencedFileName, isDefaultLib, /*packageId*/ undefined, file, ref.pos, ref.end);
17371737
});
17381738
}
17391739

@@ -1764,7 +1764,7 @@ namespace ts {
17641764
if (resolvedTypeReferenceDirective) {
17651765
if (resolvedTypeReferenceDirective.primary) {
17661766
// resolved from the primary path
1767-
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, refFile, refPos, refEnd);
1767+
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, refFile, refPos, refEnd);
17681768
}
17691769
else {
17701770
// If we already resolved to this file, it must have been a secondary reference. Check file contents
@@ -1787,7 +1787,7 @@ namespace ts {
17871787
}
17881788
else {
17891789
// First resolution of this library
1790-
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, refFile, refPos, refEnd);
1790+
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, refFile, refPos, refEnd);
17911791
}
17921792
}
17931793
}

src/compiler/types.ts

+6
Original file line numberDiff line numberDiff line change
@@ -3977,6 +3977,11 @@ namespace ts {
39773977
* If accessing a non-index file, this should include its name e.g. "foo/bar".
39783978
*/
39793979
name: string;
3980+
/**
3981+
* Name of a submodule within this package.
3982+
* May be "".
3983+
*/
3984+
subModuleName: string;
39803985
/** Version of the package, e.g. "1.2.3" */
39813986
version: string;
39823987
}
@@ -4000,6 +4005,7 @@ namespace ts {
40004005
primary: boolean;
40014006
// The location of the .d.ts file we located, or undefined if resolution failed
40024007
resolvedFileName?: string;
4008+
packageId?: PackageId;
40034009
}
40044010

40054011
export interface ResolvedTypeReferenceDirectiveWithFailedLookupLocations {

src/compiler/utilities.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ namespace ts {
106106
}
107107

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

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

0 commit comments

Comments
 (0)