diff --git a/README.md b/README.md index 5d4f84e..1f4c17b 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ import {} from 'mlly' ### `createCommonJS` -This utility creates a compatible context that we loose when migrating to ECMA modules. +This utility creates a compatible CommonJS context that is missing in ECMAScript modules. ```js import { createCommonJS } from 'mlly' @@ -37,42 +37,71 @@ const { __dirname, __filename, require } = createCommonJS(import.meta) ## Resolving Modules -There are several utils exposed allow resolving another module URL or Path. (internally using [wooorm/import-meta-resolve](https://github.com/wooorm/import-meta-resolve) that re-exports Node.js code). +### `resolve` -- **`resolve(id, resolveOptions?)`** -- **`resolvePath(id, resolveOptions?)`** -- **`createResolve(import.meta)`** | **`createResolve(base)`** -- `resolveSync(id, resolveOptions?)` -- `resolvePathSync(id, resolveOptions?)` +Resolve a module by respecting [ECMAScript Resolver algorithm](https://nodejs.org/dist/latest-v14.x/docs/api/esm.html#esm_resolver_algorithm) +(internally using [wooorm/import-meta-resolve](https://github.com/wooorm/import-meta-resolve) that exposes Node.js implementation). -It is recommended to use `resolve` and `createResolve` since module resolution spec allows aync resolution. +```js +import { resolve } from 'mlly' + +// file:///home/user/project/module.mjs +console.log(await resolve('./module.mjs', { from: import.meta.url })) +``` + +**Resolve options:** + +- `from`: URL or string (default is `pwd()`) +- `conditions`: Array of conditions used for resolution algorithm (default is `['node', 'import']`) + +### `resolvePath` + +Similar to `resolve` but returns a path instead of URL using `fileURLToPath`. ```js -import { resolve, resolvePath, createResolve } from 'mlly' +import { resolvePath } from 'mlly' // //home/user/project/module.mjs console.log(await resolvePath('./module.mjs', { from: import.meta.url })) +``` -// file:///home/user/project/module.mjs -console.log(await resolve('./module.mjs', { from: import.meta.url })) +### `createResolve` + +Create a `resolve` function with defaults. + +```js +import { createResolve } from 'mlly' + +const importResolve = createResolve({ from: import.meta.url }) // file:///home/user/project/module.mjs -const _resolve = createResolve(import.meta) +console.log(await importResolve('./module.mjs')) +``` + +**Example:** Ponyfill [import.meta.resolve](https://nodejs.org/api/esm.html#esm_import_meta_resolve_specifier_parent): + +```js +import { createResolve } from 'mlly' -console.log(await _resolve('./module.mjs')) +import.meta.resolve = createResolve({ from: import.meta.url }) ``` -**Resolve options:** +### `resolveImports` -- `from`: URL or string (default is `pwd()`) -- `conditions`: Array of conditions used for resolution algorithm (default is `['node', 'import']`) +Resolve all static and dynamic imports with relative paths to full resolved path. +```js +import { resolveImports } from 'mlly' + +// import foo from 'file:///home/user/project/bar.mjs' +console.log(await resolveImports(`import foo from './bar.mjs'`, { from: import.meta.url })) +``` ## Evaluating Moduls ### `loadModule` -Dynamically loads a module by evaluating source code. (useful to bypass import cache) +Dynamically loads a module by evaluating source code. ```js import { loadModule } from 'mlly' @@ -84,6 +113,8 @@ await loadModule('./hello.mjs', { from: import.meta.url }) Evaluates JavaScript module code using dynamic [`data:`](https://nodejs.org/api/esm.html#esm_data_imports) import. +All relative imports will be automatically resolved with `from` param. + ```js import { evalModule } from 'mlly' @@ -92,19 +123,17 @@ await evalModule(`console.log("Hello World!")`) await evalModule(` import { reverse } from './utils.mjs' console.log(reverse('!emosewa si sj')) -`, { - from: import.meta.url -}) +`, { from: import.meta.url }) ``` ### `readModule` -Resolves id using `resolve` and reads source code. +Resolves id using `resolve` and reads sourcecode. ```js import { readModule } from 'mlly' -console.log(await readModule('./index.mjs', import.meta.url)) +console.log(await readModule('./index.mjs', { from: import.meta.url })) ``` ### `toDataURL` diff --git a/lib/index.d.ts b/lib/index.d.ts index f5b8947..52f42d0 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -1,9 +1,13 @@ +// CommonJS + export interface CommonjsContext { __filename: string __dirname: string } export type createCommonJS = (importMeta: ImportMeta) => CommonjsContext +// Resolve + export interface ResolveOptions { from?: string | URL conditions?: string[] @@ -11,19 +15,22 @@ export interface ResolveOptions { export type ResolveFn = (id: string, opts: ResolveOptions) => T export type resolve = ResolveFn> -export type resolveSync = ResolveFn export type resolvePath = ResolveFn> +export type resolveSync = ResolveFn export type resolvePathSync = ResolveFn +export type createResolve = (defaults: ResolveOptions) => ResolveFn> +export type resolveImports = (code: string, opts: ResolveOptions) => Promise -export type createResolve = (from: ImportMeta|string) => ResolveFn> -export interface EvaluateOptions extends ResolveOptions { - from?: URL | string -} -export interface ReadOptions extends ResolveOptions { -} +// Evaluate + +export interface EvaluateOptions extends ResolveOptions {} export type loadModule = (id: string, opts?: EvaluateOptions) => Promise export type evalModule = (code: string, opts?: EvaluateOptions) => Promise -export type readModule = (id: string, opts?: ReadOptions) => Promise +export type readModule = (id: string, opts?: ResolveOptions) => Promise export type toDataURL = (code: string) => string + +// Path Utils + +export type fileURLToPath = (id: URL | string) => string diff --git a/lib/index.mjs b/lib/index.mjs index c774468..e9dc3f4 100644 --- a/lib/index.mjs +++ b/lib/index.mjs @@ -74,20 +74,23 @@ export async function loadModule (id, opts = {}) { return evalModule(code, opts) } -export function evalModule (code, opts = {}) { +export async function evalModule (code, opts = {}) { + code = await resolveImports(code) return import(toDataURL(code, opts)) } export async function readModule (id, opts) { const resolved = await resolve(id, opts) - return await fsp.readFile(fileURLToPath(resolved), 'utf-8') + const code = await fsp.readFile(fileURLToPath(resolved), 'utf-8') + return code } -export function toDataURL (code, opts = {}) { - if (opts.from !== false) { - const from = opts.from || DEFAULT_FROM - code = code.replace(ESM_IMPORT_RE, id => resolveSync(id, { from })) - } +export function resolveImports (code, opts) { + // TODO: Reimplement with resolve (async replace) + return Promise.resolve(code.replace(ESM_IMPORT_RE, id => resolveSync(id, opts))) +} + +export function toDataURL (code) { const base64 = Buffer.from(code).toString('base64') return `data:text/javascript;base64,${base64}` } diff --git a/test/resolve.mjs b/test/resolve.mjs index 03a0152..11e8495 100644 --- a/test/resolve.mjs +++ b/test/resolve.mjs @@ -1,6 +1,9 @@ -import { resolvePath, createResolve } from 'mlly' +import { resolvePath, createResolve, resolveImports } from 'mlly' -const importResolve = createResolve(import.meta) -console.log(await importResolve('./cjs.mjs')) +import.meta.resolve = import.meta.resolve || createResolve({ from : import.meta.url }) + +console.log(await import.meta.resolve('./cjs.mjs')) console.log(await resolvePath('./cjs.mjs', { from: import.meta.url })) + +console.log(await resolveImports(`import foo from './eval.mjs'`, { from: import.meta.url }))