From cdcbcb789cce7d84cfa5d962be106e4d46beefcd Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Wed, 21 Feb 2024 15:52:27 +0100 Subject: [PATCH] feat(utils): add `pathToFileURL` --- README.md | 14 ++++++++++++++ src/utils.ts | 9 ++++++++- test/resolve.test.ts | 3 +-- test/utils.test.ts | 28 ++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a33d253..5000d87 100644 --- a/README.md +++ b/README.md @@ -424,6 +424,20 @@ console.log(fileURLToPath("file:///foo/bar.js")); console.log(fileURLToPath("file:///C:/path/")); ``` +### `pathToFileURL` + +Similar to [url.pathToFileURL](https://nodejs.org/api/url.html#urlpathtofileurlpath) but also handles `URL` input and returns a **string** with `file://` protocol. + +```js +import { pathToFileURL } from "mlly"; + +// /foo/bar.js +console.log(pathToFileURL("foo/bar.js")); + +// C:/path +console.log(pathToFileURL("C:\\path")); +``` + ### `normalizeid` Ensures id has either of `node:`, `data:`, `http:`, `https:` or `file:` protocols. diff --git a/src/utils.ts b/src/utils.ts index 0f715cc..55f64ed 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,7 @@ -import { fileURLToPath as _fileURLToPath } from "node:url"; +import { + fileURLToPath as _fileURLToPath, + pathToFileURL as _pathToFileURL, +} from "node:url"; import { promises as fsp } from "node:fs"; import { normalizeSlash, BUILTIN_MODULES } from "./_utils"; @@ -9,6 +12,10 @@ export function fileURLToPath(id: string | URL): string { return normalizeSlash(_fileURLToPath(id)); } +export function pathToFileURL(id: string | URL): string { + return _pathToFileURL(fileURLToPath(id)).toString(); +} + // https://datatracker.ietf.org/doc/html/rfc2396 // eslint-disable-next-line no-control-regex const INVALID_CHAR_RE = /[\u0000-\u001F"#$&*+,/:;<=>?@[\]^`{|}\u007F]+/g; diff --git a/test/resolve.test.ts b/test/resolve.test.ts index 8f21b77..e23750b 100644 --- a/test/resolve.test.ts +++ b/test/resolve.test.ts @@ -1,7 +1,6 @@ import { existsSync } from "node:fs"; -import { fileURLToPath } from "node:url"; import { describe, it, expect } from "vitest"; -import { resolveSync, resolvePathSync } from "../src"; +import { resolveSync, resolvePathSync, fileURLToPath } from "../src"; const tests = [ // Resolve to path diff --git a/test/utils.test.ts b/test/utils.test.ts index cc493f9..3d473f3 100644 --- a/test/utils.test.ts +++ b/test/utils.test.ts @@ -6,6 +6,8 @@ import { getProtocol, parseNodeModulePath, lookupNodeModuleSubpath, + fileURLToPath, + pathToFileURL, } from "../src"; describe("isNodeBuiltin", () => { @@ -155,3 +157,29 @@ describe("lookupNodeModuleSubpath", () => { }); } }); + +describe("fileURLToPath", () => { + const tests = [ + // ["file:///C:/path/", "C:\\path\\"], // TODO + // ["file://nas/foo.txt", "\\\\nas\\foo.txt"], // TODO + ["file:///你好.txt", "/你好.txt"], + ["file:///hello world", "/hello world"], + ] as const; + for (const t of tests) { + it(`${t[0]} should resolve to ${t[1]}`, () => { + expect(fileURLToPath(t[0])).toBe(t[1]); + }); + } +}); + +describe("pathToFileURL", () => { + const tests = [ + ["/foo#1", "file:///foo%231"], + ["/some/path%.c", "file:///some/path%25.c"], + ] as const; + for (const t of tests) { + it(`${t[0]} should resolve to ${t[1]}`, () => { + expect(pathToFileURL(t[0])).toBe(t[1]); + }); + } +});