Skip to content

Commit f5cdabe

Browse files
ExE-BossRaisinTen
andcommitted
module: add support for node:‑prefixed require(…) calls
Fixes: #36098 Co-authored-by: Darshan Sen <[email protected]>
1 parent 72f9c53 commit f5cdabe

File tree

7 files changed

+94
-9
lines changed

7 files changed

+94
-9
lines changed

doc/api/esm.md

Lines changed: 4 additions & 0 deletions
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

Lines changed: 26 additions & 4 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.
@@ -288,8 +294,13 @@ The core modules are defined within the Node.js source and are located in the
288294
`lib/` folder.
289295

290296
Core modules are always preferentially loaded if their identifier is
291-
passed to `require()`. For instance, `require('http')` will always
292-
return the built in HTTP module, even if there is a file by that name.
297+
passed to `require()`. For instance, `require('node:http')` will always
298+
return the built in HTTP module, even if there is a file or `require.cache`
299+
entry by that name.
300+
301+
```js
302+
const fs = require('node:fs/promises');
303+
```
293304

294305
## Cycles
295306

@@ -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
@@ -109,7 +109,8 @@ let hasLoadedAnyUserCJSModule = false;
109109
const {
110110
ERR_INVALID_ARG_VALUE,
111111
ERR_INVALID_MODULE_SPECIFIER,
112-
ERR_REQUIRE_ESM
112+
ERR_REQUIRE_ESM,
113+
ERR_UNKNOWN_BUILTIN_MODULE,
113114
} = require('internal/errors').codes;
114115
const { validateString } = require('internal/validators');
115116
const pendingDeprecation = getOptionValue('--pending-deprecation');
@@ -766,6 +767,17 @@ Module._load = function(request, parent, isMain) {
766767
}
767768

768769
const filename = Module._resolveFilename(request, parent, isMain);
770+
if (StringPrototypeStartsWith(filename, 'node:')) {
771+
// Slice 'node:' prefix
772+
const id = StringPrototypeSlice(filename, 5);
773+
774+
const module = loadNativeModule(id, request);
775+
if (!module?.canBeRequiredByUsers) {
776+
throw new ERR_UNKNOWN_BUILTIN_MODULE(filename);
777+
}
778+
779+
return module.exports;
780+
}
769781

770782
const cachedModule = Module._cache[filename];
771783
if (cachedModule !== undefined) {
@@ -837,7 +849,8 @@ Module._load = function(request, parent, isMain) {
837849
};
838850

839851
Module._resolveFilename = function(request, parent, isMain, options) {
840-
if (NativeModule.canBeRequiredByUsers(request)) {
852+
if (StringPrototypeStartsWith(request, 'node:') ||
853+
NativeModule.canBeRequiredByUsers(request)) {
841854
return request;
842855
}
843856

lib/internal/modules/esm/translators.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,9 @@ 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);
286-
if (!StringPrototypeStartsWith(url, 'node:') || !module) {
285+
const module = loadNativeModule(id, url);
286+
if (!StringPrototypeStartsWith(url, 'node:') ||
287+
!module?.canBeRequiredByUsers) {
287288
throw new ERR_UNKNOWN_BUILTIN_MODULE(url);
288289
}
289290
debug(`Loading BuiltinModule ${url}`);

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)