diff --git a/src/server/protocol.ts b/src/server/protocol.ts index d054867cd1c..d12aa2e0a48 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -2127,6 +2127,17 @@ namespace ts.server.protocol { payload: any; } + export type TypesInstallerInitializationFailedEventName = "typesInstallerInitializationFailed"; + + export interface TypesInstallerInitializationFailedEvent extends Event { + event: TypesInstallerInitializationFailedEventName; + body: TypesInstallerInitializationFailedEventBody; + } + + export interface TypesInstallerInitializationFailedEventBody { + message: string; + } + export type TypingsInstalledTelemetryEventName = "typingsInstalled"; export interface TypingsInstalledTelemetryEventBody extends TelemetryEventBody { diff --git a/src/server/server.ts b/src/server/server.ts index 475e5cdf920..bfd4e422047 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -304,11 +304,23 @@ namespace ts.server { }); } - private handleMessage(response: SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes) { + private handleMessage(response: SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes | InitializationFailedResponse) { if (this.logger.hasLevel(LogLevel.verbose)) { this.logger.info(`Received response: ${JSON.stringify(response)}`); } + if (response.kind === EventInitializationFailed) { + if (!this.eventSender) { + return; + } + const body: protocol.TypesInstallerInitializationFailedEventBody = { + message: response.message + } + const eventName: protocol.TypesInstallerInitializationFailedEventName = "typesInstallerInitializationFailed"; + this.eventSender.event(body, eventName); + return; + } + if (response.kind === EventBeginInstallTypes) { if (!this.eventSender) { return; diff --git a/src/server/shared.ts b/src/server/shared.ts index 77f66fc5a2d..5d65f8c90e9 100644 --- a/src/server/shared.ts +++ b/src/server/shared.ts @@ -5,6 +5,7 @@ namespace ts.server { export const ActionInvalidate: ActionInvalidate = "action::invalidate"; export const EventBeginInstallTypes: EventBeginInstallTypes = "event::beginInstallTypes"; export const EventEndInstallTypes: EventEndInstallTypes = "event::endInstallTypes"; + export const EventInitializationFailed: EventInitializationFailed = "event::initializationFailed"; export namespace Arguments { export const GlobalCacheLocation = "--globalTypingsCacheLocation"; diff --git a/src/server/types.ts b/src/server/types.ts index 6edf424cbe6..81e1f09639f 100644 --- a/src/server/types.ts +++ b/src/server/types.ts @@ -47,9 +47,15 @@ declare namespace ts.server { export type ActionInvalidate = "action::invalidate"; export type EventBeginInstallTypes = "event::beginInstallTypes"; export type EventEndInstallTypes = "event::endInstallTypes"; + export type EventInitializationFailed = "event::initializationFailed"; export interface TypingInstallerResponse { - readonly kind: ActionSet | ActionInvalidate | EventBeginInstallTypes | EventEndInstallTypes; + readonly kind: ActionSet | ActionInvalidate | EventBeginInstallTypes | EventEndInstallTypes | EventInitializationFailed; + } + + export interface InitializationFailedResponse extends TypingInstallerResponse { + readonly kind: EventInitializationFailed; + readonly message: string; } export interface ProjectResponse extends TypingInstallerResponse { diff --git a/src/server/typingsInstaller/nodeTypingsInstaller.ts b/src/server/typingsInstaller/nodeTypingsInstaller.ts index b9d08937467..74497504ba3 100644 --- a/src/server/typingsInstaller/nodeTypingsInstaller.ts +++ b/src/server/typingsInstaller/nodeTypingsInstaller.ts @@ -74,6 +74,8 @@ namespace ts.server.typingsInstaller { private readonly npmPath: string; readonly typesRegistry: Map; + private delayedInitializationError: InitializationFailedResponse; + constructor(globalTypingsCacheLocation: string, throttleLimit: number, log: Log) { super( sys, @@ -99,6 +101,11 @@ namespace ts.server.typingsInstaller { if (this.log.isEnabled()) { this.log.writeLine(`Error updating ${TypesRegistryPackageName} package: ${(e).message}`); } + // store error info to report it later when it is known that server is already listening to events from typings installer + this.delayedInitializationError = { + kind: "event::initializationFailed", + message: (e).message + }; } this.typesRegistry = loadTypesRegistryFile(getTypesRegistryFileLocation(globalTypingsCacheLocation), this.installTypingHost, this.log); @@ -106,6 +113,11 @@ namespace ts.server.typingsInstaller { listen() { process.on("message", (req: DiscoverTypings | CloseProject) => { + if (this.delayedInitializationError) { + // report initializationFailed error + this.sendResponse(this.delayedInitializationError); + this.delayedInitializationError = undefined; + } switch (req.kind) { case "discover": this.install(req); @@ -116,7 +128,7 @@ namespace ts.server.typingsInstaller { }); } - protected sendResponse(response: SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes) { + protected sendResponse(response: SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes | InitializationFailedResponse) { if (this.log.isEnabled()) { this.log.writeLine(`Sending response: ${JSON.stringify(response)}`); }