Skip to content

path: relative() on Windows drops leading characters when a path contains "İ" #7336

Open

Description

@kortikol

Describe the bug

relative() from @std/path/windows lowercases both paths to compare them case-insensitively. It then slices the original to path using indexes it found in the lowercased strings. Some characters get longer when lowercased: "İ" (U+0130) becomes "i̇", two UTF-16 code units. Each such character before the end of the common prefix shifts the indexes by one, so the result loses that many leading characters.

Node.js had the same bug in path.win32.relative(), which this function was ported from. It was fixed in nodejs/node#53991: when lowercasing changes a path's length, Node falls back to comparing the paths segment by segment. That PR's tests include İ cases that could be reused here.

Steps to Reproduce

import { relative } from "jsr:@std/path@1.1.6/windows";

console.log(relative("C:\\İPTV\\player", "C:\\İPTV\\player\\node_modules\\zod\\index.ts"));
console.log(relative("C:\\İPTV\\player", "C:\\İPTV\\player\\src\\main.ts"));
console.log(relative("C:\\İ\\İ\\a", "C:\\İ\\İ\\a\\bcd.ts"));
console.log(relative("C:\\IPTV\\player", "C:\\IPTV\\player\\src\\main.ts")); // ASCII, for comparison

Expected behavior

node_modules\zod\index.ts
src\main.ts
bcd.ts
src\main.ts

Actual behavior

ode_modules\zod\index.ts
rc\main.ts
d.ts
src\main.ts

This also affects ESLint on Windows. @eslint/config-array bundles this module to match ignore patterns against paths relative to the config folder. In a project inside a folder such as C:\Users\…\İPTV, every relative path loses its first character, so no ignore pattern matches, not even ESLint's default **/node_modules/. As a result eslint . lints all of node_modules. Seen with @eslint/config-array 0.21.2 and 0.23.5.

Environment

  • OS: Windows 11. The function is plain string logic, so it reproduces on any OS.
  • deno version: I reproduced it with Node.js 24 importing the npm build of @std/path (@jsr/std__path); the code path does not depend on the runtime.
  • std version: @std/path 1.1.6

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions