feat(path/unstable): support URL as 1st arg of basename() (#5900)

Co-authored-by: Asher Gomez <ashersaupingomez@gmail.com>
This commit is contained in:
Yoshiya Hinosawa 2024-09-04 12:39:39 +09:00 committed by GitHub
parent b808ee6de6
commit 7e2616031a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 110 additions and 19 deletions

View File

@ -28,8 +28,37 @@ import { basename as windowsBasename } from "./windows/basename.ts";
*
* @returns The basename of the path.
*/
export function basename(path: string, suffix = ""): string {
export function basename(path: string, suffix?: string): string;
/**
* Return the last portion of a path.
*
* @experimental **UNSTABLE**: New API, yet to be vetted.
*
* The trailing directory separators are ignored, and optional suffix is
* removed.
*
* @example Usage
* ```ts
* import { basename } from "@std/path/basename";
* import { assertEquals } from "@std/assert";
*
* if (Deno.build.os === "windows") {
* assertEquals(basename(new URL("file:///C:/user/Documents/image.png")), "image.png");
* } else {
* assertEquals(basename(new URL("file:///home/user/Documents/image.png")), "image.png");
* }
* ```
*
* @param path Path to extract the name from.
* @param suffix Suffix to remove from extracted name.
*
* @returns The basename of the path.
*/
export function basename(path: string | URL, suffix?: string): string;
export function basename(path: string | URL, suffix = ""): string {
return isWindows
? windowsBasename(path, suffix)
: posixBasename(path, suffix);
// deno-lint-ignore no-explicit-any
? windowsBasename(path as any, suffix)
// deno-lint-ignore no-explicit-any
: posixBasename(path as any, suffix);
}

View File

@ -1,7 +1,7 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// Copyright the Browserify authors. MIT License.
// Ported from https://github.com/browserify/path-browserify/
import { assertEquals } from "@std/assert";
import { assertEquals, assertThrows } from "@std/assert";
import { basename } from "./basename.ts";
import * as posix from "./posix/mod.ts";
import * as windows from "./windows/mod.ts";
@ -58,6 +58,15 @@ const POSIX_TESTSUITE = [
[["///"], "/"],
[["///", "bbb"], "/"],
[["//", "bbb"], "/"],
[[new URL("file:///dir/basename.ext")], "basename.ext"],
[[new URL("file:///basename.ext"), ".ext"], "basename"],
[[new URL("file:///dir/basename.ext")], "basename.ext"],
[[new URL("file:///aaa/bbb/")], "bbb"],
[[new URL("file:///aaa/bbb"), "b"], "bb"],
[[new URL("file:///aaa/bbb"), "bb"], "b"],
[[new URL("file:///aaa/bbb"), "bbb"], "bbb"],
[[new URL("file:///aaa/bbb"), "a/bbb"], "bbb"],
[[new URL("file://///a")], "a"],
] as const;
const WIN32_TESTSUITE = [
@ -84,6 +93,10 @@ const WIN32_TESTSUITE = [
[["C:basename.ext\\\\"], "basename.ext"],
[["C:foo"], "foo"],
[["file:stream"], "file:stream"],
[[new URL("file:///")], "\\"],
[[new URL("file:///C:/")], "\\"],
[[new URL("file:///C:/aaa")], "aaa"],
[[new URL("file://///")], "\\"],
] as const;
Deno.test("posix.basename()", function () {
@ -92,7 +105,8 @@ Deno.test("posix.basename()", function () {
}
for (const [[name, suffix], expected] of POSIX_TESTSUITE) {
assertEquals(posix.basename(name, suffix), expected);
// deno-lint-ignore no-explicit-any
assertEquals(posix.basename(name as any, suffix), expected);
}
// On unix a backslash is just treated as any other character.
@ -114,17 +128,36 @@ Deno.test("posix.basename()", function () {
);
});
Deno.test("posix.basename() throws with non-file URL", () => {
assertThrows(
() => posix.basename(new URL("https://deno.land/")),
TypeError,
'URL must be a file URL: received "https:"',
);
});
Deno.test("windows.basename()", function () {
for (const [[name, suffix], expected] of WIN32_TESTSUITE) {
assertEquals(windows.basename(name, suffix), expected);
// deno-lint-ignore no-explicit-any
assertEquals(windows.basename(name as any, suffix), expected);
}
// windows should pass all "forward slash" posix tests as well.
for (const [[name, suffix], expected] of COREUTILS_TESTSUITE) {
assertEquals(windows.basename(name, suffix), expected);
// deno-lint-ignore no-explicit-any
assertEquals(windows.basename(name as any, suffix), expected);
}
for (const [[name, suffix], expected] of POSIX_TESTSUITE) {
assertEquals(windows.basename(name, suffix), expected);
// deno-lint-ignore no-explicit-any
assertEquals(windows.basename(name as any, suffix), expected);
}
});
Deno.test("windows.basename() throws with non-file URL", () => {
assertThrows(
() => windows.basename(new URL("https://deno.land/")),
TypeError,
'URL must be a file URL: received "https:"',
);
});

View File

@ -8,6 +8,7 @@ import {
} from "../_common/basename.ts";
import { stripTrailingSeparators } from "../_common/strip_trailing_separators.ts";
import { isPosixPathSeparator } from "./_util.ts";
import { fromFileUrl } from "./from_file_url.ts";
/**
* Return the last portion of a `path`.
@ -23,28 +24,33 @@ import { isPosixPathSeparator } from "./_util.ts";
* assertEquals(basename("/home/user/Documents/image.png", ".png"), "image");
* ```
*
* @example Working with URLs
* @param path The path to extract the name from.
* @param suffix The suffix to remove from extracted name.
* @returns The extracted name.
*/
export function basename(path: string, suffix?: string): string;
/**
* Return the last portion of a `path`.
* Trailing directory separators are ignored, and optional suffix is removed.
*
* Note: This function doesn't automatically strip hash and query parts from
* URLs. If your URL contains a hash or query, remove them before passing the
* URL to the function. This can be done by passing the URL to `new URL(url)`,
* and setting the `hash` and `search` properties to empty strings.
* @experimental **UNSTABLE**: New API, yet to be vetted.
*
* @example Usage
* ```ts
* import { basename } from "@std/path/posix/basename";
* import { assertEquals } from "@std/assert";
*
* assertEquals(basename("https://deno.land/std/path/mod.ts"), "mod.ts");
* assertEquals(basename("https://deno.land/std/path/mod.ts", ".ts"), "mod");
* assertEquals(basename("https://deno.land/std/path/mod.ts?a=b"), "mod.ts?a=b");
* assertEquals(basename("https://deno.land/std/path/mod.ts#header"), "mod.ts#header");
* assertEquals(basename(new URL("file:///home/user/Documents/image.png")), "image.png");
* assertEquals(basename(new URL("file:///home/user/Documents/image.png"), ".png"), "image");
* ```
*
* @param path The path to extract the name from.
* @param suffix The suffix to remove from extracted name.
* @returns The extracted name.
*/
export function basename(path: string, suffix = ""): string {
export function basename(path: URL, suffix?: string): string;
export function basename(path: string | URL, suffix = ""): string {
path = path instanceof URL ? fromFileUrl(path) : path;
assertArgs(path, suffix);
const lastSegment = lastPathSegment(path, isPosixPathSeparator);

View File

@ -9,6 +9,7 @@ import {
import { CHAR_COLON } from "../_common/constants.ts";
import { stripTrailingSeparators } from "../_common/strip_trailing_separators.ts";
import { isPathSeparator, isWindowsDeviceRoot } from "./_util.ts";
import { fromFileUrl } from "./from_file_url.ts";
/**
* Return the last portion of a `path`.
@ -28,7 +29,29 @@ import { isPathSeparator, isWindowsDeviceRoot } from "./_util.ts";
* @param suffix The suffix to remove from extracted name.
* @returns The extracted name.
*/
export function basename(path: string, suffix = ""): string {
export function basename(path: string, suffix?: string): string;
/**
* Return the last portion of a `path`.
* Trailing directory separators are ignored, and optional suffix is removed.
*
* @experimental **UNSTABLE**: New API, yet to be vetted.
*
* @example Usage
* ```ts
* import { basename } from "@std/path/windows/basename";
* import { assertEquals } from "@std/assert";
*
* assertEquals(basename(new URL("file:///C:/user/Documents/image.png")), "image.png");
* assertEquals(basename(new URL("file:///C:/user/Documents/image.png"), ".png"), "image");
* ```
*
* @param path The path to extract the name from.
* @param suffix The suffix to remove from extracted name.
* @returns The extracted name.
*/
export function basename(path: URL, suffix?: string): string;
export function basename(path: string | URL, suffix = ""): string {
path = path instanceof URL ? fromFileUrl(path) : path;
assertArgs(path, suffix);
// Check for a drive letter prefix so as not to mistake the following