Merge pull request #25884 from Microsoft/optimizeOpenExternalProject
Delay load configured project referenced from external project when opening it
This commit is contained in:
commit
0f97620b9c
7 changed files with 184 additions and 168 deletions
|
@ -350,6 +350,12 @@ namespace ts.server {
|
|||
return project.dirty && project.updateGraph();
|
||||
}
|
||||
|
||||
function setProjectOptionsUsed(project: ConfiguredProject | ExternalProject) {
|
||||
if (project.projectKind === ProjectKind.Configured) {
|
||||
(project as ConfiguredProject).projectOptions = true;
|
||||
}
|
||||
}
|
||||
|
||||
export class ProjectService {
|
||||
|
||||
/*@internal*/
|
||||
|
@ -1404,47 +1410,6 @@ namespace ts.server {
|
|||
return findProjectByName(projectFileName, this.externalProjects);
|
||||
}
|
||||
|
||||
private convertConfigFileContentToProjectOptions(configFilename: string, cachedDirectoryStructureHost: CachedDirectoryStructureHost) {
|
||||
configFilename = normalizePath(configFilename);
|
||||
|
||||
const configFileContent = this.host.readFile(configFilename)!; // TODO: GH#18217
|
||||
|
||||
const result = parseJsonText(configFilename, configFileContent);
|
||||
if (!result.endOfFileToken) {
|
||||
result.endOfFileToken = <EndOfFileToken>{ kind: SyntaxKind.EndOfFileToken };
|
||||
}
|
||||
const errors = result.parseDiagnostics as Diagnostic[];
|
||||
const parsedCommandLine = parseJsonSourceFileConfigFileContent(
|
||||
result,
|
||||
cachedDirectoryStructureHost,
|
||||
getDirectoryPath(configFilename),
|
||||
/*existingOptions*/ {},
|
||||
configFilename,
|
||||
/*resolutionStack*/[],
|
||||
this.hostConfiguration.extraFileExtensions);
|
||||
|
||||
if (parsedCommandLine.errors.length) {
|
||||
errors.push(...parsedCommandLine.errors);
|
||||
}
|
||||
|
||||
Debug.assert(!!parsedCommandLine.fileNames);
|
||||
|
||||
const projectOptions: ProjectOptions = {
|
||||
files: parsedCommandLine.fileNames,
|
||||
compilerOptions: parsedCommandLine.options,
|
||||
configHasExtendsProperty: parsedCommandLine.raw.extends !== undefined,
|
||||
configHasFilesProperty: parsedCommandLine.raw.files !== undefined,
|
||||
configHasIncludeProperty: parsedCommandLine.raw.include !== undefined,
|
||||
configHasExcludeProperty: parsedCommandLine.raw.exclude !== undefined,
|
||||
wildcardDirectories: createMapFromTemplate(parsedCommandLine.wildcardDirectories!), // TODO: GH#18217
|
||||
typeAcquisition: parsedCommandLine.typeAcquisition,
|
||||
compileOnSave: parsedCommandLine.compileOnSave,
|
||||
projectReferences: parsedCommandLine.projectReferences
|
||||
};
|
||||
|
||||
return { projectOptions, configFileErrors: errors, configFileSpecs: parsedCommandLine.configFileSpecs };
|
||||
}
|
||||
|
||||
/** Get a filename if the language service exceeds the maximum allowed program size; otherwise returns undefined. */
|
||||
private getFilenameForExceededTotalSizeLimitForNonTsFiles<T>(name: string, options: CompilerOptions | undefined, fileNames: T[], propertyReader: FilePropertyReader<T>): string | undefined {
|
||||
if (options && options.disableSizeLimit || !this.host.getFileSize) {
|
||||
|
@ -1501,24 +1466,28 @@ namespace ts.server {
|
|||
options.compileOnSave === undefined ? true : options.compileOnSave);
|
||||
project.excludedFiles = excludedFiles;
|
||||
|
||||
this.addFilesToNonInferredProjectAndUpdateGraph(project, files, externalFilePropertyReader, typeAcquisition);
|
||||
this.addFilesToNonInferredProject(project, files, externalFilePropertyReader, typeAcquisition);
|
||||
this.externalProjects.push(project);
|
||||
this.sendProjectTelemetry(projectFileName, project);
|
||||
return project;
|
||||
}
|
||||
|
||||
private sendProjectTelemetry(projectKey: string, project: ExternalProject | ConfiguredProject, projectOptions?: ProjectOptions): void {
|
||||
if (this.seenProjects.has(projectKey)) {
|
||||
/*@internal*/
|
||||
sendProjectTelemetry(project: ExternalProject | ConfiguredProject): void {
|
||||
if (this.seenProjects.has(project.projectName)) {
|
||||
setProjectOptionsUsed(project);
|
||||
return;
|
||||
}
|
||||
this.seenProjects.set(projectKey, true);
|
||||
this.seenProjects.set(project.projectName, true);
|
||||
|
||||
if (!this.eventHandler || !this.host.createSHA256Hash) {
|
||||
setProjectOptionsUsed(project);
|
||||
return;
|
||||
}
|
||||
|
||||
const projectOptions = project.projectKind === ProjectKind.Configured ? (project as ConfiguredProject).projectOptions as ProjectOptions : undefined;
|
||||
setProjectOptionsUsed(project);
|
||||
const data: ProjectInfoTelemetryEventData = {
|
||||
projectId: this.host.createSHA256Hash(projectKey),
|
||||
projectId: this.host.createSHA256Hash(project.projectName),
|
||||
fileStats: countEachFileTypes(project.getScriptInfos()),
|
||||
compilerOptions: convertCompilerOptionsForTelemetry(project.getCompilationSettings()),
|
||||
typeAcquisition: convertTypeAcquisition(project.getTypeAcquisition()),
|
||||
|
@ -1539,8 +1508,7 @@ namespace ts.server {
|
|||
return "other";
|
||||
}
|
||||
|
||||
const configFilePath = project instanceof ConfiguredProject ? project.getConfigFilePath() : undefined!; // TODO: GH#18217
|
||||
return getBaseConfigFileName(configFilePath) || "other";
|
||||
return getBaseConfigFileName(project.getConfigFilePath()) || "other";
|
||||
}
|
||||
|
||||
function convertTypeAcquisition({ enable, include, exclude }: TypeAcquisition): ProjectInfoTypeAcquisitionData {
|
||||
|
@ -1552,30 +1520,19 @@ namespace ts.server {
|
|||
}
|
||||
}
|
||||
|
||||
private addFilesToNonInferredProjectAndUpdateGraph<T>(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader<T>, typeAcquisition: TypeAcquisition): void {
|
||||
private addFilesToNonInferredProject<T>(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader<T>, typeAcquisition: TypeAcquisition): void {
|
||||
this.updateNonInferredProjectFiles(project, files, propertyReader);
|
||||
project.setTypeAcquisition(typeAcquisition);
|
||||
// This doesnt need scheduling since its either creation or reload of the project
|
||||
project.updateGraph();
|
||||
}
|
||||
|
||||
private createConfiguredProject(configFileName: NormalizedPath) {
|
||||
const cachedDirectoryStructureHost = createCachedDirectoryStructureHost(this.host, this.host.getCurrentDirectory(), this.host.useCaseSensitiveFileNames)!; // TODO: GH#18217
|
||||
const { projectOptions, configFileErrors, configFileSpecs } = this.convertConfigFileContentToProjectOptions(configFileName, cachedDirectoryStructureHost);
|
||||
this.logger.info(`Opened configuration file ${configFileName}`);
|
||||
const lastFileExceededProgramSize = this.getFilenameForExceededTotalSizeLimitForNonTsFiles(configFileName, projectOptions.compilerOptions, projectOptions.files!, fileNamePropertyReader); // TODO: GH#18217
|
||||
const project = new ConfiguredProject(
|
||||
configFileName,
|
||||
this,
|
||||
this.documentRegistry,
|
||||
projectOptions.configHasFilesProperty,
|
||||
projectOptions.compilerOptions!, // TODO: GH#18217
|
||||
lastFileExceededProgramSize,
|
||||
projectOptions.compileOnSave === undefined ? false : projectOptions.compileOnSave,
|
||||
cachedDirectoryStructureHost,
|
||||
projectOptions.projectReferences);
|
||||
|
||||
project.configFileSpecs = configFileSpecs;
|
||||
cachedDirectoryStructureHost);
|
||||
// TODO: We probably should also watch the configFiles that are extended
|
||||
project.configFileWatcher = this.watchFactory.watchFile(
|
||||
this.host,
|
||||
|
@ -1585,19 +1542,82 @@ namespace ts.server {
|
|||
WatchType.ConfigFilePath,
|
||||
project
|
||||
);
|
||||
if (!lastFileExceededProgramSize) {
|
||||
project.watchWildcards(projectOptions.wildcardDirectories!); // TODO: GH#18217
|
||||
}
|
||||
|
||||
project.setProjectErrors(configFileErrors);
|
||||
const filesToAdd = projectOptions.files!.concat(project.getExternalFiles());
|
||||
this.addFilesToNonInferredProjectAndUpdateGraph(project, filesToAdd, fileNamePropertyReader, projectOptions.typeAcquisition!); // TODO: GH#18217
|
||||
this.configuredProjects.set(project.canonicalConfigFilePath, project);
|
||||
this.setConfigFileExistenceByNewConfiguredProject(project);
|
||||
this.sendProjectTelemetry(configFileName, project, projectOptions);
|
||||
return project;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
private createConfiguredProjectWithDelayLoad(configFileName: NormalizedPath) {
|
||||
const project = this.createConfiguredProject(configFileName);
|
||||
project.pendingReload = ConfigFileProgramReloadLevel.Full;
|
||||
return project;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
private createAndLoadConfiguredProject(configFileName: NormalizedPath) {
|
||||
const project = this.createConfiguredProject(configFileName);
|
||||
this.loadConfiguredProject(project);
|
||||
return project;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the config file of the project, and update the project root file names.
|
||||
*/
|
||||
/* @internal */
|
||||
private loadConfiguredProject(project: ConfiguredProject) {
|
||||
// Read updated contents from disk
|
||||
const configFilename = normalizePath(project.getConfigFilePath());
|
||||
|
||||
const configFileContent = this.host.readFile(configFilename)!; // TODO: GH#18217
|
||||
|
||||
const result = parseJsonText(configFilename, configFileContent);
|
||||
if (!result.endOfFileToken) {
|
||||
result.endOfFileToken = <EndOfFileToken>{ kind: SyntaxKind.EndOfFileToken };
|
||||
}
|
||||
const configFileErrors = result.parseDiagnostics as Diagnostic[];
|
||||
const parsedCommandLine = parseJsonSourceFileConfigFileContent(
|
||||
result,
|
||||
project.getCachedDirectoryStructureHost(),
|
||||
getDirectoryPath(configFilename),
|
||||
/*existingOptions*/ {},
|
||||
configFilename,
|
||||
/*resolutionStack*/[],
|
||||
this.hostConfiguration.extraFileExtensions);
|
||||
|
||||
if (parsedCommandLine.errors.length) {
|
||||
configFileErrors.push(...parsedCommandLine.errors);
|
||||
}
|
||||
|
||||
Debug.assert(!!parsedCommandLine.fileNames);
|
||||
const compilerOptions = parsedCommandLine.options;
|
||||
|
||||
// Update the project
|
||||
if (!project.projectOptions) {
|
||||
project.projectOptions = {
|
||||
configHasExtendsProperty: parsedCommandLine.raw.extends !== undefined,
|
||||
configHasFilesProperty: parsedCommandLine.raw.files !== undefined,
|
||||
configHasIncludeProperty: parsedCommandLine.raw.include !== undefined,
|
||||
configHasExcludeProperty: parsedCommandLine.raw.exclude !== undefined
|
||||
};
|
||||
}
|
||||
project.configFileSpecs = parsedCommandLine.configFileSpecs;
|
||||
project.setProjectErrors(configFileErrors);
|
||||
project.updateReferences(parsedCommandLine.projectReferences);
|
||||
const lastFileExceededProgramSize = this.getFilenameForExceededTotalSizeLimitForNonTsFiles(project.canonicalConfigFilePath, compilerOptions, parsedCommandLine.fileNames, fileNamePropertyReader);
|
||||
if (lastFileExceededProgramSize) {
|
||||
project.disableLanguageService(lastFileExceededProgramSize);
|
||||
project.stopWatchingWildCards();
|
||||
}
|
||||
else {
|
||||
project.enableLanguageService();
|
||||
project.watchWildcards(createMapFromTemplate(parsedCommandLine.wildcardDirectories!)); // TODO: GH#18217
|
||||
}
|
||||
project.enablePluginsWithOptions(compilerOptions);
|
||||
const filesToAdd = parsedCommandLine.fileNames.concat(project.getExternalFiles());
|
||||
this.updateRootAndOptionsOfNonInferredProject(project, filesToAdd, fileNamePropertyReader, compilerOptions, parsedCommandLine.typeAcquisition!, parsedCommandLine.compileOnSave!); // TODO: GH#18217
|
||||
}
|
||||
|
||||
private updateNonInferredProjectFiles<T>(project: ExternalProject | ConfiguredProject, files: T[], propertyReader: FilePropertyReader<T>) {
|
||||
const projectRootFilesMap = project.getRootFilesMap();
|
||||
const newRootScriptInfoMap = createMap<ProjectRoot>();
|
||||
|
@ -1656,31 +1676,31 @@ namespace ts.server {
|
|||
project.markAsDirty();
|
||||
}
|
||||
|
||||
private updateNonInferredProject<T>(project: ExternalProject | ConfiguredProject, newUncheckedFiles: T[], propertyReader: FilePropertyReader<T>, newOptions: CompilerOptions, newTypeAcquisition: TypeAcquisition, compileOnSave: boolean | undefined) {
|
||||
private updateRootAndOptionsOfNonInferredProject<T>(project: ExternalProject | ConfiguredProject, newUncheckedFiles: T[], propertyReader: FilePropertyReader<T>, newOptions: CompilerOptions, newTypeAcquisition: TypeAcquisition, compileOnSave: boolean | undefined) {
|
||||
project.setCompilerOptions(newOptions);
|
||||
// VS only set the CompileOnSaveEnabled option in the request if the option was changed recently
|
||||
// therefore if it is undefined, it should not be updated.
|
||||
if (compileOnSave !== undefined) {
|
||||
project.compileOnSaveEnabled = compileOnSave;
|
||||
}
|
||||
this.addFilesToNonInferredProjectAndUpdateGraph(project, newUncheckedFiles, propertyReader, newTypeAcquisition);
|
||||
this.addFilesToNonInferredProject(project, newUncheckedFiles, propertyReader, newTypeAcquisition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload the file names from config file specs and update the project graph
|
||||
*/
|
||||
/*@internal*/
|
||||
reloadFileNamesOfConfiguredProject(project: ConfiguredProject): boolean {
|
||||
reloadFileNamesOfConfiguredProject(project: ConfiguredProject) {
|
||||
const configFileSpecs = project.configFileSpecs!; // TODO: GH#18217
|
||||
const configFileName = project.getConfigFilePath();
|
||||
const fileNamesResult = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), project.getCompilationSettings(), project.getCachedDirectoryStructureHost(), this.hostConfiguration.extraFileExtensions);
|
||||
project.updateErrorOnNoInputFiles(fileNamesResult.fileNames.length !== 0);
|
||||
this.updateNonInferredProjectFiles(project, fileNamesResult.fileNames, fileNamePropertyReader);
|
||||
this.updateNonInferredProjectFiles(project, fileNamesResult.fileNames.concat(project.getExternalFiles()), fileNamePropertyReader);
|
||||
return project.updateGraph();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the config file of the project again and update the project
|
||||
* Read the config file of the project again by clearing the cache and update the project graph
|
||||
*/
|
||||
/* @internal */
|
||||
reloadConfiguredProject(project: ConfiguredProject) {
|
||||
|
@ -1692,23 +1712,10 @@ namespace ts.server {
|
|||
const configFileName = project.getConfigFilePath();
|
||||
this.logger.info(`Reloading configured project ${configFileName}`);
|
||||
|
||||
// Read updated contents from disk
|
||||
const { projectOptions, configFileErrors, configFileSpecs } = this.convertConfigFileContentToProjectOptions(configFileName, host);
|
||||
// Load project from the disk
|
||||
this.loadConfiguredProject(project);
|
||||
project.updateGraph();
|
||||
|
||||
// Update the project
|
||||
project.configFileSpecs = configFileSpecs;
|
||||
project.setProjectErrors(configFileErrors);
|
||||
project.updateReferences(projectOptions.projectReferences);
|
||||
const lastFileExceededProgramSize = this.getFilenameForExceededTotalSizeLimitForNonTsFiles(project.canonicalConfigFilePath, projectOptions.compilerOptions, projectOptions.files!, fileNamePropertyReader); // TODO: GH#18217
|
||||
if (lastFileExceededProgramSize) {
|
||||
project.disableLanguageService(lastFileExceededProgramSize);
|
||||
project.stopWatchingWildCards();
|
||||
}
|
||||
else {
|
||||
project.enableLanguageService();
|
||||
project.watchWildcards(projectOptions.wildcardDirectories!); // TODO: GH#18217
|
||||
}
|
||||
this.updateNonInferredProject(project, projectOptions.files!, fileNamePropertyReader, projectOptions.compilerOptions!, projectOptions.typeAcquisition!, projectOptions.compileOnSave!); // TODO: GH#18217
|
||||
this.sendConfigFileDiagEvent(project, configFileName);
|
||||
}
|
||||
|
||||
|
@ -2040,17 +2047,14 @@ namespace ts.server {
|
|||
// otherwise we create a new one.
|
||||
const configFileName = this.getConfigFileNameForFile(info);
|
||||
if (configFileName) {
|
||||
const project = this.findConfiguredProjectByProjectName(configFileName);
|
||||
if (!project) {
|
||||
this.createConfiguredProject(configFileName);
|
||||
updatedProjects.set(configFileName, true);
|
||||
}
|
||||
else if (!updatedProjects.has(configFileName)) {
|
||||
const project = this.findConfiguredProjectByProjectName(configFileName) || this.createConfiguredProject(configFileName);
|
||||
if (!updatedProjects.has(configFileName)) {
|
||||
if (delayReload) {
|
||||
project.pendingReload = ConfigFileProgramReloadLevel.Full;
|
||||
this.delayUpdateProjectGraph(project);
|
||||
}
|
||||
else {
|
||||
// reload from the disk
|
||||
this.reloadConfiguredProject(project);
|
||||
}
|
||||
updatedProjects.set(configFileName, true);
|
||||
|
@ -2138,7 +2142,7 @@ namespace ts.server {
|
|||
const configFileName = this.getConfigFileNameForFile(originalFileInfo);
|
||||
if (!configFileName) return undefined;
|
||||
|
||||
const configuredProject = this.findConfiguredProjectByProjectName(configFileName) || this.createConfiguredProject(configFileName);
|
||||
const configuredProject = this.findConfiguredProjectByProjectName(configFileName) || this.createAndLoadConfiguredProject(configFileName);
|
||||
updateProjectIfDirty(configuredProject);
|
||||
// Keep this configured project as referenced from project
|
||||
addOriginalConfiguredProject(configuredProject);
|
||||
|
@ -2188,7 +2192,8 @@ namespace ts.server {
|
|||
if (configFileName) {
|
||||
project = this.findConfiguredProjectByProjectName(configFileName);
|
||||
if (!project) {
|
||||
project = this.createConfiguredProject(configFileName);
|
||||
project = this.createAndLoadConfiguredProject(configFileName);
|
||||
project.updateGraph();
|
||||
// Send the event only if the project got created as part of this open request and info is part of the project
|
||||
if (info.isOrphan()) {
|
||||
// Since the file isnt part of configured project, do not send config file info
|
||||
|
@ -2578,7 +2583,9 @@ namespace ts.server {
|
|||
externalProject.enableLanguageService();
|
||||
}
|
||||
// external project already exists and not config files were added - update the project and return;
|
||||
this.updateNonInferredProject(externalProject, proj.rootFiles, externalFilePropertyReader, compilerOptions, proj.typeAcquisition, proj.options.compileOnSave);
|
||||
// The graph update here isnt postponed since any file open operation needs all updated external projects
|
||||
this.updateRootAndOptionsOfNonInferredProject(externalProject, proj.rootFiles, externalFilePropertyReader, compilerOptions, proj.typeAcquisition, proj.options.compileOnSave);
|
||||
externalProject.updateGraph();
|
||||
return;
|
||||
}
|
||||
// some config files were added to external project (that previously were not there)
|
||||
|
@ -2625,8 +2632,8 @@ namespace ts.server {
|
|||
for (const tsconfigFile of tsConfigFiles) {
|
||||
let project = this.findConfiguredProjectByProjectName(tsconfigFile);
|
||||
if (!project) {
|
||||
// errors are stored in the project
|
||||
project = this.createConfiguredProject(tsconfigFile);
|
||||
// errors are stored in the project, do not need to update the graph
|
||||
project = this.createConfiguredProjectWithDelayLoad(tsconfigFile);
|
||||
}
|
||||
if (project && !contains(exisingConfigFiles, tsconfigFile)) {
|
||||
// keep project alive even if no documents are opened - its lifetime is bound to the lifetime of containing external project
|
||||
|
@ -2636,8 +2643,11 @@ namespace ts.server {
|
|||
}
|
||||
else {
|
||||
// no config files - remove the item from the collection
|
||||
// Create external project and update its graph, do not delay update since
|
||||
// any file open operation needs all updated external projects
|
||||
this.externalProjectToConfiguredProjectMap.delete(proj.projectFileName);
|
||||
this.createExternalProject(proj.projectFileName, rootFiles, proj.options, proj.typeAcquisition, excludedFiles);
|
||||
const project = this.createExternalProject(proj.projectFileName, rootFiles, proj.options, proj.typeAcquisition, excludedFiles);
|
||||
project.updateGraph();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -149,6 +149,8 @@ namespace ts.server {
|
|||
*/
|
||||
private projectStateVersion = 0;
|
||||
|
||||
protected isInitialLoadPending: () => boolean = returnFalse;
|
||||
|
||||
/*@internal*/
|
||||
dirty = false;
|
||||
|
||||
|
@ -1033,7 +1035,10 @@ namespace ts.server {
|
|||
|
||||
/* @internal */
|
||||
getChangesSinceVersion(lastKnownVersion?: number): ProjectFilesWithTSDiagnostics {
|
||||
updateProjectIfDirty(this);
|
||||
// Update the graph only if initial configured project load is not pending
|
||||
if (!this.isInitialLoadPending()) {
|
||||
updateProjectIfDirty(this);
|
||||
}
|
||||
|
||||
const info: protocol.ProjectVersionInfo = {
|
||||
projectName: this.getProjectName(),
|
||||
|
@ -1091,9 +1096,8 @@ namespace ts.server {
|
|||
this.rootFilesMap.delete(info.path);
|
||||
}
|
||||
|
||||
protected enableGlobalPlugins() {
|
||||
protected enableGlobalPlugins(options: CompilerOptions) {
|
||||
const host = this.projectService.host;
|
||||
const options = this.getCompilationSettings();
|
||||
|
||||
if (!host.require) {
|
||||
this.projectService.logger.info("Plugins were requested but not running in environment that supports 'require'. Nothing will be loaded");
|
||||
|
@ -1244,7 +1248,7 @@ namespace ts.server {
|
|||
if (!projectRootPath && !projectService.useSingleInferredProject) {
|
||||
this.canonicalCurrentDirectory = projectService.toCanonicalFileName(this.currentDirectory);
|
||||
}
|
||||
this.enableGlobalPlugins();
|
||||
this.enableGlobalPlugins(this.getCompilerOptions());
|
||||
}
|
||||
|
||||
addRoot(info: ScriptInfo) {
|
||||
|
@ -1316,28 +1320,29 @@ namespace ts.server {
|
|||
|
||||
private projectErrors: Diagnostic[] | undefined;
|
||||
|
||||
private projectReferences: ReadonlyArray<ProjectReference> | undefined;
|
||||
|
||||
/*@internal*/
|
||||
projectOptions?: ProjectOptions | true;
|
||||
|
||||
protected isInitialLoadPending: () => boolean = returnTrue;
|
||||
|
||||
/*@internal*/
|
||||
constructor(configFileName: NormalizedPath,
|
||||
projectService: ProjectService,
|
||||
documentRegistry: DocumentRegistry,
|
||||
hasExplicitListOfFiles: boolean,
|
||||
compilerOptions: CompilerOptions,
|
||||
lastFileExceededProgramSize: string | undefined,
|
||||
public compileOnSaveEnabled: boolean,
|
||||
cachedDirectoryStructureHost: CachedDirectoryStructureHost,
|
||||
private projectReferences: ReadonlyArray<ProjectReference> | undefined) {
|
||||
cachedDirectoryStructureHost: CachedDirectoryStructureHost) {
|
||||
super(configFileName,
|
||||
ProjectKind.Configured,
|
||||
projectService,
|
||||
documentRegistry,
|
||||
hasExplicitListOfFiles,
|
||||
lastFileExceededProgramSize,
|
||||
compilerOptions,
|
||||
compileOnSaveEnabled,
|
||||
/*hasExplicitListOfFiles*/ false,
|
||||
/*lastFileExceededProgramSize*/ undefined,
|
||||
/*compilerOptions*/ {},
|
||||
/*compileOnSaveEnabled*/ false,
|
||||
cachedDirectoryStructureHost,
|
||||
getDirectoryPath(configFileName));
|
||||
this.canonicalConfigFilePath = asNormalizedPath(projectService.toCanonicalFileName(configFileName));
|
||||
this.enablePlugins();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1345,17 +1350,23 @@ namespace ts.server {
|
|||
* @returns: true if set of files in the project stays the same and false - otherwise.
|
||||
*/
|
||||
updateGraph(): boolean {
|
||||
this.isInitialLoadPending = returnFalse;
|
||||
const reloadLevel = this.pendingReload;
|
||||
this.pendingReload = ConfigFileProgramReloadLevel.None;
|
||||
let result: boolean;
|
||||
switch (reloadLevel) {
|
||||
case ConfigFileProgramReloadLevel.Partial:
|
||||
return this.projectService.reloadFileNamesOfConfiguredProject(this);
|
||||
result = this.projectService.reloadFileNamesOfConfiguredProject(this);
|
||||
break;
|
||||
case ConfigFileProgramReloadLevel.Full:
|
||||
this.projectService.reloadConfiguredProject(this);
|
||||
return true;
|
||||
result = true;
|
||||
break;
|
||||
default:
|
||||
return super.updateGraph();
|
||||
result = super.updateGraph();
|
||||
}
|
||||
this.projectService.sendProjectTelemetry(this);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
|
@ -1382,8 +1393,12 @@ namespace ts.server {
|
|||
}
|
||||
|
||||
enablePlugins() {
|
||||
this.enablePluginsWithOptions(this.getCompilerOptions());
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
enablePluginsWithOptions(options: CompilerOptions) {
|
||||
const host = this.projectService.host;
|
||||
const options = this.getCompilationSettings();
|
||||
|
||||
if (!host.require) {
|
||||
this.projectService.logger.info("Plugins were requested but not running in environment that supports 'require'. Nothing will be loaded");
|
||||
|
@ -1407,7 +1422,7 @@ namespace ts.server {
|
|||
}
|
||||
}
|
||||
|
||||
this.enableGlobalPlugins();
|
||||
this.enableGlobalPlugins(options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1547,6 +1562,12 @@ namespace ts.server {
|
|||
getDirectoryPath(projectFilePath || normalizeSlashes(externalProjectName)));
|
||||
}
|
||||
|
||||
updateGraph() {
|
||||
const result = super.updateGraph();
|
||||
this.projectService.sendProjectTelemetry(this);
|
||||
return result;
|
||||
}
|
||||
|
||||
getExcludedFiles() {
|
||||
return this.excludedFiles;
|
||||
}
|
||||
|
|
|
@ -120,6 +120,7 @@ namespace ts.server {
|
|||
};
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
export interface ProjectOptions {
|
||||
configHasExtendsProperty: boolean;
|
||||
/**
|
||||
|
@ -128,16 +129,6 @@ namespace ts.server {
|
|||
configHasFilesProperty: boolean;
|
||||
configHasIncludeProperty: boolean;
|
||||
configHasExcludeProperty: boolean;
|
||||
|
||||
projectReferences: ReadonlyArray<ProjectReference> | undefined;
|
||||
/**
|
||||
* these fields can be present in the project file
|
||||
*/
|
||||
files?: string[];
|
||||
wildcardDirectories?: Map<WatchDirectoryFlags>;
|
||||
compilerOptions?: CompilerOptions;
|
||||
typeAcquisition?: TypeAcquisition;
|
||||
compileOnSave?: boolean;
|
||||
}
|
||||
|
||||
export function isInferredProjectName(name: string) {
|
||||
|
|
|
@ -68,6 +68,7 @@ namespace ts.projectSystem {
|
|||
}, "/hunter2/foo.csproj");
|
||||
|
||||
// Also test that opening an external project only sends an event once.
|
||||
et.service.closeClientFile(file1.path);
|
||||
|
||||
et.service.closeExternalProject(projectFileName);
|
||||
checkNumberOfProjects(et.service, { externalProjects: 0 });
|
||||
|
@ -82,6 +83,7 @@ namespace ts.projectSystem {
|
|||
projectFileName,
|
||||
});
|
||||
checkNumberOfProjects(et.service, { externalProjects: 1 });
|
||||
et.service.openClientFile(file1.path); // Only on file open the project will be updated
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -663,12 +663,15 @@ namespace ts.projectSystem {
|
|||
options: {}
|
||||
});
|
||||
service.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(configuredProjectAt(service, 0), [upperCaseConfigFilePath]);
|
||||
const project = service.configuredProjects.get(config.path)!;
|
||||
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.Full); // External project referenced configured project pending to be reloaded
|
||||
checkProjectActualFiles(project, emptyArray);
|
||||
|
||||
service.openClientFile(f1.path);
|
||||
service.checkNumberOfProjects({ configuredProjects: 1, inferredProjects: 1 });
|
||||
|
||||
checkProjectActualFiles(configuredProjectAt(service, 0), [upperCaseConfigFilePath]);
|
||||
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.None); // External project referenced configured project is updated
|
||||
checkProjectActualFiles(project, [upperCaseConfigFilePath]);
|
||||
checkProjectActualFiles(service.inferredProjects[0], [f1.path]);
|
||||
});
|
||||
|
||||
|
@ -778,7 +781,7 @@ namespace ts.projectSystem {
|
|||
|
||||
// Add a tsconfig file
|
||||
host.reloadFS(filesWithConfig);
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
host.checkTimeoutQueueLengthAndRun(2); // load configured project from disk + ensureProjectsForOpenFiles
|
||||
|
||||
projectService.checkNumberOfProjects({ inferredProjects: 2, configuredProjects: 1 });
|
||||
assert.isTrue(projectService.inferredProjects[0].isOrphan());
|
||||
|
@ -1229,7 +1232,7 @@ namespace ts.projectSystem {
|
|||
|
||||
|
||||
host.reloadFS([file1, configFile, file2, file3, libFile]);
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
host.checkTimeoutQueueLengthAndRun(2); // load configured project from disk + ensureProjectsForOpenFiles
|
||||
checkNumberOfConfiguredProjects(projectService, 1);
|
||||
checkNumberOfInferredProjects(projectService, 1);
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [file2.path, file3.path, libFile.path]);
|
||||
|
@ -1893,7 +1896,7 @@ namespace ts.projectSystem {
|
|||
checkProjectActualFiles(projectService.inferredProjects[1], [file3.path]);
|
||||
|
||||
host.reloadFS([file1, file2, file3, configFile]);
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
host.checkTimeoutQueueLengthAndRun(2); // load configured project from disk + ensureProjectsForOpenFiles
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 2 });
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [file1.path, file2.path, file3.path, configFile.path]);
|
||||
assert.isTrue(projectService.inferredProjects[0].isOrphan());
|
||||
|
@ -2973,10 +2976,7 @@ namespace ts.projectSystem {
|
|||
checkNumberOfProjects(projectService, { configuredProjects: 1, externalProjects: 0, inferredProjects: 0 });
|
||||
|
||||
const configProject = configuredProjectAt(projectService, 0);
|
||||
checkProjectActualFiles(configProject, [libFile.path, configFile.path]);
|
||||
|
||||
const diagnostics = configProject.getAllProjectErrors();
|
||||
assert.equal(diagnostics[0].code, Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code);
|
||||
checkProjectActualFiles(configProject, []); // Since no files opened from this project, its not loaded
|
||||
|
||||
host.reloadFS([libFile, site]);
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
|
@ -3334,6 +3334,9 @@ namespace ts.projectSystem {
|
|||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
|
||||
const configuredProject = configuredProjectAt(projectService, 0);
|
||||
// configured project is just created and not yet loaded
|
||||
checkProjectActualFiles(configuredProject, emptyArray);
|
||||
projectService.ensureInferredProjectsUpToDate_TestOnly();
|
||||
checkProjectActualFiles(configuredProject, [file1.path, tsconfig.path]);
|
||||
|
||||
// Allow allowNonTsExtensions will be set to true for deferred extensions.
|
||||
|
@ -3975,6 +3978,8 @@ namespace ts.projectSystem {
|
|||
options: {}
|
||||
});
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
|
||||
projectService.ensureInferredProjectsUpToDate_TestOnly();
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [f1.path, tsconfig.path]);
|
||||
|
||||
// rename tsconfig.json back to lib.ts
|
||||
|
@ -4032,6 +4037,9 @@ namespace ts.projectSystem {
|
|||
options: {}
|
||||
});
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 2 });
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 1), emptyArray); // Configured project created but not loaded till actually needed
|
||||
projectService.ensureInferredProjectsUpToDate_TestOnly();
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [cLib.path, cTsconfig.path]);
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 1), [dLib.path, dTsconfig.path]);
|
||||
|
||||
|
@ -4063,6 +4071,9 @@ namespace ts.projectSystem {
|
|||
options: {}
|
||||
});
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 2 });
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 1), emptyArray); // Configured project created but not loaded till actually needed
|
||||
projectService.ensureInferredProjectsUpToDate_TestOnly();
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [cLib.path, cTsconfig.path]);
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 1), [dLib.path, dTsconfig.path]);
|
||||
|
||||
|
|
|
@ -430,7 +430,6 @@ namespace ts.projectSystem {
|
|||
|
||||
const p = projectService.externalProjects[0];
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
|
||||
checkProjectActualFiles(p, [jqueryJs.path]);
|
||||
|
||||
installer.checkPendingCommands(/*expectedCount*/ 0);
|
||||
|
|
|
@ -5751,24 +5751,6 @@ declare namespace ts.server {
|
|||
remove(path: NormalizedPath): void;
|
||||
}
|
||||
function createNormalizedPathMap<T>(): NormalizedPathMap<T>;
|
||||
interface ProjectOptions {
|
||||
configHasExtendsProperty: boolean;
|
||||
/**
|
||||
* true if config file explicitly listed files
|
||||
*/
|
||||
configHasFilesProperty: boolean;
|
||||
configHasIncludeProperty: boolean;
|
||||
configHasExcludeProperty: boolean;
|
||||
projectReferences: ReadonlyArray<ProjectReference> | undefined;
|
||||
/**
|
||||
* these fields can be present in the project file
|
||||
*/
|
||||
files?: string[];
|
||||
wildcardDirectories?: Map<WatchDirectoryFlags>;
|
||||
compilerOptions?: CompilerOptions;
|
||||
typeAcquisition?: TypeAcquisition;
|
||||
compileOnSave?: boolean;
|
||||
}
|
||||
function isInferredProjectName(name: string): boolean;
|
||||
function makeInferredProjectName(counter: number): string;
|
||||
function createSortedArray<T>(): SortedArray<T>;
|
||||
|
@ -8238,6 +8220,7 @@ declare namespace ts.server {
|
|||
* This property is different from projectStructureVersion since in most cases edits don't affect set of files in the project
|
||||
*/
|
||||
private projectStateVersion;
|
||||
protected isInitialLoadPending: () => boolean;
|
||||
private readonly cancellationToken;
|
||||
isNonTsProject(): boolean;
|
||||
isJsOnlyProject(): boolean;
|
||||
|
@ -8322,7 +8305,7 @@ declare namespace ts.server {
|
|||
filesToString(writeProjectFileNames: boolean): string;
|
||||
setCompilerOptions(compilerOptions: CompilerOptions): void;
|
||||
protected removeRoot(info: ScriptInfo): void;
|
||||
protected enableGlobalPlugins(): void;
|
||||
protected enableGlobalPlugins(options: CompilerOptions): void;
|
||||
protected enablePlugin(pluginConfigEntry: PluginImport, searchPaths: string[]): void;
|
||||
/** Starts a new check for diagnostics. Call this if some file has updated that would cause diagnostics to be changed. */
|
||||
refreshDiagnostics(): void;
|
||||
|
@ -8351,14 +8334,14 @@ declare namespace ts.server {
|
|||
* Otherwise it will create an InferredProject.
|
||||
*/
|
||||
class ConfiguredProject extends Project {
|
||||
compileOnSaveEnabled: boolean;
|
||||
private projectReferences;
|
||||
private typeAcquisition;
|
||||
private directoriesWatchedForWildcards;
|
||||
readonly canonicalConfigFilePath: NormalizedPath;
|
||||
/** Ref count to the project when opened from external project */
|
||||
private externalProjectRefCount;
|
||||
private projectErrors;
|
||||
private projectReferences;
|
||||
protected isInitialLoadPending: () => boolean;
|
||||
/**
|
||||
* If the project has reload from disk pending, it reloads (and then updates graph as part of that) instead of just updating the graph
|
||||
* @returns: true if set of files in the project stays the same and false - otherwise.
|
||||
|
@ -8391,6 +8374,7 @@ declare namespace ts.server {
|
|||
compileOnSaveEnabled: boolean;
|
||||
excludedFiles: ReadonlyArray<NormalizedPath>;
|
||||
private typeAcquisition;
|
||||
updateGraph(): boolean;
|
||||
getExcludedFiles(): ReadonlyArray<NormalizedPath>;
|
||||
getTypeAcquisition(): TypeAcquisition;
|
||||
setTypeAcquisition(newTypeAcquisition: TypeAcquisition): void;
|
||||
|
@ -8692,15 +8676,13 @@ declare namespace ts.server {
|
|||
private findConfiguredProjectByProjectName;
|
||||
private getConfiguredProjectByCanonicalConfigFilePath;
|
||||
private findExternalProjectByProjectName;
|
||||
private convertConfigFileContentToProjectOptions;
|
||||
/** Get a filename if the language service exceeds the maximum allowed program size; otherwise returns undefined. */
|
||||
private getFilenameForExceededTotalSizeLimitForNonTsFiles;
|
||||
private createExternalProject;
|
||||
private sendProjectTelemetry;
|
||||
private addFilesToNonInferredProjectAndUpdateGraph;
|
||||
private addFilesToNonInferredProject;
|
||||
private createConfiguredProject;
|
||||
private updateNonInferredProjectFiles;
|
||||
private updateNonInferredProject;
|
||||
private updateRootAndOptionsOfNonInferredProject;
|
||||
private sendConfigFileDiagEvent;
|
||||
private getOrCreateInferredProjectForProjectRootPathIfEnabled;
|
||||
private getOrCreateSingleInferredProjectIfEnabled;
|
||||
|
|
Loading…
Reference in a new issue