Merge pull request #11577 from Microsoft/vladima/configure-typing-acquisition

Disable automatic type acquisition with command line option, replace npm view with request to npm registry
This commit is contained in:
Vladimir Matveev
2016-10-13 10:19:18 -07:00
parent 31a55e6452
commit 224582c4f3
5 changed files with 105 additions and 55 deletions

View File

@@ -265,13 +265,16 @@ namespace ts.server {
installerEventPort: number,
canUseEvents: boolean,
useSingleInferredProject: boolean,
disableAutomaticTypingAcquisition: boolean,
globalTypingsCacheLocation: string,
logger: server.Logger) {
super(
host,
cancellationToken,
useSingleInferredProject,
new NodeTypingsInstaller(logger, installerEventPort, globalTypingsCacheLocation, host.newLine),
disableAutomaticTypingAcquisition
? nullTypingsInstaller
: new NodeTypingsInstaller(logger, installerEventPort, globalTypingsCacheLocation, host.newLine),
Buffer.byteLength,
process.hrtime,
logger,
@@ -512,12 +515,14 @@ namespace ts.server {
}
const useSingleInferredProject = sys.args.indexOf("--useSingleInferredProject") >= 0;
const disableAutomaticTypingAcquisition = sys.args.indexOf("--disableAutomaticTypingAcquisition") >= 0;
const ioSession = new IOSession(
sys,
cancellationToken,
eventPort,
/*canUseEvents*/ eventPort === undefined,
useSingleInferredProject,
disableAutomaticTypingAcquisition,
getGlobalTypingsCacheLocation(),
logger);
process.on("uncaughtException", function (err: Error) {

View File

@@ -33,23 +33,38 @@ namespace ts.server.typingsInstaller {
}
}
export class NodeTypingsInstaller extends TypingsInstaller {
private readonly exec: { (command: string, options: { cwd: string }, callback?: (error: Error, stdout: string, stderr: string) => void): any };
type HttpGet = {
(url: string, callback: (response: HttpResponse) => void): NodeJS.EventEmitter;
}
interface HttpResponse extends NodeJS.ReadableStream {
statusCode: number;
statusMessage: string;
destroy(): void;
}
type Exec = {
(command: string, options: { cwd: string }, callback?: (error: Error, stdout: string, stderr: string) => void): any
}
export class NodeTypingsInstaller extends TypingsInstaller {
private readonly exec: Exec;
private readonly httpGet: HttpGet;
private readonly npmPath: string;
readonly installTypingHost: InstallTypingHost = sys;
constructor(globalTypingsCacheLocation: string, throttleLimit: number, log: Log) {
super(
globalTypingsCacheLocation,
/*npmPath*/ getNPMLocation(process.argv[0]),
toPath("typingSafeList.json", __dirname, createGetCanonicalFileName(sys.useCaseSensitiveFileNames)),
throttleLimit,
log);
if (this.log.isEnabled()) {
this.log.writeLine(`Process id: ${process.pid}`);
}
const { exec } = require("child_process");
this.exec = exec;
this.npmPath = getNPMLocation(process.argv[0]);
this.exec = require("child_process").exec;
this.httpGet = require("http").get;
}
init() {
@@ -75,17 +90,54 @@ namespace ts.server.typingsInstaller {
}
}
protected runCommand(requestKind: RequestKind, requestId: number, command: string, cwd: string, onRequestCompleted: RequestCompletedAction): void {
protected executeRequest(requestKind: RequestKind, requestId: number, args: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void {
if (this.log.isEnabled()) {
this.log.writeLine(`#${requestId} running command '${command}'.`);
this.log.writeLine(`#${requestId} executing ${requestKind}, arguments'${JSON.stringify(args)}'.`);
}
switch (requestKind) {
case NpmViewRequest: {
// const command = `${self.npmPath} view @types/${typing} --silent name`;
// use http request to global npm registry instead of running npm view
Debug.assert(args.length === 1);
const url = `http://registry.npmjs.org/@types%2f${args[0]}`;
const start = Date.now();
this.httpGet(url, response => {
let ok = false;
if (this.log.isEnabled()) {
this.log.writeLine(`${requestKind} #${requestId} request to ${url}:: status code ${response.statusCode}, status message '${response.statusMessage}', took ${Date.now() - start} ms`);
}
switch (response.statusCode) {
case 200: // OK
case 301: // redirect - Moved - treat package as present
case 302: // redirect - Found - treat package as present
ok = true;
break;
}
response.destroy();
onRequestCompleted(ok);
}).on("error", (err: Error) => {
if (this.log.isEnabled()) {
this.log.writeLine(`${requestKind} #${requestId} query to npm registry failed with error ${err.message}, stack ${err.stack}`);
}
onRequestCompleted(/*success*/ false);
});
}
break;
case NpmInstallRequest: {
const command = `${this.npmPath} install ${args.join(" ")} --save-dev`;
const start = Date.now();
this.exec(command, { cwd }, (err, stdout, stderr) => {
if (this.log.isEnabled()) {
this.log.writeLine(`${requestKind} #${requestId} took: ${Date.now() - start} ms${sys.newLine}stdout: ${stdout}${sys.newLine}stderr: ${stderr}`);
}
// treat any output on stdout as success
onRequestCompleted(!!stdout);
});
}
break;
default:
Debug.assert(false, `Unknown request kind ${requestKind}`);
}
this.exec(command, { cwd }, (err, stdout, stderr) => {
if (this.log.isEnabled()) {
this.log.writeLine(`${requestKind} #${requestId} stdout: ${stdout}`);
this.log.writeLine(`${requestKind} #${requestId} stderr: ${stderr}`);
}
onRequestCompleted(err, stdout, stderr);
});
}
}

View File

@@ -65,11 +65,11 @@ namespace ts.server.typingsInstaller {
export type RequestKind = typeof NpmViewRequest | typeof NpmInstallRequest;
export type RequestCompletedAction = (err: Error, stdout: string, stderr: string) => void;
export type RequestCompletedAction = (success: boolean) => void;
type PendingRequest = {
requestKind: RequestKind;
requestId: number;
command: string;
args: string[];
cwd: string;
onRequestCompleted: RequestCompletedAction
};
@@ -88,7 +88,6 @@ namespace ts.server.typingsInstaller {
constructor(
readonly globalCachePath: string,
readonly npmPath: string,
readonly safeListPath: Path,
readonly throttleLimit: number,
protected readonly log = nullLog) {
@@ -331,13 +330,12 @@ namespace ts.server.typingsInstaller {
let execInstallCmdCount = 0;
const filteredTypings: string[] = [];
for (const typing of typingsToInstall) {
execNpmViewTyping(this, typing);
filterExistingTypings(this, typing);
}
function execNpmViewTyping(self: TypingsInstaller, typing: string) {
const command = `${self.npmPath} view @types/${typing} --silent name`;
self.execAsync(NpmViewRequest, requestId, command, cachePath, (err, stdout, stderr) => {
if (stdout) {
function filterExistingTypings(self: TypingsInstaller, typing: string) {
self.execAsync(NpmViewRequest, requestId, [typing], cachePath, ok => {
if (ok) {
filteredTypings.push(typing);
}
execInstallCmdCount++;
@@ -353,9 +351,8 @@ namespace ts.server.typingsInstaller {
return;
}
const scopedTypings = filteredTypings.map(t => "@types/" + t);
const command = `${self.npmPath} install ${scopedTypings.join(" ")} --save-dev`;
self.execAsync(NpmInstallRequest, requestId, command, cachePath, (err, stdout, stderr) => {
postInstallAction(stdout ? scopedTypings : []);
self.execAsync(NpmInstallRequest, requestId, scopedTypings, cachePath, ok => {
postInstallAction(ok ? scopedTypings : []);
});
}
}
@@ -403,8 +400,8 @@ namespace ts.server.typingsInstaller {
};
}
private execAsync(requestKind: RequestKind, requestId: number, command: string, cwd: string, onRequestCompleted: RequestCompletedAction): void {
this.pendingRunRequests.unshift({ requestKind, requestId, command, cwd, onRequestCompleted });
private execAsync(requestKind: RequestKind, requestId: number, args: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void {
this.pendingRunRequests.unshift({ requestKind, requestId, args, cwd, onRequestCompleted });
this.executeWithThrottling();
}
@@ -412,15 +409,15 @@ namespace ts.server.typingsInstaller {
while (this.inFlightRequestCount < this.throttleLimit && this.pendingRunRequests.length) {
this.inFlightRequestCount++;
const request = this.pendingRunRequests.pop();
this.runCommand(request.requestKind, request.requestId, request.command, request.cwd, (err, stdout, stderr) => {
this.executeRequest(request.requestKind, request.requestId, request.args, request.cwd, ok => {
this.inFlightRequestCount--;
request.onRequestCompleted(err, stdout, stderr);
request.onRequestCompleted(ok);
this.executeWithThrottling();
});
}
}
protected abstract runCommand(requestKind: RequestKind, requestId: number, command: string, cwd: string, onRequestCompleted: RequestCompletedAction): void;
protected abstract executeRequest(requestKind: RequestKind, requestId: number, args: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void;
protected abstract sendResponse(response: SetTypings | InvalidateCachedTypings): void;
}
}