Fix loading ESM-style shared config file in Node.js 23 (#16857)

* Return `.default` when Node.js 23 module feature is enabled

* Add changelog

* Add `ERR_REQUIRE_ASYNC_MODULE` to ignored error code
This commit is contained in:
SUZUKI Sosuke authored and GitHub committed 2024-11-25 21:33:48 +09:00
1 parent 269246db27
commit a3ba537295
2 files changed
+23 -1

No files matched your search

+12
View File
@@ -0,0 +1,12 @@
#### Fix loading ESM-style shared config file in Node.js 23 (#16857 by @sosukesuzuki)
In Prettier 3.3, attempting to load an ESM-style [shared config file](https://prettier.io/docs/en/sharing-configurations) in Node.js 23 resulted in the following warnings, preventing the options from being loaded:
```
[warn] Ignored unknown option { __esModule: true }.
[warn] Ignored unknown option { default: { trailingComma: "es5", tabWidth: 4, singleQuote: true } }.
```
This issue was caused by a new module feature in Node.js 23, known as [`require(ESM)`](https://nodejs.org/api/modules.html#loading-ecmascript-modules-using-require). Prettier 3.4 resolves this problem, allowing the options to load correctly.
For more details, please see https://github.com/prettier/prettier/issues/16812.
@@ -5,6 +5,7 @@ const requireErrorCodesShouldBeIgnored = new Set([
"MODULE_NOT_FOUND",
"ERR_REQUIRE_ESM",
"ERR_PACKAGE_PATH_NOT_EXPORTED",
"ERR_REQUIRE_ASYNC_MODULE",
]);
async function loadExternalConfig(externalConfig, configFile) {
/*
@@ -15,7 +16,16 @@ async function loadExternalConfig(externalConfig, configFile) {
3. is a dirname with index.js inside
*/
try {
return requireFromFile(externalConfig, configFile);
const required = requireFromFile(externalConfig, configFile);
// Since Node.js v23 onwards, it is possible to load ESM using require.
// If that feature is enabled, it is necessary to return the default.
// https://github.com/prettier/prettier/issues/16812
// FIXME: We want to add tests but https://github.com/jestjs/jest/issues/15363 blocks
// @ts-expect-error
if (process.features.require_module && required.__esModule) {
return required.default;
}
return required;
} catch (/** @type {any} */ error) {
if (!requireErrorCodesShouldBeIgnored.has(error?.code)) {
throw error;