You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

182 lines
6.5 KiB

4 years ago
  1. const { MAX_SAFE_COMPONENT_LENGTH } = require('./constants')
  2. const debug = require('./debug')
  3. exports = module.exports = {}
  4. // The actual regexps go on exports.re
  5. const re = exports.re = []
  6. const src = exports.src = []
  7. const t = exports.t = {}
  8. let R = 0
  9. const createToken = (name, value, isGlobal) => {
  10. const index = R++
  11. debug(index, value)
  12. t[name] = index
  13. src[index] = value
  14. re[index] = new RegExp(value, isGlobal ? 'g' : undefined)
  15. }
  16. // The following Regular Expressions can be used for tokenizing,
  17. // validating, and parsing SemVer version strings.
  18. // ## Numeric Identifier
  19. // A single `0`, or a non-zero digit followed by zero or more digits.
  20. createToken('NUMERICIDENTIFIER', '0|[1-9]\\d*')
  21. createToken('NUMERICIDENTIFIERLOOSE', '[0-9]+')
  22. // ## Non-numeric Identifier
  23. // Zero or more digits, followed by a letter or hyphen, and then zero or
  24. // more letters, digits, or hyphens.
  25. createToken('NONNUMERICIDENTIFIER', '\\d*[a-zA-Z-][a-zA-Z0-9-]*')
  26. // ## Main Version
  27. // Three dot-separated numeric identifiers.
  28. createToken('MAINVERSION', `(${src[t.NUMERICIDENTIFIER]})\\.` +
  29. `(${src[t.NUMERICIDENTIFIER]})\\.` +
  30. `(${src[t.NUMERICIDENTIFIER]})`)
  31. createToken('MAINVERSIONLOOSE', `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +
  32. `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +
  33. `(${src[t.NUMERICIDENTIFIERLOOSE]})`)
  34. // ## Pre-release Version Identifier
  35. // A numeric identifier, or a non-numeric identifier.
  36. createToken('PRERELEASEIDENTIFIER', `(?:${src[t.NUMERICIDENTIFIER]
  37. }|${src[t.NONNUMERICIDENTIFIER]})`)
  38. createToken('PRERELEASEIDENTIFIERLOOSE', `(?:${src[t.NUMERICIDENTIFIERLOOSE]
  39. }|${src[t.NONNUMERICIDENTIFIER]})`)
  40. // ## Pre-release Version
  41. // Hyphen, followed by one or more dot-separated pre-release version
  42. // identifiers.
  43. createToken('PRERELEASE', `(?:-(${src[t.PRERELEASEIDENTIFIER]
  44. }(?:\\.${src[t.PRERELEASEIDENTIFIER]})*))`)
  45. createToken('PRERELEASELOOSE', `(?:-?(${src[t.PRERELEASEIDENTIFIERLOOSE]
  46. }(?:\\.${src[t.PRERELEASEIDENTIFIERLOOSE]})*))`)
  47. // ## Build Metadata Identifier
  48. // Any combination of digits, letters, or hyphens.
  49. createToken('BUILDIDENTIFIER', '[0-9A-Za-z-]+')
  50. // ## Build Metadata
  51. // Plus sign, followed by one or more period-separated build metadata
  52. // identifiers.
  53. createToken('BUILD', `(?:\\+(${src[t.BUILDIDENTIFIER]
  54. }(?:\\.${src[t.BUILDIDENTIFIER]})*))`)
  55. // ## Full Version String
  56. // A main version, followed optionally by a pre-release version and
  57. // build metadata.
  58. // Note that the only major, minor, patch, and pre-release sections of
  59. // the version string are capturing groups. The build metadata is not a
  60. // capturing group, because it should not ever be used in version
  61. // comparison.
  62. createToken('FULLPLAIN', `v?${src[t.MAINVERSION]
  63. }${src[t.PRERELEASE]}?${
  64. src[t.BUILD]}?`)
  65. createToken('FULL', `^${src[t.FULLPLAIN]}$`)
  66. // like full, but allows v1.2.3 and =1.2.3, which people do sometimes.
  67. // also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty
  68. // common in the npm registry.
  69. createToken('LOOSEPLAIN', `[v=\\s]*${src[t.MAINVERSIONLOOSE]
  70. }${src[t.PRERELEASELOOSE]}?${
  71. src[t.BUILD]}?`)
  72. createToken('LOOSE', `^${src[t.LOOSEPLAIN]}$`)
  73. createToken('GTLT', '((?:<|>)?=?)')
  74. // Something like "2.*" or "1.2.x".
  75. // Note that "x.x" is a valid xRange identifer, meaning "any version"
  76. // Only the first item is strictly required.
  77. createToken('XRANGEIDENTIFIERLOOSE', `${src[t.NUMERICIDENTIFIERLOOSE]}|x|X|\\*`)
  78. createToken('XRANGEIDENTIFIER', `${src[t.NUMERICIDENTIFIER]}|x|X|\\*`)
  79. createToken('XRANGEPLAIN', `[v=\\s]*(${src[t.XRANGEIDENTIFIER]})` +
  80. `(?:\\.(${src[t.XRANGEIDENTIFIER]})` +
  81. `(?:\\.(${src[t.XRANGEIDENTIFIER]})` +
  82. `(?:${src[t.PRERELEASE]})?${
  83. src[t.BUILD]}?` +
  84. `)?)?`)
  85. createToken('XRANGEPLAINLOOSE', `[v=\\s]*(${src[t.XRANGEIDENTIFIERLOOSE]})` +
  86. `(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` +
  87. `(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` +
  88. `(?:${src[t.PRERELEASELOOSE]})?${
  89. src[t.BUILD]}?` +
  90. `)?)?`)
  91. createToken('XRANGE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAIN]}$`)
  92. createToken('XRANGELOOSE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAINLOOSE]}$`)
  93. // Coercion.
  94. // Extract anything that could conceivably be a part of a valid semver
  95. createToken('COERCE', `${'(^|[^\\d])' +
  96. '(\\d{1,'}${MAX_SAFE_COMPONENT_LENGTH}})` +
  97. `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?` +
  98. `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?` +
  99. `(?:$|[^\\d])`)
  100. createToken('COERCERTL', src[t.COERCE], true)
  101. // Tilde ranges.
  102. // Meaning is "reasonably at or greater than"
  103. createToken('LONETILDE', '(?:~>?)')
  104. createToken('TILDETRIM', `(\\s*)${src[t.LONETILDE]}\\s+`, true)
  105. exports.tildeTrimReplace = '$1~'
  106. createToken('TILDE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAIN]}$`)
  107. createToken('TILDELOOSE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAINLOOSE]}$`)
  108. // Caret ranges.
  109. // Meaning is "at least and backwards compatible with"
  110. createToken('LONECARET', '(?:\\^)')
  111. createToken('CARETTRIM', `(\\s*)${src[t.LONECARET]}\\s+`, true)
  112. exports.caretTrimReplace = '$1^'
  113. createToken('CARET', `^${src[t.LONECARET]}${src[t.XRANGEPLAIN]}$`)
  114. createToken('CARETLOOSE', `^${src[t.LONECARET]}${src[t.XRANGEPLAINLOOSE]}$`)
  115. // A simple gt/lt/eq thing, or just "" to indicate "any version"
  116. createToken('COMPARATORLOOSE', `^${src[t.GTLT]}\\s*(${src[t.LOOSEPLAIN]})$|^$`)
  117. createToken('COMPARATOR', `^${src[t.GTLT]}\\s*(${src[t.FULLPLAIN]})$|^$`)
  118. // An expression to strip any whitespace between the gtlt and the thing
  119. // it modifies, so that `> 1.2.3` ==> `>1.2.3`
  120. createToken('COMPARATORTRIM', `(\\s*)${src[t.GTLT]
  121. }\\s*(${src[t.LOOSEPLAIN]}|${src[t.XRANGEPLAIN]})`, true)
  122. exports.comparatorTrimReplace = '$1$2$3'
  123. // Something like `1.2.3 - 1.2.4`
  124. // Note that these all use the loose form, because they'll be
  125. // checked against either the strict or loose comparator form
  126. // later.
  127. createToken('HYPHENRANGE', `^\\s*(${src[t.XRANGEPLAIN]})` +
  128. `\\s+-\\s+` +
  129. `(${src[t.XRANGEPLAIN]})` +
  130. `\\s*$`)
  131. createToken('HYPHENRANGELOOSE', `^\\s*(${src[t.XRANGEPLAINLOOSE]})` +
  132. `\\s+-\\s+` +
  133. `(${src[t.XRANGEPLAINLOOSE]})` +
  134. `\\s*$`)
  135. // Star ranges basically just allow anything at all.
  136. createToken('STAR', '(<|>)?=?\\s*\\*')
  137. // >=0.0.0 is like a star
  138. createToken('GTE0', '^\\s*>=\\s*0\.0\.0\\s*$')
  139. createToken('GTE0PRE', '^\\s*>=\\s*0\.0\.0-0\\s*$')