diff --git a/src/compiler/path.ts b/src/compiler/path.ts index a06359d51e549..4a4c0f91434c4 100644 --- a/src/compiler/path.ts +++ b/src/compiler/path.ts @@ -736,7 +736,10 @@ function simpleNormalizePath(path: string): string | undefined { } // Some paths only require cleanup of `/./` or leading `./` let simplified = path.replace(/\/\.\//g, "/"); - if (simplified.startsWith("./")) { + // Only strip a leading `./` when it is an actual `.` segment, not the + // start of a `.//` sequence where the second slash is a redundant + // separator (stripping there would turn a relative path into a rooted one). + if (simplified.startsWith("./") && simplified.charCodeAt(2) !== CharacterCodes.slash) { simplified = simplified.slice(2); } if (simplified !== path) { diff --git a/src/testRunner/unittests/paths.ts b/src/testRunner/unittests/paths.ts index 743e791baa181..8b65053281a7c 100644 --- a/src/testRunner/unittests/paths.ts +++ b/src/testRunner/unittests/paths.ts @@ -318,6 +318,10 @@ describe("unittests:: core paths", () => { assert.strictEqual(ts.getNormalizedAbsolutePath(".", ""), ""); assert.strictEqual(ts.getNormalizedAbsolutePath("./", ""), ""); assert.strictEqual(ts.getNormalizedAbsolutePath("./a", ""), "a"); + // A `./` followed by a redundant separator must stay relative, not become rooted. + assert.strictEqual(ts.getNormalizedAbsolutePath(".//a", ""), "a"); + assert.strictEqual(ts.getNormalizedAbsolutePath(".//a/b", ""), "a/b"); + assert.strictEqual(ts.getNormalizedAbsolutePath("././/a", ""), "a"); // Strangely, these do not normalize to the empty string. assert.strictEqual(ts.getNormalizedAbsolutePath("..", ""), ".."); assert.strictEqual(ts.getNormalizedAbsolutePath("../", ""), "..");