Closed StewEucen closed 1 week ago
I can reproduce this locally. Thanks for the detailed analysis of the cause!
https://github.com/StewEucen/eslint-bug-with-jest?tab=readme-ov-file#cause
Retrier#retry()
usesPromise
declared inlib.es2015.iterable.d.ts
fs.readFile()
usesPromise
declared inlib.es5.d.ts
So it seems that in Node.js different operations may use different Promise
constructors?
@nzakas perhaps @humanwhocodes/retry
could check if the result is a thenable instead of checking result instanceof Promise
?
@mdjermanovic
So it seems that in Node.js different operations may use different Promise constructors?
- I think that since
fs
is old package, it uses polyfill Promise class.
I have already found two ways to fix now.
Change argument of #retry()
function to async in ESLint repository.
- return retrier.retry(() => fs.readFile(...)
+ return retrier.retry(async () => fs.readFile(...)
Change the logic to confirm Promise in #retry()
of @humanwhocodes/retry
repository.
- if (!(result instanceof Promise)) {
+ if (
+ !(result instanceof Promise)
+ && (!result || typeof result.then !== 'function')
+ ) {
I think that 2.
is a fixing ad-hoc, thus we can select 1.
The Promise
object returned by fs.readFile
is from a different realm:
This only occurs when the Node.js process is launched with the --experimental-vm-modules
flag. It may have something to do with Jest's experimental ESM support, which is enabled by the flag.
@fasttime
--experimental-vm-modules
on run Jest, it was due to the following reasons.I had used ESLint#lintFiles()
in Jest with ESLint@8.x.x
. As we know, Jest works as CommonJS. But It did not require to use --experimental-vm-modules
flag.
I changed the code to fit Flat Config with 9.1.1
, but it was not work. Because new code of ESLint#lintFiles()
uses dynamic import.
https://github.com/eslint/eslint/blob/v9.1.1/lib/eslint/eslint.js#L317
async function loadFlatConfigFile(filePath) {
...
const config = (await import(fileURL)).default;
...
}
https://github.com/eslint/eslint/blob/v9.1.1/lib/eslint/eslint.js#L369
async function calculateConfigArray(eslint, { ... }) {
...
if (configFilePath) {
const fileConfig = await loadFlatConfigFile(configFilePath);
...
}
...
}
https://github.com/eslint/eslint/blob/v9.1.1/lib/eslint/eslint.js#L815
class ESLint {
...
async lintFiles(patterns) {
...
const configs = await calculateConfigArray(this, eslintOptions);
...
}
...
}
To solve the problem, I added a --experimental-vm-modules
flag for Jest script.
https://github.com/StewEucen/eslint-bug-with-jest/blob/main/package.json#L7
{
"scripts": {
"test": "export NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\"; jest --forceExit"
},
}
And then, I got the behavior that I reported in this issue.
I can update retry
. Never occurred to me that there might be a promise from another realm involved.
A new version of @humanwhocodes/retry
was released as a patch version that should fix this.
Here's a PR to update the dependency: https://github.com/eslint/eslint/pull/18416
@nzakas @mdjermanovic @fasttime
Environment
Node version: 20.12.2 npm version: 10.5.0 Local ESLint version: 9.1.1 Global ESLint version: 9.1.1 Operating System: macOS
What parser are you using?
Default (Espree)
What did you do?
Configuration
* eslint.config.js ```js 'use strict' module.exports = [ { languageOptions: { parserOptions: { ecmaVersion: 'latest', }, }, ignores: [ '**/node_modules/**', ], rules: { indent: ['error', 2], quotes: ['error', 'single'], semi: ['error', 'never'], }, }, ] ```Actual code
Test Resources
What did you expect to happen?
I expect that Jest shows the log below
What actually happened?
An error
Result is not a promise.
is thrown.Link to Minimal Reproducible Example
https://github.com/StewEucen/eslint-bug-with-jest
Participation
Additional comments
I wrote details as the problem in
README.md
of this repository https://github.com/StewEucen/eslint-bug-with-jest