From ae7b2ecb5ec6e609f5a069977d4fa82d414f8e5f Mon Sep 17 00:00:00 2001 From: Lexus Drumgold Date: Sun, 3 Nov 2024 20:35:46 -0500 Subject: [PATCH] feat(lib): `toUrl` Signed-off-by: Lexus Drumgold --- README.md | 4 +++- src/__snapshots__/index.e2e.snap | 1 + src/lib/__snapshots__/to-url.snap | 11 ++++++++++ src/lib/__tests__/to-url.spec.mts | 20 +++++++++++++++++ src/lib/get-source.mts | 17 ++------------- src/lib/index.mts | 1 + src/lib/resolver.mts | 5 ++--- src/lib/to-relative-specifier.mts | 7 +++--- src/lib/to-url.mts | 36 +++++++++++++++++++++++++++++++ 9 files changed, 80 insertions(+), 22 deletions(-) create mode 100644 src/lib/__snapshots__/to-url.snap create mode 100644 src/lib/__tests__/to-url.spec.mts create mode 100644 src/lib/to-url.mts diff --git a/README.md b/README.md index 6d59ad29..5f96446a 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,8 @@ import { resolveModule, resolver, root, - toRelativeSpecifier + toRelativeSpecifier, + toUrl } from '@flex-development/mlly' ``` @@ -132,6 +133,7 @@ This package exports the following identifiers: - `packageTargetResolve` - [`root`](./src/lib/root.mts) - [`toRelativeSpecifier`](./src/lib/to-relative-specifier.mts) +- [`toUrl`](./src/lib/to-url.mts) There is no default export. diff --git a/src/__snapshots__/index.e2e.snap b/src/__snapshots__/index.e2e.snap index 8294d058..15ebe8f7 100644 --- a/src/__snapshots__/index.e2e.snap +++ b/src/__snapshots__/index.e2e.snap @@ -34,5 +34,6 @@ exports[`e2e:mlly > should expose public api 1`] = ` "resolver", "root", "toRelativeSpecifier", + "toUrl", ] `; diff --git a/src/lib/__snapshots__/to-url.snap b/src/lib/__snapshots__/to-url.snap new file mode 100644 index 00000000..eb47be86 --- /dev/null +++ b/src/lib/__snapshots__/to-url.snap @@ -0,0 +1,11 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`unit:lib/toUrl > should return \`id\` as \`URL\` (0) 1`] = `"node:os"`; + +exports[`unit:lib/toUrl > should return \`id\` as \`URL\` (1) 1`] = `file://\${process.cwd()}/path`; + +exports[`unit:lib/toUrl > should return \`id\` as \`URL\` (2) 1`] = `file://\${process.cwd()}/src/lib/__tests__/to-url.spec.mts`; + +exports[`unit:lib/toUrl > should return \`id\` as \`URL\` (3) 1`] = `"node:test"`; + +exports[`unit:lib/toUrl > should return \`id\` as \`URL\` (4) 1`] = `file://\${process.cwd()}/tsconfig.build.json`; diff --git a/src/lib/__tests__/to-url.spec.mts b/src/lib/__tests__/to-url.spec.mts new file mode 100644 index 00000000..aaeebc4e --- /dev/null +++ b/src/lib/__tests__/to-url.spec.mts @@ -0,0 +1,20 @@ +/** + * @file Unit Tests - toUrl + * @module mlly/lib/tests/unit/toUrl + */ + +import cwd from '#lib/cwd' +import testSubject from '#lib/to-url' +import pathe from '@flex-development/pathe' + +describe('unit:lib/toUrl', () => { + it.each>([ + ['os'], + ['path', cwd()], + [import.meta.url], + [new URL('node:test')], + [pathe.resolve('tsconfig.build.json')] + ])('should return `id` as `URL` (%#)', (id, parent) => { + expect(testSubject(id, parent)).toMatchSnapshot() + }) +}) diff --git a/src/lib/get-source.mts b/src/lib/get-source.mts index 6c51faa3..481a093f 100644 --- a/src/lib/get-source.mts +++ b/src/lib/get-source.mts @@ -5,13 +5,12 @@ import fs from '#internal/fs' import process from '#internal/process' -import canParseUrl from '#lib/can-parse-url' import isFile from '#lib/is-file' +import toUrl from '#lib/to-url' import { ERR_UNSUPPORTED_ESM_URL_SCHEME, type ErrUnsupportedEsmUrlScheme } from '@flex-development/errnode' -import { isBuiltin } from '@flex-development/is-builtin' import type { GetSourceContext, GetSourceHandler, @@ -19,7 +18,6 @@ import type { ModuleId, Protocol } from '@flex-development/mlly' -import pathe from '@flex-development/pathe' import { ok } from 'devlop' export default getSource @@ -27,13 +25,6 @@ export default getSource /** * Get the source code for `id`. * - * > 👉 **Note**: If `id` is not a [builtin module][builtin-module] and also - * > cannot be parsed as an {@linkcode URL}, it will be assumed to be a path and - * > converted to a [`file:` URL][file-url]. - * - * [builtin-module]: https://nodejs.org/api/esm.html#builtin-modules - * [file-url]: https://nodejs.org/api/esm.html#file-urls - * * @see {@linkcode ErrUnsupportedEsmUrlScheme} * @see {@linkcode GetSourceOptions} * @see {@linkcode ModuleId} @@ -85,11 +76,7 @@ async function getSource( * * @const {URL} url */ - const url: URL = isBuiltin(id) - ? new URL('node:' + String(id).replace(/^node:/, '')) - : canParseUrl(id) - ? new URL(id) - : (ok(typeof id === 'string', 'expected string'), pathe.pathToFileURL(id)) + const url: URL = toUrl(id) /** * Source code handler for {@linkcode url}. diff --git a/src/lib/index.mts b/src/lib/index.mts index 9651d6c8..923508cf 100644 --- a/src/lib/index.mts +++ b/src/lib/index.mts @@ -38,3 +38,4 @@ export * from '#lib/resolver' export * as resolver from '#lib/resolver' export { default as root } from '#lib/root' export { default as toRelativeSpecifier } from '#lib/to-relative-specifier' +export { default as toUrl } from '#lib/to-url' diff --git a/src/lib/resolver.mts b/src/lib/resolver.mts index 47f1b775..43e700e7 100644 --- a/src/lib/resolver.mts +++ b/src/lib/resolver.mts @@ -20,6 +20,7 @@ import lookupPackageScope from '#lib/lookup-package-scope' import patternMatch from '#lib/pattern-match' import readPackageJson from '#lib/read-package-json' import root from '#lib/root' +import toUrl from '#lib/to-url' import { codes, ERR_INVALID_MODULE_SPECIFIER, @@ -672,9 +673,7 @@ async function packageResolve( mainFields?: MainField[] | Set | null | undefined, fs?: FileSystem | null | undefined ): Promise { - if (isBuiltin(specifier)) { - return new URL('node:' + specifier.replace(/^node:/, '')) - } + if (isBuiltin(specifier)) return toUrl(specifier) /** * Index of separator in {@linkcode specifier}. diff --git a/src/lib/to-relative-specifier.mts b/src/lib/to-relative-specifier.mts index 5e291c14..3adfad91 100644 --- a/src/lib/to-relative-specifier.mts +++ b/src/lib/to-relative-specifier.mts @@ -3,6 +3,7 @@ * @module mlly/lib/toRelativeSpecifier */ +import toUrl from '#lib/to-url' import type { ModuleId } from '@flex-development/mlly' import pathe from '@flex-development/pathe' @@ -20,7 +21,7 @@ import pathe from '@flex-development/pathe' * @param {ModuleId} url * The `file:` URL to convert * @param {ModuleId} parent - * URL of parent module + * Parent module id * @return {string} * Relative specifier */ @@ -31,8 +32,8 @@ function toRelativeSpecifier(url: ModuleId, parent: ModuleId): string { * @var {string} specifier */ let specifier: string = pathe.relative( - pathe.fileURLToPath(parent), - pathe.fileURLToPath(url) + pathe.fileURLToPath(toUrl(parent)), + pathe.fileURLToPath(toUrl(url)) ) if (/(?:\.\.\/){2,}/.test(specifier)) { diff --git a/src/lib/to-url.mts b/src/lib/to-url.mts new file mode 100644 index 00000000..192972a9 --- /dev/null +++ b/src/lib/to-url.mts @@ -0,0 +1,36 @@ +/** + * @file toUrl + * @module mlly/lib/toUrl + */ + +import canParseUrl from '#lib/can-parse-url' +import { isBuiltin } from '@flex-development/is-builtin' +import type { ModuleId } from '@flex-development/mlly' +import pathe from '@flex-development/pathe' + +/** + * Convert `id` to a {@linkcode URL}. + * + * > 👉 **Note**: If `id` cannot be parsed as a `URL` (in conjunction with + * > `parent`) and is also not a [builtin module][builtin-module], it will be + * > assumed to be a path and converted to a [`file:` URL][file-url]. + * + * [builtin-module]: https://nodejs.org/api/esm.html#builtin-modules + * [file-url]: https://nodejs.org/api/esm.html#file-urls + * + * @param {ModuleId} id + * The module id to convert + * @param {ModuleId | null | undefined} [parent] + * Base URL to resolve against if `id` is not absolute + * @return {URL} + * New URL + */ +function toUrl(id: ModuleId, parent?: ModuleId | null | undefined): URL { + return canParseUrl(id, parent) + ? new URL(id, parent ?? undefined) + : isBuiltin(id) + ? new URL('node:' + String(id)) + : pathe.pathToFileURL(String(id)) +} + +export default toUrl