Skip to content

Commit 083a92d

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

File tree

7 files changed

+86
-6
lines changed

7 files changed

+86
-6
lines changed

doc/api/esm.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,16 +204,24 @@ 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
210214
modules. This URL scheme allows for builtin modules to be referenced by valid
211215
absolute URL strings.
212216

213-
```js
217+
```js esm
214218
import fs from 'node:fs/promises';
215219
```
216220

221+
```js cjs
222+
const fs = require('node:fs/promises');
223+
```
224+
217225
## Builtin modules
218226

219227
[Core modules][] provide named exports of their public API. A

doc/api/modules.md

Lines changed: 6 additions & 0 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.

lib/internal/modules/cjs/helpers.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,14 @@ let debug = require('internal/util/debuglog').debuglog('module', (fn) => {
3232
// TODO: Use this set when resolving pkg#exports conditions in loader.js.
3333
const cjsConditions = new SafeSet(['require', 'node', ...userConditions]);
3434

35-
function loadNativeModule(filename, request) {
35+
function loadNativeModule(filename, request, skipCompile = false) {
3636
const mod = NativeModule.map.get(filename);
3737
if (mod) {
3838
debug('load native module %s', request);
39-
mod.compileForPublicLoader();
39+
if (skipCompile) {
40+
// compileForPublicLoader() throws if mod.canBeRequiredByUsers is false:
41+
mod.compileForPublicLoader();
42+
}
4043
return mod;
4144
}
4245
}

lib/internal/modules/cjs/loader.js

Lines changed: 16 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,18 @@ 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+
// compileForPublicLoader() throws if mod.canBeRequiredByUsers is false:
775+
const module = loadNativeModule(id, request, true);
776+
if (!module?.canBeRequiredByUsers) {
777+
throw new ERR_UNKNOWN_BUILTIN_MODULE(filename);
778+
}
779+
780+
return module.compileForPublicLoader();
781+
}
769782

770783
const cachedModule = Module._cache[filename];
771784
if (cachedModule !== undefined) {
@@ -837,7 +850,8 @@ Module._load = function(request, parent, isMain) {
837850
};
838851

839852
Module._resolveFilename = function(request, parent, isMain, options) {
840-
if (NativeModule.canBeRequiredByUsers(request)) {
853+
if (StringPrototypeStartsWith(request, 'node:') ||
854+
NativeModule.canBeRequiredByUsers(request)) {
841855
return request;
842856
}
843857

lib/internal/modules/esm/translators.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,10 +282,15 @@ translators.set('builtin', async function builtinStrategy(url) {
282282
debug(`Translating BuiltinModule ${url}`);
283283
// Slice 'node:' scheme
284284
const id = StringPrototypeSlice(url, 5);
285+
286+
// compileForPublicLoader() throws if mod.canBeRequiredByUsers is false:
285287
const module = loadNativeModule(id, url, true);
286-
if (!StringPrototypeStartsWith(url, 'node:') || !module) {
288+
if (!StringPrototypeStartsWith(url, 'node:') ||
289+
!module?.canBeRequiredByUsers) {
287290
throw new ERR_UNKNOWN_BUILTIN_MODULE(url);
288291
}
292+
293+
module.compileForPublicLoader();
289294
debug(`Loading BuiltinModule ${url}`);
290295
return module.getESMFacade();
291296
});

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)