deno.land / std@0.201.0 / semver / parse_range.ts
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.import { ALL } from "./constants.ts";import type { SemVerRange } from "./types.ts";import { CARET, HYPHENRANGE, re, STAR, TILDE, XRANGE } from "./_shared.ts";import { parseComparator } from "./parse_comparator.ts";
// ~, ~> --> * (any, kinda silly)// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0function replaceTildes(comp: string): string { return comp .trim() .split(/\s+/) .map((comp) => replaceTilde(comp)) .join(" ");}
function replaceTilde(comp: string): string { const r: RegExp = re[TILDE]; return comp.replace( r, (_: string, M: string, m: string, p: string, pr: string) => { let ret: string;
if (isX(M)) { ret = ""; } else if (isX(m)) { ret = ">=" + M + ".0.0 <" + (+M + 1) + ".0.0"; } else if (isX(p)) { // ~1.2 == >=1.2.0 <1.3.0 ret = ">=" + M + "." + m + ".0 <" + M + "." + (+m + 1) + ".0"; } else if (pr) { ret = ">=" + M + "." + m + "." + p + "-" + pr + " <" + M + "." + (+m + 1) + ".0"; } else { // ~1.2.3 == >=1.2.3 <1.3.0 ret = ">=" + M + "." + m + "." + p + " <" + M + "." + (+m + 1) + ".0"; }
return ret; }, );}
// ^ --> * (any, kinda silly)// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0// ^1.2.3 --> >=1.2.3 <2.0.0// ^1.2.0 --> >=1.2.0 <2.0.0function replaceCarets(comp: string): string { return comp .trim() .split(/\s+/) .map((comp) => replaceCaret(comp)) .join(" ");}
function replaceCaret(comp: string): string { const r: RegExp = re[CARET]; return comp.replace(r, (_: string, M, m, p, pr) => { let ret: string;
if (isX(M)) { ret = ""; } else if (isX(m)) { ret = ">=" + M + ".0.0 <" + (+M + 1) + ".0.0"; } else if (isX(p)) { if (M === "0") { ret = ">=" + M + "." + m + ".0 <" + M + "." + (+m + 1) + ".0"; } else { ret = ">=" + M + "." + m + ".0 <" + (+M + 1) + ".0.0"; } } else if (pr) { if (M === "0") { if (m === "0") { ret = ">=" + M + "." + m + "." + p + "-" + pr + " <" + M + "." + m + "." + (+p + 1); } else { ret = ">=" + M + "." + m + "." + p + "-" + pr + " <" + M + "." + (+m + 1) + ".0"; } } else { ret = ">=" + M + "." + m + "." + p + "-" + pr + " <" + (+M + 1) + ".0.0"; } } else { if (M === "0") { if (m === "0") { ret = ">=" + M + "." + m + "." + p + " <" + M + "." + m + "." + (+p + 1); } else { ret = ">=" + M + "." + m + "." + p + " <" + M + "." + (+m + 1) + ".0"; } } else { ret = ">=" + M + "." + m + "." + p + " <" + (+M + 1) + ".0.0"; } }
return ret; });}
function replaceXRanges(comp: string): string { return comp .split(/\s+/) .map((comp) => replaceXRange(comp)) .join(" ");}
function replaceXRange(comp: string): string { comp = comp.trim(); const r: RegExp = re[XRANGE]; return comp.replace(r, (ret: string, gtlt, M, m, p, _pr) => { const xM: boolean = isX(M); const xm: boolean = xM || isX(m); const xp: boolean = xm || isX(p); const anyX: boolean = xp;
if (gtlt === "=" && anyX) { gtlt = ""; }
if (xM) { if (gtlt === ">" || gtlt === "<") { // nothing is allowed ret = "<0.0.0"; } else { // nothing is forbidden ret = "*"; } } else if (gtlt && anyX) { // we know patch is an x, because we have any x at all. // replace X with 0 if (xm) { m = 0; } p = 0;
if (gtlt === ">") { // >1 => >=2.0.0 // >1.2 => >=1.3.0 // >1.2.3 => >= 1.2.4 gtlt = ">="; if (xm) { M = +M + 1; m = 0; p = 0; } else { m = +m + 1; p = 0; } } else if (gtlt === "<=") { // <=0.7.x is actually <0.8.0, since any 0.7.x should // pass. Similarly, <=7.x is actually <8.0.0, etc. gtlt = "<"; if (xm) { M = +M + 1; } else { m = +m + 1; } }
ret = gtlt + M + "." + m + "." + p; } else if (xm) { ret = ">=" + M + ".0.0 <" + (+M + 1) + ".0.0"; } else if (xp) { ret = ">=" + M + "." + m + ".0 <" + M + "." + (+m + 1) + ".0"; }
return ret; });}
// Because * is AND-ed with everything else in the comparator,// and '' means "any version", just remove the *s entirely.function replaceStars(comp: string): string { return comp.trim().replace(re[STAR], "");}
// This function is passed to string.replace(re[HYPHENRANGE])// M, m, patch, prerelease, build// 1.2 - 3.4.5 -> >=1.2.0 <=3.4.5// 1.2.3 - 3.4 -> >=1.2.0 <3.5.0 Any 3.4.x will do// 1.2 - 3.4 -> >=1.2.0 <3.5.0function hyphenReplace( _$0: string, from: string, fM: string, fm: string, fp: string, _fpr: string, _fb: string, to: string, tM: string, tm: string, tp: string, tpr: string, _tb: string,) { if (isX(fM)) { from = ""; } else if (isX(fm)) { from = ">=" + fM + ".0.0"; } else if (isX(fp)) { from = ">=" + fM + "." + fm + ".0"; } else { from = ">=" + from; }
if (isX(tM)) { to = ""; } else if (isX(tm)) { to = "<" + (+tM + 1) + ".0.0"; } else if (isX(tp)) { to = "<" + tM + "." + (+tm + 1) + ".0"; } else if (tpr) { to = "<=" + tM + "." + tm + "." + tp + "-" + tpr; } else { to = "<=" + to; }
return (from + " " + to).trim();}
function isX(id: string): boolean { return !id || id.toLowerCase() === "x" || id === "*";}
/** * Parses a range string into a SemVerRange object or throws a TypeError. * @param range The range string * @returns A valid semantic version range */export function parseRange(range: string): SemVerRange { // handle spaces around and between comparator and version range = range.trim().replaceAll(/(?<=<|>|=) /g, "");
if (range === "") { return { ranges: [[ALL]] }; }
// Split into groups of comparators, these are considered OR'd together. const ranges = range .trim() .split(/\s*\|\|\s*/) .map((range) => { // convert `1.2.3 - 1.2.4` into `>=1.2.3 <=1.2.4` const hr: RegExp = re[HYPHENRANGE]; range = range.replace(hr, hyphenReplace); range = replaceCarets(range); range = replaceTildes(range); range = replaceXRanges(range); range = replaceStars(range);
// At this point, the range is completely trimmed and // ready to be split into comparators. // These are considered AND's if (range === "") { return [ALL]; } else { return range .split(" ") .filter((r) => r) .map((r) => parseComparator(r)); } });
return { ranges };}
Version Info