diff --git a/lib/internal/bootstrap/realm.js b/lib/internal/bootstrap/realm.js index f49f0814bbc687..6a9f22c2e51683 100644 --- a/lib/internal/bootstrap/realm.js +++ b/lib/internal/bootstrap/realm.js @@ -310,21 +310,46 @@ class BuiltinModule { } static isBuiltin(id) { - return BuiltinModule.canBeRequiredWithoutScheme(id) || ( - typeof id === 'string' && - StringPrototypeStartsWith(id, 'node:') && + if (typeof id !== 'string') { + return false; + } + + // Modules starting with underscore (e.g. `_http_agent`) can be required + // by users but those are discouraged and should not be exposed as proper + // public ones, so if `id` starts with `_` (or `node:_`) return `false` right away + if (StringPrototypeStartsWith(id, '_') || StringPrototypeStartsWith(id, 'node:_')) { + return false; + } + + // Check if `id` matches a builtin module without the `node:` prefix + if (BuiltinModule.canBeRequiredWithoutScheme(id)) { + return true; + } + + // Check if `id` matches a builtin module with the `node:` prefix + if ( + StringPrototypeStartsWith(id, 'node:') && BuiltinModule.canBeRequiredByUsers(StringPrototypeSlice(id, 5)) - ); + ) { + return true; + } + + return false; } static getSchemeOnlyModuleNames() { return ArrayFrom(schemelessBlockList); } - static getAllBuiltinModuleIds() { + static getAllPublicBuiltinModuleIds() { const allBuiltins = ArrayFrom(canBeRequiredByUsersWithoutSchemeList); ArrayPrototypePushApply(allBuiltins, ArrayFrom(schemelessBlockList, (x) => `node:${x}`)); - return allBuiltins; + return ArrayPrototypeFilter(allBuiltins, (id) => { + // Modules starting with an underscore (e.g. `_http_agent`) can be + // required by users because of historical reasons, but they are not + // proper documented public modules, so they need to be filtered out + return id[0] !== '_'; + }); } // Used by user-land module loaders to compile and load builtins. diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index be4bedeb12a535..e135b1f798fa5e 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -439,7 +439,7 @@ Module.isBuiltin = BuiltinModule.isBuiltin; function initializeCJS() { // This need to be done at runtime in case --expose-internals is set. - let modules = Module.builtinModules = BuiltinModule.getAllBuiltinModuleIds(); + let modules = Module.builtinModules = BuiltinModule.getAllPublicBuiltinModuleIds(); if (!getOptionValue('--experimental-quic')) { modules = modules.filter((i) => i !== 'node:quic'); } diff --git a/test/parallel/test-module-builtin.js b/test/parallel/test-module-builtin.js index 3897d71ecf4405..c32fd535c1a516 100644 --- a/test/parallel/test-module-builtin.js +++ b/test/parallel/test-module-builtin.js @@ -12,3 +12,10 @@ assert.deepStrictEqual( builtinModules.filter((mod) => mod.startsWith('internal/')), [] ); + +// Does not include modules starting with an underscore +// (these are exposed to users but not proper public documented modules) +assert.deepStrictEqual( + builtinModules.filter((mod) => mod.startsWith('_')), + [] +); diff --git a/test/parallel/test-module-isBuiltin.js b/test/parallel/test-module-isBuiltin.js index a7815a8dfc1c18..af45f881c74c7f 100644 --- a/test/parallel/test-module-isBuiltin.js +++ b/test/parallel/test-module-isBuiltin.js @@ -14,3 +14,28 @@ assert(!isBuiltin('internal/errors')); assert(!isBuiltin('test')); assert(!isBuiltin('')); assert(!isBuiltin(undefined)); + +const underscoreModules = [ + '_http_agent', + '_http_client', + '_http_common', + '_http_incoming', + '_http_outgoing', + '_http_server', + '_stream_duplex', + '_stream_passthrough', + '_stream_readable', + '_stream_transform', + '_stream_wrap', + '_stream_writable', + '_tls_common', + '_tls_wrap', +]; + +// Does not include modules starting with underscore +// (these can be required for historical reasons but +// are not proper documented public modules) +for (const module of underscoreModules) { + assert(!isBuiltin(module)); + assert(!isBuiltin(`node:${module}`)); +}