From 1ab25927d75fb3e8f9f52c88aaef7fc0715491df Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Tue, 5 Mar 2024 17:19:35 +0100 Subject: [PATCH] module: support require()ing synchronous ESM graphs This patch adds `require()` support for synchronous ESM graphs under the flag --experimental-require-module. This is based on the the following design aspect of ESM: - The resolution can be synchronous (up to the host) - The evaluation of a synchronous graph (without top-level await) is also synchronous, and, by the time the module graph is instantiated (before evaluation starts), this is is already known. When the module being require()ed has .mjs extension or there are other explicit indicators that it's an ES module, we load it as an ES module. If the graph is synchronous, we return the module namespace as the exports. If the graph contains top-level await, we throw an error before evaluating the module. If an additional flag --print-pending-tla is passed, we proceeds to evaluation but do not run the microtasks, only to find out where the TLA is and print their location to help users fix them. If there are not explicit indicators whether a .js file is CJS or ESM, we parse it as CJS first. If the parse error indicates that it contains ESM syntax, we parse it again as ESM. If the second parsing succeeds, we continue to treat it as ESM. --- .eslintignore | 1 + doc/api/cli.md | 17 ++ lib/internal/modules/cjs/loader.js | 115 +++++++++---- lib/internal/modules/esm/loader.js | 44 +++-- lib/internal/modules/esm/module_job.js | 75 +++++---- lib/internal/modules/esm/resolve.js | 12 +- lib/internal/modules/esm/translators.js | 154 +++++++----------- lib/internal/util/embedding.js | 2 +- src/module_wrap.cc | 150 +++++++++++++++++ src/module_wrap.h | 2 + src/node_contextify.cc | 103 ++++++++---- src/node_internals.h | 10 +- src/node_options.cc | 5 + src/node_options.h | 1 + .../test-esm-cjs-load-error-note.mjs | 6 +- .../test-require-module-entry-point.js | 8 + test/es-module/test-require-module.js | 37 +++++ 17 files changed, 525 insertions(+), 217 deletions(-) create mode 100644 test/es-module/test-require-module-entry-point.js create mode 100644 test/es-module/test-require-module.js diff --git a/.eslintignore b/.eslintignore index 64f34660d87a8f..a4baecb2397456 100644 --- a/.eslintignore +++ b/.eslintignore @@ -13,3 +13,4 @@ doc/changelogs/CHANGELOG_v1*.md !doc/changelogs/CHANGELOG_v18.md !doc/api_assets/*.js !.eslintrc.js +test/es-module/test-require-module-entry-point.js diff --git a/doc/api/cli.md b/doc/api/cli.md index 2d7e079b685a30..4dfa65b586473d 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -871,6 +871,22 @@ added: v11.8.0 Use the specified file as a security policy. +### `--experimental-require-module` + + + +> Stability: 1.1 - Active Developement + +Supports loading a synchronous ES module graph in `require()`. If the module +graph is not synchronous (contains top-level await), it throws an error. + +By default, a `.js` file will be parsed as a CommonJS module first. If it +contains ES module syntax, Node.js will try to parse and evaluate the module +again as an ES module. If it turns out to be synchronous and can be evaluated +successfully, the module namespace object will be returned by `require()`. + ### `--experimental-sea-config`