Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

doc: fix ESM hook mistypes and links to types #34240

Closed
wants to merge 3 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
80 changes: 54 additions & 26 deletions doc/api/esm.md
Expand Up @@ -1139,11 +1139,18 @@ CommonJS modules loaded.

### Hooks

#### <code>resolve</code> hook
#### `resolve(specifier, context, defaultResolve)`

> Note: The loaders API is being redesigned. This hook may disappear or its
> signature may change. Do not rely on the API described below.

* `specifier` {string}
* `context` {Object}
* `conditions` {string[]}
* `parentURL` {string}
* `defaultResolve` {Function}
* Returns: {Promise}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For async functions, do the Node.js API ref. docs types usually express what the promise is wrapping? Compare to the JSDoc below it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: AFAIK, the Node.js API ref. docs type system lacks the ability to express what we want here. So, since this API is not strictly Promise-based (it works whether this is an async function or not), IMO this should be expressed as an Object the same way the others are.

Suggested change
* Returns: {Promise}
* Returns: {Object}
* `url` {string}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yet to be documented:

Whether or not this hook should ever be written as a non-async function depends on the circumstance.

Unless others feel strongly about this or have a better suggestion, I'll take the suggestion I've proposed above. The point of this PR was that it was supposed to be as frictionless as possible, so being stuck on this seems counterproductive especially since we would want these changes made before continuing w/ #33812.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's some additional useful information in the thread where the following was mentioned.

if you know your value will be available synchronously don't use a promise? the entire point of promises is "value will be available at some point that I have no inference or control of and my code should still operate consistently through that"
https://twitter.com/devsnek/status/1284528435671465988

I'm willing to continue the discussion as it relates to this PR, so please feel free to chime if interested.


The `resolve` hook returns the resolved file URL for a given module specifier
and parent URL. The module specifier is the string in an `import` statement or
`import()` expression, and the parent URL is the URL of the module that imported
Expand All @@ -1164,11 +1171,11 @@ Node.js module specifier resolution behavior_ when calling `defaultResolve`, the
/**
* @param {string} specifier
* @param {{
* conditions: !Array<string>,
* parentURL: !(string | undefined),
* conditions: !(Array<string>),
* }} context
* @param {Function} defaultResolve
* @returns {!(Promise<{ url: string }>)}
* @returns {Promise<{ url: string }>}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Justification for this change is as follows.

In strict null checking mode, the null and undefined values are not in the domain of every type and are only assignable to themselves and any (the one exception being that undefined is also assignable to void).
https://www.typescriptlang.org/docs/handbook/compiler-options.html

The playground link in the commit message confirms silent compilation.

*/
export async function resolve(specifier, context, defaultResolve) {
const { parentURL = null } = context;
Expand All @@ -1194,29 +1201,34 @@ export async function resolve(specifier, context, defaultResolve) {
}
```

#### <code>getFormat</code> hook
#### `getFormat(url, context, defaultGetFormat)`

> Note: The loaders API is being redesigned. This hook may disappear or its
> signature may change. Do not rely on the API described below.

* `url` {string}
* `context` {Object}
* `defaultGetFormat` {Function}
* Returns: {Object}
* `format` {string}

The `getFormat` hook provides a way to define a custom method of determining how
a URL should be interpreted. The `format` returned also affects what the
acceptable forms of source values are for a module when parsing. This can be one
of the following:

| `format` | Description | Acceptable Types For `source` Returned by `getSource` or `transformSource` |
| --- | --- | --- |
| `'builtin'` | Load a Node.js builtin module | Not applicable |
| `'commonjs'` | Load a Node.js CommonJS module | Not applicable |
| `'json'` | Load a JSON file | { [ArrayBuffer][], [string][], [TypedArray][] } |
| `'module'` | Load an ES module | { [ArrayBuffer][], [string][], [TypedArray][] } |
| `'wasm'` | Load a WebAssembly module | { [ArrayBuffer][], [string][], [TypedArray][] } |
| `format` | Description | Acceptable Types For `source` Returned by `getSource` or `transformSource` |
| ------------ | ------------------------------ | -------------------------------------------------------------------------- |
| `'builtin'` | Load a Node.js builtin module | Not applicable |
| `'commonjs'` | Load a Node.js CommonJS module | Not applicable |
| `'json'` | Load a JSON file | { [`string`][], [`ArrayBuffer`][], [`TypedArray`][] } |
| `'module'` | Load an ES module | { [`string`][], [`ArrayBuffer`][], [`TypedArray`][] } |
| `'wasm'` | Load a WebAssembly module | { [`ArrayBuffer`][], [`TypedArray`][] } |

Note: These types all correspond to classes defined in ECMAScript.

* The specific [ArrayBuffer][] object is a [SharedArrayBuffer][].
* The specific [string][] object is not the class constructor, but an instance.
* The specific [TypedArray][] object is a [Uint8Array][].
* The specific [`ArrayBuffer`][] object is a [`SharedArrayBuffer`][].
* The specific [`TypedArray`][] object is a [`Uint8Array`][].

Note: If the source value of a text-based format (i.e., `'json'`, `'module'`) is
not a string, it will be converted to a string using [`util.TextDecoder`][].
Expand All @@ -1242,11 +1254,18 @@ export async function getFormat(url, context, defaultGetFormat) {
}
```

#### <code>getSource</code> hook
#### `getSource(url, context, defaultGetSource)`

> Note: The loaders API is being redesigned. This hook may disappear or its
> signature may change. Do not rely on the API described below.

* `url` {string}
* `context` {Object}
* `format` {string}
* `defaultGetSource` {Function}
* Returns: {Object}
* `source` {string|SharedArrayBuffer|Uint8Array}

The `getSource` hook provides a way to define a custom method for retrieving
the source code of an ES module specifier. This would allow a loader to
potentially avoid reading files from disk.
Expand All @@ -1256,7 +1275,7 @@ potentially avoid reading files from disk.
* @param {string} url
* @param {{ format: string }} context
* @param {Function} defaultGetSource
* @returns {Promise<{ source: !(SharedArrayBuffer | string | Uint8Array) }>}
* @returns {Promise<{ source: !(string | SharedArrayBuffer | Uint8Array) }>}
*/
export async function getSource(url, context, defaultGetSource) {
const { format } = context;
Expand All @@ -1272,11 +1291,18 @@ export async function getSource(url, context, defaultGetSource) {
}
```

#### <code>transformSource</code> hook
#### `transformSource(source, context, defaultTransformSource)`

> Note: The loaders API is being redesigned. This hook may disappear or its
> signature may change. Do not rely on the API described below.

* `source` {string|SharedArrayBuffer|Uint8Array}
* `context` {Object}
* `format` {string}
* `url` {string}
* Returns: {Object}
* `source` {string|SharedArrayBuffer|Uint8Array}

The `transformSource` hook provides a way to modify the source code of a loaded
ES module file after the source string has been loaded but before Node.js has
done anything with it.
Expand All @@ -1287,13 +1313,13 @@ unknown-to-Node.js file extensions. See the [transpiler loader example][] below.

```js
/**
* @param {!(SharedArrayBuffer | string | Uint8Array)} source
* @param {!(string | SharedArrayBuffer | Uint8Array)} source
* @param {{
* url: string,
* format: string,
* }} context
* @param {Function} defaultTransformSource
* @returns {Promise<{ source: !(SharedArrayBuffer | string | Uint8Array) }>}
* @returns {Promise<{ source: !(string | SharedArrayBuffer | Uint8Array) }>}
*/
export async function transformSource(source, context, defaultTransformSource) {
const { url, format } = context;
Expand All @@ -1309,11 +1335,13 @@ export async function transformSource(source, context, defaultTransformSource) {
}
```

#### <code>getGlobalPreloadCode</code> hook
#### `getGlobalPreloadCode()`

> Note: The loaders API is being redesigned. This hook may disappear or its
> signature may change. Do not rely on the API described below.

* Returns: {string}

Sometimes it can be necessary to run some code inside of the same global scope
that the application will run in. This hook allows to return a string that will
be ran as sloppy-mode script on startup.
Expand Down Expand Up @@ -1816,12 +1844,12 @@ success!
[`import`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
[`module.createRequire()`]: modules.html#modules_module_createrequire_filename
[`module.syncBuiltinESMExports()`]: modules.html#modules_module_syncbuiltinesmexports
[`transformSource` hook]: #esm_code_transformsource_code_hook
[ArrayBuffer]: https://www.ecma-international.org/ecma-262/6.0/#sec-arraybuffer-constructor
[SharedArrayBuffer]: https://tc39.es/ecma262/#sec-sharedarraybuffer-constructor
[string]: https://www.ecma-international.org/ecma-262/6.0/#sec-string-constructor
[TypedArray]: https://www.ecma-international.org/ecma-262/6.0/#sec-typedarray-objects
[Uint8Array]: https://www.ecma-international.org/ecma-262/6.0/#sec-uint8array
[`transformSource` hook]: #esm_transformsource_source_context_defaulttransformsource
[`ArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
[`SharedArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer
[`string`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String
[`TypedArray`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray
[`Uint8Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array
DerekNonGeneric marked this conversation as resolved.
Show resolved Hide resolved
[`util.TextDecoder`]: util.html#util_class_util_textdecoder
[import an ES or CommonJS module for its side effects only]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Import_a_module_for_its_side_effects_only
[special scheme]: https://url.spec.whatwg.org/#special-scheme
Expand Down