Eeslint

eslint/v8-to-v9-config

Migrate ESLint configuration from v8 to v9 flat config format

transformationmigrationeslintv8v9
Public
149 executions
Run locally
npx codemod @eslint/v8-to-v9-config
Documentation

@eslint/v8-to-v9-config

Migrate ESLint v8 to v9 format automatically.

Quick Start

bash

Workflow Params

When running workflow.yaml directly, you can control the optional formatting step with these params:

  • codeFormattingCommandEnabled (boolean, default: false): Enables/disables the formatting step.
  • codeFormattingCommand (string, default: npx prettier --write "**/eslint.config.mjs" --ignore-path /dev/null --no-config --no-error-on-unmatched-pattern): Command to run when formatting is enabled.

Example:

bash

After running, the codemod will display a list of packages that need to be installed. Install them:

bash

Note: If your config uses extends, you'll also need @eslint/eslintrc for FlatCompat support.
⚠️ Important: The codemod will display a yellow note reminding you to verify that all packages are not deprecated and still supported for ESLint v9. Please check each package before installing.

Then test your config:

bash

Migration Steps

Step 1: Config File Conversion

Converts .eslintrc.js, .eslintrc.json, .eslintrc.yaml, and .eslintrc.yml to the new config format (eslint.config.mjs).

What gets migrated:

  • env settings → languageOptions.globals
  • globalslanguageOptions.globals
  • parserOptionslanguageOptions.parserOptions
  • overrides → separate configuration objects in the array
  • noInlineConfiglinterOptions.noInlineConfig
  • reportUnusedDisableDirectiveslinterOptions.reportUnusedDisableDirectives (boolean true becomes "warn", false becomes "off"; explicit severity strings are preserved)

Step 2: Rule Schema Updates

Updates rules with breaking schema changes in ESLint v9:

RuleMigration
no-unused-varsAdds caughtErrors: 'none' (v9 changed default to 'all')
no-useless-computed-keyAdds enforceForClassMembers: false (v9 changed default to true)
no-sequencesMigrates allowInParentheses to new format
no-constructor-returnEnsures proper array format
camelcaseValidates allow option (must be array of strings)
no-restricted-importsRestructures paths configuration

Step 3: JSDoc Rules Migration

The require-jsdoc and valid-jsdoc rules were removed in ESLint v9. This codemod migrates them to eslint-plugin-jsdoc.

After running, install the plugin:

bash

⚠️ Manual step: If you have custom JSDoc settings, look for // TODO: Migrate settings manually comments in your config and update them accordingly.

Step 4: Comment Cleanup

Fixes ESLint comment syntax that became invalid in v9:

  • Duplicate /* eslint */ comments: Removes duplicate rule comments for the same rule
  • Malformed /* exported */ comments: Fixes to proper format

Step 5: Extends & Plugin Migration

All extends and plugins are preserved exactly as they were - no additions or removals.

Extends Migration

The codemod uses FlatCompat from @eslint/eslintrc to migrate extends to the flat config format:

  • eslint:recommended and eslint:all: These are automatically detected and handled with special FlatCompat instances that include recommendedConfig and/or allConfig properties
  • All other extends: Preserved exactly as they were and converted using compat.extends() method
  • eslint:recommended and eslint:all are filtered out from the extends array since they're handled by the FlatCompat configuration

Example:

If your original config had:

json

The migrated config will be:

javascript

Note that eslint:recommended is handled by the recommendedConfig in FlatCompat, so it's filtered out from the extends array.

Required dependency:

bash

Plugin Migration

All plugins are preserved exactly as they were - the codemod extracts them from the original config and maintains their exact format, including:

  • Plugin names
  • Plugin values (imports, require calls, etc.)
  • Plugin structure (object or array format)

The codemod automatically adds import statements for plugins when they're detected in the original config.

Plugin Naming Conventions:

The codemod follows ESLint v9 conventions for plugin package names and import identifiers:

  • Unscoped packages: eslint-plugin-foo → imports as fooPlugin from "eslint-plugin-foo"
  • Scoped packages: @foo/eslint-plugin → imports as fooPlugin from "@foo/eslint-plugin"
  • Scoped packages with suffix: @foo/eslint-plugin-bar → imports as fooBarPlugin from "@foo/eslint-plugin-bar"

The import identifiers are automatically generated to be valid JavaScript identifiers, converting package names to camelCase format.

Step 6: Ignore File Migration

ESLint v9 uses the ignores property instead of .eslintignore files.

This codemod attempts to migrate .eslintignore content to the config file's ignores array.

⚠️ Manual step: The codemod attempts to delete .eslintignore files, but may fail due to permissions. After running, verify and remove manually if needed:

bash

Before (.eslintrc.json):

json

After (eslint.config.mjs):

javascript

Note: If your config has other extends (e.g., "plugin:react/recommended"), they will be included in the compat.extends() array, while eslint:recommended and eslint:all are automatically handled by the FlatCompat configuration.

Resources

Before

This is one example from the codemod's test cases. The codemod may handle many more cases.

Ready to contribute?

Build your own codemod and share it with the community.