Skip to content

fix(node:path): win32 normalize/basename/toNamespacedPath edge cases (#1728)#1749

Merged
proggeramlug merged 1 commit into
mainfrom
fix-win32-path-1728
May 25, 2026
Merged

fix(node:path): win32 normalize/basename/toNamespacedPath edge cases (#1728)#1749
proggeramlug merged 1 commit into
mainfrom
fix-win32-path-1728

Conversation

@proggeramlug
Copy link
Copy Markdown
Contributor

Summary

Fixes #1728path.win32.{normalize,basename,toNamespacedPath} diverged from Node on several Windows path edge cases. POSIX path was already correct; this is isolated to win32.

normalize_win32_str is rewritten as a faithful port of Node's win32.normalize assembly (device + root separator + tail), replacing the special-case pile that only handled the bare-drive case. win32_basename_inner now matches Node's algorithm (skip a leading drive letter, then take the last non-empty segment — UNC server/share segments count as ordinary segments). toNamespacedPath needed no direct change; it follows once normalize keeps the UNC root's trailing separator.

Cases fixed (Perry now byte-for-byte with Node v25)

input was now / Node
win32.normalize("C:") C: (already fixed on main) C:.
win32.normalize(".\\") . .\
win32.normalize("\\\\?\\C:\\") \\?\C: \\?\C:\
win32.normalize("\\\\server\\share") \\server\share \\server\share\
win32.basename("\\\\server\\share\\") `` (empty) share
win32.toNamespacedPath("\\\\server\\share") \\?\UNC\server\share \\?\UNC\server\share\

Root causes (from the issue): a bare drive letter is a drive-relative ref (the drive's cwd, → C:.); a trailing separator the input carried must be preserved; and win32.basename of a UNC root returns the share segment.

Validation

  • New parity fixture test-parity/node-suite/path/win32/drive-relative-trailing-unc.ts — diffs byte-for-byte against Node v25.0 (15/15 lines identical).
  • 6 crate-level unit tests added to path::win32_normalize_tests (drive-relative, trailing-sep, UNC root, basename) — all pass.
  • Existing path node-suite fixtures (join-normalize, basename-dirname, unc-and-drive, parse-format, relative-resolve) unchanged — no regressions.

Out of scope

  • toNamespacedPath of a relative/driveless path (foo, /tmp/x) is cwd-dependent in Node (it resolves against cwd first); the issue flagged this as lower priority and it is not addressed here.
  • Separately discovered while writing the fixture: binding a path sub-namespace to a local (const w = path.win32; w.normalize(...), and likewise path.posix) segfaults — a pre-existing namespace-aliasing codegen bug, unrelated to this fix. The existing unc-and-drive-relative.ts fixture already crashes for the same reason. Filed/tracked separately.

…1728)

path.win32 diverged from Node on several Windows path edge cases (POSIX
path was already correct):

- normalize(".\\") dropped the trailing separator; Node keeps it.
- normalize("\\\\?\\C:\\") and bare UNC roots ("\\\\server\\share") dropped
  the root's trailing separator.
- basename("\\\\server\\share\\") returned "" instead of the share segment.
- toNamespacedPath of a UNC root lost its trailing separator (follows from
  the normalize fix).

Rewrite normalize_win32_str as a faithful port of Node's win32.normalize
assembly (device + root separator + tail), replacing the accumulated
special-cases that only handled the bare-drive "C:" -> "C:." case. Rewrite
win32_basename_inner to match Node: skip a leading drive letter, then take
the last non-empty separator-delimited segment, so UNC server/share
segments count as ordinary segments.

Adds a node-suite parity fixture (byte-for-byte with Node v25) and crate
unit tests for the drive-relative, trailing-separator, UNC-root, and
basename cases. All previously-passing path fixtures are unchanged.
@proggeramlug proggeramlug merged commit ebbf273 into main May 25, 2026
10 checks passed
@proggeramlug proggeramlug deleted the fix-win32-path-1728 branch May 25, 2026 05:41
proggeramlug added a commit that referenced this pull request May 25, 2026
7 PRs landed on main after the v0.5.1027 bump (50c391f) without
per-PR tags. Neither v0.5.1026 nor v0.5.1027 were tagged on the
remote — v0.5.1028 is the first tag in this window.

- #1738 feat(compile): --trace/--focus debugging flags.
- #1723/#1741 fix(lockdown): #503 ns[dynamicKey].staticMember.
- #1673/#1742 fix(dynamic-import): literal node: builtin specifier.
- #1724/#1747 fix(node): Blob/URL globals trigger http-client feature.
- #1728/#1749 fix(node:path): win32 normalize/basename/toNamespacedPath.
- #1751 test(parity): stream consumers/promises/static batch.
- #1754 test(node-core): enrich common shim for #800.
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.win32 normalize/basename/toNamespacedPath diverge from Node (drive-relative, trailing sep, UNC)

1 participant