-
-
Notifications
You must be signed in to change notification settings - Fork 33.9k
Description
Version
v20.9.0
Platform
Linux abulia 6.5.0-2-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.5.6-1 (2023-10-07) x86_64 GNU/Linux
Subsystem
No response
What steps will reproduce the bug?
-
Create the following files:
register-hooks.mjs
import { register } from 'node:module'; register( './hooks.mjs', import.meta.url );hooks.mjs
import { readFileSync } from 'node:fs'; export async function load(url, context, nextLoad) { if ( context.format !== 'commonjs' && context.format !== 'module' ) { return nextLoad(url, context); } // This might in practice be a check for `node_modules`. if ( url.endsWith( '/bar.cjs' ) ) { return nextLoad(url, context); } // Do some transformation of the source here. const source = readFileSync(new URL(url)); return { format: context.format, source, shortCircuit: true, }; }foo.cjs
const bar = require( './bar.cjs' ); bar.hello();bar.cjs
function hello() { console.log( "hello, world" ); } module.exports = { hello }; -
Run
node --import ./register-hooks.mjs foo.cjs
How often does it reproduce? Is there a required condition?
Always.
What is the expected behavior? Why is that the expected behavior?
Script runs, printing "hello, world".
Because that's what this very simple code does. 🤷
What do you see instead?
node:internal/errors:497
ErrorCaptureStackTrace(err);
^
TypeError [ERR_INVALID_RETURN_PROPERTY_VALUE]: Expected array buffer, or typed array to be returned for the "source" from the "transformSource" function but got null.
at new NodeError (node:internal/errors:406:5)
at assertBufferSource (node:internal/modules/esm/translators:84:9)
at stringify (node:internal/modules/esm/translators:94:3)
at createCJSModuleWrap (node:internal/modules/esm/translators:219:12)
at ModuleLoader.<anonymous> (node:internal/modules/esm/translators:267:10)
at callTranslator (node:internal/modules/esm/loader:273:14)
at ModuleLoader.<anonymous> (node:internal/modules/esm/loader:277:24)
at new ModuleJob (node:internal/modules/esm/module_job:65:26)
at #createModuleJob (node:internal/modules/esm/loader:290:17)
at ModuleLoader.getJobFromResolveResult (node:internal/modules/esm/loader:248:34) {
code: 'ERR_INVALID_RETURN_PROPERTY_VALUE'
}
Node.js v20.9.0
Additional information
This seems to have been introduced in v20.6.0, as v20.5.1 is not affected. Likely it's part of the enhancements to the load hook added in that version.
What seems to be happening is that once one commonjs file returns a non-null source, any sub-requires of that file will fail if null is returned for source despite that being the default behavior for the passed-in nextLoad function.
I ran into this while trying to figure out why playwright started giving me this error when I tried to update from Node 18 to Node 20. The loader can be found here, and the bug was being triggered by a mjs config file that imports a cjs file that requires something out of node_modules. They've also got some other stuff going on in there, if I hack that loader to always fill in source then it winds up hanging eventually at this line (it seems that sends a message on the MessagePort that the other side never reacts to?).
Considering the comment I see in the doc that "This behavior for nullish source is temporary — in the future, nullish source will not be supported", I suppose you might decide this isn't a bug, saying that any loader that "opts in" to this behavior should carry it all the way through.