diff options
Diffstat (limited to 'src/vs/platform/extensions/common/extensionValidator.ts')
-rw-r--r-- | src/vs/platform/extensions/common/extensionValidator.ts | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/src/vs/platform/extensions/common/extensionValidator.ts b/src/vs/platform/extensions/common/extensionValidator.ts index dfeea91105f..c36ca616ae2 100644 --- a/src/vs/platform/extensions/common/extensionValidator.ts +++ b/src/vs/platform/extensions/common/extensionValidator.ts @@ -3,7 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { isEqualOrParent, joinPath } from 'vs/base/common/resources'; +import Severity from 'vs/base/common/severity'; +import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; +import * as semver from 'vs/base/common/semver/semver'; import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; export interface IParsedVersion { @@ -235,6 +239,98 @@ export function isValidVersion(_inputVersion: string | INormalizedVersion, _inpu type ProductDate = string | Date | undefined; +export function validateExtensionManifest(productVersion: string, productDate: ProductDate, extensionLocation: URI, extensionManifest: IExtensionManifest, extensionIsBuiltin: boolean): readonly [Severity, string][] { + const validations: [Severity, string][] = []; + if (typeof extensionManifest.publisher !== 'undefined' && typeof extensionManifest.publisher !== 'string') { + validations.push([Severity.Error, nls.localize('extensionDescription.publisher', "property publisher must be of type `string`.")]); + return validations; + } + if (typeof extensionManifest.name !== 'string') { + validations.push([Severity.Error, nls.localize('extensionDescription.name', "property `{0}` is mandatory and must be of type `string`", 'name')]); + return validations; + } + if (typeof extensionManifest.version !== 'string') { + validations.push([Severity.Error, nls.localize('extensionDescription.version', "property `{0}` is mandatory and must be of type `string`", 'version')]); + return validations; + } + if (!extensionManifest.engines) { + validations.push([Severity.Error, nls.localize('extensionDescription.engines', "property `{0}` is mandatory and must be of type `object`", 'engines')]); + return validations; + } + if (typeof extensionManifest.engines.vscode !== 'string') { + validations.push([Severity.Error, nls.localize('extensionDescription.engines.vscode', "property `{0}` is mandatory and must be of type `string`", 'engines.vscode')]); + return validations; + } + if (typeof extensionManifest.extensionDependencies !== 'undefined') { + if (!isStringArray(extensionManifest.extensionDependencies)) { + validations.push([Severity.Error, nls.localize('extensionDescription.extensionDependencies', "property `{0}` can be omitted or must be of type `string[]`", 'extensionDependencies')]); + return validations; + } + } + if (typeof extensionManifest.activationEvents !== 'undefined') { + if (!isStringArray(extensionManifest.activationEvents)) { + validations.push([Severity.Error, nls.localize('extensionDescription.activationEvents1', "property `{0}` can be omitted or must be of type `string[]`", 'activationEvents')]); + return validations; + } + if (typeof extensionManifest.main === 'undefined' && typeof extensionManifest.browser === 'undefined') { + validations.push([Severity.Error, nls.localize('extensionDescription.activationEvents2', "properties `{0}` and `{1}` must both be specified or must both be omitted", 'activationEvents', 'main')]); + return validations; + } + } + if (typeof extensionManifest.extensionKind !== 'undefined') { + if (typeof extensionManifest.main === 'undefined') { + validations.push([Severity.Warning, nls.localize('extensionDescription.extensionKind', "property `{0}` can be defined only if property `main` is also defined.", 'extensionKind')]); + // not a failure case + } + } + if (typeof extensionManifest.main !== 'undefined') { + if (typeof extensionManifest.main !== 'string') { + validations.push([Severity.Error, nls.localize('extensionDescription.main1', "property `{0}` can be omitted or must be of type `string`", 'main')]); + return validations; + } else { + const mainLocation = joinPath(extensionLocation, extensionManifest.main); + if (!isEqualOrParent(mainLocation, extensionLocation)) { + validations.push([Severity.Warning, nls.localize('extensionDescription.main2', "Expected `main` ({0}) to be included inside extension's folder ({1}). This might make the extension non-portable.", mainLocation.path, extensionLocation.path)]); + // not a failure case + } + } + if (typeof extensionManifest.activationEvents === 'undefined') { + validations.push([Severity.Error, nls.localize('extensionDescription.main3', "properties `{0}` and `{1}` must both be specified or must both be omitted", 'activationEvents', 'main')]); + return validations; + } + } + if (typeof extensionManifest.browser !== 'undefined') { + if (typeof extensionManifest.browser !== 'string') { + validations.push([Severity.Error, nls.localize('extensionDescription.browser1', "property `{0}` can be omitted or must be of type `string`", 'browser')]); + return validations; + } else { + const browserLocation = joinPath(extensionLocation, extensionManifest.browser); + if (!isEqualOrParent(browserLocation, extensionLocation)) { + validations.push([Severity.Warning, nls.localize('extensionDescription.browser2', "Expected `browser` ({0}) to be included inside extension's folder ({1}). This might make the extension non-portable.", browserLocation.path, extensionLocation.path)]); + // not a failure case + } + } + if (typeof extensionManifest.activationEvents === 'undefined') { + validations.push([Severity.Error, nls.localize('extensionDescription.browser3', "properties `{0}` and `{1}` must both be specified or must both be omitted", 'activationEvents', 'browser')]); + return validations; + } + } + + if (!semver.valid(extensionManifest.version)) { + validations.push([Severity.Error, nls.localize('notSemver', "Extension version is not semver compatible.")]); + return validations; + } + + const notices: string[] = []; + const isValid = isValidExtensionVersion(productVersion, productDate, extensionManifest, extensionIsBuiltin, notices); + if (!isValid) { + for (const notice of notices) { + validations.push([Severity.Error, notice]); + } + } + return validations; +} + export function isValidExtensionVersion(productVersion: string, productDate: ProductDate, extensionManifest: IExtensionManifest, extensionIsBuiltin: boolean, notices: string[]): boolean { if (extensionIsBuiltin || (typeof extensionManifest.main === 'undefined' && typeof extensionManifest.browser === 'undefined')) { @@ -282,3 +378,15 @@ function isVersionValid(currentVersion: string, date: ProductDate, requestedVers return true; } + +function isStringArray(arr: string[]): boolean { + if (!Array.isArray(arr)) { + return false; + } + for (let i = 0, len = arr.length; i < len; i++) { + if (typeof arr[i] !== 'string') { + return false; + } + } + return true; +} |