mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-11 10:38:31 -05:00
SQL Operations Studio Public Preview 1 (0.23) release source code
This commit is contained in:
343
src/vs/platform/extensions/node/extensionValidator.ts
Normal file
343
src/vs/platform/extensions/node/extensionValidator.ts
Normal file
@@ -0,0 +1,343 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { valid } from 'semver';
|
||||
import { join } from 'path';
|
||||
|
||||
export interface IParsedVersion {
|
||||
hasCaret: boolean;
|
||||
hasGreaterEquals: boolean;
|
||||
majorBase: number;
|
||||
majorMustEqual: boolean;
|
||||
minorBase: number;
|
||||
minorMustEqual: boolean;
|
||||
patchBase: number;
|
||||
patchMustEqual: boolean;
|
||||
preRelease: string;
|
||||
}
|
||||
|
||||
export interface INormalizedVersion {
|
||||
majorBase: number;
|
||||
majorMustEqual: boolean;
|
||||
minorBase: number;
|
||||
minorMustEqual: boolean;
|
||||
patchBase: number;
|
||||
patchMustEqual: boolean;
|
||||
isMinimum: boolean;
|
||||
}
|
||||
|
||||
const VERSION_REGEXP = /^(\^|>=)?((\d+)|x)\.((\d+)|x)\.((\d+)|x)(\-.*)?$/;
|
||||
|
||||
export function isValidVersionStr(version: string): boolean {
|
||||
version = version.trim();
|
||||
return (version === '*' || VERSION_REGEXP.test(version));
|
||||
}
|
||||
|
||||
export function parseVersion(version: string): IParsedVersion {
|
||||
if (!isValidVersionStr(version)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
version = version.trim();
|
||||
|
||||
if (version === '*') {
|
||||
return {
|
||||
hasCaret: false,
|
||||
hasGreaterEquals: false,
|
||||
majorBase: 0,
|
||||
majorMustEqual: false,
|
||||
minorBase: 0,
|
||||
minorMustEqual: false,
|
||||
patchBase: 0,
|
||||
patchMustEqual: false,
|
||||
preRelease: null
|
||||
};
|
||||
}
|
||||
|
||||
let m = version.match(VERSION_REGEXP);
|
||||
return {
|
||||
hasCaret: m[1] === '^',
|
||||
hasGreaterEquals: m[1] === '>=',
|
||||
majorBase: m[2] === 'x' ? 0 : parseInt(m[2], 10),
|
||||
majorMustEqual: (m[2] === 'x' ? false : true),
|
||||
minorBase: m[4] === 'x' ? 0 : parseInt(m[4], 10),
|
||||
minorMustEqual: (m[4] === 'x' ? false : true),
|
||||
patchBase: m[6] === 'x' ? 0 : parseInt(m[6], 10),
|
||||
patchMustEqual: (m[6] === 'x' ? false : true),
|
||||
preRelease: m[8] || null
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeVersion(version: IParsedVersion): INormalizedVersion {
|
||||
if (!version) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let majorBase = version.majorBase,
|
||||
majorMustEqual = version.majorMustEqual,
|
||||
minorBase = version.minorBase,
|
||||
minorMustEqual = version.minorMustEqual,
|
||||
patchBase = version.patchBase,
|
||||
patchMustEqual = version.patchMustEqual;
|
||||
|
||||
if (version.hasCaret) {
|
||||
if (majorBase === 0) {
|
||||
patchMustEqual = false;
|
||||
} else {
|
||||
minorMustEqual = false;
|
||||
patchMustEqual = false;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
majorBase: majorBase,
|
||||
majorMustEqual: majorMustEqual,
|
||||
minorBase: minorBase,
|
||||
minorMustEqual: minorMustEqual,
|
||||
patchBase: patchBase,
|
||||
patchMustEqual: patchMustEqual,
|
||||
isMinimum: version.hasGreaterEquals
|
||||
};
|
||||
}
|
||||
|
||||
export function isValidVersion(_version: string | INormalizedVersion, _desiredVersion: string | INormalizedVersion): boolean {
|
||||
let version: INormalizedVersion;
|
||||
if (typeof _version === 'string') {
|
||||
version = normalizeVersion(parseVersion(_version));
|
||||
} else {
|
||||
version = _version;
|
||||
}
|
||||
|
||||
let desiredVersion: INormalizedVersion;
|
||||
if (typeof _desiredVersion === 'string') {
|
||||
desiredVersion = normalizeVersion(parseVersion(_desiredVersion));
|
||||
} else {
|
||||
desiredVersion = _desiredVersion;
|
||||
}
|
||||
|
||||
if (!version || !desiredVersion) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let majorBase = version.majorBase;
|
||||
let minorBase = version.minorBase;
|
||||
let patchBase = version.patchBase;
|
||||
|
||||
let desiredMajorBase = desiredVersion.majorBase;
|
||||
let desiredMinorBase = desiredVersion.minorBase;
|
||||
let desiredPatchBase = desiredVersion.patchBase;
|
||||
|
||||
let majorMustEqual = desiredVersion.majorMustEqual;
|
||||
let minorMustEqual = desiredVersion.minorMustEqual;
|
||||
let patchMustEqual = desiredVersion.patchMustEqual;
|
||||
|
||||
if (desiredVersion.isMinimum) {
|
||||
if (majorBase > desiredMajorBase) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (majorBase < desiredMajorBase) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (minorBase > desiredMinorBase) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (minorBase < desiredMinorBase) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return patchBase >= desiredPatchBase;
|
||||
}
|
||||
|
||||
// Anything < 1.0.0 is compatible with >= 1.0.0, except exact matches
|
||||
if (majorBase === 1 && desiredMajorBase === 0 && (!majorMustEqual || !minorMustEqual || !patchMustEqual)) {
|
||||
desiredMajorBase = 1;
|
||||
desiredMinorBase = 0;
|
||||
desiredPatchBase = 0;
|
||||
majorMustEqual = true;
|
||||
minorMustEqual = false;
|
||||
patchMustEqual = false;
|
||||
}
|
||||
|
||||
if (majorBase < desiredMajorBase) {
|
||||
// smaller major version
|
||||
return false;
|
||||
}
|
||||
|
||||
if (majorBase > desiredMajorBase) {
|
||||
// higher major version
|
||||
return (!majorMustEqual);
|
||||
}
|
||||
|
||||
// at this point, majorBase are equal
|
||||
|
||||
if (minorBase < desiredMinorBase) {
|
||||
// smaller minor version
|
||||
return false;
|
||||
}
|
||||
|
||||
if (minorBase > desiredMinorBase) {
|
||||
// higher minor version
|
||||
return (!minorMustEqual);
|
||||
}
|
||||
|
||||
// at this point, minorBase are equal
|
||||
|
||||
if (patchBase < desiredPatchBase) {
|
||||
// smaller patch version
|
||||
return false;
|
||||
}
|
||||
|
||||
if (patchBase > desiredPatchBase) {
|
||||
// higher patch version
|
||||
return (!patchMustEqual);
|
||||
}
|
||||
|
||||
// at this point, patchBase are equal
|
||||
return true;
|
||||
}
|
||||
|
||||
export interface IReducedExtensionDescription {
|
||||
isBuiltin: boolean;
|
||||
engines: {
|
||||
vscode: string;
|
||||
};
|
||||
main?: string;
|
||||
}
|
||||
|
||||
export function isValidExtensionVersion(version: string, extensionDesc: IReducedExtensionDescription, notices: string[]): boolean {
|
||||
|
||||
if (extensionDesc.isBuiltin || typeof extensionDesc.main === 'undefined') {
|
||||
// No version check for builtin or declarative extensions
|
||||
return true;
|
||||
}
|
||||
|
||||
return isVersionValid(version, extensionDesc.engines.vscode, notices);
|
||||
}
|
||||
|
||||
export function isVersionValid(currentVersion: string, requestedVersion: string, notices: string[] = []): boolean {
|
||||
|
||||
let desiredVersion = normalizeVersion(parseVersion(requestedVersion));
|
||||
if (!desiredVersion) {
|
||||
notices.push(nls.localize('versionSyntax', "Could not parse `engines.vscode` value {0}. Please use, for example: ^0.10.0, ^1.2.3, ^0.11.0, ^0.10.x, etc.", requestedVersion));
|
||||
return false;
|
||||
}
|
||||
|
||||
// enforce that a breaking API version is specified.
|
||||
// for 0.X.Y, that means up to 0.X must be specified
|
||||
// otherwise for Z.X.Y, that means Z must be specified
|
||||
if (desiredVersion.majorBase === 0) {
|
||||
// force that major and minor must be specific
|
||||
if (!desiredVersion.majorMustEqual || !desiredVersion.minorMustEqual) {
|
||||
notices.push(nls.localize('versionSpecificity1', "Version specified in `engines.vscode` ({0}) is not specific enough. For vscode versions before 1.0.0, please define at a minimum the major and minor desired version. E.g. ^0.10.0, 0.10.x, 0.11.0, etc.", requestedVersion));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// force that major must be specific
|
||||
if (!desiredVersion.majorMustEqual) {
|
||||
notices.push(nls.localize('versionSpecificity2', "Version specified in `engines.vscode` ({0}) is not specific enough. For vscode versions after 1.0.0, please define at a minimum the major desired version. E.g. ^1.10.0, 1.10.x, 1.x.x, 2.x.x, etc.", requestedVersion));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValidVersion(currentVersion, desiredVersion)) {
|
||||
notices.push(nls.localize('versionMismatch', "Extension is not compatible with Code {0}. Extension requires: {1}.", currentVersion, requestedVersion));
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
function baseIsValidExtensionDescription(extensionFolderPath: string, extensionDescription: IExtensionDescription, notices: string[]): boolean {
|
||||
if (!extensionDescription) {
|
||||
notices.push(nls.localize('extensionDescription.empty', "Got empty extension description"));
|
||||
return false;
|
||||
}
|
||||
if (typeof extensionDescription.publisher !== 'string') {
|
||||
notices.push(nls.localize('extensionDescription.publisher', "property `{0}` is mandatory and must be of type `string`", 'publisher'));
|
||||
return false;
|
||||
}
|
||||
if (typeof extensionDescription.name !== 'string') {
|
||||
notices.push(nls.localize('extensionDescription.name', "property `{0}` is mandatory and must be of type `string`", 'name'));
|
||||
return false;
|
||||
}
|
||||
if (typeof extensionDescription.version !== 'string') {
|
||||
notices.push(nls.localize('extensionDescription.version', "property `{0}` is mandatory and must be of type `string`", 'version'));
|
||||
return false;
|
||||
}
|
||||
if (!extensionDescription.engines) {
|
||||
notices.push(nls.localize('extensionDescription.engines', "property `{0}` is mandatory and must be of type `object`", 'engines'));
|
||||
return false;
|
||||
}
|
||||
if (typeof extensionDescription.engines.vscode !== 'string') {
|
||||
notices.push(nls.localize('extensionDescription.engines.vscode', "property `{0}` is mandatory and must be of type `string`", 'engines.vscode'));
|
||||
return false;
|
||||
}
|
||||
if (typeof extensionDescription.extensionDependencies !== 'undefined') {
|
||||
if (!_isStringArray(extensionDescription.extensionDependencies)) {
|
||||
notices.push(nls.localize('extensionDescription.extensionDependencies', "property `{0}` can be omitted or must be of type `string[]`", 'extensionDependencies'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (typeof extensionDescription.activationEvents !== 'undefined') {
|
||||
if (!_isStringArray(extensionDescription.activationEvents)) {
|
||||
notices.push(nls.localize('extensionDescription.activationEvents1', "property `{0}` can be omitted or must be of type `string[]`", 'activationEvents'));
|
||||
return false;
|
||||
}
|
||||
if (typeof extensionDescription.main === 'undefined') {
|
||||
notices.push(nls.localize('extensionDescription.activationEvents2', "properties `{0}` and `{1}` must both be specified or must both be omitted", 'activationEvents', 'main'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (typeof extensionDescription.main !== 'undefined') {
|
||||
if (typeof extensionDescription.main !== 'string') {
|
||||
notices.push(nls.localize('extensionDescription.main1', "property `{0}` can be omitted or must be of type `string`", 'main'));
|
||||
return false;
|
||||
} else {
|
||||
let normalizedAbsolutePath = join(extensionFolderPath, extensionDescription.main);
|
||||
|
||||
if (normalizedAbsolutePath.indexOf(extensionFolderPath)) {
|
||||
notices.push(nls.localize('extensionDescription.main2', "Expected `main` ({0}) to be included inside extension's folder ({1}). This might make the extension non-portable.", normalizedAbsolutePath, extensionFolderPath));
|
||||
// not a failure case
|
||||
}
|
||||
}
|
||||
if (typeof extensionDescription.activationEvents === 'undefined') {
|
||||
notices.push(nls.localize('extensionDescription.main3', "properties `{0}` and `{1}` must both be specified or must both be omitted", 'activationEvents', 'main'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function isValidExtensionDescription(version: string, extensionFolderPath: string, extensionDescription: IExtensionDescription, notices: string[]): boolean {
|
||||
|
||||
if (!baseIsValidExtensionDescription(extensionFolderPath, extensionDescription, notices)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!valid(extensionDescription.version)) {
|
||||
notices.push(nls.localize('notSemver', "Extension version is not semver compatible."));
|
||||
return false;
|
||||
}
|
||||
|
||||
return isValidExtensionVersion(version, extensionDescription, notices);
|
||||
}
|
||||
Reference in New Issue
Block a user