-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Description
Environment
Node version: v22.12.0
npm version: v10.9.0
Local ESLint version: v9.14.0 (Currently used)
Global ESLint version: Not found
Operating System: darwin 24.1.0
What parser are you using?
Default (Espree)
What did you do?
Configuration
new ESLint({
overrideConfigFile: true,
overrideConfig: [
...compat.plugins("eslint-plugin-local-rules"),
{ rules: { "local-rules/some-unstable-rule": "error" } },
],
allowInlineConfig: false,
ignore: true,
ignorePatterns: ["!**/node_modules/"],
});
// eslint-local-rules.cjs
"use strict";
module.exports = {
"some-unstable-rule": {
meta: {
docs: {
description: "intentionally crash",
category: "Possible Errors",
recommended: false,
},
schema: [],
},
create: function () {
return {
Identifier: function (node) {
if (node.name === "attributeForCrashing") {
node.name.attributeForCrashing.someAttribute;
}
},
};
},
},
};
// index.mjs
import { ESLint } from "eslint";
import { FlatCompat } from "@eslint/eslintrc";
const compat = new FlatCompat({ baseDirectory: process.cwd() });
const linter = new ESLint({
overrideConfigFile: true,
overrideConfig: [
...compat.plugins("eslint-plugin-local-rules"),
{ rules: { "local-rules/some-unstable-rule": "error" } },
],
});
try {
await linter.lintFiles("./source.mjs");
console.log("Lint success");
} catch (error) {
console.error("Caught error");
}
console.log("First");
await new Promise((r) => setImmediate(r)); // tick tick
console.log("Second");
// source.mjs
// Identifier.name = attributeForCrashing
window.attributeForCrashing();
What did you expect to happen?
On ESLint 9.13.0
it works as expected:
$ node index.mjs
Caught error
First
Second
Check exit code:
$ echo $?
0
What actually happened?
On ESLint 9.14.0
and above. The linter.lintFiles
does something automatically on backgroud, and sets Node to exit on next tick (narrowing down root cause here was quite difficult). This is unexpected. I would expect my script to reach at the end of file and not exit automatically. (If there was automatic exit, I would expect it to happen immediatelly and not after next tick.)
try {
await linter.lintFiles("./source.mjs");
} catch (error) {
console.error("Caught error");
}
console.log("First");
await new Promise((r) => setImmediate(r)); // tick tick
console.log("Second"); // This line is never reached
$ node index.mjs
Caught error
First
/Users/x/repros/eslint/eslint-local-rules.cjs:17
node.name.attributeForCrashing.someAttribute;
^
TypeError: Cannot read properties of undefined (reading 'someAttribute')
Occurred while linting /Users/x/repros/eslint/source.mjs:2
Rule: "local-rules/some-unstable-rule"
at Identifier (/Users/x/repros/eslint/eslint-local-rules.cjs:17:44)
at ruleErrorHandler (/Users/x/repros/eslint/node_modules/.pnpm/eslint@9.14.0/node_modules/eslint/lib/linter/linter.js:1084:48)
at /Users/x/repros/eslint/node_modules/.pnpm/eslint@9.14.0/node_modules/eslint/lib/linter/safe-emitter.js:45:58
at Array.forEach (<anonymous>)
at Object.emit (/Users/x/repros/eslint/node_modules/.pnpm/eslint@9.14.0/node_modules/eslint/lib/linter/safe-emitter.js:45:38)
at NodeEventGenerator.applySelector (/Users/x/repros/eslint/node_modules/.pnpm/eslint@9.14.0/node_modules/eslint/lib/linter/node-event-generator.js:297:26)
at NodeEventGenerator.applySelectors (/Users/x/repros/eslint/node_modules/.pnpm/eslint@9.14.0/node_modules/eslint/lib/linter/node-event-generator.js:326:22)
at NodeEventGenerator.enterNode (/Users/x/repros/eslint/node_modules/.pnpm/eslint@9.14.0/node_modules/eslint/lib/linter/node-event-generator.js:337:14)
at runRules (/Users/x/repros/eslint/node_modules/.pnpm/eslint@9.14.0/node_modules/eslint/lib/linter/linter.js:1128:40)
at #flatVerifyWithoutProcessors (/Users/x/repros/eslint/node_modules/.pnpm/eslint@9.14.0/node_modules/eslint/lib/linter/linter.js:1911:31) {
ruleId: 'local-rules/some-unstable-rule',
currentNode: <ref *2> Node {
type: 'Identifier',
start: 49,
end: 69,
loc: SourceLocation {
start: Position { line: 2, column: 7 },
end: Position { line: 2, column: 27 }
},
range: [ 49, 69 ],
name: 'attributeForCrashing',
parent: <ref *1> Node {
type: 'MemberExpression',
start: 42,
end: 69,
loc: SourceLocation {
start: Position { line: 2, column: 0 },
end: Position { line: 2, column: 27 }
},
range: [ 42, 69 ],
object: Node {
type: 'Identifier',
start: 42,
end: 48,
loc: SourceLocation {
start: Position { line: 2, column: 0 },
end: Position { line: 2, column: 6 }
},
range: [ 42, 48 ],
name: 'window',
parent: [Circular *1]
},
property: [Circular *2],
computed: false,
optional: false,
parent: <ref *3> Node {
type: 'CallExpression',
start: 42,
end: 71,
loc: SourceLocation {
start: Position { line: 2, column: 0 },
end: Position { line: 2, column: 29 }
},
range: [ 42, 71 ],
callee: [Circular *1],
arguments: [],
optional: false,
parent: Node {
type: 'ExpressionStatement',
start: 42,
end: 72,
loc: SourceLocation { start: [Position], end: [Position] },
range: [ 42, 72 ],
expression: [Circular *3],
parent: Node {
type: 'Program',
start: 42,
end: 72,
loc: [SourceLocation],
range: [Array],
body: [Array],
sourceType: 'module',
comments: [Array],
tokens: [Array],
parent: null
}
}
}
}
}
}
Node.js v22.12.0
Check exit code:
$ echo $?
1
Link to Minimal Reproducible Example
https://stackblitz.com/~/edit/eslint-exit-on-crash-9-14, https://stackblitz.com/~/edit/eslint-exit-on-crash-9-13
Participation
- I am willing to submit a pull request for this issue.
Additional comments
My use case is here: https://github.com/AriPerkkio/eslint-remote-tester/blob/6cbee8ff67cac7d467e7d8099d3f175d6174ff2c/packages/eslint-remote-tester/src/engine/worker-task.ts#L243-L277
This is run inside node:worker_threads
, which now started to unexpectedly crash due to this bug.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status