Skip to content

Commit a09f00b

Browse files
authored
Merge pull request #11593 from Microsoft/vladima/port-11577
ports PR #11577 into master
2 parents 1d82193 + 9c41e42 commit a09f00b

File tree

5 files changed

+105
-55
lines changed

5 files changed

+105
-55
lines changed

src/harness/unittests/tsserverProjectSystem.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,8 @@ namespace ts.projectSystem {
1919

2020
export interface PostExecAction {
2121
readonly requestKind: TI.RequestKind;
22-
readonly error: Error;
23-
readonly stdout: string;
24-
readonly stderr: string;
25-
readonly callback: (err: Error, stdout: string, stderr: string) => void;
22+
readonly success: boolean;
23+
readonly callback: TI.RequestCompletedAction;
2624
}
2725

2826
export function notImplemented(): any {
@@ -54,7 +52,7 @@ namespace ts.projectSystem {
5452
export class TestTypingsInstaller extends TI.TypingsInstaller implements server.ITypingsInstaller {
5553
protected projectService: server.ProjectService;
5654
constructor(readonly globalTypingsCacheLocation: string, throttleLimit: number, readonly installTypingHost: server.ServerHost, log?: TI.Log) {
57-
super(globalTypingsCacheLocation, "npm", safeList.path, throttleLimit, log);
55+
super(globalTypingsCacheLocation, safeList.path, throttleLimit, log);
5856
this.init();
5957
}
6058

@@ -65,7 +63,7 @@ namespace ts.projectSystem {
6563
const actionsToRun = this.postExecActions;
6664
this.postExecActions = [];
6765
for (const action of actionsToRun) {
68-
action.callback(action.error, action.stdout, action.stderr);
66+
action.callback(action.success);
6967
}
7068
}
7169

@@ -85,7 +83,7 @@ namespace ts.projectSystem {
8583
return this.installTypingHost;
8684
}
8785

88-
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: (err: Error, stdout: string, stderr: string) => void): void {
86+
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: TI.RequestCompletedAction): void {
8987
switch (requestKind) {
9088
case TI.NpmViewRequest:
9189
case TI.NpmInstallRequest:
@@ -108,9 +106,7 @@ namespace ts.projectSystem {
108106
addPostExecAction(requestKind: TI.RequestKind, stdout: string | string[], cb: TI.RequestCompletedAction) {
109107
const out = typeof stdout === "string" ? stdout : createNpmPackageJsonString(stdout);
110108
const action: PostExecAction = {
111-
error: undefined,
112-
stdout: out,
113-
stderr: "",
109+
success: !!out,
114110
callback: cb,
115111
requestKind
116112
};

src/harness/unittests/typingsInstaller.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ namespace ts.projectSystem {
3131
function executeCommand(self: Installer, host: TestServerHost, installedTypings: string[], typingFiles: FileOrFolder[], requestKind: TI.RequestKind, cb: TI.RequestCompletedAction): void {
3232
switch (requestKind) {
3333
case TI.NpmInstallRequest:
34-
self.addPostExecAction(requestKind, installedTypings, (err, stdout, stderr) => {
34+
self.addPostExecAction(requestKind, installedTypings, success => {
3535
for (const file of typingFiles) {
3636
host.createFileOrFolder(file, /*createParentDirectory*/ true);
3737
}
38-
cb(err, stdout, stderr);
38+
cb(success);
3939
});
4040
break;
4141
case TI.NpmViewRequest:
@@ -81,7 +81,7 @@ namespace ts.projectSystem {
8181
constructor() {
8282
super(host);
8383
}
84-
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
84+
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
8585
const installedTypings = ["@types/jquery"];
8686
const typingFiles = [jquery];
8787
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
@@ -125,7 +125,7 @@ namespace ts.projectSystem {
125125
constructor() {
126126
super(host);
127127
}
128-
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
128+
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
129129
const installedTypings = ["@types/jquery"];
130130
const typingFiles = [jquery];
131131
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
@@ -221,7 +221,7 @@ namespace ts.projectSystem {
221221
enqueueIsCalled = true;
222222
super.enqueueInstallTypingsRequest(project, typingOptions);
223223
}
224-
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
224+
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: TI.RequestCompletedAction): void {
225225
const installedTypings = ["@types/jquery"];
226226
const typingFiles = [jquery];
227227
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
@@ -275,7 +275,7 @@ namespace ts.projectSystem {
275275
constructor() {
276276
super(host);
277277
}
278-
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
278+
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: TI.RequestCompletedAction): void {
279279
const installedTypings = ["@types/lodash", "@types/react"];
280280
const typingFiles = [lodash, react];
281281
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
@@ -323,7 +323,7 @@ namespace ts.projectSystem {
323323
enqueueIsCalled = true;
324324
super.enqueueInstallTypingsRequest(project, typingOptions);
325325
}
326-
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
326+
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: TI.RequestCompletedAction): void {
327327
const installedTypings: string[] = [];
328328
const typingFiles: FileOrFolder[] = [];
329329
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
@@ -398,7 +398,7 @@ namespace ts.projectSystem {
398398
constructor() {
399399
super(host);
400400
}
401-
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
401+
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: TI.RequestCompletedAction): void {
402402
const installedTypings = ["@types/commander", "@types/express", "@types/jquery", "@types/moment"];
403403
const typingFiles = [commander, express, jquery, moment];
404404
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
@@ -477,7 +477,7 @@ namespace ts.projectSystem {
477477
constructor() {
478478
super(host, { throttleLimit: 3 });
479479
}
480-
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
480+
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: TI.RequestCompletedAction): void {
481481
const installedTypings = ["@types/commander", "@types/express", "@types/jquery", "@types/moment", "@types/lodash"];
482482
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
483483
}
@@ -567,10 +567,10 @@ namespace ts.projectSystem {
567567
constructor() {
568568
super(host, { throttleLimit: 3 });
569569
}
570-
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
570+
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: TI.RequestCompletedAction): void {
571571
if (requestKind === TI.NpmInstallRequest) {
572572
let typingFiles: (FileOrFolder & { typings: string}) [] = [];
573-
if (command.indexOf("commander") >= 0) {
573+
if (args.indexOf("@types/commander") >= 0) {
574574
typingFiles = [commander, jquery, lodash, cordova];
575575
}
576576
else {
@@ -655,7 +655,7 @@ namespace ts.projectSystem {
655655
constructor() {
656656
super(host, { globalTypingsCacheLocation: "/tmp" });
657657
}
658-
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
658+
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
659659
const installedTypings = ["@types/jquery"];
660660
const typingFiles = [jqueryDTS];
661661
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
@@ -701,7 +701,7 @@ namespace ts.projectSystem {
701701
constructor() {
702702
super(host, { globalTypingsCacheLocation: "/tmp" });
703703
}
704-
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
704+
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
705705
const installedTypings = ["@types/jquery"];
706706
const typingFiles = [jqueryDTS];
707707
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
@@ -766,7 +766,7 @@ namespace ts.projectSystem {
766766
constructor() {
767767
super(host, { globalTypingsCacheLocation: "/tmp" }, { isEnabled: () => true, writeLine: msg => messages.push(msg) });
768768
}
769-
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
769+
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
770770
assert(false, "runCommand should not be invoked");
771771
}
772772
})();

src/server/server.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,13 +265,16 @@ namespace ts.server {
265265
installerEventPort: number,
266266
canUseEvents: boolean,
267267
useSingleInferredProject: boolean,
268+
disableAutomaticTypingAcquisition: boolean,
268269
globalTypingsCacheLocation: string,
269270
logger: server.Logger) {
270271
super(
271272
host,
272273
cancellationToken,
273274
useSingleInferredProject,
274-
new NodeTypingsInstaller(logger, installerEventPort, globalTypingsCacheLocation, host.newLine),
275+
disableAutomaticTypingAcquisition
276+
? nullTypingsInstaller
277+
: new NodeTypingsInstaller(logger, installerEventPort, globalTypingsCacheLocation, host.newLine),
275278
Buffer.byteLength,
276279
process.hrtime,
277280
logger,
@@ -512,12 +515,14 @@ namespace ts.server {
512515
}
513516

514517
const useSingleInferredProject = sys.args.indexOf("--useSingleInferredProject") >= 0;
518+
const disableAutomaticTypingAcquisition = sys.args.indexOf("--disableAutomaticTypingAcquisition") >= 0;
515519
const ioSession = new IOSession(
516520
sys,
517521
cancellationToken,
518522
eventPort,
519523
/*canUseEvents*/ eventPort === undefined,
520524
useSingleInferredProject,
525+
disableAutomaticTypingAcquisition,
521526
getGlobalTypingsCacheLocation(),
522527
logger);
523528
process.on("uncaughtException", function (err: Error) {

src/server/typingsInstaller/nodeTypingsInstaller.ts

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,23 +33,38 @@ namespace ts.server.typingsInstaller {
3333
}
3434
}
3535

36-
export class NodeTypingsInstaller extends TypingsInstaller {
37-
private readonly exec: { (command: string, options: { cwd: string }, callback?: (error: Error, stdout: string, stderr: string) => void): any };
36+
type HttpGet = {
37+
(url: string, callback: (response: HttpResponse) => void): NodeJS.EventEmitter;
38+
};
39+
40+
interface HttpResponse extends NodeJS.ReadableStream {
41+
statusCode: number;
42+
statusMessage: string;
43+
destroy(): void;
44+
}
45+
46+
type Exec = {
47+
(command: string, options: { cwd: string }, callback?: (error: Error, stdout: string, stderr: string) => void): any
48+
};
3849

50+
export class NodeTypingsInstaller extends TypingsInstaller {
51+
private readonly exec: Exec;
52+
private readonly httpGet: HttpGet;
53+
private readonly npmPath: string;
3954
readonly installTypingHost: InstallTypingHost = sys;
4055

4156
constructor(globalTypingsCacheLocation: string, throttleLimit: number, log: Log) {
4257
super(
4358
globalTypingsCacheLocation,
44-
/*npmPath*/ getNPMLocation(process.argv[0]),
4559
toPath("typingSafeList.json", __dirname, createGetCanonicalFileName(sys.useCaseSensitiveFileNames)),
4660
throttleLimit,
4761
log);
4862
if (this.log.isEnabled()) {
4963
this.log.writeLine(`Process id: ${process.pid}`);
5064
}
51-
const { exec } = require("child_process");
52-
this.exec = exec;
65+
this.npmPath = getNPMLocation(process.argv[0]);
66+
this.exec = require("child_process").exec;
67+
this.httpGet = require("http").get;
5368
}
5469

5570
init() {
@@ -75,17 +90,54 @@ namespace ts.server.typingsInstaller {
7590
}
7691
}
7792

78-
protected runCommand(requestKind: RequestKind, requestId: number, command: string, cwd: string, onRequestCompleted: RequestCompletedAction): void {
93+
protected executeRequest(requestKind: RequestKind, requestId: number, args: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void {
7994
if (this.log.isEnabled()) {
80-
this.log.writeLine(`#${requestId} running command '${command}'.`);
95+
this.log.writeLine(`#${requestId} executing ${requestKind}, arguments'${JSON.stringify(args)}'.`);
96+
}
97+
switch (requestKind) {
98+
case NpmViewRequest: {
99+
// const command = `${self.npmPath} view @types/${typing} --silent name`;
100+
// use http request to global npm registry instead of running npm view
101+
Debug.assert(args.length === 1);
102+
const url = `http://registry.npmjs.org/@types%2f${args[0]}`;
103+
const start = Date.now();
104+
this.httpGet(url, response => {
105+
let ok = false;
106+
if (this.log.isEnabled()) {
107+
this.log.writeLine(`${requestKind} #${requestId} request to ${url}:: status code ${response.statusCode}, status message '${response.statusMessage}', took ${Date.now() - start} ms`);
108+
}
109+
switch (response.statusCode) {
110+
case 200: // OK
111+
case 301: // redirect - Moved - treat package as present
112+
case 302: // redirect - Found - treat package as present
113+
ok = true;
114+
break;
115+
}
116+
response.destroy();
117+
onRequestCompleted(ok);
118+
}).on("error", (err: Error) => {
119+
if (this.log.isEnabled()) {
120+
this.log.writeLine(`${requestKind} #${requestId} query to npm registry failed with error ${err.message}, stack ${err.stack}`);
121+
}
122+
onRequestCompleted(/*success*/ false);
123+
});
124+
}
125+
break;
126+
case NpmInstallRequest: {
127+
const command = `${this.npmPath} install ${args.join(" ")} --save-dev`;
128+
const start = Date.now();
129+
this.exec(command, { cwd }, (err, stdout, stderr) => {
130+
if (this.log.isEnabled()) {
131+
this.log.writeLine(`${requestKind} #${requestId} took: ${Date.now() - start} ms${sys.newLine}stdout: ${stdout}${sys.newLine}stderr: ${stderr}`);
132+
}
133+
// treat any output on stdout as success
134+
onRequestCompleted(!!stdout);
135+
});
136+
}
137+
break;
138+
default:
139+
Debug.assert(false, `Unknown request kind ${requestKind}`);
81140
}
82-
this.exec(command, { cwd }, (err, stdout, stderr) => {
83-
if (this.log.isEnabled()) {
84-
this.log.writeLine(`${requestKind} #${requestId} stdout: ${stdout}`);
85-
this.log.writeLine(`${requestKind} #${requestId} stderr: ${stderr}`);
86-
}
87-
onRequestCompleted(err, stdout, stderr);
88-
});
89141
}
90142
}
91143

0 commit comments

Comments
 (0)