mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-15 04:43:37 -05:00
Optimizes project loading in few scenarios (#41126)
* Some refactoring of forEachResolvedProjectReference * More refactoring * Test before the change * When loading project tree, load projects that directly or indirectly reference the projects we are looking for * Optimize finding project in solution scenario by directly finding possible default project through projectReferenceRedirect This helps in avoiding loading indirect projects when solution indirectly referenced default project
This commit is contained in:
@@ -530,6 +530,51 @@ namespace ts {
|
||||
return resolutions;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function forEachResolvedProjectReference<T>(
|
||||
resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined,
|
||||
cb: (resolvedProjectReference: ResolvedProjectReference, parent: ResolvedProjectReference | undefined) => T | undefined
|
||||
): T | undefined {
|
||||
return forEachProjectReference(/*projectReferences*/ undefined, resolvedProjectReferences, (resolvedRef, parent) => resolvedRef && cb(resolvedRef, parent));
|
||||
}
|
||||
|
||||
function forEachProjectReference<T>(
|
||||
projectReferences: readonly ProjectReference[] | undefined,
|
||||
resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined,
|
||||
cbResolvedRef: (resolvedRef: ResolvedProjectReference | undefined, parent: ResolvedProjectReference | undefined, index: number) => T | undefined,
|
||||
cbRef?: (projectReferences: readonly ProjectReference[] | undefined, parent: ResolvedProjectReference | undefined) => T | undefined
|
||||
): T | undefined {
|
||||
let seenResolvedRefs: Set<Path> | undefined;
|
||||
|
||||
return worker(projectReferences, resolvedProjectReferences, /*parent*/ undefined);
|
||||
|
||||
function worker(
|
||||
projectReferences: readonly ProjectReference[] | undefined,
|
||||
resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined,
|
||||
parent: ResolvedProjectReference | undefined,
|
||||
): T | undefined {
|
||||
|
||||
// Visit project references first
|
||||
if (cbRef) {
|
||||
const result = cbRef(projectReferences, parent);
|
||||
if (result) { return result; }
|
||||
}
|
||||
|
||||
return forEach(resolvedProjectReferences, (resolvedRef, index) => {
|
||||
if (resolvedRef && seenResolvedRefs?.has(resolvedRef.sourceFile.path)) {
|
||||
// ignore recursives
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const result = cbResolvedRef(resolvedRef, parent, index);
|
||||
if (result || !resolvedRef) return result;
|
||||
|
||||
(seenResolvedRefs ||= new Set()).add(resolvedRef.sourceFile.path);
|
||||
return worker(resolvedRef.commandLine.projectReferences, resolvedRef.references, resolvedRef);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export const inferredTypesContainingFile = "__inferred type names__.ts";
|
||||
|
||||
@@ -914,8 +959,8 @@ namespace ts {
|
||||
host.onReleaseOldSourceFile(oldSourceFile, oldProgram.getCompilerOptions(), !!getSourceFileByPath(oldSourceFile.path));
|
||||
}
|
||||
}
|
||||
oldProgram.forEachResolvedProjectReference((resolvedProjectReference, resolvedProjectReferencePath) => {
|
||||
if (resolvedProjectReference && !getResolvedProjectReferenceByPath(resolvedProjectReferencePath)) {
|
||||
oldProgram.forEachResolvedProjectReference(resolvedProjectReference => {
|
||||
if (!getResolvedProjectReferenceByPath(resolvedProjectReference.sourceFile.path)) {
|
||||
host.onReleaseOldSourceFile!(resolvedProjectReference.sourceFile, oldProgram!.getCompilerOptions(), /*hasSourceFileByPath*/ false);
|
||||
}
|
||||
});
|
||||
@@ -1038,7 +1083,6 @@ namespace ts {
|
||||
if (!source) return undefined;
|
||||
// Output of .d.ts file so return resolved ref that matches the out file name
|
||||
return forEachResolvedProjectReference(resolvedRef => {
|
||||
if (!resolvedRef) return undefined;
|
||||
const out = outFile(resolvedRef.commandLine.options);
|
||||
if (!out) return undefined;
|
||||
return toPath(out) === filePath ? resolvedRef : undefined;
|
||||
@@ -1251,7 +1295,7 @@ namespace ts {
|
||||
return !forEachProjectReference(
|
||||
oldProgram!.getProjectReferences(),
|
||||
oldProgram!.getResolvedProjectReferences(),
|
||||
(oldResolvedRef, index, parent) => {
|
||||
(oldResolvedRef, parent, index) => {
|
||||
const newRef = (parent ? parent.commandLine.projectReferences : projectReferences)![index];
|
||||
const newResolvedRef = parseProjectReferenceConfigFile(newRef);
|
||||
if (oldResolvedRef) {
|
||||
@@ -2115,9 +2159,7 @@ namespace ts {
|
||||
if (!options.configFile) { return emptyArray; }
|
||||
let diagnostics = programDiagnostics.getDiagnostics(options.configFile.fileName);
|
||||
forEachResolvedProjectReference(resolvedRef => {
|
||||
if (resolvedRef) {
|
||||
diagnostics = concatenate(diagnostics, programDiagnostics.getDiagnostics(resolvedRef.sourceFile.fileName));
|
||||
}
|
||||
diagnostics = concatenate(diagnostics, programDiagnostics.getDiagnostics(resolvedRef.sourceFile.fileName));
|
||||
});
|
||||
return diagnostics;
|
||||
}
|
||||
@@ -2597,12 +2639,11 @@ namespace ts {
|
||||
function getResolvedProjectReferenceToRedirect(fileName: string) {
|
||||
if (mapFromFileToProjectReferenceRedirects === undefined) {
|
||||
mapFromFileToProjectReferenceRedirects = new Map();
|
||||
forEachResolvedProjectReference((referencedProject, referenceProjectPath) => {
|
||||
forEachResolvedProjectReference(referencedProject => {
|
||||
// not input file from the referenced project, ignore
|
||||
if (referencedProject &&
|
||||
toPath(options.configFilePath!) !== referenceProjectPath) {
|
||||
if (toPath(options.configFilePath!) !== referencedProject.sourceFile.path) {
|
||||
referencedProject.commandLine.fileNames.forEach(f =>
|
||||
mapFromFileToProjectReferenceRedirects!.set(toPath(f), referenceProjectPath));
|
||||
mapFromFileToProjectReferenceRedirects!.set(toPath(f), referencedProject.sourceFile.path));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2612,13 +2653,9 @@ namespace ts {
|
||||
}
|
||||
|
||||
function forEachResolvedProjectReference<T>(
|
||||
cb: (resolvedProjectReference: ResolvedProjectReference | undefined, resolvedProjectReferencePath: Path) => T | undefined
|
||||
cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined
|
||||
): T | undefined {
|
||||
return forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, index, parent) => {
|
||||
const ref = (parent ? parent.commandLine.projectReferences : projectReferences)![index];
|
||||
const resolvedRefPath = toPath(resolveProjectReferencePath(ref));
|
||||
return cb(resolvedRef, resolvedRefPath);
|
||||
});
|
||||
return ts.forEachResolvedProjectReference(resolvedProjectReferences, cb);
|
||||
}
|
||||
|
||||
function getSourceOfProjectReferenceRedirect(file: string) {
|
||||
@@ -2626,21 +2663,19 @@ namespace ts {
|
||||
if (mapFromToProjectReferenceRedirectSource === undefined) {
|
||||
mapFromToProjectReferenceRedirectSource = new Map();
|
||||
forEachResolvedProjectReference(resolvedRef => {
|
||||
if (resolvedRef) {
|
||||
const out = outFile(resolvedRef.commandLine.options);
|
||||
if (out) {
|
||||
// Dont know which source file it means so return true?
|
||||
const outputDts = changeExtension(out, Extension.Dts);
|
||||
mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), true);
|
||||
}
|
||||
else {
|
||||
forEach(resolvedRef.commandLine.fileNames, fileName => {
|
||||
if (!fileExtensionIs(fileName, Extension.Dts) && !fileExtensionIs(fileName, Extension.Json)) {
|
||||
const outputDts = getOutputDeclarationFileName(fileName, resolvedRef.commandLine, host.useCaseSensitiveFileNames());
|
||||
mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), fileName);
|
||||
}
|
||||
});
|
||||
}
|
||||
const out = outFile(resolvedRef.commandLine.options);
|
||||
if (out) {
|
||||
// Dont know which source file it means so return true?
|
||||
const outputDts = changeExtension(out, Extension.Dts);
|
||||
mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), true);
|
||||
}
|
||||
else {
|
||||
forEach(resolvedRef.commandLine.fileNames, fileName => {
|
||||
if (!fileExtensionIs(fileName, Extension.Dts) && !fileExtensionIs(fileName, Extension.Json)) {
|
||||
const outputDts = getOutputDeclarationFileName(fileName, resolvedRef.commandLine, host.useCaseSensitiveFileNames());
|
||||
mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), fileName);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2651,49 +2686,6 @@ namespace ts {
|
||||
return useSourceOfProjectReferenceRedirect && !!getResolvedProjectReferenceToRedirect(fileName);
|
||||
}
|
||||
|
||||
function forEachProjectReference<T>(
|
||||
projectReferences: readonly ProjectReference[] | undefined,
|
||||
resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined,
|
||||
cbResolvedRef: (resolvedRef: ResolvedProjectReference | undefined, index: number, parent: ResolvedProjectReference | undefined) => T | undefined,
|
||||
cbRef?: (projectReferences: readonly ProjectReference[] | undefined, parent: ResolvedProjectReference | undefined) => T | undefined
|
||||
): T | undefined {
|
||||
let seenResolvedRefs: ResolvedProjectReference[] | undefined;
|
||||
|
||||
return worker(projectReferences, resolvedProjectReferences, /*parent*/ undefined, cbResolvedRef, cbRef);
|
||||
|
||||
function worker(
|
||||
projectReferences: readonly ProjectReference[] | undefined,
|
||||
resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined,
|
||||
parent: ResolvedProjectReference | undefined,
|
||||
cbResolvedRef: (resolvedRef: ResolvedProjectReference | undefined, index: number, parent: ResolvedProjectReference | undefined) => T | undefined,
|
||||
cbRef?: (projectReferences: readonly ProjectReference[] | undefined, parent: ResolvedProjectReference | undefined) => T | undefined,
|
||||
): T | undefined {
|
||||
|
||||
// Visit project references first
|
||||
if (cbRef) {
|
||||
const result = cbRef(projectReferences, parent);
|
||||
if (result) { return result; }
|
||||
}
|
||||
|
||||
return forEach(resolvedProjectReferences, (resolvedRef, index) => {
|
||||
if (contains(seenResolvedRefs, resolvedRef)) {
|
||||
// ignore recursives
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const result = cbResolvedRef(resolvedRef, index, parent);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!resolvedRef) return undefined;
|
||||
|
||||
(seenResolvedRefs || (seenResolvedRefs = [])).push(resolvedRef);
|
||||
return worker(resolvedRef.commandLine.projectReferences, resolvedRef.references, resolvedRef, cbResolvedRef, cbRef);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getResolvedProjectReferenceByPath(projectReferencePath: Path): ResolvedProjectReference | undefined {
|
||||
if (!projectReferenceRedirects) {
|
||||
return undefined;
|
||||
@@ -3348,7 +3340,7 @@ namespace ts {
|
||||
|
||||
function verifyProjectReferences() {
|
||||
const buildInfoPath = !options.suppressOutputPathCheck ? getTsBuildInfoEmitOutputFilePath(options) : undefined;
|
||||
forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, index, parent) => {
|
||||
forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, parent, index) => {
|
||||
const ref = (parent ? parent.commandLine.projectReferences : projectReferences)![index];
|
||||
const parentFile = parent && parent.sourceFile as JsonSourceFile;
|
||||
if (!resolvedRef) {
|
||||
@@ -3546,7 +3538,7 @@ namespace ts {
|
||||
toPath(fileName: string): Path;
|
||||
getResolvedProjectReferences(): readonly (ResolvedProjectReference | undefined)[] | undefined;
|
||||
getSourceOfProjectReferenceRedirect(fileName: string): SourceOfProjectReferenceRedirect | undefined;
|
||||
forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference | undefined, resolvedProjectReferencePath: Path) => T | undefined): T | undefined;
|
||||
forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined): T | undefined;
|
||||
}
|
||||
|
||||
function updateHostForUseSourceOfProjectReferenceRedirect(host: HostForUseSourceOfProjectReferenceRedirect) {
|
||||
@@ -3576,7 +3568,6 @@ namespace ts {
|
||||
if (!setOfDeclarationDirectories) {
|
||||
setOfDeclarationDirectories = new Set();
|
||||
host.forEachResolvedProjectReference(ref => {
|
||||
if (!ref) return;
|
||||
const out = outFile(ref.commandLine.options);
|
||||
if (out) {
|
||||
setOfDeclarationDirectories!.add(getDirectoryPath(host.toPath(out)));
|
||||
|
||||
@@ -3800,7 +3800,7 @@ namespace ts {
|
||||
getResolvedProjectReferences(): readonly (ResolvedProjectReference | undefined)[] | undefined;
|
||||
/*@internal*/ getProjectReferenceRedirect(fileName: string): string | undefined;
|
||||
/*@internal*/ getResolvedProjectReferenceToRedirect(fileName: string): ResolvedProjectReference | undefined;
|
||||
/*@internal*/ forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference | undefined, resolvedProjectReferencePath: Path) => T | undefined): T | undefined;
|
||||
/*@internal*/ forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined): T | undefined;
|
||||
/*@internal*/ getResolvedProjectReferenceByPath(projectReferencePath: Path): ResolvedProjectReference | undefined;
|
||||
/*@internal*/ isSourceOfProjectReferenceRedirect(fileName: string): boolean;
|
||||
/*@internal*/ getProgramBuildInfo?(): ProgramBuildInfo | undefined;
|
||||
@@ -6333,7 +6333,7 @@ namespace ts {
|
||||
/*@internal*/
|
||||
export interface ResolvedProjectReferenceCallbacks {
|
||||
getSourceOfProjectReferenceRedirect(fileName: string): SourceOfProjectReferenceRedirect | undefined;
|
||||
forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference | undefined, resolvedProjectReferencePath: Path) => T | undefined): T | undefined;
|
||||
forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined): T | undefined;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
|
||||
@@ -444,64 +444,105 @@ namespace ts.server {
|
||||
/*@internal*/
|
||||
export function forEachResolvedProjectReferenceProject<T>(
|
||||
project: ConfiguredProject,
|
||||
fileName: string | undefined,
|
||||
cb: (child: ConfiguredProject) => T | undefined,
|
||||
projectReferenceProjectLoadKind: ProjectReferenceProjectLoadKind.Find | ProjectReferenceProjectLoadKind.FindCreate,
|
||||
): T | undefined;
|
||||
/*@internal*/
|
||||
export function forEachResolvedProjectReferenceProject<T>(
|
||||
project: ConfiguredProject,
|
||||
fileName: string | undefined,
|
||||
cb: (child: ConfiguredProject) => T | undefined,
|
||||
projectReferenceProjectLoadKind: ProjectReferenceProjectLoadKind,
|
||||
reason: string
|
||||
): T | undefined;
|
||||
export function forEachResolvedProjectReferenceProject<T>(
|
||||
project: ConfiguredProject,
|
||||
fileName: string | undefined,
|
||||
cb: (child: ConfiguredProject) => T | undefined,
|
||||
projectReferenceProjectLoadKind: ProjectReferenceProjectLoadKind,
|
||||
reason?: string
|
||||
): T | undefined {
|
||||
const resolvedRefs = project.getCurrentProgram()?.getResolvedProjectReferences();
|
||||
if (!resolvedRefs) return undefined;
|
||||
let seenResolvedRefs: ESMap<string, ProjectReferenceProjectLoadKind> | undefined;
|
||||
return worker(project.getCurrentProgram()?.getResolvedProjectReferences(), project.getCompilerOptions());
|
||||
|
||||
function worker(resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined, parentOptions: CompilerOptions): T | undefined {
|
||||
const loadKind = parentOptions.disableReferencedProjectLoad ? ProjectReferenceProjectLoadKind.Find : projectReferenceProjectLoadKind;
|
||||
return forEach(resolvedProjectReferences, ref => {
|
||||
if (!ref) return undefined;
|
||||
|
||||
const configFileName = toNormalizedPath(ref.sourceFile.fileName);
|
||||
const canonicalPath = project.projectService.toCanonicalFileName(configFileName);
|
||||
const seenValue = seenResolvedRefs?.get(canonicalPath);
|
||||
if (seenValue !== undefined && seenValue >= loadKind) {
|
||||
return undefined;
|
||||
}
|
||||
const child = project.projectService.findConfiguredProjectByProjectName(configFileName) || (
|
||||
loadKind === ProjectReferenceProjectLoadKind.Find ?
|
||||
undefined :
|
||||
loadKind === ProjectReferenceProjectLoadKind.FindCreate ?
|
||||
project.projectService.createConfiguredProject(configFileName) :
|
||||
loadKind === ProjectReferenceProjectLoadKind.FindCreateLoad ?
|
||||
project.projectService.createAndLoadConfiguredProject(configFileName, reason!) :
|
||||
Debug.assertNever(loadKind)
|
||||
const possibleDefaultRef = fileName ? project.getResolvedProjectReferenceToRedirect(fileName) : undefined;
|
||||
if (possibleDefaultRef) {
|
||||
// Try to find the name of the file directly through resolved project references
|
||||
const configFileName = toNormalizedPath(possibleDefaultRef.sourceFile.fileName);
|
||||
const child = project.projectService.findConfiguredProjectByProjectName(configFileName);
|
||||
if (child) {
|
||||
const result = cb(child);
|
||||
if (result) return result;
|
||||
}
|
||||
else if (projectReferenceProjectLoadKind !== ProjectReferenceProjectLoadKind.Find) {
|
||||
seenResolvedRefs = new Map();
|
||||
// Try to see if this project can be loaded
|
||||
const result = forEachResolvedProjectReferenceProjectWorker(
|
||||
resolvedRefs,
|
||||
project.getCompilerOptions(),
|
||||
(ref, loadKind) => possibleDefaultRef === ref ? callback(ref, loadKind) : undefined,
|
||||
projectReferenceProjectLoadKind,
|
||||
project.projectService,
|
||||
seenResolvedRefs
|
||||
);
|
||||
if (result) return result;
|
||||
// Cleanup seenResolvedRefs
|
||||
seenResolvedRefs.clear();
|
||||
}
|
||||
}
|
||||
|
||||
const result = child && cb(child);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
return forEachResolvedProjectReferenceProjectWorker(
|
||||
resolvedRefs,
|
||||
project.getCompilerOptions(),
|
||||
(ref, loadKind) => possibleDefaultRef !== ref ? callback(ref, loadKind) : undefined,
|
||||
projectReferenceProjectLoadKind,
|
||||
project.projectService,
|
||||
seenResolvedRefs
|
||||
);
|
||||
|
||||
(seenResolvedRefs || (seenResolvedRefs = new Map())).set(canonicalPath, loadKind);
|
||||
return worker(ref.references, ref.commandLine.options);
|
||||
});
|
||||
function callback(ref: ResolvedProjectReference, loadKind: ProjectReferenceProjectLoadKind) {
|
||||
const configFileName = toNormalizedPath(ref.sourceFile.fileName);
|
||||
const child = project.projectService.findConfiguredProjectByProjectName(configFileName) || (
|
||||
loadKind === ProjectReferenceProjectLoadKind.Find ?
|
||||
undefined :
|
||||
loadKind === ProjectReferenceProjectLoadKind.FindCreate ?
|
||||
project.projectService.createConfiguredProject(configFileName) :
|
||||
loadKind === ProjectReferenceProjectLoadKind.FindCreateLoad ?
|
||||
project.projectService.createAndLoadConfiguredProject(configFileName, reason!) :
|
||||
Debug.assertNever(loadKind)
|
||||
);
|
||||
|
||||
return child && cb(child);
|
||||
}
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
export function forEachResolvedProjectReference<T>(
|
||||
project: ConfiguredProject,
|
||||
cb: (resolvedProjectReference: ResolvedProjectReference | undefined, resolvedProjectReferencePath: Path) => T | undefined
|
||||
function forEachResolvedProjectReferenceProjectWorker<T>(
|
||||
resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[],
|
||||
parentOptions: CompilerOptions,
|
||||
cb: (resolvedRef: ResolvedProjectReference, loadKind: ProjectReferenceProjectLoadKind) => T | undefined,
|
||||
projectReferenceProjectLoadKind: ProjectReferenceProjectLoadKind,
|
||||
projectService: ProjectService,
|
||||
seenResolvedRefs: ESMap<string, ProjectReferenceProjectLoadKind> | undefined,
|
||||
): T | undefined {
|
||||
const program = project.getCurrentProgram();
|
||||
return program && program.forEachResolvedProjectReference(cb);
|
||||
const loadKind = parentOptions.disableReferencedProjectLoad ? ProjectReferenceProjectLoadKind.Find : projectReferenceProjectLoadKind;
|
||||
return forEach(resolvedProjectReferences, ref => {
|
||||
if (!ref) return undefined;
|
||||
|
||||
const configFileName = toNormalizedPath(ref.sourceFile.fileName);
|
||||
const canonicalPath = projectService.toCanonicalFileName(configFileName);
|
||||
const seenValue = seenResolvedRefs?.get(canonicalPath);
|
||||
if (seenValue !== undefined && seenValue >= loadKind) {
|
||||
return undefined;
|
||||
}
|
||||
const result = cb(ref, loadKind);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
(seenResolvedRefs || (seenResolvedRefs = new Map())).set(canonicalPath, loadKind);
|
||||
return ref.references && forEachResolvedProjectReferenceProjectWorker(ref.references, ref.commandLine.options, cb, loadKind, projectService, seenResolvedRefs);
|
||||
});
|
||||
}
|
||||
|
||||
function forEachPotentialProjectReference<T>(
|
||||
@@ -514,12 +555,12 @@ namespace ts.server {
|
||||
|
||||
function forEachAnyProjectReferenceKind<T>(
|
||||
project: ConfiguredProject,
|
||||
cb: (resolvedProjectReference: ResolvedProjectReference | undefined, resolvedProjectReferencePath: Path) => T | undefined,
|
||||
cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined,
|
||||
cbProjectRef: (projectReference: ProjectReference) => T | undefined,
|
||||
cbPotentialProjectRef: (potentialProjectReference: Path) => T | undefined
|
||||
): T | undefined {
|
||||
return project.getCurrentProgram() ?
|
||||
forEachResolvedProjectReference(project, cb) :
|
||||
project.forEachResolvedProjectReference(cb) :
|
||||
project.isInitialLoadPending() ?
|
||||
forEachPotentialProjectReference(project, cbPotentialProjectRef) :
|
||||
forEach(project.getProjectReferences(), cbProjectRef);
|
||||
@@ -540,8 +581,8 @@ namespace ts.server {
|
||||
): T | undefined {
|
||||
return forEachAnyProjectReferenceKind(
|
||||
project,
|
||||
resolvedRef => callbackRefProject(project, cb, resolvedRef && resolvedRef.sourceFile.path),
|
||||
projectRef => callbackRefProject(project, cb, project.toPath(projectRef.path)),
|
||||
resolvedRef => callbackRefProject(project, cb, resolvedRef.sourceFile.path),
|
||||
projectRef => callbackRefProject(project, cb, project.toPath(resolveProjectReferencePath(projectRef))),
|
||||
potentialProjectRef => callbackRefProject(project, cb, potentialProjectRef)
|
||||
);
|
||||
}
|
||||
@@ -2875,6 +2916,7 @@ namespace ts.server {
|
||||
if (!projectContainsInfoDirectly(project, info)) {
|
||||
const referencedProject = forEachResolvedProjectReferenceProject(
|
||||
project,
|
||||
info.path,
|
||||
child => {
|
||||
reloadChildProject(child);
|
||||
return projectContainsInfoDirectly(child, info);
|
||||
@@ -2885,6 +2927,7 @@ namespace ts.server {
|
||||
// Reload the project's tree that is already present
|
||||
forEachResolvedProjectReferenceProject(
|
||||
project,
|
||||
/*fileName*/ undefined,
|
||||
reloadChildProject,
|
||||
ProjectReferenceProjectLoadKind.Find
|
||||
);
|
||||
@@ -2990,11 +3033,12 @@ namespace ts.server {
|
||||
// Find the project that is referenced from this solution that contains the script info directly
|
||||
configuredProject = forEachResolvedProjectReferenceProject(
|
||||
configuredProject,
|
||||
fileName,
|
||||
child => {
|
||||
updateProjectIfDirty(child);
|
||||
return projectContainsOriginalInfo(child) ? child : undefined;
|
||||
},
|
||||
configuredProject.getCompilerOptions().disableReferencedProjectLoad ? ProjectReferenceProjectLoadKind.Find : ProjectReferenceProjectLoadKind.FindCreateLoad,
|
||||
ProjectReferenceProjectLoadKind.FindCreateLoad,
|
||||
`Creating project referenced in solution ${configuredProject.projectName} to find possible configured project for original file: ${originalFileInfo.fileName}${location !== originalLocation ? " for location: " + location.fileName : ""}`
|
||||
);
|
||||
if (!configuredProject) return undefined;
|
||||
@@ -3070,6 +3114,7 @@ namespace ts.server {
|
||||
if (!projectContainsInfoDirectly(project, info)) {
|
||||
forEachResolvedProjectReferenceProject(
|
||||
project,
|
||||
info.path,
|
||||
child => {
|
||||
updateProjectIfDirty(child);
|
||||
// Retain these projects
|
||||
@@ -3184,32 +3229,37 @@ namespace ts.server {
|
||||
// Work on array copy as we could add more projects as part of callback
|
||||
for (const project of arrayFrom(this.configuredProjects.values())) {
|
||||
// If this project has potential project reference for any of the project we are loading ancestor tree for
|
||||
// we need to load this project tree
|
||||
if (forEachPotentialProjectReference(
|
||||
project,
|
||||
potentialRefPath => forProjects!.has(potentialRefPath)
|
||||
) || forEachResolvedProjectReference(
|
||||
project,
|
||||
(_ref, resolvedPath) => forProjects!.has(resolvedPath)
|
||||
)) {
|
||||
// Load children
|
||||
this.ensureProjectChildren(project, seenProjects);
|
||||
// load this project first
|
||||
if (forEachPotentialProjectReference(project, potentialRefPath => forProjects!.has(potentialRefPath))) {
|
||||
updateProjectIfDirty(project);
|
||||
}
|
||||
this.ensureProjectChildren(project, forProjects, seenProjects);
|
||||
}
|
||||
}
|
||||
|
||||
private ensureProjectChildren(project: ConfiguredProject, seenProjects: Set<NormalizedPath>) {
|
||||
private ensureProjectChildren(project: ConfiguredProject, forProjects: ReadonlyCollection<string>, seenProjects: Set<NormalizedPath>) {
|
||||
if (!tryAddToSet(seenProjects, project.canonicalConfigFilePath)) return;
|
||||
// Update the project
|
||||
updateProjectIfDirty(project);
|
||||
|
||||
// Create tree because project is uptodate we only care of resolved references
|
||||
forEachResolvedProjectReferenceProject(
|
||||
project,
|
||||
child => this.ensureProjectChildren(child, seenProjects),
|
||||
ProjectReferenceProjectLoadKind.FindCreateLoad,
|
||||
`Creating project for reference of project: ${project.projectName}`
|
||||
);
|
||||
// If this project disables child load ignore it
|
||||
if (project.getCompilerOptions().disableReferencedProjectLoad) return;
|
||||
|
||||
const children = project.getCurrentProgram()?.getResolvedProjectReferences();
|
||||
if (!children) return;
|
||||
|
||||
for (const child of children) {
|
||||
if (!child) continue;
|
||||
const referencedProject = forEachResolvedProjectReference(child.references, ref => forProjects.has(ref.sourceFile.path) ? ref : undefined);
|
||||
if (!referencedProject) continue;
|
||||
|
||||
// Load this project,
|
||||
const configFileName = toNormalizedPath(child.sourceFile.fileName);
|
||||
const childProject = project.projectService.findConfiguredProjectByProjectName(configFileName) ||
|
||||
project.projectService.createAndLoadConfiguredProject(configFileName, `Creating project referenced by : ${project.projectName} as it references project ${referencedProject.sourceFile.fileName}`);
|
||||
updateProjectIfDirty(childProject);
|
||||
|
||||
// Ensure children for this project
|
||||
this.ensureProjectChildren(childProject, forProjects, seenProjects);
|
||||
}
|
||||
}
|
||||
|
||||
private cleanupAfterOpeningFile(toRetainConfigProjects: readonly ConfiguredProject[] | ConfiguredProject | undefined) {
|
||||
|
||||
@@ -750,11 +750,8 @@ namespace ts.server {
|
||||
for (const f of this.program.getSourceFiles()) {
|
||||
this.detachScriptInfoIfNotRoot(f.fileName);
|
||||
}
|
||||
this.program.forEachResolvedProjectReference(ref => {
|
||||
if (ref) {
|
||||
this.detachScriptInfoFromProject(ref.sourceFile.fileName);
|
||||
}
|
||||
});
|
||||
this.program.forEachResolvedProjectReference(ref =>
|
||||
this.detachScriptInfoFromProject(ref.sourceFile.fileName));
|
||||
}
|
||||
|
||||
// Release external files
|
||||
@@ -1099,8 +1096,8 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
oldProgram.forEachResolvedProjectReference((resolvedProjectReference, resolvedProjectReferencePath) => {
|
||||
if (resolvedProjectReference && !this.program!.getResolvedProjectReferenceByPath(resolvedProjectReferencePath)) {
|
||||
oldProgram.forEachResolvedProjectReference(resolvedProjectReference => {
|
||||
if (!this.program!.getResolvedProjectReferenceByPath(resolvedProjectReference.sourceFile.path)) {
|
||||
this.detachScriptInfoFromProject(resolvedProjectReference.sourceFile.fileName);
|
||||
}
|
||||
});
|
||||
@@ -2201,6 +2198,13 @@ namespace ts.server {
|
||||
return program && program.getResolvedProjectReferenceToRedirect(fileName);
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
forEachResolvedProjectReference<T>(
|
||||
cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined
|
||||
): T | undefined {
|
||||
return this.getCurrentProgram()?.forEachResolvedProjectReference(cb);
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
enablePluginsWithOptions(options: CompilerOptions, pluginConfigOverrides: ESMap<string, any> | undefined) {
|
||||
const host = this.projectService.host;
|
||||
@@ -2301,6 +2305,7 @@ namespace ts.server {
|
||||
getDefaultChildProjectFromProjectWithReferences(info: ScriptInfo) {
|
||||
return forEachResolvedProjectReferenceProject(
|
||||
this,
|
||||
info.path,
|
||||
child => projectContainsInfoDirectly(child, info) ?
|
||||
child :
|
||||
undefined,
|
||||
@@ -2338,6 +2343,7 @@ namespace ts.server {
|
||||
return this.containsScriptInfo(info) ||
|
||||
!!forEachResolvedProjectReferenceProject(
|
||||
this,
|
||||
info.path,
|
||||
child => child.containsScriptInfo(info),
|
||||
ProjectReferenceProjectLoadKind.Find
|
||||
);
|
||||
|
||||
@@ -2189,19 +2189,14 @@ export function bar() {}`
|
||||
verifySolutionScenario({
|
||||
configRefs: ["./tsconfig-indirect1.json", "./tsconfig-indirect2.json"],
|
||||
additionalFiles: [tsconfigIndirect, indirect, tsconfigIndirect2, indirect2],
|
||||
additionalProjects: [{
|
||||
projectName: tsconfigIndirect.path,
|
||||
files: [tsconfigIndirect.path, main.path, helper.path, indirect.path, libFile.path]
|
||||
}],
|
||||
additionalProjects: emptyArray,
|
||||
expectedOpenEvents: [
|
||||
...expectedSolutionLoadAndTelemetry(),
|
||||
...expectedProjectReferenceLoadAndTelemetry(tsconfigIndirect.path),
|
||||
...expectedProjectReferenceLoadAndTelemetry(tsconfigSrcPath),
|
||||
configFileDiagEvent(main.path, tsconfigSrcPath, [])
|
||||
],
|
||||
expectedReloadEvents: [
|
||||
...expectedReloadEvent(tsconfigPath),
|
||||
...expectedReloadEvent(tsconfigIndirect.path),
|
||||
...expectedReloadEvent(tsconfigSrcPath),
|
||||
],
|
||||
expectedReferences: {
|
||||
@@ -2217,8 +2212,8 @@ export function bar() {}`
|
||||
refs: [
|
||||
...expectedIndirectRefs(fileResolvingToMainDts),
|
||||
...refs,
|
||||
...expectedIndirectRefs(indirect2),
|
||||
...expectedIndirectRefs(indirect),
|
||||
...expectedIndirectRefs(indirect2),
|
||||
],
|
||||
symbolDisplayString: "(alias) const foo: 1\nimport foo",
|
||||
}
|
||||
@@ -2296,8 +2291,6 @@ export function bar() {}`
|
||||
const expectedProjectsOnOpen: VerifyProjects = {
|
||||
configuredProjects: [
|
||||
{ projectName: tsconfigPath, files: [tsconfigPath] },
|
||||
{ projectName: tsconfigIndirect.path, files: [tsconfigIndirect.path, main.path, helper.path, indirect.path, libFile.path] },
|
||||
{ projectName: tsconfigIndirect2.path, files: [tsconfigIndirect2.path, main.path, helper.path, indirect2.path, libFile.path] },
|
||||
{ projectName: tsconfigSrcPath, files: [tsconfigSrcPath, main.path, helper.path, libFile.path] },
|
||||
],
|
||||
inferredProjects: emptyArray
|
||||
@@ -2307,8 +2300,6 @@ export function bar() {}`
|
||||
additionalFiles: [tsconfigIndirect, indirect, tsconfigIndirect2, indirect2],
|
||||
expectedOpenEvents: [
|
||||
...expectedSolutionLoadAndTelemetry(),
|
||||
...expectedProjectReferenceLoadAndTelemetry(tsconfigIndirect.path),
|
||||
...expectedProjectReferenceLoadAndTelemetry(tsconfigIndirect2.path),
|
||||
...expectedProjectReferenceLoadAndTelemetry(tsconfigSrcPath),
|
||||
configFileDiagEvent(main.path, tsconfigSrcPath, [])
|
||||
],
|
||||
@@ -2317,9 +2308,7 @@ export function bar() {}`
|
||||
expectedProjectsOnOpen,
|
||||
expectedReloadEvents: [
|
||||
...expectedReloadEvent(tsconfigPath),
|
||||
...expectedReloadEvent(tsconfigIndirect.path),
|
||||
...expectedReloadEvent(tsconfigSrcPath),
|
||||
...expectedReloadEvent(tsconfigIndirect2.path),
|
||||
]
|
||||
});
|
||||
});
|
||||
@@ -2387,19 +2376,14 @@ bar;`
|
||||
solutionProject: [tsconfigPath, indirect.path, ownMain.path, main.path, libFile.path, helper.path],
|
||||
configRefs: ["./tsconfig-indirect1.json", "./tsconfig-indirect2.json"],
|
||||
additionalFiles: [tsconfigIndirect, indirect, tsconfigIndirect2, indirect2, ownMain],
|
||||
additionalProjects: [{
|
||||
projectName: tsconfigIndirect.path,
|
||||
files: [tsconfigIndirect.path, main.path, helper.path, indirect.path, libFile.path]
|
||||
}],
|
||||
additionalProjects: emptyArray,
|
||||
expectedOpenEvents: [
|
||||
...expectedSolutionLoadAndTelemetry(),
|
||||
...expectedProjectReferenceLoadAndTelemetry(tsconfigIndirect.path),
|
||||
...expectedProjectReferenceLoadAndTelemetry(tsconfigSrcPath),
|
||||
configFileDiagEvent(main.path, tsconfigSrcPath, [])
|
||||
],
|
||||
expectedReloadEvents: [
|
||||
...expectedReloadEvent(tsconfigPath),
|
||||
...expectedReloadEvent(tsconfigIndirect.path),
|
||||
...expectedReloadEvent(tsconfigSrcPath),
|
||||
],
|
||||
expectedReferences: {
|
||||
@@ -2415,8 +2399,8 @@ bar;`
|
||||
refs: [
|
||||
...expectedIndirectRefs(fileResolvingToMainDts),
|
||||
...refs,
|
||||
...expectedIndirectRefs(indirect2),
|
||||
...expectedIndirectRefs(indirect),
|
||||
...expectedIndirectRefs(indirect2),
|
||||
],
|
||||
symbolDisplayString: "(alias) const foo: 1\nimport foo",
|
||||
}
|
||||
@@ -2502,8 +2486,6 @@ bar;`
|
||||
const expectedProjectsOnOpen: VerifyProjects = {
|
||||
configuredProjects: [
|
||||
{ projectName: tsconfigPath, files: [tsconfigPath, indirect.path, ownMain.path, main.path, libFile.path, helper.path] },
|
||||
{ projectName: tsconfigIndirect.path, files: [tsconfigIndirect.path, main.path, helper.path, indirect.path, libFile.path] },
|
||||
{ projectName: tsconfigIndirect2.path, files: [tsconfigIndirect2.path, main.path, helper.path, indirect2.path, libFile.path] },
|
||||
{ projectName: tsconfigSrcPath, files: [tsconfigSrcPath, main.path, helper.path, libFile.path] },
|
||||
],
|
||||
inferredProjects: emptyArray
|
||||
@@ -2518,8 +2500,6 @@ bar;`
|
||||
additionalFiles: [tsconfigIndirect, indirect, tsconfigIndirect2, indirect2, ownMain],
|
||||
expectedOpenEvents: [
|
||||
...expectedSolutionLoadAndTelemetry(),
|
||||
...expectedProjectReferenceLoadAndTelemetry(tsconfigIndirect.path),
|
||||
...expectedProjectReferenceLoadAndTelemetry(tsconfigIndirect2.path),
|
||||
...expectedProjectReferenceLoadAndTelemetry(tsconfigSrcPath),
|
||||
configFileDiagEvent(main.path, tsconfigSrcPath, [])
|
||||
],
|
||||
@@ -2528,9 +2508,7 @@ bar;`
|
||||
expectedProjectsOnOpen,
|
||||
expectedReloadEvents: [
|
||||
...expectedReloadEvent(tsconfigPath),
|
||||
...expectedReloadEvent(tsconfigIndirect.path),
|
||||
...expectedReloadEvent(tsconfigSrcPath),
|
||||
...expectedReloadEvent(tsconfigIndirect2.path),
|
||||
]
|
||||
});
|
||||
});
|
||||
@@ -2645,5 +2623,75 @@ bar;`
|
||||
verifyAutoImport(/*built*/ true, /*disableSourceOfProjectReferenceRedirect*/ true);
|
||||
});
|
||||
});
|
||||
|
||||
it("when files from two projects are open and one project references", () => {
|
||||
function getPackageAndFile(packageName: string, references?: string[], optionsToExtend?: CompilerOptions): [file: File, config: File] {
|
||||
const file: File = {
|
||||
path: `${tscWatch.projectRoot}/${packageName}/src/file1.ts`,
|
||||
content: `export const ${packageName}Const = 10;`
|
||||
};
|
||||
const config: File = {
|
||||
path: `${tscWatch.projectRoot}/${packageName}/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
compilerOptions: { composite: true, ...optionsToExtend || {} },
|
||||
references: references?.map(path => ({ path: `../${path}` }))
|
||||
})
|
||||
};
|
||||
return [file, config];
|
||||
}
|
||||
const [mainFile, mainConfig] = getPackageAndFile("main", ["core", "indirect", "noCoreRef1", "indirectDisabledChildLoad1", "indirectDisabledChildLoad2", "refToCoreRef3", "indirectNoCoreRef"]);
|
||||
const [coreFile, coreConfig] = getPackageAndFile("core");
|
||||
const [noCoreRef1File, noCoreRef1Config] = getPackageAndFile("noCoreRef1");
|
||||
const [indirectFile, indirectConfig] = getPackageAndFile("indirect", ["coreRef1"]);
|
||||
const [coreRef1File, coreRef1Config] = getPackageAndFile("coreRef1", ["core"]);
|
||||
const [indirectDisabledChildLoad1File, indirectDisabledChildLoad1Config] = getPackageAndFile("indirectDisabledChildLoad1", ["coreRef2"], { disableReferencedProjectLoad: true });
|
||||
const [coreRef2File, coreRef2Config] = getPackageAndFile("coreRef2", ["core"]);
|
||||
const [indirectDisabledChildLoad2File, indirectDisabledChildLoad2Config] = getPackageAndFile("indirectDisabledChildLoad2", ["coreRef3"], { disableReferencedProjectLoad: true });
|
||||
const [coreRef3File, coreRef3Config] = getPackageAndFile("coreRef3", ["core"]);
|
||||
const [refToCoreRef3File, refToCoreRef3Config] = getPackageAndFile("refToCoreRef3", ["coreRef3"]);
|
||||
const [indirectNoCoreRefFile, indirectNoCoreRefConfig] = getPackageAndFile("indirectNoCoreRef", ["noCoreRef2"]);
|
||||
const [noCoreRef2File, noCoreRef2Config] = getPackageAndFile("noCoreRef2");
|
||||
|
||||
const host = createServerHost([
|
||||
libFile, mainFile, mainConfig, coreFile, coreConfig, noCoreRef1File, noCoreRef1Config,
|
||||
indirectFile, indirectConfig, coreRef1File, coreRef1Config,
|
||||
indirectDisabledChildLoad1File, indirectDisabledChildLoad1Config, coreRef2File, coreRef2Config,
|
||||
indirectDisabledChildLoad2File, indirectDisabledChildLoad2Config, coreRef3File, coreRef3Config,
|
||||
refToCoreRef3File, refToCoreRef3Config,
|
||||
indirectNoCoreRefFile, indirectNoCoreRefConfig, noCoreRef2File, noCoreRef2Config
|
||||
], { useCaseSensitiveFileNames: true });
|
||||
const session = createSession(host);
|
||||
const service = session.getProjectService();
|
||||
openFilesForSession([mainFile, coreFile], session);
|
||||
|
||||
verifyProject(mainConfig);
|
||||
verifyProject(coreConfig);
|
||||
|
||||
// Find all refs in coreFile
|
||||
session.executeCommandSeq<protocol.ReferencesRequest>({
|
||||
command: protocol.CommandTypes.References,
|
||||
arguments: protocolFileLocationFromSubstring(coreFile, `coreConst`)
|
||||
});
|
||||
verifyProject(mainConfig);
|
||||
verifyProject(coreConfig);
|
||||
verifyNoProject(noCoreRef1Config); // Should not be loaded
|
||||
verifyProject(indirectConfig);
|
||||
verifyProject(coreRef1Config);
|
||||
verifyProject(indirectDisabledChildLoad1Config);
|
||||
verifyNoProject(coreRef2Config); // Should not be loaded
|
||||
verifyProject(indirectDisabledChildLoad2Config);
|
||||
verifyProject(coreRef3Config);
|
||||
verifyProject(refToCoreRef3Config);
|
||||
verifyNoProject(indirectNoCoreRefConfig); // Should not be loaded
|
||||
verifyNoProject(noCoreRef2Config); // Should not be loaded
|
||||
|
||||
function verifyProject(config: File) {
|
||||
assert.isDefined(service.configuredProjects.get(config.path), `Expected to find ${config.path}`);
|
||||
}
|
||||
|
||||
function verifyNoProject(config: File) {
|
||||
assert.isUndefined(service.configuredProjects.get(config.path), `Expected to not find ${config.path}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user