@oazmi/kitchensink
Version:
a collection of personal utility functions
1,090 lines • 68.9 kB
TypeScript
/** utility tools for manipulating paths and obtaining `URL`s.
*
* TODO: write document level examples.
*
* url terminology:
* - urls are a subset of uris
* - a url protocol is defined as: `[scheme]://`
* - a url is defined as: `[scheme]://[host]/[path]?[queryString]#[fragmentHash]`
* - or equivalently, a url is: `[protocol][host]/[path]?[queryString]#[fragmentHash]`
* - a uri is defined as: `[scheme]:[someIdentifier]`
*
* @module
*/
import "./_dnt.polyfills.js";
type PackageUriScheme = "jsr" | "npm" | "node";
type PackageUriProtocol = "jsr:" | "npm:" | "node:";
/** recognized uri schemes (i.e. the url protocol's scheme) that are returned by {@link getUriScheme}.
* - `local`: "C://absolute/path/to/file.txt"
* - `relative`: "./path/to/file.txt" or "../path/to/file.txt"
* - `file`: "file://C://absolute/path/to/file.txt"
* - `http`: "http://example.com/path/to/file.txt"
* - `https`: "https://example.com/path/to/file.txt"
* - `data`: "data:text/plain;base64,SGVsbG9Xb3JsZA==" or "data:text/plain,HelloWorld"
* - `jsr`: "jsr:@scope/package-name"
* - `npm`: "npm:@scope/package-name" or "npm:package-name"
* - `node`: "node:module" or "node:module/submodule"
*/
export type UriScheme = undefined | "local" | "relative" | "file" | "http" | "https" | "data" | "blob" | PackageUriScheme;
/** this is global mapping of uri-protocol schemes that are identifiable by {@link getUriScheme} and {@link resolveAsUrl}.
* you may mutate this 2-tuple array to add or remove custom identifiable uri-schemes.
*
* @example
* adding a new uri protocol scheme named `"inline-scheme"` to our registry:
* ```ts
* import { assertEquals, assertThrows } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, err = assertThrows
*
* // initially, our custom "inline" scheme is unidentifiable, and cannot be used in `resolveAsUrl` as a base url
* eq(getUriScheme("inline://a/b/c.txt"), "relative")
* err(() => resolveAsUrl("./w.xyz", "inline://a/b/c.txt")) // "inline://a/b/c.txt" is identified as a relative path, and cannot be used as a base path
*
* // registering the custom protocol-scheme mapping.
* // note that you will have to declare `as any`, since the schemes are tightly defined by the type `UriScheme`.
* uriProtocolSchemeMap.push(["inline://", "inline-scheme" as any])
*
* // and now, our custom "inline" scheme becomes identifiable
* eq(getUriScheme("inline://a/b/c.txt"), "inline-scheme")
*
* // it is also now accepted by `resolveAsUrl` as a base uri
* eq(resolveAsUrl("./w.xyz", "inline://a/b/c.txt"), new URL("inline://a/b/w.xyz"))
* ```
*/
export declare const uriProtocolSchemeMap: Array<[protocol: string, scheme: UriScheme]>;
/** here, you can specify which uri schemes cannot be used as a base url for resolving a url via the {@link resolveAsUrl} function.
*
* @example
* adding a new uri protocol scheme named `"base64-scheme"` to our registry, and then forbidding it from being used as a base url:
* ```ts
* import { assertEquals, assertThrows } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, err = assertThrows
*
* // initially, our custom "base64" scheme is unidentifiable, and cannot be used in `resolveAsUrl` as a base url
* eq(getUriScheme("base64://a/b/c.txt"), "relative")
* err(() => resolveAsUrl("./w.xyz", "base64://a/b/c.txt")) // "base64://a/b/c.txt" is identified as a relative path, and cannot be used as a base path
*
* // registering the custom protocol-scheme mapping.
* // note that you will have to declare `as any`, since the schemes are tightly defined by the type `UriScheme`.
* uriProtocolSchemeMap.push(["base64://", "base64-scheme" as any])
*
* // and now, our custom "base64" scheme becomes identifiable.
* eq(getUriScheme("base64://a/b/c.txt"), "base64-scheme")
*
* // it is also now accepted by `resolveAsUrl` as a base uri
* eq(resolveAsUrl("./w.xyz", "base64://a/b/c.txt"), new URL("base64://a/b/w.xyz"))
*
* // since we don't want to make it possible to have "base64-scheme" as a base uri, so we'll put it in the forbidden list.
* // once again `as any` is needed, since the `UriScheme` is tightly defined, and its definition cannot be changed.
* forbiddenBaseUriSchemes.push("base64-scheme" as any)
* err(() => resolveAsUrl("./w.xyz", "base64://a/b/c.txt")) // "base64://a/b/c.txt" is now amongst the forbidden schemes that cannot be combined with relative paths.
* eq(resolveAsUrl("base64://a/b/c.txt"), new URL("base64://a/b/c.txt")) // this is of course not stopping us from building urls with the "base64" scheme, so long as no relative path is attached.
* ```
*/
export declare const forbiddenBaseUriSchemes: UriScheme[];
/** test whether a given path is an absolute path (either windows or posix).
*
* > [!note]
* > currently, we do consider the tilde expansion ("~") as an absolute path, even though it is not an os/fs-level path, but rather a shell feature.
* > this may result in misclassification on windows, since "~" is a valid starting character for a file or folder name
*
* @example
* ```ts
* import { assertEquals } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, fn = isAbsolutePath
*
* eq(fn("/a/b/c.txt"), true)
* eq(fn("~/a/b/c.txt"), true)
* eq(fn("C:/a/b/c.txt"), true)
* eq(fn("/c:/a/b/c.txt"), true)
*
* eq(fn("a/b/c.txt"), false)
* eq(fn("./a/b/c.txt"), false)
* eq(fn("../a/b/c.txt"), false)
* ```
*/
export declare const isAbsolutePath: (path: string) => boolean;
/** guesses the scheme of a url string. see {@link UriScheme} for more details.
*
* @example
* ```ts
* import { assertEquals } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, fn = getUriScheme
*
* eq(fn("C:/Users/me/path/to/file.txt"), "local")
* eq(fn("~/path/to/file.txt"), "local")
* eq(fn("/usr/me/path/to/file.txt"), "local")
* eq(fn("path/to/file.txt"), "relative")
* eq(fn("./path/to/file.txt"), "relative")
* eq(fn("../path/to/file.txt"), "relative")
* eq(fn("file:///c://users/me/path/to/file.txt"), "file")
* eq(fn("file:///usr/me/path/to/file.txt"), "file")
* eq(fn("jsr:@user/path/to/file"), "jsr")
* eq(fn("jsr:/@user/path/to/file"), "jsr")
* eq(fn("npm:lib/path/to/file"), "npm")
* eq(fn("npm:/lib/path/to/file"), "npm")
* eq(fn("npm:/@scope/lib/path/to/file"), "npm")
* eq(fn("node:http"), "node")
* eq(fn("node:fs/promises"), "node")
* eq(fn("data:text/plain;charset=utf-8;base64,aGVsbG8="), "data")
* eq(fn("blob:https://example.com/4800d2d8-a78c-4895"), "blob")
* eq(fn("http://google.com/style.css"), "http")
* eq(fn("https://google.com/style.css"), "https")
* eq(fn(""), undefined)
* ```
*/
export declare const getUriScheme: (path: string) => UriScheme;
/** a description of a parsed jsr/npm package, that somewhat resembles the properties of regular URL. */
export interface PackagePseudoUrl {
/** the full package string, compatible to use with the `URL` constructor.
*
* > [!note]
* > this string is IS uri-encoded.
* > however, vs-code doc popup may decode the uri-encoded string,
* > giving a deceptive representation.
*
* examples:
* - `jsr:/@scope/package@version/pathname`
* - `jsr:/@scope/package`
* - `npm:/package@version/pathname`
* - `npm:/@scope/package@version`
* - `npm:/@scope/package@1.0%20-%201.2` // the version range is "1.0 - 1.2"
* - `npm:/@scope/package@%5E29` // the version range is "^9"
* - `node:/http`
* - `node:/fs/promises`
*/
href: string | `${PackageUriScheme}:/${PackagePseudoUrl["host"]}${PackagePseudoUrl["pathname"]}`;
protocol: PackageUriProtocol;
/** optional scope name. */
scope?: string;
/** name of the package. the reason why we call it "pkg" instead of "package" is because "package" is a reserved word in javascript. */
pkg: string;
/** optional version string of the package. */
version?: string;
/** the pathname of the subpath that is being accessed within the package.
* this will always begin with a leading slash ("/"), even if there is no subpath being accessed.
*/
pathname: string;
/** the host contains the full information about the package's string.
* that is, it has the optional scope information, the package name information, and the optional version information.
*
* > [!note]
* > this string is NOT uri-encoded, unlike {@link href}.
*/
host: string | `${PackagePseudoUrl["pkg"]}` | `${PackagePseudoUrl["pkg"]}@${PackagePseudoUrl["version"]}` | `@${PackagePseudoUrl["scope"]}/${PackagePseudoUrl["pkg"]}` | `@${PackagePseudoUrl["scope"]}/${PackagePseudoUrl["pkg"]}@${PackagePseudoUrl["version"]}`;
}
/** this function parses npm and jsr package strings, and returns a pseudo URL-like object.
*
* the regex we use for parsing the input `href` string is quoted below:
* > /^(?<protocol>jsr:|npm:|node:)(\/*(@(?<scope>[^\/\s]+)\/)?(?<pkg>[^@\/\s]+)(@(?<version>[^\/\r\n\t\f\v]+))?)?(?<pathname>\/.*)?$/
*
* see the regex in action with the test cases on regex101 link: [regex101.com/r/mX3v1z/2](https://regex101.com/r/mX3v1z/2)
*
* @throws `Error` an error will be thrown if either the package name (`pkg`), or the `protocol` cannot be deduced by the regex.
*
* @example
* ```ts
* import { assertEquals, assertThrows } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, err = assertThrows, fn = parsePackageUrl
*
* // basic breakdown of a package's resource uri
* eq(fn("jsr:@scope/package@version/pathname/file.ts"), {
* href: "jsr:/@scope/package@version/pathname/file.ts",
* protocol: "jsr:",
* scope: "scope",
* pkg: "package",
* version: "version",
* pathname: "/pathname/file.ts",
* host: "@scope/package@version",
* })
*
* // showing that jsr package uri's without a scope are perfectly permitted.
* // even though it isn't actually possible to do so on "jsr.io".
* // thus it is left up to the end-user to make of it what they will.
* eq(fn("jsr:package@version/pathname/"), {
* href: "jsr:/package@version/pathname/",
* protocol: "jsr:",
* scope: undefined,
* pkg: "package",
* version: "version",
* pathname: "/pathname/",
* host: "package@version",
* })
*
* // testing a case with multiple slashes ("/") after the protocol colon (":"), and no trailing slash after the version
* eq(fn("npm:///@scope/package@version"), {
* href: "npm:/@scope/package@version/",
* protocol: "npm:",
* scope: "scope",
* pkg: "package",
* version: "version",
* pathname: "/",
* host: "@scope/package@version",
* })
*
* // testing a no-scope and no-version case
* eq(fn("npm:package"), {
* href: "npm:/package/",
* protocol: "npm:",
* scope: undefined,
* pkg: "package",
* version: undefined,
* pathname: "/",
* host: "package",
* })
*
* // testing the "node:" protocol
* eq(fn("node:fs"), {
* href: "node:/fs/",
* protocol: "node:",
* scope: undefined,
* pkg: "fs",
* version: undefined,
* pathname: "/",
* host: "fs",
* })
*
* // testing the "node:" protocol with a certain pathname
* eq(fn("node:fs/promises"), {
* href: "node:/fs/promises",
* protocol: "node:",
* scope: undefined,
* pkg: "fs",
* version: undefined,
* pathname: "/promises",
* host: "fs",
* })
*
* // testing a `version` query string that contains whitespaces and url-encoded characters.
* // NOTE: the url-encoded characters in vs-code's doc popup appear decoded, so don't be fooled!
* // but the `host` is always a url-decoded string.
* eq(fn("jsr:@scope/package@1.0.0 - 1.2.0/pathname/file.ts"), {
* href: "jsr:/@scope/package@1.0.0%20-%201.2.0/pathname/file.ts",
* protocol: "jsr:",
* scope: "scope",
* pkg: "package",
* version: "1.0.0 - 1.2.0",
* pathname: "/pathname/file.ts",
* host: "@scope/package@1.0.0 - 1.2.0",
* })
*
* // testing a `version` query string that has its some of its characters (such as whitespaces) url-encoded.
* // NOTE: the url-encoded characters in vs-code's doc popup appear decoded, so don't be fooled!
* // but the `host` is always a url-decoded string.
* eq(fn("jsr:@scope/package@^2%20<2.2%20||%20>%202.3/pathname/file.ts"), {
* href: "jsr:/@scope/package@%5E2%20%3C2.2%20%7C%7C%20%3E%202.3/pathname/file.ts",
* protocol: "jsr:",
* scope: "scope",
* pkg: "package",
* version: "^2 <2.2 || > 2.3",
* pathname: "/pathname/file.ts",
* host: "@scope/package@^2 <2.2 || > 2.3",
* })
*
* // testing cases where an error should be invoked
* err(() => fn("npm:@scope/")) // missing a package name
* err(() => fn("npm:@scope//package")) // more than one slash after scope
* err(() => fn("pnpm:@scope/package@version")) // only "node:", "npm:", and "jsr:" protocols are recognized
* ```
*/
export declare const parsePackageUrl: (url_href: string | URL) => PackagePseudoUrl;
/** convert a url string to an actual `URL` object.
* your input `path` url can use any scheme supported by the {@link getUriScheme} function.
* and you may also use paths with windows dir-separators ("\\"), as this function implicitly converts them a posix separator ("/").
*
* if you pass a `URL` object, then it will be returned as is.
*
* @throws `Error` an error will be thrown if `base` uri is either a relative path, or uses a data uri scheme,
* or if the provided `path` is relative, but no absolute `base` path is provided.
*
* @example
* ```ts
* import { assertEquals, assertThrows } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, err = assertThrows, fn = resolveAsUrl
*
* eq(fn(new URL("some://url:8000/a/b c.txt")), new URL("some://url:8000/a/b%20c.txt"))
*
* eq(fn("/a/b/c d e.txt"), new URL("file:///a/b/c%20d%20e.txt"))
* eq(fn("~/a/b/c.txt"), new URL("file://~/a/b/c.txt"))
* eq(fn("C:/a/b/c/d/e.txt"), new URL("file:///C:/a/b/c/d/e.txt"))
* eq(fn("C:\\a\\b\\c\\d\\e.txt"), new URL("file:///C:/a/b/c/d/e.txt"))
* eq(fn("./e/f g.txt", "C:/a\\b\\c d/"), new URL("file:///C:/a/b/c%20d/e/f%20g.txt"))
* eq(fn("../c d/e/f g.txt", "C:/a/b/c d/"), new URL("file:///C:/a/b/c%20d/e/f%20g.txt"))
* eq(fn("d/../.././e.txt", "C:/a/b/c/"), new URL("file:///C:/a/b/e.txt"))
* eq(fn("d/../.././e.txt", "C:/a/b/c"), new URL("file:///C:/a/e.txt"))
* eq(fn("D:/a/b.txt", "C:/c/d.txt"), new URL("file:///D:/a/b.txt"))
* eq(fn("/a/b.txt", "C:/c/d.txt"), new URL("file:///C:/a/b.txt"))
* eq(fn("/a/b.txt", "/sys/admin/"), new URL("file:///a/b.txt"))
* eq(fn("/a/b.txt", ""), new URL("file:///a/b.txt"))
*
* eq(fn("http://cdn.esm.sh/a/b/c.txt"), new URL("http://cdn.esm.sh/a/b/c.txt"))
* eq(fn("http://cdn.esm.sh/a.txt", "file:///b/"), new URL("http://cdn.esm.sh/a.txt"))
* eq(fn("http://cdn.esm.sh/a.txt", "/b/"), new URL("http://cdn.esm.sh/a.txt"))
* eq(fn("/a/b/c.txt", "http://cdn.esm.sh/"), new URL("http://cdn.esm.sh/a/b/c.txt"))
*
* eq(fn("b/c.txt", "http://cdn.esm.sh/a/"), new URL("http://cdn.esm.sh/a/b/c.txt"))
* eq(fn("b/c.txt", "http://cdn.esm.sh/a"), new URL("http://cdn.esm.sh/b/c.txt"))
* eq(fn("./b/c.txt", "http://cdn.esm.sh/a/"), new URL("http://cdn.esm.sh/a/b/c.txt"))
* eq(fn("./b/c.txt", "http://cdn.esm.sh/a"), new URL("http://cdn.esm.sh/b/c.txt"))
* eq(fn("../b/c.txt", "https://cdn.esm.sh/a/"), new URL("https://cdn.esm.sh/b/c.txt"))
* eq(fn("../c/d.txt", "https://cdn.esm.sh/a/b"), new URL("https://cdn.esm.sh/c/d.txt"))
* eq(fn("/c/d.txt", "https://cdn.esm.sh/a/b"), new URL("https://cdn.esm.sh/c/d.txt"))
*
* eq(fn("node:fs"), new URL("node:/fs/"))
* eq(fn("node:fs/promises"), new URL("node:/fs/promises"))
* eq(fn("promises", "node:fs"), new URL("node:/fs/promises"))
* eq(fn("./promises", "node:fs"), new URL("node:/fs/promises"))
* eq(fn("./promises", "node:fs/"), new URL("node:/fs/promises"))
* eq(fn("mkdir", "node:fs/promises"), new URL("node:/fs/mkdir"))
* eq(fn("mkdir", "node:fs/promises/"), new URL("node:/fs/promises/mkdir"))
* eq(fn("./mkdir", "node:fs/promises"), new URL("node:/fs/mkdir"))
* eq(fn("./mkdir", "node:fs/promises/"), new URL("node:/fs/promises/mkdir"))
* eq(fn("/sync", "node:fs/promises/mkdir"), new URL("node:/fs/sync"))
*
* eq(fn("npm:react"), new URL("npm:/react/"))
* eq(fn("npm:react/file.txt"), new URL("npm:/react/file.txt"))
* eq(fn("npm:@facebook/react"), new URL("npm:/@facebook/react/"))
* eq(fn("./to/file.txt", "npm:react"), new URL("npm:/react/to/file.txt"))
* eq(fn("./to/file.txt", "npm:react/"), new URL("npm:/react/to/file.txt"))
* eq(fn("/to/file.txt", "npm:react/native/bin"), new URL("npm:/react/to/file.txt"))
* eq(fn("npm:react@19/jsx runtime.ts"), new URL("npm:/react@19/jsx%20runtime.ts"))
* eq(fn("npm:react@^19 <19.5/jsx.ts"), new URL("npm:/react@%5E19%20%3C19.5/jsx.ts"))
*
* eq(fn("jsr:@scope/my-lib/b.txt"), new URL("jsr:/@scope/my-lib/b.txt"))
* eq(fn("a/b.txt", "jsr:///@scope/my-lib"), new URL("jsr:/@scope/my-lib/a/b.txt"))
* eq(fn("./a/b.txt", "jsr:///@scope/my-lib"), new URL("jsr:/@scope/my-lib/a/b.txt"))
* eq(fn("a/b.txt", "jsr:///@scope/my-lib/c"), new URL("jsr:/@scope/my-lib/a/b.txt"))
* eq(fn("./a/b.txt", "jsr:///@scope/my-lib/c"), new URL("jsr:/@scope/my-lib/a/b.txt"))
* eq(fn("./a/b.txt", "jsr:///@scope/my-lib//c"), new URL("jsr:/@scope/my-lib/a/b.txt"))
* eq(fn("../a/b.txt", "jsr:/@scope/my-lib///c/"), new URL("jsr:/@scope/my-lib/a/b.txt"))
* eq(fn("./a/b.txt", "jsr:///@scope/my-lib/c/"), new URL("jsr:/@scope/my-lib/c/a/b.txt"))
* eq(fn("/a/b.txt", "jsr:my-lib/x/y/"), new URL("jsr:/my-lib/a/b.txt"))
* eq(fn("/a/b.txt", "jsr:@scope/my-lib/x/y/z"), new URL("jsr:/@scope/my-lib/a/b.txt"))
* eq(fn("/a/b.txt", "jsr:my-lib@1 || 2/x/y/z"), new URL("jsr:/my-lib@1%20%7C%7C%202/a/b.txt"))
* eq(fn("/a/b.txt", "jsr:@my/lib@1||2/x/y/z"), new URL("jsr:/@my/lib@1%7C%7C2/a/b.txt"))
*
* eq(fn("C:/a/b.txt", "jsr:@my/lib/x/y"), new URL("file:///C:/a/b.txt"))
* eq(fn("jsr:@my/lib/x/y", "C:/a/b.txt"), new URL("jsr:/@my/lib/x/y"))
* eq(fn("http://test.io/abc", "C:/a/b.txt"), new URL("http://test.io/abc"))
*
* eq(fn("blob:https://example.com/480-a78"), new URL("blob:https://example.com/480-a78"))
* eq(fn("data:text/plain;utf8,hello"), new URL("data:text/plain;utf8,hello"))
* eq(fn("data:text/plain;utf8,hello", "C:/a/b/"), new URL("data:text/plain;utf8,hello"))
*
* err(() => fn("./a/b.txt", "data:text/plain;charset=utf-8;base64,aGVsbG8="))
* err(() => fn("./a/b.txt", "blob:https://example.com/4800d2d8-a78c-4895-b68b-3690b69a0d6a"))
* err(() => fn("./a/b.txt", "./path/")) // a base path must not be relative
* err(() => fn("./a/b.txt")) // a relative path cannot be resolved on its own without a base path
* err(() => fn("./a/b.txt", "")) // an empty base path is as good as a non-existing one
* err(() => fn("fs/promises", "node:")) // the base protocol ("node:") MUST be accompanied with a package name
* ```
*/
export declare const resolveAsUrl: (path: string | URL, base?: string | URL | undefined) => URL;
/** trim the leading forward-slashes at the beginning of a string.
*
* @example
* ```ts
* import { assertEquals } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, fn = trimStartSlashes
*
* eq(fn("///a/b.txt//"), "a/b.txt//")
* eq(fn("/.//a/b.txt//"), ".//a/b.txt//")
* eq(fn(".///../a/b.txt//"), ".///../a/b.txt//")
* eq(fn("file:///a/b.txt//"), "file:///a/b.txt//")
* ```
*/
export declare const trimStartSlashes: (str: string) => string;
/** trim the trailing forward-slashes at the end of a string, except for those that are preceded by a dotslash ("/./") or a dotdotslash ("/../")
*
* @example
* ```ts
* import { assertEquals } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, fn = trimEndSlashes
*
* eq(fn("///a/b.zip///"), "///a/b.zip")
* eq(fn("///a/b.zip/.///"), "///a/b.zip/./")
* eq(fn("///a/b.zip/..///"), "///a/b.zip/../")
* eq(fn("///a/b.zip/...///"), "///a/b.zip/...")
* eq(fn("///a/b.zip/wut.///"), "///a/b.zip/wut.")
* eq(fn("///a/b.zip/wut..///"), "///a/b.zip/wut..")
* eq(fn("///a/b.zip/wut...///"), "///a/b.zip/wut...")
* eq(fn(".///../a/b.zip/"), ".///../a/b.zip")
* eq(fn("file:///a/b.zip//c.txt"), "file:///a/b.zip//c.txt")
* ```
*/
export declare const trimEndSlashes: (str: string) => string;
/** trim leading and trailing forward-slashes, at the beginning and end of a string.
* this is a combination of {@link trimStartSlashes} and {@link trimEndSlashes}, so see their doc comments for more precise test cases.
*
* @example
* ```ts
* import { assertEquals } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, fn = trimSlashes
*
* eq(fn("///a/b.zip//"), "a/b.zip")
* eq(fn("///a/b.zip/..///"), "a/b.zip/../")
* eq(fn("///a/b.zip/...///"), "a/b.zip/...")
* eq(fn("///a/b.zip/.//..///"), "a/b.zip/.//../")
* eq(fn("///a/b.zip/.//.///"), "a/b.zip/.//./")
* eq(fn(".///../a/b.zip//"), ".///../a/b.zip")
* eq(fn("file:///a/b.zip//c.txt"), "file:///a/b.zip//c.txt")
* ```
*/
export declare const trimSlashes: (str: string) => string;
/** ensure that there is at least one leading slash at the beginning.
*
* @example
* ```ts
* import { assertEquals } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, fn = ensureStartSlash
*
* eq(fn("a/b.zip"), "/a/b.zip")
* eq(fn(".///../a/b.zip/"), "/.///../a/b.zip/")
* eq(fn("///../a/b.zip/"), "///../a/b.zip/")
* eq(fn("file:///a/b.zip//c.txt"), "/file:///a/b.zip//c.txt")
* ```
*/
export declare const ensureStartSlash: (str: string) => string;
/** ensure that there is at least one leading dot-slash at the beginning.
*
* @example
* ```ts
* import { assertEquals } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, fn = ensureStartDotSlash
*
* eq(fn("a/b.zip"), "./a/b.zip")
* eq(fn(".///../a/b.zip/"), ".///../a/b.zip/")
* eq(fn("///../a/b.zip/"), ".///../a/b.zip/")
* eq(fn("file:///a/b.zip//c.txt"), "./file:///a/b.zip//c.txt")
* ```
*/
export declare const ensureStartDotSlash: (str: string) => string;
/** ensure that there is at least one trailing slash at the end.
*
* @example
* ```ts
* import { assertEquals } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, fn = ensureEndSlash
*
* eq(fn("///a/b.zip//"), "///a/b.zip//")
* eq(fn(".///../a/b.zip/"), ".///../a/b.zip/")
* eq(fn(".///../a/b.zip/."), ".///../a/b.zip/./")
* eq(fn(".///../a/b.zip/./"), ".///../a/b.zip/./")
* eq(fn(".///../a/b.zip/.."), ".///../a/b.zip/../")
* eq(fn(".///../a/b.zip/../"), ".///../a/b.zip/../")
* eq(fn("file:///a/b.zip//c.txt"), "file:///a/b.zip//c.txt/")
* ```
*/
export declare const ensureEndSlash: (str: string) => string;
/** trim leading forward-slashes ("/") and dot-slashes ("./"), at the beginning a string.
* but exclude non-trivial dotdotslash ("/../") from being wrongfully trimmed.
*
* @example
* ```ts
* import { assertEquals } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, fn = trimStartDotSlashes
*
* eq(fn("///a/b.zip/c.txt"), "a/b.zip/c.txt")
* eq(fn("///a/b.zip//"), "a/b.zip//")
* eq(fn("//..//a/b.zip//"), "..//a/b.zip//")
* eq(fn("/./..//a/b.zip//"), "..//a/b.zip//")
* eq(fn("/./.././a/b.zip//"), ".././a/b.zip//")
* eq(fn("///././///.////a/b.zip//"), "a/b.zip//")
* eq(fn(".///././///.////a/b.zip//"), "a/b.zip//")
* eq(fn("./././//././///.////a/b.zip//"), "a/b.zip//")
* eq(fn("file:///a/b.zip//c.txt"), "file:///a/b.zip//c.txt")
* ```
*/
export declare const trimStartDotSlashes: (str: string) => string;
/** trim all trivial trailing forward-slashes ("/") and dot-slashes ("./"), at the end a string.
* but exclude non-trivial dotdotslash ("/../") from being wrongfully trimmed.
*
* TODO: this operation is somewhat expensive, because:
* - the implementation uses regex, however it was not possible for me to design a regex that handles the input string as is,
* so I resort to reversing the input string, and using a slightly easier-to-design regex that discovers trivial (dot)slashes in reverse order,
* and then after the string replacement, I reverse it again and return it as the output.
*
* @example
* ```ts
* import { assertEquals } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, fn = trimEndDotSlashes
*
* eq(fn("a/b.zip/c.txt"), "a/b.zip/c.txt")
* eq(fn("//a/b.zip//"), "//a/b.zip")
* eq(fn("/"), "")
* eq(fn("./"), "")
* eq(fn("//././//./"), "")
* eq(fn(".//././//./"), "")
* eq(fn(".//./..///./"), ".//./../")
* eq(fn("/a/b.zip/./"), "/a/b.zip")
* eq(fn("/a/b.zip/../"), "/a/b.zip/../")
* eq(fn("/a/b.zip/..//"), "/a/b.zip/../")
* eq(fn("/a/b.zip/.././"), "/a/b.zip/../")
* eq(fn("a/b.zip///././///.////"), "a/b.zip")
* eq(fn("/a/b.zip///././///.////"), "/a/b.zip")
* eq(fn("/a/b.zip/.././/.././///.////"), "/a/b.zip/.././/../")
* eq(fn("/a/b.zip/././././///.////"), "/a/b.zip")
* eq(fn("/a/b.zip./././././///.////"), "/a/b.zip.")
* eq(fn("/a/b.zip../././././///.////"), "/a/b.zip..")
* eq(fn("/a/b.zip.../././././///.////"), "/a/b.zip...")
* eq(fn("file:///a/b.zip//c.txt"), "file:///a/b.zip//c.txt")
* ```
*/
export declare const trimEndDotSlashes: (str: string) => string;
/** trim leading and trailing forward-slashes ("/") and dot-slashes ("./"), at the beginning and end of a string, but keep trailing non-trivial ones intact.
* this is a combination of {@link trimStartDotSlashes} and {@link trimEndDotSlashes}, so see their doc comments for more precise test cases.
*
* @example
* ```ts
* import { assertEquals } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, fn = trimDotSlashes
*
* eq(fn("///a/b.zip//"), "a/b.zip")
* eq(fn(".///../a/b.zip//"), "../a/b.zip")
* eq(fn("//./././///././//../a/b.zip//"), "../a/b.zip")
* eq(fn("file:///a/b.zip//c.txt"), "file:///a/b.zip//c.txt")
* ```
*/
export declare const trimDotSlashes: (str: string) => string;
/** TODO: purge this function in the future, if you absolutely do not use it anywhere.
* @deprecated
*
* > [!note]
* > you'd probably want to use {@link joinPaths} instead of this function, for any realistic set of path segments.
* > not only is this more expensive to compute, it does not distinguish between a directory and a file path (intentionally).
*
* join path segments with forward-slashes in between, and remove redundant slashes ("/") and dotslashes ("./") around each segment (if any).
* however, the first segment's leading and trailing slashes are left untouched,
* because that would potentially strip away location information (such as relative path ("./"), or absolute path ("/"), or some uri ("file:///")).
*
* if you want to ensure that your first segment is shortened, use either the {@link normalizePath} or {@link normalizePosixPath} function on it before passing it here.
*
* > [!warning]
* > it is recommended that you use segments with posix path dir-separators ("/").
*
* @example
* ```ts
* import { assertEquals } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, fn = joinSlash
*
* eq(fn(".///../a", "b", "c.txt"), ".///../a/b/c.txt")
* eq(fn("file:///a/", "b.zip//", "./c.txt"), "file:///a/b.zip/c.txt")
* eq(fn("file:///", "a/", "b.zip//", "./c.txt"), "file:///a/b.zip/c.txt")
* eq(fn("///a//", "b.//", "zip..//"), "///a//b./zip..")
* eq(fn("a/", "b.zip", "./c.txt", ""), "a/b.zip/c.txt/")
* eq(fn("a/", "b.zip", "./c.txt", "."), "a/b.zip/c.txt/")
* eq(fn("a/", "b.zip", "./c.txt", "./"), "a/b.zip/c.txt/")
* eq(fn("a/", "b.zip", "./c.txt", ".."), "a/b.zip/c.txt/..")
* eq(fn("a/", "b.zip", "./c.txt", "..."), "a/b.zip/c.txt/...")
* eq(fn("", "", ""), "")
* eq(fn("/", "", ""), "/")
* eq(fn("/", "/", ""), "/")
* eq(fn("/", "", "/"), "/")
* eq(fn("/", "/", "/"), "/")
* eq(fn("./", "", ""), "./")
* eq(fn("./", "./", ""), "./")
* eq(fn("./", "", "./"), "./")
* eq(fn("./", "./", "./"), "./")
* eq(fn(
* "//./././///././//../a/b.zip/.////",
* "///.////././.././c.txt/./../",
* "../../d.xyz//.//",
* ), "//./././///././//../a/b.zip/.////.././c.txt/./../../../d.xyz")
* ```
*/
export declare const joinSlash: (first_segment?: string, ...segments: string[]) => string;
/** config options for {@link normalizePosixPath} and {@link normalizePath} */
export interface NormalizePathConfig {
/** specify whether or not to preserve the relative leading dotslash ("./") directory specifier?
*
* here is what you would get for each of the following input values, based on how you configure this setting:
* - `input = "././//../a/./././b.txt"`
* - `true`: `".//a/./././b.txt"`
* - `false`: `"/a/./././b.txt"`
* - `input = "./././././"`
* - `true`: `"./"`
* - `false`: `""`
* - `input = "a/b.txt/././../././"`
* - `true`: `"a"`
* - `false`: `"a"`
* - `input = "/./././/a/b.txt"`
* - `true`: `"//a/b.txt"`
* - `false`: `"//a/b.txt"`
*
* @defaultValue `true`
*/
keepRelative?: boolean;
}
/** normalize a path by reducing and removing redundant dot-slash ("./" and "../") path navigators from a path.
*
* if you provide the optional `config` with the `keepRelative` set to `false`, then in the output, there will no be leading dot-slashes ("./").
* read more about the option here: {@link NormalizePathConfig.keepRelative}.
* but note that irrespective of what you set this option to be, leading leading dotdot-slashes ("../") and leading slashes ("/") will not be trimmed.
*
* even though `config` should be of {@link NormalizePathConfig} type, it also accepts `number` so that the function's signature becomes compatible with the `Array.prototype.map` method,
* however, unless you pass the correct config object type, only the default action will be taken.
*
* > [!warning]
* > you MUST provide a posix path (i.e. use "/" for dir-separator).
* > there will not be any implicit conversion of windows "\\" dir-separator.
*
* @example
* ```ts
* import { assertEquals } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, fn = normalizePosixPath
* // aliasing the config for disabling the preservation of leading "./"
* const remove_rel: NormalizePathConfig = { keepRelative: false }
*
* eq(fn("../a/./b/../././/c.txt"), "../a//c.txt")
* eq(fn("./././a/b/.././././//c.txt"), "./a///c.txt")
* eq(fn("./././a/b/.././././//c.txt", remove_rel), "a///c.txt")
* eq(fn("/a/b/.././././//c.txt"), "/a///c.txt")
* eq(fn("///a/b/.././././//c.txt"), "///a///c.txt")
* eq(fn("///a/b/.././.././//c.txt"), "/////c.txt")
* eq(fn("file:///./././a/b/.././././c.txt"), "file:///a/c.txt")
* eq(fn("/a/../"), "/")
* // NOTICE: the test in the next line may seem like a weird behavior.
* eq(fn("/a/../../"), "")
* eq(fn("/a/../../../"), "../")
* eq(fn("./a/../../"), "../")
* eq(fn("./a/../../../"), "../../")
* eq(fn("/a/b/../.."), "/")
* eq(fn("/a/b/."), "/a/b/")
* eq(fn("/a/b/./"), "/a/b/")
* eq(fn("/a/b/c/.."), "/a/b/")
* eq(fn("/a/b/c/../."), "/a/b/")
* eq(fn("/a/b/c/d/../.."), "/a/b/")
* eq(fn("/a/b/c/../.nomedia"), "/a/b/.nomedia")
* eq(fn(""), "")
* eq(fn("."), ".")
* eq(fn(".."), "..")
* eq(fn("./"), "./")
* eq(fn("../"), "../")
* eq(fn("../."), "../")
* eq(fn("../.."), "../../")
* eq(fn("./././././"), "./")
* eq(fn(".././././"), "../")
* eq(fn("./././.././././"), "../")
* eq(fn("./././.././.././"), "../../")
* eq(fn(".", remove_rel), "")
* eq(fn("./", remove_rel), "")
* eq(fn("./././././", remove_rel), "")
* eq(fn("./././.././././", remove_rel), "../")
* eq(fn("./././.././.././", remove_rel), "../../")
* ```
*/
export declare const normalizePosixPath: (path: string, config?: NormalizePathConfig | number) => string;
/** normalize a path by reducing and removing redundant dot-slash ("./", "../", ".\\", and "..\\") path navigators from a path.
* the returned output is always a posix-style path.
*
* to read about the optional `config` parameter, refer to the docs of {@link normalizePosixPath}, which is the underlying function that takes care most of the normalization.
*
* @example
* ```ts
* import { assertEquals } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, fn = normalizePath
*
* eq(fn("../a/./b/../././/c.txt"), "../a//c.txt")
* eq(fn("./.\\.\\a\\b\\.././.\\.///c.txt"), "./a///c.txt")
* eq(fn("/home\\.config/a\\..\\...\\b\\./c.txt"), "/home/.config/.../b/c.txt")
* eq(fn("file:///./././a\\b/..\\././.\\c.txt"), "file:///a/c.txt")
* ```
*/
export declare const normalizePath: (path: string, config?: NormalizePathConfig | number) => string;
/** convert windows directory slash "\\" to posix directory slash "/".
*
* @example
* ```ts
* import { assertEquals } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, fn = pathToPosixPath
*
* eq(fn("C:\\Users/my name\\file.txt"), "C:/Users/my name/file.txt")
* eq(fn("~/path/to/file.txt"), "~/path/to/file.txt")
* eq(fn("/path\\to file.txt"), "/path/to file.txt")
* ```
*/
export declare const pathToPosixPath: (path: string) => string;
/** convert an array of paths to cli compatible list of paths, suitable for setting as an environment variable.
*
* @example
* ```ts
* import { assertEquals } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, fn = pathsToCliArg
*
* // conversion example with windows separator (";")
* eq(fn(";", [
* "./a/b/c.txt",
* "C:\\Android Studio\\sdk\\",
* "build\\libs\\"
* ]), `"./a/b/c.txt;C:/Android Studio/sdk/;build/libs/"`)
*
* // conversion example with unix separator (":")
* eq(fn(":", [
* "./a/b/c.txt",
* "~/Android Studio/sdk/",
* "build/libs/"
* ]), `"./a/b/c.txt:~/Android Studio/sdk/:build/libs/"`)
* ```
*/
export declare const pathsToCliArg: (separator: ";" | ":", paths: string[]) => string;
/** find the prefix path directory common to all provided `paths`.
* > [!warning]
* > your paths MUST be normalized beforehand, and use posix dir-separators ("/").
*
* @example
* ```ts
* import { assertEquals } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, fn = commonNormalizedPosixPath
*
* eq(fn([
* "C:/Hello/World/This/Is/An/Example/Bla.cs",
* "C:/Hello/World/This/Is/Not/An/Example/",
* "C:/Hello/Earth/Bla/Bla/Bla",
* ]), "C:/Hello/")
*
* eq(fn([
* "C:/Hello/World/This/Is/An/Example/Bla.cs",
* "C:/Hello/World/This/is/an/example/bla.cs",
* "C:/Hello/World/This/Is/Not/An/Example/",
* ]), "C:/Hello/World/This/")
*
* eq(fn([
* "./../Hello/World/Users/This/Is/An/Example/Bla.cs",
* "./../Hello/World Users/This/Is/An/example/bla.cs",
* "./../Hello/World-Users/This/Is/Not/An/Example/",
* ]), "./../Hello/")
*
* eq(fn([
* "./Hello/World/Users/This/Is/An/Example/Bla.cs",
* "./Hello/World/",
* "./Hello/World", // the "World" here segment is not treated as a directory
* ]), "./Hello/")
*
* eq(fn([
* "C:/Hello/World/",
* "/C:/Hello/World/",
* "C:/Hello/World/",
* ]), "") // no common prefix was identified
* ```
*/
export declare const commonNormalizedPosixPath: (paths: string[]) => string;
/** find the prefix path directory common to all provided `paths`.
* your input `paths` do not need to be normalized nor necessarily use posix-style separator "/".
* under the hood, this function normalizes and converts all paths to posix-style, then applies the {@link commonNormalizedPosixPath} onto them.
*
* @example
* ```ts
* import { assertEquals } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, fn = commonPath
*
* eq(fn([
* "C:/Hello/World/This/Is/An/Example/Bla.cs",
* "C:\\Hello\\World\\This\\Is\\Not/An/Example/",
* "C:/Hello/Earth/Bla/Bla/Bla",
* ]), "C:/Hello/")
*
* eq(fn([
* "./Hello/World/This/Used/to-be-an/example/../../../Is/An/Example/Bla.cs",
* ".\\Hello/World/This/Is/an/example/bla.cs",
* "./Hello/World/This/Is/Not/An/Example/",
* ]), "./Hello/World/This/Is/")
*
* eq(fn([
* "./../home/Hello/World/Users/This/Is/An/Example/Bla.cs",
* "././../home\\Hello\\World Users\\This\\Is/An\\example/bla.cs",
* "./../home/./.\\.\\././Hello/World-Users/./././././This/Is/Not/An/Example/",
* ]), "../home/Hello/")
*
* eq(fn([
* "\\C:/Hello/World/Users/This/Is/An/Example/Bla.cs",
* "/C:\\Hello\\World Users\\This\\Is/An\\example/bla.cs",
* "/C:/Hello/World", // the "World" here segment is not treated as a directory
* ]), "/C:/Hello/")
* ```
*/
export declare const commonPath: (paths: string[]) => string;
/** replace the common path among all provided `paths` by transforming it with a custom `map_fn` function.
* all `paths` are initially normalized and converted into posix-style (so that no "\\" windows separator is prevelent).
*
* the `map_fn` function's first argument (`path_info`), is a 2-tuple of the form `[common_dir: string, subpath: string]`,
* where `common_dir` represents the directory common to all of the input `paths`, and the `subpath` represents the remaining relative path that comes after common_dir.
* - the `common_dir` always ends with a trailing slash ("/"), unless there is absolutely no common directory among the `paths` at all.
* - the `subpath` never begins with any slash (nor any dot-slashes), unless of course, you had initially provided a path containing two or more consecutive slashes.
*
* @example
* ```ts
* import { assertEquals } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, fn = commonPathTransform
*
* const subpath_map_fn = ([common_dir, subpath]: [string, string]) => (subpath)
*
* eq(fn([
* "C:/Hello/World/This/Is/An/Example/Bla.cs",
* "C:\\Hello\\World\\This\\Is\\Not/An/Example/",
* "C:/Hello/Earth/Bla/Bla/Bla",
* ], subpath_map_fn), [
* "World/This/Is/An/Example/Bla.cs",
* "World/This/Is/Not/An/Example/",
* "Earth/Bla/Bla/Bla",
* ])
*
* eq(fn([
* "./../././home/Hello/World/This/Used/to-be-an/example/../../../Is/An/Example/Bla.cs",
* "./././../home/Hello/World/This/Is/an/example/bla.cs",
* "./../home/Hello/World/This/Is/Not/An/Example/",
* ], subpath_map_fn), [
* "An/Example/Bla.cs",
* "an/example/bla.cs",
* "Not/An/Example/",
* ])
*
* eq(fn([
* "/C:/Hello///World/Users/This/Is/An/Example/Bla.cs",
* "/C:\\Hello\\World Users\\This\\Is/An\\example/bla.cs",
* "/C:/./.\\.\\././Hello/World-Users/./././././This/Is/Not/An/Example/",
* ], subpath_map_fn), [
* "//World/Users/This/Is/An/Example/Bla.cs",
* "World Users/This/Is/An/example/bla.cs",
* "World-Users/This/Is/Not/An/Example/",
* ])
* ```
*/
export declare const commonPathTransform: <T = string, PathInfo extends [common_dir: string, subpath: string] = [common_dir: string, subpath: string]>(paths: string[], map_fn: ((path_info: PathInfo, index: number, path_infos: Array<PathInfo>) => T)) => T[];
/** purge the common path among all provided `paths`, and replace (join) it with a `new_common_dir` path.
*
* @example
* ```ts
* import { assertEquals } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, fn = commonPathReplace
*
* eq(fn([
* "C:/Hello/World/This/Is/An/Example/Bla.cs",
* "C:\\Hello\\World\\This\\Is\\Not/An/Example/",
* "C:/Hello/Earth/Bla/Bla/Bla",
* ], "D:/"), [
* "D:/World/This/Is/An/Example/Bla.cs",
* "D:/World/This/Is/Not/An/Example/",
* "D:/Earth/Bla/Bla/Bla",
* ])
*
* eq(fn([
* "C:/Hello/World/This/Used/to-be-an/example/../../../Is/An/Example/Bla.cs",
* "C:/Hello/World/This/Is/an/example/bla.cs",
* "C:/Hello/World/This/Is/Not/An/Example/",
* ], "D:/temp"), [ // an implicit forward slash is added.
* "D:/temp/An/Example/Bla.cs",
* "D:/temp/an/example/bla.cs",
* "D:/temp/Not/An/Example/",
* ])
*
* eq(fn([
* // there is no common ancestor among each of the paths (even "C:/" and "./C:/" are not considered to be equivalent to one another)
* "http:/Hello/World.cs",
* "./C:/Hello/World.cs",
* "C:/Hello/World/file.cs",
* ], "D:/temp/"), [
* "D:/temp/http:/Hello/World.cs",
* "D:/temp/C:/Hello/World.cs",
* "D:/temp/C:/Hello/World/file.cs",
* ])
*
* eq(fn([
* "/C:/Hello///World/Users/This/Is/An/Example/Bla.cs",
* "/C:\\Hello\\World Users\\This\\Is/An\\example/bla.cs",
* "/C:/./.\\.\\././Hello/World-Users/./././././This/Is/Not/An/Example/",
* ], "file:///./.\\HELLO.\\./../"), [ // the `new_common_dir` is not normalized by this function
* "file:///./.\\HELLO.\\./..///World/Users/This/Is/An/Example/Bla.cs",
* "file:///./.\\HELLO.\\./../World Users/This/Is/An/example/bla.cs",
* "file:///./.\\HELLO.\\./../World-Users/This/Is/Not/An/Example/",
* ])
* ```
*/
export declare const commonPathReplace: (paths: string[], new_common_dir: string) => string[];
/** the file path info data parsed by {@link parseFilepath}.
*
* example: if we have a file path `"D:/Hello\\World\\temp/.././dist for web/file.tar.gz"`, then the following will be its parsed components:
* - `path = "D:/Hello/World/dist for web/file.tar.gz"` - the normalized full path.
* - `dirpath = "D:/Hello/World/dist for web/"` - the normalized full path of the directory in which the file resides in. always has a trailing slash ("/").
* - `dirname = "dist for web"` - the name of the directory in which the file exists, without any leading or trailing slashes ("/").
* - `filename = "file.tar.gz"` - the name of the file, without any leading slashes ("/"), and cannot possibly have a trailing slash without being parsed as a directory instead of a file.
* - `basename = "file.tar"` - the `filename`, but with the final extension portion removed.
* - `extname = ".gz"` - the final extension portion of the `filename`.
*/
export interface FilepathInfo {
path: string;
dirpath: string;
dirname: string;
filename: string;
basename: string;
extname: string;
}
/** parses the provided file path and breaks it down into useful bit described by the interface {@link FilepathInfo}.
* note that a file path must never end in a trailing slash ("/"), and conversely,
* a folder path must always in a trailing slash ("/"), otherwise it will be parsed as a file.
*
* @example
* ```ts
* import { assertEquals } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const eq = assertEquals, fn = parseFilepathInfo
*
* eq(fn("/home\\user/docs"), {
* path: "/home/user/docs",
* dirpath: "/home/user/",
* dirname: "user",
* filename: "docs",
* basename: "docs",
* extname: "",
* })
*
* eq(fn("home\\user/docs/"), {
* path: "home/user/docs/",
* dirpath: "home/user/docs/",
* dirname: "docs",
* filename: "",
* basename: "",
* extname: "",
* })
*
* eq(fn("/home/xyz/.././././user/.bashrc."), {
* path: "/home/user/.bashrc.",
* dirpath: "/home/user/",
* dirname: "user",
* filename: ".bashrc.",
* basename: ".bashrc.",
* extname: "",
* })
*
* eq(fn("C:\\home\\user/.file.tar.gz"), {
* path: "C:/home/user/.file.tar.gz",
* dirpath: "C:/home/user/",
* dirname: "user",
* filename: ".file.tar.gz",
* basename: ".file.tar",
* extname: ".gz", // only the last bit of the extension makes it to here
* })
*
* eq(fn("/home/user///file.txt"), {
* path: "/home/user///file.txt",
* dirpath: "/home/user///",
* dirname: "", // this is because the there is no name attached between the last two slashes of the `dirpath = "/home/user///"`
* filename: "file.txt",
* basename: "file",
* extname: ".txt",
* })
*
* eq(fn("file://C:/home\\hello world.txt"), {
* path: "file://C:/home/hello world.txt", // file-urls are not converted, nor is any kind of url
* dirpath: "file://C:/home/",
* dirname: "home",
* filename: "hello world.txt",
* basename: "hello world",
* extname: ".txt",
* })
* ```
*/
export declare const parseFilepathInfo: (file_path: string) => FilepathInfo;
/** convert the input file-url to a filesystem local-path.
* however, if the input uri is not a file url (for instance `"C:/x/y/z"`, or `"http://hello.com"`),
* then `undefined` will be returned.
*
* if you are looking to convert any _potential_ file-url back to a filesystem local-path,
* then the {@link ensureFileUrlIsLocalPath} function would be better suited for your need.
*
* @example
* ```ts
* import { assertEquals } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const
* fn = fileUrlToLocalPath,
* eq = assertEquals
*
* eq( fn("file:///C:/Users/me/projects/"), "C:/Users/me/projects/")
* eq( fn("file:///C:\\Users\\me/projects/"), "C:/Users/me/projects/")
* eq( fn("file:///sys/etc/bin/deno.so"), "/sys/etc/bin/deno.so")
* eq( fn("file:///sys\\etc/bin\\deno.so"), "/sys/etc/bin/deno.so")
* eq( fn("file://localhost/C:/Users/me/projects/"), "C:/Users/me/projects/")
* eq( fn("file://localhost/sys/etc/bin/deno.so"), "/sys/etc/bin/deno.so")
* eq(fn(new URL("file:///C:/Users/me/projects/")), "C:/Users/me/projects/")
* eq(fn(new URL("file:///sys/etc/bin/deno.so")), "/sys/etc/bin/deno.so")
* eq(fn(new URL("file://localhost/C:/Users/me/projects/")), "C:/Users/me/projects/")
* eq(fn(new URL("file://localhost/sys/etc/bin/deno.so")), "/sys/etc/bin/deno.so")
*
* // everything below is not a file-url, and therefore cannot be converted.
* eq( fn("http://localhost:8000/hello/world/"), undefined)
* eq( fn("C:/Users/me/projects/"), undefined)
* eq( fn("/sys/etc/bin/deno.so"), undefined)
* eq( fn(""), undefined)
* ```
*/
export declare const fileUrlToLocalPath: (file_url: URL | string) => string | undefined;
/** a fault tolerant variant of {@link fileUrlToLocalPath} that assures you that any file-url path will get converted into a filesystem local-path.
* otherwise, when a non-file-url is provided, its string representation (href) will be returned if it was a `URL`,
* else the original string will be returned back.
*
* @example
* ```ts
* import { assertEquals } from "jsr:@std/assert"
*
* // aliasing our functions for brevity
* const
* fn = ensureFileUrlIsLocalPath,
* eq = assertEquals
*
* eq( fn("C:/Users/me/projects/"), "C:/Users/me/projects/")
* eq( fn("C:\\Users\\me/projects/"), "C:/Users/me/projects/")
* eq( fn("/C:/Users\\me/projects/"), "/C:/Users/me/projects/") // note the erroneous leading slash
* eq( fn("/sys\\etc/bin\\deno.so"), "/sys/etc/bin/deno.so")
* eq( fn("file:///C:/Users/me/projects/"), "C:/Users/me/projects/")
* eq( fn("file://////C:\\Users\\me/projects/"), "C:/Users/me/projects/")
* eq( fn("file:///sys\\etc/bin\\deno.so"), "/sys/etc/bin/deno.so")
* eq( fn("file://localhost/C:/Users/me/projects/"), "C:/Users/me/projects/