@theia/plugin-ext 1.27.0-next.39 → 1.27.0-next.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/common/plugin-identifiers.d.ts +41 -0
- package/lib/common/plugin-identifiers.d.ts.map +1 -0
- package/lib/common/plugin-identifiers.js +81 -0
- package/lib/common/plugin-identifiers.js.map +1 -0
- package/lib/common/plugin-protocol.d.ts +28 -7
- package/lib/common/plugin-protocol.d.ts.map +1 -1
- package/lib/common/plugin-protocol.js +3 -1
- package/lib/common/plugin-protocol.js.map +1 -1
- package/lib/hosted/browser/hosted-plugin.d.ts +2 -2
- package/lib/hosted/browser/hosted-plugin.d.ts.map +1 -1
- package/lib/hosted/browser/hosted-plugin.js +20 -13
- package/lib/hosted/browser/hosted-plugin.js.map +1 -1
- package/lib/hosted/browser/worker/plugin-manifest-loader.d.ts.map +1 -1
- package/lib/hosted/browser/worker/plugin-manifest-loader.js +4 -1
- package/lib/hosted/browser/worker/plugin-manifest-loader.js.map +1 -1
- package/lib/hosted/node/hosted-plugin-deployer-handler.d.ts +10 -5
- package/lib/hosted/node/hosted-plugin-deployer-handler.d.ts.map +1 -1
- package/lib/hosted/node/hosted-plugin-deployer-handler.js +54 -12
- package/lib/hosted/node/hosted-plugin-deployer-handler.js.map +1 -1
- package/lib/hosted/node/hosted-plugin-process.d.ts +2 -2
- package/lib/hosted/node/hosted-plugin-process.d.ts.map +1 -1
- package/lib/hosted/node/hosted-plugin-process.js.map +1 -1
- package/lib/hosted/node/hosted-plugin.d.ts +2 -2
- package/lib/hosted/node/hosted-plugin.d.ts.map +1 -1
- package/lib/hosted/node/hosted-plugin.js.map +1 -1
- package/lib/hosted/node/metadata-scanner.d.ts +3 -2
- package/lib/hosted/node/metadata-scanner.d.ts.map +1 -1
- package/lib/hosted/node/metadata-scanner.js +8 -3
- package/lib/hosted/node/metadata-scanner.js.map +1 -1
- package/lib/hosted/node/plugin-manifest-loader.d.ts.map +1 -1
- package/lib/hosted/node/plugin-manifest-loader.js +3 -0
- package/lib/hosted/node/plugin-manifest-loader.js.map +1 -1
- package/lib/hosted/node/plugin-service.d.ts +18 -4
- package/lib/hosted/node/plugin-service.d.ts.map +1 -1
- package/lib/hosted/node/plugin-service.js +73 -18
- package/lib/hosted/node/plugin-service.js.map +1 -1
- package/lib/hosted/node/scanners/scanner-theia.d.ts.map +1 -1
- package/lib/hosted/node/scanners/scanner-theia.js +4 -2
- package/lib/hosted/node/scanners/scanner-theia.js.map +1 -1
- package/lib/main/node/handlers/plugin-theia-directory-handler.d.ts +6 -1
- package/lib/main/node/handlers/plugin-theia-directory-handler.d.ts.map +1 -1
- package/lib/main/node/handlers/plugin-theia-directory-handler.js +61 -20
- package/lib/main/node/handlers/plugin-theia-directory-handler.js.map +1 -1
- package/lib/main/node/handlers/plugin-theia-file-handler.d.ts +4 -0
- package/lib/main/node/handlers/plugin-theia-file-handler.d.ts.map +1 -1
- package/lib/main/node/handlers/plugin-theia-file-handler.js +25 -4
- package/lib/main/node/handlers/plugin-theia-file-handler.js.map +1 -1
- package/lib/main/node/plugin-cli-contribution.d.ts +3 -0
- package/lib/main/node/plugin-cli-contribution.d.ts.map +1 -1
- package/lib/main/node/plugin-cli-contribution.js +13 -0
- package/lib/main/node/plugin-cli-contribution.js.map +1 -1
- package/lib/main/node/plugin-deployer-directory-handler-context-impl.d.ts +1 -0
- package/lib/main/node/plugin-deployer-directory-handler-context-impl.d.ts.map +1 -1
- package/lib/main/node/plugin-deployer-directory-handler-context-impl.js +17 -0
- package/lib/main/node/plugin-deployer-directory-handler-context-impl.js.map +1 -1
- package/lib/main/node/plugin-deployer-file-handler-context-impl.d.ts.map +1 -1
- package/lib/main/node/plugin-deployer-file-handler-context-impl.js +0 -1
- package/lib/main/node/plugin-deployer-file-handler-context-impl.js.map +1 -1
- package/lib/main/node/plugin-deployer-impl.d.ts +5 -2
- package/lib/main/node/plugin-deployer-impl.d.ts.map +1 -1
- package/lib/main/node/plugin-deployer-impl.js +72 -28
- package/lib/main/node/plugin-deployer-impl.js.map +1 -1
- package/lib/main/node/plugin-ext-backend-module.d.ts.map +1 -1
- package/lib/main/node/plugin-ext-backend-module.js +2 -0
- package/lib/main/node/plugin-ext-backend-module.js.map +1 -1
- package/lib/main/node/plugin-server-handler.d.ts +3 -2
- package/lib/main/node/plugin-server-handler.d.ts.map +1 -1
- package/lib/main/node/plugin-server-handler.js +3 -0
- package/lib/main/node/plugin-server-handler.js.map +1 -1
- package/lib/main/node/plugin-uninstallation-manager.d.ts +12 -0
- package/lib/main/node/plugin-uninstallation-manager.d.ts.map +1 -0
- package/lib/main/node/plugin-uninstallation-manager.js +66 -0
- package/lib/main/node/plugin-uninstallation-manager.js.map +1 -0
- package/package.json +25 -24
- package/src/common/plugin-identifiers.ts +84 -0
- package/src/common/plugin-protocol.ts +32 -8
- package/src/hosted/browser/hosted-plugin.ts +22 -16
- package/src/hosted/browser/worker/plugin-manifest-loader.ts +4 -2
- package/src/hosted/node/hosted-plugin-deployer-handler.ts +64 -20
- package/src/hosted/node/hosted-plugin-process.ts +2 -2
- package/src/hosted/node/hosted-plugin.ts +2 -2
- package/src/hosted/node/metadata-scanner.ts +8 -6
- package/src/hosted/node/plugin-manifest-loader.ts +2 -0
- package/src/hosted/node/plugin-service.ts +79 -23
- package/src/hosted/node/scanners/scanner-theia.ts +5 -3
- package/src/main/node/handlers/plugin-theia-directory-handler.ts +56 -28
- package/src/main/node/handlers/plugin-theia-file-handler.ts +25 -4
- package/src/main/node/plugin-cli-contribution.ts +12 -0
- package/src/main/node/plugin-deployer-directory-handler-context-impl.ts +17 -1
- package/src/main/node/plugin-deployer-file-handler-context-impl.ts +0 -1
- package/src/main/node/plugin-deployer-impl.ts +75 -30
- package/src/main/node/plugin-ext-backend-module.ts +3 -0
- package/src/main/node/plugin-server-handler.ts +6 -2
- package/src/main/node/plugin-uninstallation-manager.ts +60 -0
|
@@ -25,7 +25,9 @@ import { PreferenceSchema, PreferenceSchemaProperties } from '@theia/core/lib/co
|
|
|
25
25
|
import { ProblemMatcherContribution, ProblemPatternContribution, TaskDefinition } from '@theia/task/lib/common';
|
|
26
26
|
import { ColorDefinition } from '@theia/core/lib/common/color';
|
|
27
27
|
import { ResourceLabelFormatter } from '@theia/core/lib/common/label-protocol';
|
|
28
|
+
import { PluginIdentifiers } from './plugin-identifiers';
|
|
28
29
|
|
|
30
|
+
export { PluginIdentifiers };
|
|
29
31
|
export const hostedServicePath = '/services/hostedPlugin';
|
|
30
32
|
|
|
31
33
|
/**
|
|
@@ -38,7 +40,8 @@ export type PluginEngine = string;
|
|
|
38
40
|
*/
|
|
39
41
|
export interface PluginPackage {
|
|
40
42
|
name: string;
|
|
41
|
-
publisher
|
|
43
|
+
// The publisher is not guaranteed to be defined for unpublished plugins. https://github.com/microsoft/vscode-vsce/commit/a38657ece04c20e4fbde15d5ac1ed39ca51cb856
|
|
44
|
+
publisher: string | undefined;
|
|
42
45
|
version: string;
|
|
43
46
|
engines: {
|
|
44
47
|
[type in PluginEngine]: string;
|
|
@@ -487,6 +490,8 @@ export interface PluginDeployerFileHandlerContext {
|
|
|
487
490
|
|
|
488
491
|
export interface PluginDeployerDirectoryHandlerContext {
|
|
489
492
|
|
|
493
|
+
copy(origin: string, target: string): Promise<void>;
|
|
494
|
+
|
|
490
495
|
pluginEntry(): PluginDeployerEntry;
|
|
491
496
|
|
|
492
497
|
}
|
|
@@ -811,6 +816,7 @@ export interface PluginMetadata {
|
|
|
811
816
|
model: PluginModel;
|
|
812
817
|
lifecycle: PluginLifecycle;
|
|
813
818
|
isUnderDevelopment?: boolean;
|
|
819
|
+
outOfSync: boolean;
|
|
814
820
|
}
|
|
815
821
|
|
|
816
822
|
export const MetadataProcessor = Symbol('MetadataProcessor');
|
|
@@ -837,6 +843,11 @@ export interface HostedPluginClient {
|
|
|
837
843
|
|
|
838
844
|
export interface PluginDependencies {
|
|
839
845
|
metadata: PluginMetadata
|
|
846
|
+
/**
|
|
847
|
+
* Actual listing of plugin dependencies.
|
|
848
|
+
* Mapping from {@link PluginIdentifiers.UnversionedId external representation} of plugin identity to a string
|
|
849
|
+
* that can be used to identify the resolver for the specific plugin case, e.g. with scheme `vscode://<id>`.
|
|
850
|
+
*/
|
|
840
851
|
mapping?: Map<string, string>
|
|
841
852
|
}
|
|
842
853
|
|
|
@@ -845,14 +856,25 @@ export interface PluginDeployerHandler {
|
|
|
845
856
|
deployFrontendPlugins(frontendPlugins: PluginDeployerEntry[]): Promise<void>;
|
|
846
857
|
deployBackendPlugins(backendPlugins: PluginDeployerEntry[]): Promise<void>;
|
|
847
858
|
|
|
848
|
-
|
|
849
|
-
|
|
859
|
+
getDeployedPluginsById(pluginId: string): DeployedPlugin[];
|
|
860
|
+
|
|
861
|
+
getDeployedPlugin(pluginId: PluginIdentifiers.VersionedId): DeployedPlugin | undefined;
|
|
862
|
+
/**
|
|
863
|
+
* Removes the plugin from the location it originally resided on disk.
|
|
864
|
+
* Unless `--uncompressed-plugins-in-place` is passed to the CLI, this operation is safe.
|
|
865
|
+
*/
|
|
866
|
+
uninstallPlugin(pluginId: PluginIdentifiers.VersionedId): Promise<boolean>;
|
|
867
|
+
/**
|
|
868
|
+
* Removes the plugin from the locations to which it had been deployed.
|
|
869
|
+
* This operation is not safe - references to deleted assets may remain.
|
|
870
|
+
*/
|
|
871
|
+
undeployPlugin(pluginId: PluginIdentifiers.VersionedId): Promise<boolean>;
|
|
850
872
|
|
|
851
873
|
getPluginDependencies(pluginToBeInstalled: PluginDeployerEntry): Promise<PluginDependencies | undefined>;
|
|
852
874
|
}
|
|
853
875
|
|
|
854
876
|
export interface GetDeployedPluginsParams {
|
|
855
|
-
pluginIds:
|
|
877
|
+
pluginIds: PluginIdentifiers.VersionedId[]
|
|
856
878
|
}
|
|
857
879
|
|
|
858
880
|
export interface DeployedPlugin {
|
|
@@ -867,7 +889,9 @@ export interface DeployedPlugin {
|
|
|
867
889
|
export const HostedPluginServer = Symbol('HostedPluginServer');
|
|
868
890
|
export interface HostedPluginServer extends JsonRpcServer<HostedPluginClient> {
|
|
869
891
|
|
|
870
|
-
getDeployedPluginIds(): Promise<
|
|
892
|
+
getDeployedPluginIds(): Promise<PluginIdentifiers.VersionedId[]>;
|
|
893
|
+
|
|
894
|
+
getUninstalledPluginIds(): Promise<readonly PluginIdentifiers.VersionedId[]>;
|
|
871
895
|
|
|
872
896
|
getDeployedPlugins(params: GetDeployedPluginsParams): Promise<DeployedPlugin[]>;
|
|
873
897
|
|
|
@@ -899,8 +923,8 @@ export interface PluginServer {
|
|
|
899
923
|
* @param type whether a plugin is installed by a system or a user, defaults to a user
|
|
900
924
|
*/
|
|
901
925
|
deploy(pluginEntry: string, type?: PluginType): Promise<void>;
|
|
902
|
-
|
|
903
|
-
undeploy(pluginId:
|
|
926
|
+
uninstall(pluginId: PluginIdentifiers.VersionedId): Promise<void>;
|
|
927
|
+
undeploy(pluginId: PluginIdentifiers.VersionedId): Promise<void>;
|
|
904
928
|
|
|
905
929
|
setStorageValue(key: string, value: KeysToAnyValues, kind: PluginStorageKind): Promise<boolean>;
|
|
906
930
|
getStorageValue(key: string, kind: PluginStorageKind): Promise<KeysToAnyValues>;
|
|
@@ -925,7 +949,7 @@ export interface ServerPluginRunner {
|
|
|
925
949
|
/**
|
|
926
950
|
* Provides additional plugin ids.
|
|
927
951
|
*/
|
|
928
|
-
getExtraDeployedPluginIds(): Promise<
|
|
952
|
+
getExtraDeployedPluginIds(): Promise<PluginIdentifiers.VersionedId[]>;
|
|
929
953
|
|
|
930
954
|
}
|
|
931
955
|
|
|
@@ -25,7 +25,7 @@ import debounce = require('@theia/core/shared/lodash.debounce');
|
|
|
25
25
|
import { UUID } from '@theia/core/shared/@phosphor/coreutils';
|
|
26
26
|
import { injectable, inject, interfaces, named, postConstruct } from '@theia/core/shared/inversify';
|
|
27
27
|
import { PluginWorker } from './plugin-worker';
|
|
28
|
-
import { PluginMetadata, getPluginId, HostedPluginServer, DeployedPlugin, PluginServer } from '../../common/plugin-protocol';
|
|
28
|
+
import { PluginMetadata, getPluginId, HostedPluginServer, DeployedPlugin, PluginServer, PluginIdentifiers } from '../../common/plugin-protocol';
|
|
29
29
|
import { HostedPluginWatcher } from './hosted-plugin-watcher';
|
|
30
30
|
import { MAIN_RPC_CONTEXT, PluginManagerExt, ConfigStorage, UIKind } from '../../common/plugin-api-rpc';
|
|
31
31
|
import { setUpPluginApi } from '../../main/browser/main-context';
|
|
@@ -165,7 +165,7 @@ export class HostedPluginSupport {
|
|
|
165
165
|
|
|
166
166
|
protected readonly managers = new Map<string, PluginManagerExt>();
|
|
167
167
|
|
|
168
|
-
private readonly contributions = new Map<
|
|
168
|
+
private readonly contributions = new Map<PluginIdentifiers.UnversionedId, PluginContributions>();
|
|
169
169
|
|
|
170
170
|
protected readonly activationEvents = new Set<string>();
|
|
171
171
|
|
|
@@ -240,7 +240,7 @@ export class HostedPluginSupport {
|
|
|
240
240
|
return plugins;
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
-
getPlugin(id:
|
|
243
|
+
getPlugin(id: PluginIdentifiers.UnversionedId): DeployedPlugin | undefined {
|
|
244
244
|
const contributions = this.contributions.get(id);
|
|
245
245
|
return contributions && contributions.plugin;
|
|
246
246
|
}
|
|
@@ -312,27 +312,33 @@ export class HostedPluginSupport {
|
|
|
312
312
|
let syncPluginsMeasurement: Measurement | undefined;
|
|
313
313
|
|
|
314
314
|
const toUnload = new Set(this.contributions.keys());
|
|
315
|
+
let didChangeInstallationStatus = false;
|
|
315
316
|
try {
|
|
316
|
-
const
|
|
317
|
-
const deployedPluginIds = await this.server.getDeployedPluginIds();
|
|
317
|
+
const newPluginIds: PluginIdentifiers.VersionedId[] = [];
|
|
318
|
+
const [deployedPluginIds, uninstalledPluginIds] = await Promise.all([this.server.getDeployedPluginIds(), this.server.getUninstalledPluginIds()]);
|
|
318
319
|
waitPluginsMeasurement.log('Waiting for backend deployment');
|
|
319
320
|
syncPluginsMeasurement = this.measure('syncPlugins');
|
|
320
|
-
for (const
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
321
|
+
for (const versionedId of deployedPluginIds) {
|
|
322
|
+
const unversionedId = PluginIdentifiers.unversionedFromVersioned(versionedId);
|
|
323
|
+
toUnload.delete(unversionedId);
|
|
324
|
+
if (!this.contributions.has(unversionedId)) {
|
|
325
|
+
newPluginIds.push(versionedId);
|
|
324
326
|
}
|
|
325
327
|
}
|
|
326
328
|
for (const pluginId of toUnload) {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
329
|
+
this.contributions.get(pluginId)?.dispose();
|
|
330
|
+
}
|
|
331
|
+
for (const versionedId of uninstalledPluginIds) {
|
|
332
|
+
const plugin = this.getPlugin(PluginIdentifiers.unversionedFromVersioned(versionedId));
|
|
333
|
+
if (plugin && PluginIdentifiers.componentsToVersionedId(plugin.metadata.model) === versionedId && !plugin.metadata.outOfSync) {
|
|
334
|
+
didChangeInstallationStatus = true;
|
|
335
|
+
plugin.metadata.outOfSync = didChangeInstallationStatus = true;
|
|
330
336
|
}
|
|
331
337
|
}
|
|
332
|
-
if (
|
|
333
|
-
const plugins = await this.server.getDeployedPlugins({ pluginIds });
|
|
338
|
+
if (newPluginIds.length) {
|
|
339
|
+
const plugins = await this.server.getDeployedPlugins({ pluginIds: newPluginIds });
|
|
334
340
|
for (const plugin of plugins) {
|
|
335
|
-
const pluginId = plugin.metadata.model
|
|
341
|
+
const pluginId = PluginIdentifiers.componentsToUnversionedId(plugin.metadata.model);
|
|
336
342
|
const contributions = new PluginContributions(plugin);
|
|
337
343
|
this.contributions.set(pluginId, contributions);
|
|
338
344
|
contributions.push(Disposable.create(() => this.contributions.delete(pluginId)));
|
|
@@ -340,7 +346,7 @@ export class HostedPluginSupport {
|
|
|
340
346
|
}
|
|
341
347
|
}
|
|
342
348
|
} finally {
|
|
343
|
-
if (initialized || toUnload.size) {
|
|
349
|
+
if (initialized || toUnload.size || didChangeInstallationStatus) {
|
|
344
350
|
this.onDidChangePluginsEmitter.fire(undefined);
|
|
345
351
|
}
|
|
346
352
|
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
18
18
|
|
|
19
|
-
import { PluginModel, PluginPackage } from '../../../common/plugin-protocol';
|
|
19
|
+
import { PluginIdentifiers, PluginModel, PluginPackage } from '../../../common/plugin-protocol';
|
|
20
20
|
import { Endpoint } from '@theia/core/lib/browser/endpoint';
|
|
21
21
|
import URI from '@theia/core/lib/common/uri';
|
|
22
22
|
|
|
@@ -54,7 +54,9 @@ function readContents(uri: string): Promise<string> {
|
|
|
54
54
|
|
|
55
55
|
async function readPluginJson(pluginModel: PluginModel, relativePath: string): Promise<any> {
|
|
56
56
|
const content = await readPluginFile(pluginModel, relativePath);
|
|
57
|
-
|
|
57
|
+
const json = JSON.parse(content) as PluginPackage;
|
|
58
|
+
json.publisher ??= PluginIdentifiers.UNPUBLISHED;
|
|
59
|
+
return json;
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
export async function loadManifest(pluginModel: PluginModel): Promise<any> {
|
|
@@ -17,11 +17,15 @@
|
|
|
17
17
|
import * as fs from '@theia/core/shared/fs-extra';
|
|
18
18
|
import { injectable, inject } from '@theia/core/shared/inversify';
|
|
19
19
|
import { ILogger } from '@theia/core';
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
PluginDeployerHandler, PluginDeployerEntry, PluginEntryPoint, DeployedPlugin,
|
|
22
|
+
PluginDependencies, PluginType, PluginIdentifiers
|
|
23
|
+
} from '../../common/plugin-protocol';
|
|
21
24
|
import { HostedPluginReader } from './plugin-reader';
|
|
22
25
|
import { Deferred } from '@theia/core/lib/common/promise-util';
|
|
23
26
|
import { HostedPluginLocalizationService } from './hosted-plugin-localization-service';
|
|
24
27
|
import { Stopwatch } from '@theia/core/lib/common';
|
|
28
|
+
import { PluginUninstallationManager } from '../../main/node/plugin-uninstallation-manager';
|
|
25
29
|
|
|
26
30
|
@injectable()
|
|
27
31
|
export class HostedPluginDeployerHandler implements PluginDeployerHandler {
|
|
@@ -38,42 +42,56 @@ export class HostedPluginDeployerHandler implements PluginDeployerHandler {
|
|
|
38
42
|
@inject(Stopwatch)
|
|
39
43
|
protected readonly stopwatch: Stopwatch;
|
|
40
44
|
|
|
41
|
-
|
|
45
|
+
@inject(PluginUninstallationManager)
|
|
46
|
+
protected readonly uninstallationManager: PluginUninstallationManager;
|
|
47
|
+
|
|
48
|
+
private readonly deployedLocations = new Map<PluginIdentifiers.VersionedId, Set<string>>();
|
|
49
|
+
protected readonly originalLocations = new Map<PluginIdentifiers.VersionedId, string>();
|
|
42
50
|
|
|
43
51
|
/**
|
|
44
52
|
* Managed plugin metadata backend entries.
|
|
45
53
|
*/
|
|
46
|
-
private readonly deployedBackendPlugins = new Map<
|
|
54
|
+
private readonly deployedBackendPlugins = new Map<PluginIdentifiers.VersionedId, DeployedPlugin>();
|
|
47
55
|
|
|
48
56
|
/**
|
|
49
57
|
* Managed plugin metadata frontend entries.
|
|
50
58
|
*/
|
|
51
|
-
private readonly deployedFrontendPlugins = new Map<
|
|
59
|
+
private readonly deployedFrontendPlugins = new Map<PluginIdentifiers.VersionedId, DeployedPlugin>();
|
|
52
60
|
|
|
53
61
|
private backendPluginsMetadataDeferred = new Deferred<void>();
|
|
54
62
|
|
|
55
63
|
private frontendPluginsMetadataDeferred = new Deferred<void>();
|
|
56
64
|
|
|
57
|
-
async getDeployedFrontendPluginIds(): Promise<
|
|
65
|
+
async getDeployedFrontendPluginIds(): Promise<PluginIdentifiers.VersionedId[]> {
|
|
58
66
|
// await first deploy
|
|
59
67
|
await this.frontendPluginsMetadataDeferred.promise;
|
|
60
68
|
// fetch the last deployed state
|
|
61
|
-
return
|
|
69
|
+
return Array.from(this.deployedFrontendPlugins.keys());
|
|
62
70
|
}
|
|
63
71
|
|
|
64
|
-
async getDeployedBackendPluginIds(): Promise<
|
|
72
|
+
async getDeployedBackendPluginIds(): Promise<PluginIdentifiers.VersionedId[]> {
|
|
65
73
|
// await first deploy
|
|
66
74
|
await this.backendPluginsMetadataDeferred.promise;
|
|
67
75
|
// fetch the last deployed state
|
|
68
|
-
return
|
|
76
|
+
return Array.from(this.deployedBackendPlugins.keys());
|
|
69
77
|
}
|
|
70
78
|
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
79
|
+
getDeployedPluginsById(pluginId: string): DeployedPlugin[] {
|
|
80
|
+
const matches: DeployedPlugin[] = [];
|
|
81
|
+
const handle = (plugins: Iterable<DeployedPlugin>): void => {
|
|
82
|
+
for (const plugin of plugins) {
|
|
83
|
+
if (PluginIdentifiers.componentsToVersionWithId(plugin.metadata.model).version === pluginId) {
|
|
84
|
+
matches.push(plugin);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
handle(this.deployedFrontendPlugins.values());
|
|
89
|
+
handle(this.deployedBackendPlugins.values());
|
|
90
|
+
return matches;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
getDeployedPlugin(pluginId: PluginIdentifiers.VersionedId): DeployedPlugin | undefined {
|
|
94
|
+
return this.deployedBackendPlugins.get(pluginId) ?? this.deployedFrontendPlugins.get(pluginId);
|
|
77
95
|
}
|
|
78
96
|
|
|
79
97
|
/**
|
|
@@ -123,6 +141,8 @@ export class HostedPluginDeployerHandler implements PluginDeployerHandler {
|
|
|
123
141
|
protected async deployPlugin(entry: PluginDeployerEntry, entryPoint: keyof PluginEntryPoint): Promise<void> {
|
|
124
142
|
const pluginPath = entry.path();
|
|
125
143
|
const deployPlugin = this.stopwatch.start('deployPlugin');
|
|
144
|
+
let id;
|
|
145
|
+
let success = true;
|
|
126
146
|
try {
|
|
127
147
|
const manifest = await this.reader.readPackage(pluginPath);
|
|
128
148
|
if (!manifest) {
|
|
@@ -133,12 +153,15 @@ export class HostedPluginDeployerHandler implements PluginDeployerHandler {
|
|
|
133
153
|
const metadata = this.reader.readMetadata(manifest);
|
|
134
154
|
metadata.isUnderDevelopment = entry.getValue('isUnderDevelopment') ?? false;
|
|
135
155
|
|
|
136
|
-
|
|
156
|
+
id = PluginIdentifiers.componentsToVersionedId(metadata.model);
|
|
157
|
+
|
|
158
|
+
const deployedLocations = this.deployedLocations.get(id) || new Set<string>();
|
|
137
159
|
deployedLocations.add(entry.rootPath);
|
|
138
|
-
this.deployedLocations.set(
|
|
160
|
+
this.deployedLocations.set(id, deployedLocations);
|
|
161
|
+
this.originalLocations.set(id, entry.originalPath());
|
|
139
162
|
|
|
140
163
|
const deployedPlugins = entryPoint === 'backend' ? this.deployedBackendPlugins : this.deployedFrontendPlugins;
|
|
141
|
-
if (deployedPlugins.has(
|
|
164
|
+
if (deployedPlugins.has(id)) {
|
|
142
165
|
deployPlugin.debug(`Skipped ${entryPoint} plugin ${metadata.model.name} already deployed`);
|
|
143
166
|
return;
|
|
144
167
|
}
|
|
@@ -147,14 +170,35 @@ export class HostedPluginDeployerHandler implements PluginDeployerHandler {
|
|
|
147
170
|
const deployed: DeployedPlugin = { metadata, type };
|
|
148
171
|
deployed.contributes = this.reader.readContribution(manifest);
|
|
149
172
|
this.localizationService.deployLocalizations(deployed);
|
|
150
|
-
deployedPlugins.set(
|
|
151
|
-
deployPlugin.log(`Deployed ${entryPoint} plugin "${
|
|
173
|
+
deployedPlugins.set(id, deployed);
|
|
174
|
+
deployPlugin.log(`Deployed ${entryPoint} plugin "${id}" from "${metadata.model.entryPoint[entryPoint] || pluginPath}"`);
|
|
152
175
|
} catch (e) {
|
|
176
|
+
success = false;
|
|
153
177
|
deployPlugin.error(`Failed to deploy ${entryPoint} plugin from '${pluginPath}' path`, e);
|
|
178
|
+
} finally {
|
|
179
|
+
if (success && id) {
|
|
180
|
+
this.uninstallationManager.markAsInstalled(id);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async uninstallPlugin(pluginId: PluginIdentifiers.VersionedId): Promise<boolean> {
|
|
186
|
+
try {
|
|
187
|
+
const originalPath = this.originalLocations.get(pluginId);
|
|
188
|
+
if (!originalPath) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
await fs.remove(originalPath);
|
|
192
|
+
this.originalLocations.delete(pluginId);
|
|
193
|
+
this.uninstallationManager.markAsUninstalled(pluginId);
|
|
194
|
+
return true;
|
|
195
|
+
} catch (e) {
|
|
196
|
+
console.error('Error uninstalling plugin', e);
|
|
197
|
+
return false;
|
|
154
198
|
}
|
|
155
199
|
}
|
|
156
200
|
|
|
157
|
-
async undeployPlugin(pluginId:
|
|
201
|
+
async undeployPlugin(pluginId: PluginIdentifiers.VersionedId): Promise<boolean> {
|
|
158
202
|
this.deployedBackendPlugins.delete(pluginId);
|
|
159
203
|
this.deployedFrontendPlugins.delete(pluginId);
|
|
160
204
|
const deployedLocations = this.deployedLocations.get(pluginId);
|
|
@@ -18,7 +18,7 @@ import * as cp from 'child_process';
|
|
|
18
18
|
import { injectable, inject, named } from '@theia/core/shared/inversify';
|
|
19
19
|
import { ILogger, ConnectionErrorHandler, ContributionProvider, MessageService } from '@theia/core/lib/common';
|
|
20
20
|
import { createIpcEnv } from '@theia/core/lib/node/messaging/ipc-protocol';
|
|
21
|
-
import { HostedPluginClient, ServerPluginRunner, PluginHostEnvironmentVariable, DeployedPlugin, PLUGIN_HOST_BACKEND } from '../../common/plugin-protocol';
|
|
21
|
+
import { HostedPluginClient, ServerPluginRunner, PluginHostEnvironmentVariable, DeployedPlugin, PLUGIN_HOST_BACKEND, PluginIdentifiers } from '../../common/plugin-protocol';
|
|
22
22
|
import { MessageType } from '../../common/rpc-protocol';
|
|
23
23
|
import { HostedPluginCliContribution } from './hosted-plugin-cli-contribution';
|
|
24
24
|
import * as psTree from 'ps-tree';
|
|
@@ -225,7 +225,7 @@ export class HostedPluginProcess implements ServerPluginRunner {
|
|
|
225
225
|
/**
|
|
226
226
|
* Provides additional plugin ids.
|
|
227
227
|
*/
|
|
228
|
-
public async getExtraDeployedPluginIds(): Promise<
|
|
228
|
+
public async getExtraDeployedPluginIds(): Promise<PluginIdentifiers.VersionedId[]> {
|
|
229
229
|
return [];
|
|
230
230
|
}
|
|
231
231
|
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import { injectable, inject, multiInject, postConstruct, optional } from '@theia/core/shared/inversify';
|
|
18
18
|
import { ILogger, ConnectionErrorHandler } from '@theia/core/lib/common';
|
|
19
|
-
import { HostedPluginClient, PluginModel, ServerPluginRunner, DeployedPlugin } from '../../common/plugin-protocol';
|
|
19
|
+
import { HostedPluginClient, PluginModel, ServerPluginRunner, DeployedPlugin, PluginIdentifiers } from '../../common/plugin-protocol';
|
|
20
20
|
import { LogPart } from '../../common/types';
|
|
21
21
|
import { HostedPluginProcess } from './hosted-plugin-process';
|
|
22
22
|
|
|
@@ -95,7 +95,7 @@ export class HostedPluginSupport {
|
|
|
95
95
|
/**
|
|
96
96
|
* Provides additional plugin ids.
|
|
97
97
|
*/
|
|
98
|
-
async getExtraDeployedPluginIds(): Promise<
|
|
98
|
+
async getExtraDeployedPluginIds(): Promise<PluginIdentifiers.VersionedId[]> {
|
|
99
99
|
return [].concat.apply([], await Promise.all(this.pluginRunners.map(runner => runner.getExtraDeployedPluginIds())));
|
|
100
100
|
}
|
|
101
101
|
|
|
@@ -14,15 +14,16 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
|
-
import { injectable, multiInject } from '@theia/core/shared/inversify';
|
|
18
|
-
import { PluginPackage, PluginScanner, PluginMetadata, PLUGIN_HOST_BACKEND } from '../../common/plugin-protocol';
|
|
17
|
+
import { inject, injectable, multiInject } from '@theia/core/shared/inversify';
|
|
18
|
+
import { PluginPackage, PluginScanner, PluginMetadata, PLUGIN_HOST_BACKEND, PluginIdentifiers } from '../../common/plugin-protocol';
|
|
19
|
+
import { PluginUninstallationManager } from '../../main/node/plugin-uninstallation-manager';
|
|
19
20
|
@injectable()
|
|
20
21
|
export class MetadataScanner {
|
|
21
22
|
private scanners: Map<string, PluginScanner> = new Map();
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
) {
|
|
24
|
+
@inject(PluginUninstallationManager) protected readonly uninstallationManager: PluginUninstallationManager;
|
|
25
|
+
|
|
26
|
+
constructor(@multiInject(PluginScanner) scanners: PluginScanner[]) {
|
|
26
27
|
scanners.forEach((scanner: PluginScanner) => {
|
|
27
28
|
this.scanners.set(scanner.apiType, scanner);
|
|
28
29
|
});
|
|
@@ -33,7 +34,8 @@ export class MetadataScanner {
|
|
|
33
34
|
return {
|
|
34
35
|
host: PLUGIN_HOST_BACKEND,
|
|
35
36
|
model: scanner.getModel(plugin),
|
|
36
|
-
lifecycle: scanner.getLifecycle(plugin)
|
|
37
|
+
lifecycle: scanner.getLifecycle(plugin),
|
|
38
|
+
outOfSync: this.uninstallationManager.isUninstalled(PluginIdentifiers.componentsToVersionedId(plugin)),
|
|
37
39
|
};
|
|
38
40
|
}
|
|
39
41
|
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import * as path from 'path';
|
|
18
18
|
import * as fs from '@theia/core/shared/fs-extra';
|
|
19
|
+
import { PluginIdentifiers } from '../../common';
|
|
19
20
|
|
|
20
21
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
22
|
export async function loadManifest(pluginPath: string): Promise<any> {
|
|
@@ -25,5 +26,6 @@ export async function loadManifest(pluginPath: string): Promise<any> {
|
|
|
25
26
|
if (manifest && manifest.name && manifest.name.startsWith(built_prefix)) {
|
|
26
27
|
manifest.name = manifest.name.substr(built_prefix.length);
|
|
27
28
|
}
|
|
29
|
+
manifest.publisher ??= PluginIdentifiers.UNPUBLISHED;
|
|
28
30
|
return manifest;
|
|
29
31
|
}
|
|
@@ -14,13 +14,14 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
import { injectable, inject, named, postConstruct } from '@theia/core/shared/inversify';
|
|
17
|
-
import { HostedPluginServer, HostedPluginClient, PluginDeployer, GetDeployedPluginsParams, DeployedPlugin } from '../../common/plugin-protocol';
|
|
17
|
+
import { HostedPluginServer, HostedPluginClient, PluginDeployer, GetDeployedPluginsParams, DeployedPlugin, PluginIdentifiers } from '../../common/plugin-protocol';
|
|
18
18
|
import { HostedPluginSupport } from './hosted-plugin';
|
|
19
|
-
import { ILogger, Disposable, ContributionProvider } from '@theia/core';
|
|
19
|
+
import { ILogger, Disposable, ContributionProvider, DisposableCollection } from '@theia/core';
|
|
20
20
|
import { ExtPluginApiProvider, ExtPluginApi } from '../../common/plugin-ext-api-contribution';
|
|
21
21
|
import { HostedPluginDeployerHandler } from './hosted-plugin-deployer-handler';
|
|
22
22
|
import { PluginDeployerImpl } from '../../main/node/plugin-deployer-impl';
|
|
23
23
|
import { HostedPluginLocalizationService } from './hosted-plugin-localization-service';
|
|
24
|
+
import { PluginUninstallationManager } from '../../main/node/plugin-uninstallation-manager';
|
|
24
25
|
|
|
25
26
|
@injectable()
|
|
26
27
|
export class HostedPluginServerImpl implements HostedPluginServer {
|
|
@@ -40,9 +41,21 @@ export class HostedPluginServerImpl implements HostedPluginServer {
|
|
|
40
41
|
@named(Symbol.for(ExtPluginApiProvider))
|
|
41
42
|
protected readonly extPluginAPIContributions: ContributionProvider<ExtPluginApiProvider>;
|
|
42
43
|
|
|
44
|
+
@inject(PluginUninstallationManager) protected readonly uninstallationManager: PluginUninstallationManager;
|
|
45
|
+
|
|
43
46
|
protected client: HostedPluginClient | undefined;
|
|
47
|
+
protected toDispose = new DisposableCollection();
|
|
48
|
+
|
|
49
|
+
protected _ignoredPlugins?: Set<PluginIdentifiers.VersionedId>;
|
|
50
|
+
// We ignore any plugins that are marked as uninstalled the first time the frontend requests information about deployed plugins.
|
|
51
|
+
protected get ignoredPlugins(): Set<PluginIdentifiers.VersionedId> {
|
|
52
|
+
if (!this._ignoredPlugins) {
|
|
53
|
+
this._ignoredPlugins = new Set(this.uninstallationManager.getUninstalledPluginIds());
|
|
54
|
+
}
|
|
55
|
+
return this._ignoredPlugins;
|
|
56
|
+
}
|
|
44
57
|
|
|
45
|
-
protected
|
|
58
|
+
protected readonly pluginVersions = new Map<PluginIdentifiers.UnversionedId, string>();
|
|
46
59
|
|
|
47
60
|
constructor(
|
|
48
61
|
@inject(HostedPluginSupport) private readonly hostedPlugin: HostedPluginSupport) {
|
|
@@ -50,38 +63,78 @@ export class HostedPluginServerImpl implements HostedPluginServer {
|
|
|
50
63
|
|
|
51
64
|
@postConstruct()
|
|
52
65
|
protected init(): void {
|
|
53
|
-
this.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
66
|
+
this.toDispose.pushAll([
|
|
67
|
+
this.pluginDeployer.onDidDeploy(() => this.client?.onDidDeploy()),
|
|
68
|
+
this.uninstallationManager.onDidChangeUninstalledPlugins(currentUninstalled => {
|
|
69
|
+
if (this._ignoredPlugins) {
|
|
70
|
+
const uninstalled = new Set(currentUninstalled);
|
|
71
|
+
for (const previouslyUninstalled of this._ignoredPlugins) {
|
|
72
|
+
if (!uninstalled.has(previouslyUninstalled)) {
|
|
73
|
+
this._ignoredPlugins.delete(previouslyUninstalled);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
this.client?.onDidDeploy();
|
|
78
|
+
}),
|
|
79
|
+
Disposable.create(() => this.hostedPlugin.clientClosed()),
|
|
80
|
+
]);
|
|
58
81
|
}
|
|
59
82
|
|
|
60
83
|
dispose(): void {
|
|
61
|
-
this.
|
|
62
|
-
this.deployedListener.dispose();
|
|
84
|
+
this.toDispose.dispose();
|
|
63
85
|
}
|
|
86
|
+
|
|
64
87
|
setClient(client: HostedPluginClient): void {
|
|
65
88
|
this.client = client;
|
|
66
89
|
this.hostedPlugin.setClient(client);
|
|
67
90
|
}
|
|
68
91
|
|
|
69
|
-
async getDeployedPluginIds(): Promise<
|
|
92
|
+
async getDeployedPluginIds(): Promise<PluginIdentifiers.VersionedId[]> {
|
|
70
93
|
const backendMetadata = await this.deployerHandler.getDeployedBackendPluginIds();
|
|
71
94
|
if (backendMetadata.length > 0) {
|
|
72
95
|
this.hostedPlugin.runPluginServer();
|
|
73
96
|
}
|
|
74
|
-
const plugins = new Set<
|
|
75
|
-
|
|
76
|
-
|
|
97
|
+
const plugins = new Set<PluginIdentifiers.VersionedId>();
|
|
98
|
+
const addIds = async (identifiers: PluginIdentifiers.VersionedId[]): Promise<void> => {
|
|
99
|
+
for (const pluginId of identifiers) {
|
|
100
|
+
if (this.isRelevantPlugin(pluginId)) {
|
|
101
|
+
plugins.add(pluginId);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
addIds(await this.deployerHandler.getDeployedFrontendPluginIds());
|
|
106
|
+
addIds(backendMetadata);
|
|
107
|
+
addIds(await this.hostedPlugin.getExtraDeployedPluginIds());
|
|
108
|
+
return Array.from(plugins);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Ensures that the plugin was not uninstalled when this session was started
|
|
113
|
+
* and that it matches the first version of the given plugin seen by this session.
|
|
114
|
+
*
|
|
115
|
+
* The deployment system may have multiple versions of the same plugin available, but
|
|
116
|
+
* a single session should only ever activate one of them.
|
|
117
|
+
*/
|
|
118
|
+
protected isRelevantPlugin(identifier: PluginIdentifiers.VersionedId): boolean {
|
|
119
|
+
const versionAndId = PluginIdentifiers.idAndVersionFromVersionedId(identifier);
|
|
120
|
+
if (!versionAndId) {
|
|
121
|
+
return false;
|
|
77
122
|
}
|
|
78
|
-
|
|
79
|
-
|
|
123
|
+
const knownVersion = this.pluginVersions.get(versionAndId.id);
|
|
124
|
+
if (knownVersion !== undefined && knownVersion !== versionAndId.version) {
|
|
125
|
+
return false;
|
|
80
126
|
}
|
|
81
|
-
|
|
82
|
-
|
|
127
|
+
if (this.ignoredPlugins.has(identifier)) {
|
|
128
|
+
return false;
|
|
83
129
|
}
|
|
84
|
-
|
|
130
|
+
if (knownVersion === undefined) {
|
|
131
|
+
this.pluginVersions.set(versionAndId.id, versionAndId.version);
|
|
132
|
+
}
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
getUninstalledPluginIds(): Promise<readonly PluginIdentifiers.VersionedId[]> {
|
|
137
|
+
return Promise.resolve(this.uninstallationManager.getUninstalledPluginIds());
|
|
85
138
|
}
|
|
86
139
|
|
|
87
140
|
async getDeployedPlugins({ pluginIds }: GetDeployedPluginsParams): Promise<DeployedPlugin[]> {
|
|
@@ -90,16 +143,19 @@ export class HostedPluginServerImpl implements HostedPluginServer {
|
|
|
90
143
|
}
|
|
91
144
|
const plugins: DeployedPlugin[] = [];
|
|
92
145
|
let extraDeployedPlugins: Map<string, DeployedPlugin> | undefined;
|
|
93
|
-
for (const
|
|
94
|
-
|
|
146
|
+
for (const versionedId of pluginIds) {
|
|
147
|
+
if (!this.isRelevantPlugin(versionedId)) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
let plugin = this.deployerHandler.getDeployedPlugin(versionedId);
|
|
95
151
|
if (!plugin) {
|
|
96
152
|
if (!extraDeployedPlugins) {
|
|
97
153
|
extraDeployedPlugins = new Map<string, DeployedPlugin>();
|
|
98
154
|
for (const extraDeployedPlugin of await this.hostedPlugin.getExtraDeployedPlugins()) {
|
|
99
|
-
extraDeployedPlugins.set(extraDeployedPlugin.metadata.model
|
|
155
|
+
extraDeployedPlugins.set(PluginIdentifiers.componentsToVersionedId(extraDeployedPlugin.metadata.model), extraDeployedPlugin);
|
|
100
156
|
}
|
|
101
157
|
}
|
|
102
|
-
plugin = extraDeployedPlugins.get(
|
|
158
|
+
plugin = extraDeployedPlugins.get(versionedId);
|
|
103
159
|
}
|
|
104
160
|
if (plugin) {
|
|
105
161
|
plugins.push(plugin);
|
|
@@ -58,7 +58,8 @@ import {
|
|
|
58
58
|
PluginPackageLocalization,
|
|
59
59
|
Localization,
|
|
60
60
|
PluginPackageTranslation,
|
|
61
|
-
Translation
|
|
61
|
+
Translation,
|
|
62
|
+
PluginIdentifiers
|
|
62
63
|
} from '../../../common/plugin-protocol';
|
|
63
64
|
import * as fs from 'fs';
|
|
64
65
|
import * as path from 'path';
|
|
@@ -109,13 +110,14 @@ export class TheiaPluginScanner implements PluginScanner {
|
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
getModel(plugin: PluginPackage): PluginModel {
|
|
113
|
+
const publisher = plugin.publisher ?? PluginIdentifiers.UNPUBLISHED;
|
|
112
114
|
const result: PluginModel = {
|
|
113
115
|
packagePath: plugin.packagePath,
|
|
114
116
|
packageUri: this.pluginUriFactory.createUri(plugin).toString(),
|
|
115
117
|
// see id definition: https://github.com/microsoft/vscode/blob/15916055fe0cb9411a5f36119b3b012458fe0a1d/src/vs/platform/extensions/common/extensions.ts#L167-L169
|
|
116
|
-
id: `${
|
|
118
|
+
id: `${publisher.toLowerCase()}.${plugin.name.toLowerCase()}`,
|
|
117
119
|
name: plugin.name,
|
|
118
|
-
publisher
|
|
120
|
+
publisher,
|
|
119
121
|
version: plugin.version,
|
|
120
122
|
displayName: plugin.displayName,
|
|
121
123
|
description: plugin.description,
|