Skip to content

Commit 6275da2

Browse files
authored
Merge pull request #10960 from Microsoft/throttleTypingsInstallerRequestsFinal
Add throttle limit to typings installer requests
2 parents 55f6d4f + ec7c7a4 commit 6275da2

File tree

5 files changed

+495
-188
lines changed

5 files changed

+495
-188
lines changed

src/harness/unittests/compileOnSave.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
/// <reference path="../../server/typingsInstaller/typingsInstaller.ts" />
44

55
namespace ts.projectSystem {
6+
function createTestTypingsInstaller(host: server.ServerHost) {
7+
return new TestTypingsInstaller("/a/data/", /*throttleLimit*/5, host);
8+
}
9+
610
describe("CompileOnSave affected list", () => {
711
function sendAffectedFileRequestAndCheckResult(session: server.Session, request: server.protocol.Request, expectedFileList: { projectFileName: string, files: FileOrFolder[] }[]) {
812
const response: server.protocol.CompileOnSaveAffectedFileListSingleProject[] = session.executeCommand(request).response;
@@ -105,7 +109,7 @@ namespace ts.projectSystem {
105109

106110
it("should contains only itself if a module file's shape didn't change, and all files referencing it if its shape changed", () => {
107111
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
108-
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
112+
const typingsInstaller = createTestTypingsInstaller(host);
109113
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
110114

111115
openFilesForSession([moduleFile1, file1Consumer1], session);
@@ -130,7 +134,7 @@ namespace ts.projectSystem {
130134

131135
it("should be up-to-date with the reference map changes", () => {
132136
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
133-
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
137+
const typingsInstaller = createTestTypingsInstaller(host);
134138
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
135139

136140
openFilesForSession([moduleFile1, file1Consumer1], session);
@@ -177,7 +181,7 @@ namespace ts.projectSystem {
177181

178182
it("should be up-to-date with changes made in non-open files", () => {
179183
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
180-
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
184+
const typingsInstaller = createTestTypingsInstaller(host);
181185
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
182186

183187
openFilesForSession([moduleFile1], session);
@@ -195,7 +199,7 @@ namespace ts.projectSystem {
195199

196200
it("should be up-to-date with deleted files", () => {
197201
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
198-
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
202+
const typingsInstaller = createTestTypingsInstaller(host);
199203
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
200204

201205
openFilesForSession([moduleFile1], session);
@@ -210,7 +214,7 @@ namespace ts.projectSystem {
210214

211215
it("should be up-to-date with newly created files", () => {
212216
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
213-
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
217+
const typingsInstaller = createTestTypingsInstaller(host);
214218
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
215219

216220
openFilesForSession([moduleFile1], session);
@@ -247,7 +251,7 @@ namespace ts.projectSystem {
247251
};
248252

249253
const host = createServerHost([moduleFile1, file1Consumer1, configFile, libFile]);
250-
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
254+
const typingsInstaller = createTestTypingsInstaller(host);
251255
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
252256

253257
openFilesForSession([moduleFile1, file1Consumer1], session);
@@ -264,7 +268,7 @@ namespace ts.projectSystem {
264268

265269
it("should return all files if a global file changed shape", () => {
266270
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
267-
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
271+
const typingsInstaller = createTestTypingsInstaller(host);
268272
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
269273

270274
openFilesForSession([globalFile3], session);
@@ -290,7 +294,7 @@ namespace ts.projectSystem {
290294
};
291295

292296
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, configFile, libFile]);
293-
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
297+
const typingsInstaller = createTestTypingsInstaller(host);
294298
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
295299
openFilesForSession([moduleFile1], session);
296300
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, []);
@@ -308,7 +312,7 @@ namespace ts.projectSystem {
308312
};
309313

310314
const host = createServerHost([moduleFile1, file1Consumer1, configFile, libFile]);
311-
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
315+
const typingsInstaller = createTestTypingsInstaller(host);
312316
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
313317
openFilesForSession([moduleFile1], session);
314318

@@ -337,7 +341,7 @@ namespace ts.projectSystem {
337341
};
338342

339343
const host = createServerHost([moduleFile1, file1Consumer1, configFile, libFile]);
340-
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
344+
const typingsInstaller = createTestTypingsInstaller(host);
341345
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
342346
openFilesForSession([moduleFile1], session);
343347

@@ -359,7 +363,7 @@ namespace ts.projectSystem {
359363
content: `import {y} from "./file1Consumer1";`
360364
};
361365
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer1Consumer1, globalFile3, configFile, libFile]);
362-
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
366+
const typingsInstaller = createTestTypingsInstaller(host);
363367
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
364368

365369
openFilesForSession([moduleFile1, file1Consumer1], session);
@@ -392,7 +396,7 @@ namespace ts.projectSystem {
392396
export var t2 = 10;`
393397
};
394398
const host = createServerHost([file1, file2, configFile]);
395-
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
399+
const typingsInstaller = createTestTypingsInstaller(host);
396400
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
397401

398402
openFilesForSession([file1, file2], session);
@@ -475,7 +479,7 @@ namespace ts.projectSystem {
475479
content: `{}`
476480
};
477481
const host = createServerHost([file1, file2, configFile, libFile]);
478-
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
482+
const typingsInstaller = createTestTypingsInstaller(host);
479483
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
480484

481485
openFilesForSession([file1, file2], session);

src/harness/unittests/tsserverProjectSystem.ts

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
/// <reference path="../../server/typingsInstaller/typingsInstaller.ts" />
33

44
namespace ts.projectSystem {
5+
import TI = server.typingsInstaller;
6+
57
const safeList = {
68
path: <Path>"/safeList.json",
79
content: JSON.stringify({
@@ -13,6 +15,14 @@ namespace ts.projectSystem {
1315
})
1416
};
1517

18+
export interface PostExecAction {
19+
readonly requestKind: TI.RequestKind;
20+
readonly error: Error;
21+
readonly stdout: string;
22+
readonly stderr: string;
23+
readonly callback: (err: Error, stdout: string, stderr: string) => void;
24+
}
25+
1626
export function notImplemented(): any {
1727
throw new Error("Not yet implemented");
1828
}
@@ -39,21 +49,27 @@ namespace ts.projectSystem {
3949
content: libFileContent
4050
};
4151

42-
export class TestTypingsInstaller extends server.typingsInstaller.TypingsInstaller implements server.ITypingsInstaller {
52+
export class TestTypingsInstaller extends TI.TypingsInstaller implements server.ITypingsInstaller {
4353
protected projectService: server.ProjectService;
44-
constructor(readonly globalTypingsCacheLocation: string, readonly installTypingHost: server.ServerHost) {
45-
super(globalTypingsCacheLocation, safeList.path);
54+
constructor(readonly globalTypingsCacheLocation: string, throttleLimit: number, readonly installTypingHost: server.ServerHost) {
55+
super(globalTypingsCacheLocation, "npm", safeList.path, throttleLimit);
4656
this.init();
4757
}
4858

4959
safeFileList = safeList.path;
50-
postInstallActions: ((map: (t: string[]) => string[]) => void)[] = [];
60+
protected postExecActions: PostExecAction[] = [];
5161

52-
runPostInstallActions(map: (t: string[]) => string[]) {
53-
for (const f of this.postInstallActions) {
54-
f(map);
62+
executePendingCommands() {
63+
const actionsToRun = this.postExecActions;
64+
this.postExecActions = [];
65+
for (const action of actionsToRun) {
66+
action.callback(action.error, action.stdout, action.stderr);
5567
}
56-
this.postInstallActions = [];
68+
}
69+
70+
checkPendingCommands(expected: TI.RequestKind[]) {
71+
assert.equal(this.postExecActions.length, expected.length, `Expected ${expected.length} post install actions`);
72+
this.postExecActions.forEach((act, i) => assert.equal(act.requestKind, expected[i], "Unexpected post install action"));
5773
}
5874

5975
onProjectClosed(p: server.Project) {
@@ -67,14 +83,15 @@ namespace ts.projectSystem {
6783
return this.installTypingHost;
6884
}
6985

70-
isPackageInstalled(packageName: string) {
71-
return true;
72-
}
73-
74-
runInstall(cachePath: string, typingsToInstall: string[], postInstallAction: (installedTypings: string[]) => void) {
75-
this.postInstallActions.push(map => {
76-
postInstallAction(map(typingsToInstall));
77-
});
86+
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: (err: Error, stdout: string, stderr: string) => void): void {
87+
switch (requestKind) {
88+
case TI.NpmViewRequest:
89+
case TI.NpmInstallRequest:
90+
break;
91+
default:
92+
assert.isTrue(false, `request ${requestKind} is not supported`);
93+
}
94+
this.addPostExecAction(requestKind, "success", cb);
7895
}
7996

8097
sendResponse(response: server.SetTypings | server.InvalidateCachedTypings) {
@@ -85,6 +102,26 @@ namespace ts.projectSystem {
85102
const request = server.createInstallTypingsRequest(project, typingOptions, this.globalTypingsCacheLocation);
86103
this.install(request);
87104
}
105+
106+
addPostExecAction(requestKind: TI.RequestKind, stdout: string | string[], cb: TI.RequestCompletedAction) {
107+
const out = typeof stdout === "string" ? stdout : createNpmPackageJsonString(stdout);
108+
const action: PostExecAction = {
109+
error: undefined,
110+
stdout: out,
111+
stderr: "",
112+
callback: cb,
113+
requestKind
114+
};
115+
this.postExecActions.push(action);
116+
}
117+
}
118+
119+
function createNpmPackageJsonString(installedTypings: string[]): string {
120+
const dependencies: MapLike<any> = {};
121+
for (const typing of installedTypings) {
122+
dependencies[typing] = "1.0.0";
123+
}
124+
return JSON.stringify({ dependencies: dependencies });
88125
}
89126

90127
export function getExecutingFilePathFromLibFile(libFilePath: string): string {
@@ -124,7 +161,7 @@ namespace ts.projectSystem {
124161

125162
export function createSession(host: server.ServerHost, typingsInstaller?: server.ITypingsInstaller) {
126163
if (typingsInstaller === undefined) {
127-
typingsInstaller = new TestTypingsInstaller("/a/data/", host);
164+
typingsInstaller = new TestTypingsInstaller("/a/data/", /*throttleLimit*/5, host);
128165
}
129166
return new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
130167
}
@@ -1615,7 +1652,7 @@ namespace ts.projectSystem {
16151652
const host: TestServerHost & ModuleResolutionHost = createServerHost([file1, lib]);
16161653
const resolutionTrace: string[] = [];
16171654
host.trace = resolutionTrace.push.bind(resolutionTrace);
1618-
const projectService = createProjectService(host, { typingsInstaller: new TestTypingsInstaller("/a/cache", host) });
1655+
const projectService = createProjectService(host, { typingsInstaller: new TestTypingsInstaller("/a/cache", /*throttleLimit*/5, host) });
16191656

16201657
projectService.setCompilerOptionsForInferredProjects({ traceResolution: true, allowJs: true });
16211658
projectService.openClientFile(file1.path);

0 commit comments

Comments
 (0)