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
This commit is contained in:
Andy
2017-09-14 11:51:32 -07:00
committed by Mohamed Hegazy
parent 14c860b9cb
commit 39ecfc0db2
42 changed files with 493 additions and 123 deletions
+63 -40
View File
@@ -51,13 +51,17 @@ namespace ts {
DtsOnly /** Only '.d.ts' */
}
interface PathAndPackageId {
readonly fileName: string;
readonly packageId: PackageId;
}
/** Used with `Extensions.DtsOnly` to extract the path from TypeScript results. */
function resolvedTypeScriptOnly(resolved: Resolved | undefined): string | undefined {
function resolvedTypeScriptOnly(resolved: Resolved | undefined): PathAndPackageId | undefined {
if (!resolved) {
return undefined;
}
Debug.assert(extensionIsTypeScript(resolved.extension));
return resolved.path;
return { fileName: resolved.path, packageId: resolved.packageId };
}
function createResolvedModuleWithFailedLookupLocations(resolved: Resolved | undefined, isExternalLibraryImport: boolean, failedLookupLocations: string[]): ResolvedModuleWithFailedLookupLocations {
@@ -201,18 +205,18 @@ namespace ts {
let resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined;
if (resolved) {
if (!options.preserveSymlinks) {
resolved = realpath(resolved, host, traceEnabled);
resolved = { ...resolved, fileName: realpath(resolved.fileName, host, traceEnabled) };
}
if (traceEnabled) {
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolved, primary);
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolved.fileName, primary);
}
resolvedTypeReferenceDirective = { primary, resolvedFileName: resolved };
resolvedTypeReferenceDirective = { primary, resolvedFileName: resolved.fileName, packageId: resolved.packageId };
}
return { resolvedTypeReferenceDirective, failedLookupLocations };
function primaryLookup(): string | undefined {
function primaryLookup(): PathAndPackageId | undefined {
// Check primary library paths
if (typeRoots && typeRoots.length) {
if (traceEnabled) {
@@ -237,8 +241,8 @@ namespace ts {
}
}
function secondaryLookup(): string | undefined {
let resolvedFile: string;
function secondaryLookup(): PathAndPackageId | undefined {
let resolvedFile: PathAndPackageId;
const initialLocationForSecondaryLookup = containingFile && getDirectoryPath(containingFile);
if (initialLocationForSecondaryLookup !== undefined) {
@@ -675,7 +679,7 @@ namespace ts {
if (extension !== undefined) {
const path = tryFile(candidate, failedLookupLocations, /*onlyRecordFailures*/ false, state);
if (path !== undefined) {
return { path, extension, packageId: undefined };
return noPackageId({ path, ext: extension });
}
}
@@ -874,38 +878,49 @@ namespace ts {
return undefined;
}
function loadNodeModuleFromDirectory(extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState, considerPackageJson = true): Resolved | undefined {
const directoryExists = !onlyRecordFailures && directoryProbablyExists(candidate, state.host);
function loadNodeModuleFromDirectory(extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState, considerPackageJson = true) {
const { packageJsonContent, packageId } = considerPackageJson
? getPackageJsonInfo(candidate, "", failedLookupLocations, onlyRecordFailures, state)
: { packageJsonContent: undefined, packageId: undefined };
return withPackageId(packageId, loadNodeModuleFromDirectoryWorker(extensions, candidate, failedLookupLocations, onlyRecordFailures, state, packageJsonContent));
}
let packageId: PackageId | undefined;
if (considerPackageJson) {
const packageJsonPath = pathToPackageJson(candidate);
if (directoryExists && state.host.fileExists(packageJsonPath)) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Found_package_json_at_0, packageJsonPath);
}
const jsonContent = readJson(packageJsonPath, state.host);
if (typeof jsonContent.name === "string" && typeof jsonContent.version === "string") {
packageId = { name: jsonContent.name, version: jsonContent.version };
}
const fromPackageJson = loadModuleFromPackageJson(jsonContent, extensions, candidate, failedLookupLocations, state);
if (fromPackageJson) {
return withPackageId(packageId, fromPackageJson);
}
}
else {
if (directoryExists && state.traceEnabled) {
trace(state.host, Diagnostics.File_0_does_not_exist, packageJsonPath);
}
// record package json as one of failed lookup locations - in the future if this file will appear it will invalidate resolution results
failedLookupLocations.push(packageJsonPath);
}
function loadNodeModuleFromDirectoryWorker(extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState, packageJsonContent: PackageJson | undefined): PathAndExtension | undefined {
const fromPackageJson = packageJsonContent && loadModuleFromPackageJson(packageJsonContent, extensions, candidate, failedLookupLocations, state);
if (fromPackageJson) {
return fromPackageJson;
}
const directoryExists = !onlyRecordFailures && directoryProbablyExists(candidate, state.host);
return loadModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocations, !directoryExists, state);
}
return withPackageId(packageId, loadModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocations, !directoryExists, state));
function getPackageJsonInfo(
nodeModuleDirectory: string,
subModuleName: string,
failedLookupLocations: Push<string>,
onlyRecordFailures: boolean,
{ host, traceEnabled }: ModuleResolutionState,
): { packageJsonContent: PackageJson | undefined, packageId: PackageId | undefined } {
const directoryExists = !onlyRecordFailures && directoryProbablyExists(nodeModuleDirectory, host);
const packageJsonPath = pathToPackageJson(nodeModuleDirectory);
if (directoryExists && host.fileExists(packageJsonPath)) {
if (traceEnabled) {
trace(host, Diagnostics.Found_package_json_at_0, packageJsonPath);
}
const packageJsonContent = readJson(packageJsonPath, host);
const packageId: PackageId = typeof packageJsonContent.name === "string" && typeof packageJsonContent.version === "string"
? { name: packageJsonContent.name, subModuleName, version: packageJsonContent.version }
: undefined;
return { packageJsonContent, packageId };
}
else {
if (directoryExists && traceEnabled) {
trace(host, Diagnostics.File_0_does_not_exist, packageJsonPath);
}
// record package json as one of failed lookup locations - in the future if this file will appear it will invalidate resolution results
failedLookupLocations.push(packageJsonPath);
return { packageJsonContent: undefined, packageId: undefined };
}
}
function loadModuleFromPackageJson(jsonContent: PackageJson, extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, state: ModuleResolutionState): PathAndExtension | undefined {
@@ -960,10 +975,18 @@ namespace ts {
}
function loadModuleFromNodeModulesFolder(extensions: Extensions, moduleName: string, nodeModulesFolder: string, nodeModulesFolderExists: boolean, failedLookupLocations: Push<string>, state: ModuleResolutionState): Resolved | undefined {
const { top, rest } = getNameOfTopDirectory(moduleName);
const packageRootPath = combinePaths(nodeModulesFolder, top);
const { packageJsonContent, packageId } = getPackageJsonInfo(packageRootPath, rest, failedLookupLocations, !nodeModulesFolderExists, state);
const candidate = normalizePath(combinePaths(nodeModulesFolder, moduleName));
const pathAndExtension = loadModuleFromFile(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state) ||
loadNodeModuleFromDirectoryWorker(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state, packageJsonContent);
return withPackageId(packageId, pathAndExtension);
}
return loadModuleFromFileNoPackageId(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state) ||
loadNodeModuleFromDirectory(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state);
function getNameOfTopDirectory(name: string): { top: string, rest: string } {
const idx = name.indexOf(directorySeparator);
return idx === -1 ? { top: name, rest: "" } : { top: name.slice(0, idx), rest: name.slice(idx + 1) };
}
function loadModuleFromNodeModules(extensions: Extensions, moduleName: string, directory: string, failedLookupLocations: Push<string>, state: ModuleResolutionState, cache: NonRelativeModuleNameResolutionCache): SearchResult<Resolved> {
+7 -7
View File
@@ -1423,7 +1423,7 @@ namespace ts {
}
function processRootFile(fileName: string, isDefaultLib: boolean) {
processSourceFile(normalizePath(fileName), isDefaultLib);
processSourceFile(normalizePath(fileName), isDefaultLib, /*packageId*/ undefined);
}
function fileReferenceIsEqualTo(a: FileReference, b: FileReference): boolean {
@@ -1589,9 +1589,9 @@ namespace ts {
}
/** This has side effects through `findSourceFile`. */
function processSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number): void {
function processSourceFile(fileName: string, isDefaultLib: boolean, packageId: PackageId | undefined, refFile?: SourceFile, refPos?: number, refEnd?: number): void {
getSourceFileFromReferenceWorker(fileName,
fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, refFile, refPos, refEnd, /*packageId*/ undefined),
fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, refFile, refPos, refEnd, packageId),
(diagnostic, ...args) => {
fileProcessingDiagnostics.add(refFile !== undefined && refEnd !== undefined && refPos !== undefined
? createFileDiagnostic(refFile, refPos, refEnd - refPos, diagnostic, ...args)
@@ -1673,7 +1673,7 @@ namespace ts {
});
if (packageId) {
const packageIdKey = `${packageId.name}@${packageId.version}`;
const packageIdKey = `${packageId.name}/${packageId.subModuleName}@${packageId.version}`;
const fileFromPackageId = packageIdToSourceFile.get(packageIdKey);
if (fileFromPackageId) {
// Some other SourceFile already exists with this package name and version.
@@ -1733,7 +1733,7 @@ namespace ts {
function processReferencedFiles(file: SourceFile, isDefaultLib: boolean) {
forEach(file.referencedFiles, ref => {
const referencedFileName = resolveTripleslashReference(ref.fileName, file.fileName);
processSourceFile(referencedFileName, isDefaultLib, file, ref.pos, ref.end);
processSourceFile(referencedFileName, isDefaultLib, /*packageId*/ undefined, file, ref.pos, ref.end);
});
}
@@ -1764,7 +1764,7 @@ namespace ts {
if (resolvedTypeReferenceDirective) {
if (resolvedTypeReferenceDirective.primary) {
// resolved from the primary path
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, refFile, refPos, refEnd);
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, refFile, refPos, refEnd);
}
else {
// If we already resolved to this file, it must have been a secondary reference. Check file contents
@@ -1787,7 +1787,7 @@ namespace ts {
}
else {
// First resolution of this library
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, refFile, refPos, refEnd);
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, refFile, refPos, refEnd);
}
}
}
+6
View File
@@ -3977,6 +3977,11 @@ namespace ts {
* If accessing a non-index file, this should include its name e.g. "foo/bar".
*/
name: string;
/**
* Name of a submodule within this package.
* May be "".
*/
subModuleName: string;
/** Version of the package, e.g. "1.2.3" */
version: string;
}
@@ -4000,6 +4005,7 @@ namespace ts {
primary: boolean;
// The location of the .d.ts file we located, or undefined if resolution failed
resolvedFileName?: string;
packageId?: PackageId;
}
export interface ResolvedTypeReferenceDirectiveWithFailedLookupLocations {
+1 -1
View File
@@ -106,7 +106,7 @@ namespace ts {
}
function packageIdIsEqual(a: PackageId | undefined, b: PackageId | undefined): boolean {
return a === b || a && b && a.name === b.name && a.version === b.version;
return a === b || a && b && a.name === b.name && a.subModuleName === b.subModuleName && a.version === b.version;
}
export function typeDirectiveIsEqualTo(oldResolution: ResolvedTypeReferenceDirective, newResolution: ResolvedTypeReferenceDirective): boolean {
+17 -14
View File
@@ -198,33 +198,34 @@ namespace ts {
const moduleFile = { name: "/a/b/node_modules/foo.ts" };
const resolution = nodeModuleNameResolver("foo", containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, moduleFile));
checkResolvedModuleWithFailedLookupLocations(resolution, createResolvedModule(moduleFile.name, /*isExternalLibraryImport*/ true), [
"/a/b/c/d/node_modules/foo/package.json",
"/a/b/c/d/node_modules/foo.ts",
"/a/b/c/d/node_modules/foo.tsx",
"/a/b/c/d/node_modules/foo.d.ts",
"/a/b/c/d/node_modules/foo/package.json",
"/a/b/c/d/node_modules/foo/index.ts",
"/a/b/c/d/node_modules/foo/index.tsx",
"/a/b/c/d/node_modules/foo/index.d.ts",
"/a/b/c/d/node_modules/@types/foo.d.ts",
"/a/b/c/d/node_modules/@types/foo/package.json",
"/a/b/c/d/node_modules/@types/foo.d.ts",
"/a/b/c/d/node_modules/@types/foo/index.d.ts",
"/a/b/c/node_modules/foo/package.json",
"/a/b/c/node_modules/foo.ts",
"/a/b/c/node_modules/foo.tsx",
"/a/b/c/node_modules/foo.d.ts",
"/a/b/c/node_modules/foo/package.json",
"/a/b/c/node_modules/foo/index.ts",
"/a/b/c/node_modules/foo/index.tsx",
"/a/b/c/node_modules/foo/index.d.ts",
"/a/b/c/node_modules/@types/foo.d.ts",
"/a/b/c/node_modules/@types/foo/package.json",
"/a/b/c/node_modules/@types/foo.d.ts",
"/a/b/c/node_modules/@types/foo/index.d.ts",
"/a/b/node_modules/foo/package.json",
]);
}
});
@@ -250,52 +251,52 @@ namespace ts {
const moduleFile: File = { name: "/a/node_modules/foo/index.d.ts" };
const resolution = nodeModuleNameResolver("foo", containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, moduleFile));
checkResolvedModuleWithFailedLookupLocations(resolution, createResolvedModule(moduleFile.name, /*isExternalLibraryImport*/ true), [
"/a/node_modules/b/c/node_modules/d/node_modules/foo/package.json",
"/a/node_modules/b/c/node_modules/d/node_modules/foo.ts",
"/a/node_modules/b/c/node_modules/d/node_modules/foo.tsx",
"/a/node_modules/b/c/node_modules/d/node_modules/foo.d.ts",
"/a/node_modules/b/c/node_modules/d/node_modules/foo/package.json",
"/a/node_modules/b/c/node_modules/d/node_modules/foo/index.ts",
"/a/node_modules/b/c/node_modules/d/node_modules/foo/index.tsx",
"/a/node_modules/b/c/node_modules/d/node_modules/foo/index.d.ts",
"/a/node_modules/b/c/node_modules/d/node_modules/@types/foo.d.ts",
"/a/node_modules/b/c/node_modules/d/node_modules/@types/foo/package.json",
"/a/node_modules/b/c/node_modules/d/node_modules/@types/foo.d.ts",
"/a/node_modules/b/c/node_modules/d/node_modules/@types/foo/index.d.ts",
"/a/node_modules/b/c/node_modules/foo/package.json",
"/a/node_modules/b/c/node_modules/foo.ts",
"/a/node_modules/b/c/node_modules/foo.tsx",
"/a/node_modules/b/c/node_modules/foo.d.ts",
"/a/node_modules/b/c/node_modules/foo/package.json",
"/a/node_modules/b/c/node_modules/foo/index.ts",
"/a/node_modules/b/c/node_modules/foo/index.tsx",
"/a/node_modules/b/c/node_modules/foo/index.d.ts",
"/a/node_modules/b/c/node_modules/@types/foo.d.ts",
"/a/node_modules/b/c/node_modules/@types/foo/package.json",
"/a/node_modules/b/c/node_modules/@types/foo.d.ts",
"/a/node_modules/b/c/node_modules/@types/foo/index.d.ts",
"/a/node_modules/b/node_modules/foo/package.json",
"/a/node_modules/b/node_modules/foo.ts",
"/a/node_modules/b/node_modules/foo.tsx",
"/a/node_modules/b/node_modules/foo.d.ts",
"/a/node_modules/b/node_modules/foo/package.json",
"/a/node_modules/b/node_modules/foo/index.ts",
"/a/node_modules/b/node_modules/foo/index.tsx",
"/a/node_modules/b/node_modules/foo/index.d.ts",
"/a/node_modules/b/node_modules/@types/foo.d.ts",
"/a/node_modules/b/node_modules/@types/foo/package.json",
"/a/node_modules/b/node_modules/@types/foo.d.ts",
"/a/node_modules/b/node_modules/@types/foo/index.d.ts",
"/a/node_modules/foo/package.json",
"/a/node_modules/foo.ts",
"/a/node_modules/foo.tsx",
"/a/node_modules/foo.d.ts",
"/a/node_modules/foo/package.json",
"/a/node_modules/foo/index.ts",
"/a/node_modules/foo/index.tsx"
@@ -707,21 +708,23 @@ import b = require("./moduleB");
"/root/generated/file6/index.d.ts",
// fallback to standard node behavior
"/root/folder1/node_modules/file6/package.json",
// load from file
"/root/folder1/node_modules/file6.ts",
"/root/folder1/node_modules/file6.tsx",
"/root/folder1/node_modules/file6.d.ts",
// load from folder
"/root/folder1/node_modules/file6/package.json",
"/root/folder1/node_modules/file6/index.ts",
"/root/folder1/node_modules/file6/index.tsx",
"/root/folder1/node_modules/file6/index.d.ts",
"/root/folder1/node_modules/@types/file6.d.ts",
"/root/folder1/node_modules/@types/file6/package.json",
"/root/folder1/node_modules/@types/file6.d.ts",
"/root/folder1/node_modules/@types/file6/index.d.ts",
"/root/node_modules/file6/package.json",
// success on /root/node_modules/file6.ts
], /*isExternalLibraryImport*/ true);
+10 -10
View File
@@ -441,20 +441,20 @@ namespace ts {
"======== Resolving module 'a' from 'file1.ts'. ========",
"Explicitly specified module resolution kind: 'NodeJs'.",
"Loading module 'a' from 'node_modules' folder, target file type 'TypeScript'.",
"File 'node_modules/a/package.json' does not exist.",
"File 'node_modules/a.ts' does not exist.",
"File 'node_modules/a.tsx' does not exist.",
"File 'node_modules/a.d.ts' does not exist.",
"File 'node_modules/a/package.json' does not exist.",
"File 'node_modules/a/index.ts' does not exist.",
"File 'node_modules/a/index.tsx' does not exist.",
"File 'node_modules/a/index.d.ts' does not exist.",
"File 'node_modules/@types/a.d.ts' does not exist.",
"File 'node_modules/@types/a/package.json' does not exist.",
"File 'node_modules/@types/a.d.ts' does not exist.",
"File 'node_modules/@types/a/index.d.ts' does not exist.",
"Loading module 'a' from 'node_modules' folder, target file type 'JavaScript'.",
"File 'node_modules/a/package.json' does not exist.",
"File 'node_modules/a.js' does not exist.",
"File 'node_modules/a.jsx' does not exist.",
"File 'node_modules/a/package.json' does not exist.",
"File 'node_modules/a/index.js' does not exist.",
"File 'node_modules/a/index.jsx' does not exist.",
"======== Module name 'a' was not resolved. ========"
@@ -474,10 +474,10 @@ namespace ts {
"======== Resolving module 'a' from 'file1.ts'. ========",
"Explicitly specified module resolution kind: 'NodeJs'.",
"Loading module 'a' from 'node_modules' folder, target file type 'TypeScript'.",
"File 'node_modules/a/package.json' does not exist.",
"File 'node_modules/a.ts' does not exist.",
"File 'node_modules/a.tsx' does not exist.",
"File 'node_modules/a.d.ts' does not exist.",
"File 'node_modules/a/package.json' does not exist.",
"File 'node_modules/a/index.ts' does not exist.",
"File 'node_modules/a/index.tsx' does not exist.",
"File 'node_modules/a/index.d.ts' exist - use it as a name resolution result.",
@@ -510,14 +510,14 @@ namespace ts {
"File '/fs.ts' does not exist.",
"File '/fs.tsx' does not exist.",
"File '/fs.d.ts' does not exist.",
"File '/a/b/node_modules/@types/fs.d.ts' does not exist.",
"File '/a/b/node_modules/@types/fs/package.json' does not exist.",
"File '/a/b/node_modules/@types/fs.d.ts' does not exist.",
"File '/a/b/node_modules/@types/fs/index.d.ts' does not exist.",
"File '/a/node_modules/@types/fs.d.ts' does not exist.",
"File '/a/node_modules/@types/fs/package.json' does not exist.",
"File '/a/node_modules/@types/fs.d.ts' does not exist.",
"File '/a/node_modules/@types/fs/index.d.ts' does not exist.",
"File '/node_modules/@types/fs.d.ts' does not exist.",
"File '/node_modules/@types/fs/package.json' does not exist.",
"File '/node_modules/@types/fs.d.ts' does not exist.",
"File '/node_modules/@types/fs/index.d.ts' does not exist.",
"File '/a/b/fs.js' does not exist.",
"File '/a/b/fs.jsx' does not exist.",
@@ -552,14 +552,14 @@ namespace ts {
"File '/fs.ts' does not exist.",
"File '/fs.tsx' does not exist.",
"File '/fs.d.ts' does not exist.",
"File '/a/b/node_modules/@types/fs.d.ts' does not exist.",
"File '/a/b/node_modules/@types/fs/package.json' does not exist.",
"File '/a/b/node_modules/@types/fs.d.ts' does not exist.",
"File '/a/b/node_modules/@types/fs/index.d.ts' does not exist.",
"File '/a/node_modules/@types/fs.d.ts' does not exist.",
"File '/a/node_modules/@types/fs/package.json' does not exist.",
"File '/a/node_modules/@types/fs.d.ts' does not exist.",
"File '/a/node_modules/@types/fs/index.d.ts' does not exist.",
"File '/node_modules/@types/fs.d.ts' does not exist.",
"File '/node_modules/@types/fs/package.json' does not exist.",
"File '/node_modules/@types/fs.d.ts' does not exist.",
"File '/node_modules/@types/fs/index.d.ts' does not exist.",
"File '/a/b/fs.js' does not exist.",
"File '/a/b/fs.jsx' does not exist.",
@@ -2496,8 +2496,8 @@ namespace ts.projectSystem {
"======== Module name 'lib' was not resolved. ========",
`Auto discovery for typings is enabled in project '${proj.getProjectName()}'. Running extra resolution pass for module 'lib' using cache location '/a/cache'.`,
"File '/a/cache/node_modules/lib.d.ts' does not exist.",
"File '/a/cache/node_modules/@types/lib.d.ts' does not exist.",
"File '/a/cache/node_modules/@types/lib/package.json' does not exist.",
"File '/a/cache/node_modules/@types/lib.d.ts' does not exist.",
"File '/a/cache/node_modules/@types/lib/index.d.ts' exist - use it as a name resolution result.",
]);
checkProjectActualFiles(proj, [file1.path, lib.path]);