Repository: nodejs/node
Pull request: process: add process.features.require_module

For detecting whether `require(esm)` is supported without triggering the experimental warning.

<!--
Before submitting a pull request, please read:

- the CONTRIBUTING guide at https://github.com/nodejs/node/blob/HEAD/CONTRIBUTING.md
- the commit message formatting guidelines at
  https://github.com/nodejs/node/blob/HEAD/doc/contributing/pull-requests.md#commit-message-guidelines

For code changes:
1. Include tests for any bug fixes or new features.
2. Update documentation if relevant.
3. Ensure that `make -j4 test` (UNIX), or `vcbuild test` (Windows) passes.

If you believe this PR should be highlighted in the Node.js CHANGELOG
please add the `notable-change` label.

Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
    have the right to submit it under the open source license
    indicated in the file; or

(b) The contribution is based upon previous work that, to the best
    of my knowledge, is covered under an appropriate open source
    license and I have the right under that license to submit that
    work with modifications, whether created in whole or in part
    by me, under the same open source license (unless I am
    permitted to submit under a different license), as indicated
    in the file; or

(c) The contribution was provided directly to me by some other
    person who certified (a), (b) or (c) and I have not modified
    it.

(d) I understand and agree that this project and the contribution
    are public and that a record of the contribution (including all
    personal information I submit with it, including my sign-off) is
    maintained indefinitely and may be redistributed consistent with
    this project or the open source license(s) involved.
-->


Changed files:
filename: doc/api/modules.md
patch: @@ -318,6 +318,8 @@ experimental and can be disabled using `--no-experimental-require-module`.
 When `require()` actually encounters an ES module for the
 first time in the process, it will emit an experimental warning. The
 warning is expected to be removed when this feature stablizes.
+This feature can be detected by checking if
+[`process.features.require_module`][] is `true`.
 
 ## All together
 
@@ -1280,6 +1282,7 @@ This section was moved to
 [`node:test`]: test.md
 [`package.json`]: packages.md#nodejs-packagejson-field-definitions
 [`path.dirname()`]: path.md#pathdirnamepath
+[`process.features.require_module`]: process.md#processfeaturesrequire_module
 [`require.main`]: #requiremain
 [exports shortcut]: #exports-shortcut
 [module resolution]: #all-together

filename: doc/api/process.md
patch: @@ -1936,6 +1936,17 @@ added: v0.5.3
 
 A boolean value that is `true` if the current Node.js build includes support for IPv6.
 
+## `process.features.require_module`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* {boolean}
+
+A boolean value that is `true` if the current Node.js build supports
+[loading ECMAScript modules using `require()`][].
+
 ## `process.features.tls`
 
 <!-- YAML
@@ -4431,6 +4442,7 @@ cases:
 [built-in modules with mandatory `node:` prefix]: modules.md#built-in-modules-with-mandatory-node-prefix
 [debugger]: debugger.md
 [deprecation code]: deprecations.md
+[loading ECMAScript modules using `require()`]: modules.md#loading-ecmascript-modules-using-require
 [note on process I/O]: #a-note-on-process-io
 [process.cpuUsage]: #processcpuusagepreviousvalue
 [process_emit_warning]: #processemitwarningwarning-type-code-ctor

filename: lib/internal/bootstrap/node.js
patch: @@ -279,6 +279,9 @@ const features = {
   get cached_builtins() {
     return binding.hasCachedBuiltins();
   },
+  get require_module() {
+    return getOptionValue('--experimental-require-module');
+  },
 };
 
 ObjectDefineProperty(process, 'features', {

filename: test/es-module/test-require-module-feature-detect.js
patch: @@ -0,0 +1,37 @@
+'use strict';
+
+// This tests that process.features.require_module can be used to feature-detect
+// require(esm) without triggering a warning.
+
+require('../common');
+const { spawnSyncAndAssert } = require('../common/child_process');
+
+spawnSyncAndAssert(process.execPath, [
+  '--experimental-require-module',
+  '-p',
+  'process.features.require_module',
+], {
+  trim: true,
+  stdout: 'true',
+  stderr: '',  // Should not emit warnings.
+});
+
+// It is now enabled by default.
+spawnSyncAndAssert(process.execPath, [
+  '-p',
+  'process.features.require_module',
+], {
+  trim: true,
+  stdout: 'true',
+  stderr: '',  // Should not emit warnings.
+});
+
+spawnSyncAndAssert(process.execPath, [
+  '--no-experimental-require-module',
+  '-p',
+  'process.features.require_module',
+], {
+  trim: true,
+  stdout: 'false',
+  stderr: '',  // Should not emit warnings.
+});

filename: test/parallel/test-process-features.js
patch: @@ -14,6 +14,7 @@ const expectedKeys = new Map([
   ['tls_ocsp', ['boolean']],
   ['tls', ['boolean']],
   ['cached_builtins', ['boolean']],
+  ['require_module', ['boolean']],
   ['typescript', ['boolean', 'string']],
 ]);