mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-17 21:06:50 -05:00
Add disableReferencedProjectLoad to stop loading child projects to allow users to disable loading large solutions (#39593)
* Use disableReferencedProjectLoad to stop loading child projects to allow users to disable loading large solutions Fixes #39144 * Handle indirect references * PR feedback
This commit is contained in:
@@ -432,34 +432,55 @@ namespace ts.server {
|
||||
/*@internal*/
|
||||
export function forEachResolvedProjectReferenceProject<T>(
|
||||
project: ConfiguredProject,
|
||||
cb: (child: ConfiguredProject, configFileName: NormalizedPath) => T | undefined,
|
||||
projectReferenceProjectLoadKind: ProjectReferenceProjectLoadKind.Find | ProjectReferenceProjectLoadKind.FindCreate
|
||||
cb: (child: ConfiguredProject) => T | undefined,
|
||||
projectReferenceProjectLoadKind: ProjectReferenceProjectLoadKind.Find | ProjectReferenceProjectLoadKind.FindCreate,
|
||||
): T | undefined;
|
||||
/*@internal*/
|
||||
export function forEachResolvedProjectReferenceProject<T>(
|
||||
project: ConfiguredProject,
|
||||
cb: (child: ConfiguredProject, configFileName: NormalizedPath) => T | undefined,
|
||||
projectReferenceProjectLoadKind: ProjectReferenceProjectLoadKind.FindCreateLoad,
|
||||
cb: (child: ConfiguredProject) => T | undefined,
|
||||
projectReferenceProjectLoadKind: ProjectReferenceProjectLoadKind,
|
||||
reason: string
|
||||
): T | undefined;
|
||||
export function forEachResolvedProjectReferenceProject<T>(
|
||||
project: ConfiguredProject,
|
||||
cb: (child: ConfiguredProject, configFileName: NormalizedPath) => T | undefined,
|
||||
cb: (child: ConfiguredProject) => T | undefined,
|
||||
projectReferenceProjectLoadKind: ProjectReferenceProjectLoadKind,
|
||||
reason?: string
|
||||
): T | undefined {
|
||||
return forEachResolvedProjectReference(project, ref => {
|
||||
if (!ref) return undefined;
|
||||
const configFileName = toNormalizedPath(ref.sourceFile.fileName);
|
||||
const child = project.projectService.findConfiguredProjectByProjectName(configFileName) || (
|
||||
projectReferenceProjectLoadKind === ProjectReferenceProjectLoadKind.FindCreate ?
|
||||
project.projectService.createConfiguredProject(configFileName) :
|
||||
projectReferenceProjectLoadKind === ProjectReferenceProjectLoadKind.FindCreateLoad ?
|
||||
project.projectService.createAndLoadConfiguredProject(configFileName, reason!) :
|
||||
undefined
|
||||
);
|
||||
return child && cb(child, configFileName);
|
||||
});
|
||||
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 result = child && cb(child);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
(seenResolvedRefs || (seenResolvedRefs = new Map())).set(canonicalPath, loadKind);
|
||||
return worker(ref.references, ref.commandLine.options);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
@@ -2773,6 +2794,12 @@ namespace ts.server {
|
||||
*/
|
||||
private reloadConfiguredProjectForFiles<T>(openFiles: ESMap<Path, T>, delayReload: boolean, shouldReloadProjectFor: (openFileValue: T) => boolean, reason: string) {
|
||||
const updatedProjects = new Map<string, true>();
|
||||
const reloadChildProject = (child: ConfiguredProject) => {
|
||||
if (!updatedProjects.has(child.canonicalConfigFilePath)) {
|
||||
updatedProjects.set(child.canonicalConfigFilePath, true);
|
||||
this.reloadConfiguredProject(child, reason);
|
||||
}
|
||||
};
|
||||
// try to reload config file for all open files
|
||||
openFiles.forEach((openFileValue, path) => {
|
||||
// Filter out the files that need to be ignored
|
||||
@@ -2801,17 +2828,22 @@ namespace ts.server {
|
||||
this.reloadConfiguredProject(project, reason);
|
||||
// If this is solution, reload the project till the reloaded project contains the script info directly
|
||||
if (!project.containsScriptInfo(info) && project.isSolution()) {
|
||||
forEachResolvedProjectReferenceProject(
|
||||
const referencedProject = forEachResolvedProjectReferenceProject(
|
||||
project,
|
||||
child => {
|
||||
if (!updatedProjects.has(child.canonicalConfigFilePath)) {
|
||||
updatedProjects.set(child.canonicalConfigFilePath, true);
|
||||
this.reloadConfiguredProject(child, reason);
|
||||
}
|
||||
reloadChildProject(child);
|
||||
return projectContainsInfoDirectly(child, info);
|
||||
},
|
||||
ProjectReferenceProjectLoadKind.FindCreate
|
||||
);
|
||||
if (referencedProject) {
|
||||
// Reload the project's tree that is already present
|
||||
forEachResolvedProjectReferenceProject(
|
||||
project,
|
||||
reloadChildProject,
|
||||
ProjectReferenceProjectLoadKind.Find
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2913,7 +2945,7 @@ namespace ts.server {
|
||||
const info = this.getScriptInfo(fileName);
|
||||
return info && projectContainsInfoDirectly(child, info) ? child : undefined;
|
||||
},
|
||||
ProjectReferenceProjectLoadKind.FindCreateLoad,
|
||||
configuredProject.getCompilerOptions().disableReferencedProjectLoad ? ProjectReferenceProjectLoadKind.Find : 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;
|
||||
@@ -2965,8 +2997,9 @@ namespace ts.server {
|
||||
let configFileName: NormalizedPath | undefined;
|
||||
let configFileErrors: readonly Diagnostic[] | undefined;
|
||||
let project: ConfiguredProject | ExternalProject | undefined = this.findExternalProjectContainingOpenScriptInfo(info);
|
||||
let defaultConfigProject: ConfiguredProject | undefined;
|
||||
let retainProjects: ConfiguredProject[] | ConfiguredProject | undefined;
|
||||
let projectForConfigFileDiag: ConfiguredProject | undefined;
|
||||
let defaultConfigProjectIsCreated = false;
|
||||
if (this.syntaxOnly) {
|
||||
// Invalidate resolutions in the file since this file is now open
|
||||
info.containingProjects.forEach(project => {
|
||||
@@ -2981,30 +3014,22 @@ namespace ts.server {
|
||||
project = this.findConfiguredProjectByProjectName(configFileName);
|
||||
if (!project) {
|
||||
project = this.createLoadAndUpdateConfiguredProject(configFileName, `Creating possible configured project for ${info.fileName} to open`);
|
||||
// Send the event only if the project got created as part of this open request and info is part of the project
|
||||
if (!project.containsScriptInfo(info)) {
|
||||
// Since the file isnt part of configured project, do not send config file info
|
||||
configFileName = undefined;
|
||||
}
|
||||
else {
|
||||
configFileErrors = project.getAllProjectErrors();
|
||||
this.sendConfigFileDiagEvent(project, info.fileName);
|
||||
}
|
||||
defaultConfigProjectIsCreated = true;
|
||||
}
|
||||
else {
|
||||
// Ensure project is ready to check if it contains opened script info
|
||||
updateProjectIfDirty(project);
|
||||
}
|
||||
|
||||
defaultConfigProject = project;
|
||||
retainProjects = defaultConfigProject;
|
||||
projectForConfigFileDiag = project.containsScriptInfo(info) ? project : undefined;
|
||||
retainProjects = project;
|
||||
|
||||
// If this configured project doesnt contain script info but
|
||||
// it is solution with project references, try those project references
|
||||
if (!project.containsScriptInfo(info) && project.isSolution()) {
|
||||
if (project.isSolution()) {
|
||||
forEachResolvedProjectReferenceProject(
|
||||
project,
|
||||
(child, childConfigFileName) => {
|
||||
child => {
|
||||
updateProjectIfDirty(child);
|
||||
// Retain these projects
|
||||
if (!isArray(retainProjects)) {
|
||||
@@ -3016,20 +3041,35 @@ namespace ts.server {
|
||||
|
||||
// If script info belongs to this child project, use this as default config project
|
||||
if (projectContainsInfoDirectly(child, info)) {
|
||||
configFileName = childConfigFileName;
|
||||
configFileErrors = child.getAllProjectErrors();
|
||||
this.sendConfigFileDiagEvent(child, info.fileName);
|
||||
projectForConfigFileDiag = child;
|
||||
return child;
|
||||
}
|
||||
|
||||
// If this project uses the script info (even through project reference), if default project is not found, use this for configFileDiag
|
||||
if (!projectForConfigFileDiag && child.containsScriptInfo(info)) {
|
||||
projectForConfigFileDiag = child;
|
||||
}
|
||||
},
|
||||
ProjectReferenceProjectLoadKind.FindCreateLoad,
|
||||
`Creating project referenced in solution ${project.projectName} to find possible configured project for ${info.fileName} to open`
|
||||
);
|
||||
}
|
||||
else {
|
||||
// Create ancestor configured project
|
||||
this.createAncestorProjects(info, defaultConfigProject || project);
|
||||
|
||||
// Send the event only if the project got created as part of this open request and info is part of the project
|
||||
if (projectForConfigFileDiag) {
|
||||
configFileName = projectForConfigFileDiag.getConfigFilePath();
|
||||
if (projectForConfigFileDiag !== project || defaultConfigProjectIsCreated) {
|
||||
configFileErrors = projectForConfigFileDiag.getAllProjectErrors();
|
||||
this.sendConfigFileDiagEvent(projectForConfigFileDiag, info.fileName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Since the file isnt part of configured project, do not send config file info
|
||||
configFileName = undefined;
|
||||
}
|
||||
|
||||
// Create ancestor configured project
|
||||
this.createAncestorProjects(info, project);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2264,9 +2264,16 @@ namespace ts.server {
|
||||
// The project is referenced only if open files impacted by this project are present in this project
|
||||
return forEachEntry(
|
||||
configFileExistenceInfo.openFilesImpactedByConfigFile,
|
||||
(_value, infoPath) => isSolution ?
|
||||
!!this.getDefaultChildProjectFromSolution(this.projectService.getScriptInfoForPath(infoPath)!) :
|
||||
this.containsScriptInfo(this.projectService.getScriptInfoForPath(infoPath)!)
|
||||
(_value, infoPath) => {
|
||||
const info = this.projectService.getScriptInfoForPath(infoPath)!;
|
||||
return isSolution ?
|
||||
!!forEachResolvedProjectReferenceProject(
|
||||
this,
|
||||
child => child.containsScriptInfo(info),
|
||||
ProjectReferenceProjectLoadKind.Find
|
||||
) :
|
||||
this.containsScriptInfo(info);
|
||||
}
|
||||
) || false;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user