shutdown typing installer if parent process is exited

This commit is contained in:
Vladimir Matveev 2016-08-17 13:01:48 -07:00
parent 758d8de8f9
commit ce35f96892
3 changed files with 54 additions and 14 deletions

View File

@ -12,6 +12,7 @@ namespace ts.server {
interface NodeChildProcess {
send(message: any, sendHandle?: any): void;
on(message: "message", f: (m: any) => void): void;
kill(): void;
}
const childProcess: {
@ -175,6 +176,10 @@ namespace ts.server {
this.installer = childProcess.fork(combinePaths(__dirname, "typingsInstaller.js"));
this.installer.on("message", m => this.handleMessage(m));
process.on("exit", () => {
this.installer.kill();
})
}
onProjectClosed(p: Project): void {

View File

@ -52,6 +52,9 @@ namespace ts.server.typingsInstaller {
constructor(log?: Log) {
super(getGlobalCacheLocation(), toPath("typingSafeList.json", __dirname, createGetCanonicalFileName(sys.useCaseSensitiveFileNames)), log);
if (this.log.isEnabled()) {
this.log.writeLine(`Process id: ${process.pid}`);
}
const { exec, execSync } = require("child_process");
this.execSync = execSync;
this.exec = exec;
@ -84,20 +87,34 @@ namespace ts.server.typingsInstaller {
protected isPackageInstalled(packageName: string) {
try {
this.execSync(`npm list --global --depth=1 ${packageName}`, { stdio: "ignore" });
const output = this.execSync(`npm list --global --depth=1 ${packageName}`, { stdio: "pipe" }).toString();
if (this.log.isEnabled()) {
this.log.writeLine(`IsPackageInstalled::stdout '${output}'`);
}
return true;
}
catch (e) {
if (this.log.isEnabled()) {
this.log.writeLine(`IsPackageInstalled::err::stdout '${e.stdout && e.stdout.toString()}'`);
this.log.writeLine(`IsPackageInstalled::err::stderr '${e.stdout && e.stderr.toString()}'`);
}
return false;
}
}
protected installPackage(packageName: string) {
try {
this.execSync(`npm install --global ${packageName}`, { stdio: "ignore" });
const output = this.execSync(`npm install --global ${packageName}`, { stdio: "pipe" }).toString();
if (this.log.isEnabled()) {
this.log.writeLine(`installPackage::stdout '${output}'`);
}
return true;
}
catch (e) {
if (this.log.isEnabled()) {
this.log.writeLine(`installPackage::err::stdout '${e.stdout && e.stdout.toString()}'`);
this.log.writeLine(`installPackage::err::stderr '${e.stdout && e.stderr.toString()}'`);
}
return false;
}
}
@ -107,6 +124,9 @@ namespace ts.server.typingsInstaller {
this.log.writeLine(`Sending response: ${JSON.stringify(response)}`)
}
process.send(response);
if (this.log.isEnabled()) {
this.log.writeLine(`Response has been sent.`)
}
}
protected runTsd(cachePath: string, typingsToInstall: string[], postInstallAction: (installedTypings: string[]) => void): void {
@ -139,11 +159,15 @@ namespace ts.server.typingsInstaller {
}
const log = new FileLog(process.env.TI_LOG_FILE);
process.on("uncaughtException", (e: Error) => {
if (log.isEnabled()) {
if (log.isEnabled()) {
process.on("uncaughtException", (e: Error) => {
log.writeLine(`Unhandled exception: ${e} at ${e.stack}`);
}
})
});
process.on("disconnect", () => {
log.writeLine(`Parent process has exited, shutting down...`);
process.exit(0);
});
}
const installer = new NodeTypingsInstaller(log);
installer.init();
}

View File

@ -72,7 +72,7 @@ namespace ts.server.typingsInstaller {
this.closeWatchers(req.projectName);
}
private closeWatchers(projectName: string): boolean {
private closeWatchers(projectName: string): void {
if (this.log.isEnabled()) {
this.log.writeLine(`Closing file watchers for project '${projectName}'`);
}
@ -82,7 +82,7 @@ namespace ts.server.typingsInstaller {
this.log.writeLine(`No watchers are registered for project '${projectName}'`);
}
return false;
return;
}
for (const w of watchers) {
w.close();
@ -93,8 +93,6 @@ namespace ts.server.typingsInstaller {
if (this.log.isEnabled()) {
this.log.writeLine(`Closing file watchers for project '${projectName}' - done.`);
}
return true;
}
install(req: DiscoverTypings) {
@ -137,7 +135,14 @@ namespace ts.server.typingsInstaller {
this.watchFiles(req.projectName, discoverTypingsResult.filesToWatch);
// install typings
this.installTypings(req, req.cachePath || this.globalCachePath, discoverTypingsResult.cachedTypingPaths, discoverTypingsResult.newTypingNames);
if (discoverTypingsResult.newTypingNames.length) {
this.installTypings(req, req.cachePath || this.globalCachePath, discoverTypingsResult.cachedTypingPaths, discoverTypingsResult.newTypingNames);
}
else {
if (this.log.isEnabled()) {
this.log.writeLine(`No new typings were requested as a result of typings discovery`);
}
}
}
private processCacheLocation(cacheLocation: string) {
@ -259,17 +264,23 @@ namespace ts.server.typingsInstaller {
if (!files.length) {
return;
}
// shut down existing watchers
this.closeWatchers(projectName);
// handler should be invoked once for the entire set of files since it will trigger full rediscovery of typings
let isInvoked = false;
const watchers: FileWatcher[] = [];
for (const file of files) {
const w = this.installTypingHost.watchFile(file, f => {
if (this.log.isEnabled()) {
this.log.writeLine(`FS notification for '${f}', sending 'clean' message for project '${projectName}'`);
this.log.writeLine(`Got FS notification for ${f}, handler is already invoked '${isInvoked}'`);
}
this.closeWatchers(projectName);
this.sendResponse(<InvalidateCachedTypings>{ projectName: projectName, kind: "invalidate" })
this.sendResponse({ projectName: projectName, kind: "invalidate" });
isInvoked = true;
});
watchers.push(w);
}
this.projectWatchers[projectName] = watchers;
}
private createSetTypings(request: DiscoverTypings, typings: string[]): SetTypings {