diff --git a/test/es-module/test-esm-assertionless-json-import.js b/test/es-module/test-esm-assertionless-json-import.js index f5dc5ff81057..70ae41088459 100644 --- a/test/es-module/test-esm-assertionless-json-import.js +++ b/test/es-module/test-esm-assertionless-json-import.js @@ -1,4 +1,4 @@ -// Flags: --experimental-loader ./test/fixtures/es-module-loaders/assertionless-json-import.mjs +// Flags: --import ./test/fixtures/es-module-loaders/assertionless-json-import.mjs 'use strict'; const common = require('../common'); const assert = require('assert'); diff --git a/test/es-module/test-esm-detect-ambiguous.mjs b/test/es-module/test-esm-detect-ambiguous.mjs index 170b15693828..c9c739a9e23c 100644 --- a/test/es-module/test-esm-detect-ambiguous.mjs +++ b/test/es-module/test-esm-detect-ambiguous.mjs @@ -147,18 +147,20 @@ describe('Module syntax detection', { concurrency: !process.env.TEST_PARALLEL }, } it('should not hint wrong format in resolve hook', async () => { - // eslint-disable-next-line no-unassigned-vars - let writeSync; const { stdout, stderr, code, signal } = await spawnPromisified(process.execPath, [ '--no-warnings', - '--loader', - `data:text/javascript,import { writeSync } from "node:fs"; export ${encodeURIComponent( - async function resolve(s, c, next) { - const result = await next(s, c); - writeSync(1, result.format + '\n'); - return result; - } - )}`, + '--import', + 'data:text/javascript,' + encodeURIComponent(` + import { writeSync } from 'node:fs'; + import { registerHooks } from 'node:module'; + registerHooks({ + resolve(specifier, context, nextResolve) { + const result = nextResolve(specifier, context); + writeSync(1, result.format + '\\n'); + return result; + }, + }); + `), fixtures.path('es-modules/package-without-type/noext-esm'), ]); @@ -166,7 +168,6 @@ describe('Module syntax detection', { concurrency: !process.env.TEST_PARALLEL }, assert.strictEqual(stdout, 'null\nexecuted\n'); assert.strictEqual(code, 0); assert.strictEqual(signal, null); - }); }); diff --git a/test/es-module/test-esm-import-meta-resolve.mjs b/test/es-module/test-esm-import-meta-resolve.mjs index 979881e19238..ba43b361c58c 100644 --- a/test/es-module/test-esm-import-meta-resolve.mjs +++ b/test/es-module/test-esm-import-meta-resolve.mjs @@ -94,7 +94,10 @@ await assert.rejects(import('data:text/javascript,export default import.meta.res const result = await spawnPromisified(execPath, [ '--no-warnings', '--input-type=module', - '--import', 'data:text/javascript,import{register}from"node:module";register("data:text/javascript,")', + '--import', 'data:text/javascript,' + encodeURIComponent( + 'import { registerHooks } from "node:module";' + + 'registerHooks({ resolve: (s, c, next) => next(s, c) });', + ), '--eval', 'console.log(import.meta.resolve(new URL("http://example.com")))', ]); diff --git a/test/es-module/test-esm-virtual-json.mjs b/test/es-module/test-esm-virtual-json.mjs index a42b037fc1f2..d7bf4ffc1226 100644 --- a/test/es-module/test-esm-virtual-json.mjs +++ b/test/es-module/test-esm-virtual-json.mjs @@ -1,10 +1,10 @@ import '../common/index.mjs'; import * as fixtures from '../common/fixtures.mjs'; -import { register } from 'node:module'; +import { registerHooks } from 'node:module'; import assert from 'node:assert'; -async function resolve(referrer, context, next) { - const result = await next(referrer, context); +function resolve(referrer, context, next) { + const result = next(referrer, context); const url = new URL(result.url); url.searchParams.set('randomSeed', Math.random()); result.url = url.href; @@ -22,7 +22,7 @@ function load(url, context, next) { return next(url, context); } -register(`data:text/javascript,export ${encodeURIComponent(resolve)};export ${encodeURIComponent(load)}`); +registerHooks({ resolve, load }); assert.notDeepStrictEqual( await import(fixtures.fileURL('empty.json'), { with: { type: 'json' } }), diff --git a/test/fixtures/es-module-loaders/assertionless-json-import.mjs b/test/fixtures/es-module-loaders/assertionless-json-import.mjs index 3ffc4c1148fe..a83c1545dc30 100644 --- a/test/fixtures/es-module-loaders/assertionless-json-import.mjs +++ b/test/fixtures/es-module-loaders/assertionless-json-import.mjs @@ -1,15 +1,22 @@ +import { registerHooks } from 'node:module'; + const DATA_URL_PATTERN = /^data:application\/json(?:[^,]*?)(;base64)?,([\s\S]*)$/; const JSON_URL_PATTERN = /^[^?]+\.json(\?[^#]*)?(#.*)?$/; -export async function resolve(specifier, context, next) { - const noAttributesSpecified = context.importAttributes.type == null; +function resolve(specifier, context, next) { + // Hooks also run for require() calls, which have no import attributes. + if (context.importAttributes === undefined) { + return next(specifier, context); + } - // Mutation from resolve hook should be discarded. - context.importAttributes.type = 'whatever'; + const noAttributesSpecified = context.importAttributes.type == null; // This fixture assumes that no other resolve hooks in the chain will error on invalid import attributes // (as defaultResolve doesn't). - const result = await next(specifier, context); + const result = next(specifier, { + ...context, + importAttributes: { ...context.importAttributes, type: 'whatever' }, + }); if (noAttributesSpecified && (DATA_URL_PATTERN.test(result.url) || JSON_URL_PATTERN.test(result.url))) { @@ -22,3 +29,5 @@ export async function resolve(specifier, context, next) { return result; } + +registerHooks({ resolve }); diff --git a/test/fixtures/es-module-shadow-realm/custom-loaders.js b/test/fixtures/es-module-shadow-realm/custom-hooks.js similarity index 69% rename from test/fixtures/es-module-shadow-realm/custom-loaders.js rename to test/fixtures/es-module-shadow-realm/custom-hooks.js index bf4402250cc4..b62d25fb62f2 100644 --- a/test/fixtures/es-module-shadow-realm/custom-loaders.js +++ b/test/fixtures/es-module-shadow-realm/custom-hooks.js @@ -1,10 +1,10 @@ -// This fixture is used to test that custom loaders are not enabled in the ShadowRealm. +// This fixture is used to test that customization hooks are not enabled in the ShadowRealm. 'use strict'; const assert = require('assert'); async function workInChildProcess() { - // Assert that the process is running with a custom loader. + // Assert that the process is running with custom hooks. const moduleNamespace = await import('file:///42.mjs'); assert.strictEqual(moduleNamespace.default, 42); diff --git a/test/fixtures/es-module-shadow-realm/register-hooks.js b/test/fixtures/es-module-shadow-realm/register-hooks.js new file mode 100644 index 000000000000..46b51a3a8421 --- /dev/null +++ b/test/fixtures/es-module-shadow-realm/register-hooks.js @@ -0,0 +1,17 @@ +'use strict'; +const { registerHooks } = require('node:module'); + +registerHooks({ + resolve(specifier, context, nextResolve) { + if (specifier.startsWith('file:///') && specifier.includes('42')) { + return { shortCircuit: true, url: specifier }; + } + return nextResolve(specifier, context); + }, + load(url, context, nextLoad) { + if (url.startsWith('file:///') && url.includes('42')) { + return { shortCircuit: true, format: 'module', source: 'export default 42' }; + } + return nextLoad(url, context); + }, +}); diff --git a/test/fixtures/module-hooks/register-logger-async-hooks.mjs b/test/fixtures/module-hooks/register-logger-async-hooks.mjs deleted file mode 100644 index 28a6ae5f223c..000000000000 --- a/test/fixtures/module-hooks/register-logger-async-hooks.mjs +++ /dev/null @@ -1,2 +0,0 @@ -import { register } from 'node:module'; -register('./logger-async-hooks.mjs', import.meta.url); diff --git a/test/fixtures/module-hooks/register-logger-hooks.mjs b/test/fixtures/module-hooks/register-logger-hooks.mjs new file mode 100644 index 000000000000..f6582b7525b1 --- /dev/null +++ b/test/fixtures/module-hooks/register-logger-hooks.mjs @@ -0,0 +1,13 @@ +import { registerHooks } from 'node:module'; +import { writeSync } from 'node:fs'; + +registerHooks({ + resolve(specifier, context, nextResolve) { + writeSync(1, `resolve ${specifier} from ${context.parentURL}\n`); + return nextResolve(specifier, context); + }, + load(url, context, nextLoad) { + writeSync(1, `load ${url}\n`); + return nextLoad(url, context); + }, +}); diff --git a/test/fixtures/test-runner/coverage-loader/hooks.mjs b/test/fixtures/test-runner/coverage-loader/hooks.mjs index 57ee712ed97c..2326d18406cd 100644 --- a/test/fixtures/test-runner/coverage-loader/hooks.mjs +++ b/test/fixtures/test-runner/coverage-loader/hooks.mjs @@ -1,3 +1,5 @@ +import { registerHooks } from 'node:module'; + const sources = { // Virtual file. Doesn't exist on disk "virtual.js": ` @@ -16,10 +18,12 @@ import{describe,it}from"node:test";import assert from"node:assert";import{sum}fr //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6ImtIQUFPLFNBQVMsT0FBUSxFQUFHLENBQ3pCLEdBQUksQ0FBQyxFQUFFLE1BQU8sS0FBUSxPQUFPLE1BQVEsUUFBUSxFQUFHLE1BQU0sSUFBSSxNQUFNLGNBQWMsRUFDOUUsT0FBTyxFQUFFLE9BQU8sQ0FBQyxJQUFLLE1BQVEsSUFBTSxHQUFHLENBQ3pDLENBSGdCIiwibmFtZXMiOltdLCJpZ25vcmVMaXN0IjpbXSwic291cmNlcyI6WyIuL3N1bS50cyJdLCJzb3VyY2VzQ29udGVudCI6W251bGxdfQ==`, }; -export async function load(url, context, nextLoad) { +function load(url, context, nextLoad) { const file = url.split('/').at(-1); if (sources[file] !== undefined) { return { format: "module", source: sources[file], shortCircuit: true }; } return nextLoad(url, context); } + +registerHooks({ load }); diff --git a/test/fixtures/test-runner/coverage-loader/register-hooks.js b/test/fixtures/test-runner/coverage-loader/register-hooks.js deleted file mode 100644 index cbbac6126411..000000000000 --- a/test/fixtures/test-runner/coverage-loader/register-hooks.js +++ /dev/null @@ -1,4 +0,0 @@ -const { register } = require('node:module'); -const { pathToFileURL } = require('node:url'); - -register('./hooks.mjs', pathToFileURL(__filename)); diff --git a/test/fixtures/typescript/ts/hook.ts b/test/fixtures/typescript/ts/hook.ts deleted file mode 100644 index e0dd46448b83..000000000000 --- a/test/fixtures/typescript/ts/hook.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { ResolveHook } from 'node:module'; - -// Pass through -export const resolve: ResolveHook = async function resolve(specifier, context, nextResolve) { - if(false){ - // https://github.com/nodejs/node/issues/54645 - // A bug in the typescript parsers swc causes - // the next line to not be parsed correctly - } - return nextResolve(specifier, context); -}; diff --git a/test/fixtures/typescript/ts/test-loader.ts b/test/fixtures/typescript/ts/test-loader.ts index 8b957bf72f0a..33b4a11ef260 100644 --- a/test/fixtures/typescript/ts/test-loader.ts +++ b/test/fixtures/typescript/ts/test-loader.ts @@ -1,4 +1,9 @@ -import { register } from 'node:module'; -import * as fixtures from '../../../common/fixtures.mjs'; +import { registerHooks } from 'node:module'; +import type { ResolveHookSync } from 'node:module'; -register(fixtures.fileURL('typescript/ts/hook.ts')); +// Pass through +const resolve: ResolveHookSync = function resolve(specifier, context, nextResolve) { + return nextResolve(specifier, context); +}; + +registerHooks({ resolve }); diff --git a/test/module-hooks/test-module-hooks-require-esm.js b/test/module-hooks/test-module-hooks-require-esm.js index a3bb0dd2c4bb..9bfe3e5e674e 100644 --- a/test/module-hooks/test-module-hooks-require-esm.js +++ b/test/module-hooks/test-module-hooks-require-esm.js @@ -1,18 +1,15 @@ 'use strict'; -// This tests that the async loader hooks can be invoked for require(esm). +// This tests that module.registerHooks() hooks are invoked for require(esm) +// and for the CommonJS and ES module graphs reachable from it. -require('../common'); const common = require('../common'); const { spawnSyncAndAssert } = require('../common/child_process'); const fixtures = require('../common/fixtures'); const assert = require('assert'); function assertSubGraph(output) { - // FIXME(node:59666): the async resolve hook invoked for require() in imported CJS only get the URL, - // not the specifier. This may be fixable if we re-implement the async loader hooks from within - // the synchronous loader hooks and use the original require() implementation. - assert.match(output, /resolve .+esm\.mjs from file:.+main\.cjs/); + assert.match(output, /resolve \.\/esm\.mjs from file:.+main\.cjs/); assert.match(output, /load file:.+esm\.mjs/); assert.match(output, /resolve \.\/inner\.mjs from file:.+esm\.mjs/); @@ -21,8 +18,7 @@ function assertSubGraph(output) { assert.match(output, /resolve \.\/cjs\.cjs from file:.+esm\.mjs/); assert.match(output, /load file:.+cjs\.cjs/); - // FIXME(node:59666): see above. - assert.match(output, /resolve .+inner\.cjs from file:.+cjs\.cjs/); + assert.match(output, /resolve \.\/inner\.cjs from file:.+cjs\.cjs/); assert.match(output, /load file:.+inner\.cjs/); assert.match(output, /esmValue in main\.cjs: esm/); @@ -31,7 +27,7 @@ function assertSubGraph(output) { spawnSyncAndAssert(process.execPath, [ '--import', - fixtures.fileURL('module-hooks', 'register-logger-async-hooks.mjs'), + fixtures.fileURL('module-hooks', 'register-logger-hooks.mjs'), fixtures.path('module-hooks', 'require-esm', 'main.cjs'), ], { stdout: common.mustCall((output) => { @@ -50,7 +46,7 @@ spawnSyncAndAssert(process.execPath, [ spawnSyncAndAssert(process.execPath, [ '--import', - fixtures.fileURL('module-hooks', 'register-logger-async-hooks.mjs'), + fixtures.fileURL('module-hooks', 'register-logger-hooks.mjs'), fixtures.path('module-hooks', 'require-esm', 'main.mjs'), ], { stdout: common.mustCall((output) => { @@ -64,7 +60,7 @@ spawnSyncAndAssert(process.execPath, [ assert.match(output, /resolve .+main\.mjs from undefined/); assert.match(output, /load file:.+main\.mjs/); - assert.match(output, /resolve .+main\.cjs from file:.+main\.mjs/); + assert.match(output, /resolve \.\/main\.cjs from file:.+main\.mjs/); assert.match(output, /load file:.+main\.cjs/); assertSubGraph(output); diff --git a/test/parallel/test-find-package-json.js b/test/parallel/test-find-package-json.js index fd9a1a85e774..d1df637dcc56 100644 --- a/test/parallel/test-find-package-json.js +++ b/test/parallel/test-find-package-json.js @@ -150,19 +150,23 @@ describe('findPackageJSON', () => { // Throws when no arguments are provided })); }); - it('should work within a loader', async () => { + it('should work within a resolve hook', async () => { const specifierBase = './packages/root-types-field'; const target = fixtures.fileURL(specifierBase, 'index.js'); const foundPjsonPath = path.toNamespacedPath(fixtures.path(specifierBase, 'package.json')); const { code, stderr, stdout } = await common.spawnPromisified(process.execPath, [ '--no-warnings', - '--loader', + '--import', [ 'data:text/javascript,', 'import fs from "node:fs";', 'import module from "node:module";', - encodeURIComponent(`fs.writeSync(1, module.findPackageJSON(${JSON.stringify(target)}));`), - 'export const resolve = async (s, c, n) => n(s);', + encodeURIComponent(`module.registerHooks({ + resolve(specifier, context, nextResolve) { + fs.writeSync(1, module.findPackageJSON(${JSON.stringify(target)})); + return nextResolve(specifier, context); + }, + });`), ].join(''), '--eval', 'import "node:os";', // Can be anything that triggers the resolve hook chain @@ -173,23 +177,6 @@ describe('findPackageJSON', () => { // Throws when no arguments are provided assert.strictEqual(code, 0); }); - it('should work with an async resolve hook registered', async () => { - const specifierBase = './packages/root-types-field'; - const target = fixtures.fileURL(specifierBase, 'index.js'); - const foundPjsonPath = path.toNamespacedPath(fixtures.path(specifierBase, 'package.json')); - const { code, stderr, stdout } = await common.spawnPromisified(process.execPath, [ - '--no-warnings', - '--loader', - 'data:text/javascript,export const resolve = async (s, c, n) => n(s);', - '--print', - `require("node:module").findPackageJSON(${JSON.stringify(target)})`, - ]); - - assert.strictEqual(stderr, ''); - assert.ok(stdout.includes(foundPjsonPath), stdout); - assert.strictEqual(code, 0); - }); - it('should work when unrecognised keys are specified within the export', async () => { const specifierBase = './packages/unrecognised-export-keys'; const target = fixtures.fileURL(specifierBase, 'index.js'); diff --git a/test/parallel/test-runner-cli.js b/test/parallel/test-runner-cli.js index 28ad56a0a937..000c9b8be894 100644 --- a/test/parallel/test-runner-cli.js +++ b/test/parallel/test-runner-cli.js @@ -283,10 +283,10 @@ for (const isolation of ['none', 'process']) { } { - // Use test with --loader and --require. + // Use test with --import and --require. // This case is common since vscode uses --require to load the debugger. const args = ['--no-warnings', - '--experimental-loader', 'data:text/javascript,', + '--import', 'data:text/javascript,', '--require', fixtures.path('empty.js'), '--test', join(testFixtures, 'default-behavior', 'index.test.js')]; const child = spawnSync(process.execPath, args); diff --git a/test/parallel/test-runner-coverage.js b/test/parallel/test-runner-coverage.js index fa56c5af3e80..020adfb825a7 100644 --- a/test/parallel/test-runner-coverage.js +++ b/test/parallel/test-runner-coverage.js @@ -357,15 +357,14 @@ test('coverage reports on lines, functions, and branches', skipIfNoInspector, as test('coverage with ESM hook - source irrelevant', skipIfNoInspector, () => { let report = [ '# start of coverage report', - '# ------------------------------------------------------------------', - '# file | line % | branch % | funcs % | uncovered lines', - '# ------------------------------------------------------------------', - '# hooks.mjs | 100.00 | 100.00 | 100.00 | ', - '# register-hooks.js | 100.00 | 100.00 | 100.00 | ', - '# virtual.js | 100.00 | 100.00 | 100.00 | ', - '# ------------------------------------------------------------------', - '# all files | 100.00 | 100.00 | 100.00 | ', - '# ------------------------------------------------------------------', + '# -----------------------------------------------------------', + '# file | line % | branch % | funcs % | uncovered lines', + '# -----------------------------------------------------------', + '# hooks.mjs | 100.00 | 100.00 | 100.00 | ', + '# virtual.js | 100.00 | 100.00 | 100.00 | ', + '# -----------------------------------------------------------', + '# all files | 100.00 | 100.00 | 100.00 | ', + '# -----------------------------------------------------------', '# end of coverage report', ].join('\n'); @@ -376,7 +375,7 @@ test('coverage with ESM hook - source irrelevant', skipIfNoInspector, () => { const fixture = fixtures.path('test-runner', 'coverage-loader'); const args = [ '--import', - './register-hooks.js', + './hooks.mjs', '--test', '--experimental-test-coverage', '--test-coverage-exclude=!test/**', @@ -394,16 +393,15 @@ test('coverage with ESM hook - source irrelevant', skipIfNoInspector, () => { test('coverage with ESM hook - source transpiled', skipIfNoInspector, () => { let report = [ '# start of coverage report', - '# ------------------------------------------------------------------', - '# file | line % | branch % | funcs % | uncovered lines', - '# ------------------------------------------------------------------', - '# hooks.mjs | 100.00 | 100.00 | 100.00 | ', - '# register-hooks.js | 100.00 | 100.00 | 100.00 | ', - '# sum.test.ts | 100.00 | 100.00 | 100.00 | ', - '# sum.ts | 100.00 | 100.00 | 100.00 | ', - '# ------------------------------------------------------------------', - '# all files | 100.00 | 100.00 | 100.00 | ', - '# ------------------------------------------------------------------', + '# ------------------------------------------------------------', + '# file | line % | branch % | funcs % | uncovered lines', + '# ------------------------------------------------------------', + '# hooks.mjs | 100.00 | 100.00 | 100.00 | ', + '# sum.test.ts | 100.00 | 100.00 | 100.00 | ', + '# sum.ts | 100.00 | 100.00 | 100.00 | ', + '# ------------------------------------------------------------', + '# all files | 100.00 | 100.00 | 100.00 | ', + '# ------------------------------------------------------------', '# end of coverage report', ].join('\n'); @@ -413,7 +411,7 @@ test('coverage with ESM hook - source transpiled', skipIfNoInspector, () => { const fixture = fixtures.path('test-runner', 'coverage-loader'); const args = [ - '--import', './register-hooks.js', + '--import', './hooks.mjs', '--test', '--experimental-test-coverage', '--test-coverage-exclude=!test/**', diff --git a/test/parallel/test-shadow-realm-custom-loaders.js b/test/parallel/test-shadow-realm-custom-loaders.js index 80cda74bb889..311a8af9b96a 100644 --- a/test/parallel/test-shadow-realm-custom-loaders.js +++ b/test/parallel/test-shadow-realm-custom-loaders.js @@ -9,14 +9,12 @@ const commonArgs = [ ]; async function main() { - // Verifies that custom loaders are not enabled in the ShadowRealm. + // Verifies that customization hooks are not enabled in the ShadowRealm. const child = await common.spawnPromisified(process.execPath, [ ...commonArgs, - '--experimental-loader', - fixtures.fileURL('es-module-loaders', 'loader-resolve-shortcircuit.mjs'), - '--experimental-loader', - fixtures.fileURL('es-module-loaders', 'loader-load-foo-or-42.mjs'), - fixtures.path('es-module-shadow-realm', 'custom-loaders.js'), + '--require', + fixtures.path('es-module-shadow-realm', 'register-hooks.js'), + fixtures.path('es-module-shadow-realm', 'custom-hooks.js'), ]); assert.strictEqual(child.stderr, '');