Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 6 additions & 10 deletions src/harness/unittests/tsserverProjectSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,8 @@ namespace ts.projectSystem {

export interface PostExecAction {
readonly requestKind: TI.RequestKind;
readonly error: Error;
readonly stdout: string;
readonly stderr: string;
readonly callback: (err: Error, stdout: string, stderr: string) => void;
readonly success: boolean;
readonly callback: TI.RequestCompletedAction;
}

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

Expand All @@ -65,7 +63,7 @@ namespace ts.projectSystem {
const actionsToRun = this.postExecActions;
this.postExecActions = [];
for (const action of actionsToRun) {
action.callback(action.error, action.stdout, action.stderr);
action.callback(action.success);
}
}

Expand All @@ -85,7 +83,7 @@ namespace ts.projectSystem {
return this.installTypingHost;
}

runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: (err: Error, stdout: string, stderr: string) => void): void {
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: TI.RequestCompletedAction): void {
switch (requestKind) {
case TI.NpmViewRequest:
case TI.NpmInstallRequest:
Expand All @@ -108,9 +106,7 @@ namespace ts.projectSystem {
addPostExecAction(requestKind: TI.RequestKind, stdout: string | string[], cb: TI.RequestCompletedAction) {
const out = typeof stdout === "string" ? stdout : createNpmPackageJsonString(stdout);
const action: PostExecAction = {
error: undefined,
stdout: out,
stderr: "",
success: !!out,
callback: cb,
requestKind
};
Expand Down
28 changes: 14 additions & 14 deletions src/harness/unittests/typingsInstaller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ namespace ts.projectSystem {
function executeCommand(self: Installer, host: TestServerHost, installedTypings: string[], typingFiles: FileOrFolder[], requestKind: TI.RequestKind, cb: TI.RequestCompletedAction): void {
switch (requestKind) {
case TI.NpmInstallRequest:
self.addPostExecAction(requestKind, installedTypings, (err, stdout, stderr) => {
self.addPostExecAction(requestKind, installedTypings, success => {
for (const file of typingFiles) {
host.createFileOrFolder(file, /*createParentDirectory*/ true);
}
cb(err, stdout, stderr);
cb(success);
});
break;
case TI.NpmViewRequest:
Expand Down Expand Up @@ -81,7 +81,7 @@ namespace ts.projectSystem {
constructor() {
super(host);
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
const installedTypings = ["@types/jquery"];
const typingFiles = [jquery];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
Expand Down Expand Up @@ -125,7 +125,7 @@ namespace ts.projectSystem {
constructor() {
super(host);
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
const installedTypings = ["@types/jquery"];
const typingFiles = [jquery];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
Expand Down Expand Up @@ -221,7 +221,7 @@ namespace ts.projectSystem {
enqueueIsCalled = true;
super.enqueueInstallTypingsRequest(project, typingOptions);
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: TI.RequestCompletedAction): void {
const installedTypings = ["@types/jquery"];
const typingFiles = [jquery];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
Expand Down Expand Up @@ -275,7 +275,7 @@ namespace ts.projectSystem {
constructor() {
super(host);
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: TI.RequestCompletedAction): void {
const installedTypings = ["@types/lodash", "@types/react"];
const typingFiles = [lodash, react];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
Expand Down Expand Up @@ -323,7 +323,7 @@ namespace ts.projectSystem {
enqueueIsCalled = true;
super.enqueueInstallTypingsRequest(project, typingOptions);
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: TI.RequestCompletedAction): void {
const installedTypings: string[] = [];
const typingFiles: FileOrFolder[] = [];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
Expand Down Expand Up @@ -398,7 +398,7 @@ namespace ts.projectSystem {
constructor() {
super(host);
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: TI.RequestCompletedAction): void {
const installedTypings = ["@types/commander", "@types/express", "@types/jquery", "@types/moment"];
const typingFiles = [commander, express, jquery, moment];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
Expand Down Expand Up @@ -477,7 +477,7 @@ namespace ts.projectSystem {
constructor() {
super(host, { throttleLimit: 3 });
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: TI.RequestCompletedAction): void {
const installedTypings = ["@types/commander", "@types/express", "@types/jquery", "@types/moment", "@types/lodash"];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
}
Expand Down Expand Up @@ -567,10 +567,10 @@ namespace ts.projectSystem {
constructor() {
super(host, { throttleLimit: 3 });
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: TI.RequestCompletedAction): void {
if (requestKind === TI.NpmInstallRequest) {
let typingFiles: (FileOrFolder & { typings: string}) [] = [];
if (command.indexOf("commander") >= 0) {
if (args.indexOf("@types/commander") >= 0) {
typingFiles = [commander, jquery, lodash, cordova];
}
else {
Expand Down Expand Up @@ -655,7 +655,7 @@ namespace ts.projectSystem {
constructor() {
super(host, { globalTypingsCacheLocation: "/tmp" });
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
const installedTypings = ["@types/jquery"];
const typingFiles = [jqueryDTS];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
Expand Down Expand Up @@ -701,7 +701,7 @@ namespace ts.projectSystem {
constructor() {
super(host, { globalTypingsCacheLocation: "/tmp" });
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
const installedTypings = ["@types/jquery"];
const typingFiles = [jqueryDTS];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
Expand Down Expand Up @@ -766,7 +766,7 @@ namespace ts.projectSystem {
constructor() {
super(host, { globalTypingsCacheLocation: "/tmp" }, { isEnabled: () => true, writeLine: msg => messages.push(msg) });
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
executeRequest(requestKind: TI.RequestKind, requestId: number, args: string[], cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
assert(false, "runCommand should not be invoked");
}
})();
Expand Down
7 changes: 6 additions & 1 deletion src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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) {
Expand Down
80 changes: 66 additions & 14 deletions src/server/typingsInstaller/nodeTypingsInstaller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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);
});
}
}

Expand Down
Loading