
We are excited to announce a partnership between ESLint and Codemod to create a better migration experience for ESLint users, starting with the ESLint v8 to v9 and v9 to v10 migrations. All official ESLint codemods live in eslint/codemods and on the Codemod Registry. Community members are encouraged to open issues for missing codemods or contribute new ones through pull requests. Once reviewed and merged by maintainers, codemods are automatically published to the Codemod Registry through GitHub Actions.
What is Codemod?
Codemod is an open source platform for building, sharing, and running automated code migrations at scale. It is an OpenJS Foundation partner, and its tooling has been adopted by projects such as React, Node.js, Express, React Router, Nuxt.js, pnpm, Webpack, MSW, i18next, and more.
Its platform includes JSSG (JS ast-grep), a codemod runtime and workflow engine that supports multi-step transformations and combines deterministic, compiler-aware transformations with AI-powered steps to deliver the right balance of reliability, efficiency, and flexibility. The source code for the Codemod CLI and engine is available on GitHub.
Key features of Codemod include:
- Workspace-wide semantic analysis
- Cross-file refactors
- Multi-language support
- Repository-scale performance
- A simple, agent-friendly API
- Built-in AST inspection through MCP
- Secure sandboxed execution
- Metrics and codebase mining
- Native support for multi-file transformations
Check out the Codemod docs to learn more about JSSG (JS ast-grep), Codemod’s open source toolkit for building codemods, and explore its features.
ESLint v8 to v9 migration
For v8 to v9 migration, we’ve released two codemods: one to help upgrade ESLint config, and another for custom rules.
Migrating configuration with @eslint/v8-to-v9-config
npx codemod @eslint/v8-to-v9-config
Unlike the original ESLint Configuration Migrator, this codemod runs as a workspace migration. It can:
- Extract
eslintConfigfrompackage.jsonand generateeslint.config.mjs - Clean up duplicate
/* eslint */comments and invalid/* exported */comments in source files - Remove deprecated
require-jsdocandvalid-jsdocrule references from inline comments - Migrate those JSDoc rules to
eslint-plugin-jsdocusingjsdoc({ config: "flat/recommended", ... }) - Add a
// TODO: Migrate settings manuallycomment when custom settings need review - Handle several ESLint v9 rule option changes, including
no-unused-vars,no-useless-computed-key,no-sequences,no-constructor-return,camelcase, andno-restricted-imports - Convert
envtolanguageOptions.globalsusing theglobalspackage - Map
excludedFilesto per-configignores - Move
noInlineConfigandreportUnusedDisableDirectivestolinterOptions - Import parser packages and plugins
- Use
defineConfigandglobalIgnoresfrom@eslint/config-helpers - Merge ignore patterns from both
.eslintignoreand.gitignore
For JavaScript config files, the codemod works from the AST and can resolve simple bindings such as const values, identifiers, and spreads. More advanced logic, such as functions, conditionals, and dynamic require() calls, still needs manual review. The workflow also supports options such as --target, eslintConfigCustomName, and codeFormattingCommandEnabled, and prompts you to install required packages after it runs.
The codemod does not migrate --ext CLI usage, so you still need to create a config entry for the file extensions you previously passed on the command line. Learn more on @eslint/v8-to-v9-config’s registry page and in the configuration migration guide.
Migrating custom rules with @eslint/v8-to-v9-custom-rules
npx codemod @eslint/v8-to-v9-custom-rules
Important: Run this codemod only in directories containing ESLint rule files. It may incorrectly transform other JavaScript files that export functions.
The codemod supports CommonJS (module.exports = function (context) { ... }), ES Modules (export default function (context) { ... }), indirect exports (function rule() {} and module.exports = rule), higher-order wrappers such as wrapRule(function (context) { ... }), and rules created with @typescript-eslint/utils RuleCreator.
It performs a comprehensive migration of custom ESLint rules from v8 to v9, including:
- Removed
contextmethods — migrates calls tosourceCodeequivalents (for example,context.getSource()→sourceCode.getText()), including methods that moved with different signatures such asgetScope(),getAncestors(),getDeclaredVariables(), andmarkVariableAsUsed() contextmethods becoming properties — migratesgetSourceCode(),getFilename(),getPhysicalFilename(), andgetCwd()to their property equivalents with compatibility fallbacks- Removed
context.getComments()— converts node-specific calls togetCommentsBefore/getCommentsInside/getCommentsAfter(node) - Removed
CodePath#currentSegments— adds code path tracking logic (verify the result matches your rule’s needs) - Function-style rules are no longer supported — converts to the object format with
metaandcreate
The codemod also moves module.exports.schema into meta, converts old-style context.report(node, message) calls to object format, detects fixable rules, and adds fixable: "code" to meta.
It does not cover context.parserOptions and context.parserPath, because those properties are not removed until ESLint v10.0.0. Prefer context.languageOptions (and context.languageOptions.parser when you need the parser) instead.
Not every change can be fully automated. After running the codemod:
- Review TODO comments. Search for
TODOin your migrated files. If a rule usescontext.options, the codemod may leave a placeholder likeschema: [] // TODO: Define schema - this rule uses context.optionsthat you need to replace with a valid JSON schema. - Review comment method changes.
getCommentsBefore(),getCommentsInside(), andgetCommentsAfter()require anodeargument, and no-argumentcontext.getComments()calls should usesourceCode.getAllComments(). - Review skipped higher-order wrappers. If a wrapper passes configuration as the second argument (for example,
wrapRule(rule, config)), confirm whether any manual migration is needed. - Test your custom rules by running your rule test suite (for example,
npm test).
Learn more on @eslint/v8-to-v9-custom-rules’s registry page. For the full list of API changes and how to adopt them manually, see the ESLint v9 migration guide and the blog post for v9 custom rules.
ESLint v9 to v10 migration
Upgrade your ESLint project from v9 to v10 with the following codemod:
npx codemod @eslint/v9-to-v10
This recipe includes four individual codemods, each of which can also be run independently:
@eslint/v9-to-v10-config: Remove legacy env vars and CLI flags@eslint/v9-to-v10-custom-rules: Replace removed context and SourceCode methods@eslint/v9-to-v10-ruletester: Cleanup RuleTester test cases@eslint/v9-to-v10-linter-api: Fix Linter/ESLint API usage
Check out Codemod Registry to learn more about this recipe and its codemods. For more details about what’s new in v10 and how to adopt the new features, refer to the official ESLint upgrade guide.
Conclusion
Upgrading across major ESLint versions has often meant carefully translating configs, custom rules, and related APIs by hand. With this partnership, official Codemod workflows give you a faster starting point for the v8 to v9 and v9 to v10 migrations, while still leaving room for the manual review steps that complex projects need.
You can try the codemods today through the Codemod Registry, and find the source in eslint/codemods. If you run into a gap, open an issue or contribute a new codemod. For questions about the migrations themselves, see the v9 and v10 upgrade guides, or stop by Discord to talk with the team.

