Skip to content

Commit fe387cc

Browse files
authored
Merge pull request #26377 from Microsoft/moduleResolutionWithSymLinks
Use original path from resolved module cache when available.
2 parents c5b74db + 8e05f4e commit fe387cc

File tree

6 files changed

+39
-20
lines changed

6 files changed

+39
-20
lines changed

src/compiler/moduleNameResolver.ts

+21-16
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ namespace ts {
2929
path: string;
3030
extension: Extension;
3131
packageId: PackageId | undefined;
32+
/**
33+
* When the resolved is not created from cache, the value is
34+
* - string if original Path if it is symbolic link to the resolved path
35+
* - undefined if path is not a symbolic link
36+
* When the resolved is created using value from cache of ResolvedModuleWithFailedLookupLocations, the value is:
37+
* - string if original Path if it is symbolic link to the resolved path
38+
* - true if path is not a symbolic link - this indicates that the originalPath calculation is already done and needs to be skipped
39+
*/
40+
originalPath?: string | true;
3241
}
3342

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

65-
function createResolvedModuleWithFailedLookupLocations(resolved: Resolved | undefined, originalPath: string | undefined, isExternalLibraryImport: boolean, failedLookupLocations: string[]): ResolvedModuleWithFailedLookupLocations {
74+
function createResolvedModuleWithFailedLookupLocations(resolved: Resolved | undefined, isExternalLibraryImport: boolean, failedLookupLocations: string[]): ResolvedModuleWithFailedLookupLocations {
6675
return {
67-
resolvedModule: resolved && { resolvedFileName: resolved.path, originalPath, extension: resolved.extension, isExternalLibraryImport, packageId: resolved.packageId },
76+
resolvedModule: resolved && { resolvedFileName: resolved.path, originalPath: resolved.originalPath === true ? undefined : resolved.originalPath, extension: resolved.extension, isExternalLibraryImport, packageId: resolved.packageId },
6877
failedLookupLocations
6978
};
7079
}
@@ -751,12 +760,12 @@ namespace ts {
751760
tryResolve(Extensions.JavaScript) ||
752761
(compilerOptions.resolveJsonModule ? tryResolve(Extensions.Json) : undefined));
753762
if (result && result.value) {
754-
const { resolved, originalPath, isExternalLibraryImport } = result.value;
755-
return createResolvedModuleWithFailedLookupLocations(resolved, originalPath, isExternalLibraryImport, failedLookupLocations);
763+
const { resolved, isExternalLibraryImport } = result.value;
764+
return createResolvedModuleWithFailedLookupLocations(resolved, isExternalLibraryImport, failedLookupLocations);
756765
}
757766
return { resolvedModule: undefined, failedLookupLocations };
758767

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

773782
let resolvedValue = resolved.value;
774-
let originalPath: string | undefined;
775-
if (!compilerOptions.preserveSymlinks && resolvedValue) {
776-
originalPath = resolvedValue.path;
783+
if (!compilerOptions.preserveSymlinks && resolvedValue && !resolvedValue.originalPath) {
777784
const path = realPath(resolvedValue.path, host, traceEnabled);
778-
if (path === originalPath) {
779-
originalPath = undefined;
780-
}
781-
resolvedValue = { ...resolvedValue, path };
785+
const originalPath = path === resolvedValue.path ? undefined : resolvedValue.path;
786+
resolvedValue = { ...resolvedValue, path, originalPath };
782787
}
783788
// For node_modules lookups, get the real path so that multiple accesses to an `npm link`-ed module do not create duplicate files.
784-
return { value: resolvedValue && { resolved: resolvedValue, originalPath, isExternalLibraryImport: true } };
789+
return { value: resolvedValue && { resolved: resolvedValue, isExternalLibraryImport: true } };
785790
}
786791
else {
787792
const { path: candidate, parts } = normalizePathAndParts(combinePaths(containingDirectory, moduleName));
@@ -1231,7 +1236,7 @@ namespace ts {
12311236
trace(host, Diagnostics.Resolution_for_module_0_was_found_in_cache_from_location_1, moduleName, containingDirectory);
12321237
}
12331238
failedLookupLocations.push(...result.failedLookupLocations);
1234-
return { value: result.resolvedModule && { path: result.resolvedModule.resolvedFileName, extension: result.resolvedModule.extension, packageId: result.resolvedModule.packageId } };
1239+
return { value: result.resolvedModule && { path: result.resolvedModule.resolvedFileName, originalPath: result.resolvedModule.originalPath || true, extension: result.resolvedModule.extension, packageId: result.resolvedModule.packageId } };
12351240
}
12361241
}
12371242

@@ -1243,7 +1248,7 @@ namespace ts {
12431248

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

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

12961301
/**

src/testRunner/unittests/moduleResolution.ts

+17
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,23 @@ namespace ts {
427427
resolution = resolveModuleName("a", "/foo.ts", compilerOptions, host, cache);
428428
assert.isUndefined(resolution.resolvedModule, "lookup in parent directory doesn't hit the cache");
429429
});
430+
431+
it("preserves originalPath on cache hit", () => {
432+
const host = createModuleResolutionHost(
433+
/*hasDirectoryExists*/ true,
434+
{ name: "/linked/index.d.ts", symlinks: ["/app/node_modules/linked/index.d.ts"] },
435+
{ name: "/app/node_modules/linked/package.json", content: '{"version": "0.0.0", "main": "./index"}' },
436+
);
437+
const cache = createModuleResolutionCache("/", (f) => f);
438+
const compilerOptions: CompilerOptions = { moduleResolution: ModuleResolutionKind.NodeJs };
439+
checkResolution(resolveModuleName("linked", "/app/src/app.ts", compilerOptions, host, cache));
440+
checkResolution(resolveModuleName("linked", "/app/lib/main.ts", compilerOptions, host, cache));
441+
442+
function checkResolution(resolution: ResolvedModuleWithFailedLookupLocations) {
443+
checkResolvedModule(resolution.resolvedModule, createResolvedModule("/linked/index.d.ts", /*isExternalLibraryImport*/ true));
444+
assert.strictEqual(resolution.resolvedModule!.originalPath, "/app/node_modules/linked/index.d.ts");
445+
}
446+
});
430447
});
431448

432449
describe("Module resolution - relative imports", () => {

src/testRunner/unittests/tsserverProjectSystem.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8415,7 +8415,7 @@ new C();`
84158415
expectedTrace.push(`Loading module '${moduleName}' from 'node_modules' folder, target file type 'TypeScript'.`);
84168416
getExpectedMissedLocationResolutionTrace(host, expectedTrace, getDirectoryPath(file.path), module, moduleName, /*useNodeModules*/ true, cacheLocation);
84178417
expectedTrace.push(`Resolution for module '${moduleName}' was found in cache from location '${cacheLocation}'.`);
8418-
getExpectedResolutionTraceFooter(expectedTrace, module, moduleName, /*addRealPathTrace*/ true, /*ignoreModuleFileFound*/ true);
8418+
getExpectedResolutionTraceFooter(expectedTrace, module, moduleName, /*addRealPathTrace*/ false, /*ignoreModuleFileFound*/ true);
84198419
return expectedTrace;
84208420
}
84218421

tests/baselines/reference/cachedModuleResolution1.trace.json

-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,5 @@
1414
"Explicitly specified module resolution kind: 'NodeJs'.",
1515
"Loading module 'foo' from 'node_modules' folder, target file type 'TypeScript'.",
1616
"Resolution for module 'foo' was found in cache from location '/a/b/c'.",
17-
"Resolving real path for '/a/b/node_modules/foo.d.ts', result '/a/b/node_modules/foo.d.ts'.",
1817
"======== Module name 'foo' was successfully resolved to '/a/b/node_modules/foo.d.ts'. ========"
1918
]

tests/baselines/reference/cachedModuleResolution2.trace.json

-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,5 @@
1414
"Directory '/a/b/c/d/e/node_modules' does not exist, skipping all lookups in it.",
1515
"Directory '/a/b/c/d/node_modules' does not exist, skipping all lookups in it.",
1616
"Resolution for module 'foo' was found in cache from location '/a/b/c'.",
17-
"Resolving real path for '/a/b/node_modules/foo.d.ts', result '/a/b/node_modules/foo.d.ts'.",
1817
"======== Module name 'foo' was successfully resolved to '/a/b/node_modules/foo.d.ts'. ========"
1918
]

tests/baselines/reference/cachedModuleResolution5.trace.json

-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,5 @@
1414
"Explicitly specified module resolution kind: 'NodeJs'.",
1515
"Loading module 'foo' from 'node_modules' folder, target file type 'TypeScript'.",
1616
"Resolution for module 'foo' was found in cache from location '/a/b'.",
17-
"Resolving real path for '/a/b/node_modules/foo.d.ts', result '/a/b/node_modules/foo.d.ts'.",
1817
"======== Module name 'foo' was successfully resolved to '/a/b/node_modules/foo.d.ts'. ========"
1918
]

0 commit comments

Comments
 (0)