Last active
November 19, 2023 10:36
-
-
Save alexanderankin/1d4c664a101ae43480cad514b35665e1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package org.npm4j.utils; | |
| import lombok.AccessLevel; | |
| import lombok.AllArgsConstructor; | |
| import lombok.Builder; | |
| import lombok.Data; | |
| import lombok.extern.slf4j.Slf4j; | |
| import org.npm4j.utils.SemVerUtils.Internal.Constants.ReleaseType; | |
| import org.springframework.util.StringUtils; | |
| import java.util.*; | |
| import java.util.function.Predicate; | |
| import java.util.regex.Matcher; | |
| import java.util.regex.Pattern; | |
| public class SemVerUtils { | |
| public static SemVer parse(String version) { | |
| return parse(version, Map.of()); | |
| } | |
| public static SemVer parse(SemVer version) { | |
| return parse(version, Map.of()); | |
| } | |
| public static SemVer parse(Object version, Map<String, Object> options) { | |
| return parse(version, options, false); | |
| } | |
| public static SemVer parse(Object version, Map<String, Object> options, boolean throwErrors) { | |
| if (version instanceof SemVer semVer) return semVer; | |
| try { | |
| return SemVer.constructor(version, options); | |
| } catch (Exception e) { | |
| if (!throwErrors) return null; | |
| throw e; | |
| } | |
| } | |
| public static boolean valid(Object version) { | |
| return valid(version, Map.of()); | |
| } | |
| public static boolean valid(Object version, Map<String, Object> options) { | |
| return null != parse(version, options); | |
| } | |
| public static String clean(String version, Map<String, Object> options) { | |
| return Optional.ofNullable(parse(version.trim().replaceAll("^[=v]+", ""), options)).map(SemVer::getVersion).orElse(null); | |
| } | |
| public static String inc( | |
| Object version, | |
| ReleaseType release, | |
| Map<String, Object> options, | |
| String identifier, | |
| String identifierBase | |
| ) { | |
| return parse(version, options).inc(release, identifier, identifierBase).getVersion(); | |
| } | |
| public static void diff() { | |
| } | |
| public static void major() { | |
| } | |
| public static void minor() { | |
| } | |
| public static void patch() { | |
| } | |
| public static void prerelease() { | |
| } | |
| public static void compare() { | |
| } | |
| public static void rcompare() { | |
| } | |
| public static void compareLoose() { | |
| } | |
| public static void compareBuild() { | |
| } | |
| public static void sort() { | |
| } | |
| public static void rsort() { | |
| } | |
| public static void gt() { | |
| } | |
| public static void lt() { | |
| } | |
| public static void eq() { | |
| } | |
| public static void neq() { | |
| } | |
| public static void gte() { | |
| } | |
| public static void lte() { | |
| } | |
| public static void cmp() { | |
| } | |
| public static void coerce() { | |
| } | |
| public static void Comparator() { | |
| } | |
| public static void Range() { | |
| } | |
| public static void satisfies() { | |
| } | |
| public static void toComparators() { | |
| } | |
| public static void maxSatisfying() { | |
| } | |
| public static void minSatisfying() { | |
| } | |
| public static void minVersion() { | |
| } | |
| public static void validRange() { | |
| } | |
| public static void outside() { | |
| } | |
| public static void gtr() { | |
| } | |
| public static void ltr() { | |
| } | |
| public static void intersects() { | |
| } | |
| public static void simplifyRange() { | |
| } | |
| public static void subset() { | |
| } | |
| @Slf4j | |
| @Data | |
| @AllArgsConstructor(access = AccessLevel.PRIVATE) | |
| @Builder(toBuilder = true, access = AccessLevel.PRIVATE) | |
| public static class SemVer implements Comparable<SemVer> { | |
| private static final int MAX_LENGTH = 256; | |
| final String version; | |
| final String raw; | |
| final private Map<String, Object> options; | |
| final boolean loose; | |
| final boolean includePrerelease; | |
| final private long major, minor, patch; | |
| final private List<String> prerelease; | |
| final private List<String> build; | |
| public SemVer(String version, Map<String, Object> options) { | |
| // consider custom builder to avoid double build here | |
| this(constructor(version, options).toBuilder()); | |
| } | |
| private SemVer(SemVerBuilder builder) { | |
| builder.version = format(builder); | |
| this.version = builder.version; | |
| this.raw = builder.raw; | |
| this.options = builder.options; | |
| this.loose = builder.loose; | |
| this.includePrerelease = builder.includePrerelease; | |
| this.major = builder.major; | |
| this.minor = builder.minor; | |
| this.patch = builder.patch; | |
| this.prerelease = builder.prerelease; | |
| this.build = builder.build; | |
| } | |
| // js constructor not like java one | |
| public static SemVer constructor(Object version, Map<String, Object> options) { | |
| if (version instanceof SemVer semVer) { | |
| if (semVer.loose == getBoolean(options, "loose") && | |
| semVer.includePrerelease == getBoolean(options, "includePrerelease")) { | |
| return semVer; | |
| } else { | |
| version = semVer.version; | |
| } | |
| } | |
| if (!(version instanceof String stringVersion)) { | |
| throw new IllegalArgumentException("Invalid version. Must be a string. Got type \"" + version.getClass().getName() + "\"."); | |
| } | |
| if (stringVersion.length() > MAX_LENGTH) | |
| throw new IllegalArgumentException("version is longer than " + MAX_LENGTH + " characters"); | |
| log.debug("{}, {}", stringVersion, options); | |
| // SemVer semVer = new SemVer(); | |
| // var options = options; | |
| var loose = getBoolean(options, "loose"); | |
| // this isn't actually relevant for versions, but keep it so that we | |
| // don't run into trouble passing this.options around. | |
| var includePrerelease = getBoolean(options, "includePrerelease"); | |
| Pattern toMatch = loose ? Internal.Regexes.LOOSE.regex : Internal.Regexes.FULL.regex; | |
| Matcher matcher = toMatch.matcher(stringVersion.trim()); | |
| if (!matcher.matches()) { | |
| if (log.isDebugEnabled()) { | |
| Matcher m = Internal.Regexes.FULLPLAIN.regex.matcher(stringVersion.trim()); | |
| if (m.find()) { | |
| System.out.println("found: " + m.group()); | |
| } else { | |
| System.out.println("not even found"); | |
| } | |
| } | |
| throw new IllegalArgumentException("Invalid Version: " + stringVersion); | |
| } | |
| var major = Long.parseLong(matcher.group(1)); | |
| var minor = Long.parseLong(matcher.group(2)); | |
| var patch = Long.parseLong(matcher.group(3)); | |
| if (major > Internal.Constants.MAX_SAFE_INTEGER || major < 0) { | |
| throw new IllegalArgumentException("Invalid major version"); | |
| } | |
| if (minor > Internal.Constants.MAX_SAFE_INTEGER || minor < 0) { | |
| throw new IllegalArgumentException("Invalid minor version"); | |
| } | |
| if (patch > Internal.Constants.MAX_SAFE_INTEGER || patch < 0) { | |
| throw new IllegalArgumentException("Invalid patch version"); | |
| } | |
| // numberify any prerelease numeric ids | |
| List<String> prerelease; | |
| if (matcher.groupCount() < 4 || null == matcher.group(4)) { | |
| prerelease = List.of(); | |
| } else { | |
| prerelease = Arrays.asList(matcher.group(4).split("\\.")); | |
| // .map((id) => { | |
| // if (/^[0-9]+$/.test(id)) { | |
| // const num = +id | |
| // if (num >= 0 && num < MAX_SAFE_INTEGER) { | |
| // return num | |
| // } | |
| // } | |
| // return id | |
| // }) | |
| } | |
| List<String> build = (matcher.groupCount() < 5 || null == matcher.group(5)) | |
| ? List.of() | |
| : Arrays.asList(matcher.group(5).split("\\.")); | |
| var builder = SemVer.builder() | |
| .raw(stringVersion) | |
| .options(options) | |
| .loose(loose) | |
| .includePrerelease(includePrerelease) | |
| .major(major) | |
| .minor(minor) | |
| .patch(patch) | |
| .prerelease(prerelease) | |
| .build(build); | |
| builder.version(format(builder)); | |
| return builder.build(); | |
| } | |
| private static boolean getBoolean(Map<String, Object> options, String optionName) { | |
| Object aFalse = options.getOrDefault(optionName, false); | |
| if (aFalse instanceof Boolean booleanValue) return booleanValue; | |
| if (aFalse instanceof String stringValue) return Boolean.parseBoolean(stringValue); | |
| return false; | |
| } | |
| private static String format(SemVerBuilder version) { | |
| StringBuilder stringBuilder = new StringBuilder() | |
| .append(version.major).append(".") | |
| .append(version.minor).append(".") | |
| .append(version.patch); | |
| if (version.prerelease != null && !version.prerelease.isEmpty()) { | |
| stringBuilder.append("-"); | |
| stringBuilder.append(String.join(".", version.prerelease)); | |
| } | |
| return stringBuilder.toString(); | |
| } | |
| @Override | |
| public String toString() { | |
| return version; | |
| } | |
| // backwards compatibility | |
| public int compare(Object other) { | |
| if (other instanceof SemVer otherSemver) { | |
| return compareTo(otherSemver); | |
| } | |
| return compareTo(constructor(other, this.options)); | |
| } | |
| /** | |
| * @see SemVerUtils#compareIdentifiers | |
| */ | |
| @Override | |
| public int compareTo(SemVer o) { | |
| if (o.version.equals(this.version)) | |
| return 0; | |
| int result; | |
| if (0 != (result = compareMain(o))) | |
| return result; | |
| if (0 != (result = comparePre(o))) | |
| return result; | |
| return 0; | |
| } | |
| public int compareMain(String o) { | |
| return compareMain(constructor(o, options)); | |
| } | |
| public int compareMain(SemVer o) { | |
| int result; | |
| if (0 != (result = Long.compare(this.major, o.major))) | |
| return result; | |
| if (0 != (result = Long.compare(this.minor, o.minor))) | |
| return result; | |
| if (0 != (result = Long.compare(this.patch, o.patch))) | |
| return result; | |
| return 0; | |
| } | |
| public int comparePre(String comparedTo) { | |
| return comparePre(constructor(comparedTo, options)); | |
| } | |
| public int comparePre(SemVer o) { | |
| // NOT having a prerelease is > having one | |
| if (!this.prerelease.isEmpty() && o.prerelease.isEmpty()) { | |
| return -1; | |
| } else if (this.prerelease.isEmpty() && !o.prerelease.isEmpty()) { | |
| return 1; | |
| } else if (this.prerelease.isEmpty() /* && o.prerelease.isEmpty() */) { | |
| return 0; | |
| } | |
| return compareIdentifierList(prerelease, o.prerelease); | |
| } | |
| private int compareIdentifierList(List<String> aList, List<String> bList) { | |
| for (int i = 0; true; i++) { | |
| var a = (aList.size() == i) ? null : aList.get(i); | |
| var b = (bList.size() == i) ? null : bList.get(i); | |
| // var a = prerelease.get(i); | |
| // var b = o.prerelease.get(i); | |
| // log.debug("prerelease compare", i, a, b); | |
| if (a == null && b == null) { | |
| return 0; | |
| } else if (b == null) { | |
| return 1; | |
| } else if (a == null) { | |
| return -1; | |
| } else if (!a.equals(b)) { | |
| return compareIdentifiers(a, b); | |
| } | |
| } | |
| } | |
| public int compareBuild(String o) { | |
| return compareBuild(constructor(o, options)); | |
| } | |
| public int compareBuild(SemVer o) { | |
| return compareIdentifierList(build, o.build); | |
| } | |
| /** | |
| * {@link ReleaseType#preminor} will bump the version up to the next minor release, and immediately | |
| * down to {@link ReleaseType#prerelease}. | |
| * {@link ReleaseType#premajor} and {@link ReleaseType#prepatch} work the same way. | |
| */ | |
| public SemVer inc(ReleaseType release, String identifier, String identifierBase) { | |
| // js compat silliness | |
| if (release == null) | |
| throw new IllegalArgumentException("invalid increment argument: null"); | |
| var builder = toBuilder(); | |
| builder = switch (release) { | |
| case premajor -> { | |
| builder.prerelease = List.of(); | |
| builder.patch = 0; | |
| builder.minor = 0; | |
| builder.major++; | |
| yield builder.build().pre(identifier, identifierBase); | |
| } | |
| case preminor -> { | |
| builder.prerelease = List.of(); | |
| builder.patch = 0; | |
| builder.minor++; | |
| yield builder.build().pre(identifier, identifierBase); | |
| } | |
| case prepatch -> { | |
| // If this is already a prerelease, it will bump to the next version | |
| // drop any pre releases that might already exist, since they are not | |
| // relevant at this point. | |
| builder.prerelease = List.of(); | |
| var tmp = builder.build(); | |
| var patchBump = tmp.inc(ReleaseType.patch, identifier, identifierBase); | |
| yield patchBump.pre(identifier, identifierBase); | |
| } | |
| // If the input is a non-prerelease version, this acts the same as | |
| // pre patch. | |
| case prerelease -> { | |
| SemVer patched; | |
| if (prerelease.isEmpty()) { | |
| patched = inc(ReleaseType.patch, identifier, identifierBase); | |
| } else { | |
| patched = this; | |
| } | |
| yield patched.pre(identifier, identifierBase); | |
| } | |
| case major -> { | |
| // If this is a pre-major version, bump up to the same major version. | |
| // Otherwise, increment major. | |
| // 1.0.0-5 bumps to 1.0.0 | |
| // 1.1.0 bumps to 2.0.0 | |
| if ( | |
| this.minor != 0 || | |
| this.patch != 0 || | |
| this.prerelease.isEmpty() | |
| ) { | |
| builder.major++; | |
| } | |
| builder.minor = 0; | |
| builder.patch = 0; | |
| builder.prerelease = List.of(); | |
| yield builder; | |
| } | |
| case minor -> { | |
| // If this is a pre-minor version, bump up to the same minor version. | |
| // Otherwise, increment minor. | |
| // 1.2.0-5 bumps to 1.2.0 | |
| // 1.2.1 bumps to 1.3.0 | |
| if (this.patch != 0 || this.prerelease.isEmpty()) { | |
| builder.minor++; | |
| } | |
| builder.patch = 0; | |
| builder.prerelease = List.of(); | |
| yield builder; | |
| } | |
| case patch -> { | |
| // If this is not a pre-release version, it will increment the patch. | |
| // If it is a pre-release it will bump up to the same patch version. | |
| // 1.2.0-5 patches to 1.2.0 | |
| // 1.2.0 patches to 1.2.1 | |
| if (this.prerelease.isEmpty()) { | |
| builder.patch++; | |
| } | |
| builder.prerelease = List.of(); | |
| yield builder; | |
| } | |
| }; | |
| /* | |
| this.raw = this.format() | |
| if (this.build.length) { | |
| this.raw += `+${this.build.join('.')}` | |
| } | |
| return this | |
| */ | |
| builder.raw = format(builder); | |
| if (builder.build != null && !builder.build.isEmpty()) | |
| builder.raw += "+" + String.join(".", builder.build); | |
| return new SemVer(builder); | |
| } | |
| private SemVerBuilder pre(String identifier, String identifierBase) { | |
| // Number(identifierBase) | |
| boolean isIdentifierBaseATruthyNumber = | |
| // null check | |
| identifierBase != null && | |
| // numeric check | |
| Internal.Regexes.IS_NUMERIC_STRING.test(identifierBase) && | |
| // truthy means not 0 | |
| Integer.parseInt(identifierBase) != 0; | |
| // const base = Number(identifierBase) ? 1 : 0 | |
| var base = isIdentifierBaseATruthyNumber ? 1 : 0; | |
| // if (!identifier && identifierBase === false) | |
| if (!StringUtils.hasText(identifier) && "false".equals(identifierBase)) { | |
| throw new IllegalArgumentException("invalid increment argument: identifier is empty"); | |
| } | |
| // make it look like it is not immutable | |
| List<String> nextPrerelease; | |
| if (this.prerelease.isEmpty()) { | |
| nextPrerelease = List.of(String.valueOf(base)); | |
| } else { | |
| nextPrerelease = new ArrayList<>(prerelease); | |
| var i = this.prerelease.size(); | |
| while (--i >= 0) { | |
| String s = this.prerelease.get(i); | |
| // if (typeof this.prerelease[i] === 'number') { | |
| if (Internal.Regexes.IS_NUMERIC_STRING.test(s)) { | |
| // this.prerelease[i]++ | |
| nextPrerelease.set(i, String.valueOf(Long.parseLong(s) + 1)); | |
| i = -2; // found | |
| } | |
| } | |
| if (i == -1) { | |
| // didn't increment anything | |
| // if (identifier === this.prerelease.join('.') && identifierBase === false) { | |
| if (String.join(".", this.prerelease).equals(identifier) && | |
| "false".equals(identifierBase)) { | |
| throw new IllegalArgumentException("invalid increment argument: identifier already exists"); | |
| } | |
| // this.prerelease.push(base) | |
| nextPrerelease.add(String.valueOf(base)); | |
| } | |
| } | |
| // if (identifier) { | |
| if (StringUtils.hasText(identifier)) { | |
| /* | |
| // 1.2.0-beta.1 bumps to 1.2.0-beta.2, | |
| // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 | |
| let prerelease = [identifier, base] | |
| if (identifierBase === false) { | |
| prerelease = [identifier] | |
| } | |
| */ | |
| var prerelease = List.of(identifier, String.valueOf(base)); | |
| if ("false".equals(identifierBase)) { | |
| prerelease = List.of(identifier); | |
| } | |
| String first = nextPrerelease.isEmpty() ? null : nextPrerelease.get(0); | |
| Objects.requireNonNull(first, "first was null: need to implement handling"); | |
| /* | |
| if (compareIdentifiers(this.prerelease[0], identifier) === 0) { | |
| if (isNaN(this.prerelease[1])) { | |
| this.prerelease = prerelease | |
| } | |
| } else { | |
| this.prerelease = prerelease | |
| } | |
| */ | |
| if (compareIdentifiers(first, identifier) == 0) { | |
| // if we don't have a second element, use empty string (not numeric) | |
| String second = nextPrerelease.size() > 1 ? nextPrerelease.get(1) : ""; | |
| if (!Internal.Regexes.IS_NUMERIC_STRING.test(second)) { | |
| nextPrerelease = prerelease; | |
| } | |
| } else { | |
| nextPrerelease = prerelease; | |
| } | |
| } | |
| var builder = toBuilder(); | |
| builder.prerelease(Collections.unmodifiableList(nextPrerelease)); | |
| return builder; | |
| } | |
| } | |
| // public static void re: internalRe.re() {} | |
| // public static void src: internalRe.src() {} | |
| // public static void tokens: internalRe.t() {} | |
| /** | |
| * Note: this is the semver.org version of the spec that it implements | |
| * Not necessarily the package version of this code. | |
| */ | |
| public static final String SEMVER_SPEC_VERSION = "2.0.0"; | |
| public static final List<String> RELEASE_TYPES = | |
| Arrays.stream(ReleaseType.values()).map(Enum::name).toList(); | |
| public static int compareIdentifiers(String a, String b) { | |
| var numeric = Pattern.compile("^[0-9]{1," + SemVer.MAX_LENGTH + "}$"); | |
| var aNum = numeric.matcher(a).matches(); | |
| var bNum = numeric.matcher(b).matches(); | |
| if (aNum && bNum) { | |
| var aN = Long.parseLong(a); | |
| var bN = Integer.parseInt(b); | |
| return Long.compare(aN, bN); | |
| } | |
| return a.equals(b) ? 0 | |
| : aNum ? -1 | |
| : bNum ? 1 | |
| : a.compareTo(b); | |
| } | |
| public static int rcompareIdentifiers(String a, String b) { | |
| return compareIdentifiers(b, a); | |
| } | |
| public static class Internal { | |
| /** | |
| * Max safe length for a build identifier. The max length minus 6 characters for | |
| * the shortest version with a build 0.0.0+BUILD. | |
| */ | |
| private static final int MAX_SAFE_BUILD_LENGTH = SemVer.MAX_LENGTH - 6; | |
| interface RegexesSupport { | |
| @SuppressWarnings("SpellCheckingInspection") | |
| String LETTERDASHNUMBER = "[a-zA-Z0-9-]"; | |
| Map<String, String> SAFE_REGEX_REPLACEMENTS = Map.of( | |
| "\\s", "1", | |
| "\\d", String.valueOf(SemVer.MAX_LENGTH), | |
| RegexesSupport.LETTERDASHNUMBER, String.valueOf(MAX_SAFE_BUILD_LENGTH) | |
| ); | |
| static String makeSafeRegex(String regex) { | |
| // https://github.com/npm/node-semver/blob/14d263faa156e408a033b9b12a2f87735c2df42c/internal/re.js#L29 | |
| for (var rep : RegexesSupport.SAFE_REGEX_REPLACEMENTS.entrySet()) { | |
| regex = regex | |
| .replaceAll(Pattern.quote(rep.getKey() + "*"), | |
| rep.getKey() + "{0," + rep.getValue() + "}") | |
| .replaceAll(Pattern.quote(rep.getKey() + "+"), | |
| rep.getKey() + "{1," + rep.getValue() + "}"); | |
| } | |
| return regex; | |
| } | |
| } | |
| interface Constants { | |
| // Note: this is the semver.org version of the spec that it implements | |
| // Not necessarily the package version of this code. | |
| String SEMVER_SPEC_VERSION = "2.0.0"; | |
| int MAX_LENGTH = 256; | |
| long MAX_SAFE_INTEGER = 9007199254740991L; | |
| // Max safe segment length for coercion. | |
| int MAX_SAFE_COMPONENT_LENGTH = 16; | |
| // Max safe length for a build identifier. The max length minus 6 characters for | |
| // the shortest version with a build 0.0.0+BUILD. | |
| int MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6; | |
| List<String> RELEASE_TYPES = Arrays.stream(ReleaseType.values()).map(Enum::name).toList(); | |
| @SuppressWarnings("SpellCheckingInspection") | |
| enum ReleaseType { | |
| major, | |
| premajor, | |
| minor, | |
| preminor, | |
| patch, | |
| prepatch, | |
| prerelease, | |
| } | |
| } | |
| /** | |
| * The following Regular Expressions can be used for tokenizing, | |
| * validating, and parsing SemVer version strings. | |
| */ | |
| @SuppressWarnings("SpellCheckingInspection") | |
| @AllArgsConstructor | |
| enum Regexes { | |
| /** | |
| * ## Numeric Identifier | |
| * <p> | |
| * A single {@code 0}, or a non-zero digit followed by zero or more digits. | |
| */ | |
| NUMERICIDENTIFIER("0|[1-9]\\d*"), | |
| NUMERICIDENTIFIERLOOSE("\\d+"), | |
| NONNUMERICIDENTIFIER("\\d*[a-zA-Z-]" + RegexesSupport.LETTERDASHNUMBER + "*"), | |
| /** | |
| * ## Main Version | |
| * <p> | |
| * Three dot-separated numeric identifiers. | |
| */ | |
| MAINVERSION("(" + NUMERICIDENTIFIER.src + ")\\." + | |
| "(" + NUMERICIDENTIFIER.src + ")\\." + | |
| "(" + NUMERICIDENTIFIER.src + ")"), | |
| MAINVERSIONLOOSE("(" + NUMERICIDENTIFIERLOOSE.src + ")\\." + | |
| "(" + NUMERICIDENTIFIERLOOSE.src + ")\\." + | |
| "(" + NUMERICIDENTIFIERLOOSE.src + ")"), | |
| /** | |
| * ## Pre-release Version Identifier | |
| * <p> | |
| * A numeric identifier, or a non-numeric identifier. | |
| */ | |
| PRERELEASEIDENTIFIER("(?:" + NUMERICIDENTIFIER.src + "|" + NONNUMERICIDENTIFIER.src + ")"), | |
| PRERELEASEIDENTIFIERLOOSE("(?:" + NUMERICIDENTIFIERLOOSE.src + "|" + NONNUMERICIDENTIFIER.src + ")"), | |
| /** | |
| * ## Pre-release Version | |
| * <p> | |
| * Hyphen, followed by one or more dot-separated pre-release version identifiers. | |
| */ | |
| PRERELEASE("(?:-(" + PRERELEASEIDENTIFIER.src + | |
| "(?:\\." + PRERELEASEIDENTIFIER.src + ")*))"), | |
| PRERELEASELOOSE("(?:-?(" + PRERELEASEIDENTIFIERLOOSE.src + | |
| "(?:\\." + PRERELEASEIDENTIFIERLOOSE.src + ")*))"), | |
| /** | |
| * ## Build Metadata Identifier | |
| * <p> | |
| * Any combination of digits, letters, or hyphens. | |
| */ | |
| BUILDIDENTIFIER(RegexesSupport.LETTERDASHNUMBER + "+"), | |
| /** | |
| * ## Build Metadata | |
| * <p> | |
| * Plus sign, followed by one or more period-separated build metadata | |
| * identifiers. | |
| */ | |
| BUILD("(?:\\+(" + BUILDIDENTIFIER.src + "(?:\\." + BUILDIDENTIFIER.src + ")*))"), | |
| /** | |
| * ## Full Version String | |
| * <p> | |
| * A main version, followed optionally by a pre-release version and | |
| * build metadata. | |
| * <p> | |
| * Note that the only major, minor, patch, and pre-release sections of | |
| * the version string are capturing groups. The build metadata is not a | |
| * capturing group, because it should not ever be used in version | |
| * comparison. | |
| */ | |
| FULLPLAIN("v?" + MAINVERSION.src + PRERELEASE.src + "?" + BUILD.src + "?"), | |
| FULL("^" + FULLPLAIN.src + "$"), | |
| /** | |
| * like full, but allows v1.2.3 and =1.2.3, which people do sometimes. | |
| * also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty | |
| * common in the npm registry. | |
| */ | |
| LOOSEPLAIN("[v=\\s]*" + MAINVERSIONLOOSE.src + PRERELEASELOOSE.src + "?" + BUILD.src /* added: */ + "?"), | |
| LOOSE("^" + LOOSEPLAIN.src + "$"), | |
| GTLT("((?:<|>)?=?)"), | |
| /** | |
| * Something like "2.*" or "1.2.x". | |
| * Note that "x.x" is a valid xRange identifer, meaning "any version" | |
| * Only the first item is strictly required. | |
| */ | |
| XRANGEIDENTIFIERLOOSE(NUMERICIDENTIFIERLOOSE.src + "|x|X|\\*"), | |
| XRANGEIDENTIFIER(NUMERICIDENTIFIER.src + "|x|X|\\*"), | |
| XRANGEPLAIN( | |
| "[v=\\s]*(" + XRANGEIDENTIFIER.src + ")" + | |
| "(?:\\.(" + XRANGEIDENTIFIER.src + ")" + | |
| "(?:\\.(" + XRANGEIDENTIFIER.src + ")" + | |
| "(?:" + PRERELEASE.src + ")?" + | |
| BUILD.src + "?" + | |
| ")?)?" | |
| ), | |
| XRANGEPLAINLOOSE( | |
| "[v=\\s]*(" + XRANGEIDENTIFIERLOOSE.src + ")" + | |
| "(?:\\.(" + XRANGEIDENTIFIERLOOSE.src + ")" + | |
| "(?:\\.(" + XRANGEIDENTIFIERLOOSE.src + ")" + | |
| "(?:" + PRERELEASELOOSE.src + ")?" + | |
| BUILD.src + "?" + | |
| ")?)?" | |
| ), | |
| XRANGE("^" + GTLT.src + "\\s*" + XRANGEPLAIN.src + "$"), | |
| XRANGELOOSE("^" + GTLT.src + "\\s*" + XRANGEPLAINLOOSE.src + "$"), | |
| // Coercion. | |
| // Extract anything that could conceivably be a part of a valid semver | |
| COERCE("(^|[^\\d])" + | |
| "(\\d{1," + Constants.MAX_SAFE_COMPONENT_LENGTH + "})" + | |
| "(?:\\.(\\d{1," + Constants.MAX_SAFE_COMPONENT_LENGTH + "}))?" + | |
| "(?:\\.(\\d{1," + Constants.MAX_SAFE_COMPONENT_LENGTH + "}))?" + | |
| "(?:$|[^\\d])"), | |
| COERCERTL(COERCE.src, true), | |
| // Tilde ranges. | |
| // Meaning is "reasonably at or greater than" | |
| LONETILDE("(?:~>?)"), | |
| TILDETRIM("(\\s*)" + LONETILDE.src + "\\s+", true), | |
| TILDE("^" + LONETILDE.src + "}" + XRANGEPLAIN.src + "$"), | |
| TILDELOOSE("^" + LONETILDE.src + "}" + XRANGEPLAINLOOSE.src + "$"), | |
| // Caret ranges. | |
| // Meaning is "at least and backwards compatible with" | |
| LONECARET("(?:\\^)"), | |
| CARETTRIM("(\\s*)" + LONECARET.src + "\\s+", true), | |
| CARET("^" + LONECARET.src + XRANGEPLAINLOOSE.src + "$"), | |
| /** | |
| * A simple gt/lt/eq thing, or just "" to indicate "any version" | |
| */ | |
| COMPARATORLOOSE("^" + GTLT.src + "\\s*(" + LOOSEPLAIN.src + ")$|^$"), | |
| /** | |
| * A simple gt/lt/eq thing, or just "" to indicate "any version" | |
| */ | |
| COMPARATOR("^" + GTLT.src + "\\s*(" + FULLPLAIN.src + ")$|^$"), | |
| /** | |
| * An expression to strip any whitespace between the gtlt and the thing | |
| * it modifies, so that {@code > 1.2.3} ==> {@code >1.2.3} | |
| */ | |
| COMPARATORTRIM("(\\s*)" + GTLT.src + "\\s*" + | |
| "(" + LOOSEPLAIN.src + "|" + XRANGEPLAIN.src + ")", true), | |
| /** | |
| * Something like {@code 1.2.3 - 1.2.4} | |
| * Note that these all use the loose form, because they'll be | |
| * checked against either the strict or loose comparator form | |
| * later. | |
| */ | |
| HYPHENRANGE("^\\s*(" + XRANGEPLAIN.src + ")" + | |
| "\\s+-\\s+" + | |
| "(" + XRANGEPLAIN.src + ")" + | |
| "\\s*$"), | |
| HYPHENRANGELOOSE("^\\s*(" + XRANGEPLAINLOOSE.src + ")" + | |
| "\\s+-\\s+" + | |
| "(" + XRANGEPLAINLOOSE.src + ")" + | |
| "\\s*$"), | |
| /** | |
| * Star ranges basically just allow anything at all. | |
| */ | |
| STAR("(<|>)?=?\\s*\\*"), | |
| /** | |
| * >=0.0.0 is like a star | |
| */ | |
| GTE0("^\\s*>=\\s*0\\.0\\.0\\s*$"), | |
| GTE0PRE("^\\s*>=\\s*0\\.0\\.0-0\\s*$"), | |
| ; | |
| static final Predicate<String> IS_NUMERIC_STRING = Pattern.compile("^\\d+$").asMatchPredicate(); | |
| final String src; | |
| final Pattern regex; | |
| final String safeSrc; | |
| final Pattern safeRegex; | |
| final boolean global; | |
| Regexes(String regex) { | |
| this(regex, false); | |
| } | |
| Regexes(String regex, boolean global) { | |
| this(regex, RegexesSupport.makeSafeRegex(regex), global); | |
| } | |
| Regexes(String regex, String safe, boolean global) { | |
| this(regex, Pattern.compile(regex), safe, Pattern.compile(safe), global); | |
| } | |
| } | |
| } | |
| public static void main(String[] args) { | |
| System.out.println(Internal.Constants.RELEASE_TYPES); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment