Skip to content

Commit

Permalink
Merge pull request #26377 from Microsoft/moduleResolutionWithSymLinks
Browse files Browse the repository at this point in the history
Use original path from resolved module cache when available.
  • Loading branch information
sheetalkamat committed Aug 11, 2018
2 parents c5b74db + 8e05f4e commit fe387cc
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 20 deletions.
37 changes: 21 additions & 16 deletions src/compiler/moduleNameResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ namespace ts {
path: string;
extension: Extension;
packageId: PackageId | undefined;
/**
* When the resolved is not created from cache, the value is
* - string if original Path if it is symbolic link to the resolved path
* - undefined if path is not a symbolic link
* When the resolved is created using value from cache of ResolvedModuleWithFailedLookupLocations, the value is:
* - string if original Path if it is symbolic link to the resolved path
* - true if path is not a symbolic link - this indicates that the originalPath calculation is already done and needs to be skipped
*/
originalPath?: string | true;
}

/** Result of trying to resolve a module at a file. Needs to have 'packageId' added later. */
Expand Down Expand Up @@ -62,9 +71,9 @@ namespace ts {
return { fileName: resolved.path, packageId: resolved.packageId };
}

function createResolvedModuleWithFailedLookupLocations(resolved: Resolved | undefined, originalPath: string | undefined, isExternalLibraryImport: boolean, failedLookupLocations: string[]): ResolvedModuleWithFailedLookupLocations {
function createResolvedModuleWithFailedLookupLocations(resolved: Resolved | undefined, isExternalLibraryImport: boolean, failedLookupLocations: string[]): ResolvedModuleWithFailedLookupLocations {
return {
resolvedModule: resolved && { resolvedFileName: resolved.path, originalPath, extension: resolved.extension, isExternalLibraryImport, packageId: resolved.packageId },
resolvedModule: resolved && { resolvedFileName: resolved.path, originalPath: resolved.originalPath === true ? undefined : resolved.originalPath, extension: resolved.extension, isExternalLibraryImport, packageId: resolved.packageId },
failedLookupLocations
};
}
Expand Down Expand Up @@ -751,12 +760,12 @@ namespace ts {
tryResolve(Extensions.JavaScript) ||
(compilerOptions.resolveJsonModule ? tryResolve(Extensions.Json) : undefined));
if (result && result.value) {
const { resolved, originalPath, isExternalLibraryImport } = result.value;
return createResolvedModuleWithFailedLookupLocations(resolved, originalPath, isExternalLibraryImport, failedLookupLocations);
const { resolved, isExternalLibraryImport } = result.value;
return createResolvedModuleWithFailedLookupLocations(resolved, isExternalLibraryImport, failedLookupLocations);
}
return { resolvedModule: undefined, failedLookupLocations };

function tryResolve(extensions: Extensions): SearchResult<{ resolved: Resolved, originalPath?: string, isExternalLibraryImport: boolean }> {
function tryResolve(extensions: Extensions): SearchResult<{ resolved: Resolved, isExternalLibraryImport: boolean }> {
const loader: ResolutionKindSpecificLoader = (extensions, candidate, failedLookupLocations, onlyRecordFailures, state) => nodeLoadModuleByRelativeName(extensions, candidate, failedLookupLocations, onlyRecordFailures, state, /*considerPackageJson*/ true);
const resolved = tryLoadModuleUsingOptionalResolutionSettings(extensions, moduleName, containingDirectory, loader, failedLookupLocations, state);
if (resolved) {
Expand All @@ -771,17 +780,13 @@ namespace ts {
if (!resolved) return undefined;

let resolvedValue = resolved.value;
let originalPath: string | undefined;
if (!compilerOptions.preserveSymlinks && resolvedValue) {
originalPath = resolvedValue.path;
if (!compilerOptions.preserveSymlinks && resolvedValue && !resolvedValue.originalPath) {
const path = realPath(resolvedValue.path, host, traceEnabled);
if (path === originalPath) {
originalPath = undefined;
}
resolvedValue = { ...resolvedValue, path };
const originalPath = path === resolvedValue.path ? undefined : resolvedValue.path;
resolvedValue = { ...resolvedValue, path, originalPath };
}
// For node_modules lookups, get the real path so that multiple accesses to an `npm link`-ed module do not create duplicate files.
return { value: resolvedValue && { resolved: resolvedValue, originalPath, isExternalLibraryImport: true } };
return { value: resolvedValue && { resolved: resolvedValue, isExternalLibraryImport: true } };
}
else {
const { path: candidate, parts } = normalizePathAndParts(combinePaths(containingDirectory, moduleName));
Expand Down Expand Up @@ -1231,7 +1236,7 @@ namespace ts {
trace(host, Diagnostics.Resolution_for_module_0_was_found_in_cache_from_location_1, moduleName, containingDirectory);
}
failedLookupLocations.push(...result.failedLookupLocations);
return { value: result.resolvedModule && { path: result.resolvedModule.resolvedFileName, extension: result.resolvedModule.extension, packageId: result.resolvedModule.packageId } };
return { value: result.resolvedModule && { path: result.resolvedModule.resolvedFileName, originalPath: result.resolvedModule.originalPath || true, extension: result.resolvedModule.extension, packageId: result.resolvedModule.packageId } };
}
}

Expand All @@ -1243,7 +1248,7 @@ namespace ts {

const resolved = tryResolve(Extensions.TypeScript) || tryResolve(Extensions.JavaScript);
// No originalPath because classic resolution doesn't resolve realPath
return createResolvedModuleWithFailedLookupLocations(resolved && resolved.value, /*originalPath*/ undefined, /*isExternalLibraryImport*/ false, failedLookupLocations);
return createResolvedModuleWithFailedLookupLocations(resolved && resolved.value, /*isExternalLibraryImport*/ false, failedLookupLocations);

function tryResolve(extensions: Extensions): SearchResult<Resolved> {
const resolvedUsingSettings = tryLoadModuleUsingOptionalResolutionSettings(extensions, moduleName, containingDirectory, loadModuleFromFileNoPackageId, failedLookupLocations, state);
Expand Down Expand Up @@ -1290,7 +1295,7 @@ namespace ts {
const state: ModuleResolutionState = { compilerOptions, host, traceEnabled };
const failedLookupLocations: string[] = [];
const resolved = loadModuleFromNodeModulesOneLevel(Extensions.DtsOnly, moduleName, globalCache, failedLookupLocations, state);
return createResolvedModuleWithFailedLookupLocations(resolved, /*originalPath*/ undefined, /*isExternalLibraryImport*/ true, failedLookupLocations);
return createResolvedModuleWithFailedLookupLocations(resolved, /*isExternalLibraryImport*/ true, failedLookupLocations);
}

/**
Expand Down
17 changes: 17 additions & 0 deletions src/testRunner/unittests/moduleResolution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,23 @@ namespace ts {
resolution = resolveModuleName("a", "/foo.ts", compilerOptions, host, cache);
assert.isUndefined(resolution.resolvedModule, "lookup in parent directory doesn't hit the cache");
});

it("preserves originalPath on cache hit", () => {
const host = createModuleResolutionHost(
/*hasDirectoryExists*/ true,
{ name: "/linked/index.d.ts", symlinks: ["/app/node_modules/linked/index.d.ts"] },
{ name: "/app/node_modules/linked/package.json", content: '{"version": "0.0.0", "main": "./index"}' },
);
const cache = createModuleResolutionCache("/", (f) => f);
const compilerOptions: CompilerOptions = { moduleResolution: ModuleResolutionKind.NodeJs };
checkResolution(resolveModuleName("linked", "/app/src/app.ts", compilerOptions, host, cache));
checkResolution(resolveModuleName("linked", "/app/lib/main.ts", compilerOptions, host, cache));

function checkResolution(resolution: ResolvedModuleWithFailedLookupLocations) {
checkResolvedModule(resolution.resolvedModule, createResolvedModule("/linked/index.d.ts", /*isExternalLibraryImport*/ true));
assert.strictEqual(resolution.resolvedModule!.originalPath, "/app/node_modules/linked/index.d.ts");
}
});
});

describe("Module resolution - relative imports", () => {
Expand Down
2 changes: 1 addition & 1 deletion src/testRunner/unittests/tsserverProjectSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8415,7 +8415,7 @@ new C();`
expectedTrace.push(`Loading module '${moduleName}' from 'node_modules' folder, target file type 'TypeScript'.`);
getExpectedMissedLocationResolutionTrace(host, expectedTrace, getDirectoryPath(file.path), module, moduleName, /*useNodeModules*/ true, cacheLocation);
expectedTrace.push(`Resolution for module '${moduleName}' was found in cache from location '${cacheLocation}'.`);
getExpectedResolutionTraceFooter(expectedTrace, module, moduleName, /*addRealPathTrace*/ true, /*ignoreModuleFileFound*/ true);
getExpectedResolutionTraceFooter(expectedTrace, module, moduleName, /*addRealPathTrace*/ false, /*ignoreModuleFileFound*/ true);
return expectedTrace;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,5 @@
"Explicitly specified module resolution kind: 'NodeJs'.",
"Loading module 'foo' from 'node_modules' folder, target file type 'TypeScript'.",
"Resolution for module 'foo' was found in cache from location '/a/b/c'.",
"Resolving real path for '/a/b/node_modules/foo.d.ts', result '/a/b/node_modules/foo.d.ts'.",
"======== Module name 'foo' was successfully resolved to '/a/b/node_modules/foo.d.ts'. ========"
]
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,5 @@
"Directory '/a/b/c/d/e/node_modules' does not exist, skipping all lookups in it.",
"Directory '/a/b/c/d/node_modules' does not exist, skipping all lookups in it.",
"Resolution for module 'foo' was found in cache from location '/a/b/c'.",
"Resolving real path for '/a/b/node_modules/foo.d.ts', result '/a/b/node_modules/foo.d.ts'.",
"======== Module name 'foo' was successfully resolved to '/a/b/node_modules/foo.d.ts'. ========"
]
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,5 @@
"Explicitly specified module resolution kind: 'NodeJs'.",
"Loading module 'foo' from 'node_modules' folder, target file type 'TypeScript'.",
"Resolution for module 'foo' was found in cache from location '/a/b'.",
"Resolving real path for '/a/b/node_modules/foo.d.ts', result '/a/b/node_modules/foo.d.ts'.",
"======== Module name 'foo' was successfully resolved to '/a/b/node_modules/foo.d.ts'. ========"
]

0 comments on commit fe387cc

Please sign in to comment.