diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index a01e9bc2db5819c9bcdd58778be4fbe9ca816e81..5c5f51c984e5148ab6b71247b94e50eb6e5cbd40 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -123,7 +123,7 @@ export interface IExtensionManifest { icon?: string; categories?: string[]; activationEvents?: string[]; - extensionDependencies?: IExtensionDependency[]; + extensionDependencies?: string[]; contributes?: IExtensionContributions; repository?: { url: string; @@ -133,13 +133,8 @@ export interface IExtensionManifest { }; } -export interface IExtensionDependency { - id: string; - optional: boolean; -} - export interface IGalleryExtensionProperties { - dependencies?: IExtensionDependency[]; + dependencies?: string[]; engine?: string; } diff --git a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts index 7c12f97c4d762562e1d25263e27e1037929f107e..87c15f9be41cd5ee12255df84e5fa93421e7bf77 100644 --- a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts @@ -9,7 +9,7 @@ import * as path from 'path'; import { TPromise } from 'vs/base/common/winjs.base'; import { distinct } from 'vs/base/common/arrays'; import { getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors'; -import { StatisticType, IGalleryExtension, IExtensionGalleryService, IGalleryExtensionAsset, IQueryOptions, SortBy, SortOrder, IExtensionManifest, IExtensionIdentifier, IReportedExtension, InstallOperation, ITranslation, IExtensionDependency } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { StatisticType, IGalleryExtension, IExtensionGalleryService, IGalleryExtensionAsset, IQueryOptions, SortBy, SortOrder, IExtensionManifest, IExtensionIdentifier, IReportedExtension, InstallOperation, ITranslation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getGalleryExtensionId, getGalleryExtensionTelemetryData, adoptToGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { assign, getOrDefault } from 'vs/base/common/objects'; import { IRequestService } from 'vs/platform/request/node/request'; @@ -262,18 +262,10 @@ function getVersionAsset(version: IRawGalleryExtensionVersion, type: string): IG }; } -function getDependencies(version: IRawGalleryExtensionVersion): IExtensionDependency[] { +function getDependencies(version: IRawGalleryExtensionVersion): string[] { const values = version.properties ? version.properties.filter(p => p.key === PropertyType.Dependency) : []; const value = values.length > 0 && values[0].value; - return value ? value.split(',').map(v => { - let dependency: IExtensionDependency; - try { - dependency = JSON.parse(v); - } catch (e) { - dependency = { id: v, optional: false }; - } - return { id: adoptToGalleryExtensionId(dependency.id), optional: dependency.optional }; - }) : []; + return value ? value.split(',').map(v => adoptToGalleryExtensionId(v)) : []; } function getEngine(version: IRawGalleryExtensionVersion): string { @@ -629,7 +621,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { const dependenciesSet = new Set(); for (const dep of loadedDependencies) { if (dep.properties.dependencies) { - dep.properties.dependencies.forEach(d => dependenciesSet.add(d.id)); + dep.properties.dependencies.forEach(d => dependenciesSet.add(d)); } } result = distinct(result.concat(loadedDependencies), d => d.identifier.uuid); diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 25d0fa52915a2f69f573e41b62aceb7384f5940e..0350a30e4c0692da683c4724649f591cc3de58f3 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -21,8 +21,7 @@ import { StatisticType, IExtensionIdentifier, IReportedExtension, - InstallOperation, - IExtensionDependency + InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getGalleryExtensionIdFromLocal, adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId, groupByExtension, getMaliciousExtensionsSet, getLocalExtensionId, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, getIdFromLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { localizeManifest } from '../common/extensionNls'; @@ -383,13 +382,13 @@ export class ExtensionManagementService extends Disposable implements IExtension return errors.length ? TPromise.wrapError(this.joinErrors(errors)) : TPromise.as(null); } - private getDependenciesToInstall(dependencies: IExtensionDependency[]): TPromise { + private getDependenciesToInstall(dependencies: string[]): TPromise { if (dependencies.length) { return this.getInstalled() .then(installed => { - const uninstalledDeps = dependencies.filter(d => installed.every(i => !areSameExtensions(i.galleryIdentifier, { id: d.id }))); + const uninstalledDeps = dependencies.filter(id => installed.every(i => !areSameExtensions(i.galleryIdentifier, { id }))); if (uninstalledDeps.length) { - return this.galleryService.loadAllDependencies(uninstalledDeps.map(dep => ({ id: dep.id }))) + return this.galleryService.loadAllDependencies(uninstalledDeps.map(id => ({ id }))) .then(allDependencies => allDependencies.filter(d => { const extensionId = getLocalExtensionIdFromGallery(d, d.version); return installed.every(({ identifier }) => identifier.id !== extensionId); @@ -569,7 +568,7 @@ export class ExtensionManagementService extends Disposable implements IExtension private hasDependencies(extension: ILocalExtension, installed: ILocalExtension[]): boolean { if (extension.manifest.extensionDependencies && extension.manifest.extensionDependencies.length) { - return installed.some(i => extension.manifest.extensionDependencies.some(dep => areSameExtensions({ id: dep.id }, i.galleryIdentifier))); + return installed.some(i => extension.manifest.extensionDependencies.some(dep => areSameExtensions({ id: dep }, i.galleryIdentifier))); } return false; } @@ -647,7 +646,7 @@ export class ExtensionManagementService extends Disposable implements IExtension if (!extension.manifest.extensionDependencies || extension.manifest.extensionDependencies.length === 0) { return []; } - const dependenciesToUninstall = installed.filter(i => extension.manifest.extensionDependencies.some(dep => areSameExtensions({ id: dep.id }, i.galleryIdentifier))); + const dependenciesToUninstall = installed.filter(i => extension.manifest.extensionDependencies.some(id => areSameExtensions({ id }, i.galleryIdentifier))); const depsOfDeps = []; for (const dep of dependenciesToUninstall) { depsOfDeps.push(...this.getAllDependenciesToUninstall(dep, installed, checked)); @@ -656,7 +655,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } private getMandatoryDependents(extension: ILocalExtension, installed: ILocalExtension[]): ILocalExtension[] { - return installed.filter(e => e.manifest.extensionDependencies && e.manifest.extensionDependencies.some(dep => !dep.optional && areSameExtensions({ id: dep.id }, extension.galleryIdentifier))); + return installed.filter(e => e.manifest.extensionDependencies && e.manifest.extensionDependencies.some(id => areSameExtensions({ id }, extension.galleryIdentifier))); } private doUninstall(extension: ILocalExtension): TPromise { @@ -762,7 +761,7 @@ export class ExtensionManagementService extends Disposable implements IExtension const readmeUrl = readme ? URI.file(path.join(extensionPath, readme)).toString() : null; const changelog = children.filter(child => /^changelog(\.txt|\.md|)$/i.test(child))[0]; const changelogUrl = changelog ? URI.file(path.join(extensionPath, changelog)).toString() : null; - manifest.extensionDependencies = manifest.extensionDependencies ? manifest.extensionDependencies.map(dep => typeof dep === 'string' ? { id: adoptToGalleryExtensionId(dep), optional: false } : { id: adoptToGalleryExtensionId(dep.id), optional: dep.optional }) : []; + manifest.extensionDependencies = manifest.extensionDependencies ? manifest.extensionDependencies = manifest.extensionDependencies.map(id => adoptToGalleryExtensionId(id)) : []; const identifier = { id: type === LocalExtensionType.System ? folderName : getLocalExtensionIdFromManifest(manifest), uuid: metadata ? metadata.id : null }; const galleryIdentifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: identifier.uuid }; return { type, identifier, galleryIdentifier, manifest, metadata, location: URI.file(extensionPath), readmeUrl, changelogUrl }; diff --git a/src/vs/workbench/api/node/extHostExtensionActivator.ts b/src/vs/workbench/api/node/extHostExtensionActivator.ts index 5a2f21ebdee7ed1342d8c71593abf3f61e926183..c37e7b2dfe759c1592736d87f1826a0a960372d3 100644 --- a/src/vs/workbench/api/node/extHostExtensionActivator.ts +++ b/src/vs/workbench/api/node/extHostExtensionActivator.ts @@ -237,39 +237,35 @@ export class ExtensionsActivator { * semantics: `redExtensions` must wait for `greenExtensions`. */ private _handleActivateRequest(currentExtension: IExtensionDescription, greenExtensions: { [id: string]: IExtensionDescription; }, redExtensions: IExtensionDescription[]): void { - let dependencies = (typeof currentExtension.extensionDependencies === 'undefined' ? [] : currentExtension.extensionDependencies); + let depIds = (typeof currentExtension.extensionDependencies === 'undefined' ? [] : currentExtension.extensionDependencies); let currentExtensionGetsGreenLight = true; - for (let j = 0, lenJ = dependencies.length; j < lenJ; j++) { - const dependency = dependencies[j]; - const depId: string = typeof dependency === 'string' ? dependency : dependency.optional ? null : dependency.id; + for (let j = 0, lenJ = depIds.length; j < lenJ; j++) { + let depId = depIds[j]; + let depDesc = this._registry.getExtensionDescription(depId); - if (depId) { - let depDesc = this._registry.getExtensionDescription(depId); + if (!depDesc) { + // Error condition 1: unknown dependency + this._host.showMessage(Severity.Error, nls.localize('unknownDep', "Extension '{1}' failed to activate. Reason: unknown dependency '{0}'.", depId, currentExtension.id)); + const error = new Error(`Unknown dependency '${depId}'`); + this._activatedExtensions[currentExtension.id] = new FailedExtension(error); + return; + } - if (!depDesc) { - // Error condition 1: unknown dependency - this._host.showMessage(Severity.Error, nls.localize('unknownDep', "Extension '{1}' failed to activate. Reason: unknown dependency '{0}'.", depId, currentExtension.id)); - const error = new Error(`Unknown dependency '${depId}'`); + if (hasOwnProperty.call(this._activatedExtensions, depId)) { + let dep = this._activatedExtensions[depId]; + if (dep.activationFailed) { + // Error condition 2: a dependency has already failed activation + this._host.showMessage(Severity.Error, nls.localize('failedDep1', "Extension '{1}' failed to activate. Reason: dependency '{0}' failed to activate.", depId, currentExtension.id)); + const error = new Error(`Dependency ${depId} failed to activate`); + (error).detail = dep.activationFailedError; this._activatedExtensions[currentExtension.id] = new FailedExtension(error); return; } - - if (hasOwnProperty.call(this._activatedExtensions, depId)) { - let dep = this._activatedExtensions[depId]; - if (dep.activationFailed) { - // Error condition 2: a dependency has already failed activation - this._host.showMessage(Severity.Error, nls.localize('failedDep1', "Extension '{1}' failed to activate. Reason: dependency '{0}' failed to activate.", depId, currentExtension.id)); - const error = new Error(`Dependency ${depId} failed to activate`); - (error).detail = dep.activationFailedError; - this._activatedExtensions[currentExtension.id] = new FailedExtension(error); - return; - } - } else { - // must first wait for the dependency to activate - currentExtensionGetsGreenLight = false; - greenExtensions[depId] = depDesc; - } + } else { + // must first wait for the dependency to activate + currentExtensionGetsGreenLight = false; + greenExtensions[depId] = depDesc; } } diff --git a/src/vs/workbench/parts/extensions/common/extensions.ts b/src/vs/workbench/parts/extensions/common/extensions.ts index 7639cd21482452bbe08b4bace52091c7b4f13b7e..64b3a90a7b2b14d4f98d64437187cd7f9278d694 100644 --- a/src/vs/workbench/parts/extensions/common/extensions.ts +++ b/src/vs/workbench/parts/extensions/common/extensions.ts @@ -8,7 +8,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { Event } from 'vs/base/common/event'; import { TPromise } from 'vs/base/common/winjs.base'; import { IPager } from 'vs/base/common/paging'; -import { IQueryOptions, IExtensionManifest, LocalExtensionType, EnablementState, ILocalExtension, IGalleryExtension, ExtensionRecommendationSource, IExtensionDependency } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IQueryOptions, IExtensionManifest, LocalExtensionType, EnablementState, ILocalExtension, IGalleryExtension, ExtensionRecommendationSource } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IViewContainersRegistry, ViewContainer, Extensions as ViewContainerExtensions } from 'vs/workbench/common/views'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -48,7 +48,7 @@ export interface IExtension { ratingCount: number; outdated: boolean; enablementState: EnablementState; - dependencies: IExtensionDependency[]; + dependencies: string[]; telemetryData: any; preview: boolean; getManifest(): TPromise; diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index 5c86ac1d497b525db303039bb7c44940fc9027be..de6b67f6a5daf7145da0ffa8b14398da2c1afc0b 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -18,7 +18,7 @@ import { IPager, mapPager, singlePagePager } from 'vs/base/common/paging'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions, IExtensionManifest, - InstallExtensionEvent, DidInstallExtensionEvent, LocalExtensionType, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, IExtensionTipsService, ExtensionRecommendationSource, IExtensionRecommendation, IExtensionManagementServerService, IExtensionDependency + InstallExtensionEvent, DidInstallExtensionEvent, LocalExtensionType, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, IExtensionTipsService, ExtensionRecommendationSource, IExtensionRecommendation, IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getGalleryExtensionIdFromLocal, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, areSameExtensions, getMaliciousExtensionsSet } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -262,7 +262,7 @@ ${this.description} return TPromise.wrapError(new Error('not available')); } - get dependencies(): IExtensionDependency[] { + get dependencies(): string[] { const { local, gallery } = this; if (gallery && !this.isGalleryOutdated()) { return gallery.properties.dependencies; @@ -303,7 +303,7 @@ class ExtensionDependencies implements IExtensionDependencies { if (!this.hasDependencies) { return []; } - return this._extension.dependencies.map(d => new ExtensionDependencies(this._map.get(d.id), d.id, this._map, this)); + return this._extension.dependencies.map(id => new ExtensionDependencies(this._map.get(id), id, this._map, this)); } private computeHasDependencies(): boolean { @@ -439,7 +439,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, .then(([allRecommendations, report]) => { const maliciousSet = getMaliciousExtensionsSet(report); - return this.galleryService.loadAllDependencies((extension).dependencies.map(dep => { id: dep.id })) + return this.galleryService.loadAllDependencies((extension).dependencies.map(id => ({ id }))) .then(galleryExtensions => galleryExtensions.map(galleryExtension => this.fromGallery(galleryExtension, maliciousSet, allRecommendations))) .then(extensions => [...this.local, ...extensions]) .then(extensions => { @@ -774,7 +774,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, if (i.enablementState === enablementState) { return false; } - return i.type === LocalExtensionType.User && extensions.some(extension => extension.dependencies.some(dep => areSameExtensions({ id: dep.id }, i))); + return i.type === LocalExtensionType.User && extensions.some(extension => extension.dependencies.some(id => areSameExtensions({ id }, i))); }); if (dependenciesToDisable.length) { const depsOfDeps = this.getDependenciesRecursively(dependenciesToDisable, installed, enablementState, checked); @@ -798,13 +798,13 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, if (extensionsToDisable.indexOf(i) !== -1) { return false; } - return i.dependencies.some(dep => !dep.optional && [extension, ...extensionsToDisable].some(d => d.id === dep.id)); + return i.dependencies.some(dep => [extension, ...extensionsToDisable].some(d => d.id === dep)); }); } private getDependentsErrorMessage(extension: IExtension, allDisabledExtensions: IExtension[], dependents: IExtension[]): string { for (const e of [extension, ...allDisabledExtensions]) { - let dependentsOfTheExtension = dependents.filter(d => d.dependencies.some(dep => areSameExtensions({ id: dep.id }, e))); + let dependentsOfTheExtension = dependents.filter(d => d.dependencies.some(id => areSameExtensions({ id }, e))); if (dependentsOfTheExtension.length) { return this.getErrorMessageForDisablingAnExtensionWithDependents(e, dependentsOfTheExtension); } diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 24e6771a1549c5b2df1056209d27be6a01c0ac8c..d8bfa847f36819551915a8093d61e6b2dd4f741e 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -21,7 +21,7 @@ export interface IExtensionDescription { readonly isBuiltin: boolean; readonly isUnderDevelopment: boolean; readonly extensionLocation: URI; - readonly extensionDependencies?: Array; + readonly extensionDependencies?: string[]; readonly activationEvents?: string[]; readonly engines: { vscode: string; diff --git a/src/vs/workbench/services/extensions/node/extensionPoints.ts b/src/vs/workbench/services/extensions/node/extensionPoints.ts index 71de9bb715a4572141fae4cc0dcbf55f228a83ed..802d0be085c022e6fdffc19bb326afa19ae8f73a 100644 --- a/src/vs/workbench/services/extensions/node/extensionPoints.ts +++ b/src/vs/workbench/services/extensions/node/extensionPoints.ts @@ -387,8 +387,8 @@ class ExtensionManifestValidator extends ExtensionManifestHandler { return false; } if (typeof extensionDescription.extensionDependencies !== 'undefined') { - if (!ExtensionManifestValidator._isExtensionDependencyArray(extensionDescription.extensionDependencies)) { - notices.push(nls.localize('extensionDescription.extensionDependencies', "property `{0}` can be omitted or must be an array of type `string` or `{id: string, optional: boolean}`", 'extensionDependencies')); + if (!ExtensionManifestValidator._isStringArray(extensionDescription.extensionDependencies)) { + notices.push(nls.localize('extensionDescription.extensionDependencies', "property `{0}` can be omitted or must be of type `string[]`", 'extensionDependencies')); return false; } } @@ -433,26 +433,6 @@ class ExtensionManifestValidator extends ExtensionManifestHandler { } return true; } - - private static _isExtensionDependencyArray(arr: any[]): boolean { - if (!Array.isArray(arr)) { - return false; - } - for (let i = 0, len = arr.length; i < len; i++) { - if (typeof arr[i] !== 'string') { - if (typeof arr[i] !== 'object') { - return false; - } - if (typeof arr[i]['id'] !== 'string') { - return false; - } - if (typeof arr[i]['optional'] !== 'undefined' && typeof arr[i]['optional'] !== 'boolean') { - return false; - } - } - } - return true; - } } export class ExtensionScannerInput { @@ -635,4 +615,4 @@ export class ExtensionScanner { return []; }); } -} +} \ No newline at end of file