Skip to content

fix(sort): make path comparator consistent for trailing-slash paths#229

Merged
thim81 merged 1 commit into
thim81:mainfrom
kistlers:fix/path-sort-trailing-slash
Jun 18, 2026
Merged

fix(sort): make path comparator consistent for trailing-slash paths#229
thim81 merged 1 commit into
thim81:mainfrom
kistlers:fix/path-sort-trailing-slash

Conversation

@kistlers

Copy link
Copy Markdown
Contributor

Fixes #228.

Problem

With sortPathsBy: path, two paths that differ only by a trailing slash (e.g. /pets and /pets/) swap order on every run, so formatting the same document never converges.

sortPathsByAlphabet splits each path on / and uses a truthy !segment test to detect a missing segment. "/pets/".split('/') ends in '' (empty segment) while "/pets".split('/') ends in undefined (no segment) — both falsy. The pathA branch then fires regardless of argument order, so compare(a, b) and compare(b, a) both return -1. That comparator is not antisymmetric, so the sort result depends on the input order and flips between runs.

Fix

Use a strict === undefined check so only a truly absent segment sorts first; an empty trailing-slash segment is compared normally as '' (which sorts before any non-empty segment). The comparator becomes a total order, output is stable across runs, and all other path orderings are unchanged.

-      if (!pathA[i]) return -1;
-      if (!pathB[i]) return 1;
+      if (pathA[i] === undefined) return -1;
+      if (pathB[i] === undefined) return 1;

Tests

Added two unit tests in test/sorting.test.js:

  • /pets is ordered before /pets/ regardless of input order (this is the regression: it fails on main for the swapped-input case).
  • /pets, /pets/, /pets/{id} keep a stable, sensible order.

Verified the new "regardless of input order" test fails on the current comparator and passes with the fix. Full suite green (npm test: 15 suites, 487 passing), sorting.js at 100% coverage, and prettier --check passes on the changed files.

sortPathsByAlphabet split each path on "/" and used a truthy `!segment`
test to detect a missing segment. That conflated an absent segment
(`undefined`, the path has ended) with an empty segment (`''`, produced
by a trailing slash).

For two paths that differ only by a trailing slash (e.g. `/pets` and
`/pets/`), the empty segment made both `compare(a, b)` and `compare(b, a)`
return -1. The comparator was therefore inconsistent (non-antisymmetric),
so Array.prototype.sort's result depended on the input order and the two
paths swapped places on every run.

Use a strict `=== undefined` check so only a truly absent segment sorts
first; an empty trailing-slash segment is compared normally as `''`. This
makes the comparator a total order and the output stable across runs,
while leaving every other path ordering unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@thim81 thim81 merged commit 0aa1f81 into thim81:main Jun 18, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Path sorting flips on every run for paths differing only by a trailing slash

2 participants