Skip to content

normalizePath roots a relative path beginning with ./ followed by a slash #63588

Description

@Osamaali313

🔎 Search Terms

normalizePath, getNormalizedAbsolutePath, simpleNormalizePath, leading ./, relative path rooted

🕗 Version & Regression Information

Regression introduced by #60812 ("Write path normalization without array allocations"), which added the simpleNormalizePath fast path.

⏯ Playground / Reproduction

normalizePath and getNormalizedAbsolutePath turn a relative path that begins with ./ followed by a redundant separator into a rooted path:

import { normalizePath, getNormalizedAbsolutePath } from "typescript";

normalizePath(".//a");                       // "/a"    — expected "a"
normalizePath("././/a");                     // "/a"    — expected "a"
getNormalizedAbsolutePath(".//a/b", "");     // "/a/b"  — expected "a/b"

🙁 Actual behavior

simpleNormalizePath runs path.replace(/\/\.\//g, "/") (a no-op for .//a) and then if (simplified.startsWith("./")) simplified = simplified.slice(2). For .//a that strips ./ and leaves /a — but the second / was a redundant separator, not a root — so the result is returned as a rooted path. A relative path is silently converted to an absolute one. The slow-path component walker returns the correct relative result.

🙂 Expected behavior

.//aa, .//a/ba/b, ././/aa (stay relative), matching the slow path.

Additional information

Proposed fix in #63587: only strip the leading ./ when the next character is not another separator (otherwise fall through to the slow path). Verified on the bundled compiler with an exhaustive differential over all strings up to length 8 from {'.', '/', 'a'} (9,840 inputs): 172 results change, every one a ./-then-separator case corrected to match the slow path; zero changes elsewhere.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions