deploy: current vibn theia state
Some checks failed
Playwright Tests / Playwright Tests (ubuntu-22.04, Node.js 22.x) (push) Has been cancelled
3PP License Check / 3PP License Check (11, 22.x, ubuntu-22.04) (push) Has been cancelled
Publish packages to NPM / Perform Publishing (push) Has been cancelled

Made-with: Cursor
This commit is contained in:
2026-02-27 12:01:08 -08:00
commit 8bb5110148
3782 changed files with 640947 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
/** @type {import('eslint').Linter.Config} */
module.exports = {
extends: [
'../../configs/build.eslintrc.json'
],
parserOptions: {
tsconfigRootDir: __dirname,
project: 'tsconfig.json'
},
rules: {
'no-null/no-null': 'off',
}
};

View File

@@ -0,0 +1,33 @@
<div align='center'>
<br />
<img src='https://raw.githubusercontent.com/eclipse-theia/theia/master/logo/theia.svg?sanitize=true' alt='theia-ext-logo' width='100px' />
<h2>ECLIPSE THEIA - HEADLESS PLUGIN-EXT EXTENSION</h2>
<hr />
</div>
## Description
The `@theia/plugin-ext-headless` extension contributes functionality for the backend-only "headless `plugin`" API.
The plugin extension host managed by this extension is scoped to the single Theia NodeJS instance.
This is unlike the plugin extension hosts managed by the [`@theia/plugin-ext` extension][plugin-ext] which are scoped on a per-frontend-connection basis.
[plugin-ext]: ../plugin-ext/README.md
## Implementation
The implementation is derived from the [`@theia/plugin-ext` extension][plugin-ext] for frontend-scoped plugins.
## License
- [Eclipse Public License 2.0](http://www.eclipse.org/legal/epl-2.0/)
- [一 (Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](https://projects.eclipse.org/license/secondary-gpl-2.0-cp)
## Trademark
"Theia" is a trademark of the Eclipse Foundation
<https://www.eclipse.org/theia>

View File

@@ -0,0 +1,56 @@
{
"name": "@theia/plugin-ext-headless",
"version": "1.68.0",
"description": "Theia - Headless (Backend-only) Plugin Extension",
"main": "lib/common/index.js",
"typings": "lib/common/index.d.ts",
"dependencies": {
"@theia/core": "1.68.0",
"@theia/plugin-ext": "1.68.0",
"@theia/terminal": "1.68.0",
"tslib": "^2.6.2"
},
"publishConfig": {
"access": "public"
},
"theiaExtensions": [
{
"backend": "lib/plugin-ext-headless-module",
"backendElectron": "lib/plugin-ext-headless-electron-module"
}
],
"keywords": [
"theia-extension"
],
"license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0",
"repository": {
"type": "git",
"url": "https://github.com/eclipse-theia/theia.git"
},
"bugs": {
"url": "https://github.com/eclipse-theia/theia/issues"
},
"homepage": "https://github.com/eclipse-theia/theia",
"files": [
"lib",
"src"
],
"scripts": {
"build": "theiaext build",
"clean": "theiaext clean",
"compile": "theiaext compile",
"lint": "theiaext lint",
"test": "theiaext test",
"watch": "theiaext watch"
},
"devDependencies": {
"@theia/ext-scripts": "1.68.0",
"@types/decompress": "^4.2.2",
"@types/escape-html": "^0.0.20",
"@types/lodash.clonedeep": "^4.5.3"
},
"nyc": {
"extends": "../../configs/nyc.json"
},
"gitHead": "21358137e41342742707f660b8e222f940a27652"
}

View File

@@ -0,0 +1,23 @@
// *****************************************************************************
// Copyright (C) 2024 EclipseSource and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
/**
* Service identifier for Inversify container modules that are used
* to configure the child Container in which scope the services supporting
* the Headless Plugin Host are isolated from connection-scoped frontend/backend
* plugin hosts and the rest of the Theia Node server.
*/
export const HeadlessPluginContainerModule = Symbol('HeadlessPluginContainerModule');

View File

@@ -0,0 +1,38 @@
// *****************************************************************************
// Copyright (C) 2024 EclipseSource and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
export * from '@theia/plugin-ext';
declare module '@theia/plugin-ext' {
/**
* Extension of the package manifest interface defined by the core plugin framework.
*/
interface PluginPackage {
/**
* Analogues of declarations offered by VS Code plugins, but for the headless instantiation.
*/
headless?: {
/** Activation events supported in headless mode, if any. */
activationEvents?: string[];
}
}
}
/**
* Name for a `string[]` injection binding contributing headless activation event names
* supported by the application.
*/
export const SupportedHeadlessActivationEvents = Symbol('SupportedHeadlessActivationEvents');

View File

@@ -0,0 +1,46 @@
// *****************************************************************************
// Copyright (C) 2024 EclipseSource and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { createProxyIdentifier } from '@theia/plugin-ext/lib/common/rpc-protocol';
import { AbstractPluginManagerExt, EnvInit } from '@theia/plugin-ext';
import { KeysToKeysToAnyValue } from '@theia/plugin-ext/lib/common/types';
import {
MAIN_RPC_CONTEXT, PLUGIN_RPC_CONTEXT
} from '@theia/plugin-ext/lib/common/plugin-api-rpc';
import { ExtPluginApi } from './plugin-ext-headless-api-contribution';
export const HEADLESSPLUGIN_RPC_CONTEXT = {
MESSAGE_REGISTRY_MAIN: PLUGIN_RPC_CONTEXT.MESSAGE_REGISTRY_MAIN,
ENV_MAIN: PLUGIN_RPC_CONTEXT.ENV_MAIN,
NOTIFICATION_MAIN: PLUGIN_RPC_CONTEXT.NOTIFICATION_MAIN,
LOCALIZATION_MAIN: PLUGIN_RPC_CONTEXT.LOCALIZATION_MAIN,
};
export const HEADLESSMAIN_RPC_CONTEXT = {
HOSTED_PLUGIN_MANAGER_EXT: createProxyIdentifier<HeadlessPluginManagerExt>('HeadlessPluginManagerExt'),
NOTIFICATION_EXT: MAIN_RPC_CONTEXT.NOTIFICATION_EXT,
};
export type HeadlessEnvInit = Pick<EnvInit, 'language' | 'shell' | 'appName' | 'appHost'>;
export interface HeadlessPluginManagerInitializeParams {
activationEvents: string[];
globalState: KeysToKeysToAnyValue;
env: HeadlessEnvInit;
extApi?: ExtPluginApi[];
}
export interface HeadlessPluginManagerExt extends AbstractPluginManagerExt<HeadlessPluginManagerInitializeParams> { }

View File

@@ -0,0 +1,23 @@
// *****************************************************************************
// Copyright (C) 2024 EclipseSource and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
export * from './headless-plugin-container';
export {
ExtPluginApi, ExtPluginHeadlessApi, ExtPluginApiProvider,
ExtPluginHeadlessApiProvider
} from './plugin-ext-headless-api-contribution';
export { PluginPackage, SupportedHeadlessActivationEvents } from './headless-plugin-protocol';
export * from './headless-plugin-rpc';

View File

@@ -0,0 +1,60 @@
// *****************************************************************************
// Copyright (C) 2024 EclipseSource and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { PluginManager } from '@theia/plugin-ext';
import { RPCProtocol } from '@theia/plugin-ext/lib/common/rpc-protocol';
export * from '@theia/plugin-ext';
declare module '@theia/plugin-ext' {
/**
* Plugin API extension description.
* This interface describes scripts for all three plugin runtimes: frontend (WebWorker), backend (NodeJs), and headless (NodeJs).
*/
interface ExtPluginApi extends ExtPluginHeadlessApi {
// Note that the frontendInitPath and backendInitPath properties are included by
// Typescript interface merge from the @theia/plugin-ext::ExtPluginApi interface.
}
}
/**
* Provider for headless extension API description.
*/
export interface ExtPluginHeadlessApiProvider {
/**
* Provide API description.
*/
provideApi(): ExtPluginHeadlessApi;
}
/**
* Headless Plugin API extension description.
* This interface describes a script for the headless (NodeJs) runtime outside of the scope of frontend connections.
*/
export interface ExtPluginHeadlessApi {
/**
* Path to the script which should be loaded to provide api, module should export `provideApi` function with
* [ExtPluginApiBackendInitializationFn](#ExtPluginApiBackendInitializationFn) signature
*/
headlessInitPath?: string;
}
/**
* Signature of the extension API initialization function for APIs contributed to headless plugins.
*/
export interface ExtPluginApiHeadlessInitializationFn {
(rpc: RPCProtocol, pluginManager: PluginManager): void;
}

View File

@@ -0,0 +1,22 @@
// *****************************************************************************
// Copyright (C) 2024 EclipseSource and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { interfaces } from '@theia/core/shared/inversify';
import { bindCommonHostedBackend } from '../node/plugin-ext-headless-hosted-module';
export function bindElectronBackend(bind: interfaces.Bind): void {
bindCommonHostedBackend(bind);
}

View File

@@ -0,0 +1,199 @@
// *****************************************************************************
// Copyright (C) 2024 EclipseSource and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// some code copied and modified from https://github.com/microsoft/vscode/blob/da5fb7d5b865aa522abc7e82c10b746834b98639/src/vs/workbench/api/node/extHostExtensionService.ts
import { generateUuid } from '@theia/core/lib/common/uuid';
import { injectable, inject, named } from '@theia/core/shared/inversify';
import { getPluginId, DeployedPlugin, HostedPluginServer, PluginDeployer } from '@theia/plugin-ext/lib/common/plugin-protocol';
import { setUpPluginApi } from '../../main/node/main-context';
import { RPCProtocol, RPCProtocolImpl } from '@theia/plugin-ext/lib/common/rpc-protocol';
import { ContributionProvider, Disposable, DisposableCollection, nls } from '@theia/core';
import { environment } from '@theia/core/shared/@theia/application-package/lib/environment';
import { IPCChannel } from '@theia/core/lib/node';
import { BackendApplicationConfigProvider } from '@theia/core/lib/node/backend-application-config-provider';
import { HostedPluginProcess } from '@theia/plugin-ext/lib/hosted/node/hosted-plugin-process';
import { IShellTerminalServer } from '@theia/terminal/lib/common/shell-terminal-protocol';
import { HeadlessPluginManagerExt, HEADLESSMAIN_RPC_CONTEXT } from '../../common/headless-plugin-rpc';
import { AbstractHostedPluginSupport, PluginContributions } from '@theia/plugin-ext/lib/hosted/common/hosted-plugin';
import { TheiaHeadlessPluginScanner } from './scanners/scanner-theia-headless';
import { SupportedHeadlessActivationEvents } from '../../common/headless-plugin-protocol';
import { PluginDeployerImpl } from '@theia/plugin-ext/lib/main/node/plugin-deployer-impl';
import URI from '@theia/core/lib/common/uri';
import * as fs from 'fs';
import * as asyncFs from 'fs/promises';
export type HeadlessPluginHost = string;
export function isHeadlessPlugin(plugin: DeployedPlugin): boolean {
return !!plugin.metadata.model.entryPoint.headless;
}
@injectable()
export class HeadlessHostedPluginSupport extends AbstractHostedPluginSupport<HeadlessPluginManagerExt, HostedPluginServer> {
@inject(HostedPluginProcess)
protected readonly pluginProcess: HostedPluginProcess;
@inject(IShellTerminalServer)
protected readonly shellTerminalServer: IShellTerminalServer;
@inject(TheiaHeadlessPluginScanner)
protected readonly scanner: TheiaHeadlessPluginScanner;
@inject(PluginDeployer)
protected readonly pluginDeployer: PluginDeployerImpl;
@inject(ContributionProvider)
@named(SupportedHeadlessActivationEvents)
protected readonly supportedActivationEventsContributions: ContributionProvider<string[]>;
constructor() {
super(generateUuid());
}
shutDown(): void {
this.pluginProcess.terminatePluginServer();
}
protected createTheiaReadyPromise(): Promise<unknown> {
return Promise.all([this.envServer.getVariables()]);
}
// Only load headless plugins
protected acceptPlugin(plugin: DeployedPlugin): boolean | DeployedPlugin {
if (!isHeadlessPlugin(plugin)) {
return false;
}
if (plugin.metadata.model.engine.type === this.scanner.apiType) {
// Easy case: take it as it is
return true;
}
// Adapt it for headless
return this.scanner.adaptForHeadless(plugin);
}
protected handleContributions(_plugin: DeployedPlugin): Disposable {
// We have no contribution points, yet, for headless plugins
return Disposable.NULL;
}
protected override async beforeSyncPlugins(toDisconnect: DisposableCollection): Promise<void> {
await super.beforeSyncPlugins(toDisconnect);
// Plugin deployment is asynchronous, so wait until that's finished.
return new Promise<void>((resolve, reject) => {
this.pluginDeployer.onDidDeploy(resolve);
toDisconnect.push(Disposable.create(reject));
});
}
protected async obtainManager(host: string, hostContributions: PluginContributions[], toDisconnect: DisposableCollection): Promise<HeadlessPluginManagerExt | undefined> {
let manager = this.managers.get(host);
if (!manager) {
const pluginId = getPluginId(hostContributions[0].plugin.metadata.model);
const rpc = this.initRpc(host, pluginId);
toDisconnect.push(rpc);
manager = rpc.getProxy(HEADLESSMAIN_RPC_CONTEXT.HOSTED_PLUGIN_MANAGER_EXT);
this.managers.set(host, manager);
toDisconnect.push(Disposable.create(() => this.managers.delete(host)));
const [extApi, globalState] = await Promise.all([
this.server.getExtPluginAPI(),
this.pluginServer.getAllStorageValues(undefined)
]);
if (toDisconnect.disposed) {
return undefined;
}
const activationEvents = this.supportedActivationEventsContributions.getContributions().flatMap(array => array);
const shell = await this.shellTerminalServer.getDefaultShell();
const isElectron = environment.electron.is();
await manager.$init({
activationEvents,
globalState,
env: {
language: nls.locale || nls.defaultLocale,
shell,
appName: BackendApplicationConfigProvider.get().applicationName,
appHost: isElectron ? 'desktop' : 'web' // TODO: 'web' could be the embedder's name, e.g. 'github.dev'
},
extApi
});
if (toDisconnect.disposed) {
return undefined;
}
}
return manager;
}
protected initRpc(host: HeadlessPluginHost, pluginId: string): RPCProtocol {
const rpc = this.createServerRpc(host);
this.container.bind(RPCProtocol).toConstantValue(rpc);
setUpPluginApi(rpc, this.container);
this.mainPluginApiProviders.getContributions().forEach(p => p.initialize(rpc, this.container));
return rpc;
}
protected createServerRpc(pluginHostId: string): RPCProtocol {
const channel = new IPCChannel(this.pluginProcess['childProcess']);
return new RPCProtocolImpl(channel);
}
protected async getStoragePath(): Promise<string | undefined> {
// Headless plugins are associated with the main Node process, so
// their storage is the global storage.
return this.getHostGlobalStoragePath();
}
protected async getHostGlobalStoragePath(): Promise<string> {
const configDirUri = await this.envServer.getConfigDirUri();
const globalStorageFolderUri = new URI(configDirUri).resolve('globalStorage');
const globalStorageFolderUrl = new URL(globalStorageFolderUri.toString());
let stat: fs.Stats | undefined;
try {
stat = await asyncFs.stat(globalStorageFolderUrl);
} catch (_) {
// OK, no such directory
}
if (stat && !stat.isDirectory()) {
throw new Error(`Global storage folder is not a directory: ${globalStorageFolderUri}`);
}
// Make sure that folder by the path exists
if (!stat) {
await asyncFs.mkdir(globalStorageFolderUrl, { recursive: true });
}
const globalStorageFolderFsPath = await asyncFs.realpath(globalStorageFolderUrl);
if (!globalStorageFolderFsPath) {
throw new Error(`Could not resolve the FS path for URI: ${globalStorageFolderUri}`);
}
return globalStorageFolderFsPath;
}
}

View File

@@ -0,0 +1,25 @@
// *****************************************************************************
// Copyright (C) 2024 EclipseSource and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { injectable } from '@theia/core/shared/inversify';
import { HostedPluginServerImpl } from '@theia/plugin-ext/lib/hosted/node/plugin-service';
@injectable()
export class HeadlessHostedPluginServerImpl extends HostedPluginServerImpl {
protected override getServerName(): string {
return 'headless-hosted-plugin';
}
}

View File

@@ -0,0 +1,76 @@
// *****************************************************************************
// Copyright (C) 2024 EclipseSource and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import * as path from 'path';
import { bindContributionProvider } from '@theia/core/lib/common/contribution-provider';
import { BackendApplicationContribution } from '@theia/core/lib/node';
import { ContainerModule, interfaces } from '@theia/core/shared/inversify';
import { ExtPluginApiProvider, HostedPluginServer, PluginHostEnvironmentVariable, PluginScanner } from '@theia/plugin-ext';
import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/node/hosted-plugin';
import { HostedPluginProcess, HostedPluginProcessConfiguration } from '@theia/plugin-ext/lib/hosted/node/hosted-plugin-process';
import { BackendPluginHostableFilter } from '@theia/plugin-ext/lib/hosted/node/plugin-service';
import { MaybePromise } from '@theia/core';
import { HeadlessPluginContainerModule } from '../../common/headless-plugin-container';
import { HeadlessHostedPluginSupport, isHeadlessPlugin } from './headless-hosted-plugin';
import { TheiaHeadlessPluginScanner } from './scanners/scanner-theia-headless';
import { SupportedHeadlessActivationEvents } from '../../common/headless-plugin-protocol';
import { HeadlessHostedPluginServerImpl } from './headless-plugin-service';
export function bindCommonHostedBackend(bind: interfaces.Bind): void {
bind(HostedPluginProcess).toSelf().inSingletonScope();
bind(HostedPluginSupport).toSelf().inSingletonScope();
bindContributionProvider(bind, Symbol.for(ExtPluginApiProvider));
bindContributionProvider(bind, PluginHostEnvironmentVariable);
bindContributionProvider(bind, SupportedHeadlessActivationEvents);
bind(HeadlessHostedPluginServerImpl).toSelf().inSingletonScope();
bind(HostedPluginServer).toService(HeadlessHostedPluginServerImpl);
bind(HeadlessHostedPluginSupport).toSelf().inSingletonScope();
bind(BackendPluginHostableFilter).toConstantValue(isHeadlessPlugin);
bind(HostedPluginProcessConfiguration).toConstantValue({
path: path.join(__dirname, 'plugin-host-headless'),
});
}
export function bindHeadlessHosted(bind: interfaces.Bind): void {
bind(TheiaHeadlessPluginScanner).toSelf().inSingletonScope();
bind(PluginScanner).toService(TheiaHeadlessPluginScanner);
bind(SupportedHeadlessActivationEvents).toConstantValue(['*', 'onStartupFinished']);
bind(BackendApplicationContribution).toDynamicValue(({container}) => {
let hostedPluginSupport: HeadlessHostedPluginSupport | undefined;
return {
onStart(): MaybePromise<void> {
// Create a child container to isolate the Headless Plugin hosting stack
// from all connection-scoped frontend/backend plugin hosts and
// also to avoid leaking it into the global container scope
const headlessPluginsContainer = container.createChild();
const modules = container.getAll<ContainerModule>(HeadlessPluginContainerModule);
headlessPluginsContainer.load(...modules);
hostedPluginSupport = headlessPluginsContainer.get(HeadlessHostedPluginSupport);
hostedPluginSupport.onStart(headlessPluginsContainer);
},
onStop(): void {
hostedPluginSupport?.shutDown();
}
};
});
}

View File

@@ -0,0 +1,77 @@
// *****************************************************************************
// Copyright (C) 2024 EclipseSource and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import '@theia/core/shared/reflect-metadata';
import { ContainerModule } from '@theia/core/shared/inversify';
import { RPCProtocol, RPCProtocolImpl } from '@theia/plugin-ext/lib/common/rpc-protocol';
import { AbstractPluginHostRPC, PluginContainerModuleLoader } from '@theia/plugin-ext/lib/hosted/node/plugin-host-rpc';
import { AbstractPluginManagerExtImpl, MinimalTerminalServiceExt } from '@theia/plugin-ext/lib/plugin/plugin-manager';
import { HeadlessPluginHostRPC } from './plugin-host-headless-rpc';
import { HeadlessPluginManagerExtImpl } from '../../plugin/headless-plugin-manager';
import { IPCChannel } from '@theia/core/lib/node';
import { InternalPluginContainerModule } from '@theia/plugin-ext/lib/plugin/node/plugin-container-module';
import { EnvExtImpl } from '@theia/plugin-ext/lib/plugin/env';
import { EnvNodeExtImpl } from '@theia/plugin-ext/lib/plugin/node/env-node-ext';
import { LocalizationExt } from '@theia/plugin-ext';
import { LocalizationExtImpl } from '@theia/plugin-ext/lib/plugin/localization-ext';
import { InternalStorageExt } from '@theia/plugin-ext/lib/plugin/plugin-storage';
import { InternalSecretsExt } from '@theia/plugin-ext/lib/plugin/secrets-ext';
import { EnvironmentVariableCollectionImpl } from '@theia/plugin-ext/lib/plugin/terminal-ext';
import { Disposable } from '@theia/core';
export default new ContainerModule(bind => {
const channel = new IPCChannel();
bind(RPCProtocol).toConstantValue(new RPCProtocolImpl(channel));
bind(PluginContainerModuleLoader).toDynamicValue(({ container }) =>
(module: ContainerModule) => {
container.load(module);
const internalModule = module as InternalPluginContainerModule;
const pluginApiCache = internalModule.initializeApi?.(container);
return pluginApiCache;
}).inSingletonScope();
bind(AbstractPluginHostRPC).toService(HeadlessPluginHostRPC);
bind(HeadlessPluginHostRPC).toSelf().inSingletonScope();
bind(AbstractPluginManagerExtImpl).toService(HeadlessPluginManagerExtImpl);
bind(HeadlessPluginManagerExtImpl).toSelf().inSingletonScope();
bind(EnvExtImpl).to(EnvNodeExtImpl).inSingletonScope();
bind(LocalizationExt).to(LocalizationExtImpl).inSingletonScope();
const dummySecrets: InternalSecretsExt = {
get: () => Promise.resolve(undefined),
store: () => Promise.resolve(undefined),
delete: () => Promise.resolve(undefined),
$onDidChangePassword: () => Promise.resolve(),
onDidChangePassword: () => Disposable.NULL,
keys: () => Promise.resolve([]),
};
const dummyStorage: InternalStorageExt = {
init: () => undefined,
setPerPluginData: () => Promise.resolve(false),
getPerPluginData: () => ({}),
storageDataChangedEvent: () => Disposable.NULL,
$updatePluginsWorkspaceData: () => undefined
};
const dummyTerminalService: MinimalTerminalServiceExt = {
$initEnvironmentVariableCollections: () => undefined,
$setShell: () => undefined,
getEnvironmentVariableCollection: () => new EnvironmentVariableCollectionImpl(false),
};
bind(InternalSecretsExt).toConstantValue(dummySecrets);
bind(InternalStorageExt).toConstantValue(dummyStorage);
bind(MinimalTerminalServiceExt).toConstantValue(dummyTerminalService);
});

View File

@@ -0,0 +1,80 @@
// *****************************************************************************
// Copyright (C) 2024 EclipseSource and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { dynamicRequire } from '@theia/core/lib/node/dynamic-require';
import { ContainerModule, injectable, inject } from '@theia/core/shared/inversify';
import { EnvExtImpl } from '@theia/plugin-ext/lib/plugin/env';
import { LocalizationExt } from '@theia/plugin-ext';
import { LocalizationExtImpl } from '@theia/plugin-ext/lib/plugin/localization-ext';
import { HEADLESSMAIN_RPC_CONTEXT } from '../../common/headless-plugin-rpc';
import { HeadlessPluginManagerExtImpl } from '../../plugin/headless-plugin-manager';
import { AbstractPluginHostRPC, ExtInterfaces } from '@theia/plugin-ext/lib/hosted/node/plugin-host-rpc';
import { PluginModel } from '@theia/plugin-ext/lib/common/plugin-protocol';
import { ExtPluginApi, ExtPluginApiHeadlessInitializationFn } from '../../common/plugin-ext-headless-api-contribution';
type HeadlessExtInterfaces = Pick<ExtInterfaces, 'envExt'|'localizationExt'>;
/**
* The RPC handler for headless plugins.
*/
@injectable()
export class HeadlessPluginHostRPC extends AbstractPluginHostRPC<HeadlessPluginManagerExtImpl, null, HeadlessExtInterfaces> {
@inject(EnvExtImpl)
protected readonly envExt: EnvExtImpl;
@inject(LocalizationExt)
protected readonly localizationExt: LocalizationExtImpl;
constructor() {
super('HEADLESS_PLUGIN_HOST', undefined,
{
$pluginManager: HEADLESSMAIN_RPC_CONTEXT.HOSTED_PLUGIN_MANAGER_EXT,
}
);
}
protected createExtInterfaces(): HeadlessExtInterfaces {
return {
envExt: this.envExt,
localizationExt: this.localizationExt
};
}
protected createAPIFactory(_extInterfaces: HeadlessExtInterfaces): null {
// As yet there is no default API namespace for backend plugins to access the Theia framework
return null;
}
protected override getBackendPluginPath(pluginModel: PluginModel): string | undefined {
return pluginModel.entryPoint.headless;
}
protected initExtApi(extApi: ExtPluginApi): void {
interface PluginExports {
containerModule?: ContainerModule;
provideApi?: ExtPluginApiHeadlessInitializationFn;
}
if (extApi.headlessInitPath) {
const { containerModule, provideApi } = dynamicRequire<PluginExports>(extApi.headlessInitPath);
if (containerModule) {
this.loadContainerModule(containerModule);
}
if (provideApi) {
provideApi(this.rpc, this.pluginManager);
}
}
}
}

View File

@@ -0,0 +1,111 @@
// *****************************************************************************
// Copyright (C) 2018 Red Hat, Inc. and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import '@theia/core/shared/reflect-metadata';
import { Container } from '@theia/core/shared/inversify';
import { ConnectionClosedError, RPCProtocol } from '@theia/plugin-ext/lib/common/rpc-protocol';
import { ProcessTerminatedMessage, ProcessTerminateMessage } from '@theia/plugin-ext/lib/hosted/node/hosted-plugin-protocol';
import { HeadlessPluginHostRPC } from './plugin-host-headless-rpc';
import pluginHostModule from './plugin-host-headless-module';
const banner = `HEADLESS_PLUGIN_HOST(${process.pid}):`;
console.log(banner, 'Starting instance');
// override exit() function, to do not allow plugin kill this node
process.exit = function (code?: number): void {
const err = new Error('A plugin called process.exit() but it was blocked.');
console.warn(banner, err.stack);
} as (code?: number) => never;
// same for 'crash'(works only in electron)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const proc = process as any;
if (proc.crash) {
proc.crash = function (): void {
const err = new Error('A plugin called process.crash() but it was blocked.');
console.warn(banner, err.stack);
};
}
process.on('uncaughtException', (err: Error) => {
console.error(banner, err);
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const unhandledPromises: Promise<any>[] = [];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
process.on('unhandledRejection', (reason: any, promise: Promise<any>) => {
unhandledPromises.push(promise);
setTimeout(() => {
const index = unhandledPromises.indexOf(promise);
if (index >= 0) {
promise.catch(err => {
unhandledPromises.splice(index, 1);
if (terminating && (ConnectionClosedError.is(err) || ConnectionClosedError.is(reason))) {
// during termination it is expected that pending rpc request are rejected
return;
}
console.error(banner, `Promise rejection not handled in one second: ${err} , reason: ${reason}`);
if (err && err.stack) {
console.error(banner, `With stack trace: ${err.stack}`);
}
});
}
}, 1000);
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
process.on('rejectionHandled', (promise: Promise<any>) => {
const index = unhandledPromises.indexOf(promise);
if (index >= 0) {
unhandledPromises.splice(index, 1);
}
});
let terminating = false;
const container = new Container();
container.load(pluginHostModule);
const rpc: RPCProtocol = container.get(RPCProtocol);
const pluginHostRPC = container.get(HeadlessPluginHostRPC);
process.on('message', async (message: string) => {
if (terminating) {
return;
}
try {
const msg = JSON.parse(message);
if (ProcessTerminateMessage.is(msg)) {
terminating = true;
if (msg.stopTimeout) {
await Promise.race([
pluginHostRPC.terminate(),
new Promise(resolve => setTimeout(resolve, msg.stopTimeout))
]);
} else {
await pluginHostRPC.terminate();
}
rpc.dispose();
if (process.send) {
process.send(JSON.stringify({ type: ProcessTerminatedMessage.TYPE }));
}
}
} catch (e) {
console.error(banner, e);
}
});

View File

@@ -0,0 +1,85 @@
// *****************************************************************************
// Copyright (C) 2024 EclipseSource and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
/* eslint-disable @theia/localization-check */
import { injectable } from '@theia/core/shared/inversify';
import { DeployedPlugin, PluginPackage, PluginEntryPoint } from '@theia/plugin-ext';
import { AbstractPluginScanner } from '@theia/plugin-ext/lib/hosted/node/scanners/scanner-theia';
import { deepClone } from '@theia/core/lib/common/objects';
@injectable()
export class TheiaHeadlessPluginScanner extends AbstractPluginScanner {
constructor() {
super('theiaHeadlessPlugin');
}
protected getEntryPoint(plugin: PluginPackage): PluginEntryPoint {
if (plugin?.theiaPlugin?.headless) {
return {
headless: plugin.theiaPlugin.headless
};
};
return {
headless: plugin.main
};
}
/**
* Adapt the given `plugin`'s metadata for headless deployment, where it does not
* already natively specify its headless deployment, such as is the case for plugins
* declaring the `"vscode"` or `"theiaPlugin"` engine. This consists of cloning the
* relevant properties of its deployment metadata and modifying them as required,
* including but not limited to:
*
* - renaming the `lifecycle` start and stop functions as 'activate' and 'deactivate'
* following the VS Code naming convention (in case the `plugin` is a Theia-style
* plugin that uses 'start' and 'stop')
* - deleting inapplicable information such as frontend and backend init script paths
* - filtering/rewriting contributions and/or activation events
*
* The cloning is necessary to retain the original information for the non-headless
* deployments that the plugin also supports.
*/
adaptForHeadless(plugin: DeployedPlugin): DeployedPlugin {
return {
type: plugin.type,
metadata: this.adaptMetadataForHeadless(plugin),
contributes: this.adaptContributesForHeadless(plugin)
};
}
protected adaptMetadataForHeadless(plugin: DeployedPlugin): DeployedPlugin['metadata'] {
const result = deepClone(plugin.metadata);
const lifecycle = result.lifecycle;
delete lifecycle.frontendInitPath;
delete lifecycle.backendInitPath;
// Same as in VS Code
lifecycle.startMethod = 'activate';
lifecycle.stopMethod = 'deactivate';
return result;
}
protected adaptContributesForHeadless(plugin: DeployedPlugin): DeployedPlugin['contributes'] {
// We don't yet support and contribution points in headless plugins
return undefined;
}
}

View File

@@ -0,0 +1,17 @@
// *****************************************************************************
// Copyright (C) 2024 EclipseSource and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
export * from './common';

View File

@@ -0,0 +1,35 @@
// *****************************************************************************
// Copyright (C) 2024 EclipseSource and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { injectable } from '@theia/core/shared/inversify';
import { PluginDeployerDirectoryHandlerContext, PluginDeployerEntryType, PluginPackage } from '@theia/plugin-ext';
import { AbstractPluginDirectoryHandler } from '@theia/plugin-ext/lib/main/node/handlers/plugin-theia-directory-handler';
@injectable()
export class PluginTheiaHeadlessDirectoryHandler extends AbstractPluginDirectoryHandler {
protected acceptManifest(plugin: PluginPackage): boolean {
return plugin?.engines?.theiaPlugin === undefined && 'theiaHeadlessPlugin' in plugin.engines;
}
async handle(context: PluginDeployerDirectoryHandlerContext): Promise<void> {
await this.copyDirectory(context);
const types: PluginDeployerEntryType[] = [PluginDeployerEntryType.HEADLESS];
context.pluginEntry().accept(...types);
}
}

View File

@@ -0,0 +1,44 @@
// *****************************************************************************
// Copyright (C) 2024 EclipseSource and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { injectable } from '@theia/core/shared/inversify';
import {
CancellationToken,
ProgressClient, ProgressMessage, ProgressUpdate
} from '@theia/core';
/**
* A simple progress client for headless plugins that just writes debug messages to the console
* because there is no one connected frontend to which it is appropriate to send the messages.
*/
@injectable()
export class HeadlessProgressClient implements ProgressClient {
async showProgress(_progressId: string, message: ProgressMessage, cancellationToken: CancellationToken): Promise<string | undefined> {
if (cancellationToken.isCancellationRequested) {
return ProgressMessage.Cancel;
}
console.debug(message.text);
}
async reportProgress(_progressId: string, update: ProgressUpdate, message: ProgressMessage, cancellationToken: CancellationToken): Promise<void> {
if (cancellationToken.isCancellationRequested) {
return;
}
const progress = update.work && update.work.total ? `[${100 * Math.min(update.work.done, update.work.total) / update.work.total}%]` : '';
const text = `${progress} ${update.message ?? 'completed ...'}`;
console.debug(text);
}
}

View File

@@ -0,0 +1,35 @@
// *****************************************************************************
// Copyright (C) 2024 EclipseSource and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { interfaces } from '@theia/core/shared/inversify';
import { RPCProtocol } from '@theia/plugin-ext/lib/common/rpc-protocol';
import { EnvMainImpl } from '@theia/plugin-ext/lib/main/common/env-main';
import { BasicMessageRegistryMainImpl } from '@theia/plugin-ext/lib/main/common/basic-message-registry-main';
import { BasicNotificationMainImpl } from '@theia/plugin-ext/lib/main/common/basic-notification-main';
import { HEADLESSMAIN_RPC_CONTEXT, HEADLESSPLUGIN_RPC_CONTEXT } from '../../common/headless-plugin-rpc';
// This sets up only the minimal plugin API required by the plugin manager to report
// messages and notifications to the main side and to initialize plugins.
export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container): void {
const envMain = new EnvMainImpl(rpc, container);
rpc.set(HEADLESSPLUGIN_RPC_CONTEXT.ENV_MAIN, envMain);
const messageRegistryMain = new BasicMessageRegistryMainImpl(container);
rpc.set(HEADLESSPLUGIN_RPC_CONTEXT.MESSAGE_REGISTRY_MAIN, messageRegistryMain);
const notificationMain = new BasicNotificationMainImpl(rpc, container, HEADLESSMAIN_RPC_CONTEXT.NOTIFICATION_EXT);
rpc.set(HEADLESSPLUGIN_RPC_CONTEXT.NOTIFICATION_MAIN, notificationMain);
}

View File

@@ -0,0 +1,42 @@
// *****************************************************************************
// Copyright (C) 2024 EclipseSource and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { interfaces } from '@theia/core/shared/inversify';
import {
MessageClient, MessageService,
ProgressClient, ProgressService,
bindContributionProvider
} from '@theia/core';
import { MainPluginApiProvider, PluginDeployerDirectoryHandler } from '@theia/plugin-ext';
import { PluginTheiaHeadlessDirectoryHandler } from './handlers/plugin-theia-headless-directory-handler';
import { HeadlessProgressClient } from './headless-progress-client';
export function bindHeadlessMain(bind: interfaces.Bind): void {
bind(PluginDeployerDirectoryHandler).to(PluginTheiaHeadlessDirectoryHandler).inSingletonScope();
}
export function bindBackendMain(bind: interfaces.Bind, unbind: interfaces.Unbind, isBound: interfaces.IsBound, rebind: interfaces.Rebind): void {
bindContributionProvider(bind, MainPluginApiProvider);
//
// Main API dependencies
//
bind(MessageService).toSelf().inSingletonScope();
bind(MessageClient).toSelf().inSingletonScope(); // Just logs to console
bind(ProgressService).toSelf().inSingletonScope();
bind(ProgressClient).to(HeadlessProgressClient).inSingletonScope();
}

View File

@@ -0,0 +1,25 @@
// *****************************************************************************
// Copyright (C) 2024 EclipseSource and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
/*
* This is a placeholder for tests that the extension package should implement
* but as yet does not.
* Please delete this file when a real test is implemented.
*/
describe('plugin-ext-headless package', () => {
it('placeholder to enable mocha', () => true);
});

View File

@@ -0,0 +1,32 @@
// *****************************************************************************
// Copyright (C) 2024 EclipseSource and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { ContainerModule } from '@theia/core/shared/inversify';
import { HeadlessPluginContainerModule } from './common/headless-plugin-container';
import { bindElectronBackend } from './hosted/node-electron/plugin-ext-headless-hosted-electron-module';
import { bindHeadlessMain, bindBackendMain } from './main/node/plugin-ext-headless-main-module';
import { bindHeadlessHosted } from './hosted/node/plugin-ext-headless-hosted-module';
const backendElectronModule = new ContainerModule((bind, unbind, isBound, rebind) => {
bindBackendMain(bind, unbind, isBound, rebind);
bindElectronBackend(bind);
});
export default new ContainerModule(bind => {
bind(HeadlessPluginContainerModule).toConstantValue(backendElectronModule);
bindHeadlessMain(bind);
bindHeadlessHosted(bind);
});

View File

@@ -0,0 +1,31 @@
// *****************************************************************************
// Copyright (C) 2024 EclipseSource and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { ContainerModule } from '@theia/core/shared/inversify';
import { HeadlessPluginContainerModule } from './common/headless-plugin-container';
import { bindHeadlessHosted, bindCommonHostedBackend } from './hosted/node/plugin-ext-headless-hosted-module';
import { bindHeadlessMain, bindBackendMain } from './main/node/plugin-ext-headless-main-module';
const backendModule = new ContainerModule((bind, unbind, isBound, rebind) => {
bindBackendMain(bind, unbind, isBound, rebind);
bindCommonHostedBackend(bind);
});
export default new ContainerModule(bind => {
bind(HeadlessPluginContainerModule).toConstantValue(backendModule);
bindHeadlessMain(bind);
bindHeadlessHosted(bind);
});

View File

@@ -0,0 +1,50 @@
// *****************************************************************************
// Copyright (C) 2024 EclipseSource and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { injectable } from '@theia/core/shared/inversify';
import { AbstractPluginManagerExtImpl } from '@theia/plugin-ext/lib/plugin/plugin-manager';
import { HeadlessPluginManagerExt, HeadlessPluginManagerInitializeParams } from '../common/headless-plugin-rpc';
import { Plugin } from '@theia/plugin-ext';
@injectable()
export class HeadlessPluginManagerExtImpl extends AbstractPluginManagerExtImpl<HeadlessPluginManagerInitializeParams> implements HeadlessPluginManagerExt {
private readonly supportedActivationEvents = new Set<string>();
async $init(params: HeadlessPluginManagerInitializeParams): Promise<void> {
params.activationEvents?.forEach(event => this.supportedActivationEvents.add(event));
this.storage.init(params.globalState, {});
this.envExt.setLanguage(params.env.language);
this.envExt.setApplicationName(params.env.appName);
this.envExt.setAppHost(params.env.appHost);
if (params.extApi) {
this.host.initExtApi(params.extApi);
}
}
protected override getActivationEvents(plugin: Plugin): string[] | undefined {
const result = plugin.rawModel?.headless?.activationEvents;
return Array.isArray(result) ? result : undefined;
}
protected isSupportedActivationEvent(activationEvent: string): boolean {
return this.supportedActivationEvents.has(activationEvent.split(':')[0]);
}
}

View File

@@ -0,0 +1,27 @@
{
"extends": "../../configs/base.tsconfig",
"compilerOptions": {
"composite": true,
"rootDir": "src",
"outDir": "lib",
"lib": [
"es6",
"dom",
"webworker"
]
},
"include": [
"src"
],
"references": [
{
"path": "../core"
},
{
"path": "../plugin-ext"
},
{
"path": "../terminal"
}
]
}