-
-
Notifications
You must be signed in to change notification settings - Fork 35.4k
working mock test #39240
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
working mock test #39240
Changes from 9 commits
13ab19e
ffdaa30
f5e96f3
f923cce
0e1e124
4e4ed13
8369c4a
57ace81
7416b27
d0276c6
0e11bde
172c038
b9e1380
5ad4c1b
d61b7d4
93ba88a
1c5996d
b9f339f
05e71c9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -773,7 +773,7 @@ source to a supported one (see [Examples](#examples) below). | |
| > signature may change. Do not rely on the API described below. | ||
|
|
||
| > Note: In a previous version of this API, this hook was named | ||
| > `getGlobalPreloadCode`. | ||
| > `globalPreload`. | ||
|
|
||
| * Returns: {string} | ||
|
|
||
|
|
@@ -806,6 +806,33 @@ const require = createRequire(cwd() + '/<preload>'); | |
| } | ||
| ``` | ||
|
|
||
| In order to allow communication between the application and the loader, another | ||
| argument is provided to the preload code: `port`. This is available as a | ||
| parameter to the loader hook and inside of the source text returned by the hook. | ||
| Some care must be taken in order to properly `ref()` and `unref()` the | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does it mean to
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ref/unref determines if an event loop is kept alive to stuff. I could link to https://nodejs.org/dist/latest-v17.x/docs/api/worker_threads.html#portref ?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, a link to the docs/example would help. |
||
| `MessagePort` to prevent a process from being in a state where it won't close | ||
| normally. | ||
|
|
||
| ```js | ||
| /** | ||
| * This example has the application context send a message to the loader | ||
| * and sends the message back to the application context | ||
| * @param {object} utilities | ||
| * @param {MessagePort} utilities.port | ||
|
bmeck marked this conversation as resolved.
Outdated
|
||
| */ | ||
| export function globalPreload({ port }) { | ||
|
GeoffreyBooth marked this conversation as resolved.
|
||
| port.onmessage = (evt) => { | ||
| port.postMessage(evt.data); | ||
| }; | ||
| return `\ | ||
| port.postMessage('console.log("I went to the Loader and back");'); | ||
| port.onmessage = (evt) => { | ||
| eval(evt.data); | ||
| }; | ||
| `; | ||
| } | ||
| ``` | ||
|
|
||
| ### Examples | ||
|
|
||
| The various loader hooks can be used together to accomplish wide-ranging | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| 'use strict'; | ||
|
|
||
|
bmeck marked this conversation as resolved.
|
||
| const { getOptionValue } = require('internal/options'); | ||
| const experimentalImportMetaResolve = | ||
| getOptionValue('--experimental-import-meta-resolve'); | ||
| const { PromisePrototypeThen, PromiseReject } = primordials; | ||
| const asyncESM = require('internal/process/esm_loader'); | ||
|
|
||
| function createImportMetaResolve(defaultParentUrl) { | ||
| return async function resolve(specifier, parentUrl = defaultParentUrl) { | ||
| return PromisePrototypeThen( | ||
| asyncESM.esmLoader.resolve(specifier, parentUrl), | ||
|
bmeck marked this conversation as resolved.
|
||
| ({ url }) => url, | ||
| (error) => ( | ||
| error.code === 'ERR_UNSUPPORTED_DIR_IMPORT' ? | ||
| error.url : PromiseReject(error)) | ||
| ); | ||
| }; | ||
|
bmeck marked this conversation as resolved.
|
||
| } | ||
|
|
||
| function initializeImportMeta(meta, context) { | ||
| const url = context.url; | ||
|
|
||
| // Alphabetical | ||
| if (experimentalImportMetaResolve) | ||
| meta.resolve = createImportMetaResolve(url); | ||
|
bmeck marked this conversation as resolved.
|
||
| meta.url = url; | ||
| } | ||
|
|
||
| module.exports = { | ||
| initializeImportMeta | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,7 @@ const { | |
| SafeWeakMap, | ||
| globalThis, | ||
| } = primordials; | ||
| const { MessageChannel } = require('internal/worker/io'); | ||
|
|
||
| const { | ||
| ERR_INVALID_ARG_TYPE, | ||
|
|
@@ -39,6 +40,9 @@ const { | |
| defaultResolve, | ||
| DEFAULT_CONDITIONS, | ||
| } = require('internal/modules/esm/resolve'); | ||
| const { | ||
| initializeImportMeta | ||
| } = require('internal/modules/esm/initialize_import_meta'); | ||
| const { defaultLoad } = require('internal/modules/esm/load'); | ||
| const { translators } = require( | ||
| 'internal/modules/esm/translators'); | ||
|
|
@@ -76,6 +80,8 @@ class ESMLoader { | |
| defaultResolve, | ||
| ]; | ||
|
|
||
| #importMetaInitializer = initializeImportMeta; | ||
|
|
||
| /** | ||
| * Map of already-loaded CJS modules to use | ||
| */ | ||
|
|
@@ -359,7 +365,18 @@ class ESMLoader { | |
| if (!count) return; | ||
|
|
||
| for (let i = 0; i < count; i++) { | ||
| const preload = this.#globalPreloaders[i](); | ||
| const channel = new MessageChannel(); | ||
| const { | ||
| port1: insidePreload, | ||
| port2: insideLoader, | ||
| } = channel; | ||
|
|
||
| insidePreload.unref(); | ||
| insideLoader.unref(); | ||
|
|
||
| const preload = this.#globalPreloaders[i]({ | ||
| port: insideLoader | ||
| }); | ||
|
|
||
| if (preload == null) return; | ||
|
|
||
|
|
@@ -373,22 +390,44 @@ class ESMLoader { | |
| const { compileFunction } = require('vm'); | ||
| const preloadInit = compileFunction( | ||
| preload, | ||
| ['getBuiltin'], | ||
| ['getBuiltin', 'port', 'setImportMetaCallback'], | ||
| { | ||
| filename: '<preload>', | ||
| } | ||
| ); | ||
| const { NativeModule } = require('internal/bootstrap/loaders'); | ||
|
|
||
| FunctionPrototypeCall(preloadInit, globalThis, (builtinName) => { | ||
| if (NativeModule.canBeRequiredByUsers(builtinName)) { | ||
| return require(builtinName); | ||
| let finished = false; | ||
| let replacedImportMetaInitializer = false; | ||
| let next = this.#importMetaInitializer; | ||
| try { | ||
| FunctionPrototypeCall(preloadInit, globalThis, (builtinName) => { | ||
| if (NativeModule.canBeRequiredByUsers(builtinName)) { | ||
| return require(builtinName); | ||
| } | ||
| throw new ERR_INVALID_ARG_VALUE('builtinName', builtinName); | ||
| }, insidePreload, (fn) => { | ||
| if (finished || typeof fn !== 'function') { | ||
| throw new ERR_INVALID_ARG_TYPE('fn', fn); | ||
| } | ||
| replacedImportMetaInitializer = true; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I’m struggling to follow this code. Are we in a callback here? Can this be split out? I see that by now we’ve apparently replaced |
||
| const parent = next; | ||
| next = (meta, context) => { | ||
| return fn(meta, context, parent); | ||
| }; | ||
| }); | ||
| } finally { | ||
| finished = true; | ||
| if (replacedImportMetaInitializer) { | ||
| this.#importMetaInitializer = next; | ||
|
GeoffreyBooth marked this conversation as resolved.
|
||
| } | ||
| throw new ERR_INVALID_ARG_VALUE('builtinName', builtinName); | ||
| }); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| importMetaInitialize(meta, context) { | ||
| this.#importMetaInitializer(meta, context); | ||
| } | ||
|
GeoffreyBooth marked this conversation as resolved.
|
||
|
|
||
| /** | ||
| * Resolve the location of the module. | ||
| * | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| // Flags: --loader ./test/fixtures/es-module-loaders/mock-loader.mjs | ||
| import '../common/index.mjs'; | ||
| import assert from 'assert/strict'; | ||
| import mock from 'node:mock'; | ||
|
bmeck marked this conversation as resolved.
|
||
|
|
||
| mock('node:events', { | ||
| EventEmitter: 'This is mocked!' | ||
| }); | ||
|
|
||
| // This resolves to node:events | ||
| assert.deepStrictEqual(await import('events'), Object.defineProperty({ | ||
|
Comment on lines
+10
to
+14
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes |
||
| __proto__: null, | ||
| EventEmitter: 'This is mocked!' | ||
| }, Symbol.toStringTag, { | ||
| enumerable: false, | ||
| value: 'Module' | ||
| })); | ||
|
|
||
| const mutator = mock('node:events', { | ||
| EventEmitter: 'This is mocked v2!' | ||
| }); | ||
|
|
||
| const mockedV2 = await import('node:events'); | ||
| assert.deepStrictEqual(mockedV2, Object.defineProperty({ | ||
| __proto__: null, | ||
| EventEmitter: 'This is mocked v2!' | ||
| }, Symbol.toStringTag, { | ||
| enumerable: false, | ||
| value: 'Module' | ||
| })); | ||
|
|
||
| mutator.EventEmitter = 'This is mocked v3!'; | ||
| assert.deepStrictEqual(mockedV2, Object.defineProperty({ | ||
| __proto__: null, | ||
| EventEmitter: 'This is mocked v3!' | ||
| }, Symbol.toStringTag, { | ||
| enumerable: false, | ||
| value: 'Module' | ||
| })); | ||
Uh oh!
There was an error while loading. Please reload this page.