Skip to content

Commit 1ed98e8

Browse files
ExE-Bosstargos
authored andcommitted
module: add support for node:‑prefixed require(…) calls
Fixes: nodejs#36098 Co-authored-by: Antoine du Hamel <[email protected]> Co-authored-by: Guy Bedford <[email protected]> Co-authored-by: Darshan Sen <[email protected]> PR-URL: nodejs#37246 Reviewed-By: Bradley Farias <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Darshan Sen <[email protected]> Reviewed-By: Matteo Collina <[email protected]>
1 parent 8444d1d commit 1ed98e8

File tree

7 files changed

+93
-7
lines changed

7 files changed

+93
-7
lines changed

doc/api/esm.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,13 @@ import _ from 'data:application/json,"world!"';
199199
#### `node:` Imports
200200

201201
<!-- YAML
202-
added: v14.13.1
202+
added:
203+
- v14.13.1
204+
- v12.20.0
205+
changes:
206+
- version: REPLACEME
207+
pr-url: https://github.com/nodejs/node/pull/37246
208+
description: Added `node:` import support to `require(...)`.
203209
-->
204210

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

doc/api/modules.md

Lines changed: 24 additions & 2 deletions
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

Lines changed: 2 additions & 1 deletion
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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ const {
110110
ERR_INVALID_ARG_VALUE,
111111
ERR_INVALID_OPT_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');
@@ -743,6 +744,17 @@ Module._load = function(request, parent, isMain) {
743744
}
744745

745746
const filename = Module._resolveFilename(request, parent, isMain);
747+
if (StringPrototypeStartsWith(filename, 'node:')) {
748+
// Slice 'node:' prefix
749+
const id = StringPrototypeSlice(filename, 5);
750+
751+
const module = loadNativeModule(id, request);
752+
if (!module?.canBeRequiredByUsers) {
753+
throw new ERR_UNKNOWN_BUILTIN_MODULE(filename);
754+
}
755+
756+
return module.exports;
757+
}
746758

747759
const cachedModule = Module._cache[filename];
748760
if (cachedModule !== undefined) {
@@ -802,7 +814,8 @@ Module._load = function(request, parent, isMain) {
802814
};
803815

804816
Module._resolveFilename = function(request, parent, isMain, options) {
805-
if (NativeModule.canBeRequiredByUsers(request)) {
817+
if (StringPrototypeStartsWith(request, 'node:') ||
818+
NativeModule.canBeRequiredByUsers(request)) {
806819
return request;
807820
}
808821

lib/internal/modules/esm/translators.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ translators.set('builtin', async function builtinStrategy(url) {
280280
debug(`Translating BuiltinModule ${url}`);
281281
// Slice 'node:' scheme
282282
const id = StringPrototypeSlice(url, 5);
283-
const module = loadNativeModule(id, url, true);
283+
const module = loadNativeModule(id, url);
284284
if (!StringPrototypeStartsWith(url, 'node:') || !module) {
285285
throw new ERR_UNKNOWN_BUILTIN_MODULE(url);
286286
}

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

Lines changed: 2 additions & 0 deletions
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'),
Lines changed: 42 additions & 0 deletions
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)