module: add support for node:‑prefixed require(…) calls

Fixes: https://github.com/nodejs/node/issues/36098

Co-authored-by: Antoine du Hamel <duhamelantoine1995@gmail.com>
Co-authored-by: Guy Bedford <guybedford@gmail.com>
Co-authored-by: Darshan Sen <raisinten@gmail.com>

PR-URL: https://github.com/nodejs/node/pull/37246
Reviewed-By: Bradley Farias <bradley.meck@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Darshan Sen <raisinten@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
authored and Antoine du Hamel committed 2021-03-19 12:34:58 +01:00
1 parent b079960b18
commit 069b5df4f6
7 files changed
+90 -6

No files matched your search

+4
View File
@@ -204,6 +204,10 @@ import _ from 'data:application/json,"world!"';
added:
- v14.13.1
- v12.20.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/37246
description: Added `node:` import support to `require(...)`.
-->
`node:` URLs are supported as an alternative means to load Node.js builtin
+24 -2
View File
@@ -280,6 +280,12 @@ irrespective of whether or not `./foo` and `./FOO` are the same file.
## Core modules
<!--type=misc-->
<!-- YAML
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/37246
description: Added `node:` import support to `require(...)`.
-->
Node.js has several modules compiled into the binary. These modules are
described in greater detail elsewhere in this documentation.
@@ -291,6 +297,11 @@ Core modules are always preferentially loaded if their identifier is
passed to `require()`. For instance, `require('http')` will always
return the built in HTTP module, even if there is a file by that name.
Core modules can also be identified using the `node:` prefix, in which case
it bypasses the `require` cache. For instance, `require('node:http')` will
always return the built in HTTP module, even if there is `require.cache` entry
by that name.
## Cycles
<!--type=misc-->
@@ -642,8 +653,19 @@ error.
Adding or replacing entries is also possible. This cache is checked before
native modules and if a name matching a native module is added to the cache,
no require call is
going to receive the native module anymore. Use with care!
only `node:`-prefixed require calls are going to receive the native module.
Use with care!
```js
const assert = require('assert');
const realFs = require('fs');
const fakeFs = {};
require.cache.fs = { exports: fakeFs };
assert.strictEqual(require('fs'), fakeFs);
assert.strictEqual(require('node:fs'), realFs);
```
#### `require.extensions`
<!-- YAML
+2 -1
View File
@@ -34,8 +34,9 @@ const cjsConditions = new SafeSet(['require', 'node', ...userConditions]);
function loadNativeModule(filename, request) {
const mod = NativeModule.map.get(filename);
if (mod) {
if (mod?.canBeRequiredByUsers) {
debug('load native module %s', request);
// compileForPublicLoader() throws if mod.canBeRequiredByUsers is false:
mod.compileForPublicLoader();
return mod;
}
+15 -2
View File
@@ -110,7 +110,8 @@ let hasLoadedAnyUserCJSModule = false;
const {
ERR_INVALID_ARG_VALUE,
ERR_INVALID_MODULE_SPECIFIER,
ERR_REQUIRE_ESM
ERR_REQUIRE_ESM,
ERR_UNKNOWN_BUILTIN_MODULE,
} = require('internal/errors').codes;
const { validateString } = require('internal/validators');
const pendingDeprecation = getOptionValue('--pending-deprecation');
@@ -770,6 +771,17 @@ Module._load = function(request, parent, isMain) {
}
const filename = Module._resolveFilename(request, parent, isMain);
if (StringPrototypeStartsWith(filename, 'node:')) {
// Slice 'node:' prefix
const id = StringPrototypeSlice(filename, 5);
const module = loadNativeModule(id, request);
if (!module?.canBeRequiredByUsers) {
throw new ERR_UNKNOWN_BUILTIN_MODULE(filename);
}
return module.exports;
}
const cachedModule = Module._cache[filename];
if (cachedModule !== undefined) {
@@ -841,7 +853,8 @@ Module._load = function(request, parent, isMain) {
};
Module._resolveFilename = function(request, parent, isMain, options) {
if (NativeModule.canBeRequiredByUsers(request)) {
if (StringPrototypeStartsWith(request, 'node:') ||
NativeModule.canBeRequiredByUsers(request)) {
return request;
}
+1 -1
View File
@@ -282,7 +282,7 @@ translators.set('builtin', async function builtinStrategy(url) {
debug(`Translating BuiltinModule ${url}`);
// Slice 'node:' scheme
const id = StringPrototypeSlice(url, 5);
const module = loadNativeModule(id, url, true);
const module = loadNativeModule(id, url);
if (!StringPrototypeStartsWith(url, 'node:') || !module) {
throw new ERR_UNKNOWN_BUILTIN_MODULE(url);
}
@@ -51,6 +51,8 @@ function expectFsNamespace(result) {
expectModuleError(import('node:unknown'),
'ERR_UNKNOWN_BUILTIN_MODULE');
expectModuleError(import('node:internal/test/binding'),
'ERR_UNKNOWN_BUILTIN_MODULE');
expectModuleError(import('./not-an-existing-module.mjs'),
'ERR_MODULE_NOT_FOUND');
expectModuleError(import('http://example.com/foo.js'),
+42
View File
@@ -0,0 +1,42 @@
'use strict';
require('../common');
const assert = require('assert');
const fs = require('fs');
const errUnknownBuiltinModuleRE = /^No such built-in module: /u;
// For direct use of require expressions inside of CJS modules,
// all kinds of specifiers should work without issue.
{
assert.strictEqual(require('fs'), fs);
assert.strictEqual(require('node:fs'), fs);
assert.throws(
() => require('node:unknown'),
{
code: 'ERR_UNKNOWN_BUILTIN_MODULE',
message: errUnknownBuiltinModuleRE,
},
);
assert.throws(
() => require('node:internal/test/binding'),
{
code: 'ERR_UNKNOWN_BUILTIN_MODULE',
message: errUnknownBuiltinModuleRE,
},
);
}
// `node:`-prefixed `require(...)` calls bypass the require cache:
{
const fakeModule = {};
require.cache.fs = { exports: fakeModule };
assert.strictEqual(require('fs'), fakeModule);
assert.strictEqual(require('node:fs'), fs);
delete require.cache.fs;
}