Skip to content

[WIP] Resolution sharing #61732

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
31009c8
Write log about unresolved imports calculation
sheetalkamat Oct 17, 2023
d81f147
Add test when type acquisition changes
sheetalkamat Nov 7, 2023
592bba4
Verify getUnresolvedImports incrementally
sheetalkamat Oct 20, 2023
cae589d
Handle unnecessary typings request force just because root files change
sheetalkamat Oct 11, 2023
3ae4463
When type acquisition is disabled, remove the typing files set as root
sheetalkamat Oct 11, 2023
e89404f
Close watchers for TI that are no longer needed
sheetalkamat Oct 11, 2023
d839cae
Update the unresolved import list only if it will be used
sheetalkamat Oct 11, 2023
457d9b7
Do not update typings for project if type aquisition is disabled
sheetalkamat Oct 12, 2023
f659bec
If typing installer is disabled invalidate all the resolutions from t…
sheetalkamat Oct 12, 2023
1637783
Use globalCacheResolution to invalidate resolutions on typings update
sheetalkamat Oct 17, 2023
dad8851
Update cached import list update as part of resolving modules instead…
sheetalkamat Oct 20, 2023
cbbbf4a
If the there is no change detected in module resolution we shouldnt n…
sheetalkamat Oct 20, 2023
8e23cb4
Now that work with unresolved imports cleanup is done, remove the tem…
sheetalkamat Nov 9, 2023
2a08064
unresolved imports of empty array and undefined are same
sheetalkamat Nov 9, 2023
c9dcebe
Tests
sheetalkamat Jul 15, 2024
2f52238
Add incremental test where cache should have same resolutions as what…
sheetalkamat Sep 27, 2023
3b993ff
Make sure reused resolutions from file are accounted if all resolutio…
sheetalkamat Sep 29, 2023
254139c
In preparation of sharing resolutions, watch the resolutions right aw…
sheetalkamat Jul 11, 2023
a51e79a
Resolutions in cache stays for lifetime..
sheetalkamat Jul 8, 2024
b3ab401
Handle package json lifetime
sheetalkamat Jul 26, 2024
fb623b9
The redirected reference use that projects tsconfig directory as the …
sheetalkamat Jul 10, 2024
0633e0e
Dont pollute resolution so we can reuse it across projects
sheetalkamat Jul 19, 2024
ee741df
Factor out cache to be shared and not shared. Projects still are not …
sheetalkamat May 16, 2025
80ba295
Real sharing across projects
sheetalkamat Sep 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions src/compiler/_namespaces/ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export * from "../builderStatePublic.js";
export * from "../builderState.js";
export * from "../builder.js";
export * from "../builderPublic.js";
export * from "../sharedResolutionCache.js";
export * from "../resolutionCache.js";
export * from "../watch.js";
export * from "../watchPublic.js";
Expand Down
367 changes: 305 additions & 62 deletions src/compiler/moduleNameResolver.ts

Large diffs are not rendered by default.

86 changes: 74 additions & 12 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,6 @@ import {
removeSuffix,
resolutionExtensionIsTSOrJson,
ResolutionMode,
ResolutionWithFailedLookupLocations,
resolveConfigFileProjectName,
ResolvedConfigFileName,
ResolvedModuleFull,
Expand Down Expand Up @@ -1011,7 +1010,8 @@ export function getResolutionModeOverride(node: ImportAttributes | undefined, gr
return elem.value.text === "import" ? ModuleKind.ESNext : ModuleKind.CommonJS;
}

const emptyResolution: ResolvedModuleWithFailedLookupLocations & ResolvedTypeReferenceDirectiveWithFailedLookupLocations = {
/** @internal */
export const emptyResolution: ResolvedModuleWithFailedLookupLocations & ResolvedTypeReferenceDirectiveWithFailedLookupLocations = {
resolvedModule: undefined,
resolvedTypeReferenceDirective: undefined,
};
Expand Down Expand Up @@ -1071,7 +1071,8 @@ function getTypeReferenceResolutionName<T extends FileReference | string>(entry:
return !isString(entry) ? entry.fileName : entry;
}

const typeReferenceResolutionNameAndModeGetter: ResolutionNameAndModeGetter<FileReference | string, SourceFile | undefined> = {
/** @internal */
export const typeReferenceResolutionNameAndModeGetter: ResolutionNameAndModeGetter<FileReference | string, SourceFile | undefined> = {
getName: getTypeReferenceResolutionName,
getMode: (entry, file, compilerOptions) => getModeForFileReference(entry, file && getDefaultResolutionModeForFileWorker(file, compilerOptions)),
};
Expand Down Expand Up @@ -1136,6 +1137,13 @@ export function loadWithModeAwareCache<Entry, SourceFile, ResolutionCache, Resol
/** @internal */
export const inferredTypesContainingFile = "__inferred type names__.ts";

/** @internal */
export function getAutomaticTypeDirectiveContainingFile(options: CompilerOptions, currentDirectory: string): string {
// This containingFilename needs to match with the one used in managed-side
const containingDirectory = options.configFilePath ? getDirectoryPath(options.configFilePath) : currentDirectory;
return combinePaths(containingDirectory, inferredTypesContainingFile);
}

/** @internal */
export function getInferredLibraryNameResolveFrom(options: CompilerOptions, currentDirectory: string, libFileName: string): string {
const containingDirectory = options.configFilePath ? getDirectoryPath(options.configFilePath) : currentDirectory;
Expand Down Expand Up @@ -1785,10 +1793,10 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
automaticTypeDirectiveResolutions = createModeAwareCache();
if (automaticTypeDirectiveNames.length) {
tracing?.push(tracing.Phase.Program, "processTypeReferences", { count: automaticTypeDirectiveNames.length });
// This containingFilename needs to match with the one used in managed-side
const containingDirectory = options.configFilePath ? getDirectoryPath(options.configFilePath) : currentDirectory;
const containingFilename = combinePaths(containingDirectory, inferredTypesContainingFile);
const resolutions = resolveTypeReferenceDirectiveNamesReusingOldState(automaticTypeDirectiveNames, containingFilename);
const resolutions = resolveTypeReferenceDirectiveNamesReusingOldState(
automaticTypeDirectiveNames,
getAutomaticTypeDirectiveContainingFile(options, currentDirectory),
);
for (let i = 0; i < automaticTypeDirectiveNames.length; i++) {
// under node16/nodenext module resolution, load `types`/ata include names as cjs resolution results by passing an `undefined` mode
automaticTypeDirectiveResolutions.set(automaticTypeDirectiveNames[i], /*mode*/ undefined, resolutions[i]);
Expand All @@ -1805,6 +1813,9 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
}
tracing?.pop();
}
else {
host.onReusedTypeReferenceDirectiveResolutions?.(/*reusedNames*/ undefined, /*containingSourceFile*/ undefined, /*redirectedReference*/ undefined, options);
}

// Do not process the default library if:
// - The '--noLib' flag is used.
Expand Down Expand Up @@ -1938,6 +1949,7 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
getResolvedProjectReferenceByPath,
forEachResolvedProjectReference,
isSourceOfProjectReferenceRedirect,
getRedirectReferenceForResolution,
getRedirectReferenceForResolutionFromSourceOfProject,
getCompilerOptionsForFile,
getDefaultResolutionModeForFile,
Expand Down Expand Up @@ -2193,6 +2205,7 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
redirectedReference: getRedirectReferenceForResolution(containingFile),
nameAndModeGetter: moduleResolutionNameAndModeGetter,
resolutionWorker: resolveModuleNamesWorker,
onReusedResolutions: maybeBind(host, host.onReusedModuleResolutions),
getResolutionFromOldProgram: (name, mode) => oldProgram?.getResolvedModule(containingFile, name, mode),
getResolved: getResolvedModuleFromResolution,
canReuseResolutionsInFile: () =>
Expand All @@ -2213,6 +2226,7 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
redirectedReference: containingSourceFile && getRedirectReferenceForResolution(containingSourceFile),
nameAndModeGetter: typeReferenceResolutionNameAndModeGetter,
resolutionWorker: resolveTypeReferenceDirectiveNamesWorker,
onReusedResolutions: maybeBind(host, host.onReusedTypeReferenceDirectiveResolutions),
getResolutionFromOldProgram: (name, mode) =>
containingSourceFile ?
oldProgram?.getResolvedTypeReferenceDirective(containingSourceFile, name, mode) :
Expand All @@ -2236,6 +2250,14 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
containingFile: SourceFileOrString,
reusedNames: readonly Entry[] | undefined,
) => readonly Resolution[];
onReusedResolutions:
| ((
resuedEntries: readonly Entry[] | undefined,
containingSourceFile: SourceFileOrUndefined,
redirectedReference: ResolvedProjectReference | undefined,
options: CompilerOptions,
) => void)
| undefined;
getResolutionFromOldProgram: (name: string, mode: ResolutionMode) => Resolution | undefined;
getResolved: (oldResolution: Resolution) => ResolutionWithResolvedFileName | undefined;
canReuseResolutionsInFile: () => boolean;
Expand All @@ -2249,12 +2271,21 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
redirectedReference,
nameAndModeGetter,
resolutionWorker,
onReusedResolutions,
getResolutionFromOldProgram,
getResolved,
canReuseResolutionsInFile,
resolveToOwnAmbientModule,
}: ResolveNamesReusingOldStateInput<Entry, SourceFileOrString, SourceFileOrUndefined, Resolution>): readonly Resolution[] {
if (!entries.length) return emptyArray;
if (!entries.length) {
onReusedResolutions?.(
entries,
containingSourceFile,
redirectedReference,
options,
);
return emptyArray;
}
if (structureIsReused === StructureIsReused.Not && (!resolveToOwnAmbientModule || !containingSourceFile!.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.
Expand Down Expand Up @@ -2323,8 +2354,20 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
(unknownEntryIndices ??= []).push(i);
}

if (!unknownEntries) return result!;
const resolutions = resolutionWorker(unknownEntries, containingFile, reusedNames);
if (!unknownEntries) {
onReusedResolutions?.(
reusedNames,
containingSourceFile,
redirectedReference,
options,
);
return result!;
}
const resolutions = resolutionWorker(
unknownEntries,
containingFile,
reusedNames,
);
if (!result) return resolutions;
resolutions.forEach((resolution, index) => result[unknownEntryIndices![index]] = resolution);
return result;
Expand Down Expand Up @@ -3748,6 +3791,9 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
}
(filesWithReferencesProcessed ??= new Set()).add(file.path);
}
else {
host.onSourceFileNotCreated?.(sourceFileOptions);
}
return file;
}

Expand Down Expand Up @@ -3872,7 +3918,15 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro

function processTypeReferenceDirectives(file: SourceFile) {
const typeDirectives = file.typeReferenceDirectives;
if (!typeDirectives.length) return;
if (!typeDirectives.length) {
host.onReusedTypeReferenceDirectiveResolutions?.(
/*reusedNames*/ undefined,
file,
getRedirectReferenceForResolution(file),
options,
);
return;
}

const resolutions = resolvedTypeReferenceDirectiveNamesProcessing?.get(file.path) ||
resolveTypeReferenceDirectiveNamesReusingOldState(typeDirectives, file);
Expand Down Expand Up @@ -4019,7 +4073,7 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
resolveModuleNamesReusingOldState(moduleNames, file);
Debug.assert(resolutions.length === moduleNames.length);
const optionsForFile = getCompilerOptionsForFile(file);
const resolutionsInFile = createModeAwareCache<ResolutionWithFailedLookupLocations>();
const resolutionsInFile = createModeAwareCache<ResolvedModuleWithFailedLookupLocations>();
(resolvedModules ??= new Map()).set(file.path, resolutionsInFile);
for (let index = 0; index < moduleNames.length; index++) {
const resolution = resolutions[index].resolvedModule;
Expand Down Expand Up @@ -4076,6 +4130,14 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
}
}
}
else {
host.onReusedModuleResolutions?.(
/*reusedNames*/ undefined,
file,
getRedirectReferenceForResolution(file),
options,
);
}
}

function checkSourceFilesBelongToPath(sourceFiles: readonly SourceFile[], rootDirectory: string): boolean {
Expand Down
Loading
Loading