[WIP] send\receive install typing requests

This commit is contained in:
Vladimir Matveev 2016-08-12 18:17:16 -07:00
parent 959b6b6c07
commit 512ec04d29
5 changed files with 107 additions and 14 deletions

View File

@ -9,6 +9,14 @@ namespace ts.server {
gzipSync(buf: Buffer): Buffer
} = require("zlib");
interface NodeChildProcess {
send(message: any, sendHandle?: any): void;
}
const childProcess: {
fork(modulePath: string): NodeChildProcess;
} = require("child_process");
interface ReadLineOptions {
input: NodeJS.ReadableStream;
output?: NodeJS.WritableStream;
@ -151,10 +159,65 @@ namespace ts.server {
}
}
class NodeTypingsInstaller implements ITypingsInstaller {
private installer: NodeChildProcess;
private session: Session;
private cachePath: string;
constructor(private readonly logger: server.Logger) {
switch (process.platform) {
case "win32":
this.cachePath = normalizeSlashes(combinePaths(process.env.LOCALAPPDATA || process.env.APPDATA, "Microsoft/TypeScript"));
break;
case "darwin":
case "linux":
// TODO:
break;
}
}
bind(session: Session) {
if (this.logger.hasLevel(LogLevel.requestTime)) {
this.logger.info("Binding...")
}
this.installer = childProcess.fork(combinePaths(__dirname, "typingsInstaller.js"));
(<any>this.installer).on("message", (m: any) => this.handleMessage(m));
}
enqueueInstallTypingsRequest(project: Project, typingOptions: TypingOptions): void {
const request: InstallTypingsRequest = {
projectName: project.getProjectName(),
fileNames: project.getFileNames(),
compilerOptions: project.getCompilerOptions(),
typingOptions,
projectRootPath: <Path>(project.projectKind === ProjectKind.Inferred ? "" : getDirectoryPath(project.getProjectName())), // TODO: fixme
safeListPath: <Path>(combinePaths(process.cwd(), "typingSafeList.json")), // TODO: fixme
packageNameToTypingLocation: {}, // TODO: fixme
cachePath: this.cachePath
};
if (this.logger.hasLevel(LogLevel.verbose)) {
this.logger.info(`Sending request: ${JSON.stringify(request)}`);
}
this.installer.send(request);
}
C = 1;
private handleMessage(response: InstallTypingsResponse) {
if (this.logger.hasLevel(LogLevel.verbose)) {
this.logger.info(`Received response: ${JSON.stringify(response)}`)
}
require("fs").appendFileSync("E:\\sources\\git\\tss.txt", this.C + " !!!::" + JSON.stringify(response) + "\r\n");
this.C++;
this.session.onTypingsInstalled(response);
require("fs").appendFileSync("E:\\sources\\git\\tss.txt", this.C + " !!!::" + "done" + "\r\n");
}
}
class IOSession extends Session {
constructor(host: ServerHost, cancellationToken: HostCancellationToken, useSingleInferredProject: boolean, logger: ts.server.Logger) {
// TODO: fixme
super(host, cancellationToken, useSingleInferredProject, undefined, Buffer.byteLength, maxUncompressedMessageSize, compress, process.hrtime, logger);
constructor(host: ServerHost, cancellationToken: HostCancellationToken, useSingleInferredProject: boolean, logger: server.Logger) {
super(host, cancellationToken, useSingleInferredProject, new NodeTypingsInstaller(logger), Buffer.byteLength, maxUncompressedMessageSize, compress, process.hrtime, logger);
(<NodeTypingsInstaller>this.typingsInstaller).bind(this);
}
exit() {

View File

@ -143,7 +143,7 @@ namespace ts.server {
private host: ServerHost,
cancellationToken: HostCancellationToken,
useSingleInferredProject: boolean,
typingsInstaller: ITypingsInstaller,
protected readonly typingsInstaller: ITypingsInstaller,
private byteLength: (buf: string, encoding?: string) => number,
private maxUncompressedMessageSize: number,
private compress: (s: string) => CompressedData,
@ -1452,6 +1452,15 @@ namespace ts.server {
}
}
public onTypingsInstalled(response: InstallTypingsResponse) {
const project = this.projectService.findProject(response.projectName);
if (!project) {
return;
}
this.projectService.typingsCache.updateTypingsForProject(response.projectName, response.compilerOptions, response.typingOptions, response.typings);
project.updateGraph();
}
public onMessage(message: string) {
this.gcTimer.scheduleCollect();
let start: number[];

View File

@ -2,7 +2,7 @@
namespace ts.server {
export interface ITypingsInstaller {
enqueueInstallTypingsRequest(p: Project): void;
enqueueInstallTypingsRequest(p: Project, typingOptions: TypingOptions): void;
}
export const nullTypingsInstaller: ITypingsInstaller = {
@ -12,7 +12,7 @@ namespace ts.server {
class TypingsCacheEntry {
readonly typingOptions: TypingOptions;
readonly compilerOptions: CompilerOptions;
readonly typings: Path[];
readonly typings: string[];
}
const emptyArray: any[] = [];
@ -88,11 +88,19 @@ namespace ts.server {
const entry = this.perProjectCache[project.getProjectName()];
if (!entry || typingOptionsChanged(typingOptions, entry.typingOptions) || compilerOptionsChanged(project.getCompilerOptions(), entry.compilerOptions)) {
this.installer.enqueueInstallTypingsRequest(project);
this.installer.enqueueInstallTypingsRequest(project, typingOptions);
}
return entry ? entry.typings : emptyArray;
}
updateTypingsForProject(projectName: string, compilerOptions: CompilerOptions, typingOptions: TypingOptions, newTypings: string[]) {
this.perProjectCache[projectName] = {
compilerOptions,
typingOptions,
typings: newTypings
};
}
deleteTypingsForProject(project: Project) {
delete this.perProjectCache[project.getProjectName()];
}

View File

@ -4,7 +4,7 @@
namespace ts.server.typingsInstaller {
export class NodeTypingsInstaller extends TypingsInstaller {
private execSync: { (command: string, options: { stdio: "ignore" }): any };
private exec: { (command: string, options: {}, callback?: (error: Error, stdout: string, stderr: string) => void): any };
private exec: { (command: string, options: { cwd: string }, callback?: (error: Error, stdout: string, stderr: string) => void): any };
constructor() {
super();
this.execSync = require("child_process").execSync;
@ -13,14 +13,14 @@ namespace ts.server.typingsInstaller {
init() {
super.init();
process.on("install", (req: InstallTypingsRequest) => {
process.on("message", (req: InstallTypingsRequest) => {
this.install(req);
})
}
protected isPackageInstalled(packageName: string) {
try {
this.execSync(`npm list --global --depth=1 ${name}`, { stdio: "ignore" });
this.execSync(`npm list --global --depth=1 ${packageName}`, { stdio: "ignore" });
return true;
}
catch (e) {
@ -30,7 +30,7 @@ namespace ts.server.typingsInstaller {
protected installPackage(packageName: string) {
try {
this.execSync(`npm install --global ${name}`, { stdio: "ignore" });
this.execSync(`npm install --global ${packageName}`, { stdio: "ignore" });
return true;
}
catch (e) {
@ -42,12 +42,17 @@ namespace ts.server.typingsInstaller {
return sys;
}
C = 1;
protected sendResponse(response: InstallTypingsResponse) {
(<any>response).___id = [this.C];
this.C++;
log("sendResponse::" + JSON.stringify(response));
process.send(response);
}
protected runTsd(cachePath: string, typingsToInstall: string[], postInstallAction: (installedTypings: string[]) => void): void {
this.exec(`tsd install ${typingsToInstall.join(" ")} -ros`, {}, (err, stdout, stderr) => {
this.exec(`tsd install ${typingsToInstall.join(" ")} -ros`, { cwd: cachePath }, (err, stdout, stderr) => {
const i = stdout.indexOf("running install");
if (i < 0) {
return;

View File

@ -2,7 +2,9 @@
/// <reference path="../types.d.ts"/>
namespace ts.server.typingsInstaller {
export function log(s: string) {
require("fs").appendFileSync("E:\\sources\\git\\installer.txt", s + "\r\n");
}
const DefaultTsdSettings = JSON.stringify({
version: "v4",
repo: "DefinitelyTyped/DefinitelyTyped",
@ -20,6 +22,7 @@ namespace ts.server.typingsInstaller {
if (!this.isTsdInstalled) {
this.isTsdInstalled = this.installPackage("tsd");
}
log(`start ${this.isTsdInstalled}`);
}
install(req: InstallTypingsRequest) {
@ -27,6 +30,7 @@ namespace ts.server.typingsInstaller {
return;
}
log(`install ${JSON.stringify(req)}`);
const discoverTypingsResult = JsTyping.discoverTypings(
this.getInstallTypingHost(),
req.fileNames,
@ -36,6 +40,7 @@ namespace ts.server.typingsInstaller {
req.typingOptions,
req.compilerOptions);
log(`install ${JSON.stringify(discoverTypingsResult)}`);
// respond with whatever cached typings we have now
this.sendResponse(this.createResponse(req, discoverTypingsResult.cachedTypingPaths));
// start watching files
@ -46,6 +51,7 @@ namespace ts.server.typingsInstaller {
private installTypings(req: InstallTypingsRequest, currentlyCachedTypings: string[], typingsToInstall: string[]) {
typingsToInstall = filter(typingsToInstall, x => !hasProperty(this.missingTypings, x));
log(`install ${JSON.stringify(typingsToInstall)}`);
if (typingsToInstall.length === 0) {
return;
}
@ -57,9 +63,11 @@ namespace ts.server.typingsInstaller {
host.writeFile(tsdPath, DefaultTsdSettings);
}
this.runTsd(tsdPath, typingsToInstall, installedTypings => {
this.runTsd(req.cachePath, typingsToInstall, installedTypings => {
// TODO: record new missing package names
// TODO: watch project directory
installedTypings = installedTypings.map(x => getNormalizedAbsolutePath(x, req.cachePath));
log(`include ${JSON.stringify(installedTypings)}`);
this.sendResponse(this.createResponse(req, currentlyCachedTypings.concat(installedTypings)));
});
}