Visitar URL original
[v18.x] Backport most ESM and customization hook changes by targos · Pull Request #50669 · nodejs/node · GitHub
Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
5a0ae4d
esm: move hook execution to separate thread
JakobJingleheimer Nov 10, 2023
7c878ef
esm: avoid try/catch when validating urls
anonrig Apr 15, 2023
4990269
esm: propagate `process.exit` from the loader thread to the main thread
aduh95 Apr 19, 2023
40fa6e4
esm: initialize `import.meta` on eval
aduh95 Apr 19, 2023
d8687c4
esm: remove support for deprecated hooks
aduh95 Apr 19, 2023
ee9cef2
loader: use default loader as cascaded loader in the in loader worker
joyeecheung Apr 21, 2023
d63f4c4
esm: rename `URLCanParse` to be consistent
aduh95 Apr 22, 2023
a31ca4e
esm: do not use `'beforeExit'` on the main thread
aduh95 May 14, 2023
2bb6dfb
esm: handle more error types thrown from the loader thread
aduh95 Jun 1, 2023
09c986e
esm: handle `globalPreload` hook returning a nullish value
aduh95 May 30, 2023
96edd85
esm: remove support for arrays in `import` internal method
aduh95 Jun 5, 2023
a0c10aa
typings: fix JSDoc in ESM loader modules
aduh95 Jun 11, 2023
1964e25
module: implement `register` utility
jlenon7 Jun 12, 2023
002a084
esm: add back `globalPreload` tests and fix failing ones
aduh95 Jul 17, 2023
afa2562
esm: unflag `Module.register` and allow nested loader `import()`
izaakschroeder Jul 19, 2023
5de6683
module: ensure successful import returns the same result
aduh95 Jul 26, 2023
0a9e81e
test: fix `es-module/test-esm-initialization`
aduh95 Jul 29, 2023
4ffa98f
fs: make `mkdtemp` accept buffers and URL
LiviaMedeiros Jul 18, 2023
17ba928
test: fix some assumptions in tests
aduh95 Jul 31, 2023
9d44275
test: order `common.mjs` in ASCII order
aduh95 Jul 31, 2023
ce1d396
test: fix snapshot tests when cwd contains spaces or backslashes
aduh95 Jul 31, 2023
f5661b0
esm: fix typo `parentUrl` -> `parentURL`
aduh95 Aug 2, 2023
c34870e
esm: add `initialize` hook, integrate with `register`
izaakschroeder Aug 3, 2023
3d58e49
test: use `fixtures.fileURL` when appropriate
aduh95 Aug 3, 2023
61aa405
esm: protect `ERR_UNSUPPORTED_DIR_IMPORT` against prototype pollution
aduh95 Aug 11, 2023
0bfcddc
test: reduce flakiness of `test-esm-loader-hooks`
aduh95 Aug 12, 2023
a256f11
esm: import.meta.resolve exact module not found errors should return
guybedford Aug 13, 2023
73809e2
esm: unflag import.meta.resolve
guybedford Aug 13, 2023
8ec3953
esm: fix `globalPreload` warning
aduh95 Aug 13, 2023
586bb8f
doc: fix name of the flag in `initialize()` docs
aduh95 Aug 14, 2023
1c020cc
test: refactor `test-esm-loader-hooks` for easier debugging
aduh95 Aug 15, 2023
222a378
doc: caveat unavailability of `import.meta.resolve` in custom loaders
JakobJingleheimer Aug 19, 2023
cf02160
test: reduce flakiness of `test-esm-loader-hooks`
aduh95 Aug 20, 2023
0b72534
doc: add signature for `module.register`
GeoffreyBooth Aug 20, 2023
3c9111e
esm: refactor mocking test
GeoffreyBooth Sep 5, 2023
f86933b
doc: move and rename loaders section
GeoffreyBooth Aug 22, 2023
dcf909c
doc: update module hooks docs
GeoffreyBooth Aug 24, 2023
16580a4
doc: edit `import.meta.resolve` documentation
aduh95 Sep 5, 2023
abedad7
test: add `tmpdir.fileURL()`
LiviaMedeiros Aug 10, 2023
d785f83
esm: refactor test-esm-named-exports
GeoffreyBooth Sep 4, 2023
9a7a8c5
esm: refactor test-esm-loader-resolve-type
GeoffreyBooth Sep 4, 2023
fbc7c3e
esm: remove return value for `Module.register`
aduh95 Sep 9, 2023
71c4ae3
typings: fix missing property in `ExportedHooks`
aduh95 Sep 9, 2023
a23a4c9
test: isolate `globalPreload` tests
GeoffreyBooth Sep 9, 2023
89dce06
test: increase coverage of `Module.register` and `initialize` hook
aduh95 Sep 10, 2023
40ffd28
esm: clarify ERR_REQUIRE_ESM errors
danielcompton Sep 15, 2023
1a2443a
esm: fix support for `URL` instances in `register`
aduh95 Sep 17, 2023
a29ddb3
esm: update loaders warning
GeoffreyBooth Sep 19, 2023
44b345c
esm: fix return type of `import.meta.resolve`
aduh95 Sep 20, 2023
aadc39f
doc: add missing history info for `import.meta.resolve`
aduh95 Sep 20, 2023
efd4e8c
esm: identify parent importing a url with invalid host
JakobJingleheimer Sep 23, 2023
f85e62f
esm: fix cache collision on JSON files using file: URL
aduh95 Sep 29, 2023
a600822
lib: merge cjs and esm package json reader caches
anonrig Jun 29, 2023
c262d3b
esm: require braces for modules code
GeoffreyBooth Sep 15, 2023
bd4eda0
module, esm: jsdoc for modules files
GeoffreyBooth Sep 19, 2023
7c5b7f3
module: move helpers out of cjs loader
GeoffreyBooth Sep 30, 2023
2f26771
esm: improve JSDoc annotation of internal functions
aduh95 Oct 1, 2023
ce76ac2
esm: --experimental-default-type flag to flip module defaults
GeoffreyBooth Sep 29, 2023
823adc8
esm: unflag extensionless javascript and wasm in module scope
GeoffreyBooth Sep 30, 2023
08201aa
util: add `getCwdSafe` internal util fn
jlenon7 Sep 22, 2023
151e812
esm: bypass CommonJS loader under --default-type
GeoffreyBooth Oct 5, 2023
9ed022b
tools: update eslint to 8.45.0
nodejs-github-bot Jul 18, 2023
5c4057a
tools: update eslint to 8.46.0
nodejs-github-bot Aug 1, 2023
9509884
tools: update eslint to 8.47.0
nodejs-github-bot Aug 15, 2023
2208832
tools: update eslint to 8.48.0
nodejs-github-bot Aug 29, 2023
96dea39
tools: update eslint to 8.49.0
nodejs-github-bot Sep 12, 2023
669e3d7
tools: update eslint to 8.50.0
nodejs-github-bot Oct 3, 2023
83f680f
tools: update eslint to 8.51.0
nodejs-github-bot Oct 8, 2023
5699370
esm: use import attributes instead of import assertions
aduh95 Oct 14, 2023
52901a7
test: deflake `test-esm-loader-resolve-type`
aduh95 Oct 21, 2023
bd6512e
test: deflake `test-loaders-workers-spawned`
aduh95 Oct 18, 2023
b0829b7
vm: use import attributes instead of import assertions
aduh95 Oct 23, 2023
8f9645d
test: refactor `test-node-output-errors`
aduh95 Aug 3, 2023
85b21a8
test: fix edge snapshot stack traces
GeoffreyBooth Sep 17, 2023
3bf8c02
tools: skip ruff on tools/node_modules
MoLow Sep 24, 2023
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
Prev Previous commit
Next Next commit
esm: remove support for arrays in import internal method
This avoids initializing arrays that we never use, and simplifies the
implementation overall.

PR-URL: #48296
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Jacob Smith <jacob@frende.me>
Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
aduh95 authored and targos committed Nov 10, 2023
commit 96edd85ea4bf340717a30df0b4eac66da9702a68
42 changes: 16 additions & 26 deletions lib/internal/modules/esm/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,37 +109,27 @@ class Hooks {
// Cache URLs we've already validated to avoid repeated validation
#validatedUrls = new SafeSet();

constructor(userLoaders) {
this.addCustomLoaders(userLoaders);
}

/**
* Collect custom/user-defined module loader hook(s).
* After all hooks have been collected, the global preload hook(s) must be initialized.
* @param {import('./loader.js).KeyedExports} customLoaders Exports from user-defined loaders
* (as returned by `ModuleLoader.import()`).
* @param {string} url Custom loader specifier
* @param {Record<string, unknown>} exports
*/
addCustomLoaders(customLoaders = []) {
for (let i = 0; i < customLoaders.length; i++) {
const {
exports,
url,
} = customLoaders[i];
const {
globalPreload,
resolve,
load,
} = pluckHooks(exports);
addCustomLoader(url, exports) {
const {
globalPreload,
resolve,
load,
} = pluckHooks(exports);

if (globalPreload) {
ArrayPrototypePush(this.#chains.globalPreload, { fn: globalPreload, url });
}
if (resolve) {
ArrayPrototypePush(this.#chains.resolve, { fn: resolve, url });
}
if (load) {
ArrayPrototypePush(this.#chains.load, { fn: load, url });
}
if (globalPreload) {
ArrayPrototypePush(this.#chains.globalPreload, { fn: globalPreload, url });
}
if (resolve) {
ArrayPrototypePush(this.#chains.resolve, { fn: resolve, url });
}
if (load) {
ArrayPrototypePush(this.#chains.load, { fn: load, url });
}
}

Expand Down
46 changes: 5 additions & 41 deletions lib/internal/modules/esm/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@
require('internal/modules/cjs/loader');

const {
Array,
ArrayIsArray,
FunctionPrototypeCall,
ObjectSetPrototypeOf,
PromisePrototypeThen,
SafePromiseAllReturnArrayLike,
SafeWeakMap,
} = primordials;

Expand Down Expand Up @@ -246,51 +243,18 @@ class DefaultModuleLoader {
*
* This method must NOT be renamed: it functions as a dynamic import on a
* loader module.
* @param {string | string[]} specifiers Path(s) to the module.
* @param {string} specifier The first parameter of an `import()` expression.
* @param {string} parentURL Path of the parent importing the module.
* @param {Record<string, string>} importAssertions Validations for the
* module import.
* @returns {Promise<ExportedHooks | KeyedExports[]>}
* A collection of module export(s) or a list of collections of module
* export(s).
*/
async import(specifiers, parentURL, importAssertions) {
// For loaders, `import` is passed multiple things to process, it returns a
// list pairing the url and exports collected. This is especially useful for
// error messaging, to identity from where an export came. But, in most
// cases, only a single url is being "imported" (ex `import()`), so there is
// only 1 possible url from which the exports were collected and it is
// already known to the caller. Nesting that in a list would only ever
// create redundant work for the caller, so it is later popped off the
// internal list.
const wasArr = ArrayIsArray(specifiers);
if (!wasArr) { specifiers = [specifiers]; }

const count = specifiers.length;
const jobs = new Array(count);

for (let i = 0; i < count; i++) {
jobs[i] = PromisePrototypeThen(
this
.getModuleJob(specifiers[i], parentURL, importAssertions)
.run(),
({ module }) => module.getNamespace(),
);
}

const namespaces = await SafePromiseAllReturnArrayLike(jobs);

if (!wasArr) { return namespaces[0]; } // We can skip the pairing below

for (let i = 0; i < count; i++) {
namespaces[i] = {
__proto__: null,
url: specifiers[i],
exports: namespaces[i],
};
}

return namespaces;
async import(specifier, parentURL, importAssertions) {
const moduleJob = this.getModuleJob(specifier, parentURL, importAssertions);
const { module } = await moduleJob.run();
return module.getNamespace();
}

/**
Expand Down
12 changes: 6 additions & 6 deletions lib/internal/modules/esm/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ function isLoaderWorker() {
}

async function initializeHooks() {
const customLoaderPaths = getOptionValue('--experimental-loader');
const customLoaderURLs = getOptionValue('--experimental-loader');

let cwd;
try {
Expand Down Expand Up @@ -149,17 +149,17 @@ async function initializeHooks() {

const parentURL = pathToFileURL(cwd).href;

for (let i = 0; i < customLoaderPaths.length; i++) {
const customLoaderPath = customLoaderPaths[i];
for (let i = 0; i < customLoaderURLs.length; i++) {
const customLoaderURL = customLoaderURLs[i];

// Importation must be handled by internal loader to avoid polluting user-land
const keyedExportsSublist = await privateModuleLoader.import(
[customLoaderPath], // Import can handle multiple paths, but custom loaders must be sequential
const keyedExports = await privateModuleLoader.import(
customLoaderURL,
parentURL,
kEmptyObject,
);

hooks.addCustomLoaders(keyedExportsSublist);
hooks.addCustomLoader(customLoaderURL, keyedExports);
}

const preloadScripts = hooks.initializeGlobalPreload();
Expand Down
10 changes: 7 additions & 3 deletions lib/internal/process/esm_loader.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
'use strict';

const {
SafePromiseAllReturnVoid,
} = primordials;

const { createModuleLoader } = require('internal/modules/esm/loader');
const { getOptionValue } = require('internal/options');
const {
Expand Down Expand Up @@ -27,11 +31,11 @@ module.exports = {
cwd = '/';
}
const parentURL = pathToFileURL(cwd).href;
await esmLoader.import(
userImports,
await SafePromiseAllReturnVoid(userImports, (specifier) => esmLoader.import(
specifier,
parentURL,
kEmptyObject,
);
));
}
await callback(esmLoader);
} catch (err) {
Expand Down