Skip to content

Commit 1d8c8b7

Browse files
ExE-Bossaduh95guybedfordRaisinTen
committed
module: add support for node:‑prefixed require(…) calls
Fixes: #36098 Co-authored-by: Antoine du Hamel <[email protected]> Co-authored-by: Guy Bedford <[email protected]> Co-authored-by: Darshan Sen <[email protected]>
1 parent 49342fe commit 1d8c8b7

File tree

7 files changed

+90
-6
lines changed

7 files changed

+90
-6
lines changed

doc/api/esm.md

+4
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,10 @@ import _ from 'data:application/json,"world!"';
204204
added:
205205
- v14.13.1
206206
- v12.20.0
207+
changes:
208+
- version: REPLACEME
209+
pr-url: https://github.com/nodejs/node/pull/37246
210+
description: Added `node:` import support to `require(...)`.
207211
-->
208212

209213
`node:` URLs are supported as an alternative means to load Node.js builtin

doc/api/modules.md

+24-2
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,12 @@ irrespective of whether or not `./foo` and `./FOO` are the same file.
280280
## Core modules
281281

282282
<!--type=misc-->
283+
<!-- YAML
284+
changes:
285+
- version: REPLACEME
286+
pr-url: https://github.com/nodejs/node/pull/37246
287+
description: Added `node:` import support to `require(...)`.
288+
-->
283289

284290
Node.js has several modules compiled into the binary. These modules are
285291
described in greater detail elsewhere in this documentation.
@@ -291,6 +297,11 @@ Core modules are always preferentially loaded if their identifier is
291297
passed to `require()`. For instance, `require('http')` will always
292298
return the built in HTTP module, even if there is a file by that name.
293299

300+
Core modules can also be identified using the `node:` prefix, in which case
301+
it bypasses the `require` cache. For instance, `require('node:http')` will
302+
always return the built in HTTP module, even if there is `require.cache` entry
303+
by that name.
304+
294305
## Cycles
295306

296307
<!--type=misc-->
@@ -642,8 +653,19 @@ error.
642653

643654
Adding or replacing entries is also possible. This cache is checked before
644655
native modules and if a name matching a native module is added to the cache,
645-
no require call is
646-
going to receive the native module anymore. Use with care!
656+
only `node:`-prefixed require calls are going to receive the native module.
657+
Use with care!
658+
659+
```js
660+
const assert = require('assert');
661+
const realFs = require('fs');
662+
663+
const fakeFs = {};
664+
require.cache.fs = { exports: fakeFs };
665+
666+
assert.strictEqual(require('fs'), fakeFs);
667+
assert.strictEqual(require('node:fs'), realFs);
668+
```
647669

648670
#### `require.extensions`
649671
<!-- YAML

lib/internal/modules/cjs/helpers.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ const cjsConditions = new SafeSet(['require', 'node', ...userConditions]);
3434

3535
function loadNativeModule(filename, request) {
3636
const mod = NativeModule.map.get(filename);
37-
if (mod) {
37+
if (mod?.canBeRequiredByUsers) {
3838
debug('load native module %s', request);
39+
// compileForPublicLoader() throws if mod.canBeRequiredByUsers is false:
3940
mod.compileForPublicLoader();
4041
return mod;
4142
}

lib/internal/modules/cjs/loader.js

+15-2
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ let hasLoadedAnyUserCJSModule = false;
110110
const {
111111
ERR_INVALID_ARG_VALUE,
112112
ERR_INVALID_MODULE_SPECIFIER,
113-
ERR_REQUIRE_ESM
113+
ERR_REQUIRE_ESM,
114+
ERR_UNKNOWN_BUILTIN_MODULE,
114115
} = require('internal/errors').codes;
115116
const { validateString } = require('internal/validators');
116117
const pendingDeprecation = getOptionValue('--pending-deprecation');
@@ -770,6 +771,17 @@ Module._load = function(request, parent, isMain) {
770771
}
771772

772773
const filename = Module._resolveFilename(request, parent, isMain);
774+
if (StringPrototypeStartsWith(filename, 'node:')) {
775+
// Slice 'node:' prefix
776+
const id = StringPrototypeSlice(filename, 5);
777+
778+
const module = loadNativeModule(id, request);
779+
if (!module?.canBeRequiredByUsers) {
780+
throw new ERR_UNKNOWN_BUILTIN_MODULE(filename);
781+
}
782+
783+
return module.exports;
784+
}
773785

774786
const cachedModule = Module._cache[filename];
775787
if (cachedModule !== undefined) {
@@ -841,7 +853,8 @@ Module._load = function(request, parent, isMain) {
841853
};
842854

843855
Module._resolveFilename = function(request, parent, isMain, options) {
844-
if (NativeModule.canBeRequiredByUsers(request)) {
856+
if (StringPrototypeStartsWith(request, 'node:') ||
857+
NativeModule.canBeRequiredByUsers(request)) {
845858
return request;
846859
}
847860

lib/internal/modules/esm/translators.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ translators.set('builtin', async function builtinStrategy(url) {
282282
debug(`Translating BuiltinModule ${url}`);
283283
// Slice 'node:' scheme
284284
const id = StringPrototypeSlice(url, 5);
285-
const module = loadNativeModule(id, url, true);
285+
const module = loadNativeModule(id, url);
286286
if (!StringPrototypeStartsWith(url, 'node:') || !module) {
287287
throw new ERR_UNKNOWN_BUILTIN_MODULE(url);
288288
}

test/es-module/test-esm-dynamic-import.js

+2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ function expectFsNamespace(result) {
5151

5252
expectModuleError(import('node:unknown'),
5353
'ERR_UNKNOWN_BUILTIN_MODULE');
54+
expectModuleError(import('node:internal/test/binding'),
55+
'ERR_UNKNOWN_BUILTIN_MODULE');
5456
expectModuleError(import('./not-an-existing-module.mjs'),
5557
'ERR_MODULE_NOT_FOUND');
5658
expectModuleError(import('http://example.com/foo.js'),
+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
'use strict';
2+
3+
require('../common');
4+
const assert = require('assert');
5+
const fs = require('fs');
6+
7+
const errUnknownBuiltinModuleRE = /^No such built-in module: /u;
8+
9+
// For direct use of require expressions inside of CJS modules,
10+
// all kinds of specifiers should work without issue.
11+
{
12+
assert.strictEqual(require('fs'), fs);
13+
assert.strictEqual(require('node:fs'), fs);
14+
15+
assert.throws(
16+
() => require('node:unknown'),
17+
{
18+
code: 'ERR_UNKNOWN_BUILTIN_MODULE',
19+
message: errUnknownBuiltinModuleRE,
20+
},
21+
);
22+
23+
assert.throws(
24+
() => require('node:internal/test/binding'),
25+
{
26+
code: 'ERR_UNKNOWN_BUILTIN_MODULE',
27+
message: errUnknownBuiltinModuleRE,
28+
},
29+
);
30+
}
31+
32+
// `node:`-prefixed `require(...)` calls bypass the require cache:
33+
{
34+
const fakeModule = {};
35+
36+
require.cache.fs = { exports: fakeModule };
37+
38+
assert.strictEqual(require('fs'), fakeModule);
39+
assert.strictEqual(require('node:fs'), fs);
40+
41+
delete require.cache.fs;
42+
}

0 commit comments

Comments
 (0)