mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
use localhost:<eventPort> to send notifications when typings are updated
This commit is contained in:
+45
-9
@@ -9,15 +9,23 @@ namespace ts.server {
|
||||
gzipSync(buf: Buffer): Buffer
|
||||
} = require("zlib");
|
||||
|
||||
const net: {
|
||||
connect(options: { port: number }, onConnect?: () => void): NodeSocket
|
||||
} = require("net");
|
||||
|
||||
const childProcess: {
|
||||
fork(modulePath: string, args: string[], options?: { execArgv: string[], env?: MapLike<string> }): NodeChildProcess;
|
||||
} = require("child_process");
|
||||
|
||||
interface NodeChildProcess {
|
||||
send(message: any, sendHandle?: any): void;
|
||||
on(message: "message", f: (m: any) => void): void;
|
||||
kill(): void;
|
||||
}
|
||||
|
||||
const childProcess: {
|
||||
fork(modulePath: string): NodeChildProcess;
|
||||
} = require("child_process");
|
||||
interface NodeSocket {
|
||||
write(data: string, encoding: string): boolean;
|
||||
}
|
||||
|
||||
interface ReadLineOptions {
|
||||
input: NodeJS.ReadableStream;
|
||||
@@ -106,6 +114,10 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
getLogFileName() {
|
||||
return this.logFilename;
|
||||
}
|
||||
|
||||
perftrc(s: string) {
|
||||
this.msg(s, Msg.Perf);
|
||||
}
|
||||
@@ -163,9 +175,15 @@ namespace ts.server {
|
||||
|
||||
class NodeTypingsInstaller implements ITypingsInstaller {
|
||||
private installer: NodeChildProcess;
|
||||
private socket: NodeSocket;
|
||||
private projectService: ProjectService;
|
||||
|
||||
constructor(private readonly logger: server.Logger) {
|
||||
constructor(private readonly logger: server.Logger, private readonly eventPort: number) {
|
||||
if (eventPort) {
|
||||
const s = net.connect({ port: eventPort }, () => {
|
||||
this.socket = s;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
attach(projectService: ProjectService) {
|
||||
@@ -174,7 +192,11 @@ namespace ts.server {
|
||||
this.logger.info("Binding...");
|
||||
}
|
||||
|
||||
this.installer = childProcess.fork(combinePaths(__dirname, "typingsInstaller.js"));
|
||||
let args: string[] = [];
|
||||
if (this.logger.loggingEnabled() && this.logger.getLogFileName()) {
|
||||
args = [ "--logFile", combinePaths(getDirectoryPath(normalizeSlashes(this.logger.getLogFileName())), `ti-${process.pid}.log`) ];
|
||||
}
|
||||
this.installer = childProcess.fork(combinePaths(__dirname, "typingsInstaller.js"), args);
|
||||
this.installer.on("message", m => this.handleMessage(m));
|
||||
process.on("exit", () => {
|
||||
this.installer.kill();
|
||||
@@ -198,12 +220,15 @@ namespace ts.server {
|
||||
this.logger.info(`Received response: ${JSON.stringify(response)}`);
|
||||
}
|
||||
this.projectService.updateTypingsForProject(response);
|
||||
if (response.kind == "set" && this.socket) {
|
||||
this.socket.write(JSON.stringify({ kind: "updateTypings", message: response }) + "\r\n", "utf8");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class IOSession extends Session {
|
||||
constructor(host: ServerHost, cancellationToken: HostCancellationToken, useSingleInferredProject: boolean, logger: server.Logger) {
|
||||
super(host, cancellationToken, useSingleInferredProject, new NodeTypingsInstaller(logger), Buffer.byteLength, maxUncompressedMessageSize, compress, process.hrtime, logger);
|
||||
constructor(host: ServerHost, cancellationToken: HostCancellationToken, eventPort: number, useSingleInferredProject: boolean, logger: server.Logger) {
|
||||
super(host, cancellationToken, useSingleInferredProject, new NodeTypingsInstaller(logger, eventPort), Buffer.byteLength, maxUncompressedMessageSize, compress, process.hrtime, logger);
|
||||
}
|
||||
|
||||
exit() {
|
||||
@@ -435,8 +460,19 @@ namespace ts.server {
|
||||
};
|
||||
};
|
||||
|
||||
const useSingleInferredProject = sys.args.some(arg => arg === "--useSingleInferredProject");
|
||||
const ioSession = new IOSession(sys, cancellationToken, useSingleInferredProject, logger);
|
||||
let eventPort: number;
|
||||
{
|
||||
const index = sys.args.indexOf("--eventPort");
|
||||
if (index >= 0 && index < sys.args.length - 1) {
|
||||
const v = parseInt(sys.args[index + 1]);
|
||||
if (!isNaN(v)) {
|
||||
eventPort = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const useSingleInferredProject = sys.args.indexOf("--useSingleInferredProject") >= 0;
|
||||
const ioSession = new IOSession(sys, cancellationToken, eventPort, useSingleInferredProject, logger);
|
||||
process.on("uncaughtException", function (err: Error) {
|
||||
ioSession.logError(err, "unknown");
|
||||
});
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace ts.server {
|
||||
readonly typingOptions: TypingOptions;
|
||||
readonly compilerOptions: CompilerOptions;
|
||||
readonly typings: TypingsArray;
|
||||
poisoned: boolean;
|
||||
}
|
||||
|
||||
const emptyArray: any[] = [];
|
||||
@@ -27,10 +28,7 @@ namespace ts.server {
|
||||
return (<ConfiguredProject>proj).getTypingOptions();
|
||||
}
|
||||
|
||||
const enableAutoDiscovery =
|
||||
proj.projectKind === ProjectKind.Inferred &&
|
||||
proj.getCompilerOptions().allowJs &&
|
||||
proj.getFileNames().every(f => fileExtensionIsAny(f, jsOrDts));
|
||||
const enableAutoDiscovery = proj.getFileNames().every(f => fileExtensionIsAny(f, jsOrDts));
|
||||
|
||||
// TODO: add .d.ts files to excludes
|
||||
return { enableAutoDiscovery, include: emptyArray, exclude: emptyArray };
|
||||
@@ -98,10 +96,20 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
const entry = this.perProjectCache[project.getProjectName()];
|
||||
const result: TypingsArray = entry ? entry.typings : <any>emptyArray;
|
||||
if (!entry || typingOptionsChanged(typingOptions, entry.typingOptions) || compilerOptionsChanged(project.getCompilerOptions(), entry.compilerOptions)) {
|
||||
// something has been changed, issue a request to update typings
|
||||
this.installer.enqueueInstallTypingsRequest(project, typingOptions);
|
||||
// Note: entry is now poisoned since it does not really contain typings for a given combination of compiler options\typings options.
|
||||
// instead it acts as a placeholder to prevent issuing multiple requests
|
||||
this.perProjectCache[project.getProjectName()] = {
|
||||
compilerOptions: project.getCompilerOptions(),
|
||||
typingOptions,
|
||||
typings: result,
|
||||
poisoned: true
|
||||
};
|
||||
}
|
||||
return entry ? entry.typings : <any>emptyArray;
|
||||
return result;
|
||||
}
|
||||
|
||||
invalidateCachedTypingsForProject(project: Project) {
|
||||
@@ -116,7 +124,8 @@ namespace ts.server {
|
||||
this.perProjectCache[projectName] = {
|
||||
compilerOptions,
|
||||
typingOptions,
|
||||
typings: toTypingsArray(newTypings)
|
||||
typings: toTypingsArray(newTypings),
|
||||
poisoned: false
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace ts.server.typingsInstaller {
|
||||
}
|
||||
|
||||
export class NodeTypingsInstaller extends TypingsInstaller {
|
||||
private execSync: { (command: string, options: { stdio: "ignore" | "pipe" }): Buffer | string };
|
||||
private execSync: { (command: string, options: { stdio: "ignore" | "pipe", cwd?: string }): Buffer | string };
|
||||
private exec: { (command: string, options: { cwd: string }, callback?: (error: Error, stdout: string, stderr: string) => void): any };
|
||||
private npmBinPath: string;
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace ts.server.typingsInstaller {
|
||||
|
||||
protected isPackageInstalled(packageName: string) {
|
||||
try {
|
||||
const output = this.execSync(`npm list --global --depth=1 ${packageName}`, { stdio: "pipe" }).toString();
|
||||
const output = this.execSync(`npm list --silent --global --depth=1 ${packageName}`, { stdio: "pipe" }).toString();
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`IsPackageInstalled::stdout '${output}'`);
|
||||
}
|
||||
@@ -103,7 +103,7 @@ namespace ts.server.typingsInstaller {
|
||||
|
||||
protected installPackage(packageName: string) {
|
||||
try {
|
||||
const output = this.execSync(`npm install --global ${packageName}`, { stdio: "pipe" }).toString();
|
||||
const output = this.execSync(`npm install --silent --global ${packageName}`, { stdio: "pipe" }).toString();
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`installPackage::stdout '${output}'`);
|
||||
}
|
||||
@@ -132,10 +132,11 @@ namespace ts.server.typingsInstaller {
|
||||
const id = this.tsdRunCount;
|
||||
this.tsdRunCount++;
|
||||
const tsdPath = combinePaths(this.npmBinPath, "tsd");
|
||||
const command = `${tsdPath} install ${typingsToInstall.join(" ")} -ros`;
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`Running tsd ${id}, tsd path '${tsdPath}, typings to install: ${JSON.stringify(typingsToInstall)}. cache path '${cachePath}'`);
|
||||
this.log.writeLine(`Running tsd ${id}, command '${command}'. cache path '${cachePath}'`);
|
||||
}
|
||||
this.exec(`${tsdPath} install ${typingsToInstall.join(" ")} -ros`, { cwd: cachePath }, (err, stdout, stderr) => {
|
||||
this.exec(command, { cwd: cachePath }, (err, stdout, stderr) => {
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`TSD ${id} stdout: ${stdout}`);
|
||||
this.log.writeLine(`TSD ${id} stderr: ${stderr}`);
|
||||
@@ -157,7 +158,14 @@ namespace ts.server.typingsInstaller {
|
||||
}
|
||||
}
|
||||
|
||||
const log = new FileLog(process.env.TI_LOG_FILE);
|
||||
let logFilePath: string;
|
||||
{
|
||||
const logFileIndex = sys.args.indexOf("--logFile");
|
||||
if (logFileIndex >= 0 && logFileIndex < sys.args.length - 1) {
|
||||
logFilePath = sys.args[logFileIndex + 1];
|
||||
}
|
||||
}
|
||||
const log = new FileLog(logFilePath);
|
||||
if (log.isEnabled()) {
|
||||
process.on("uncaughtException", (e: Error) => {
|
||||
log.writeLine(`Unhandled exception: ${e} at ${e.stack}`);
|
||||
|
||||
+16
-1
@@ -17,6 +17,7 @@ namespace ts.server {
|
||||
startGroup(): void;
|
||||
endGroup(): void;
|
||||
msg(s: string, type?: Msg.Types): void;
|
||||
getLogFileName(): string;
|
||||
}
|
||||
|
||||
export namespace Msg {
|
||||
@@ -29,13 +30,27 @@ namespace ts.server {
|
||||
export type Types = Err | Info | Perf;
|
||||
}
|
||||
|
||||
function getProjectRootPath(project: Project): Path {
|
||||
switch (project.projectKind) {
|
||||
case ProjectKind.Configured:
|
||||
return <Path>project.getProjectName();
|
||||
case ProjectKind.Inferred:
|
||||
// TODO: fixme
|
||||
return <Path>"";
|
||||
case ProjectKind.External:
|
||||
const projectName = project.getProjectName();
|
||||
const host = project.projectService.host;
|
||||
return host.fileExists(projectName) ? <Path>getDirectoryPath(projectName) : <Path>projectName;
|
||||
}
|
||||
}
|
||||
|
||||
export function createInstallTypingsRequest(project: Project, typingOptions: TypingOptions, cachePath?: string): DiscoverTypings {
|
||||
return {
|
||||
projectName: project.getProjectName(),
|
||||
fileNames: project.getFileNames(),
|
||||
compilerOptions: project.getCompilerOptions(),
|
||||
typingOptions,
|
||||
projectRootPath: <Path>(project.projectKind === ProjectKind.Inferred ? "" : getDirectoryPath(project.getProjectName())), // TODO: fixme
|
||||
projectRootPath: getProjectRootPath(project),
|
||||
cachePath,
|
||||
kind: "discover"
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user