diff --git a/lib/internal/modules/esm/hooks.js b/lib/internal/modules/esm/hooks.js index 309c77153e9b59..fa34e4b39a5950 100644 --- a/lib/internal/modules/esm/hooks.js +++ b/lib/internal/modules/esm/hooks.js @@ -31,6 +31,7 @@ const { ERR_INVALID_RETURN_PROPERTY_VALUE, ERR_INVALID_RETURN_VALUE, ERR_LOADER_CHAIN_INCOMPLETE, + ERR_UNKNOWN_BUILTIN_MODULE, ERR_WORKER_UNSERIALIZABLE_ERROR, } = require('internal/errors').codes; const { exitCodes: { kUnfinishedTopLevelAwait } } = internalBinding('errors'); @@ -653,13 +654,13 @@ class HooksProxy { (builtinName) => { if (StringPrototypeStartsWith(builtinName, 'node:')) { builtinName = StringPrototypeSlice(builtinName, 5); - } else if (BuiltinModule.canBeRequiredWithoutScheme(builtinName)) { - throw new ERR_INVALID_ARG_VALUE('builtinName', builtinName); + } else if (!BuiltinModule.canBeRequiredWithoutScheme(builtinName)) { + throw new ERR_UNKNOWN_BUILTIN_MODULE(builtinName); } if (BuiltinModule.canBeRequiredByUsers(builtinName)) { return require(builtinName); } - throw new ERR_INVALID_ARG_VALUE('builtinName', builtinName); + throw new ERR_UNKNOWN_BUILTIN_MODULE(builtinName); }, // Param port port, diff --git a/test/es-module/test-esm-loader-hooks.mjs b/test/es-module/test-esm-loader-hooks.mjs index 4e6f7156ee131a..7c83f65f71a807 100644 --- a/test/es-module/test-esm-loader-hooks.mjs +++ b/test/es-module/test-esm-loader-hooks.mjs @@ -1,6 +1,7 @@ import { spawnPromisified } from '../common/index.mjs'; import * as fixtures from '../common/fixtures.mjs'; import assert from 'node:assert'; +import os from 'node:os'; import { execPath } from 'node:process'; import { describe, it } from 'node:test'; @@ -422,60 +423,118 @@ describe('Loader hooks', { concurrency: true }, () => { }); }); - it('should handle globalPreload returning undefined', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - 'data:text/javascript,export function globalPreload(){}', - fixtures.path('empty.js'), - ]); + describe('globalPreload', () => { + it('should handle globalPreload returning undefined', async () => { + const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ + '--no-warnings', + '--experimental-loader', + 'data:text/javascript,export function globalPreload(){}', + fixtures.path('empty.js'), + ]); - assert.strictEqual(stderr, ''); - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }); + assert.strictEqual(stderr, ''); + assert.strictEqual(stdout, ''); + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + }); - it('should register globals set from globalPreload', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - 'data:text/javascript,export function globalPreload(){return "this.myGlobal=4"}', - '--print', 'myGlobal', - ]); + it('should handle loading node:test', async () => { + const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ + '--no-warnings', + '--experimental-loader', + 'data:text/javascript,export function globalPreload(){return `getBuiltin("node:test")()`}', + fixtures.path('empty.js'), + ]); - assert.strictEqual(stderr, ''); - assert.strictEqual(stdout.trim(), '4'); - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }); + assert.strictEqual(stderr, ''); + assert.match(stdout, /\n# pass 1\r?\n/); + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + }); - it('should log console.log calls returned from globalPreload', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - 'data:text/javascript,export function globalPreload(){return `console.log("Hello from globalPreload")`}', - fixtures.path('empty.js'), - ]); + it('should handle loading node:os with node: prefix', async () => { + const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ + '--no-warnings', + '--experimental-loader', + 'data:text/javascript,export function globalPreload(){return `console.log(getBuiltin("node:os").arch())`}', + fixtures.path('empty.js'), + ]); - assert.strictEqual(stderr, ''); - assert.strictEqual(stdout.trim(), 'Hello from globalPreload'); - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }); + assert.strictEqual(stderr, ''); + assert.strictEqual(stdout.trim(), os.arch()); + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + }); - it('should crash if globalPreload returns code that throws', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - 'data:text/javascript,export function globalPreload(){return `throw new Error("error from globalPreload")`}', - fixtures.path('empty.js'), - ]); + it('should handle loading node:os without node: prefix', async () => { + const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ + '--no-warnings', + '--experimental-loader', + 'data:text/javascript,export function globalPreload(){return `console.log(getBuiltin("os").arch())`}', + fixtures.path('empty.js'), + ]); - assert.match(stderr, /error from globalPreload/); - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 1); - assert.strictEqual(signal, null); + assert.strictEqual(stderr, ''); + assert.strictEqual(stdout.trim(), os.arch()); + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + }); + + it('should throw when loading node:test without node: prefix', async () => { + const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ + '--no-warnings', + '--experimental-loader', + 'data:text/javascript,export function globalPreload(){return `getBuiltin("test")()`}', + fixtures.path('empty.js'), + ]); + + assert.match(stderr, /ERR_UNKNOWN_BUILTIN_MODULE/); + assert.strictEqual(stdout, ''); + assert.strictEqual(code, 1); + assert.strictEqual(signal, null); + }); + + it('should register globals set from globalPreload', async () => { + const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ + '--no-warnings', + '--experimental-loader', + 'data:text/javascript,export function globalPreload(){return "this.myGlobal=4"}', + '--print', 'myGlobal', + ]); + + assert.strictEqual(stderr, ''); + assert.strictEqual(stdout.trim(), '4'); + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + }); + + it('should log console.log calls returned from globalPreload', async () => { + const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ + '--no-warnings', + '--experimental-loader', + 'data:text/javascript,export function globalPreload(){return `console.log("Hello from globalPreload")`}', + fixtures.path('empty.js'), + ]); + + assert.strictEqual(stderr, ''); + assert.strictEqual(stdout.trim(), 'Hello from globalPreload'); + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + }); + + it('should crash if globalPreload returns code that throws', async () => { + const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ + '--no-warnings', + '--experimental-loader', + 'data:text/javascript,export function globalPreload(){return `throw new Error("error from globalPreload")`}', + fixtures.path('empty.js'), + ]); + + assert.match(stderr, /error from globalPreload/); + assert.strictEqual(stdout, ''); + assert.strictEqual(code, 1); + assert.strictEqual(signal, null); + }); }); it('should be fine to call `process.removeAllListeners("beforeExit")` from the main thread', async () => {