diff --git a/lib/internal/main/eval_string.js b/lib/internal/main/eval_string.js index ad068b935dfee0..2784204f6002e9 100644 --- a/lib/internal/main/eval_string.js +++ b/lib/internal/main/eval_string.js @@ -16,7 +16,7 @@ const { addBuiltinLibsToObject } = require('internal/modules/cjs/helpers'); const { getOptionValue } = require('internal/options'); prepareMainThreadExecution(); -addBuiltinLibsToObject(globalThis); +addBuiltinLibsToObject(globalThis, ''); markBootstrapComplete(); const source = getOptionValue('--eval'); diff --git a/lib/internal/modules/cjs/helpers.js b/lib/internal/modules/cjs/helpers.js index 1e85362a9449d9..bba572b32cb65d 100644 --- a/lib/internal/modules/cjs/helpers.js +++ b/lib/internal/modules/cjs/helpers.js @@ -131,9 +131,16 @@ function stripBOM(content) { return content; } -function addBuiltinLibsToObject(object) { +function addBuiltinLibsToObject(object, dummyModuleName) { // Make built-in modules available directly (loaded lazily). - const { builtinModules } = require('internal/modules/cjs/loader').Module; + const Module = require('internal/modules/cjs/loader').Module; + const { builtinModules } = Module; + + // To require built-in modules in user-land and ignore modules whose + // `canBeRequiredByUsers` is false. So we create a dummy module object and not + // use `require()` directly. + const dummyModule = new Module(dummyModuleName); + ArrayPrototypeForEach(builtinModules, (name) => { // Neither add underscored modules, nor ones that contain slashes (e.g., // 'fs/promises') or ones that are already defined. @@ -157,7 +164,7 @@ function addBuiltinLibsToObject(object) { ObjectDefineProperty(object, name, { get: () => { - const lib = require(name); + const lib = dummyModule.require(name); // Disable the current getter/setter and set up a new // non-enumerable property. diff --git a/lib/repl.js b/lib/repl.js index 19011841519351..7ebfd91fd28999 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -1098,7 +1098,7 @@ REPLServer.prototype.createContext = function() { value: makeRequireFunction(replModule) }); - addBuiltinLibsToObject(context); + addBuiltinLibsToObject(context, ''); return context; }; diff --git a/test/parallel/test-repl-built-in-modules.js b/test/parallel/test-repl-built-in-modules.js new file mode 100644 index 00000000000000..3877b506b22c34 --- /dev/null +++ b/test/parallel/test-repl-built-in-modules.js @@ -0,0 +1,57 @@ +'use strict'; + +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const spawn = require('child_process').spawn; +const path = require('path'); + +tmpdir.refresh(); + +const requirePath = JSON.stringify(path.join(tmpdir.path, 'non-existent.json')); + +function run(wasiPreview) { + let resolve; + const promise = new Promise((_resolve) => { + resolve = _resolve; + }); + + let out = ''; + + // Use -i to force node into interactive mode, despite stdout not being a TTY + const args = ['-i']; + if (wasiPreview) args.push('--experimental-wasi-unstable-preview1'); + const child = spawn(process.execPath, args); + + const input = `require('events'); +require('wasi');`; + let stdout = ''; + let stderr = ''; + + child.stdout.setEncoding('utf8'); + child.stdout.on('data', (c) => stdout += c); + child.stderr.setEncoding('utf8'); + child.stderr.on('data', (c) => stderr += c); + + child.stdin.end(input); + + child.on('exit', () => { + resolve({ stdout, stderr }); + }); + + return promise; +} + +(async function() { + const ret1 = await run(false); + const ret2 = await run(true); + + assert(/\[Function: EventEmitter\] {/.test(ret1.stdout)); + assert(/Uncaught Error: Cannot find module 'wasi'[\w\W]+- \n/.test( + ret1.stdout)); + + assert(/\[Function: EventEmitter\] {/.test(ret2.stdout)); + assert(!/Uncaught Error: Cannot find module 'wasi'[\w\W]+- \n/.test( + ret2.stdout)); + assert(/{ WASI: \[class WASI\] }/.test(ret2.stdout)); +})();