What I would like
To use @theguild/eslint-config on ESLint 10 in some repos while others stay on ESLint 9, and to
migrate them one at a time rather than all at once.
What blocks it today
1. The declared peer range excludes ESLint 10. peerDependencies.eslint is ^8 || ^9, so npm
refuses to install:
npm error Could not resolve dependency:
npm error peer eslint@"^8 || ^9" from @theguild/eslint-config@0.13.3
pnpm and yarn only warn, but the declared range is still the package's statement of support.
2. The package ships only eslintrc-format configs. Every file under src/ is built on eslintrc
constructs (extends, overrides, excludedFiles, env, ignorePatterns, plus
@rushstack/eslint-patch/modern-module-resolution). ESLint 10 removed that config format. It also no
longer bundles @eslint/eslintrc, which is absent from eslint@10.8.0's dependencies, so a
FlatCompat shim would now require installing that package separately. I have not tested whether
that path works end to end.
What I think needs to change
1. Add a flat config entry point, for example @theguild/eslint-config/flat.
Flat config is supported by both ESLint 9 and 10, so a single flat entry covers both. Consumers then
migrate per repo by swapping .eslintrc.cjs for eslint.config.js, changing the import, and dropping
ESLINT_USE_FLAT_CONFIG=false and --ignore-path. No rule changes are required if the rule set is
held constant.
2. Widen peerDependencies.eslint to ^8 || ^9 || ^10.
Otherwise installs fail on npm and warn elsewhere, even once a flat entry exists.
3. Keep the existing eslintrc entry during the transition.
Not every consumer can move at once, and there is no need to force it. Both entries can live in one
package.
4. Pin the plugin dependencies to CJS-capable versions while both entries exist.
This is what allows one dependency tree to serve both formats: CJS plugins work fine under flat
config, whereas ESM-only plugins break under eslintrc, which loads plugins with CJS require().
Two dependencies currently sit on the wrong side of that line:
eslint-plugin-unicorn is pinned to 57.0.0, the first release declaring "type": "module". Under
eslintrc, require() of an ESM package returns { __esModule, default }, so ESLint reads
plugin.rules as undefined and reports every configured unicorn/* rule as "Definition for rule
not found", once per linted file. Reverting to 56.0.1 fixes it, and 56.0.1 still contains all 17
unicorn rules base.js references and still uses the same ignoreUsedVariables option name, so no
config changes are needed.
eslint-plugin-mdx@3.2.0 calls loadEsmModule from eslint-mdx, but its floating ^3.2.0 range
now resolves to 3.6.2, which no longer exports it. Every .md and .mdx file then fails with a
fatal (0 , eslint_mdx_1.loadEsmModule) is not a function. Pinning eslint-mdx to 3.2.x, the
last CJS release, fixes it.
Both of these arrived in 0.13.4, which bumped unicorn from 56.0.1 to 57.0.0 and mdx from 3.1.5
to 3.2.0. Consumers on 0.13.3 and earlier are unaffected. This part is worth shipping as a patch
release on its own, since it helps anyone already on 0.13.4 regardless of the flat work.
5. Once consumers are on flat, bump the plugins and drop the eslintrc entry.
The pins above are transitional. When they are lifted, these are the changes that alter behaviour
silently rather than erroring:
unicorn renames: no-array-push-push to prefer-single-call, no-instanceof-array to
no-instanceof-builtins, no-array-for-each to no-for-each
unicorn/prefer-export-from: the option is renamed and inverted, ignoreUsedVariables: true
becomes checkUsedVariables: false, and omitting it starts reporting
unicorn/filename-case: newer versions also check directory names, which breaks __tests__/ and
Next.js route directories such as [[...mdxPath]]/
sonarjs: no-one-iteration-loop no longer exists in 4.x
- if
eslint-plugin-import-x is adopted, registering it under the import namespace keeps existing
inline // eslint-disable ... import/* comments working
- flat config does not read
.gitignore, so any --ignore-path .gitignore behaviour has to be
replicated in ignores
Notes on feasibility
The currently pinned versions already support flat config, so item 1 does not require bumping
anything: jsonc@2.19.1, yml@1.17.0, react@7.37.4 and jsx-a11y@6.10.2 expose flat presets, and
eslint-plugin-mdx@3.2.0 exports flat and flatCodeBlocks. react-hooks@5.2.0 and sonarjs@3.0.2
do not expose flat presets, but this config enables individual sonarjs rules rather than a preset, and
react-hooks needs only its two rules registered manually.
One open question
eslint-import-resolver-typescript@4.2.1 is the only other ESM dependency. Under eslintrc it is
loaded by eslint-plugin-import when a resolver-dependent rule runs. This config does not enable
import/no-unresolved, so it may never be invoked, but I have not confirmed that.
What I would like
To use
@theguild/eslint-configon ESLint 10 in some repos while others stay on ESLint 9, and tomigrate them one at a time rather than all at once.
What blocks it today
1. The declared peer range excludes ESLint 10.
peerDependencies.eslintis^8 || ^9, so npmrefuses to install:
pnpm and yarn only warn, but the declared range is still the package's statement of support.
2. The package ships only eslintrc-format configs. Every file under
src/is built on eslintrcconstructs (
extends,overrides,excludedFiles,env,ignorePatterns, plus@rushstack/eslint-patch/modern-module-resolution). ESLint 10 removed that config format. It also nolonger bundles
@eslint/eslintrc, which is absent fromeslint@10.8.0's dependencies, so aFlatCompatshim would now require installing that package separately. I have not tested whetherthat path works end to end.
What I think needs to change
1. Add a flat config entry point, for example
@theguild/eslint-config/flat.Flat config is supported by both ESLint 9 and 10, so a single flat entry covers both. Consumers then
migrate per repo by swapping
.eslintrc.cjsforeslint.config.js, changing the import, and droppingESLINT_USE_FLAT_CONFIG=falseand--ignore-path. No rule changes are required if the rule set isheld constant.
2. Widen
peerDependencies.eslintto^8 || ^9 || ^10.Otherwise installs fail on npm and warn elsewhere, even once a flat entry exists.
3. Keep the existing eslintrc entry during the transition.
Not every consumer can move at once, and there is no need to force it. Both entries can live in one
package.
4. Pin the plugin dependencies to CJS-capable versions while both entries exist.
This is what allows one dependency tree to serve both formats: CJS plugins work fine under flat
config, whereas ESM-only plugins break under eslintrc, which loads plugins with CJS
require().Two dependencies currently sit on the wrong side of that line:
eslint-plugin-unicornis pinned to57.0.0, the first release declaring"type": "module". Undereslintrc,
require()of an ESM package returns{ __esModule, default }, so ESLint readsplugin.rulesasundefinedand reports every configuredunicorn/*rule as "Definition for rulenot found", once per linted file. Reverting to
56.0.1fixes it, and56.0.1still contains all 17unicorn rules
base.jsreferences and still uses the sameignoreUsedVariablesoption name, so noconfig changes are needed.
eslint-plugin-mdx@3.2.0callsloadEsmModulefromeslint-mdx, but its floating^3.2.0rangenow resolves to
3.6.2, which no longer exports it. Every.mdand.mdxfile then fails with afatal
(0 , eslint_mdx_1.loadEsmModule) is not a function. Pinningeslint-mdxto3.2.x, thelast CJS release, fixes it.
Both of these arrived in
0.13.4, which bumped unicorn from56.0.1to57.0.0and mdx from3.1.5to
3.2.0. Consumers on0.13.3and earlier are unaffected. This part is worth shipping as a patchrelease on its own, since it helps anyone already on
0.13.4regardless of the flat work.5. Once consumers are on flat, bump the plugins and drop the eslintrc entry.
The pins above are transitional. When they are lifted, these are the changes that alter behaviour
silently rather than erroring:
unicornrenames:no-array-push-pushtoprefer-single-call,no-instanceof-arraytono-instanceof-builtins,no-array-for-eachtono-for-eachunicorn/prefer-export-from: the option is renamed and inverted,ignoreUsedVariables: truebecomes
checkUsedVariables: false, and omitting it starts reportingunicorn/filename-case: newer versions also check directory names, which breaks__tests__/andNext.js route directories such as
[[...mdxPath]]/sonarjs:no-one-iteration-loopno longer exists in 4.xeslint-plugin-import-xis adopted, registering it under theimportnamespace keeps existinginline
// eslint-disable ... import/*comments working.gitignore, so any--ignore-path .gitignorebehaviour has to bereplicated in
ignoresNotes on feasibility
The currently pinned versions already support flat config, so item 1 does not require bumping
anything:
jsonc@2.19.1,yml@1.17.0,react@7.37.4andjsx-a11y@6.10.2expose flat presets, andeslint-plugin-mdx@3.2.0exportsflatandflatCodeBlocks.react-hooks@5.2.0andsonarjs@3.0.2do not expose flat presets, but this config enables individual sonarjs rules rather than a preset, and
react-hooks needs only its two rules registered manually.
One open question
eslint-import-resolver-typescript@4.2.1is the only other ESM dependency. Under eslintrc it isloaded by
eslint-plugin-importwhen a resolver-dependent rule runs. This config does not enableimport/no-unresolved, so it may never be invoked, but I have not confirmed that.