diff --git a/src/vs/workbench/api/node/extHostExtensionActivator.ts b/src/vs/workbench/api/node/extHostExtensionActivator.ts index c37e7b2dfe7..5a2f21ebdee 100644 --- a/src/vs/workbench/api/node/extHostExtensionActivator.ts +++ b/src/vs/workbench/api/node/extHostExtensionActivator.ts @@ -237,35 +237,39 @@ export class ExtensionsActivator { * semantics: `redExtensions` must wait for `greenExtensions`. */ private _handleActivateRequest(currentExtension: IExtensionDescription, greenExtensions: { [id: string]: IExtensionDescription; }, redExtensions: IExtensionDescription[]): void { - let depIds = (typeof currentExtension.extensionDependencies === 'undefined' ? [] : currentExtension.extensionDependencies); + let dependencies = (typeof currentExtension.extensionDependencies === 'undefined' ? [] : currentExtension.extensionDependencies); let currentExtensionGetsGreenLight = true; - for (let j = 0, lenJ = depIds.length; j < lenJ; j++) { - let depId = depIds[j]; - let depDesc = this._registry.getExtensionDescription(depId); + 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; - 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 (depId) { + let depDesc = this._registry.getExtensionDescription(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; + 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; } - } else { - // must first wait for the dependency to activate - currentExtensionGetsGreenLight = false; - greenExtensions[depId] = depDesc; + + 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; + } } } diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index d8bfa847f36..24e6771a154 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?: string[]; + readonly extensionDependencies?: Array; 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 90cfe42920d..71de9bb715a 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._isStringArray(extensionDescription.extensionDependencies)) { - notices.push(nls.localize('extensionDescription.extensionDependencies', "property `{0}` can be omitted or must be of type `string[]`", 'extensionDependencies')); + 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')); return false; } } @@ -433,6 +433,26 @@ 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 {