Repository navigation
fix: parse each version once in sort and rsort - #948
Open
TheNetherWatcher wants to merge 1 commit into
Open
TheNetherWatcher wants to merge 1 commit into
TheNetherWatcher wants to merge 1 commit into
Conversation
The comparator used compareBuild, which constructs two SemVer objects on every comparison. Cache the parsed version per element so each one is parsed once. Comparator results and call order are unchanged, so output, stability, in-place mutation and thrown errors are identical.
There was a problem hiding this comment.
🟢 Approval recommended
No active review issues are identified for the sorting cache changes.
No open issues 🎉
What changed in this PR
Optimizes sort and rsort by caching parsed versions within each call, addressing the sorting performance opportunity described in #814.
Changes:
- Reuses parsed
SemVerobjects instead of reconstructing them on every comparison. - Retains lazy parsing and the existing comparison order for ascending and descending sorts.
- Adds tests for in-place mutation, stable ordering, instance identity, loose parsing, and invalid input.
| File | Description |
|---|---|
test/functions/sort.js |
Adds ascending-sort compatibility tests. |
test/functions/rsort.js |
Adds descending-sort compatibility tests. |
functions/sort.js |
Caches parsed versions during ascending sorting. |
functions/rsort.js |
Caches parsed versions during descending sorting. |
Review effort: Balanced
TheNetherWatcher
force-pushed
the
perf/sort-parse-once
branch
from
November 7, 2026 16:04
d8c6877 to
e29c60f
Compare
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
sortandrsortusecompareBuildas the comparator, andcompareBuildbuilds twoSemVerobjects every time it is called. That runs the full regex parse about 2·n·log n times for a list of n versions.This change keeps a small per-call
Mapfrom each list element to its parsedSemVer, so each element is parsed once. The comparator gives the same results and is called in the same order, so the output, stability, in-place mutation and any thrown error stay exactly the same. That includes the edge cases: one-element lists aren't validated,undefinedentries still go to the end, and with several invalid versions the error still names the same one.Context: #814, where it was suggested that perf improvements be sent as PRs.
Benchmark
Random lists (about 30% prereleases, 10% build metadata), Node 24, ms per call:
rsortnumbers are the same.Equivalence
I compared the old and new
sort/rsorton 120k random lists. The lists mixed valid strings,v/=-prefixed and padded strings, build metadata,SemVerinstances (some with mismatched options), invalid strings, numbers,undefinedand holes, and each run used one of the option valuesundefined,true,false,{ loose },{ includePrerelease }or both. Both versions returned the same element references in the same order, mutated the input the same way, and threw the same error type and message.Tests
Added tests for in-place return, stability of equal versions,
SemVerinstance inputs, andloose(including that invalid input throwsTypeError).npm testpasses with 100% coverage, lint and template-oss-check.