strong-tie/inbound-calls
0
1const {2 MAX_SAFE_COMPONENT_LENGTH,3 MAX_SAFE_BUILD_LENGTH,4 MAX_LENGTH,5} = require('./constants')6const debug = require('./debug')7exports = module.exports = {}8 9// The actual regexps go on exports.re10const re = exports.re = []11const safeRe = exports.safeRe = []12const src = exports.src = []13const safeSrc = exports.safeSrc = []14const t = exports.t = {}15let R = 016 17const LETTERDASHNUMBER = '[a-zA-Z0-9-]'18 19// Replace some greedy regex tokens to prevent regex dos issues. These regex are20// used internally via the safeRe object since all inputs in this library get21// normalized first to trim and collapse all extra whitespace. The original22// regexes are exported for userland consumption and lower level usage. A23// future breaking change could export the safer regex only with a note that24// all input should have extra whitespace removed.25const safeRegexReplacements = [26 ['\\s', 1],27 ['\\d', MAX_LENGTH],28 [LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH],29]30 31const makeSafeRegex = (value) => {32 for (const [token, max] of safeRegexReplacements) {33 value = value34 .split(`${token}*`).join(`${token}{0,${max}}`)35 .split(`${token}+`).join(`${token}{1,${max}}`)36 }37 return value38}39 40const createToken = (name, value, isGlobal) => {41 const safe = makeSafeRegex(value)42 const index = R++43 debug(name, index, value)44 t[name] = index45 src[index] = value46 safeSrc[index] = safe47 re[index] = new RegExp(value, isGlobal ? 'g' : undefined)48 safeRe[index] = new RegExp(safe, isGlobal ? 'g' : undefined)49}50 51// The following Regular Expressions can be used for tokenizing,52// validating, and parsing SemVer version strings.53 54// ## Numeric Identifier55// A single `0`, or a non-zero digit followed by zero or more digits.56 57createToken('NUMERICIDENTIFIER', '0|[1-9]\\d*')58createToken('NUMERICIDENTIFIERLOOSE', '\\d+')59 60// ## Non-numeric Identifier61// Zero or more digits, followed by a letter or hyphen, and then zero or62// more letters, digits, or hyphens.63 64createToken('NONNUMERICIDENTIFIER', `\\d*[a-zA-Z-]${LETTERDASHNUMBER}*`)65 66// ## Main Version67// Three dot-separated numeric identifiers.68 69createToken('MAINVERSION', `(${src[t.NUMERICIDENTIFIER]})\\.` +70 `(${src[t.NUMERICIDENTIFIER]})\\.` +71 `(${src[t.NUMERICIDENTIFIER]})`)72 73createToken('MAINVERSIONLOOSE', `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +74 `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +75 `(${src[t.NUMERICIDENTIFIERLOOSE]})`)76 77// ## Pre-release Version Identifier78// A numeric identifier, or a non-numeric identifier.79 80createToken('PRERELEASEIDENTIFIER', `(?:${src[t.NUMERICIDENTIFIER]81}|${src[t.NONNUMERICIDENTIFIER]})`)82 83createToken('PRERELEASEIDENTIFIERLOOSE', `(?:${src[t.NUMERICIDENTIFIERLOOSE]84}|${src[t.NONNUMERICIDENTIFIER]})`)85 86// ## Pre-release Version87// Hyphen, followed by one or more dot-separated pre-release version88// identifiers.89 90createToken('PRERELEASE', `(?:-(${src[t.PRERELEASEIDENTIFIER]91}(?:\\.${src[t.PRERELEASEIDENTIFIER]})*))`)92 93createToken('PRERELEASELOOSE', `(?:-?(${src[t.PRERELEASEIDENTIFIERLOOSE]94}(?:\\.${src[t.PRERELEASEIDENTIFIERLOOSE]})*))`)95 96// ## Build Metadata Identifier97// Any combination of digits, letters, or hyphens.98 99createToken('BUILDIDENTIFIER', `${LETTERDASHNUMBER}+`)100 101// ## Build Metadata102// Plus sign, followed by one or more period-separated build metadata103// identifiers.104 105createToken('BUILD', `(?:\\+(${src[t.BUILDIDENTIFIER]106}(?:\\.${src[t.BUILDIDENTIFIER]})*))`)107 108// ## Full Version String109// A main version, followed optionally by a pre-release version and110// build metadata.111 112// Note that the only major, minor, patch, and pre-release sections of113// the version string are capturing groups. The build metadata is not a114// capturing group, because it should not ever be used in version115// comparison.116 117createToken('FULLPLAIN', `v?${src[t.MAINVERSION]118}${src[t.PRERELEASE]}?${119 src[t.BUILD]}?`)120 121createToken('FULL', `^${src[t.FULLPLAIN]}$`)122 123// like full, but allows v1.2.3 and =1.2.3, which people do sometimes.124// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty125// common in the npm registry.126createToken('LOOSEPLAIN', `[v=\\s]*${src[t.MAINVERSIONLOOSE]127}${src[t.PRERELEASELOOSE]}?${128 src[t.BUILD]}?`)129 130createToken('LOOSE', `^${src[t.LOOSEPLAIN]}$`)131 132createToken('GTLT', '((?:<|>)?=?)')133 134// Something like "2.*" or "1.2.x".135// Note that "x.x" is a valid xRange identifer, meaning "any version"136// Only the first item is strictly required.137createToken('XRANGEIDENTIFIERLOOSE', `${src[t.NUMERICIDENTIFIERLOOSE]}|x|X|\\*`)138createToken('XRANGEIDENTIFIER', `${src[t.NUMERICIDENTIFIER]}|x|X|\\*`)139 140createToken('XRANGEPLAIN', `[v=\\s]*(${src[t.XRANGEIDENTIFIER]})` +141 `(?:\\.(${src[t.XRANGEIDENTIFIER]})` +142 `(?:\\.(${src[t.XRANGEIDENTIFIER]})` +143 `(?:${src[t.PRERELEASE]})?${144 src[t.BUILD]}?` +145 `)?)?`)146 147createToken('XRANGEPLAINLOOSE', `[v=\\s]*(${src[t.XRANGEIDENTIFIERLOOSE]})` +148 `(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` +149 `(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` +150 `(?:${src[t.PRERELEASELOOSE]})?${151 src[t.BUILD]}?` +152 `)?)?`)153 154createToken('XRANGE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAIN]}$`)155createToken('XRANGELOOSE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAINLOOSE]}$`)156 157// Coercion.158// Extract anything that could conceivably be a part of a valid semver159createToken('COERCEPLAIN', `${'(^|[^\\d])' +160 '(\\d{1,'}${MAX_SAFE_COMPONENT_LENGTH}})` +161 `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?` +162 `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?`)163createToken('COERCE', `${src[t.COERCEPLAIN]}(?:$|[^\\d])`)164createToken('COERCEFULL', src[t.COERCEPLAIN] +165 `(?:${src[t.PRERELEASE]})?` +166 `(?:${src[t.BUILD]})?` +167 `(?:$|[^\\d])`)168createToken('COERCERTL', src[t.COERCE], true)169createToken('COERCERTLFULL', src[t.COERCEFULL], true)170 171// Tilde ranges.172// Meaning is "reasonably at or greater than"173createToken('LONETILDE', '(?:~>?)')174 175createToken('TILDETRIM', `(\\s*)${src[t.LONETILDE]}\\s+`, true)176exports.tildeTrimReplace = '$1~'177 178createToken('TILDE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAIN]}$`)179createToken('TILDELOOSE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAINLOOSE]}$`)180 181// Caret ranges.182// Meaning is "at least and backwards compatible with"183createToken('LONECARET', '(?:\\^)')184 185createToken('CARETTRIM', `(\\s*)${src[t.LONECARET]}\\s+`, true)186exports.caretTrimReplace = '$1^'187 188createToken('CARET', `^${src[t.LONECARET]}${src[t.XRANGEPLAIN]}$`)189createToken('CARETLOOSE', `^${src[t.LONECARET]}${src[t.XRANGEPLAINLOOSE]}$`)190 191// A simple gt/lt/eq thing, or just "" to indicate "any version"192createToken('COMPARATORLOOSE', `^${src[t.GTLT]}\\s*(${src[t.LOOSEPLAIN]})$|^$`)193createToken('COMPARATOR', `^${src[t.GTLT]}\\s*(${src[t.FULLPLAIN]})$|^$`)194 195// An expression to strip any whitespace between the gtlt and the thing196// it modifies, so that `> 1.2.3` ==> `>1.2.3`197createToken('COMPARATORTRIM', `(\\s*)${src[t.GTLT]198}\\s*(${src[t.LOOSEPLAIN]}|${src[t.XRANGEPLAIN]})`, true)199exports.comparatorTrimReplace = '$1$2$3'200 201// Something like `1.2.3 - 1.2.4`202// Note that these all use the loose form, because they'll be203// checked against either the strict or loose comparator form204// later.205createToken('HYPHENRANGE', `^\\s*(${src[t.XRANGEPLAIN]})` +206 `\\s+-\\s+` +207 `(${src[t.XRANGEPLAIN]})` +208 `\\s*$`)209 210createToken('HYPHENRANGELOOSE', `^\\s*(${src[t.XRANGEPLAINLOOSE]})` +211 `\\s+-\\s+` +212 `(${src[t.XRANGEPLAINLOOSE]})` +213 `\\s*$`)214 215// Star ranges basically just allow anything at all.216createToken('STAR', '(<|>)?=?\\s*\\*')217// >=0.0.0 is like a star218createToken('GTE0', '^\\s*>=\\s*0\\.0\\.0\\s*$')219createToken('GTE0PRE', '^\\s*>=\\s*0\\.0\\.0-0\\s*$')220 