Pass in information for the module name resolution when resolutions from file are partially used (#49738)

* Test showing wrong resolution is returned because of incorrect mode calculation
Test for #48229

* Pass in information for the module name resolution when resolutions from file are partially used
Fixes #48229

* Make the resolution info complete
This commit is contained in:
Sheetal Nandi 2022-11-07 11:51:04 -08:00 committed by GitHub
parent fa4b49d541
commit 7b0df1ff99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 559 additions and 97 deletions

View File

@ -734,8 +734,9 @@ namespace ts {
/* @internal */
export function createModeAwareCache<T>(): ModeAwareCache<T> {
const underlying = new Map<string, T>();
const memoizedReverseKeys = new Map<string, [specifier: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined]>();
const underlying = new Map<ModeAwareCacheKey, T>();
type ModeAwareCacheKey = string & { __modeAwareCacheKey: any; };
const memoizedReverseKeys = new Map<ModeAwareCacheKey, [specifier: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined]>();
const cache: ModeAwareCache<T> = {
get(specifier, mode) {
@ -765,22 +766,30 @@ namespace ts {
return cache;
function getUnderlyingCacheKey(specifier: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined) {
const result = mode === undefined ? specifier : `${mode}|${specifier}`;
const result = (mode === undefined ? specifier : `${mode}|${specifier}`) as ModeAwareCacheKey;
memoizedReverseKeys.set(result, [specifier, mode]);
return result;
}
}
/* @internal */
export function zipToModeAwareCache<V>(file: SourceFile, keys: readonly string[] | readonly FileReference[], values: readonly V[]): ModeAwareCache<V> {
export function getResolutionName(entry: FileReference | StringLiteralLike) {
// We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
return isStringLiteralLike(entry) ? entry.text : entry.fileName.toLowerCase();
}
/* @internal */
export function getResolutionMode(entry: FileReference | StringLiteralLike, file: SourceFile) {
return isStringLiteralLike(entry) ? getModeForUsageLocation(file, entry) : entry.resolutionMode || file.impliedNodeFormat;
}
/* @internal */
export function zipToModeAwareCache<V>(file: SourceFile, keys: readonly StringLiteralLike[] | readonly FileReference[], values: readonly V[]): ModeAwareCache<V> {
Debug.assert(keys.length === values.length);
const map = createModeAwareCache<V>();
for (let i = 0; i < keys.length; ++i) {
const entry = keys[i];
// We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
const name = !isString(entry) ? entry.fileName.toLowerCase() : entry;
const mode = !isString(entry) ? entry.resolutionMode || file.impliedNodeFormat : getModeForResolutionAtIndex(file, i);
map.set(name, mode, values[i]);
map.set(getResolutionName(entry), getResolutionMode(entry, file), values[i]);
}
return map;
}

View File

@ -623,17 +623,20 @@ namespace ts {
}
/* @internal */
export function loadWithModeAwareCache<T>(names: string[], containingFile: SourceFile, containingFileName: string, redirectedReference: ResolvedProjectReference | undefined, loader: (name: string, resolverMode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined, containingFileName: string, redirectedReference: ResolvedProjectReference | undefined) => T): T[] {
export function loadWithModeAwareCache<T>(names: readonly StringLiteralLike[] | readonly string[], containingFile: SourceFile, containingFileName: string, redirectedReference: ResolvedProjectReference | undefined, resolutionInfo: ModuleResolutionInfo | undefined, loader: (name: string, resolverMode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined, containingFileName: string, redirectedReference: ResolvedProjectReference | undefined) => T): T[] {
if (names.length === 0) {
return [];
}
const resolutions: T[] = [];
const cache = new Map<string, T>();
let i = 0;
for (const name of names) {
for (const entry of resolutionInfo ? resolutionInfo.names : names) {
let result: T;
const mode = getModeForResolutionAtIndex(containingFile, i);
const mode = !isString(entry) ?
getModeForUsageLocation(containingFile, entry) :
getModeForResolutionAtIndex(containingFile, i);
i++;
const name = isString(entry) ? entry : entry.text;
const cacheKey = mode !== undefined ? `${mode}|${name}` : name;
if (cache.has(cacheKey)) {
result = cache.get(cacheKey)!;
@ -1098,24 +1101,41 @@ namespace ts {
let moduleResolutionCache: ModuleResolutionCache | undefined;
let typeReferenceDirectiveResolutionCache: TypeReferenceDirectiveResolutionCache | undefined;
let actualResolveModuleNamesWorker: (moduleNames: string[], containingFile: SourceFile, containingFileName: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference) => ResolvedModuleFull[];
let actualResolveModuleNamesWorker: (
moduleNames: readonly StringLiteralLike[],
containingFile: SourceFile,
containingFileName: string,
redirectedReference: ResolvedProjectReference | undefined,
resolutionInfo: ModuleResolutionInfo | undefined,
) => (ResolvedModuleFull | undefined)[];
const hasInvalidatedResolutions = host.hasInvalidatedResolutions || returnFalse;
if (host.resolveModuleNames) {
actualResolveModuleNamesWorker = (moduleNames, containingFile, containingFileName, reusedNames, redirectedReference) => host.resolveModuleNames!(Debug.checkEachDefined(moduleNames), containingFileName, reusedNames, redirectedReference, options, containingFile).map(resolved => {
// An older host may have omitted extension, in which case we should infer it from the file extension of resolvedFileName.
if (!resolved || (resolved as ResolvedModuleFull).extension !== undefined) {
return resolved as ResolvedModuleFull;
}
const withExtension = clone(resolved) as ResolvedModuleFull;
withExtension.extension = extensionFromPath(resolved.resolvedFileName);
return withExtension;
});
actualResolveModuleNamesWorker = (moduleNames, containingFile, containingFileName, redirectedReference, resolutionInfo) =>
host.resolveModuleNames!(
moduleNames.map(literal => literal.text),
containingFileName,
resolutionInfo?.reusedNames?.map(literal => literal.text),
redirectedReference,
options,
containingFile,
resolutionInfo,
).map(resolved => {
// An older host may have omitted extension, in which case we should infer it from the file extension of resolvedFileName.
if (!resolved || (resolved as ResolvedModuleFull).extension !== undefined) {
return resolved as ResolvedModuleFull;
}
const withExtension = clone(resolved) as ResolvedModuleFull;
withExtension.extension = extensionFromPath(resolved.resolvedFileName);
return withExtension;
});
moduleResolutionCache = host.getModuleResolutionCache?.();
}
else {
moduleResolutionCache = createModuleResolutionCache(currentDirectory, getCanonicalFileName, options);
const loader = (moduleName: string, resolverMode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined, containingFileName: string, redirectedReference: ResolvedProjectReference | undefined) => resolveModuleName(moduleName, containingFileName, options, host, moduleResolutionCache, redirectedReference, resolverMode).resolvedModule!; // TODO: GH#18217
actualResolveModuleNamesWorker = (moduleNames, containingFile, containingFileName, _reusedNames, redirectedReference) => loadWithModeAwareCache<ResolvedModuleFull>(Debug.checkEachDefined(moduleNames), containingFile, containingFileName, redirectedReference, loader);
const loader = (moduleName: string, resolverMode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined, containingFileName: string, redirectedReference: ResolvedProjectReference | undefined) =>
resolveModuleName(moduleName, containingFileName, options, host, moduleResolutionCache, redirectedReference, resolverMode).resolvedModule;
actualResolveModuleNamesWorker = (moduleNames, containingFile, containingFileName, redirectedReference, resolutionInfo) =>
loadWithModeAwareCache(moduleNames, containingFile, containingFileName, redirectedReference, resolutionInfo, loader);
}
let actualResolveTypeReferenceDirectiveNamesWorker: (typeDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference?: ResolvedProjectReference, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined) => (ResolvedTypeReferenceDirective | undefined)[];
@ -1398,18 +1418,15 @@ namespace ts {
}
}
function pullDiagnosticsFromCache(names: string[] | readonly FileReference[], containingFile: SourceFile) {
function pullDiagnosticsFromCache(names: readonly StringLiteralLike[] | readonly FileReference[], containingFile: SourceFile) {
if (!moduleResolutionCache) return;
const containingFileName = getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory);
const containingFileMode = !isString(containingFile) ? containingFile.impliedNodeFormat : undefined;
const containingDir = getDirectoryPath(containingFileName);
const redirectedReference = getRedirectReferenceForResolution(containingFile);
let i = 0;
for (const n of names) {
// mimics logic done in the resolution cache, should be resilient to upgrading it to use `FileReference`s for non-type-reference modal lookups to make it rely on the index in the list less
const mode = typeof n === "string" ? getModeForResolutionAtIndex(containingFile, i) : getModeForFileReference(n, containingFileMode);
const name = typeof n === "string" ? n : n.fileName;
i++;
const mode = getResolutionMode(n, containingFile);
const name = getResolutionName(n);
// only nonrelative names hit the cache, and, at least as of right now, only nonrelative names can issue diagnostics
// (Since diagnostics are only issued via import or export map lookup)
// This may totally change if/when the issue of output paths not mapping to input files is fixed in a broader context
@ -1421,13 +1438,13 @@ namespace ts {
}
}
function resolveModuleNamesWorker(moduleNames: string[], containingFile: SourceFile, reusedNames: string[] | undefined): readonly ResolvedModuleFull[] {
function resolveModuleNamesWorker(moduleNames: readonly StringLiteralLike[], containingFile: SourceFile, resolutionInfo: ModuleResolutionInfo | undefined): readonly (ResolvedModuleFull | undefined)[] {
if (!moduleNames.length) return emptyArray;
const containingFileName = getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory);
const redirectedReference = getRedirectReferenceForResolution(containingFile);
tracing?.push(tracing.Phase.Program, "resolveModuleNamesWorker", { containingFileName });
performance.mark("beforeResolveModule");
const result = actualResolveModuleNamesWorker(moduleNames, containingFile, containingFileName, reusedNames, redirectedReference);
const result = actualResolveModuleNamesWorker(moduleNames, containingFile, containingFileName, redirectedReference, resolutionInfo);
performance.mark("afterResolveModule");
performance.measure("ResolveModule", "beforeResolveModule", "afterResolveModule");
tracing?.pop();
@ -1530,11 +1547,11 @@ namespace ts {
return classifiableNames;
}
function resolveModuleNamesReusingOldState(moduleNames: string[], file: SourceFile): readonly (ResolvedModuleFull | undefined)[] {
function resolveModuleNamesReusingOldState(moduleNames: readonly StringLiteralLike[], file: SourceFile): readonly (ResolvedModuleFull | undefined)[] {
if (structureIsReused === StructureIsReused.Not && !file.ambientModuleNames.length) {
// If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules,
// the best we can do is fallback to the default logic.
return resolveModuleNamesWorker(moduleNames, file, /*reusedNames*/ undefined);
return resolveModuleNamesWorker(moduleNames, file, /*resolutionInfo*/ undefined);
}
const oldSourceFile = oldProgram && oldProgram.getSourceFile(file.fileName);
@ -1548,10 +1565,8 @@ namespace ts {
// Since we assume the filesystem does not change during program creation,
// it is safe to reuse resolutions from the earlier call.
const result: (ResolvedModuleFull | undefined)[] = [];
let i = 0;
for (const moduleName of moduleNames) {
const resolvedModule = file.resolvedModules.get(moduleName, getModeForResolutionAtIndex(file, i));
i++;
const resolvedModule = file.resolvedModules.get(moduleName.text, getModeForUsageLocation(file, moduleName));
result.push(resolvedModule);
}
return result;
@ -1562,7 +1577,7 @@ namespace ts {
// With this information, we can infer some module resolutions without performing resolution.
/** An ordered list of module names for which we cannot recover the resolution. */
let unknownModuleNames: string[] | undefined;
let unknownModuleNames: StringLiteralLike[] | undefined;
/**
* The indexing of elements in this list matches that of `moduleNames`.
*
@ -1573,7 +1588,7 @@ namespace ts {
* * ResolvedModuleFull instance: can be reused.
*/
let result: (ResolvedModuleFull | undefined)[] | undefined;
let reusedNames: string[] | undefined;
let reusedNames: StringLiteralLike[] | undefined;
/** A transient placeholder used to mark predicted resolution in the result list. */
const predictedToResolveToAmbientModuleMarker: ResolvedModuleFull = {} as any;
@ -1581,21 +1596,22 @@ namespace ts {
const moduleName = moduleNames[i];
// If the source file is unchanged and doesnt have invalidated resolution, reuse the module resolutions
if (file === oldSourceFile && !hasInvalidatedResolutions(oldSourceFile.path)) {
const oldResolvedModule = getResolvedModule(oldSourceFile, moduleName, getModeForResolutionAtIndex(oldSourceFile, i));
const mode = getModeForUsageLocation(file, moduleName);
const oldResolvedModule = getResolvedModule(oldSourceFile, moduleName.text, mode);
if (oldResolvedModule) {
if (isTraceEnabled(options, host)) {
trace(host,
oldResolvedModule.packageId ?
Diagnostics.Reusing_resolution_of_module_0_from_1_of_old_program_it_was_successfully_resolved_to_2_with_Package_ID_3 :
Diagnostics.Reusing_resolution_of_module_0_from_1_of_old_program_it_was_successfully_resolved_to_2,
moduleName,
moduleName.text,
getNormalizedAbsolutePath(file.originalFileName, currentDirectory),
oldResolvedModule.resolvedFileName,
oldResolvedModule.packageId && packageIdToString(oldResolvedModule.packageId)
);
}
(result || (result = new Array(moduleNames.length)))[i] = oldResolvedModule;
(reusedNames || (reusedNames = [])).push(moduleName);
(result ??= new Array(moduleNames.length))[i] = oldResolvedModule;
(reusedNames ??= []).push(moduleName);
continue;
}
}
@ -1604,14 +1620,14 @@ namespace ts {
// - resolved to an ambient module in the old program whose declaration is in an unmodified file
// (so the same module declaration will land in the new program)
let resolvesToAmbientModuleInNonModifiedFile = false;
if (contains(file.ambientModuleNames, moduleName)) {
if (contains(file.ambientModuleNames, moduleName.text)) {
resolvesToAmbientModuleInNonModifiedFile = true;
if (isTraceEnabled(options, host)) {
trace(host, Diagnostics.Module_0_was_resolved_as_locally_declared_ambient_module_in_file_1, moduleName, getNormalizedAbsolutePath(file.originalFileName, currentDirectory));
trace(host, Diagnostics.Module_0_was_resolved_as_locally_declared_ambient_module_in_file_1, moduleName.text, getNormalizedAbsolutePath(file.originalFileName, currentDirectory));
}
}
else {
resolvesToAmbientModuleInNonModifiedFile = moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName, i);
resolvesToAmbientModuleInNonModifiedFile = moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName);
}
if (resolvesToAmbientModuleInNonModifiedFile) {
@ -1619,12 +1635,12 @@ namespace ts {
}
else {
// Resolution failed in the old program, or resolved to an ambient module for which we can't reuse the result.
(unknownModuleNames || (unknownModuleNames = [])).push(moduleName);
(unknownModuleNames ??= []).push(moduleName);
}
}
const resolutions = unknownModuleNames && unknownModuleNames.length
? resolveModuleNamesWorker(unknownModuleNames, file, reusedNames)
? resolveModuleNamesWorker(unknownModuleNames, file, { names: unknownModuleNames, reusedNames })
: emptyArray;
// Combine results of resolutions and predicted results
@ -1654,9 +1670,8 @@ namespace ts {
// If we change our policy of rechecking failed lookups on each program create,
// we should adjust the value returned here.
function moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName: string, index: number): boolean {
if (index >= length(oldSourceFile?.imports) + length(oldSourceFile?.moduleAugmentations)) return false; // mode index out of bounds, don't reuse resolution
const resolutionToFile = getResolvedModule(oldSourceFile, moduleName, oldSourceFile && getModeForResolutionAtIndex(oldSourceFile, index));
function moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName: StringLiteralLike): boolean {
const resolutionToFile = getResolvedModule(oldSourceFile, moduleName.text, getModeForUsageLocation(file, moduleName));
const resolvedFile = resolutionToFile && oldProgram!.getSourceFile(resolutionToFile.resolvedFileName);
if (resolutionToFile && resolvedFile) {
// In the old program, we resolved to an ambient module that was in the same
@ -1667,14 +1682,14 @@ namespace ts {
}
// at least one of declarations should come from non-modified source file
const unmodifiedFile = ambientModuleNameToUnmodifiedFileName.get(moduleName);
const unmodifiedFile = ambientModuleNameToUnmodifiedFileName.get(moduleName.text);
if (!unmodifiedFile) {
return false;
}
if (isTraceEnabled(options, host)) {
trace(host, Diagnostics.Module_0_was_resolved_as_ambient_module_declared_in_1_since_this_file_was_not_modified, moduleName, unmodifiedFile);
trace(host, Diagnostics.Module_0_was_resolved_as_ambient_module_declared_in_1_since_this_file_was_not_modified, moduleName.text, unmodifiedFile);
}
return true;
}
@ -1874,7 +1889,7 @@ namespace ts {
const moduleNames = getModuleNames(newSourceFile);
const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFile);
// ensure that module resolution results are still correct
const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, oldSourceFile, moduleResolutionIsEqualTo);
const resolutionsChanged = hasChangesInResolutions(moduleNames, newSourceFile, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo);
if (resolutionsChanged) {
structureIsReused = StructureIsReused.SafeModules;
newSourceFile.resolvedModules = zipToModeAwareCache(newSourceFile, moduleNames, resolutions);
@ -1885,7 +1900,7 @@ namespace ts {
const typesReferenceDirectives = newSourceFile.typeReferenceDirectives;
const typeReferenceResolutions = resolveTypeReferenceDirectiveNamesWorker(typesReferenceDirectives, newSourceFile);
// ensure that types resolutions are still correct
const typeReferenceResolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, typeReferenceResolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, oldSourceFile, typeDirectiveIsEqualTo);
const typeReferenceResolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, newSourceFile, typeReferenceResolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, typeDirectiveIsEqualTo);
if (typeReferenceResolutionsChanged) {
structureIsReused = StructureIsReused.SafeModules;
newSourceFile.resolvedTypeReferenceDirectiveNames = zipToModeAwareCache(newSourceFile, typesReferenceDirectives, typeReferenceResolutions);
@ -3288,7 +3303,7 @@ namespace ts {
const optionsForFile = (useSourceOfProjectReferenceRedirect ? getRedirectReferenceForResolution(file)?.commandLine.options : undefined) || options;
for (let index = 0; index < moduleNames.length; index++) {
const resolution = resolutions[index];
setResolvedModule(file, moduleNames[index], resolution, getModeForResolutionAtIndex(file, index));
setResolvedModule(file, moduleNames[index].text, resolution, getModeForUsageLocation(file, moduleNames[index]));
if (!resolution) {
continue;
@ -4379,11 +4394,11 @@ namespace ts {
}
}
function getModuleNames({ imports, moduleAugmentations }: SourceFile): string[] {
const res = imports.map(i => i.text);
function getModuleNames({ imports, moduleAugmentations }: SourceFile): StringLiteralLike[] {
const res = imports.map(i => i);
for (const aug of moduleAugmentations) {
if (aug.kind === SyntaxKind.StringLiteral) {
res.push(aug.text);
res.push(aug);
}
// Do nothing if it's an Identifier; we don't need to do module resolution for `declare global`.
}

View File

@ -5,7 +5,14 @@ namespace ts {
startRecordingFilesWithChangedResolutions(): void;
finishRecordingFilesWithChangedResolutions(): Path[] | undefined;
resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference?: ResolvedProjectReference, containingSourceFile?: SourceFile): (ResolvedModuleFull | undefined)[];
resolveModuleNames(
moduleNames: string[],
containingFile: string,
reusedNames: string[] | undefined,
redirectedReference: ResolvedProjectReference | undefined,
containingSourceFile: SourceFile | undefined,
resolutionInfo: ModuleResolutionInfo | undefined
): (ResolvedModuleFull | undefined)[];
getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): CachedResolvedModuleWithFailedLookupLocations | undefined;
resolveTypeReferenceDirectives(typeDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference?: ResolvedProjectReference, containingFileMode?: SourceFile["impliedNodeFormat"]): (ResolvedTypeReferenceDirective | undefined)[];
@ -409,6 +416,7 @@ namespace ts {
getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName<T, R>;
shouldRetryResolution: (t: T) => boolean;
reusedNames?: readonly string[];
resolutionInfo?: ModuleResolutionInfo;
logChanges?: boolean;
containingSourceFile?: SourceFile;
containingSourceFileMode?: SourceFile["impliedNodeFormat"];
@ -417,7 +425,7 @@ namespace ts {
names, containingFile, redirectedReference,
cache, perDirectoryCacheWithRedirects,
loader, getResolutionWithResolvedFileName,
shouldRetryResolution, reusedNames, logChanges, containingSourceFile, containingSourceFileMode
shouldRetryResolution, reusedNames, resolutionInfo, logChanges, containingSourceFile, containingSourceFileMode
}: ResolveNamesWithLocalCacheInput<T, R>): (R | undefined)[] {
const path = resolutionHost.toPath(containingFile);
const resolutionsInFile = cache.get(path) || cache.set(path, createModeAwareCache()).get(path)!;
@ -441,15 +449,20 @@ namespace ts {
const seenNamesInFile = createModeAwareCache<true>();
let i = 0;
for (const entry of names) {
const name = isString(entry) ? entry : entry.fileName.toLowerCase();
for (const entry of containingSourceFile && resolutionInfo ? resolutionInfo.names : names) {
const name = !isString(entry) ? getResolutionName(entry) : entry;
// Imports supply a `containingSourceFile` but no `containingSourceFileMode` - it would be redundant
// they require calculating the mode for a given import from it's position in the resolution table, since a given
// import's syntax may override the file's default mode.
// Type references instead supply a `containingSourceFileMode` and a non-string entry which contains
// a default file mode override if applicable.
const mode = !isString(entry) ? getModeForFileReference(entry, containingSourceFileMode) :
containingSourceFile ? getModeForResolutionAtIndex(containingSourceFile, i) : undefined;
const mode = !isString(entry) ?
isStringLiteralLike(entry) ?
getModeForUsageLocation(containingSourceFile!, entry) :
getModeForFileReference(entry, containingSourceFileMode) :
containingSourceFile ?
getModeForResolutionAtIndex(containingSourceFile, i) :
undefined;
i++;
let resolution = resolutionsInFile.get(name, mode);
// Resolution is valid if it is present and not invalidated
@ -533,13 +546,19 @@ namespace ts {
resolvedModules.push(getResolutionWithResolvedFileName(resolution));
}
// Stop watching and remove the unused name
resolutionsInFile.forEach((resolution, name, mode) => {
if (!seenNamesInFile.has(name, mode) && !contains(reusedNames, name)) {
stopWatchFailedLookupLocationOfResolution(resolution, path, getResolutionWithResolvedFileName);
resolutionsInFile.delete(name, mode);
}
});
if (containingSourceFile && resolutionInfo) {
resolutionInfo.reusedNames?.forEach(literal => seenNamesInFile.set(literal.text, getModeForUsageLocation(containingSourceFile, literal), true));
reusedNames = undefined;
}
if (resolutionsInFile.size() !== seenNamesInFile.size()) {
// Stop watching and remove the unused name
resolutionsInFile.forEach((resolution, name, mode) => {
if (!seenNamesInFile.has(name, mode) && !contains(reusedNames, name)) {
stopWatchFailedLookupLocationOfResolution(resolution, path, getResolutionWithResolvedFileName);
resolutionsInFile.delete(name, mode);
}
});
}
return resolvedModules;
@ -576,7 +595,14 @@ namespace ts {
});
}
function resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference?: ResolvedProjectReference, containingSourceFile?: SourceFile): (ResolvedModuleFull | undefined)[] {
function resolveModuleNames(
moduleNames: string[],
containingFile: string,
reusedNames: string[] | undefined,
redirectedReference?: ResolvedProjectReference,
containingSourceFile?: SourceFile,
resolutionInfo?: ModuleResolutionInfo
): (ResolvedModuleFull | undefined)[] {
return resolveNamesWithLocalCache<CachedResolvedModuleWithFailedLookupLocations, ResolvedModuleFull>({
names: moduleNames,
containingFile,
@ -587,6 +613,7 @@ namespace ts {
getResolutionWithResolvedFileName: getResolvedModule,
shouldRetryResolution: resolution => !resolution.resolvedModule || !resolutionExtensionIsTSOrJson(resolution.resolvedModule.extension),
reusedNames,
resolutionInfo,
logChanges: logChangesWhenResolvingModule,
containingSourceFile,
});

View File

@ -306,9 +306,10 @@ namespace ts {
const moduleResolutionCache = !compilerHost.resolveModuleNames ? createModuleResolutionCache(currentDirectory, getCanonicalFileName) : undefined;
const typeReferenceDirectiveResolutionCache = !compilerHost.resolveTypeReferenceDirectives ? createTypeReferenceDirectiveResolutionCache(currentDirectory, getCanonicalFileName, /*options*/ undefined, moduleResolutionCache?.getPackageJsonInfoCache()) : undefined;
if (!compilerHost.resolveModuleNames) {
const loader = (moduleName: string, resolverMode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => resolveModuleName(moduleName, containingFile, state.projectCompilerOptions, compilerHost, moduleResolutionCache, redirectedReference, resolverMode).resolvedModule!;
compilerHost.resolveModuleNames = (moduleNames, containingFile, _reusedNames, redirectedReference, _options, containingSourceFile) =>
loadWithModeAwareCache<ResolvedModuleFull>(Debug.checkEachDefined(moduleNames), Debug.checkDefined(containingSourceFile), containingFile, redirectedReference, loader);
const loader = (moduleName: string, resolverMode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) =>
resolveModuleName(moduleName, containingFile, state.projectCompilerOptions, compilerHost, moduleResolutionCache, redirectedReference, resolverMode).resolvedModule;
compilerHost.resolveModuleNames = (moduleNames, containingFile, _reusedNames, redirectedReference, _options, containingSourceFile, resolutionInfo) =>
loadWithModeAwareCache(Debug.checkEachDefined(moduleNames), Debug.checkDefined(containingSourceFile), containingFile, redirectedReference, resolutionInfo, loader);
compilerHost.getModuleResolutionCache = () => moduleResolutionCache;
}
if (!compilerHost.resolveTypeReferenceDirectives) {

View File

@ -7193,6 +7193,11 @@ namespace ts {
/* @internal */
export type HasChangedAutomaticTypeDirectiveNames = () => boolean;
export interface ModuleResolutionInfo {
names: readonly StringLiteralLike[];
reusedNames: readonly StringLiteralLike[] | undefined;
}
export interface CompilerHost extends ModuleResolutionHost {
getSourceFile(fileName: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined;
getSourceFileByPath?(fileName: string, path: Path, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined;
@ -7213,7 +7218,7 @@ namespace ts {
* If resolveModuleNames is implemented then implementation for members from ModuleResolutionHost can be just
* 'throw new Error("NotImplemented")'
*/
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModule | undefined)[];
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModule | undefined)[];
/**
* Returns the module resolution cache used by a provided `resolveModuleNames` implementation so that any non-name module resolution operations (eg, package.json lookup) can reuse it
*/

View File

@ -211,19 +211,18 @@ namespace ts {
}
export function hasChangesInResolutions<T>(
names: readonly string[] | readonly FileReference[],
names: readonly StringLiteralLike[] | readonly FileReference[],
newSourceFile: SourceFile,
newResolutions: readonly T[],
oldResolutions: ModeAwareCache<T> | undefined,
oldSourceFile: SourceFile | undefined,
comparer: (oldResolution: T, newResolution: T) => boolean): boolean {
Debug.assert(names.length === newResolutions.length);
for (let i = 0; i < names.length; i++) {
const newResolution = newResolutions[i];
const entry = names[i];
// We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
const name = !isString(entry) ? entry.fileName.toLowerCase() : entry;
const mode = !isString(entry) ? getModeForFileReference(entry, oldSourceFile?.impliedNodeFormat) : oldSourceFile && getModeForResolutionAtIndex(oldSourceFile, i);
const name = getResolutionName(entry);
const mode = getResolutionMode(entry, newSourceFile);
const oldResolution = oldResolutions && oldResolutions.get(name, mode);
const changed =
oldResolution

View File

@ -2072,8 +2072,8 @@ namespace ts {
return indentation === MAX_SMI_X86 ? undefined : indentation;
}
export function isStringLiteralLike(node: Node): node is StringLiteralLike {
return node.kind === SyntaxKind.StringLiteral || node.kind === SyntaxKind.NoSubstitutionTemplateLiteral;
export function isStringLiteralLike(node: Node | FileReference): node is StringLiteralLike {
return (node as Node).kind === SyntaxKind.StringLiteral || (node as Node).kind === SyntaxKind.NoSubstitutionTemplateLiteral;
}
export function isJSDocLinkLike(node: Node): node is JSDocLink | JSDocLinkCode | JSDocLinkPlain {

View File

@ -109,7 +109,7 @@ namespace ts {
getEnvironmentVariable?(name: string): string | undefined;
/** If provided, used to resolve the module names, otherwise typescript's default module resolution */
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModule | undefined)[];
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModule | undefined)[];
/** If provided, used to resolve type reference directives, otherwise typescript's default resolution */
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[];
/** If provided along with custom resolveModuleNames or resolveTypeReferenceDirectives, used to determine if unchanged file path needs to re-resolve modules/type reference directives */
@ -365,7 +365,7 @@ namespace ts {
// Resolve module using host module resolution strategy if provided otherwise use resolution cache to resolve module names
compilerHost.resolveModuleNames = host.resolveModuleNames ?
((...args) => host.resolveModuleNames!(...args)) :
((moduleNames, containingFile, reusedNames, redirectedReference, _options, sourceFile) => resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames, redirectedReference, sourceFile));
((moduleNames, containingFile, reusedNames, redirectedReference, _options, sourceFile, resolutionInfo) => resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames, redirectedReference, sourceFile, resolutionInfo));
compilerHost.resolveTypeReferenceDirectives = host.resolveTypeReferenceDirectives ?
((...args) => host.resolveTypeReferenceDirectives!(...args)) :
((typeDirectiveNames, containingFile, redirectedReference, _options, containingFileMode) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile, redirectedReference, containingFileMode));

View File

@ -506,8 +506,8 @@ namespace ts.server {
return !this.isWatchedMissingFile(path) && this.directoryStructureHost.fileExists(file);
}
resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference, _options?: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModuleFull | undefined)[] {
return this.resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames, redirectedReference, containingSourceFile);
resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference, _options?: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModuleFull | undefined)[] {
return this.resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames, redirectedReference, containingSourceFile, resolutionInfo);
}
getModuleResolutionCache(): ModuleResolutionCache | undefined {

View File

@ -284,7 +284,7 @@ namespace ts {
*
* If this is implemented, `getResolvedModuleWithFailedLookupLocationsFromCache` should be too.
*/
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModule | undefined)[];
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModule | undefined)[];
getResolvedModuleWithFailedLookupLocationsFromCache?(modulename: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations | undefined;
resolveTypeReferenceDirectives?(typeDirectiveNames: string[] | FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[];
/* @internal */ hasInvalidatedResolutions?: HasInvalidatedResolutions;

View File

@ -256,5 +256,75 @@ namespace ts.tscWatch {
],
});
});
verifyTscWatch({
scenario: "moduleResolution",
subScenario: "module resolutions from file are partially used",
sys: () => createWatchedSystem([
{
path: `${projectRoot}/tsconfig.json`,
content: JSON.stringify({
compilerOptions: { moduleResolution: "node16" },
})
},
{
path: `${projectRoot}/index.ts`,
content: Utils.dedent`
import type { ImportInterface } from "pkg" assert { "resolution-mode": "import" };
import type { RequireInterface } from "pkg1" assert { "resolution-mode": "require" };
import {x} from "./a";
`
},
{
path: `${projectRoot}/a.ts`,
content: Utils.dedent`
export const x = 10;
`
},
{
path: `${projectRoot}/node_modules/pkg/package.json`,
content: JSON.stringify({
name: "pkg",
version: "0.0.1",
exports: {
import: "./import.js",
require: "./require.js"
}
})
},
{
path: `${projectRoot}/node_modules/pkg/import.d.ts`,
content: `export interface ImportInterface {}`
},
{
path: `${projectRoot}/node_modules/pkg/require.d.ts`,
content: `export interface RequireInterface {}`
},
{
path: `${projectRoot}/node_modules/pkg1/package.json`,
content: JSON.stringify({
name: "pkg1",
version: "0.0.1",
exports: {
import: "./import.js",
require: "./require.js"
}
})
},
{
path: `${projectRoot}/node_modules/pkg1/import.d.ts`,
content: `export interface ImportInterface {}`
},
libFile
], { currentDirectory: projectRoot }),
commandLineArgs: ["-w", "--traceResolution"],
changes: [
{
caption: "modify aFile by adding import",
change: sys => sys.appendFile(`${projectRoot}/a.ts`, `import type { ImportInterface } from "pkg" assert { "resolution-mode": "import" }`),
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
}
]
});
});
}

View File

@ -3301,6 +3301,10 @@ declare namespace ts {
readonly resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined;
readonly failedLookupLocations: string[];
}
export interface ModuleResolutionInfo {
names: readonly StringLiteralLike[];
reusedNames: readonly StringLiteralLike[] | undefined;
}
export interface CompilerHost extends ModuleResolutionHost {
getSourceFile(fileName: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined;
getSourceFileByPath?(fileName: string, path: Path, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined;
@ -3313,7 +3317,7 @@ declare namespace ts {
useCaseSensitiveFileNames(): boolean;
getNewLine(): string;
readDirectory?(rootDir: string, extensions: readonly string[], excludes: readonly string[] | undefined, includes: readonly string[], depth?: number): string[];
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModule | undefined)[];
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModule | undefined)[];
/**
* Returns the module resolution cache used by a provided `resolveModuleNames` implementation so that any non-name module resolution operations (eg, package.json lookup) can reuse it
*/
@ -4567,7 +4571,7 @@ declare namespace ts {
/** True if has initializer node attached to it. */
function hasOnlyExpressionInitializer(node: Node): node is HasExpressionInitializer;
function isObjectLiteralElement(node: Node): node is ObjectLiteralElement;
function isStringLiteralLike(node: Node): node is StringLiteralLike;
function isStringLiteralLike(node: Node | FileReference): node is StringLiteralLike;
function isJSDocLinkLike(node: Node): node is JSDocLink | JSDocLinkCode | JSDocLinkPlain;
function hasRestParameter(s: SignatureDeclaration | JSDocSignature): boolean;
function isRestParameter(node: ParameterDeclaration | JSDocParameterTag): boolean;
@ -5444,7 +5448,7 @@ declare namespace ts {
/** If provided is used to get the environment variable */
getEnvironmentVariable?(name: string): string | undefined;
/** If provided, used to resolve the module names, otherwise typescript's default module resolution */
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModule | undefined)[];
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModule | undefined)[];
/** If provided, used to resolve type reference directives, otherwise typescript's default resolution */
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[];
/** If provided along with custom resolveModuleNames or resolveTypeReferenceDirectives, used to determine if unchanged file path needs to re-resolve modules/type reference directives */
@ -5851,7 +5855,7 @@ declare namespace ts {
readFile(path: string, encoding?: string): string | undefined;
fileExists(path: string): boolean;
getTypeRootsVersion?(): number;
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModule | undefined)[];
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModule | undefined)[];
getResolvedModuleWithFailedLookupLocationsFromCache?(modulename: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations | undefined;
resolveTypeReferenceDirectives?(typeDirectiveNames: string[] | FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[];
getDirectories?(directoryName: string): string[];
@ -10154,7 +10158,7 @@ declare namespace ts.server {
readFile(fileName: string): string | undefined;
writeFile(fileName: string, content: string): void;
fileExists(file: string): boolean;
resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference, _options?: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModuleFull | undefined)[];
resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference, _options?: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModuleFull | undefined)[];
getModuleResolutionCache(): ModuleResolutionCache | undefined;
getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations | undefined;
resolveTypeReferenceDirectives(typeDirectiveNames: string[] | FileReference[], containingFile: string, redirectedReference?: ResolvedProjectReference, _options?: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[];

View File

@ -3301,6 +3301,10 @@ declare namespace ts {
readonly resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined;
readonly failedLookupLocations: string[];
}
export interface ModuleResolutionInfo {
names: readonly StringLiteralLike[];
reusedNames: readonly StringLiteralLike[] | undefined;
}
export interface CompilerHost extends ModuleResolutionHost {
getSourceFile(fileName: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined;
getSourceFileByPath?(fileName: string, path: Path, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined;
@ -3313,7 +3317,7 @@ declare namespace ts {
useCaseSensitiveFileNames(): boolean;
getNewLine(): string;
readDirectory?(rootDir: string, extensions: readonly string[], excludes: readonly string[] | undefined, includes: readonly string[], depth?: number): string[];
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModule | undefined)[];
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModule | undefined)[];
/**
* Returns the module resolution cache used by a provided `resolveModuleNames` implementation so that any non-name module resolution operations (eg, package.json lookup) can reuse it
*/
@ -4567,7 +4571,7 @@ declare namespace ts {
/** True if has initializer node attached to it. */
function hasOnlyExpressionInitializer(node: Node): node is HasExpressionInitializer;
function isObjectLiteralElement(node: Node): node is ObjectLiteralElement;
function isStringLiteralLike(node: Node): node is StringLiteralLike;
function isStringLiteralLike(node: Node | FileReference): node is StringLiteralLike;
function isJSDocLinkLike(node: Node): node is JSDocLink | JSDocLinkCode | JSDocLinkPlain;
function hasRestParameter(s: SignatureDeclaration | JSDocSignature): boolean;
function isRestParameter(node: ParameterDeclaration | JSDocParameterTag): boolean;
@ -5444,7 +5448,7 @@ declare namespace ts {
/** If provided is used to get the environment variable */
getEnvironmentVariable?(name: string): string | undefined;
/** If provided, used to resolve the module names, otherwise typescript's default module resolution */
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModule | undefined)[];
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModule | undefined)[];
/** If provided, used to resolve type reference directives, otherwise typescript's default resolution */
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[];
/** If provided along with custom resolveModuleNames or resolveTypeReferenceDirectives, used to determine if unchanged file path needs to re-resolve modules/type reference directives */
@ -5851,7 +5855,7 @@ declare namespace ts {
readFile(path: string, encoding?: string): string | undefined;
fileExists(path: string): boolean;
getTypeRootsVersion?(): number;
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModule | undefined)[];
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModule | undefined)[];
getResolvedModuleWithFailedLookupLocationsFromCache?(modulename: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations | undefined;
resolveTypeReferenceDirectives?(typeDirectiveNames: string[] | FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[];
getDirectories?(directoryName: string): string[];

View File

@ -0,0 +1,328 @@
Input::
//// [/user/username/projects/myproject/tsconfig.json]
{"compilerOptions":{"moduleResolution":"node16"}}
//// [/user/username/projects/myproject/index.ts]
import type { ImportInterface } from "pkg" assert { "resolution-mode": "import" };
import type { RequireInterface } from "pkg1" assert { "resolution-mode": "require" };
import {x} from "./a";
//// [/user/username/projects/myproject/a.ts]
export const x = 10;
//// [/user/username/projects/myproject/node_modules/pkg/package.json]
{"name":"pkg","version":"0.0.1","exports":{"import":"./import.js","require":"./require.js"}}
//// [/user/username/projects/myproject/node_modules/pkg/import.d.ts]
export interface ImportInterface {}
//// [/user/username/projects/myproject/node_modules/pkg/require.d.ts]
export interface RequireInterface {}
//// [/user/username/projects/myproject/node_modules/pkg1/package.json]
{"name":"pkg1","version":"0.0.1","exports":{"import":"./import.js","require":"./require.js"}}
//// [/user/username/projects/myproject/node_modules/pkg1/import.d.ts]
export interface ImportInterface {}
//// [/a/lib/lib.d.ts]
/// <reference no-default-lib="true"/>
interface Boolean {}
interface Function {}
interface CallableFunction {}
interface NewableFunction {}
interface IArguments {}
interface Number { toExponential: any; }
interface Object {}
interface RegExp {}
interface String { charAt: any; }
interface Array<T> { length: number; [n: number]: T; }
/a/lib/tsc.js -w --traceResolution
Output::
>> Screen clear
[12:00:39 AM] Starting compilation in watch mode...
File '/user/username/projects/myproject/package.json' does not exist.
File '/user/username/projects/package.json' does not exist.
File '/user/username/package.json' does not exist.
File '/user/package.json' does not exist.
File '/package.json' does not exist.
File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups.
File '/user/username/projects/package.json' does not exist according to earlier cached lookups.
File '/user/username/package.json' does not exist according to earlier cached lookups.
File '/user/package.json' does not exist according to earlier cached lookups.
File '/package.json' does not exist according to earlier cached lookups.
======== Resolving module 'pkg' from '/user/username/projects/myproject/index.ts'. ========
Explicitly specified module resolution kind: 'Node16'.
Resolving in ESM mode with conditions 'node', 'import', 'types'.
File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups.
File '/user/username/projects/package.json' does not exist according to earlier cached lookups.
File '/user/username/package.json' does not exist according to earlier cached lookups.
File '/user/package.json' does not exist according to earlier cached lookups.
File '/package.json' does not exist according to earlier cached lookups.
Loading module 'pkg' from 'node_modules' folder, target file type 'TypeScript'.
Found 'package.json' at '/user/username/projects/myproject/node_modules/pkg/package.json'.
'package.json' does not have a 'typesVersions' field.
Matched 'exports' condition 'import'.
Using 'exports' subpath '.' with target './import.js'.
File name '/user/username/projects/myproject/node_modules/pkg/import.js' has a '.js' extension - stripping it.
File '/user/username/projects/myproject/node_modules/pkg/import.ts' does not exist.
File '/user/username/projects/myproject/node_modules/pkg/import.tsx' does not exist.
File '/user/username/projects/myproject/node_modules/pkg/import.d.ts' exist - use it as a name resolution result.
Resolving real path for '/user/username/projects/myproject/node_modules/pkg/import.d.ts', result '/user/username/projects/myproject/node_modules/pkg/import.d.ts'.
======== Module name 'pkg' was successfully resolved to '/user/username/projects/myproject/node_modules/pkg/import.d.ts' with Package ID 'pkg/import.d.ts@0.0.1'. ========
======== Resolving module 'pkg1' from '/user/username/projects/myproject/index.ts'. ========
Explicitly specified module resolution kind: 'Node16'.
Resolving in CJS mode with conditions 'node', 'require', 'types'.
File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups.
File '/user/username/projects/package.json' does not exist according to earlier cached lookups.
File '/user/username/package.json' does not exist according to earlier cached lookups.
File '/user/package.json' does not exist according to earlier cached lookups.
File '/package.json' does not exist according to earlier cached lookups.
Loading module 'pkg1' from 'node_modules' folder, target file type 'TypeScript'.
Found 'package.json' at '/user/username/projects/myproject/node_modules/pkg1/package.json'.
'package.json' does not have a 'typesVersions' field.
Saw non-matching condition 'import'.
Matched 'exports' condition 'require'.
Using 'exports' subpath '.' with target './require.js'.
File name '/user/username/projects/myproject/node_modules/pkg1/require.js' has a '.js' extension - stripping it.
File '/user/username/projects/myproject/node_modules/pkg1/require.ts' does not exist.
File '/user/username/projects/myproject/node_modules/pkg1/require.tsx' does not exist.
File '/user/username/projects/myproject/node_modules/pkg1/require.d.ts' does not exist.
Directory '/user/username/projects/myproject/node_modules/@types' does not exist, skipping all lookups in it.
Directory '/user/username/projects/node_modules' does not exist, skipping all lookups in it.
Directory '/user/username/node_modules' does not exist, skipping all lookups in it.
Directory '/user/node_modules' does not exist, skipping all lookups in it.
Directory '/node_modules' does not exist, skipping all lookups in it.
File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups.
File '/user/username/projects/package.json' does not exist according to earlier cached lookups.
File '/user/username/package.json' does not exist according to earlier cached lookups.
File '/user/package.json' does not exist according to earlier cached lookups.
File '/package.json' does not exist according to earlier cached lookups.
Loading module 'pkg1' from 'node_modules' folder, target file type 'JavaScript'.
File '/user/username/projects/myproject/node_modules/pkg1/package.json' exists according to earlier cached lookups.
Saw non-matching condition 'import'.
Matched 'exports' condition 'require'.
Using 'exports' subpath '.' with target './require.js'.
File name '/user/username/projects/myproject/node_modules/pkg1/require.js' has a '.js' extension - stripping it.
File '/user/username/projects/myproject/node_modules/pkg1/require.js' does not exist.
File '/user/username/projects/myproject/node_modules/pkg1/require.jsx' does not exist.
Directory '/user/username/projects/node_modules' does not exist, skipping all lookups in it.
Directory '/user/username/node_modules' does not exist, skipping all lookups in it.
Directory '/user/node_modules' does not exist, skipping all lookups in it.
Directory '/node_modules' does not exist, skipping all lookups in it.
======== Module name 'pkg1' was not resolved. ========
======== Resolving module './a' from '/user/username/projects/myproject/index.ts'. ========
Explicitly specified module resolution kind: 'Node16'.
Resolving in CJS mode with conditions 'node', 'require', 'types'.
Loading module as file / folder, candidate module location '/user/username/projects/myproject/a', target file type 'TypeScript'.
File '/user/username/projects/myproject/a.ts' exist - use it as a name resolution result.
======== Module name './a' was successfully resolved to '/user/username/projects/myproject/a.ts'. ========
File '/user/username/projects/myproject/node_modules/pkg/package.json' exists according to earlier cached lookups.
File '/a/lib/package.json' does not exist.
File '/a/package.json' does not exist.
File '/package.json' does not exist according to earlier cached lookups.
index.ts:2:39 - error TS2307: Cannot find module 'pkg1' or its corresponding type declarations.
2 import type { RequireInterface } from "pkg1" assert { "resolution-mode": "require" };
   ~~~~~~
[12:00:44 AM] Found 1 error. Watching for file changes.
Program root files: ["/user/username/projects/myproject/a.ts","/user/username/projects/myproject/index.ts"]
Program options: {"moduleResolution":3,"watch":true,"traceResolution":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"}
Program structureReused: Not
Program files::
/a/lib/lib.d.ts
/user/username/projects/myproject/a.ts
/user/username/projects/myproject/node_modules/pkg/import.d.ts
/user/username/projects/myproject/index.ts
Semantic diagnostics in builder refreshed for::
/a/lib/lib.d.ts
/user/username/projects/myproject/a.ts
/user/username/projects/myproject/node_modules/pkg/import.d.ts
/user/username/projects/myproject/index.ts
Shape signatures in builder refreshed for::
/a/lib/lib.d.ts (used version)
/user/username/projects/myproject/a.ts (used version)
/user/username/projects/myproject/node_modules/pkg/import.d.ts (used version)
/user/username/projects/myproject/index.ts (used version)
PolledWatches::
/user/username/projects/myproject/package.json:
{"pollingInterval":2000}
/user/username/projects/package.json:
{"pollingInterval":2000}
/user/username/projects/myproject/node_modules/@types:
{"pollingInterval":500}
FsWatches::
/user/username/projects/myproject/tsconfig.json:
{}
/user/username/projects/myproject/a.ts:
{}
/user/username/projects/myproject/index.ts:
{}
/user/username/projects/myproject/node_modules/pkg/import.d.ts:
{}
/a/lib/lib.d.ts:
{}
/user/username/projects/myproject:
{}
/user/username/projects/myproject/node_modules/pkg/package.json:
{}
/user/username/projects/myproject/node_modules/pkg1/package.json:
{}
FsWatchesRecursive::
/user/username/projects/myproject/node_modules:
{}
/user/username/projects/myproject:
{}
exitCode:: ExitStatus.undefined
//// [/user/username/projects/myproject/a.js]
"use strict";
exports.__esModule = true;
exports.x = void 0;
exports.x = 10;
//// [/user/username/projects/myproject/index.js]
"use strict";
exports.__esModule = true;
Change:: modify aFile by adding import
Input::
//// [/user/username/projects/myproject/a.ts]
export const x = 10;
import type { ImportInterface } from "pkg" assert { "resolution-mode": "import" }
Output::
>> Screen clear
[12:00:47 AM] File change detected. Starting incremental compilation...
File '/a/lib/package.json' does not exist according to earlier cached lookups.
File '/a/package.json' does not exist according to earlier cached lookups.
File '/package.json' does not exist according to earlier cached lookups.
File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups.
File '/user/username/projects/package.json' does not exist according to earlier cached lookups.
File '/user/username/package.json' does not exist according to earlier cached lookups.
File '/user/package.json' does not exist according to earlier cached lookups.
File '/package.json' does not exist according to earlier cached lookups.
File '/user/username/projects/myproject/node_modules/pkg/package.json' exists according to earlier cached lookups.
File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups.
File '/user/username/projects/package.json' does not exist according to earlier cached lookups.
File '/user/username/package.json' does not exist according to earlier cached lookups.
File '/user/package.json' does not exist according to earlier cached lookups.
File '/package.json' does not exist according to earlier cached lookups.
File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups.
File '/user/username/projects/package.json' does not exist according to earlier cached lookups.
File '/user/username/package.json' does not exist according to earlier cached lookups.
File '/user/package.json' does not exist according to earlier cached lookups.
File '/package.json' does not exist according to earlier cached lookups.
======== Resolving module 'pkg' from '/user/username/projects/myproject/a.ts'. ========
Explicitly specified module resolution kind: 'Node16'.
Resolving in ESM mode with conditions 'node', 'import', 'types'.
File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups.
File '/user/username/projects/package.json' does not exist according to earlier cached lookups.
File '/user/username/package.json' does not exist according to earlier cached lookups.
File '/user/package.json' does not exist according to earlier cached lookups.
File '/package.json' does not exist according to earlier cached lookups.
Loading module 'pkg' from 'node_modules' folder, target file type 'TypeScript'.
File '/user/username/projects/myproject/node_modules/pkg/package.json' exists according to earlier cached lookups.
Matched 'exports' condition 'import'.
Using 'exports' subpath '.' with target './import.js'.
File name '/user/username/projects/myproject/node_modules/pkg/import.js' has a '.js' extension - stripping it.
File '/user/username/projects/myproject/node_modules/pkg/import.ts' does not exist.
File '/user/username/projects/myproject/node_modules/pkg/import.tsx' does not exist.
File '/user/username/projects/myproject/node_modules/pkg/import.d.ts' exist - use it as a name resolution result.
Resolving real path for '/user/username/projects/myproject/node_modules/pkg/import.d.ts', result '/user/username/projects/myproject/node_modules/pkg/import.d.ts'.
======== Module name 'pkg' was successfully resolved to '/user/username/projects/myproject/node_modules/pkg/import.d.ts' with Package ID 'pkg/import.d.ts@0.0.1'. ========
File '/user/username/projects/myproject/node_modules/pkg/package.json' exists according to earlier cached lookups.
File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups.
File '/user/username/projects/package.json' does not exist according to earlier cached lookups.
File '/user/username/package.json' does not exist according to earlier cached lookups.
File '/user/package.json' does not exist according to earlier cached lookups.
File '/package.json' does not exist according to earlier cached lookups.
Reusing resolution of module 'pkg' from '/user/username/projects/myproject/index.ts' of old program, it was successfully resolved to '/user/username/projects/myproject/node_modules/pkg/import.d.ts' with Package ID 'pkg/import.d.ts@0.0.1'.
Reusing resolution of module './a' from '/user/username/projects/myproject/index.ts' of old program, it was successfully resolved to '/user/username/projects/myproject/a.ts'.
Reusing resolution of module 'pkg1' from '/user/username/projects/myproject/index.ts' of old program, it was not resolved.
File '/a/lib/package.json' does not exist according to earlier cached lookups.
File '/a/package.json' does not exist according to earlier cached lookups.
File '/package.json' does not exist according to earlier cached lookups.
index.ts:2:39 - error TS2307: Cannot find module 'pkg1' or its corresponding type declarations.
2 import type { RequireInterface } from "pkg1" assert { "resolution-mode": "require" };
   ~~~~~~
[12:00:54 AM] Found 1 error. Watching for file changes.
Program root files: ["/user/username/projects/myproject/a.ts","/user/username/projects/myproject/index.ts"]
Program options: {"moduleResolution":3,"watch":true,"traceResolution":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"}
Program structureReused: SafeModules
Program files::
/a/lib/lib.d.ts
/user/username/projects/myproject/node_modules/pkg/import.d.ts
/user/username/projects/myproject/a.ts
/user/username/projects/myproject/index.ts
Semantic diagnostics in builder refreshed for::
/user/username/projects/myproject/a.ts
/user/username/projects/myproject/index.ts
Shape signatures in builder refreshed for::
/user/username/projects/myproject/a.ts (computed .d.ts)
/user/username/projects/myproject/index.ts (computed .d.ts)
PolledWatches::
/user/username/projects/myproject/package.json:
{"pollingInterval":2000}
/user/username/projects/package.json:
{"pollingInterval":2000}
/user/username/projects/myproject/node_modules/@types:
{"pollingInterval":500}
FsWatches::
/user/username/projects/myproject/tsconfig.json:
{}
/user/username/projects/myproject/a.ts:
{}
/user/username/projects/myproject/index.ts:
{}
/user/username/projects/myproject/node_modules/pkg/import.d.ts:
{}
/a/lib/lib.d.ts:
{}
/user/username/projects/myproject:
{}
/user/username/projects/myproject/node_modules/pkg/package.json:
{}
/user/username/projects/myproject/node_modules/pkg1/package.json:
{}
FsWatchesRecursive::
/user/username/projects/myproject/node_modules:
{}
/user/username/projects/myproject:
{}
exitCode:: ExitStatus.undefined
//// [/user/username/projects/myproject/a.js] file written with same contents
//// [/user/username/projects/myproject/index.js] file written with same contents