Skip to content

Commit

Permalink
feat: expose resolveImports and improve docs
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 committed Jul 22, 2021
1 parent ee89629 commit 074ef52
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 40 deletions.
73 changes: 51 additions & 22 deletions README.md
Expand Up @@ -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'
Expand All @@ -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'
Expand All @@ -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'

Expand All @@ -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`
Expand Down
23 changes: 15 additions & 8 deletions lib/index.d.ts
@@ -1,29 +1,36 @@
// CommonJS

export interface CommonjsContext {
__filename: string
__dirname: string
}
export type createCommonJS = (importMeta: ImportMeta) => CommonjsContext

// Resolve

export interface ResolveOptions {
from?: string | URL
conditions?: string[]
}

export type ResolveFn<T> = (id: string, opts: ResolveOptions) => T
export type resolve = ResolveFn<Promise<string>>
export type resolveSync = ResolveFn<string>
export type resolvePath = ResolveFn<Promise<string>>
export type resolveSync = ResolveFn<string>
export type resolvePathSync = ResolveFn<string>
export type createResolve = (defaults: ResolveOptions) => ResolveFn<Promise<string>>
export type resolveImports = (code: string, opts: ResolveOptions) => Promise<string>

export type createResolve = (from: ImportMeta|string) => ResolveFn<Promise<string>>

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<any>
export type evalModule = (code: string, opts?: EvaluateOptions) => Promise<any>
export type readModule = (id: string, opts?: ReadOptions) => Promise<any>
export type readModule = (id: string, opts?: ResolveOptions) => Promise<any>
export type toDataURL = (code: string) => string

// Path Utils

export type fileURLToPath = (id: URL | string) => string
17 changes: 10 additions & 7 deletions lib/index.mjs
Expand Up @@ -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}`
}
Expand Down
9 changes: 6 additions & 3 deletions 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 }))

0 comments on commit 074ef52

Please sign in to comment.