Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions doc/design/overview.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Loaders Design

There are currently [three loader hooks](https://github.com/nodejs/node/tree/master/doc/api/esm.md#hooks):
There are currently the following [loader hooks](https://github.com/nodejs/node/tree/master/doc/api/esm.html#hooks):

1. `resolve`: Takes a specifier (the string after `from` in an `import` statement) and converts it into an URL to be loaded.

Expand All @@ -11,7 +11,11 @@ There are currently [three loader hooks](https://github.com/nodejs/node/tree/mas
* `json` (with `--experimental-json-modules`)
* `wasm` (with `--experimental-wasm-modules`)

* `globalPreload`: Defines a string of JavaScript to be injected into the application global scope.
1. `statFile`: Takes the resolved URL and returns its [`fs.Stats` record](https://nodejs.org/api/fs.html#class-fsstats) (or `null` if it doesn't exist).

1. `readFile`: Takes the resolved URL and returns its binary content (or `null` if it doesn't exist).

1. `globalPreload`: Defines a string of JavaScript to be injected into the application global scope.

## Chaining

Expand Down
50 changes: 50 additions & 0 deletions doc/design/proposal-chaining-iterative.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,3 +282,53 @@ const babelOutputToFormat = new Map([
]);
```
</details>

## Chaining `readFile` hooks

Say you had a chain of three loaders:

* `zip` adds a virtual filesystem layer for in-zip access
* `tgz` does the same but for tgz archives
* `warc` does the same for warc archives.

Following the pattern of `--require`:

```console
node \
--loader zip \
--loader tgz \
--loader warc
```

These would be called in the following sequence:

(`zip` OR `defaultReadFile`) → `tgz` → `warc`

1. `defaultReadFile` / `zip` needs to be first to know whether the manifest exists on the actual filesystem, which is fed to the subsequent loader
1. `tgz` receives the raw source from the previous loader and, if necessary, checks for the manifest existence via its own rules
1. `warc` does the same thing

LoadManifest hooks would have the following signature:

```ts
export async function readFile(
url: string, // A URL that point to a location; whether the file
// exists or not isn't guaranteed
interimResult: { // result from the previous hook
data: string | ArrayBuffer | TypedArray | null, // The content of the
// file, or `null` if it doesn't exist.
},
context: {
conditions = string[], // Export conditions of the relevant package.json
},
defaultLoadManifest: function, // Node's default load hook
): {
signals?: { // Signals from this hook to the ESMLoader
contextOverride?: object, // A new `context` argument for the next hook
interimIgnored?: true, // interimResult was intentionally ignored
shortCircuit?: true, // `resolve` chain should be terminated
},
data: string | ArrayBuffer | TypedArray | null, // The content of the
// file, or `null` if it doesn't exist.
} {
```
39 changes: 39 additions & 0 deletions doc/design/proposal-chaining-middleware.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,3 +263,42 @@ export async function load(
}
```
</details>

## Chaining `readFile` hooks

Say you had a chain of three loaders:

* `zip` adds a virtual filesystem layer for in-zip access
* `tgz` does the same but for tgz archives
* `warc` does the same for warc archives.

Following the pattern of `--require`:

```console
node \
--loader zip \
--loader tgz \
--loader warc
```

These would be called in the following sequence: `zip` calls `tgz`, which calls `warc`. Or in JavaScript terms, `zip(tgz(warc(input)))`:

Load hooks would have the following signature:

```ts
export async function readFile(
url: string, // A URL that point to a location; whether the file
// exists or not isn't guaranteed
context: {
conditions = string[], // Export conditions of the relevant `package.json`
},
next: function, // The subsequent `readFile` hook in the chain,
// or Node’s default `readFile` hook after the
// last user-supplied `readFile` hook
): {
data: string | ArrayBuffer | TypedArray | null, // The content of the
// file, or `null` if it doesn't exist.
shortCircuit?: true, // A signal that this hook intends to terminate
// the chain of `load` hooks
} {
```