Upgrade ESLint to v10#76654
Conversation
|
Size Change: -186 B (0%) Total Size: 7.74 MB 📦 View Changed
ℹ️ View Unchanged
|
- Add globals and typescript-eslint as devDependencies - Use web-first Playwright assertions (toHaveAttribute, toHaveValue, toHaveText) instead of imperative getAttribute/inputValue/textContent - Trim leading/trailing spaces in Playwright test titles - Remove stale JSDoc @param "this" entries in e2e-test-utils-playwright - Fix @cite JSDoc tag to @see in gutenberg-test-env typings - Rename render result variables per testing-library conventions - Suppress intentional wrong-cased SVG props in serialize tests - Suppress role="presentation" on interactive elements in dataviews - Add config override for react.d.ts type augmentation import - Fix @babel/eslint-parser JSDoc tag name false positive
ESLint v10 throws "Cannot redefine plugin" when multiple config objects define the same plugin namespace. The exported configs (e.g. recommended) include multiple objects that each define @WordPress, causing errors for consumers spreading them directly. Fix by deduplicating plugin references at export time so all config objects sharing a plugin namespace point to the same instance.
Provide a backward-compatible eslintrc export at @wordpress/eslint-plugin/eslintrc for consumers still using ESLint v9 in eslintrc mode. The wrapper converts flat config arrays to eslintrc-compatible objects (single config with plugins as strings, rules, settings, overrides). Widen eslint peer dep to ^9.0.0 || ^10.0.0 to support the transition. The eslintrc wrapper prints a deprecation warning and will be removed in the next major version.
Add example-*/** to global ignores in eslint.config.mjs since these directories are generated by @wordpress/create-block during CI build tests and should not be linted. Convert remaining no-dom-globals-in-* test files from eslintrc format (top-level parser/parserOptions) to flat config format (languageOptions.parser as object reference).
…formatter Convert the scaffolded ESLint config in test-create-block.sh from .eslintrc.js to eslint.config.cjs since ESLint v10 no longer supports the legacy eslintrc format. Install eslint-formatter-compact as a devDependency since the compact formatter was removed from ESLint core in v10.
Playwright's toHaveText doesn't support Jest asymmetric matchers like expect.arrayContaining. Revert these two assertions back to the original allInnerTexts() + toEqual() pattern with an eslint-disable comment explaining why.
Use 'example-*/' instead of 'example-*/**' to ignore the entire directory including top-level files like eslint.config.cjs.
- Add globals conversion to flatToEslintrc (was missing, causing no-undef errors for eslintrc consumers) - Use process.emitWarning instead of console.warn for the deprecation notice (less noisy, shown once by Node.js by default) - Add @eslint-community/eslint-plugin-eslint-comments as a dependency of @wordpress/eslint-plugin (consumers need it since rule prefixes changed to @eslint-community/eslint-comments/*) - Fix inaccurate CHANGELOG entry: we still use eslint-plugin-import (with fixupPluginRules), not eslint-plugin-import-x
Incorporate the @wordpress/no-unmerged-classname rule for packages/ui/src/** that was added to .eslintrc.js on trunk while this branch was in flight.
Convert parserOptions to languageOptions format required by ESLint v10's RuleTester.
|
Kudos for getting this over the line 👏 This upgrade seemed both daunting and long-overdue so I'm glad we're able to check it off 🎉 |
|
Thank you so much for getting this across the finish line!! ❤️ This should make my life so much easier. |
| @@ -0,0 +1,69 @@ | |||
| /* eslint-disable jsdoc/check-tag-names -- Package names containing @ are not JSDoc tags */ | |||
There was a problem hiding this comment.
We could probably be using backticks (`) to work around this. Based on the syntax highlighting in GitHub, it does make me think that a JSDoc parser might actually try to pick up @babel as a JSDoc tag.
|
Yay, great to see this land, amazing work @manzoorwanijk and team! |
|
Thank you all for the appreciation and feedback that helped us ship this. I will create a few follow-up PRs to clean things up a bit as a part of the related issue - #76506. |
What?
Part of the ESLint v10 upgrade effort — see #76506.
Upgrades ESLint from v8 to v10 and migrates the entire codebase to flat config format. Also updates
@wordpress/eslint-pluginand@wordpress/scriptsto support the new config format while maintaining backward compatibility for existing consumers.Why?
ESLint v8 reached end of life. v9 introduced flat config as the default, and v10 removes the legacy eslintrc format entirely. This upgrade ensures Gutenberg stays on a supported ESLint version and benefits from the latest rule improvements across all plugins.
How?
This is a multi-layered migration:
1. Dependency upgrades (
package.json)eslint-plugin-import→eslint-plugin-import-x,@typescript-eslint/*v6 → v8)globalsandtypescript-eslintas devDependencies (replacing the oldenvand parser config patterns)eslint-formatter-compact(compact formatter was removed from ESLint core in v10)2.
@wordpress/eslint-pluginflat config conversionpackages/eslint-plugin/configs/rewritten to export flat config arrays instead of eslintrc objectsbabel-parser-compat.js— a compatibility wrapper that adapts@babel/eslint-parserfor flat config'slanguageOptions.parserformatfixupPluginRulesfrom@eslint/compatfor plugins that haven't yet migrated to flat config natively (eslint-plugin-import,eslint-plugin-react)dedupePluginsutility inindex.jsto prevent ESLint v10's "Cannot redefine plugin" error when consumers spread config arrays that share plugin namespaces"eslint": "^9.0.0 || ^10.0.0"for transition period3. eslintrc compatibility wrapper (
packages/eslint-plugin/eslintrc/)@wordpress/eslint-plugin/eslintrcentry point for ESLint v9 consumers still using.eslintrc.*files@wordpress/babel-eslint-parser-compat→@babel/eslint-parser)@eslint-community/eslint-comments→@eslint-community/eslint-plugin-eslint-comments)4. Root config migration
.eslintrc.js→eslint.config.mjsusing flat config format.eslintrc.strict.js→eslint.config.strict.mjs.eslintignore(patterns moved toignoresin flat config).eslintrc.jsonfiles replaced witheslint-overrides.cjsfiles loaded from the root config5.
@wordpress/scriptslint-js updates (packages/scripts/)lint-js.jsupdated to detect and use flat config (eslint.config.*) with fallback to eslintrc.eslintrc.js→eslint.config.cjsconfig/.eslintrc.jsandconfig/.eslintignore, replaced withconfig/eslint.config.cjs6. Pre-existing lint error fixes
no-unused-varscaughtErrorsdefaults to"all")eslint-comments/*rule prefixes to@eslint-community/eslint-comments/*// eslint-disable-next-line react-compiler/react-compilerfor known false positives onref.currentmutations in effects (facebook/react#29196)toHaveAttribute,toHaveValue, etc.)7. RuleTester compatibility
@wordpress/eslint-pluginrule tests fromparserOptionstolanguageOptionsformatparserstring references converted tolanguageOptions.parserobject references8. CI fixes
bin/test-create-block.shupdated to generateeslint.config.cjsinstead of.eslintrc.jsexample-*/to global ignores for directories generated during CI build testsTesting Instructions
Automated
npm run lint:js— should pass with zero errorsnpx jest --testPathPattern packages/eslint-plugin— all 456 tests passnpx jest --testPathPattern packages/scripts— all 32 tests passManual: Flat config consumer (ESLint v10)
npx eslint --print-config test.js # Should output merged config without "Cannot redefine plugin" errorsManual: eslintrc consumer (ESLint v9)
ESLINT_USE_FLAT_CONFIG=false npx eslint --print-config test.js # Should output eslintrc-compatible config with deprecation warningTesting Instructions for Keyboard
N/A — no UI changes.
Screenshots or screencast
N/A — tooling/infrastructure change only.
Use of AI Tools
Claude Code (Claude Opus) was used to assist with: