217 lines
		
	
	
	
		
			7.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			217 lines
		
	
	
	
		
			7.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
const {
 | 
						|
  MAX_SAFE_COMPONENT_LENGTH,
 | 
						|
  MAX_SAFE_BUILD_LENGTH,
 | 
						|
  MAX_LENGTH,
 | 
						|
} = require('./constants')
 | 
						|
const debug = require('./debug')
 | 
						|
exports = module.exports = {}
 | 
						|
 | 
						|
// The actual regexps go on exports.re
 | 
						|
const re = exports.re = []
 | 
						|
const safeRe = exports.safeRe = []
 | 
						|
const src = exports.src = []
 | 
						|
const t = exports.t = {}
 | 
						|
let R = 0
 | 
						|
 | 
						|
const LETTERDASHNUMBER = '[a-zA-Z0-9-]'
 | 
						|
 | 
						|
// Replace some greedy regex tokens to prevent regex dos issues. These regex are
 | 
						|
// used internally via the safeRe object since all inputs in this library get
 | 
						|
// normalized first to trim and collapse all extra whitespace. The original
 | 
						|
// regexes are exported for userland consumption and lower level usage. A
 | 
						|
// future breaking change could export the safer regex only with a note that
 | 
						|
// all input should have extra whitespace removed.
 | 
						|
const safeRegexReplacements = [
 | 
						|
  ['\\s', 1],
 | 
						|
  ['\\d', MAX_LENGTH],
 | 
						|
  [LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH],
 | 
						|
]
 | 
						|
 | 
						|
const makeSafeRegex = (value) => {
 | 
						|
  for (const [token, max] of safeRegexReplacements) {
 | 
						|
    value = value
 | 
						|
      .split(`${token}*`).join(`${token}{0,${max}}`)
 | 
						|
      .split(`${token}+`).join(`${token}{1,${max}}`)
 | 
						|
  }
 | 
						|
  return value
 | 
						|
}
 | 
						|
 | 
						|
const createToken = (name, value, isGlobal) => {
 | 
						|
  const safe = makeSafeRegex(value)
 | 
						|
  const index = R++
 | 
						|
  debug(name, index, value)
 | 
						|
  t[name] = index
 | 
						|
  src[index] = value
 | 
						|
  re[index] = new RegExp(value, isGlobal ? 'g' : undefined)
 | 
						|
  safeRe[index] = new RegExp(safe, isGlobal ? 'g' : undefined)
 | 
						|
}
 | 
						|
 | 
						|
// The following Regular Expressions can be used for tokenizing,
 | 
						|
// validating, and parsing SemVer version strings.
 | 
						|
 | 
						|
// ## Numeric Identifier
 | 
						|
// A single `0`, or a non-zero digit followed by zero or more digits.
 | 
						|
 | 
						|
createToken('NUMERICIDENTIFIER', '0|[1-9]\\d*')
 | 
						|
createToken('NUMERICIDENTIFIERLOOSE', '\\d+')
 | 
						|
 | 
						|
// ## Non-numeric Identifier
 | 
						|
// Zero or more digits, followed by a letter or hyphen, and then zero or
 | 
						|
// more letters, digits, or hyphens.
 | 
						|
 | 
						|
createToken('NONNUMERICIDENTIFIER', `\\d*[a-zA-Z-]${LETTERDASHNUMBER}*`)
 | 
						|
 | 
						|
// ## Main Version
 | 
						|
// Three dot-separated numeric identifiers.
 | 
						|
 | 
						|
createToken('MAINVERSION', `(${src[t.NUMERICIDENTIFIER]})\\.` +
 | 
						|
                   `(${src[t.NUMERICIDENTIFIER]})\\.` +
 | 
						|
                   `(${src[t.NUMERICIDENTIFIER]})`)
 | 
						|
 | 
						|
createToken('MAINVERSIONLOOSE', `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +
 | 
						|
                        `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +
 | 
						|
                        `(${src[t.NUMERICIDENTIFIERLOOSE]})`)
 | 
						|
 | 
						|
// ## Pre-release Version Identifier
 | 
						|
// A numeric identifier, or a non-numeric identifier.
 | 
						|
 | 
						|
createToken('PRERELEASEIDENTIFIER', `(?:${src[t.NUMERICIDENTIFIER]
 | 
						|
}|${src[t.NONNUMERICIDENTIFIER]})`)
 | 
						|
 | 
						|
createToken('PRERELEASEIDENTIFIERLOOSE', `(?:${src[t.NUMERICIDENTIFIERLOOSE]
 | 
						|
}|${src[t.NONNUMERICIDENTIFIER]})`)
 | 
						|
 | 
						|
// ## Pre-release Version
 | 
						|
// Hyphen, followed by one or more dot-separated pre-release version
 | 
						|
// identifiers.
 | 
						|
 | 
						|
createToken('PRERELEASE', `(?:-(${src[t.PRERELEASEIDENTIFIER]
 | 
						|
}(?:\\.${src[t.PRERELEASEIDENTIFIER]})*))`)
 | 
						|
 | 
						|
createToken('PRERELEASELOOSE', `(?:-?(${src[t.PRERELEASEIDENTIFIERLOOSE]
 | 
						|
}(?:\\.${src[t.PRERELEASEIDENTIFIERLOOSE]})*))`)
 | 
						|
 | 
						|
// ## Build Metadata Identifier
 | 
						|
// Any combination of digits, letters, or hyphens.
 | 
						|
 | 
						|
createToken('BUILDIDENTIFIER', `${LETTERDASHNUMBER}+`)
 | 
						|
 | 
						|
// ## Build Metadata
 | 
						|
// Plus sign, followed by one or more period-separated build metadata
 | 
						|
// identifiers.
 | 
						|
 | 
						|
createToken('BUILD', `(?:\\+(${src[t.BUILDIDENTIFIER]
 | 
						|
}(?:\\.${src[t.BUILDIDENTIFIER]})*))`)
 | 
						|
 | 
						|
// ## Full Version String
 | 
						|
// A main version, followed optionally by a pre-release version and
 | 
						|
// build metadata.
 | 
						|
 | 
						|
// 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.
 | 
						|
 | 
						|
createToken('FULLPLAIN', `v?${src[t.MAINVERSION]
 | 
						|
}${src[t.PRERELEASE]}?${
 | 
						|
  src[t.BUILD]}?`)
 | 
						|
 | 
						|
createToken('FULL', `^${src[t.FULLPLAIN]}$`)
 | 
						|
 | 
						|
// 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.
 | 
						|
createToken('LOOSEPLAIN', `[v=\\s]*${src[t.MAINVERSIONLOOSE]
 | 
						|
}${src[t.PRERELEASELOOSE]}?${
 | 
						|
  src[t.BUILD]}?`)
 | 
						|
 | 
						|
createToken('LOOSE', `^${src[t.LOOSEPLAIN]}$`)
 | 
						|
 | 
						|
createToken('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.
 | 
						|
createToken('XRANGEIDENTIFIERLOOSE', `${src[t.NUMERICIDENTIFIERLOOSE]}|x|X|\\*`)
 | 
						|
createToken('XRANGEIDENTIFIER', `${src[t.NUMERICIDENTIFIER]}|x|X|\\*`)
 | 
						|
 | 
						|
createToken('XRANGEPLAIN', `[v=\\s]*(${src[t.XRANGEIDENTIFIER]})` +
 | 
						|
                   `(?:\\.(${src[t.XRANGEIDENTIFIER]})` +
 | 
						|
                   `(?:\\.(${src[t.XRANGEIDENTIFIER]})` +
 | 
						|
                   `(?:${src[t.PRERELEASE]})?${
 | 
						|
                     src[t.BUILD]}?` +
 | 
						|
                   `)?)?`)
 | 
						|
 | 
						|
createToken('XRANGEPLAINLOOSE', `[v=\\s]*(${src[t.XRANGEIDENTIFIERLOOSE]})` +
 | 
						|
                        `(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` +
 | 
						|
                        `(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` +
 | 
						|
                        `(?:${src[t.PRERELEASELOOSE]})?${
 | 
						|
                          src[t.BUILD]}?` +
 | 
						|
                        `)?)?`)
 | 
						|
 | 
						|
createToken('XRANGE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAIN]}$`)
 | 
						|
createToken('XRANGELOOSE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAINLOOSE]}$`)
 | 
						|
 | 
						|
// Coercion.
 | 
						|
// Extract anything that could conceivably be a part of a valid semver
 | 
						|
createToken('COERCEPLAIN', `${'(^|[^\\d])' +
 | 
						|
              '(\\d{1,'}${MAX_SAFE_COMPONENT_LENGTH}})` +
 | 
						|
              `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?` +
 | 
						|
              `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?`)
 | 
						|
createToken('COERCE', `${src[t.COERCEPLAIN]}(?:$|[^\\d])`)
 | 
						|
createToken('COERCEFULL', src[t.COERCEPLAIN] +
 | 
						|
              `(?:${src[t.PRERELEASE]})?` +
 | 
						|
              `(?:${src[t.BUILD]})?` +
 | 
						|
              `(?:$|[^\\d])`)
 | 
						|
createToken('COERCERTL', src[t.COERCE], true)
 | 
						|
createToken('COERCERTLFULL', src[t.COERCEFULL], true)
 | 
						|
 | 
						|
// Tilde ranges.
 | 
						|
// Meaning is "reasonably at or greater than"
 | 
						|
createToken('LONETILDE', '(?:~>?)')
 | 
						|
 | 
						|
createToken('TILDETRIM', `(\\s*)${src[t.LONETILDE]}\\s+`, true)
 | 
						|
exports.tildeTrimReplace = '$1~'
 | 
						|
 | 
						|
createToken('TILDE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAIN]}$`)
 | 
						|
createToken('TILDELOOSE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAINLOOSE]}$`)
 | 
						|
 | 
						|
// Caret ranges.
 | 
						|
// Meaning is "at least and backwards compatible with"
 | 
						|
createToken('LONECARET', '(?:\\^)')
 | 
						|
 | 
						|
createToken('CARETTRIM', `(\\s*)${src[t.LONECARET]}\\s+`, true)
 | 
						|
exports.caretTrimReplace = '$1^'
 | 
						|
 | 
						|
createToken('CARET', `^${src[t.LONECARET]}${src[t.XRANGEPLAIN]}$`)
 | 
						|
createToken('CARETLOOSE', `^${src[t.LONECARET]}${src[t.XRANGEPLAINLOOSE]}$`)
 | 
						|
 | 
						|
// A simple gt/lt/eq thing, or just "" to indicate "any version"
 | 
						|
createToken('COMPARATORLOOSE', `^${src[t.GTLT]}\\s*(${src[t.LOOSEPLAIN]})$|^$`)
 | 
						|
createToken('COMPARATOR', `^${src[t.GTLT]}\\s*(${src[t.FULLPLAIN]})$|^$`)
 | 
						|
 | 
						|
// An expression to strip any whitespace between the gtlt and the thing
 | 
						|
// it modifies, so that `> 1.2.3` ==> `>1.2.3`
 | 
						|
createToken('COMPARATORTRIM', `(\\s*)${src[t.GTLT]
 | 
						|
}\\s*(${src[t.LOOSEPLAIN]}|${src[t.XRANGEPLAIN]})`, true)
 | 
						|
exports.comparatorTrimReplace = '$1$2$3'
 | 
						|
 | 
						|
// Something like `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.
 | 
						|
createToken('HYPHENRANGE', `^\\s*(${src[t.XRANGEPLAIN]})` +
 | 
						|
                   `\\s+-\\s+` +
 | 
						|
                   `(${src[t.XRANGEPLAIN]})` +
 | 
						|
                   `\\s*$`)
 | 
						|
 | 
						|
createToken('HYPHENRANGELOOSE', `^\\s*(${src[t.XRANGEPLAINLOOSE]})` +
 | 
						|
                        `\\s+-\\s+` +
 | 
						|
                        `(${src[t.XRANGEPLAINLOOSE]})` +
 | 
						|
                        `\\s*$`)
 | 
						|
 | 
						|
// Star ranges basically just allow anything at all.
 | 
						|
createToken('STAR', '(<|>)?=?\\s*\\*')
 | 
						|
// >=0.0.0 is like a star
 | 
						|
createToken('GTE0', '^\\s*>=\\s*0\\.0\\.0\\s*$')
 | 
						|
createToken('GTE0PRE', '^\\s*>=\\s*0\\.0\\.0-0\\s*$')
 |