Skip to content

disableRuleFixes creates infinite recursion and crashes ESLint after prolonged use #4

@romansp

Description

@romansp

Describe the bug

I noticed that after prolonged use of @antfu/eslint-config ESLint extension in VS Code will crash with an exception indicating infinite recursion like this one:

RangeError: Error while loading rule 'prefer-const': Maximum call stack size exceeded
Occurred while linting <input>
    at Reflect.get (<anonymous>)
    at Object.get (file:///Users/roman/Documents/dev/eslint-flat-config-recursion/node_modules/eslint-flat-config-utils/dist/index.mjs:32:24)
    at Reflect.get (<anonymous>)
    at Object.get (file:///Users/roman/Documents/dev/eslint-flat-config-recursion/node_modules/eslint-flat-config-utils/dist/index.mjs:32:24)
    at Reflect.get (<anonymous>)
    at Object.get (file:///Users/roman/Documents/dev/eslint-flat-config-recursion/node_modules/eslint-flat-config-utils/dist/index.mjs:32:24)
    at Reflect.get (<anonymous>)
    at Object.get (file:///Users/roman/Documents/dev/eslint-flat-config-recursion/node_modules/eslint-flat-config-utils/dist/index.mjs:32:24)
    at Reflect.get (<anonymous>)
    at Object.get (file:///Users/roman/Documents/dev/eslint-flat-config-recursion/node_modules/eslint-flat-config-utils/dist/index.mjs:32:24)

It was getting quite annoying to keep restarting ESLint server each time that happened so I decided to investigate and it led me to disableRuleFixes function in this repo.

export function disableRuleFixes(rule: Rule.RuleModule): Rule.RuleModule {
if ((rule as any)[FLAG_DISABLE_FIXES]) {
return rule
}
const originalCreate = rule.create.bind(rule)
rule.create = (context): any => {
const clonedContext = { ...context }
const proxiedContext = new Proxy(clonedContext, {
get(target, prop, receiver): any {
if (prop === 'report') {
return function (report: any) {
if (report.fix) {
delete report.fix
}
return (Reflect.get(context, prop, receiver) as any)({
...report,
fix: undefined,
})
}
}
return Reflect.get(context, prop, receiver)
},
set(target, prop, value, receiver): any {
return Reflect.set(context, prop, value, receiver)
},
})
Object.defineProperty(proxiedContext, FLAG_DISABLE_FIXES, { value: true, enumerable: false })
return originalCreate(proxiedContext)
}
return rule
}

Here FLAG_DISABLE_FIXES is used to guard against repeatedly setting proxy. But the flag is set on proxiedContext while it's checked on the rule itself causing proxy to be set again and again. Which eventually leads to stack trace overflow.

Kind of hard to come up with a proper minimal repro so I created an artificial example that calls disableRuleFixes 10_000 times on a single rule and then runs that rule through ESLint. This consistently crashes it on my machine but you may need to increase number of iterations.

After I update disableRuleFixes function to correctly set flag on the rule itself - stack overflow error goes away.

Repro steps:

  1. Clone https://github.com/romansp/eslint-flat-config-recursion
  2. npm install
  3. run node test-disable-rule-fixes.mjs

Expected: No exception is thrown.
Actual: Maximum call stack size exceeded exception is thrown.

Relevant thread in Discord: https://discord.com/channels/937808017016119440/937977751472840704/threads/1344413385986408561

Fix seems to be trivial and I'd like to submit a PR.

Reproduction

https://github.com/romansp/eslint-flat-config-recursion

System Info

System:
    OS: macOS 15.5
    CPU: (10) arm64 Apple M1 Pro
    Memory: 133.66 MB / 32.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 22.17.1 - ~/.nvm/versions/node/v22.17.1/bin/node
    Yarn: 4.9.2 - ~/.nvm/versions/node/v22.17.1/bin/yarn
    npm: 10.9.2 - ~/.nvm/versions/node/v22.17.1/bin/npm
    bun: 1.2.19 - ~/.bun/bin/bun
  Browsers:
    Chrome: 138.0.7204.169
    Safari: 18.5

Used Package Manager

npm

Validations

  • Follow our Code of Conduct
  • Read the Contributing Guide.
  • Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
  • Check that this is a concrete bug. For Q&A, please open a GitHub Discussion instead.
  • The provided reproduction is a minimal reproducible of the bug.

Contributions

  • I am willing to submit a PR to fix this issue
  • I am willing to submit a PR with failing tests (actually just go ahead and do it, thanks!)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions