Add support for running extensions in the browser
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
import * as vscode from "vscode";
|
||||
import { CoderApi, VSCodeApi } from "../typings/api";
|
||||
import { CoderApi, VSCodeApi } from "../../typings/api";
|
||||
import { createCSSRule } from "vs/base/browser/dom";
|
||||
import { Emitter, Event } from "vs/base/common/event";
|
||||
import { IDisposable } from "vs/base/common/lifecycle";
|
@ -1,13 +1,18 @@
|
||||
import { Emitter } from "vs/base/common/event";
|
||||
import { URI } from "vs/base/common/uri";
|
||||
import { registerSingleton } from "vs/platform/instantiation/common/extensions";
|
||||
import { ServiceCollection } from "vs/platform/instantiation/common/serviceCollection";
|
||||
import { ITelemetryService } from "vs/platform/telemetry/common/telemetry";
|
||||
import { ILocalizationsService } from "vs/platform/localizations/common/localizations";
|
||||
import { LocalizationsService } from "vs/platform/localizations/electron-browser/localizationsService";
|
||||
import { ITelemetryService } from "vs/platform/telemetry/common/telemetry";
|
||||
import { IUpdateService } from "vs/platform/update/common/update";
|
||||
import { UpdateService } from "vs/platform/update/electron-browser/updateService";
|
||||
import { TelemetryChannelClient } from "vs/server/src/telemetry";
|
||||
import { IUploadService, UploadService } from 'vs/server/src/upload';
|
||||
import { coderApi, vscodeApi } from "vs/server/src/browser/api";
|
||||
import { IUploadService, UploadService } from "vs/server/src/browser/upload";
|
||||
import { INodeProxyService, NodeProxyChannelClient } from "vs/server/src/common/nodeProxy";
|
||||
import { TelemetryChannelClient } from "vs/server/src/common/telemetry";
|
||||
import "vs/workbench/contrib/localizations/browser/localizations.contribution";
|
||||
import "vs/workbench/contrib/update/electron-browser/update.contribution";
|
||||
import { IRemoteAgentService } from "vs/workbench/services/remote/common/remoteAgentService";
|
||||
|
||||
class TelemetryService extends TelemetryChannelClient {
|
||||
@ -18,16 +23,28 @@ class TelemetryService extends TelemetryChannelClient {
|
||||
}
|
||||
}
|
||||
|
||||
class NodeProxyService extends NodeProxyChannelClient implements INodeProxyService {
|
||||
private readonly _onClose = new Emitter<void>();
|
||||
public readonly onClose = this._onClose.event;
|
||||
private readonly _onDown = new Emitter<void>();
|
||||
public readonly onDown = this._onDown.event;
|
||||
private readonly _onUp = new Emitter<void>();
|
||||
public readonly onUp = this._onUp.event;
|
||||
|
||||
public constructor(
|
||||
@IRemoteAgentService remoteAgentService: IRemoteAgentService,
|
||||
) {
|
||||
// TODO: up/down/close
|
||||
super(remoteAgentService.getConnection()!.getChannel("nodeProxy"));
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(ILocalizationsService, LocalizationsService);
|
||||
registerSingleton(INodeProxyService, NodeProxyService);
|
||||
registerSingleton(ITelemetryService, TelemetryService);
|
||||
registerSingleton(IUpdateService, UpdateService);
|
||||
registerSingleton(IUploadService, UploadService, true);
|
||||
|
||||
import "vs/workbench/contrib/update/electron-browser/update.contribution";
|
||||
import 'vs/workbench/contrib/localizations/browser/localizations.contribution';
|
||||
|
||||
import { coderApi, vscodeApi } from "vs/server/src/api";
|
||||
|
||||
/**
|
||||
* This is called by vs/workbench/browser/web.main.ts after the workbench has
|
||||
* been initialized so we can initialize our own client-side code.
|
46
src/browser/extHostNodeProxy.ts
Normal file
46
src/browser/extHostNodeProxy.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { Emitter } from "vs/base/common/event";
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ExtHostNodeProxyShape, MainContext, MainThreadNodeProxyShape } from "vs/workbench/api/common/extHost.protocol";
|
||||
import { IExtHostRpcService } from "vs/workbench/api/common/extHostRpcService";
|
||||
|
||||
export class ExtHostNodeProxy implements ExtHostNodeProxyShape {
|
||||
_serviceBrand: any;
|
||||
|
||||
private readonly _onMessage = new Emitter<string>();
|
||||
public readonly onMessage = this._onMessage.event;
|
||||
private readonly _onClose = new Emitter<void>();
|
||||
public readonly onClose = this._onClose.event;
|
||||
private readonly _onDown = new Emitter<void>();
|
||||
public readonly onDown = this._onDown.event;
|
||||
private readonly _onUp = new Emitter<void>();
|
||||
public readonly onUp = this._onUp.event;
|
||||
|
||||
private readonly proxy: MainThreadNodeProxyShape;
|
||||
|
||||
constructor(@IExtHostRpcService rpc: IExtHostRpcService) {
|
||||
this.proxy = rpc.getProxy(MainContext.MainThreadNodeProxy);
|
||||
}
|
||||
|
||||
public $onMessage(message: string): void {
|
||||
this._onMessage.fire(message);
|
||||
}
|
||||
|
||||
public $onClose(): void {
|
||||
this._onClose.fire();
|
||||
}
|
||||
|
||||
public $onUp(): void {
|
||||
this._onUp.fire();
|
||||
}
|
||||
|
||||
public $onDown(): void {
|
||||
this._onDown.fire();
|
||||
}
|
||||
|
||||
public send(message: string): void {
|
||||
this.proxy.$send(message);
|
||||
}
|
||||
}
|
||||
|
||||
export interface IExtHostNodeProxy extends ExtHostNodeProxy { }
|
||||
export const IExtHostNodeProxy = createDecorator<IExtHostNodeProxy>('IExtHostNodeProxy');
|
37
src/browser/mainThreadNodeProxy.ts
Normal file
37
src/browser/mainThreadNodeProxy.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { IDisposable } from "vs/base/common/lifecycle";
|
||||
import { INodeProxyService } from "vs/server/src/common/nodeProxy";
|
||||
import { ExtHostContext, IExtHostContext, MainContext, MainThreadNodeProxyShape } from "vs/workbench/api/common/extHost.protocol";
|
||||
import { extHostNamedCustomer } from "vs/workbench/api/common/extHostCustomers";
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadNodeProxy)
|
||||
export class MainThreadNodeProxy implements MainThreadNodeProxyShape {
|
||||
private disposed = false;
|
||||
private disposables = <IDisposable[]>[];
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@INodeProxyService private readonly proxyService: INodeProxyService,
|
||||
) {
|
||||
if (!extHostContext.remoteAuthority) { // HACK: A terrible way to detect if running in the worker.
|
||||
const proxy = extHostContext.getProxy(ExtHostContext.ExtHostNodeProxy);
|
||||
this.disposables = [
|
||||
this.proxyService.onMessage((message: string) => proxy.$onMessage(message)),
|
||||
this.proxyService.onClose(() => proxy.$onClose()),
|
||||
this.proxyService.onDown(() => proxy.$onDown()),
|
||||
this.proxyService.onUp(() => proxy.$onUp()),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$send(message: string): void {
|
||||
if (!this.disposed) {
|
||||
this.proxyService.send(message);
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposables.forEach((d) => d.dispose());
|
||||
this.disposables = [];
|
||||
this.disposed = true;
|
||||
}
|
||||
}
|
47
src/common/nodeProxy.ts
Normal file
47
src/common/nodeProxy.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { Event } from "vs/base/common/event";
|
||||
import { IChannel, IServerChannel } from "vs/base/parts/ipc/common/ipc";
|
||||
import { createDecorator } from "vs/platform/instantiation/common/instantiation";
|
||||
import { ReadWriteConnection } from "vs/server/node_modules/@coder/node-browser/out/common/connection";
|
||||
|
||||
export const INodeProxyService = createDecorator<INodeProxyService>("nodeProxyService");
|
||||
|
||||
export interface INodeProxyService extends ReadWriteConnection {
|
||||
_serviceBrand: any;
|
||||
send(message: string): void;
|
||||
onMessage: Event<string>;
|
||||
onUp: Event<void>;
|
||||
onClose: Event<void>;
|
||||
onDown: Event<void>;
|
||||
}
|
||||
|
||||
export class NodeProxyChannel implements IServerChannel {
|
||||
constructor(private service: INodeProxyService) {}
|
||||
|
||||
listen(_: unknown, event: string): Event<any> {
|
||||
switch (event) {
|
||||
case "onMessage": return this.service.onMessage;
|
||||
}
|
||||
throw new Error(`Invalid listen ${event}`);
|
||||
}
|
||||
|
||||
async call(_: unknown, command: string, args?: any): Promise<any> {
|
||||
switch (command) {
|
||||
case "send": return this.service.send(args[0]);
|
||||
}
|
||||
throw new Error(`Invalid call ${command}`);
|
||||
}
|
||||
}
|
||||
|
||||
export class NodeProxyChannelClient {
|
||||
_serviceBrand: any;
|
||||
|
||||
public readonly onMessage: Event<string>;
|
||||
|
||||
constructor(private readonly channel: IChannel) {
|
||||
this.onMessage = this.channel.listen<string>("onMessage");
|
||||
}
|
||||
|
||||
public send(data: string): void {
|
||||
this.channel.call("send", [data]);
|
||||
}
|
||||
}
|
@ -16,9 +16,11 @@ import pkg from "vs/platform/product/node/package";
|
||||
import product from "vs/platform/product/node/product";
|
||||
import { IRemoteAgentEnvironment } from "vs/platform/remote/common/remoteAgentEnvironment";
|
||||
import { ITelemetryService } from "vs/platform/telemetry/common/telemetry";
|
||||
import { getTranslations } from "vs/server/src/nls";
|
||||
import { getUriTransformer } from "vs/server/src/util";
|
||||
import { INodeProxyService } from "vs/server/src/common/nodeProxy";
|
||||
import { getTranslations } from "vs/server/src/node/nls";
|
||||
import { getUriTransformer } from "vs/server/src/node/util";
|
||||
import { ExtensionScanner, ExtensionScannerInput } from "vs/workbench/services/extensions/node/extensionPoints";
|
||||
import { Server } from "vs/server/node_modules/@coder/node-browser/out/server/server";
|
||||
|
||||
/**
|
||||
* Extend the file provider to allow unwatching.
|
||||
@ -274,3 +276,37 @@ export class ExtensionEnvironmentChannel implements IServerChannel {
|
||||
this.telemetry.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
export class NodeProxyService implements INodeProxyService {
|
||||
public _serviceBrand = undefined;
|
||||
|
||||
public readonly server: Server;
|
||||
|
||||
private readonly _onMessage = new Emitter<string>();
|
||||
public readonly onMessage = this._onMessage.event;
|
||||
private readonly _$onMessage = new Emitter<string>();
|
||||
public readonly $onMessage = this._$onMessage.event;
|
||||
private readonly _onClose = new Emitter<void>();
|
||||
public readonly onClose = this._onClose.event;
|
||||
private readonly _onDown = new Emitter<void>();
|
||||
public readonly onDown = this._onDown.event;
|
||||
private readonly _onUp = new Emitter<void>();
|
||||
public readonly onUp = this._onUp.event;
|
||||
|
||||
public constructor() {
|
||||
// TODO: close/down/up
|
||||
this.server = new Server({
|
||||
onMessage: this.$onMessage,
|
||||
onClose: this.onClose,
|
||||
onDown: this.onDown,
|
||||
onUp: this.onUp,
|
||||
send: (message: string): void => {
|
||||
this._onMessage.fire(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public send(message: string): void {
|
||||
this._$onMessage.fire(message);
|
||||
}
|
||||
}
|
@ -9,10 +9,10 @@ import { buildHelpMessage, buildVersionMessage, Option as VsOption, options as v
|
||||
import { parseMainProcessArgv } from "vs/platform/environment/node/argvHelper";
|
||||
import pkg from "vs/platform/product/node/package";
|
||||
import product from "vs/platform/product/node/product";
|
||||
import { ipcMain } from "vs/server/src/ipc";
|
||||
import { enableCustomMarketplace } from "vs/server/src/marketplace";
|
||||
import { MainServer } from "vs/server/src/server";
|
||||
import { AuthType, buildAllowedMessage, enumToArray, FormatType, generateCertificate, generatePassword, localRequire, open, unpackExecutables } from "vs/server/src/util";
|
||||
import { ipcMain } from "vs/server/src/node/ipc";
|
||||
import { enableCustomMarketplace } from "vs/server/src/node/marketplace";
|
||||
import { MainServer } from "vs/server/src/node/server";
|
||||
import { AuthType, buildAllowedMessage, enumToArray, FormatType, generateCertificate, generatePassword, localRequire, open, unpackExecutables } from "vs/server/src/node/util";
|
||||
|
||||
const { logger } = localRequire<typeof import("@coder/logger/out/index")>("@coder/logger/out/index");
|
||||
setUnexpectedErrorHandler((error) => logger.warn(error.message));
|
@ -6,9 +6,9 @@ import { ISocket } from "vs/base/parts/ipc/common/ipc.net";
|
||||
import { NodeSocket } from "vs/base/parts/ipc/node/ipc.net";
|
||||
import { IEnvironmentService } from "vs/platform/environment/common/environment";
|
||||
import { ILogService } from "vs/platform/log/common/log";
|
||||
import { getNlsConfiguration } from "vs/server/src/nls";
|
||||
import { Protocol } from "vs/server/src/protocol";
|
||||
import { uriTransformerPath } from "vs/server/src/util";
|
||||
import { getNlsConfiguration } from "vs/server/src/node/nls";
|
||||
import { Protocol } from "vs/server/src/node/protocol";
|
||||
import { uriTransformerPath } from "vs/server/src/node/util";
|
||||
import { IExtHostReadyMessage } from "vs/workbench/services/extensions/common/extensionHostProtocol";
|
||||
|
||||
export abstract class Connection {
|
@ -6,7 +6,7 @@ import { mkdirp } from "vs/base/node/pfs";
|
||||
import * as vszip from "vs/base/node/zip";
|
||||
import * as nls from "vs/nls";
|
||||
import product from "vs/platform/product/node/product";
|
||||
import { localRequire } from "vs/server/src/util";
|
||||
import { localRequire } from "vs/server/src/node/util";
|
||||
|
||||
const tarStream = localRequire<typeof import("tar-stream")>("tar-stream/index");
|
||||
|
@ -57,14 +57,15 @@ import { combinedAppender, LogAppender, NullTelemetryService } from "vs/platform
|
||||
import { AppInsightsAppender } from "vs/platform/telemetry/node/appInsightsAppender";
|
||||
import { resolveCommonProperties } from "vs/platform/telemetry/node/commonProperties";
|
||||
import { UpdateChannel } from "vs/platform/update/node/updateIpc";
|
||||
import { ExtensionEnvironmentChannel, FileProviderChannel } from "vs/server/src/channel";
|
||||
import { Connection, ExtensionHostConnection, ManagementConnection } from "vs/server/src/connection";
|
||||
import { TelemetryClient } from "vs/server/src/insights";
|
||||
import { getLocaleFromConfig, getNlsConfiguration } from "vs/server/src/nls";
|
||||
import { Protocol } from "vs/server/src/protocol";
|
||||
import { TelemetryChannel } from "vs/server/src/telemetry";
|
||||
import { UpdateService } from "vs/server/src/update";
|
||||
import { AuthType, getMediaMime, getUriTransformer, localRequire, tmpdir } from "vs/server/src/util";
|
||||
import { ExtensionEnvironmentChannel, FileProviderChannel, NodeProxyService } from "vs/server/src/node/channel";
|
||||
import { Connection, ExtensionHostConnection, ManagementConnection } from "vs/server/src/node/connection";
|
||||
import { TelemetryClient } from "vs/server/src/node/insights";
|
||||
import { getLocaleFromConfig, getNlsConfiguration } from "vs/server/src/node/nls";
|
||||
import { NodeProxyChannel } from "vs/server/src/common/nodeProxy";
|
||||
import { Protocol } from "vs/server/src/node/protocol";
|
||||
import { TelemetryChannel } from "vs/server/src/common/telemetry";
|
||||
import { UpdateService } from "vs/server/src/node/update";
|
||||
import { AuthType, getMediaMime, getUriTransformer, localRequire, tmpdir } from "vs/server/src/node/util";
|
||||
import { RemoteExtensionLogFileName } from "vs/workbench/services/remote/common/remoteAgentService";
|
||||
import { IWorkbenchConstructionOptions } from "vs/workbench/workbench.web.api";
|
||||
|
||||
@ -125,7 +126,7 @@ export interface ServerOptions {
|
||||
|
||||
export abstract class Server {
|
||||
protected readonly server: http.Server | https.Server;
|
||||
protected rootPath = path.resolve(__dirname, "../../../..");
|
||||
protected rootPath = path.resolve(__dirname, "../../../../..");
|
||||
protected serverRoot = path.join(this.rootPath, "/out/vs/server/src");
|
||||
protected readonly allowedRequestPaths: string[] = [this.rootPath];
|
||||
private listenPromise: Promise<string> | undefined;
|
||||
@ -707,11 +708,13 @@ export class MainServer extends Server {
|
||||
const requestChannel = new RequestChannel(this.services.get(IRequestService) as IRequestService);
|
||||
const telemetryChannel = new TelemetryChannel(telemetryService);
|
||||
const updateChannel = new UpdateChannel(instantiationService.createInstance(UpdateService));
|
||||
const nodeProxyChannel = new NodeProxyChannel(instantiationService.createInstance(NodeProxyService));
|
||||
|
||||
this.ipc.registerChannel("extensions", extensionsChannel);
|
||||
this.ipc.registerChannel("remoteextensionsenvironment", extensionsEnvironmentChannel);
|
||||
this.ipc.registerChannel("request", requestChannel);
|
||||
this.ipc.registerChannel("telemetry", telemetryChannel);
|
||||
this.ipc.registerChannel("nodeProxy", nodeProxyChannel);
|
||||
this.ipc.registerChannel("update", updateChannel);
|
||||
this.ipc.registerChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME, fileChannel);
|
||||
resolve(new ErrorTelemetry(telemetryService));
|
@ -15,9 +15,9 @@ import pkg from "vs/platform/product/node/package";
|
||||
import { asJson, IRequestService } from "vs/platform/request/common/request";
|
||||
import { AvailableForDownload, State, StateType, UpdateType } from "vs/platform/update/common/update";
|
||||
import { AbstractUpdateService } from "vs/platform/update/electron-main/abstractUpdateService";
|
||||
import { ipcMain } from "vs/server/src/ipc";
|
||||
import { extract } from "vs/server/src/marketplace";
|
||||
import { tmpdir } from "vs/server/src/util";
|
||||
import { ipcMain } from "vs/server/src/node/ipc";
|
||||
import { extract } from "vs/server/src/node/marketplace";
|
||||
import { tmpdir } from "vs/server/src/node/util";
|
||||
import * as zlib from "zlib";
|
||||
|
||||
interface IUpdate {
|
@ -5,20 +5,20 @@ module.exports = (remoteAuthority) => {
|
||||
transformIncoming: (uri) => {
|
||||
switch (uri.scheme) {
|
||||
case "code-server": return { scheme: "file", path: uri.path };
|
||||
case "file": return { scheme: "code-server-local", path: uri.path };
|
||||
case "file": return { scheme: "code-server", path: uri.path };
|
||||
default: return uri;
|
||||
}
|
||||
},
|
||||
transformOutgoing: (uri) => {
|
||||
switch (uri.scheme) {
|
||||
case "code-server-local": return { scheme: "file", path: uri.path };
|
||||
case "code-server": return { scheme: "file", path: uri.path };
|
||||
case "file": return { scheme: "code-server", authority: remoteAuthority, path: uri.path };
|
||||
default: return uri;
|
||||
}
|
||||
},
|
||||
transformOutgoingScheme: (scheme) => {
|
||||
switch (scheme) {
|
||||
case "code-server-local": return "file";
|
||||
case "code-server": return "file";
|
||||
case "file": return "code-server";
|
||||
default: return scheme;
|
||||
}
|
@ -53,7 +53,7 @@ export const generateCertificate = async (): Promise<{ cert: string, certKey: st
|
||||
return paths;
|
||||
};
|
||||
|
||||
export const uriTransformerPath = getPathFromAmdModule(require, "vs/server/src/uriTransformer");
|
||||
export const uriTransformerPath = getPathFromAmdModule(require, "vs/server/src/node/uriTransformer");
|
||||
export const getUriTransformer = (remoteAuthority: string): URITransformer => {
|
||||
const rawURITransformerFactory = <any>require.__$__nodeRequire(uriTransformerPath);
|
||||
const rawURITransformer = <IRawURITransformer>rawURITransformerFactory(remoteAuthority);
|
||||
@ -135,5 +135,5 @@ export const buildAllowedMessage = (t: any): string => {
|
||||
* at the root for Node modules.
|
||||
*/
|
||||
export const localRequire = <T>(modulePath: string): T => {
|
||||
return require.__$__nodeRequire(path.resolve(__dirname, "../node_modules", modulePath));
|
||||
return require.__$__nodeRequire(path.resolve(__dirname, "../../node_modules", modulePath));
|
||||
};
|
Reference in New Issue
Block a user