Skip to content

Commit 069b5df

Browse files
ExE-Bossaduh95
authored andcommitted
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]> PR-URL: #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 b079960 commit 069b5df

File tree

7 files changed

+90
-6
lines changed

7 files changed

+90
-6
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: 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+
constassert=require('assert');
661+
constrealFs=require('fs');
662+
663+
constfakeFs={};
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
functionloadNativeModule(filename,request){
3636
constmod=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
returnmod;
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 @@ 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
constpendingDeprecation=getOptionValue('--pending-deprecation');
@@ -770,6 +771,17 @@ Module._load = function(request, parent, isMain){
770771
}
771772

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

774786
constcachedModule=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
returnrequest;
846859
}
847860

‎lib/internal/modules/esm/translators.js‎

Lines changed: 1 addition & 1 deletion
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
constid=StringPrototypeSlice(url,5);
285-
constmodule=loadNativeModule(id,url,true);
285+
constmodule=loadNativeModule(id,url);
286286
if(!StringPrototypeStartsWith(url,'node:')||!module){
287287
thrownewERR_UNKNOWN_BUILTIN_MODULE(url);
288288
}

‎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+
constassert=require('assert');
5+
constfs=require('fs');
6+
7+
consterrUnknownBuiltinModuleRE=/^Nosuchbuilt-inmodule:/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+
constfakeModule={};
35+
36+
require.cache.fs={exports: fakeModule};
37+
38+
assert.strictEqual(require('fs'),fakeModule);
39+
assert.strictEqual(require('node:fs'),fs);
40+
41+
deleterequire.cache.fs;
42+
}

0 commit comments

Comments
(0)