diff --git a/path/join.ts b/path/join.ts index 3c09ef727a29..dc86795e1611 100644 --- a/path/join.ts +++ b/path/join.ts @@ -23,6 +23,26 @@ import { join as windowsJoin } from "./windows/join.ts"; * @param paths Paths to be joined and normalized. * @returns The joined and normalized path. */ -export function join(...paths: string[]): string { - return isWindows ? windowsJoin(...paths) : posixJoin(...paths); +export function join(...paths: string[]): string; +/** + * Join all given a sequence of `paths`, then normalizes the resulting path. + * + * @experimental **UNSTABLE**: New API, yet to be vetted. + * + * @example Usage + * ```ts + * import { join } from "@std/path/posix/join"; + * import { assertEquals } from "@std/assert"; + * + * const path = join(new URL("file:///foo"), "bar", "baz/asdf", "quux", ".."); + * assertEquals(path, "/foo/bar/baz/asdf"); + * ``` + * + * @param path The path to join. This can be string or file URL. + * @param paths The paths to join. + * @returns The joined path. + */ +export function join(path?: URL | string, ...paths: string[]): string; +export function join(path?: URL | string, ...paths: string[]): string { + return isWindows ? windowsJoin(path, ...paths) : posixJoin(path, ...paths); } diff --git a/path/join_test.ts b/path/join_test.ts index c23d725b8073..c91a9c9a5f55 100644 --- a/path/join_test.ts +++ b/path/join_test.ts @@ -6,7 +6,9 @@ import { join } from "./join.ts"; const backslashRE = /\\/g; -const joinTests = +type TestCase = [string[] | [URL, ...string[]], string]; + +const joinTests: TestCase[] = // arguments result [ [[".", "x/b", "..", "/b/c.js"], "x/b/c.js"], @@ -56,10 +58,18 @@ const joinTests = [["/", "", "/foo"], "/foo"], [["", "/", "foo"], "/foo"], [["", "/", "/foo"], "/foo"], + + // URLs + [[new URL("file:///"), "x/b", "..", "/b/c.js"], "/x/b/c.js"], + [[new URL("file:///foo"), "../../../bar"], "/bar"], + [ + [new URL("file:///foo"), "bar", "baz/asdf", "quux", ".."], + "/foo/bar/baz/asdf", + ], ]; // Windows-specific join tests -const windowsJoinTests = [ +const windowsJoinTests: TestCase[] = [ // arguments result // UNC path expected [["//foo/bar"], "\\\\foo\\bar\\"], @@ -106,11 +116,15 @@ const windowsJoinTests = [ [["c:.", "file"], "c:file"], [["c:", "/"], "c:\\"], [["c:", "file"], "c:\\file"], + // URLs + [[new URL("file:///c:")], "c:\\"], + [[new URL("file:///c:"), "file"], "c:\\file"], + [[new URL("file:///c:/"), "file"], "c:\\file"], ]; Deno.test("posix.join()", function () { joinTests.forEach(function (p) { - const _p = p[0] as string[]; + const _p = p[0]; const actual = posix.join.apply(null, _p); assertEquals(actual, p[1]); }); @@ -118,12 +132,12 @@ Deno.test("posix.join()", function () { Deno.test("windows.join()", function () { joinTests.forEach(function (p) { - const _p = p[0] as string[]; + const _p = p[0]; const actual = windows.join.apply(null, _p).replace(backslashRE, "/"); assertEquals(actual, p[1]); }); windowsJoinTests.forEach(function (p) { - const _p = p[0] as string[]; + const _p = p[0]; const actual = windows.join.apply(null, _p); assertEquals(actual, p[1]); }); diff --git a/path/posix/join.ts b/path/posix/join.ts index 032b1e63ad0c..4500650a448b 100644 --- a/path/posix/join.ts +++ b/path/posix/join.ts @@ -3,6 +3,7 @@ import { assertPath } from "../_common/assert_path.ts"; import { normalize } from "./normalize.ts"; +import { fromFileUrl } from "./from_file_url.ts"; /** * Join all given a sequence of `paths`,then normalizes the resulting path. @@ -16,24 +17,33 @@ import { normalize } from "./normalize.ts"; * assertEquals(path, "/foo/bar/baz/asdf"); * ``` * - * @example Working with URLs + * @param paths The paths to join. + * @returns The joined path. + */ +export function join(...paths: string[]): string; +/** + * Join all given a sequence of `paths`, then normalizes the resulting path. + * + * @experimental **UNSTABLE**: New API, yet to be vetted. + * + * @example Usage * ```ts * import { join } from "@std/path/posix/join"; * import { assertEquals } from "@std/assert"; * - * const url = new URL("https://deno.land"); - * url.pathname = join("std", "path", "mod.ts"); - * assertEquals(url.href, "https://deno.land/std/path/mod.ts"); - * - * url.pathname = join("//std", "path/", "/mod.ts"); - * assertEquals(url.href, "https://deno.land/std/path/mod.ts"); + * const path = join(new URL("file:///foo"), "bar", "baz/asdf", "quux", ".."); + * assertEquals(path, "/foo/bar/baz/asdf"); * ``` * + * @param path The path to join. This can be string or file URL. * @param paths The paths to join. * @returns The joined path. */ -export function join(...paths: string[]): string { - if (paths.length === 0) return "."; +export function join(path?: URL | string, ...paths: string[]): string; +export function join(path?: URL | string, ...paths: string[]): string { + if (path === undefined) return "."; + path = path instanceof URL ? fromFileUrl(path) : path; + paths = path ? [path, ...paths] : paths; paths.forEach((path) => assertPath(path)); const joined = paths.filter((path) => path.length > 0).join("/"); return joined === "" ? "." : normalize(joined); diff --git a/path/windows/join.ts b/path/windows/join.ts index 58f0e9592149..029b36e85200 100644 --- a/path/windows/join.ts +++ b/path/windows/join.ts @@ -4,6 +4,7 @@ import { assertPath } from "../_common/assert_path.ts"; import { isPathSeparator } from "./_util.ts"; import { normalize } from "./normalize.ts"; +import { fromFileUrl } from "./from_file_url.ts"; /** * Join all given a sequence of `paths`,then normalizes the resulting path. @@ -20,7 +21,29 @@ import { normalize } from "./normalize.ts"; * @param paths The paths to join. * @returns The joined path. */ -export function join(...paths: string[]): string { +export function join(...paths: string[]): string; +/** + * Join all given a sequence of `paths`, then normalizes the resulting path. + * + * @experimental **UNSTABLE**: New API, yet to be vetted. + * + * @example Usage + * ```ts + * import { join } from "@std/path/windows/join"; + * import { assertEquals } from "@std/assert"; + * + * const joined = join(new URL("file:///C:/foo"), "bar", "baz\\.."); + * assertEquals(joined, "C:\\foo\\bar"); + * ``` + * + * @param path The path to join. This can be string or file URL. + * @param paths The paths to join. + * @returns The joined path. + */ +export function join(path?: URL | string, ...paths: string[]): string; +export function join(path?: URL | string, ...paths: string[]): string { + path = path instanceof URL ? fromFileUrl(path) : path; + paths = path ? [path, ...paths] : paths; paths.forEach((path) => assertPath(path)); paths = paths.filter((path) => path.length > 0); if (paths.length === 0) return ".";