Skip to content

Commit a0190e3

Browse files
committed
Delay update graph when opening external project, and delay load configured project referenced from external project when opening it
1 parent 2e017b8 commit a0190e3

File tree

7 files changed

+215
-164
lines changed

7 files changed

+215
-164
lines changed

src/server/editorServices.ts

Lines changed: 107 additions & 102 deletions
Large diffs are not rendered by default.

src/server/project.ts

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,9 +1091,8 @@ namespace ts.server {
10911091
this.rootFilesMap.delete(info.path);
10921092
}
10931093

1094-
protected enableGlobalPlugins() {
1094+
protected enableGlobalPlugins(options: CompilerOptions) {
10951095
const host = this.projectService.host;
1096-
const options = this.getCompilationSettings();
10971096

10981097
if (!host.require) {
10991098
this.projectService.logger.info("Plugins were requested but not running in environment that supports 'require'. Nothing will be loaded");
@@ -1244,7 +1243,7 @@ namespace ts.server {
12441243
if (!projectRootPath && !projectService.useSingleInferredProject) {
12451244
this.canonicalCurrentDirectory = projectService.toCanonicalFileName(this.currentDirectory);
12461245
}
1247-
this.enableGlobalPlugins();
1246+
this.enableGlobalPlugins(this.getCompilerOptions());
12481247
}
12491248

12501249
addRoot(info: ScriptInfo) {
@@ -1316,28 +1315,27 @@ namespace ts.server {
13161315

13171316
private projectErrors: Diagnostic[] | undefined;
13181317

1318+
private projectReferences: ReadonlyArray<ProjectReference> | undefined;
1319+
1320+
/*@internal*/
1321+
projectOptions?: ProjectOptions | true;
1322+
13191323
/*@internal*/
13201324
constructor(configFileName: NormalizedPath,
13211325
projectService: ProjectService,
13221326
documentRegistry: DocumentRegistry,
1323-
hasExplicitListOfFiles: boolean,
1324-
compilerOptions: CompilerOptions,
1325-
lastFileExceededProgramSize: string | undefined,
1326-
public compileOnSaveEnabled: boolean,
1327-
cachedDirectoryStructureHost: CachedDirectoryStructureHost,
1328-
private projectReferences: ReadonlyArray<ProjectReference> | undefined) {
1327+
cachedDirectoryStructureHost: CachedDirectoryStructureHost) {
13291328
super(configFileName,
13301329
ProjectKind.Configured,
13311330
projectService,
13321331
documentRegistry,
1333-
hasExplicitListOfFiles,
1334-
lastFileExceededProgramSize,
1335-
compilerOptions,
1336-
compileOnSaveEnabled,
1332+
/*hasExplicitListOfFiles*/ false,
1333+
/*lastFileExceededProgramSize*/ undefined,
1334+
/*compilerOptions*/ {},
1335+
/*compileOnSaveEnabled*/ false,
13371336
cachedDirectoryStructureHost,
13381337
getDirectoryPath(configFileName));
13391338
this.canonicalConfigFilePath = asNormalizedPath(projectService.toCanonicalFileName(configFileName));
1340-
this.enablePlugins();
13411339
}
13421340

13431341
/**
@@ -1347,15 +1345,20 @@ namespace ts.server {
13471345
updateGraph(): boolean {
13481346
const reloadLevel = this.pendingReload;
13491347
this.pendingReload = ConfigFileProgramReloadLevel.None;
1348+
let result: boolean;
13501349
switch (reloadLevel) {
13511350
case ConfigFileProgramReloadLevel.Partial:
1352-
return this.projectService.reloadFileNamesOfConfiguredProject(this);
1351+
result = this.projectService.reloadFileNamesOfConfiguredProject(this);
1352+
break;
13531353
case ConfigFileProgramReloadLevel.Full:
13541354
this.projectService.reloadConfiguredProject(this);
1355-
return true;
1355+
result = true;
1356+
break;
13561357
default:
1357-
return super.updateGraph();
1358+
result = super.updateGraph();
13581359
}
1360+
this.projectService.sendProjectTelemetry(this);
1361+
return result;
13591362
}
13601363

13611364
/*@internal*/
@@ -1382,8 +1385,12 @@ namespace ts.server {
13821385
}
13831386

13841387
enablePlugins() {
1388+
this.enablePluginsWithOptions(this.getCompilerOptions());
1389+
}
1390+
1391+
/*@internal*/
1392+
enablePluginsWithOptions(options: CompilerOptions) {
13851393
const host = this.projectService.host;
1386-
const options = this.getCompilationSettings();
13871394

13881395
if (!host.require) {
13891396
this.projectService.logger.info("Plugins were requested but not running in environment that supports 'require'. Nothing will be loaded");
@@ -1407,7 +1414,7 @@ namespace ts.server {
14071414
}
14081415
}
14091416

1410-
this.enableGlobalPlugins();
1417+
this.enableGlobalPlugins(options);
14111418
}
14121419

14131420
/**
@@ -1547,6 +1554,12 @@ namespace ts.server {
15471554
getDirectoryPath(projectFilePath || normalizeSlashes(externalProjectName)));
15481555
}
15491556

1557+
updateGraph() {
1558+
const result = super.updateGraph();
1559+
this.projectService.sendProjectTelemetry(this);
1560+
return result;
1561+
}
1562+
15501563
getExcludedFiles() {
15511564
return this.excludedFiles;
15521565
}

src/server/utilities.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ namespace ts.server {
120120
};
121121
}
122122

123+
/*@internal*/
123124
export interface ProjectOptions {
124125
configHasExtendsProperty: boolean;
125126
/**
@@ -128,16 +129,6 @@ namespace ts.server {
128129
configHasFilesProperty: boolean;
129130
configHasIncludeProperty: boolean;
130131
configHasExcludeProperty: boolean;
131-
132-
projectReferences: ReadonlyArray<ProjectReference> | undefined;
133-
/**
134-
* these fields can be present in the project file
135-
*/
136-
files?: string[];
137-
wildcardDirectories?: Map<WatchDirectoryFlags>;
138-
compilerOptions?: CompilerOptions;
139-
typeAcquisition?: TypeAcquisition;
140-
compileOnSave?: boolean;
141132
}
142133

143134
export function isInferredProjectName(name: string) {

src/testRunner/unittests/telemetry.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ namespace ts.projectSystem {
6868
}, "/hunter2/foo.csproj");
6969

7070
// Also test that opening an external project only sends an event once.
71+
et.service.closeClientFile(file1.path);
7172

7273
et.service.closeExternalProject(projectFileName);
7374
checkNumberOfProjects(et.service, { externalProjects: 0 });
@@ -82,6 +83,7 @@ namespace ts.projectSystem {
8283
projectFileName,
8384
});
8485
checkNumberOfProjects(et.service, { externalProjects: 1 });
86+
et.service.openClientFile(file1.path); // Only on file open the project will be updated
8587
}
8688
});
8789

src/testRunner/unittests/tsserverProjectSystem.ts

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -663,12 +663,15 @@ namespace ts.projectSystem {
663663
options: {}
664664
});
665665
service.checkNumberOfProjects({ configuredProjects: 1 });
666-
checkProjectActualFiles(configuredProjectAt(service, 0), [upperCaseConfigFilePath]);
666+
const project = service.configuredProjects.get(config.path)!;
667+
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.Full); // External project referenced configured project pending to be reloaded
668+
checkProjectActualFiles(project, emptyArray);
667669

668670
service.openClientFile(f1.path);
669671
service.checkNumberOfProjects({ configuredProjects: 1, inferredProjects: 1 });
670672

671-
checkProjectActualFiles(configuredProjectAt(service, 0), [upperCaseConfigFilePath]);
673+
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.None); // External project referenced configured project is updated
674+
checkProjectActualFiles(project, [upperCaseConfigFilePath]);
672675
checkProjectActualFiles(service.inferredProjects[0], [f1.path]);
673676
});
674677

@@ -778,7 +781,7 @@ namespace ts.projectSystem {
778781

779782
// Add a tsconfig file
780783
host.reloadFS(filesWithConfig);
781-
host.checkTimeoutQueueLengthAndRun(1);
784+
host.checkTimeoutQueueLengthAndRun(2); // load configured project from disk + ensureProjectsForOpenFiles
782785

783786
projectService.checkNumberOfProjects({ inferredProjects: 2, configuredProjects: 1 });
784787
assert.isTrue(projectService.inferredProjects[0].isOrphan());
@@ -1229,7 +1232,7 @@ namespace ts.projectSystem {
12291232

12301233

12311234
host.reloadFS([file1, configFile, file2, file3, libFile]);
1232-
host.checkTimeoutQueueLengthAndRun(1);
1235+
host.checkTimeoutQueueLengthAndRun(2); // load configured project from disk + ensureProjectsForOpenFiles
12331236
checkNumberOfConfiguredProjects(projectService, 1);
12341237
checkNumberOfInferredProjects(projectService, 1);
12351238
checkProjectActualFiles(projectService.inferredProjects[0], [file2.path, file3.path, libFile.path]);
@@ -1773,8 +1776,11 @@ namespace ts.projectSystem {
17731776
try {
17741777
projectService.openExternalProject({ projectFileName: "project", options: {}, rootFiles: toExternalFiles([file1.path, office.path]) });
17751778
const proj = projectService.externalProjects[0];
1776-
assert.deepEqual(proj.getFileNames(/*excludeFilesFromExternalLibraries*/ true), [file1.path]);
1779+
// Since the file is not yet open, the project wont have program yet
1780+
assert.deepEqual(proj.getFileNames(/*excludeFilesFromExternalLibraries*/ true), emptyArray);
17771781
assert.deepEqual(proj.getTypeAcquisition().include, ["duck-types"]);
1782+
projectService.openClientFile(file1.path);
1783+
assert.deepEqual(proj.getFileNames(/*excludeFilesFromExternalLibraries*/ true), [file1.path]);
17781784
} finally {
17791785
projectService.resetSafeList();
17801786
}
@@ -1815,8 +1821,11 @@ namespace ts.projectSystem {
18151821
try {
18161822
projectService.openExternalProject({ projectFileName: "project", options: {}, rootFiles: toExternalFiles(files.map(f => f.path)) });
18171823
const proj = projectService.externalProjects[0];
1818-
assert.deepEqual(proj.getFileNames(/*excludeFilesFromExternalLibraries*/ true), [file1.path]);
1824+
// Since the file is not yet open, the project wont have program yet
1825+
assert.deepEqual(proj.getFileNames(/*excludeFilesFromExternalLibraries*/ true), emptyArray);
18191826
assert.deepEqual(proj.getTypeAcquisition().include, ["kendo-ui", "office"]);
1827+
projectService.openClientFile(file1.path);
1828+
assert.deepEqual(proj.getFileNames(/*excludeFilesFromExternalLibraries*/ true), [file1.path]);
18201829
} finally {
18211830
projectService.resetSafeList();
18221831
}
@@ -1856,6 +1865,9 @@ namespace ts.projectSystem {
18561865
try {
18571866
projectService.openExternalProject({ projectFileName: "project", options: {}, rootFiles: toExternalFiles([file1.path, file2.path]), typeAcquisition: { enable: true } });
18581867
const proj = projectService.externalProjects[0];
1868+
// Since the file is not yet open, the project wont have program yet
1869+
assert.deepEqual(proj.getFileNames(), emptyArray);
1870+
projectService.openClientFile(file2.path);
18591871
assert.deepEqual(proj.getFileNames(), [file2.path]);
18601872
} finally {
18611873
projectService.resetSafeList();
@@ -1893,7 +1905,7 @@ namespace ts.projectSystem {
18931905
checkProjectActualFiles(projectService.inferredProjects[1], [file3.path]);
18941906

18951907
host.reloadFS([file1, file2, file3, configFile]);
1896-
host.checkTimeoutQueueLengthAndRun(1);
1908+
host.checkTimeoutQueueLengthAndRun(2); // load configured project from disk + ensureProjectsForOpenFiles
18971909
checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 2 });
18981910
checkProjectActualFiles(configuredProjectAt(projectService, 0), [file1.path, file2.path, file3.path, configFile.path]);
18991911
assert.isTrue(projectService.inferredProjects[0].isOrphan());
@@ -2071,6 +2083,9 @@ namespace ts.projectSystem {
20712083

20722084
projectService.openExternalProject({ projectFileName: "project", options: {}, rootFiles: toExternalFiles([file1.path]) });
20732085
checkNumberOfProjects(projectService, { externalProjects: 1 });
2086+
// Since the file is not yet open, the project wont have program yet
2087+
assert.deepEqual(projectService.externalProjects[0].getFileNames(), emptyArray);
2088+
projectService.openClientFile(file1.path);
20742089
checkProjectActualFiles(projectService.externalProjects[0], [file1.path]);
20752090

20762091
projectService.openExternalProject({ projectFileName: "project", options: {}, rootFiles: toExternalFiles([file1.path, file2.path]) });
@@ -2098,11 +2113,17 @@ namespace ts.projectSystem {
20982113
projectService.openExternalProject({ projectFileName: "project", options: { moduleResolution: ModuleResolutionKind.NodeJs }, rootFiles: toExternalFiles([file1.path, file2.path]) });
20992114
checkNumberOfProjects(projectService, { externalProjects: 1 });
21002115
checkProjectRootFiles(projectService.externalProjects[0], [file1.path, file2.path]);
2116+
// Since the file is not yet open, the project wont have program yet
2117+
checkProjectActualFiles(projectService.externalProjects[0], emptyArray);
2118+
projectService.openClientFile(file1.path);
21012119
checkProjectActualFiles(projectService.externalProjects[0], [file1.path, file2.path]);
21022120

21032121
projectService.openExternalProject({ projectFileName: "project", options: { moduleResolution: ModuleResolutionKind.Classic }, rootFiles: toExternalFiles([file1.path, file2.path]) });
21042122
checkNumberOfProjects(projectService, { externalProjects: 1 });
21052123
checkProjectRootFiles(projectService.externalProjects[0], [file1.path, file2.path]);
2124+
// The update doesnt happen right away until needed, so either open a file or ensure projects uptodate
2125+
checkProjectActualFiles(projectService.externalProjects[0], [file1.path, file2.path]);
2126+
projectService.ensureInferredProjectsUpToDate_TestOnly();
21062127
checkProjectActualFiles(projectService.externalProjects[0], [file1.path, file2.path, file3.path]);
21072128
});
21082129

@@ -2404,6 +2425,10 @@ namespace ts.projectSystem {
24042425
projectService.openExternalProject({ projectFileName, options: {}, rootFiles: [{ fileName: file1.path, scriptKind: ScriptKind.JS, hasMixedContent: true }] });
24052426

24062427
checkNumberOfProjects(projectService, { externalProjects: 1 });
2428+
// Since the external project is not updated till needed (eg opening client file/ensuringProjectStructureUptodate)
2429+
// watched files will be empty at first
2430+
checkWatchedFiles(host, emptyArray);
2431+
projectService.ensureInferredProjectsUpToDate_TestOnly();
24072432
checkWatchedFiles(host, [libFile.path]); // watching the "missing" lib file
24082433

24092434
const project = projectService.externalProjects[0];
@@ -2988,6 +3013,9 @@ namespace ts.projectSystem {
29883013
projectService.openExternalProjects([externalProject]);
29893014

29903015
checkNumberOfProjects(projectService, { configuredProjects: 0, externalProjects: 1, inferredProjects: 0 });
3016+
// Since the external project is not updated till needed (eg opening client file/ensuringProjectStructureUptodate)
3017+
checkProjectActualFiles(projectService.externalProjects[0], emptyArray);
3018+
projectService.ensureInferredProjectsUpToDate_TestOnly();
29913019
checkProjectActualFiles(projectService.externalProjects[0], [site.path, libFile.path]);
29923020
});
29933021

@@ -3334,6 +3362,9 @@ namespace ts.projectSystem {
33343362
checkNumberOfProjects(projectService, { configuredProjects: 1 });
33353363

33363364
const configuredProject = configuredProjectAt(projectService, 0);
3365+
// configured project is just created and not yet loaded
3366+
checkProjectActualFiles(configuredProject, emptyArray);
3367+
projectService.ensureInferredProjectsUpToDate_TestOnly();
33373368
checkProjectActualFiles(configuredProject, [file1.path, tsconfig.path]);
33383369

33393370
// Allow allowNonTsExtensions will be set to true for deferred extensions.
@@ -3975,6 +4006,8 @@ namespace ts.projectSystem {
39754006
options: {}
39764007
});
39774008
projectService.checkNumberOfProjects({ configuredProjects: 1 });
4009+
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
4010+
projectService.ensureInferredProjectsUpToDate_TestOnly();
39784011
checkProjectActualFiles(configuredProjectAt(projectService, 0), [f1.path, tsconfig.path]);
39794012

39804013
// rename tsconfig.json back to lib.ts
@@ -3986,6 +4019,8 @@ namespace ts.projectSystem {
39864019
});
39874020

39884021
projectService.checkNumberOfProjects({ externalProjects: 1 });
4022+
checkProjectActualFiles(projectService.externalProjects[0], emptyArray); // external project created but not updated till actually needed
4023+
projectService.ensureInferredProjectsUpToDate_TestOnly();
39894024
checkProjectActualFiles(projectService.externalProjects[0], [f1.path, f2.path]);
39904025
});
39914026

@@ -4023,6 +4058,8 @@ namespace ts.projectSystem {
40234058
});
40244059

40254060
projectService.checkNumberOfProjects({ externalProjects: 1 });
4061+
checkProjectActualFiles(projectService.externalProjects[0], emptyArray); // external project created but program is not created till its needed
4062+
projectService.ensureInferredProjectsUpToDate_TestOnly();
40264063
checkProjectActualFiles(projectService.externalProjects[0], [f1.path]);
40274064

40284065
// add two config file as root files
@@ -4032,6 +4069,9 @@ namespace ts.projectSystem {
40324069
options: {}
40334070
});
40344071
projectService.checkNumberOfProjects({ configuredProjects: 2 });
4072+
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
4073+
checkProjectActualFiles(configuredProjectAt(projectService, 1), emptyArray); // Configured project created but not loaded till actually needed
4074+
projectService.ensureInferredProjectsUpToDate_TestOnly();
40354075
checkProjectActualFiles(configuredProjectAt(projectService, 0), [cLib.path, cTsconfig.path]);
40364076
checkProjectActualFiles(configuredProjectAt(projectService, 1), [dLib.path, dTsconfig.path]);
40374077

@@ -4053,6 +4093,8 @@ namespace ts.projectSystem {
40534093
});
40544094

40554095
projectService.checkNumberOfProjects({ externalProjects: 1 });
4096+
checkProjectActualFiles(projectService.externalProjects[0], emptyArray); // external project created but program is not created till its needed
4097+
projectService.ensureInferredProjectsUpToDate_TestOnly();
40564098
checkProjectActualFiles(projectService.externalProjects[0], [f1.path]);
40574099

40584100
// open two config files
@@ -4063,6 +4105,9 @@ namespace ts.projectSystem {
40634105
options: {}
40644106
});
40654107
projectService.checkNumberOfProjects({ configuredProjects: 2 });
4108+
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
4109+
checkProjectActualFiles(configuredProjectAt(projectService, 1), emptyArray); // Configured project created but not loaded till actually needed
4110+
projectService.ensureInferredProjectsUpToDate_TestOnly();
40664111
checkProjectActualFiles(configuredProjectAt(projectService, 0), [cLib.path, cTsconfig.path]);
40674112
checkProjectActualFiles(configuredProjectAt(projectService, 1), [dLib.path, dTsconfig.path]);
40684113

0 commit comments

Comments
 (0)