From 6aaf191081b22759f4aec3ec3b7a7f7834d0f78f Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Wed, 4 Sep 2024 13:28:33 +0900 Subject: [PATCH] feat(path/unstable): support file `URL` arg in `normalize()` (#5902) --- path/normalize.ts | 30 +++++++++++++++++++++++++++++- path/normalize_test.ts | 15 +++++++++++++++ path/posix/normalize.ts | 29 +++++++++++++++++------------ path/windows/normalize.ts | 25 ++++++++++++++++++++++++- 4 files changed, 85 insertions(+), 14 deletions(-) diff --git a/path/normalize.ts b/path/normalize.ts index d31f24195..c069a16dc 100644 --- a/path/normalize.ts +++ b/path/normalize.ts @@ -26,6 +26,34 @@ import { normalize as windowsNormalize } from "./windows/normalize.ts"; * @param path Path to be normalized * @returns The normalized path. */ -export function normalize(path: string): string { +export function normalize(path: string): string; +/** + * Normalize the path, resolving `'..'` and `'.'` segments. + * + * @experimental **UNSTABLE**: New API, yet to be vetted. + * + * Note: Resolving these segments does not necessarily mean that all will be + * eliminated. A `'..'` at the top-level will be preserved, and an empty path is + * canonically `'.'`. + * + * @example Usage + * ```ts + * import { normalize } from "@std/path/normalize"; + * import { assertEquals } from "@std/assert"; + * + * if (Deno.build.os === "windows") { + * assertEquals(normalize("C:\\foo\\bar\\..\\baz\\quux"), "C:\\foo\\baz\\quux"); + * assertEquals(normalize(new URL("file:///C:/foo/bar/../baz/quux")), "C:\\foo\\baz\\quux"); + * } else { + * assertEquals(normalize("/foo/bar/../baz/quux"), "/foo/baz/quux"); + * assertEquals(normalize(new URL("file:///foo/bar/../baz/quux")), "/foo/baz/quux"); + * } + * ``` + * + * @param path Path to be normalized. Path can be a string or a file URL object. + * @returns The normalized path. + */ +export function normalize(path: string | URL): string; +export function normalize(path: string | URL): string { return isWindows ? windowsNormalize(path) : posixNormalize(path); } diff --git a/path/normalize_test.ts b/path/normalize_test.ts index 19eec85b0..0107b2877 100644 --- a/path/normalize_test.ts +++ b/path/normalize_test.ts @@ -8,9 +8,24 @@ Deno.test(`normalize() returns "." if input is empty`, function () { assertEquals(windows.normalize(""), "."); }); +Deno.test("posix.normalize() normalizes posix specific paths", () => { + assertEquals( + posix.normalize("/foo/bar//baz/asdf/quux/.."), + "/foo/bar/baz/asdf", + ); + assertEquals( + posix.normalize(new URL("file:///foo/bar//baz/asdf/quux/..")), + "/foo/bar/baz/asdf/", + ); +}); + Deno.test("windows.normalize() normalizes windows specific paths", () => { assertEquals( windows.normalize("//server/share/dir/file.ext"), "\\\\server\\share\\dir\\file.ext", ); + assertEquals( + windows.normalize(new URL("file:///C:/foo/bar/../baz/quux")), + "C:\\foo\\baz\\quux", + ); }); diff --git a/path/posix/normalize.ts b/path/posix/normalize.ts index 91bc0e6fa..c27d5e663 100644 --- a/path/posix/normalize.ts +++ b/path/posix/normalize.ts @@ -4,6 +4,7 @@ import { assertArg } from "../_common/normalize.ts"; import { normalizeString } from "../_common/normalize_string.ts"; import { isPosixPathSeparator } from "./_util.ts"; +import { fromFileUrl } from "./from_file_url.ts"; /** * Normalize the `path`, resolving `'..'` and `'.'` segments. @@ -19,28 +20,32 @@ import { isPosixPathSeparator } from "./_util.ts"; * assertEquals(path, "/foo/bar/baz/asdf"); * ``` * - * @example Working with URLs + * @param path The path to normalize. + * @returns The normalized path. + */ +export function normalize(path: string): string; +/** + * Normalize the `path`, resolving `'..'` and `'.'` segments. + * Note that resolving these segments does not necessarily mean that all will be eliminated. + * A `'..'` at the top-level will be preserved, and an empty path is canonically `'.'`. * - * Note: This function will remove the double slashes from a URL's scheme. - * Hence, do not pass a full URL to this function. Instead, pass the pathname of - * the URL. + * @experimental **UNSTABLE**: New API, yet to be vetted. * + * @example Usage * ```ts * import { normalize } from "@std/path/posix/normalize"; * import { assertEquals } from "@std/assert"; * - * const url = new URL("https://deno.land"); - * url.pathname = normalize("//std//assert//.//mod.ts"); - * assertEquals(url.href, "https://deno.land/std/assert/mod.ts"); - * - * url.pathname = normalize("std/assert/../async/retry.ts"); - * assertEquals(url.href, "https://deno.land/std/async/retry.ts"); + * assertEquals(normalize("/foo/bar//baz/asdf/quux/.."), "/foo/bar/baz/asdf"); + * assertEquals(normalize(new URL("file:///foo/bar//baz/asdf/quux/..")), "/foo/bar/baz/asdf/"); * ``` * - * @param path The path to normalize. + * @param path The path to normalize. Path can be a string or a file URL object. * @returns The normalized path. */ -export function normalize(path: string): string { +export function normalize(path: string | URL): string; +export function normalize(path: string | URL): string { + path = path instanceof URL ? fromFileUrl(path) : path; assertArg(path); const isAbsolute = isPosixPathSeparator(path.charCodeAt(0)); diff --git a/path/windows/normalize.ts b/path/windows/normalize.ts index ddecd10fa..29bdd0c83 100644 --- a/path/windows/normalize.ts +++ b/path/windows/normalize.ts @@ -5,6 +5,7 @@ import { assertArg } from "../_common/normalize.ts"; import { CHAR_COLON } from "../_common/constants.ts"; import { normalizeString } from "../_common/normalize_string.ts"; import { isPathSeparator, isWindowsDeviceRoot } from "./_util.ts"; +import { fromFileUrl } from "./from_file_url.ts"; /** * Normalize the `path`, resolving `'..'` and `'.'` segments. @@ -23,7 +24,29 @@ import { isPathSeparator, isWindowsDeviceRoot } from "./_util.ts"; * @param path The path to normalize * @returns The normalized path */ -export function normalize(path: string): string { +export function normalize(path: string): string; +/** + * Normalize the `path`, resolving `'..'` and `'.'` segments. + * Note that resolving these segments does not necessarily mean that all will be eliminated. + * A `'..'` at the top-level will be preserved, and an empty path is canonically `'.'`. + * + * @experimental **UNSTABLE**: New API, yet to be vetted. + * + * @example Usage + * ```ts + * import { normalize } from "@std/path/windows/normalize"; + * import { assertEquals } from "@std/assert"; + * + * assertEquals(normalize("C:\\foo\\..\\bar"), "C:\\bar"); + * assertEquals(normalize(new URL("file:///C:/foo/../bar")), "C:\\bar"); + * ``` + * + * @param path The path to normalize. Path can be a string or a file URL object. + * @returns The normalized path + */ +export function normalize(path: string | URL): string; +export function normalize(path: string | URL): string { + path = path instanceof URL ? fromFileUrl(path) : path; assertArg(path); const len = path.length;