1 | ;
|
2 | // -----------------------------
|
3 | // The Version class processes string versions into comparable
|
4 | // values. A version string should normally be a series of numbers
|
5 | // separated by periods. Each part (digits separated by periods) is
|
6 | // considered its own number, and these are used for sorting. So for
|
7 | // instance, 3.10 sorts higher than 3.2 because ten is greater than
|
8 | // two.
|
9 | //
|
10 | // If any part contains letters (currently only a-z are supported) then
|
11 | // that version is considered prerelease. Versions with a prerelease
|
12 | // part in the Nth part sort less than versions with N-1
|
13 | // parts. Prerelease parts are sorted alphabetically using the normal
|
14 | // Ruby string sorting rules. If a prerelease part contains both
|
15 | // letters and numbers, it will be broken into multiple parts to
|
16 | // provide expected sort behavior (1.0.a10 becomes 1.0.a.10, and is
|
17 | // greater than 1.0.a9).
|
18 | //
|
19 | // Prereleases sort between real releases (newest to oldest):
|
20 | //
|
21 | // 1. 1.0
|
22 | // 2. 1.0.b1
|
23 | // 3. 1.0.a.2
|
24 | // 4. 0.9
|
25 | //
|
26 | // If you want to specify a version restriction that includes both prereleases
|
27 | // and regular releases of the 1.x series this is the best way:
|
28 | //
|
29 | // s.add_dependency 'example', '>= 1.0.0.a', '< 2.0.0'
|
30 | //
|
31 | // == How Software Changes
|
32 | //
|
33 | // Users expect to be able to specify a version constraint that gives them
|
34 | // some reasonable expectation that new versions of a library will work with
|
35 | // their software if the version constraint is true, and not work with their
|
36 | // software if the version constraint is false. In other words, the perfect
|
37 | // system will accept all compatible versions of the library and reject all
|
38 | // incompatible versions.
|
39 | //
|
40 | // Libraries change in 3 ways (well, more than 3, but stay focused here!).
|
41 | //
|
42 | // 1. The change may be an implementation detail only and have no effect on
|
43 | // the client software.
|
44 | // 2. The change may add new features, but do so in a way that client software
|
45 | // written to an earlier version is still compatible.
|
46 | // 3. The change may change the public interface of the library in such a way
|
47 | // that old software is no longer compatible.
|
48 | //
|
49 | // Some examples are appropriate at this point. Suppose I have a Stack class
|
50 | // that supports a <tt>push</tt> and a <tt>pop</tt> method.
|
51 | //
|
52 | // === Examples of Category 1 changes:
|
53 | //
|
54 | // * Switch from an array based implementation to a linked-list based
|
55 | // implementation.
|
56 | // * Provide an automatic (and transparent) backing store for large stacks.
|
57 | //
|
58 | // === Examples of Category 2 changes might be:
|
59 | //
|
60 | // * Add a <tt>depth</tt> method to return the current depth of the stack.
|
61 | // * Add a <tt>top</tt> method that returns the current top of stack (without
|
62 | // changing the stack).
|
63 | // * Change <tt>push</tt> so that it returns the item pushed (previously it
|
64 | // had no usable return value).
|
65 | //
|
66 | // === Examples of Category 3 changes might be:
|
67 | //
|
68 | // * Changes <tt>pop</tt> so that it no longer returns a value (you must use
|
69 | // <tt>top</tt> to get the top of the stack).
|
70 | // * Rename the methods to <tt>push_item</tt> and <tt>pop_item</tt>.
|
71 | //
|
72 | // == RubyGems Rational Versioning
|
73 | //
|
74 | // * Versions shall be represented by three non-negative integers, separated
|
75 | // by periods (e.g. 3.1.4). The first integers is the "major" version
|
76 | // number, the second integer is the "minor" version number, and the third
|
77 | // integer is the "build" number.
|
78 | //
|
79 | // * A category 1 change (implementation detail) will increment the build
|
80 | // number.
|
81 | //
|
82 | // * A category 2 change (backwards compatible) will increment the minor
|
83 | // version number and reset the build number.
|
84 | //
|
85 | // * A category 3 change (incompatible) will increment the major build number
|
86 | // and reset the minor and build numbers.
|
87 | //
|
88 | // * Any "public" release of a gem should have a different version. Normally
|
89 | // that means incrementing the build number. This means a developer can
|
90 | // generate builds all day long, but as soon as they make a public release,
|
91 | // the version must be updated.
|
92 | //
|
93 | // === Examples
|
94 | //
|
95 | // Let's work through a project lifecycle using our Stack example from above.
|
96 | //
|
97 | // Version 0.0.1:: The initial Stack class is release.
|
98 | // Version 0.0.2:: Switched to a linked=list implementation because it is
|
99 | // cooler.
|
100 | // Version 0.1.0:: Added a <tt>depth</tt> method.
|
101 | // Version 1.0.0:: Added <tt>top</tt> and made <tt>pop</tt> return nil
|
102 | // (<tt>pop</tt> used to return the old top item).
|
103 | // Version 1.1.0:: <tt>push</tt> now returns the value pushed (it used it
|
104 | // return nil).
|
105 | // Version 1.1.1:: Fixed a bug in the linked list implementation.
|
106 | // Version 1.1.2:: Fixed a bug introduced in the last fix.
|
107 | //
|
108 | // Client A needs a stack with basic push/pop capability. They write to the
|
109 | // original interface (no <tt>top</tt>), so their version constraint looks like:
|
110 | //
|
111 | // gem 'stack', '>= 0.0'
|
112 | //
|
113 | // Essentially, any version is OK with Client A. An incompatible change to
|
114 | // the library will cause them grief, but they are willing to take the chance
|
115 | // (we call Client A optimistic).
|
116 | //
|
117 | // Client B is just like Client A except for two things: (1) They use the
|
118 | // <tt>depth</tt> method and (2) they are worried about future
|
119 | // incompatibilities, so they write their version constraint like this:
|
120 | //
|
121 | // gem 'stack', '~> 0.1'
|
122 | //
|
123 | // The <tt>depth</tt> method was introduced in version 0.1.0, so that version
|
124 | // or anything later is fine, as long as the version stays below version 1.0
|
125 | // where incompatibilities are introduced. We call Client B pessimistic
|
126 | // because they are worried about incompatible future changes (it is OK to be
|
127 | // pessimistic!).
|
128 | //
|
129 | // == Preventing Version Catastrophe:
|
130 | //
|
131 | // From: http://blog.zenspider.com/2008/10/rubygems-howto-preventing-cata.html
|
132 | //
|
133 | // Let's say you're depending on the fnord gem version 2.y.z. If you
|
134 | // specify your dependency as ">= 2.0.0" then, you're good, right? What
|
135 | // happens if fnord 3.0 comes out and it isn't backwards compatible
|
136 | // with 2.y.z? Your stuff will break as a result of using ">=". The
|
137 | // better route is to specify your dependency with an "approximate" version
|
138 | // specifier ("~>"). They're a tad confusing, so here is how the dependency
|
139 | // specifiers work:
|
140 | //
|
141 | // Specification From ... To (exclusive)
|
142 | // ">= 3.0" 3.0 ... ∞
|
143 | // "~> 3.0" 3.0 ... 4.0
|
144 | // "~> 3.0.0" 3.0.0 ... 3.1
|
145 | // "~> 3.5" 3.5 ... 4.0
|
146 | // "~> 3.5.0" 3.5.0 ... 3.6
|
147 | // "~> 3" 3.0 ... 4.0
|
148 | //
|
149 | // For the last example, single-digit versions are automatically extended with
|
150 | // a zero to give a sensible result.
|
151 | Object.defineProperty(exports, "__esModule", { value: true });
|
152 | const VERSION_PATTERN = '[0-9]+(\\.[0-9a-zA-Z]+)*(-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?';
|
153 | const ANCHORED_VERSION_PATTERN = new RegExp(`^\\s*(${VERSION_PATTERN})?\\s*$`);
|
154 | class GemVersion {
|
155 | // @@all = {}
|
156 | // def self.new version # :nodoc:
|
157 | // return super unless GemVersion == self
|
158 | // @@all[version] ||= super
|
159 | // end
|
160 | // -----------------------------
|
161 | // Constructs a Version from the +version+ string. A version string is a
|
162 | // series of digits or ASCII letters separated by dots.
|
163 | constructor(version) {
|
164 | if (!GemVersion.isCorrect(version)) {
|
165 | throw new Error(`Malformed version number string ${version}`);
|
166 | }
|
167 | this.version = String(version).trim().replace('-', '.pre.');
|
168 | }
|
169 | // include Comparable
|
170 | // -----------------------------
|
171 | // A string representation of this Version.
|
172 | toString() {
|
173 | return this.version;
|
174 | }
|
175 | // -----------------------------
|
176 | // True if the +version+ string matches RubyGems' requirements.
|
177 | static isCorrect(version) {
|
178 | return ANCHORED_VERSION_PATTERN.test(version);
|
179 | }
|
180 | // -----------------------------
|
181 | // Factory method to create a Version object. Input may be a Version
|
182 | // or a String. Intended to simplify client code.
|
183 | //
|
184 | // ver1 = Version.create('1.3.17') // -> (Version object)
|
185 | // ver2 = Version.create(ver1) // -> (ver1)
|
186 | // ver3 = Version.create(nil) // -> nil
|
187 | static create(input) {
|
188 | if (input instanceof GemVersion) {
|
189 | return input;
|
190 | }
|
191 | if (!input) {
|
192 | return undefined;
|
193 | }
|
194 | return new GemVersion(input);
|
195 | }
|
196 | // -----------------------------
|
197 | // Return a new version object where the next to the last revision
|
198 | // number is one greater (e.g., 5.3.1 => 5.4).
|
199 | //
|
200 | // Pre-release (alpha) parts, e.g, 5.3.1.b.2 => 5.4, are ignored.
|
201 | // def bump
|
202 | // @bump ||= begin
|
203 | // segments = self.segments.dup
|
204 | // segments.pop while segments.any? { |s| String === s }
|
205 | // segments.pop if segments.size > 1
|
206 | // segments[-1] = segments[-1].succ
|
207 | // self.class.new segments.join(".")
|
208 | // end
|
209 | // end
|
210 | bump() {
|
211 | if (!this._bump) {
|
212 | const segments = this.getSegments();
|
213 | while (segments.some((x) => typeof x === 'string')) {
|
214 | segments.pop();
|
215 | }
|
216 | if (segments.length > 1) {
|
217 | segments.pop();
|
218 | }
|
219 | const last = segments.pop();
|
220 | segments.push(Number(last) + 1);
|
221 | this._bump = new GemVersion(segments.join('.'));
|
222 | }
|
223 | return this._bump;
|
224 | }
|
225 | // -----------------------------
|
226 | // A Version is only eql? to another version if it's specified to the
|
227 | // same precision. Version "1.0" is not the same as version "1".
|
228 | isIdentical(other) {
|
229 | return other instanceof GemVersion && other.version === this.version;
|
230 | }
|
231 | // def hash # :nodoc:
|
232 | // @version.hash
|
233 | // end
|
234 | // def init_with coder # :nodoc:
|
235 | // yaml_initialize coder.tag, coder.map
|
236 | // end
|
237 | // def inspect # :nodoc:
|
238 | // "#<#{self.class} #{version.inspect}>"
|
239 | // end
|
240 | // -----------------------------
|
241 | // Dump only the raw version string, not the complete object. It's a
|
242 | // string for backwards (RubyGems 1.3.5 and earlier) compatibility.
|
243 | // def marshal_dump
|
244 | // [version]
|
245 | // end
|
246 | // -----------------------------
|
247 | // Load custom marshal format. It's a string for backwards (RubyGems
|
248 | // 1.3.5 and earlier) compatibility.
|
249 | // def marshal_load array
|
250 | // initialize array[0]
|
251 | // end
|
252 | // def yaml_initialize(tag, map) # :nodoc:
|
253 | // @version = map['version']
|
254 | // @segments = nil
|
255 | // @hash = nil
|
256 | // end
|
257 | // def to_yaml_properties # :nodoc:
|
258 | // ["@version"]
|
259 | // end
|
260 | // def encode_with coder # :nodoc:
|
261 | // coder.add 'version', @version
|
262 | // end
|
263 | // -----------------------------
|
264 | // A version is considered a prerelease if it contains a letter.
|
265 | isPrerelease() {
|
266 | if (this._isPrerelease === undefined) {
|
267 | this._isPrerelease = /[a-zA-Z]/.test(this.version);
|
268 | }
|
269 | return this._isPrerelease;
|
270 | }
|
271 | // def pretty_print q # :nodoc:
|
272 | // q.text "GemVersion.new(#{version.inspect})"
|
273 | // end
|
274 | // -----------------------------
|
275 | // The release for this version (e.g. 1.2.0.a -> 1.2.0).
|
276 | // Non-prerelease versions return themselves.
|
277 | release() {
|
278 | if (!this._release) {
|
279 | if (this.isPrerelease) {
|
280 | const segments = this.getSegments();
|
281 | while (segments.some((x) => typeof x === 'string')) {
|
282 | segments.pop();
|
283 | }
|
284 | this._release = new GemVersion(segments.join('.'));
|
285 | }
|
286 | else {
|
287 | this._release = this;
|
288 | }
|
289 | }
|
290 | return this._release;
|
291 | }
|
292 | // def segments # :nodoc:
|
293 | // // segments is lazy so it can pick up version values that come from
|
294 | // // old marshaled versions, which don't go through marshal_load.
|
295 | // @segments ||= @version.scan(/[0-9]+|[a-z]+/i).map do |s|
|
296 | // /^\d+$/ =~ s ? s.to_i : s
|
297 | // end
|
298 | // end
|
299 | getSegments() {
|
300 | return this.version
|
301 | .match(/[0-9]+|[a-z]+/gi)
|
302 | .map((s) => (/^\d+$/.test(s) ? Number(s) : s));
|
303 | }
|
304 | compare(other) {
|
305 | if (!(other instanceof GemVersion)) {
|
306 | return undefined;
|
307 | }
|
308 | if (other.version === this.version) {
|
309 | return 0;
|
310 | }
|
311 | const lhsegments = this.getSegments();
|
312 | const rhsegments = other.getSegments();
|
313 | const lhsize = lhsegments.length;
|
314 | const rhsize = rhsegments.length;
|
315 | const limit = (lhsize > rhsize ? lhsize : rhsize) - 1;
|
316 | let i = 0;
|
317 | while (i <= limit) {
|
318 | const lhs = lhsegments[i] || 0;
|
319 | const rhs = rhsegments[i] || 0;
|
320 | i += 1;
|
321 | if (lhs == rhs) {
|
322 | continue;
|
323 | }
|
324 | if (isString(lhs) && isNumber(rhs)) {
|
325 | return -1;
|
326 | }
|
327 | if (isNumber(lhs) && isString(rhs)) {
|
328 | return 1;
|
329 | }
|
330 | if (lhs < rhs) {
|
331 | return -1;
|
332 | }
|
333 | if (lhs > rhs) {
|
334 | return 1;
|
335 | }
|
336 | }
|
337 | return 0;
|
338 | }
|
339 | }
|
340 | exports.GemVersion = GemVersion;
|
341 | function isString(val) {
|
342 | return typeof val === 'string';
|
343 | }
|
344 | function isNumber(val) {
|
345 | return typeof val === 'number';
|
346 | }
|
347 | GemVersion.VERSION_PATTERN = VERSION_PATTERN;
|
348 | //# sourceMappingURL=gem-version.js.map |
\ | No newline at end of file |