@codingame/monaco-vscode-extensions-service-override
Version:
VSCode public API plugged on the monaco editor - extensions service-override
490 lines (486 loc) • 23 kB
JavaScript
import { __decorate, __param } from '@codingame/monaco-vscode-api/external/tslib/tslib.es6';
import { CancellationToken } from '@codingame/monaco-vscode-api/vscode/vs/base/common/cancellation';
import { getErrorMessage, isCancellationError } from '@codingame/monaco-vscode-api/vscode/vs/base/common/errors';
import { Schemas } from '@codingame/monaco-vscode-api/vscode/vs/base/common/network';
import { basename } from '@codingame/monaco-vscode-api/vscode/vs/base/common/resources';
import { semverExports } from '@codingame/monaco-vscode-api/_virtual/semver';
import { URI } from '@codingame/monaco-vscode-api/vscode/vs/base/common/uri';
import { localize } from '@codingame/monaco-vscode-api/vscode/vs/nls';
import { InstallOperation, EXTENSION_IDENTIFIER_REGEX } from '@codingame/monaco-vscode-api/vscode/vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionManagementService, IExtensionGalleryService } from '@codingame/monaco-vscode-api/vscode/vs/platform/extensionManagement/common/extensionManagement.service';
import { getIdAndVersion, areSameExtensions, getGalleryExtensionId, getExtensionId } from '@codingame/monaco-vscode-api/vscode/vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionType, EXTENSION_CATEGORIES } from '@codingame/monaco-vscode-api/vscode/vs/platform/extensions/common/extensions';
import { IProductService } from '@codingame/monaco-vscode-api/vscode/vs/platform/product/common/productService.service';
const notFound = id => ( localize(1898, "Extension '{0}' not found.", id));
const useId = ( localize(
1899,
"Make sure you use the full extension ID, including the publisher, e.g.: {0}",
"ms-dotnettools.csharp"
));
let ExtensionManagementCLI = class ExtensionManagementCLI {
constructor(
extensionsForceVersionByQuality,
logger,
extensionManagementService,
extensionGalleryService,
productService
) {
this.extensionsForceVersionByQuality = extensionsForceVersionByQuality;
this.logger = logger;
this.extensionManagementService = extensionManagementService;
this.extensionGalleryService = extensionGalleryService;
this.productService = productService;
this.extensionsForceVersionByQuality = ( this.extensionsForceVersionByQuality.map(e => e.toLowerCase()));
}
get location() {
return undefined;
}
async listExtensions(showVersions, category, profileLocation) {
let extensions = await this.extensionManagementService.getInstalled(ExtensionType.User, profileLocation);
const categories = ( EXTENSION_CATEGORIES.map(c => c.toLowerCase()));
if (category && category !== "") {
if (categories.indexOf(category.toLowerCase()) < 0) {
this.logger.info(
"Invalid category please enter a valid category. To list valid categories run --category without a category specified"
);
return;
}
extensions = extensions.filter(e => {
if (e.manifest.categories) {
const lowerCaseCategories = ( e.manifest.categories.map(c => c.toLowerCase()));
return lowerCaseCategories.indexOf(category.toLowerCase()) > -1;
}
return false;
});
} else if (category === "") {
this.logger.info("Possible Categories: ");
categories.forEach(category => {
this.logger.info(category);
});
return;
}
if (this.location) {
this.logger.info(( localize(1900, "Extensions installed on {0}:", this.location)));
}
extensions = extensions.sort((e1, e2) => e1.identifier.id.localeCompare(e2.identifier.id));
let lastId = undefined;
for (const extension of extensions) {
if (lastId !== extension.identifier.id) {
lastId = extension.identifier.id;
this.logger.info(showVersions ? `${lastId}@${extension.manifest.version}` : lastId);
}
}
}
async installExtensions(extensions, builtinExtensions, installOptions, force) {
const failed = [];
try {
if (extensions.length) {
this.logger.info(this.location ? ( localize(1901, "Installing extensions on {0}...", this.location)) : ( localize(1902, "Installing extensions...")));
}
const installVSIXInfos = [];
const installExtensionInfos = [];
const addInstallExtensionInfo = (id, version, isBuiltin) => {
if (this.extensionsForceVersionByQuality?.some(e => e === id.toLowerCase())) {
version = this.productService.quality !== "stable" ? "prerelease" : undefined;
}
installExtensionInfos.push({
id,
version: version !== "prerelease" ? version : undefined,
installOptions: {
...installOptions,
isBuiltin,
installPreReleaseVersion: version === "prerelease" || installOptions.installPreReleaseVersion
}
});
};
for (const extension of extensions) {
if (extension instanceof URI) {
installVSIXInfos.push({
vsix: extension,
installOptions
});
} else {
const [id, version] = getIdAndVersion(extension);
addInstallExtensionInfo(id, version, false);
}
}
for (const extension of builtinExtensions) {
if (extension instanceof URI) {
installVSIXInfos.push({
vsix: extension,
installOptions: {
...installOptions,
isBuiltin: true,
donotIncludePackAndDependencies: true
}
});
} else {
const [id, version] = getIdAndVersion(extension);
addInstallExtensionInfo(id, version, true);
}
}
const installed = await this.extensionManagementService.getInstalled(undefined, installOptions.profileLocation);
if (installVSIXInfos.length) {
await Promise.all(( installVSIXInfos.map(async (
{
vsix,
installOptions
}
) => {
try {
await this.installVSIX(vsix, installOptions, force, installed);
} catch (err) {
this.logger.error(err);
failed.push(( vsix.toString()));
}
})));
}
if (installExtensionInfos.length) {
const failedGalleryExtensions = await this.installGalleryExtensions(installExtensionInfos, installed, force);
failed.push(...failedGalleryExtensions);
}
} catch (error) {
this.logger.error(( localize(1903, "Error while installing extensions: {0}", getErrorMessage(error))));
throw error;
}
if (failed.length) {
throw ( new Error(( localize(1904, "Failed Installing Extensions: {0}", failed.join(", ")))));
}
}
async updateExtensions(profileLocation) {
const installedExtensions = await this.extensionManagementService.getInstalled(ExtensionType.User, profileLocation);
const installedExtensionsQuery = [];
for (const extension of installedExtensions) {
if (!!extension.identifier.uuid) {
installedExtensionsQuery.push({
...extension.identifier,
preRelease: extension.preRelease
});
}
}
this.logger.trace(( localize(
1905,
"Fetching latest versions for {0} extensions",
installedExtensionsQuery.length
)));
const availableVersions = await this.extensionGalleryService.getExtensions(installedExtensionsQuery, {
compatible: true
}, CancellationToken.None);
const extensionsToUpdate = [];
for (const newVersion of availableVersions) {
for (const oldVersion of installedExtensions) {
if (areSameExtensions(oldVersion.identifier, newVersion.identifier) && semverExports.gt(newVersion.version, oldVersion.manifest.version)) {
extensionsToUpdate.push({
extension: newVersion,
options: {
operation: InstallOperation.Update,
installPreReleaseVersion: oldVersion.preRelease,
profileLocation,
isApplicationScoped: oldVersion.isApplicationScoped
}
});
}
}
}
if (!extensionsToUpdate.length) {
this.logger.info(( localize(1906, "No extension to update")));
return;
}
this.logger.info(( localize(
1907,
"Updating extensions: {0}",
( extensionsToUpdate.map(ext => ext.extension.identifier.id)).join(", ")
)));
const installationResult = await this.extensionManagementService.installGalleryExtensions(extensionsToUpdate);
for (const extensionResult of installationResult) {
if (extensionResult.error) {
this.logger.error(( localize(
1908,
"Error while updating extension {0}: {1}",
extensionResult.identifier.id,
getErrorMessage(extensionResult.error)
)));
} else {
this.logger.info(( localize(
1909,
"Extension '{0}' v{1} was successfully updated.",
extensionResult.identifier.id,
extensionResult.local?.manifest.version
)));
}
}
}
async installGalleryExtensions(installExtensionInfos, installed, force) {
installExtensionInfos = installExtensionInfos.filter(installExtensionInfo => {
const {
id,
version,
installOptions
} = installExtensionInfo;
const installedExtension = installed.find(i => areSameExtensions(i.identifier, {
id
}));
if (installedExtension) {
const builtinAutoUpdateMessage = this.validateBuiltinExtensionEnabledWithAutoUpdates(installedExtension);
if (builtinAutoUpdateMessage) {
this.logger.info(builtinAutoUpdateMessage);
return false;
}
if (!force && (!version || (version === "prerelease" && installedExtension.preRelease))) {
this.logger.info(( localize(
1910,
"Extension '{0}' v{1} is already installed. Use '--force' option to update to latest version or provide '@<version>' to install a specific version, for example: '{2}@1.2.3'.",
id,
installedExtension.manifest.version,
id
)));
return false;
}
if (version && installedExtension.manifest.version === version) {
this.logger.info(( localize(1911, "Extension '{0}' is already installed.", `${id}@${version}`)));
return false;
}
if (installedExtension.preRelease && version !== "prerelease") {
installOptions.preRelease = false;
}
}
return true;
});
if (!installExtensionInfos.length) {
return [];
}
const failed = [];
const extensionsToInstall = [];
const galleryExtensions = await this.getGalleryExtensions(installExtensionInfos);
await Promise.all(( installExtensionInfos.map(async (
{
id,
version,
installOptions
}
) => {
const gallery = galleryExtensions.get(id.toLowerCase());
if (!gallery) {
this.logger.error(`${notFound(version ? `${id}@${version}` : id)}\n${useId}`);
failed.push(id);
return;
}
try {
const manifest = await this.extensionGalleryService.getManifest(gallery, CancellationToken.None);
if (manifest && !this.validateExtensionKind(manifest)) {
return;
}
} catch (err) {
this.logger.error(err.message || err.stack || err);
failed.push(id);
return;
}
const installedExtension = installed.find(e => areSameExtensions(e.identifier, gallery.identifier));
if (installedExtension) {
if (gallery.version === installedExtension.manifest.version) {
this.logger.info(( localize(
1911,
"Extension '{0}' is already installed.",
version ? `${id}@${version}` : id
)));
return;
}
this.logger.info(( localize(
1912,
"Updating the extension '{0}' to the version {1}",
id,
gallery.version
)));
}
if (installOptions.isBuiltin) {
this.logger.info(version ? ( localize(1913, "Installing builtin extension '{0}' v{1}...", id, version)) : ( localize(1914, "Installing builtin extension '{0}'...", id)));
} else {
this.logger.info(version ? ( localize(1915, "Installing extension '{0}' v{1}...", id, version)) : ( localize(1916, "Installing extension '{0}'...", id)));
}
extensionsToInstall.push({
extension: gallery,
options: {
...installOptions,
installGivenVersion: !!version,
isApplicationScoped: installOptions.isApplicationScoped || installedExtension?.isApplicationScoped
}
});
})));
if (extensionsToInstall.length) {
const installationResult = await this.extensionManagementService.installGalleryExtensions(extensionsToInstall);
for (const extensionResult of installationResult) {
if (extensionResult.error) {
this.logger.error(( localize(
1917,
"Error while installing extension {0}: {1}",
extensionResult.identifier.id,
getErrorMessage(extensionResult.error)
)));
failed.push(extensionResult.identifier.id);
} else {
this.logger.info(( localize(
1918,
"Extension '{0}' v{1} was successfully installed.",
extensionResult.identifier.id,
extensionResult.local?.manifest.version
)));
}
}
}
return failed;
}
async installVSIX(vsix, installOptions, force, installedExtensions) {
const manifest = await this.extensionManagementService.getManifest(vsix);
if (!manifest) {
throw ( new Error("Invalid vsix"));
}
const valid = await this.validateVSIX(manifest, force, installOptions.profileLocation, installedExtensions);
if (valid) {
try {
await this.extensionManagementService.install(vsix, {
...installOptions,
installGivenVersion: true
});
this.logger.info(( localize(1919, "Extension '{0}' was successfully installed.", basename(vsix))));
} catch (error) {
if (isCancellationError(error)) {
this.logger.info(( localize(1920, "Cancelled installing extension '{0}'.", basename(vsix))));
} else {
throw error;
}
}
}
}
async getGalleryExtensions(extensions) {
const galleryExtensions = ( new Map());
const preRelease = ( extensions.some(e => e.installOptions.installPreReleaseVersion));
const targetPlatform = await this.extensionManagementService.getTargetPlatform();
const extensionInfos = [];
for (const extension of extensions) {
if (EXTENSION_IDENTIFIER_REGEX.test(extension.id)) {
extensionInfos.push({
...extension,
preRelease
});
}
}
if (extensionInfos.length) {
const result = await this.extensionGalleryService.getExtensions(extensionInfos, {
targetPlatform
}, CancellationToken.None);
for (const extension of result) {
galleryExtensions.set(extension.identifier.id.toLowerCase(), extension);
}
}
return galleryExtensions;
}
validateExtensionKind(_manifest) {
return true;
}
async validateVSIX(manifest, force, profileLocation, installedExtensions) {
const extensionIdentifier = {
id: getGalleryExtensionId(manifest.publisher, manifest.name)
};
const existingExtension = installedExtensions.find(local => areSameExtensions(extensionIdentifier, local.identifier));
if (existingExtension) {
const builtinAutoUpdateMessage = this.validateBuiltinExtensionEnabledWithAutoUpdates(existingExtension);
if (builtinAutoUpdateMessage) {
this.logger.info(builtinAutoUpdateMessage);
return false;
}
if (!force) {
if (semverExports.gt(existingExtension.manifest.version, manifest.version)) {
this.logger.info(( localize(
1921,
"A newer version of extension '{0}' v{1} is already installed. Use '--force' option to downgrade to older version.",
existingExtension.identifier.id,
existingExtension.manifest.version,
manifest.version
)));
return false;
}
}
}
return this.validateExtensionKind(manifest);
}
async uninstallExtensions(extensions, force, profileLocation) {
const getId = async extensionDescription => {
if (extensionDescription instanceof URI) {
const manifest = await this.extensionManagementService.getManifest(extensionDescription);
return getExtensionId(manifest.publisher, manifest.name);
}
return extensionDescription;
};
for (const extension of extensions) {
const id = await getId(extension);
const installed = await this.extensionManagementService.getInstalled(undefined, profileLocation);
const extensionsToUninstall = installed.filter(e => areSameExtensions(e.identifier, {
id
}));
if (!extensionsToUninstall.length) {
throw ( new Error(`${this.notInstalled(id)}\n${useId}`));
}
if (( extensionsToUninstall.some(e => e.type === ExtensionType.System))) {
this.logger.info(( localize(
1922,
"Extension '{0}' is a Built-in extension and cannot be uninstalled",
id
)));
return;
}
if (!force && ( extensionsToUninstall.some(e => e.isBuiltin))) {
this.logger.info(( localize(
1923,
"Extension '{0}' is marked as a Built-in extension by user. Please use '--force' option to uninstall it.",
id
)));
return;
}
this.logger.info(( localize(1924, "Uninstalling {0}...", id)));
for (const extensionToUninstall of extensionsToUninstall) {
await this.extensionManagementService.uninstall(extensionToUninstall, {
profileLocation
});
}
if (this.location) {
this.logger.info(( localize(
1925,
"Extension '{0}' was successfully uninstalled from {1}!",
id,
this.location
)));
} else {
this.logger.info(( localize(1926, "Extension '{0}' was successfully uninstalled!", id)));
}
}
}
async locateExtension(extensions) {
const installed = await this.extensionManagementService.getInstalled();
extensions.forEach(e => {
installed.forEach(i => {
if (i.identifier.id === e) {
if (i.location.scheme === Schemas.file) {
this.logger.info(i.location.fsPath);
return;
}
}
});
});
}
notInstalled(id) {
return this.location ? ( localize(1927, "Extension '{0}' is not installed on {1}.", id, this.location)) : ( localize(1928, "Extension '{0}' is not installed.", id));
}
validateBuiltinExtensionEnabledWithAutoUpdates(extension) {
if (extension.isBuiltin && ( this.productService.builtInExtensionsEnabledWithAutoUpdates.some(e => e.toLowerCase() === extension.identifier.id.toLowerCase())) && !extension.forceAutoUpdate) {
return localize(
1929,
"Extension '{0}' is a built-in extension and not allowed to be updated in the current product quality '{1}'.",
extension.identifier.id,
this.productService.quality
);
}
return undefined;
}
};
ExtensionManagementCLI = ( __decorate([( __param(2, IExtensionManagementService)), ( __param(3, IExtensionGalleryService)), ( __param(4, IProductService))], ExtensionManagementCLI));
export { ExtensionManagementCLI };