@sussudio/platform
Version:
Internal APIs for VS Code's service injection the base services.
263 lines (262 loc) • 10.2 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Emitter, Event } from '@sussudio/base/common/event.mjs';
import { Disposable } from '@sussudio/base/common/lifecycle.mjs';
import { revive } from '@sussudio/base/common/marshalling.mjs';
import { cloneAndChange } from '@sussudio/base/common/objects.mjs';
import { URI } from '@sussudio/base/common/uri.mjs';
import { DefaultURITransformer, transformAndReviveIncomingURIs } from '@sussudio/base/common/uriIpc.mjs';
import { isTargetPlatformCompatible } from './extensionManagement.mjs';
function transformIncomingURI(uri, transformer) {
return URI.revive(transformer ? transformer.transformIncoming(uri) : uri);
}
function transformOutgoingURI(uri, transformer) {
return transformer ? transformer.transformOutgoingURI(uri) : uri;
}
function transformIncomingExtension(extension, transformer) {
transformer = transformer ? transformer : DefaultURITransformer;
const manifest = extension.manifest;
const transformed = transformAndReviveIncomingURIs({ ...extension, ...{ manifest: undefined } }, transformer);
return { ...transformed, ...{ manifest } };
}
function transformOutgoingExtension(extension, transformer) {
return transformer
? cloneAndChange(extension, (value) => (value instanceof URI ? transformer.transformOutgoingURI(value) : undefined))
: extension;
}
export class ExtensionManagementChannel {
service;
getUriTransformer;
onInstallExtension;
onDidInstallExtensions;
onUninstallExtension;
onDidUninstallExtension;
constructor(service, getUriTransformer) {
this.service = service;
this.getUriTransformer = getUriTransformer;
this.onInstallExtension = Event.buffer(service.onInstallExtension, true);
this.onDidInstallExtensions = Event.buffer(service.onDidInstallExtensions, true);
this.onUninstallExtension = Event.buffer(service.onUninstallExtension, true);
this.onDidUninstallExtension = Event.buffer(service.onDidUninstallExtension, true);
}
listen(context, event) {
const uriTransformer = this.getUriTransformer(context);
switch (event) {
case 'onInstallExtension':
return this.onInstallExtension;
case 'onDidInstallExtensions':
return Event.map(this.onDidInstallExtensions, (results) =>
results.map((i) => ({
...i,
local: i.local ? transformOutgoingExtension(i.local, uriTransformer) : i.local,
})),
);
case 'onUninstallExtension':
return this.onUninstallExtension;
case 'onDidUninstallExtension':
return this.onDidUninstallExtension;
}
throw new Error('Invalid listen');
}
call(context, command, args) {
const uriTransformer = this.getUriTransformer(context);
switch (command) {
case 'zip':
return this.service
.zip(transformIncomingExtension(args[0], uriTransformer))
.then((uri) => transformOutgoingURI(uri, uriTransformer));
case 'unzip':
return this.service.unzip(transformIncomingURI(args[0], uriTransformer));
case 'install':
return this.service.install(transformIncomingURI(args[0], uriTransformer), revive(args[1]));
case 'installFromLocation':
return this.service.installFromLocation(transformIncomingURI(args[0], uriTransformer), URI.revive(args[1]));
case 'getManifest':
return this.service.getManifest(transformIncomingURI(args[0], uriTransformer));
case 'getTargetPlatform':
return this.service.getTargetPlatform();
case 'canInstall':
return this.service.canInstall(args[0]);
case 'installFromGallery':
return this.service.installFromGallery(args[0], revive(args[1]));
case 'uninstall':
return this.service.uninstall(transformIncomingExtension(args[0], uriTransformer), revive(args[1]));
case 'reinstallFromGallery':
return this.service.reinstallFromGallery(transformIncomingExtension(args[0], uriTransformer));
case 'getInstalled':
return this.service
.getInstalled(args[0], URI.revive(args[1]))
.then((extensions) => extensions.map((e) => transformOutgoingExtension(e, uriTransformer)));
case 'getMetadata':
return this.service.getMetadata(transformIncomingExtension(args[0], uriTransformer));
case 'updateMetadata':
return this.service
.updateMetadata(transformIncomingExtension(args[0], uriTransformer), args[1])
.then((e) => transformOutgoingExtension(e, uriTransformer));
case 'updateExtensionScope':
return this.service
.updateExtensionScope(transformIncomingExtension(args[0], uriTransformer), args[1])
.then((e) => transformOutgoingExtension(e, uriTransformer));
case 'getExtensionsControlManifest':
return this.service.getExtensionsControlManifest();
case 'download':
return this.service.download(args[0], args[1]);
}
throw new Error('Invalid call');
}
}
export class ExtensionManagementChannelClient extends Disposable {
channel;
_onInstallExtension = this._register(new Emitter());
get onInstallExtension() {
return this._onInstallExtension.event;
}
_onDidInstallExtensions = this._register(new Emitter());
get onDidInstallExtensions() {
return this._onDidInstallExtensions.event;
}
_onUninstallExtension = this._register(new Emitter());
get onUninstallExtension() {
return this._onUninstallExtension.event;
}
_onDidUninstallExtension = this._register(new Emitter());
get onDidUninstallExtension() {
return this._onDidUninstallExtension.event;
}
constructor(channel) {
super();
this.channel = channel;
this._register(
this.channel.listen('onInstallExtension')((e) =>
this._onInstallExtension.fire({
identifier: e.identifier,
source: this.isUriComponents(e.source) ? URI.revive(e.source) : e.source,
profileLocation: URI.revive(e.profileLocation),
}),
),
);
this._register(
this.channel.listen('onDidInstallExtensions')((results) =>
this._onDidInstallExtensions.fire(
results.map((e) => ({
...e,
local: e.local ? transformIncomingExtension(e.local, null) : e.local,
source: this.isUriComponents(e.source) ? URI.revive(e.source) : e.source,
profileLocation: URI.revive(e.profileLocation),
})),
),
),
);
this._register(
this.channel.listen('onUninstallExtension')((e) =>
this._onUninstallExtension.fire({ identifier: e.identifier, profileLocation: URI.revive(e.profileLocation) }),
),
);
this._register(
this.channel.listen('onDidUninstallExtension')((e) =>
this._onDidUninstallExtension.fire({ ...e, profileLocation: URI.revive(e.profileLocation) }),
),
);
}
isUriComponents(thing) {
if (!thing) {
return false;
}
return typeof thing.path === 'string' && typeof thing.scheme === 'string';
}
_targetPlatformPromise;
getTargetPlatform() {
if (!this._targetPlatformPromise) {
this._targetPlatformPromise = this.channel.call('getTargetPlatform');
}
return this._targetPlatformPromise;
}
async canInstall(extension) {
const currentTargetPlatform = await this.getTargetPlatform();
return extension.allTargetPlatforms.some((targetPlatform) =>
isTargetPlatformCompatible(targetPlatform, extension.allTargetPlatforms, currentTargetPlatform),
);
}
zip(extension) {
return Promise.resolve(this.channel.call('zip', [extension]).then((result) => URI.revive(result)));
}
unzip(zipLocation) {
return Promise.resolve(this.channel.call('unzip', [zipLocation]));
}
install(vsix, options) {
return Promise.resolve(this.channel.call('install', [vsix, options])).then((local) =>
transformIncomingExtension(local, null),
);
}
installFromLocation(location, profileLocation) {
return Promise.resolve(this.channel.call('installFromLocation', [location, profileLocation])).then((local) =>
transformIncomingExtension(local, null),
);
}
getManifest(vsix) {
return Promise.resolve(this.channel.call('getManifest', [vsix]));
}
installFromGallery(extension, installOptions) {
return Promise.resolve(this.channel.call('installFromGallery', [extension, installOptions])).then((local) =>
transformIncomingExtension(local, null),
);
}
uninstall(extension, options) {
return Promise.resolve(this.channel.call('uninstall', [extension, options]));
}
reinstallFromGallery(extension) {
return Promise.resolve(this.channel.call('reinstallFromGallery', [extension])).then((local) =>
transformIncomingExtension(local, null),
);
}
getInstalled(type = null, extensionsProfileResource) {
return Promise.resolve(this.channel.call('getInstalled', [type, extensionsProfileResource])).then((extensions) =>
extensions.map((extension) => transformIncomingExtension(extension, null)),
);
}
getMetadata(local) {
return Promise.resolve(this.channel.call('getMetadata', [local]));
}
updateMetadata(local, metadata) {
return Promise.resolve(this.channel.call('updateMetadata', [local, metadata])).then((extension) =>
transformIncomingExtension(extension, null),
);
}
updateExtensionScope(local, isMachineScoped) {
return Promise.resolve(this.channel.call('updateExtensionScope', [local, isMachineScoped])).then((extension) =>
transformIncomingExtension(extension, null),
);
}
getExtensionsControlManifest() {
return Promise.resolve(this.channel.call('getExtensionsControlManifest'));
}
async download(extension, operation) {
const result = await this.channel.call('download', [extension, operation]);
return URI.revive(result);
}
registerParticipant() {
throw new Error('Not Supported');
}
}
export class ExtensionTipsChannel {
service;
constructor(service) {
this.service = service;
}
listen(context, event) {
throw new Error('Invalid listen');
}
call(context, command, args) {
switch (command) {
case 'getConfigBasedTips':
return this.service.getConfigBasedTips(URI.revive(args[0]));
case 'getImportantExecutableBasedTips':
return this.service.getImportantExecutableBasedTips();
case 'getOtherExecutableBasedTips':
return this.service.getOtherExecutableBasedTips();
}
throw new Error('Invalid call');
}
}