Skip to content

Commit

Permalink
Always use resolved file to figure out subModule name in package id
Browse files Browse the repository at this point in the history
Fixes #30429
  • Loading branch information
sheetalkamat committed May 22, 2019
1 parent 85d3c5d commit 5d188a8
Show file tree
Hide file tree
Showing 26 changed files with 154 additions and 238 deletions.
129 changes: 52 additions & 77 deletions src/compiler/moduleNameResolver.ts
Expand Up @@ -16,12 +16,23 @@ namespace ts {
push(value: T): void;
}

function withPackageId(packageId: PackageId | undefined, r: PathAndExtension | undefined): Resolved | undefined {
function withPackageId(packageInfo: PackageJsonInfo | undefined, r: PathAndExtension | undefined): Resolved | undefined {
let packageId: PackageId | undefined;
if (r && packageInfo) {
const packageJsonContent = packageInfo.packageJsonContent as PackageJson;
if (typeof packageJsonContent.name === "string" && typeof packageJsonContent.version === "string") {
packageId = {
name: packageJsonContent.name,
subModuleName: r.path.slice(packageInfo.packageDirectory.length + directorySeparator.length),
version: packageJsonContent.version
};
}
}
return r && { path: r.path, extension: r.ext, packageId };
}

function noPackageId(r: PathAndExtension | undefined): Resolved | undefined {
return withPackageId(/*packageId*/ undefined, r);
return withPackageId(/*packageInfo*/ undefined, r);
}

function removeIgnoredPackageId(r: Resolved | undefined): PathAndExtension | undefined {
Expand Down Expand Up @@ -978,10 +989,9 @@ namespace ts {
}
const resolvedFromFile = loadModuleFromFile(extensions, candidate, onlyRecordFailures, state);
if (resolvedFromFile) {
const nm = considerPackageJson ? parseNodeModuleFromPath(resolvedFromFile) : undefined;
const packageInfo = nm && getPackageJsonInfo(nm.packageDirectory, nm.subModuleName, /*onlyRecordFailures*/ false, state);
const packageId = packageInfo && packageInfo.packageId;
return withPackageId(packageId, resolvedFromFile);
const packageDirectory = considerPackageJson ? parseNodeModuleFromPath(resolvedFromFile) : undefined;
const packageInfo = packageDirectory ? getPackageJsonInfo(packageDirectory, /*onlyRecordFailures*/ false, state) : undefined;
return withPackageId(packageInfo, resolvedFromFile);
}
}
if (!onlyRecordFailures) {
Expand All @@ -1008,13 +1018,12 @@ namespace ts {
* (Not neeeded for `loadModuleFromNodeModules` as that looks up the `package.json` as part of resolution.)
*
* packageDirectory is the directory of the package itself.
* subModuleName is the path within the package.
* For `blah/node_modules/foo/index.d.ts` this is { packageDirectory: "foo", subModuleName: "index.d.ts" }. (Part before "/node_modules/" is ignored.)
* For `/node_modules/foo/bar.d.ts` this is { packageDirectory: "foo", subModuleName": "bar/index.d.ts" }.
* For `/node_modules/@types/foo/bar/index.d.ts` this is { packageDirectory: "@types/foo", subModuleName: "bar/index.d.ts" }.
* For `/node_modules/foo/bar/index.d.ts` this is { packageDirectory: "foo", subModuleName": "bar/index.d.ts" }.
* For `blah/node_modules/foo/index.d.ts` this is packageDirectory: "foo"
* For `/node_modules/foo/bar.d.ts` this is packageDirectory: "foo"
* For `/node_modules/@types/foo/bar/index.d.ts` this is packageDirectory: "@types/foo"
* For `/node_modules/foo/bar/index.d.ts` this is packageDirectory: "foo"
*/
function parseNodeModuleFromPath(resolved: PathAndExtension): { packageDirectory: string, subModuleName: string } | undefined {
function parseNodeModuleFromPath(resolved: PathAndExtension): string | undefined {
const path = normalizePath(resolved.path);
const idx = path.lastIndexOf(nodeModulesPathPart);
if (idx === -1) {
Expand All @@ -1026,29 +1035,14 @@ namespace ts {
if (path.charCodeAt(indexAfterNodeModules) === CharacterCodes.at) {
indexAfterPackageName = moveToNextDirectorySeparatorIfAvailable(path, indexAfterPackageName);
}
const packageDirectory = path.slice(0, indexAfterPackageName);
const subModuleName = removeExtension(path.slice(indexAfterPackageName + 1), resolved.ext) + Extension.Dts;
return { packageDirectory, subModuleName };
return path.slice(0, indexAfterPackageName);
}

function moveToNextDirectorySeparatorIfAvailable(path: string, prevSeparatorIndex: number): number {
const nextSeparatorIndex = path.indexOf(directorySeparator, prevSeparatorIndex + 1);
return nextSeparatorIndex === -1 ? prevSeparatorIndex : nextSeparatorIndex;
}

function addExtensionAndIndex(path: string): string {
if (path === "") {
return "index.d.ts";
}
if (endsWith(path, ".d.ts")) {
return path;
}
if (path === "index" || endsWith(path, "/index")) {
return path + ".d.ts";
}
return path + "/index.d.ts";
}

function loadModuleFromFileNoPackageId(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState): Resolved | undefined {
return noPackageId(loadModuleFromFile(extensions, candidate, onlyRecordFailures, state));
}
Expand Down Expand Up @@ -1129,56 +1123,29 @@ namespace ts {
}

function loadNodeModuleFromDirectory(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState, considerPackageJson = true) {
const packageInfo = considerPackageJson ? getPackageJsonInfo(candidate, "", onlyRecordFailures, state) : undefined;
const packageId = packageInfo && packageInfo.packageId;
const packageInfo = considerPackageJson ? getPackageJsonInfo(candidate, onlyRecordFailures, state) : undefined;
const packageJsonContent = packageInfo && packageInfo.packageJsonContent;
const versionPaths = packageInfo && packageInfo.versionPaths;
return withPackageId(packageId, loadNodeModuleFromDirectoryWorker(extensions, candidate, onlyRecordFailures, state, packageJsonContent, versionPaths));
return withPackageId(packageInfo, loadNodeModuleFromDirectoryWorker(extensions, candidate, onlyRecordFailures, state, packageJsonContent, versionPaths));
}

interface PackageJsonInfo {
packageJsonContent: PackageJsonPathFields | undefined;
packageId: PackageId | undefined;
packageDirectory: string;
packageJsonContent: PackageJsonPathFields;
versionPaths: VersionPaths | undefined;
}

function getPackageJsonInfo(packageDirectory: string, subModuleName: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PackageJsonInfo | undefined {
function getPackageJsonInfo(packageDirectory: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PackageJsonInfo | undefined {
const { host, traceEnabled } = state;
const directoryExists = !onlyRecordFailures && directoryProbablyExists(packageDirectory, host);
const packageJsonPath = combinePaths(packageDirectory, "package.json");
if (directoryExists && host.fileExists(packageJsonPath)) {
const packageJsonContent = readJson(packageJsonPath, host) as PackageJson;
if (subModuleName === "") { // looking up the root - need to handle types/typings/main redirects for subModuleName
const path = readPackageJsonTypesFields(packageJsonContent, packageDirectory, state);
if (typeof path === "string") {
subModuleName = addExtensionAndIndex(path.substring(packageDirectory.length + 1));
}
else {
const jsPath = readPackageJsonMainField(packageJsonContent, packageDirectory, state);
if (typeof jsPath === "string" && jsPath.length > packageDirectory.length) {
const potentialSubModule = jsPath.substring(packageDirectory.length + 1);
subModuleName = (forEach(supportedJSExtensions, extension =>
tryRemoveExtension(potentialSubModule, extension)) || potentialSubModule) + Extension.Dts;
}
else {
subModuleName = "index.d.ts";
}
}
}

if (!endsWith(subModuleName, Extension.Dts)) {
subModuleName = addExtensionAndIndex(subModuleName);
}

const versionPaths = readPackageJsonTypesVersionPaths(packageJsonContent, state);
const packageId: PackageId | undefined = typeof packageJsonContent.name === "string" && typeof packageJsonContent.version === "string"
? { name: packageJsonContent.name, subModuleName, version: packageJsonContent.version }
: undefined;
if (traceEnabled) {
trace(host, Diagnostics.Found_package_json_at_0, packageJsonPath);
}

return { packageJsonContent, packageId, versionPaths };
const versionPaths = readPackageJsonTypesVersionPaths(packageJsonContent, state);
return { packageDirectory, packageJsonContent, versionPaths };
}
else {
if (directoryExists && traceEnabled) {
Expand Down Expand Up @@ -1333,42 +1300,50 @@ namespace ts {
const candidate = normalizePath(combinePaths(nodeModulesDirectory, moduleName));

// First look for a nested package.json, as in `node_modules/foo/bar/package.json`.
let packageJsonContent: PackageJsonPathFields | undefined;
let packageId: PackageId | undefined;
let versionPaths: VersionPaths | undefined;

const packageInfo = getPackageJsonInfo(candidate, "", !nodeModulesDirectoryExists, state);
let packageInfo = getPackageJsonInfo(candidate, !nodeModulesDirectoryExists, state);
if (packageInfo) {
({ packageJsonContent, packageId, versionPaths } = packageInfo);
const fromFile = loadModuleFromFile(extensions, candidate, !nodeModulesDirectoryExists, state);
if (fromFile) {
return noPackageId(fromFile);
}

const fromDirectory = loadNodeModuleFromDirectoryWorker(extensions, candidate, !nodeModulesDirectoryExists, state, packageJsonContent, versionPaths);
return withPackageId(packageId, fromDirectory);
const fromDirectory = loadNodeModuleFromDirectoryWorker(
extensions,
candidate,
!nodeModulesDirectoryExists,
state,
packageInfo.packageJsonContent,
packageInfo.versionPaths
);
return withPackageId(packageInfo, fromDirectory);
}

const loader: ResolutionKindSpecificLoader = (extensions, candidate, onlyRecordFailures, state) => {
const pathAndExtension =
loadModuleFromFile(extensions, candidate, onlyRecordFailures, state) ||
loadNodeModuleFromDirectoryWorker(extensions, candidate, onlyRecordFailures, state, packageJsonContent, versionPaths);
return withPackageId(packageId, pathAndExtension);
loadNodeModuleFromDirectoryWorker(
extensions,
candidate,
onlyRecordFailures,
state,
packageInfo && packageInfo.packageJsonContent,
packageInfo && packageInfo.versionPaths
);
return withPackageId(packageInfo, pathAndExtension);
};

const { packageName, rest } = parsePackageName(moduleName);
if (rest !== "") { // If "rest" is empty, we just did this search above.
const packageDirectory = combinePaths(nodeModulesDirectory, packageName);

// Don't use a "types" or "main" from here because we're not loading the root, but a subdirectory -- just here for the packageId and path mappings.
const packageInfo = getPackageJsonInfo(packageDirectory, rest, !nodeModulesDirectoryExists, state);
if (packageInfo) ({ packageId, versionPaths } = packageInfo);
if (versionPaths) {
packageInfo = getPackageJsonInfo(packageDirectory, !nodeModulesDirectoryExists, state);
if (packageInfo && packageInfo.versionPaths) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.package_json_has_a_typesVersions_entry_0_that_matches_compiler_version_1_looking_for_a_pattern_to_match_module_name_2, versionPaths.version, version, rest);
trace(state.host, Diagnostics.package_json_has_a_typesVersions_entry_0_that_matches_compiler_version_1_looking_for_a_pattern_to_match_module_name_2, packageInfo.versionPaths.version, version, rest);
}
const packageDirectoryExists = nodeModulesDirectoryExists && directoryProbablyExists(packageDirectory, state.host);
const fromPaths = tryLoadModuleUsingPaths(extensions, rest, packageDirectory, versionPaths.paths, loader, !packageDirectoryExists, state);
const fromPaths = tryLoadModuleUsingPaths(extensions, rest, packageDirectory, packageInfo.versionPaths.paths, loader, !packageDirectoryExists, state);
if (fromPaths) {
return fromPaths.value;
}
Expand Down
Expand Up @@ -2,13 +2,13 @@
"======== Resolving module 'foo/use' from '/index.ts'. ========",
"Module resolution kind is not specified, using 'NodeJs'.",
"Loading module 'foo/use' from 'node_modules' folder, target file type 'TypeScript'.",
"'package.json' does not have a 'typesVersions' field.",
"Found 'package.json' at '/node_modules/foo/package.json'.",
"'package.json' does not have a 'typesVersions' field.",
"File '/node_modules/foo/use.ts' does not exist.",
"File '/node_modules/foo/use.tsx' does not exist.",
"File '/node_modules/foo/use.d.ts' exist - use it as a name resolution result.",
"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/use/index.d.ts@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 'NodeJs'.",
"Loading module 'a' from 'node_modules' folder, target file type 'TypeScript'.",
Expand All @@ -27,17 +27,14 @@
"File '/node_modules/foo/index.ts' does not exist.",
"File '/node_modules/foo/index.tsx' does not exist.",
"File '/node_modules/foo/index.d.ts' exist - use it as a name resolution result.",
"'package.json' does not have a 'typesVersions' field.",
"Found 'package.json' at '/node_modules/foo/package.json'.",
"'package.json' does not have a 'typesVersions' field.",
"======== Module name './index' was successfully resolved to '/node_modules/foo/index.d.ts' with Package ID 'foo/index.d.ts@1.2.3'. ========",
"======== Resolving module 'foo' from '/node_modules/a/index.d.ts'. ========",
"Module resolution kind is not specified, using 'NodeJs'.",
"Loading module 'foo' from 'node_modules' folder, target file type 'TypeScript'.",
"'package.json' does not have a 'typings' field.",
"'package.json' does not have a 'types' field.",
"'package.json' does not have a 'main' field.",
"'package.json' does not have a 'typesVersions' field.",
"Found 'package.json' at '/node_modules/a/node_modules/foo/package.json'.",
"'package.json' does not have a 'typesVersions' field.",
"File '/node_modules/a/node_modules/foo.ts' does not exist.",
"File '/node_modules/a/node_modules/foo.tsx' does not exist.",
"File '/node_modules/a/node_modules/foo.d.ts' does not exist.",
Expand Down
Expand Up @@ -2,13 +2,13 @@
"======== Resolving module '@foo/bar/use' from '/index.ts'. ========",
"Module resolution kind is not specified, using 'NodeJs'.",
"Loading module '@foo/bar/use' from 'node_modules' folder, target file type 'TypeScript'.",
"'package.json' does not have a 'typesVersions' field.",
"Found 'package.json' at '/node_modules/@foo/bar/package.json'.",
"'package.json' does not have a 'typesVersions' field.",
"File '/node_modules/@foo/bar/use.ts' does not exist.",
"File '/node_modules/@foo/bar/use.tsx' does not exist.",
"File '/node_modules/@foo/bar/use.d.ts' exist - use it as a name resolution result.",
"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/use/index.d.ts@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 'NodeJs'.",
"Loading module 'a' from 'node_modules' folder, target file type 'TypeScript'.",
Expand All @@ -27,17 +27,14 @@
"File '/node_modules/@foo/bar/index.ts' does not exist.",
"File '/node_modules/@foo/bar/index.tsx' does not exist.",
"File '/node_modules/@foo/bar/index.d.ts' exist - use it as a name resolution result.",
"'package.json' does not have a 'typesVersions' field.",
"Found 'package.json' at '/node_modules/@foo/bar/package.json'.",
"'package.json' does not have a 'typesVersions' field.",
"======== Module name './index' was successfully resolved to '/node_modules/@foo/bar/index.d.ts' with Package ID '@foo/bar/index.d.ts@1.2.3'. ========",
"======== Resolving module '@foo/bar' from '/node_modules/a/index.d.ts'. ========",
"Module resolution kind is not specified, using 'NodeJs'.",
"Loading module '@foo/bar' from 'node_modules' folder, target file type 'TypeScript'.",
"'package.json' does not have a 'typings' field.",
"'package.json' does not have a 'types' field.",
"'package.json' does not have a 'main' field.",
"'package.json' does not have a 'typesVersions' field.",
"Found 'package.json' at '/node_modules/a/node_modules/@foo/bar/package.json'.",
"'package.json' does not have a 'typesVersions' field.",
"File '/node_modules/a/node_modules/@foo/bar.ts' does not exist.",
"File '/node_modules/a/node_modules/@foo/bar.tsx' does not exist.",
"File '/node_modules/a/node_modules/@foo/bar.d.ts' does not exist.",
Expand Down

0 comments on commit 5d188a8

Please sign in to comment.