mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-07 14:34:35 -06:00
Add semver range parsing support
This commit is contained in:
parent
37ec065d93
commit
04a524511e
@ -570,12 +570,12 @@ gulp.task(
|
||||
});
|
||||
}, /*timeout*/ 100, { max: 500 });
|
||||
|
||||
gulp.watch(watchPatterns, () => project.wait().then(fn));
|
||||
gulp.watch(watchPatterns, () => project.wait(runTestsSource && runTestsSource.token).then(fn));
|
||||
|
||||
// NOTE: gulp.watch is far too slow when watching tests/cases/**/* as it first enumerates *every* file
|
||||
const testFilePattern = /(\.ts|[\\/]tsconfig\.json)$/;
|
||||
fs.watch("tests/cases", { recursive: true }, (_, file) => {
|
||||
if (testFilePattern.test(file)) project.wait().then(fn);
|
||||
if (testFilePattern.test(file)) project.wait(runTestsSource && runTestsSource.token).then(fn);
|
||||
});
|
||||
|
||||
function runTests() {
|
||||
|
||||
62
scripts/build/countdown.js
Normal file
62
scripts/build/countdown.js
Normal file
@ -0,0 +1,62 @@
|
||||
// @ts-check
|
||||
const { CancelToken } = require("./cancellation");
|
||||
|
||||
class Countdown {
|
||||
constructor(initialCount = 0) {
|
||||
if (initialCount < 0) throw new Error();
|
||||
this._remainingCount = initialCount;
|
||||
this._promise = undefined;
|
||||
this._resolve = undefined;
|
||||
}
|
||||
|
||||
get remainingCount() {
|
||||
return this._remainingCount;
|
||||
}
|
||||
|
||||
add(count = 1) {
|
||||
if (count < 1 || !isFinite(count) || Math.trunc(count) !== count) throw new Error();
|
||||
if (this._remainingCount === 0) {
|
||||
this._promise = undefined;
|
||||
this._resolve = undefined;
|
||||
}
|
||||
|
||||
this._remainingCount += count;
|
||||
}
|
||||
|
||||
signal(count = 1) {
|
||||
if (count < 1 || !isFinite(count) || Math.trunc(count) !== count) throw new Error();
|
||||
if (this._remainingCount - count < 0) throw new Error();
|
||||
this._remainingCount -= count;
|
||||
if (this._remainingCount == 0) {
|
||||
if (this._resolve) {
|
||||
this._resolve();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @param {CancelToken} [token] */
|
||||
wait(token) {
|
||||
if (!this._promise) {
|
||||
this._promise = new Promise(resolve => { this._resolve = resolve; });
|
||||
}
|
||||
if (this._remainingCount === 0) {
|
||||
this._resolve();
|
||||
}
|
||||
if (!token) return this._promise;
|
||||
return new Promise((resolve, reject) => {
|
||||
const subscription = token.subscribe(reject);
|
||||
this._promise.then(
|
||||
value => {
|
||||
subscription.unsubscribe();
|
||||
resolve(value);
|
||||
},
|
||||
error => {
|
||||
subscription.unsubscribe();
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.Countdown = Countdown;
|
||||
@ -25,8 +25,10 @@ function exec(cmd, args, options = {}) {
|
||||
const command = isWin ? [possiblyQuote(cmd), ...args] : [`${cmd} ${args.join(" ")}`];
|
||||
const ex = cp.spawn(isWin ? "cmd" : "/bin/sh", [subshellFlag, ...command], { stdio: "inherit", windowsVerbatimArguments: true });
|
||||
const subscription = options.cancelToken && options.cancelToken.subscribe(() => {
|
||||
log(`${chalk.red("killing")} '${chalk.green(cmd)} ${args.join(" ")}'...`);
|
||||
ex.kill("SIGINT");
|
||||
ex.kill("SIGTERM");
|
||||
ex.kill();
|
||||
reject(new CancelError());
|
||||
});
|
||||
ex.on("exit", exitCode => {
|
||||
|
||||
@ -13,8 +13,27 @@ const del = require("del");
|
||||
const needsUpdate = require("./needsUpdate");
|
||||
const mkdirp = require("./mkdirp");
|
||||
const { reportDiagnostics } = require("./diagnostics");
|
||||
const { Countdown } = require("./countdown");
|
||||
const { CancelToken } = require("./cancellation");
|
||||
|
||||
const countdown = new Countdown();
|
||||
|
||||
class CompilationGulp extends gulp.Gulp {
|
||||
constructor() {
|
||||
super();
|
||||
this.on("start", () => {
|
||||
const onDone = () => {
|
||||
this.removeListener("stop", onDone);
|
||||
this.removeListener("err", onDone);
|
||||
countdown.signal();
|
||||
};
|
||||
|
||||
this.on("stop", onDone);
|
||||
this.on("err", onDone);
|
||||
countdown.add();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean} [verbose]
|
||||
*/
|
||||
@ -38,6 +57,17 @@ class ForkedGulp extends gulp.Gulp {
|
||||
constructor(tasks) {
|
||||
super();
|
||||
this.tasks = tasks;
|
||||
this.on("start", () => {
|
||||
const onDone = () => {
|
||||
this.removeListener("stop", onDone);
|
||||
this.removeListener("err", onDone);
|
||||
countdown.signal();
|
||||
};
|
||||
|
||||
this.on("stop", onDone);
|
||||
this.on("err", onDone);
|
||||
countdown.add();
|
||||
});
|
||||
}
|
||||
|
||||
// Do not reset tasks
|
||||
@ -211,22 +241,10 @@ exports.flatten = flatten;
|
||||
|
||||
/**
|
||||
* Returns a Promise that resolves when all pending build tasks have completed
|
||||
* @param {CancelToken} [token]
|
||||
*/
|
||||
function wait() {
|
||||
return new Promise(resolve => {
|
||||
if (compilationGulp.allDone()) {
|
||||
resolve();
|
||||
}
|
||||
else {
|
||||
const onDone = () => {
|
||||
compilationGulp.removeListener("onDone", onDone);
|
||||
compilationGulp.removeListener("err", onDone);
|
||||
resolve();
|
||||
};
|
||||
compilationGulp.on("stop", onDone);
|
||||
compilationGulp.on("err", onDone);
|
||||
}
|
||||
});
|
||||
function wait(token) {
|
||||
return countdown.wait(token);
|
||||
}
|
||||
exports.wait = wait;
|
||||
|
||||
|
||||
@ -22233,7 +22233,7 @@ namespace ts {
|
||||
for (const decl of indexSymbol.declarations) {
|
||||
const declaration = <SignatureDeclaration>decl;
|
||||
if (declaration.parameters.length === 1 && declaration.parameters[0].type) {
|
||||
switch (declaration.parameters[0].type!.kind) {
|
||||
switch (declaration.parameters[0].type.kind) {
|
||||
case SyntaxKind.StringKeyword:
|
||||
if (!seenStringIndexer) {
|
||||
seenStringIndexer = true;
|
||||
|
||||
@ -65,6 +65,7 @@ namespace ts {
|
||||
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
export const emptyArray: never[] = [] as never[];
|
||||
|
||||
/** Create a MapLike with good performance. */
|
||||
function createDictionaryObject<T>(): MapLike<T> {
|
||||
|
||||
@ -30,6 +30,8 @@ namespace ts {
|
||||
* Describes a precise semantic version number, https://semver.org
|
||||
*/
|
||||
export class Version {
|
||||
static readonly zero = new Version(0, 0, 0);
|
||||
|
||||
readonly major: number;
|
||||
readonly minor: number;
|
||||
readonly patch: number;
|
||||
@ -85,6 +87,15 @@ namespace ts {
|
||||
|| comparePrerelaseIdentifiers(this.prerelease, other.prerelease);
|
||||
}
|
||||
|
||||
increment(field: "major" | "minor" | "patch") {
|
||||
switch (field) {
|
||||
case "major": return new Version(this.major + 1, 0, 0);
|
||||
case "minor": return new Version(this.major, this.minor + 1, 0);
|
||||
case "patch": return new Version(this.major, this.minor, this.patch + 1);
|
||||
default: return Debug.assertNever(field);
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
let result = `${this.major}.${this.minor}.${this.patch}`;
|
||||
if (some(this.prerelease)) result += `-${this.prerelease.join(".")}`;
|
||||
@ -152,4 +163,229 @@ namespace ts {
|
||||
// > of the preceding identifiers are equal.
|
||||
return compareValues(left.length, right.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a semantic version range, per https://github.com/npm/node-semver#ranges
|
||||
*/
|
||||
export class VersionRange {
|
||||
private _alternatives: ReadonlyArray<ReadonlyArray<Comparator>>;
|
||||
|
||||
constructor(spec: string) {
|
||||
this._alternatives = spec ? Debug.assertDefined(parseRange(spec), "Invalid range spec.") : emptyArray;
|
||||
}
|
||||
|
||||
static tryParse(text: string) {
|
||||
const sets = parseRange(text);
|
||||
if (sets) {
|
||||
const range = new VersionRange("");
|
||||
range._alternatives = sets;
|
||||
return range;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
test(version: Version | string) {
|
||||
if (typeof version === "string") version = new Version(version);
|
||||
return testDisjunction(version, this._alternatives);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return formatDisjunction(this._alternatives);
|
||||
}
|
||||
}
|
||||
|
||||
interface Comparator {
|
||||
readonly operator: "<" | "<=" | ">" | ">=" | "=";
|
||||
readonly operand: Version;
|
||||
}
|
||||
|
||||
// https://github.com/npm/node-semver#range-grammar
|
||||
//
|
||||
// range-set ::= range ( logical-or range ) *
|
||||
// range ::= hyphen | simple ( ' ' simple ) * | ''
|
||||
// logical-or ::= ( ' ' ) * '||' ( ' ' ) *
|
||||
const logicalOrRegExp = /\s*\|\|\s*/g;
|
||||
const whitespaceRegExp = /\s+/g;
|
||||
|
||||
// https://github.com/npm/node-semver#range-grammar
|
||||
//
|
||||
// partial ::= xr ( '.' xr ( '.' xr qualifier ? )? )?
|
||||
// xr ::= 'x' | 'X' | '*' | nr
|
||||
// nr ::= '0' | ['1'-'9'] ( ['0'-'9'] ) *
|
||||
// qualifier ::= ( '-' pre )? ( '+' build )?
|
||||
// pre ::= parts
|
||||
// build ::= parts
|
||||
// parts ::= part ( '.' part ) *
|
||||
// part ::= nr | [-0-9A-Za-z]+
|
||||
const partialRegExp = /^([xX*0]|[1-9]\d*)(?:\.([xX*0]|[1-9]\d*)(?:\.([xX*0]|[1-9]\d*)(?:-([a-z0-9-.]+))?(?:\+([a-z0-9-.]+))?)?)?$/i;
|
||||
|
||||
// https://github.com/npm/node-semver#range-grammar
|
||||
//
|
||||
// hyphen ::= partial ' - ' partial
|
||||
const hyphenRegExp = /^\s*([a-z0-9-+.*]+)\s+-\s+([a-z0-9-+.*]+)\s*$/i;
|
||||
|
||||
// https://github.com/npm/node-semver#range-grammar
|
||||
//
|
||||
// simple ::= primitive | partial | tilde | caret
|
||||
// primitive ::= ( '<' | '>' | '>=' | '<=' | '=' ) partial
|
||||
// tilde ::= '~' partial
|
||||
// caret ::= '^' partial
|
||||
const rangeRegExp = /^\s*(~|\^|<|<=|>|>=|=)?\s*([a-z0-9-+.*]+)$/i;
|
||||
|
||||
function parseRange(text: string) {
|
||||
const alternatives: Comparator[][] = [];
|
||||
for (const range of text.trim().split(logicalOrRegExp)) {
|
||||
if (!range) continue;
|
||||
const comparators: Comparator[] = [];
|
||||
const match = hyphenRegExp.exec(range);
|
||||
if (match) {
|
||||
if (!parseHyphen(match[1], match[2], comparators)) return undefined;
|
||||
}
|
||||
else {
|
||||
for (const simple of range.split(whitespaceRegExp)) {
|
||||
const match = rangeRegExp.exec(simple);
|
||||
if (!match || !parseComparator(match[1], match[2], comparators)) return undefined;
|
||||
}
|
||||
}
|
||||
alternatives.push(comparators);
|
||||
}
|
||||
return alternatives;
|
||||
}
|
||||
|
||||
function parsePartial(text: string) {
|
||||
const match = partialRegExp.exec(text);
|
||||
if (!match) return undefined;
|
||||
|
||||
const [, major, minor = "*", patch = "*", prerelease, build] = match;
|
||||
const version = new Version(
|
||||
isWildcard(major) ? 0 : parseInt(major, 10),
|
||||
isWildcard(major) || isWildcard(minor) ? 0 : parseInt(minor, 10),
|
||||
isWildcard(major) || isWildcard(minor) || isWildcard(patch) ? 0 : parseInt(patch, 10),
|
||||
prerelease,
|
||||
build);
|
||||
|
||||
return { version, major, minor, patch };
|
||||
}
|
||||
|
||||
function parseHyphen(left: string, right: string, comparators: Comparator[]) {
|
||||
const leftResult = parsePartial(left);
|
||||
if (!leftResult) return false;
|
||||
|
||||
const rightResult = parsePartial(right);
|
||||
if (!rightResult) return false;
|
||||
|
||||
if (!isWildcard(leftResult.major)) {
|
||||
comparators.push(createComparator(">=", leftResult.version));
|
||||
}
|
||||
|
||||
if (!isWildcard(rightResult.major)) {
|
||||
comparators.push(
|
||||
isWildcard(rightResult.minor) ? createComparator("<", rightResult.version.increment("major")) :
|
||||
isWildcard(rightResult.patch) ? createComparator("<", rightResult.version.increment("minor")) :
|
||||
createComparator("<=", rightResult.version));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function parseComparator(operator: string, text: string, comparators: Comparator[]) {
|
||||
const result = parsePartial(text);
|
||||
if (!result) return false;
|
||||
|
||||
const { version, major, minor, patch } = result;
|
||||
if (!isWildcard(major)) {
|
||||
switch (operator) {
|
||||
case "~":
|
||||
comparators.push(createComparator(">=", version));
|
||||
comparators.push(createComparator("<", version.increment(
|
||||
isWildcard(minor) ? "major" :
|
||||
"minor")));
|
||||
break;
|
||||
case "^":
|
||||
comparators.push(createComparator(">=", version));
|
||||
comparators.push(createComparator("<", version.increment(
|
||||
version.major > 0 || isWildcard(minor) ? "major" :
|
||||
version.minor > 0 || isWildcard(patch) ? "minor" :
|
||||
"patch")));
|
||||
break;
|
||||
case "<":
|
||||
case ">=":
|
||||
comparators.push(createComparator(operator, version));
|
||||
break;
|
||||
case "<=":
|
||||
case ">":
|
||||
comparators.push(
|
||||
isWildcard(minor) ? createComparator(operator === "<=" ? "<" : ">=", version.increment("major")) :
|
||||
isWildcard(patch) ? createComparator(operator === "<=" ? "<" : ">=", version.increment("minor")) :
|
||||
createComparator(operator, version));
|
||||
break;
|
||||
case "=":
|
||||
case undefined:
|
||||
if (isWildcard(minor) || isWildcard(patch)) {
|
||||
comparators.push(createComparator(">=", version));
|
||||
comparators.push(createComparator("<", version.increment(isWildcard(minor) ? "major" : "minor")));
|
||||
}
|
||||
else {
|
||||
comparators.push(createComparator("=", version));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// unrecognized
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (operator === "<" || operator === ">") {
|
||||
comparators.push(createComparator("<", Version.zero));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function isWildcard(part: string) {
|
||||
return part === "*" || part === "x" || part === "X";
|
||||
}
|
||||
|
||||
function createComparator(operator: Comparator["operator"], operand: Version) {
|
||||
return { operator, operand };
|
||||
}
|
||||
|
||||
function testDisjunction(version: Version, alternatives: ReadonlyArray<ReadonlyArray<Comparator>>) {
|
||||
// an empty disjunction is treated as "*" (all versions)
|
||||
if (alternatives.length === 0) return true;
|
||||
for (const alternative of alternatives) {
|
||||
if (testAlternative(version, alternative)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function testAlternative(version: Version, comparators: ReadonlyArray<Comparator>) {
|
||||
for (const comparator of comparators) {
|
||||
if (!testComparator(version, comparator.operator, comparator.operand)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function testComparator(version: Version, operator: Comparator["operator"], operand: Version) {
|
||||
const cmp = version.compareTo(operand);
|
||||
switch (operator) {
|
||||
case "<": return cmp < 0;
|
||||
case "<=": return cmp <= 0;
|
||||
case ">": return cmp > 0;
|
||||
case ">=": return cmp >= 0;
|
||||
case "=": return cmp === 0;
|
||||
default: return Debug.assertNever(operator);
|
||||
}
|
||||
}
|
||||
|
||||
function formatDisjunction(alternatives: ReadonlyArray<ReadonlyArray<Comparator>>) {
|
||||
return map(alternatives, formatAlternative).join(" || ") || "*";
|
||||
}
|
||||
|
||||
function formatAlternative(comparators: ReadonlyArray<Comparator>) {
|
||||
return map(comparators, formatComparator).join(" ");
|
||||
}
|
||||
|
||||
function formatComparator(comparator: Comparator) {
|
||||
return `${comparator.operator}${comparator.operand}`;
|
||||
}
|
||||
}
|
||||
@ -14,7 +14,6 @@ namespace ts {
|
||||
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
export const emptyArray: never[] = [] as never[];
|
||||
export const resolvingEmptyArray: never[] = [] as never[];
|
||||
export const emptyMap: ReadonlyMap<never> = createMap<never>();
|
||||
export const emptyUnderscoreEscapedMap: ReadonlyUnderscoreEscapedMap<never> = emptyMap as ReadonlyUnderscoreEscapedMap<never>;
|
||||
|
||||
@ -82,4 +82,16 @@ namespace utils {
|
||||
export function addUTF8ByteOrderMark(text: string) {
|
||||
return getByteOrderMarkLength(text) === 0 ? "\u00EF\u00BB\u00BF" + text : text;
|
||||
}
|
||||
|
||||
export function theory<T extends any[]>(name: string, cb: (...args: T) => void, data: T[]) {
|
||||
for (const entry of data) {
|
||||
it(`${name}(${entry.map(formatTheoryDatum).join(", ")})`, () => cb(...entry));
|
||||
}
|
||||
}
|
||||
|
||||
function formatTheoryDatum(value: any) {
|
||||
return typeof value === "function" ? value.name || "<anonymous function>" :
|
||||
value === undefined ? "undefined" :
|
||||
JSON.stringify(value);
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
namespace ts {
|
||||
import theory = utils.theory;
|
||||
describe("semver", () => {
|
||||
describe("Version", () => {
|
||||
function assertVersion(version: Version, [major, minor, patch, prerelease, build]: [number, number, number, string[]?, string[]?]) {
|
||||
@ -89,6 +90,139 @@ namespace ts {
|
||||
// > Build metadata does not figure into precedence
|
||||
assert.strictEqual(new Version("1.0.0+build").compareTo(new Version("1.0.0")), Comparison.EqualTo);
|
||||
});
|
||||
it("increment", () => {
|
||||
assertVersion(new Version(1, 2, 3, "pre.4", "build.5").increment("major"), [2, 0, 0]);
|
||||
assertVersion(new Version(1, 2, 3, "pre.4", "build.5").increment("minor"), [1, 3, 0]);
|
||||
assertVersion(new Version(1, 2, 3, "pre.4", "build.5").increment("patch"), [1, 2, 4]);
|
||||
});
|
||||
});
|
||||
describe("VersionRange", () => {
|
||||
function assertRange(rangeText: string, versionText: string, inRange = true) {
|
||||
const range = new VersionRange(rangeText);
|
||||
const version = new Version(versionText);
|
||||
assert.strictEqual(range.test(version), inRange, `Expected version '${version}' ${inRange ? `to be` : `to not be`} in range '${rangeText}' (${range})`);
|
||||
}
|
||||
theory("comparators", assertRange, [
|
||||
["", "1.0.0"],
|
||||
["*", "1.0.0"],
|
||||
["1", "1.0.0"],
|
||||
["1", "2.0.0", false],
|
||||
["1.0", "1.0.0"],
|
||||
["1.0", "1.1.0", false],
|
||||
["1.0.0", "1.0.0"],
|
||||
["1.0.0", "1.0.1", false],
|
||||
["1.*", "1.0.0"],
|
||||
["1.*", "2.0.0", false],
|
||||
["1.x", "1.0.0"],
|
||||
["1.x", "2.0.0", false],
|
||||
["=1", "1.0.0"],
|
||||
["=1", "1.1.0"],
|
||||
["=1", "1.0.1"],
|
||||
["=1.0", "1.0.0"],
|
||||
["=1.0", "1.0.1"],
|
||||
["=1.0.0", "1.0.0"],
|
||||
["=*", "0.0.0"],
|
||||
["=*", "1.0.0"],
|
||||
[">1", "2"],
|
||||
[">1.0", "1.1"],
|
||||
[">1.0.0", "1.0.1"],
|
||||
[">1.0.0", "1.0.1-pre"],
|
||||
[">*", "0.0.0", false],
|
||||
[">*", "1.0.0", false],
|
||||
[">=1", "1.0.0"],
|
||||
[">=1.0", "1.0.0"],
|
||||
[">=1.0.0", "1.0.0"],
|
||||
[">=1.0.0", "1.0.1-pre"],
|
||||
[">=*", "0.0.0"],
|
||||
[">=*", "1.0.0"],
|
||||
["<2", "1.0.0"],
|
||||
["<2.1", "2.0.0"],
|
||||
["<2.0.1", "2.0.0"],
|
||||
["<2.0.0", "2.0.0-pre"],
|
||||
["<*", "0.0.0", false],
|
||||
["<*", "1.0.0", false],
|
||||
["<=2", "2.0.0"],
|
||||
["<=2.1", "2.1.0"],
|
||||
["<=2.0.1", "2.0.1"],
|
||||
["<=*", "0.0.0"],
|
||||
["<=*", "1.0.0"],
|
||||
]);
|
||||
theory("conjunctions", assertRange, [
|
||||
[">1.0.0 <2.0.0", "1.0.1"],
|
||||
[">1.0.0 <2.0.0", "2.0.0", false],
|
||||
[">1.0.0 <2.0.0", "1.0.0", false],
|
||||
[">1 >2", "3.0.0"],
|
||||
]);
|
||||
theory("disjunctions", assertRange, [
|
||||
[">=1.0.0 <2.0.0 || >=3.0.0 <4.0.0", "1.0.0"],
|
||||
[">=1.0.0 <2.0.0 || >=3.0.0 <4.0.0", "2.0.0", false],
|
||||
[">=1.0.0 <2.0.0 || >=3.0.0 <4.0.0", "3.0.0"],
|
||||
]);
|
||||
theory("hyphen", assertRange, [
|
||||
["1.0.0 - 2.0.0", "1.0.0"],
|
||||
["1.0.0 - 2.0.0", "2.0.0"],
|
||||
["1.0.0 - 2.0.0", "3.0.0", false],
|
||||
]);
|
||||
theory("tilde", assertRange, [
|
||||
["~0", "0.0.0"],
|
||||
["~0", "0.1.0"],
|
||||
["~0", "0.1.2"],
|
||||
["~0", "0.1.9"],
|
||||
["~0", "1.0.0", false],
|
||||
["~0.1", "0.1.0"],
|
||||
["~0.1", "0.1.2"],
|
||||
["~0.1", "0.1.9"],
|
||||
["~0.1", "0.2.0", false],
|
||||
["~0.1.2", "0.1.2"],
|
||||
["~0.1.2", "0.1.9"],
|
||||
["~0.1.2", "0.2.0", false],
|
||||
["~1", "1.0.0"],
|
||||
["~1", "1.2.0"],
|
||||
["~1", "1.2.3"],
|
||||
["~1", "1.2.0"],
|
||||
["~1", "1.2.3"],
|
||||
["~1", "0.0.0", false],
|
||||
["~1", "2.0.0", false],
|
||||
["~1.2", "1.2.0"],
|
||||
["~1.2", "1.2.3"],
|
||||
["~1.2", "1.1.0", false],
|
||||
["~1.2", "1.3.0", false],
|
||||
["~1.2.3", "1.2.3"],
|
||||
["~1.2.3", "1.2.9"],
|
||||
["~1.2.3", "1.1.0", false],
|
||||
["~1.2.3", "1.3.0", false],
|
||||
]);
|
||||
theory("caret", assertRange, [
|
||||
["^0", "0.0.0"],
|
||||
["^0", "0.1.0"],
|
||||
["^0", "0.9.0"],
|
||||
["^0", "0.1.2"],
|
||||
["^0", "0.1.9"],
|
||||
["^0", "1.0.0", false],
|
||||
["^0.1", "0.1.0"],
|
||||
["^0.1", "0.1.2"],
|
||||
["^0.1", "0.1.9"],
|
||||
["^0.1.2", "0.1.2"],
|
||||
["^0.1.2", "0.1.9"],
|
||||
["^0.1.2", "0.0.0", false],
|
||||
["^0.1.2", "0.2.0", false],
|
||||
["^0.1.2", "1.0.0", false],
|
||||
["^1", "1.0.0"],
|
||||
["^1", "1.2.0"],
|
||||
["^1", "1.2.3"],
|
||||
["^1", "1.9.0"],
|
||||
["^1", "0.0.0", false],
|
||||
["^1", "2.0.0", false],
|
||||
["^1.2", "1.2.0"],
|
||||
["^1.2", "1.2.3"],
|
||||
["^1.2", "1.9.0"],
|
||||
["^1.2", "1.1.0", false],
|
||||
["^1.2", "2.0.0", false],
|
||||
["^1.2.3", "1.2.3"],
|
||||
["^1.2.3", "1.9.0"],
|
||||
["^1.2.3", "1.2.2", false],
|
||||
["^1.2.3", "2.0.0", false],
|
||||
]);
|
||||
});
|
||||
});
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user