Skip to content

feat: add @eslint/v9-to-v10-config codemod - #12

Merged
alexbit-codemod merged 8 commits into
eslint:mainfrom
khadatkarrohit:feat/v9-to-v10-config
Jun 9, 2026
Merged

feat: add @eslint/v9-to-v10-config codemod#12
alexbit-codemod merged 8 commits into
eslint:mainfrom
khadatkarrohit:feat/v9-to-v10-config

Conversation

@khadatkarrohit

Copy link
Copy Markdown
Contributor

Summary

ESLint v10 removes several configuration mechanisms that were deprecated or made obsolete by the mandatory flat config format. Projects that still reference /* eslint-env */ comments, ESLINT_USE_FLAT_CONFIG, v10_config_lookup_from_file, or the removed CLI flags (--no-eslintrc, --env, --rulesdir, --ignore-path, --resolve-plugins-relative-to) will break after upgrading.

This PR adds a new codemod @eslint/v9-to-v10-config that automatically removes all of those so any project can upgrade without manual search-and-replace across scripts, CI configs, and source files.

What it transforms

eslint-env inline comments — now a lint error in v10

// Before
/* eslint-env browser */
const el = document.getElementById('app')

/* eslint-env node, browser */
function read() { return process.env.NODE_ENV }

// After
const el = document.getElementById('app')

function read() { return process.env.NODE_ENV }

Legacy env vars and CLI flags

Before After
ESLINT_USE_FLAT_CONFIG=true eslint . eslint .
ESLINT_USE_FLAT_CONFIG=false eslint . eslint .
export ESLINT_USE_FLAT_CONFIG=true && eslint . eslint .
cross-env ESLINT_USE_FLAT_CONFIG=true eslint . cross-env eslint .
v10_config_lookup_from_file in ESLINT_FLAGS Removed
unstable_config_lookup_from_file in ESLINT_FLAGS Removed
--no-eslintrc Removed
--env browser Removed
--rulesdir ./rules Removed
--ignore-path .gitignore Removed
--resolve-plugins-relative-to . Removed

How to use

npx codemod @eslint/v9-to-v10-config

Testing

6 fixtures covering all transform cases. Validated against 30 real-world scenarios including edge cases found in production repos (microsoft/pyright, strvcom/code-quality-tools, gradio-app/gradio).

Full scenario results: https://github.com/khadatkarrohit/eslint-v9-ruletester-test/blob/main/SCENARIOS-config.md

pnpm --filter @eslint/v9-to-v10-config test
pnpm --filter @eslint/v9-to-v10-config check-types

All tests pass.

Related

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new codemod package @eslint/v9-to-v10-config intended to help projects upgrade to ESLint v10 by removing now-invalid /* eslint-env */ comments and stripping deprecated env vars / CLI flags from common command strings and configs.

Changes:

  • Added a new codemod package at codemods/v10/config with workflow, scripts, TypeScript config, and fixtures.
  • Updated workspace lockfile to include the new package.
  • Minor README formatting fixes in the existing @eslint/v9-to-v10-custom-rules codemod docs.

Reviewed changes

Copilot reviewed 21 out of 22 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
pnpm-lock.yaml Adds the new codemods/v10/config importer entry and devDependencies in the lockfile.
codemods/v10/custom-rules/README.md Markdown formatting adjustments in examples/table.
codemods/v10/config/workflow.yaml New workflow defining transform execution order and file globs.
codemods/v10/config/tsconfig.json New TS config for the codemod scripts.
codemods/v10/config/scripts/remove-eslint-env-comments.ts Implements removal of /* eslint-env ... */ comments (including dropping comment-only lines).
codemods/v10/config/scripts/remove-legacy-flags.ts Implements stripping of ESLINT_USE_FLAT_CONFIG, certain ESLINT_FLAGS values, and removed CLI flags.
codemods/v10/config/package.json New package metadata and test/typecheck scripts.
codemods/v10/config/codemod.yaml New codemod registry metadata (name, version, workflow entrypoint, etc.).
codemods/v10/config/README.md Documentation for the new codemod’s intent, transforms, and usage.
codemods/v10/config/tests/legacy-flags-yaml/input.js Fixture for legacy env var / flag patterns in YAML-like content.
codemods/v10/config/tests/legacy-flags-yaml/expected.js Expected output fixture for YAML-like legacy flags removal.
codemods/v10/config/tests/legacy-flags-packagejson/input.js Fixture for package.json scripts using removed env var.
codemods/v10/config/tests/legacy-flags-packagejson/expected.js Expected output fixture for package.json script rewrites.
codemods/v10/config/tests/legacy-flags-env-var/input.js Fixture for JS env var assignments and exec strings.
codemods/v10/config/tests/legacy-flags-env-var/expected.js Expected output fixture for JS env var assignments and exec strings.
codemods/v10/config/tests/legacy-flags-cli/input.js Fixture for removed CLI flags in exec strings.
codemods/v10/config/tests/legacy-flags-cli/expected.js Expected output fixture for removed CLI flags in exec strings.
codemods/v10/config/tests/eslint-env-only-line/input.js Fixture covering eslint-env comment-only line and inline comment case.
codemods/v10/config/tests/eslint-env-only-line/expected.js Expected output for the comment-only line drop and inline removal.
codemods/v10/config/tests/eslint-env-basic/input.js Fixture covering basic eslint-env comment removal.
codemods/v10/config/tests/eslint-env-basic/expected.js Expected output for basic eslint-env comment removal.
.changeset/v9-to-v10-config.md Changeset announcing the new codemod as a minor release.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread codemods/v10/config/workflow.yaml Outdated
Comment thread codemods/v10/config/scripts/remove-legacy-flags.ts
Comment thread codemods/v10/config/scripts/remove-legacy-flags.ts Outdated
Comment thread codemods/v10/config/scripts/remove-legacy-flags.ts Outdated
Comment thread codemods/v10/config/tests/eslint-env-basic/input.js Outdated
Comment thread codemods/v10/config/workflow.yaml Outdated
@lumirlumir

Copy link
Copy Markdown
Member

I think the current transform implementation also follows direct source code editing instead of using SgNode#commitEdits(edits), as mentioned in #9 (comment).

It would be nice if we could consistently use the SgNode#commitEdits(edits) pattern here too, to prevent index-shift issues: once an earlier range is removed, later source indexes no longer point to the same text.

@khadatkarrohit

Copy link
Copy Markdown
Contributor Author

#9 (comment)

Thanks for flagging this! This transform is actually regex-based (replaceAll) rather than position-based, so it doesn't have the index-shift problem. SgRoot is only used to get the raw source text, no findAll or SgNode ranges involved, so commitEdits doesn't apply here. anything i am missing here?

@AugustinMauroy AugustinMauroy left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you must not do string transformation

Comment thread codemods/v10/config/workflow.yaml Outdated
Comment thread codemods/v10/config/workflow.yaml Outdated
Comment thread codemods/v10/config/scripts/remove-legacy-flags.ts Outdated
@lumirlumir

Copy link
Copy Markdown
Member

I see that this uses commitEdits, but the transform still looks too broad. It uses ast-grep only to find string/scalar nodes, then applies regex replacements inside every string.

That means we aren’t matching semantic locations like the scripts field in package.json, run steps in GitHub Actions, or command strings in JS execSync() calls. This can rewrite unrelated documentation strings, snapshots, config values, or YAML block text.

I think we should either narrow the AST matches to known command-bearing fields or call sites, or use structured selectors for the relevant formats instead of running global string replacements over arbitrary literals.

Also, the current regex pattern approach doesn’t seem like a good fit, as it would make future maintenance more difficult.

@khadatkarrohit

Copy link
Copy Markdown
Contributor Author

I see that this uses commitEdits, but the transform still looks too broad. It uses ast-grep only to find string/scalar nodes, then applies regex replacements inside every string.

That means we aren’t matching semantic locations like the scripts field in package.json, run steps in GitHub Actions, or command strings in JS execSync() calls. This can rewrite unrelated documentation strings, snapshots, config values, or YAML block text.

I think we should either narrow the AST matches to known command-bearing fields or call sites, or use structured selectors for the relevant formats instead of running global string replacements over arbitrary literals.

Also, the current regex pattern approach doesn’t seem like a good fit, as it would make future maintenance more difficult.

The current transforms operate on string/scalar nodes without semantic filtering, which can affect snapshots and documentation strings.

I’ll narrow the YAML transform to run: step values and the JSON transform to scripts fields using structured selectors. JS is trickier since commands can appear in many APIs (execSync, spawnSync, execa, etc.), but I’ll at least exclude obvious non-command contexts such as snapshots and test fixtures and iterate from there.

@AugustinMauroy

Copy link
Copy Markdown

JS is trickier since commands can appear in many APIs (execSync, spawnSync, execa, etc.)

IMO you (eslint) should only catch node api, other thing can be "skipped"

Removes eslint-env inline comments and legacy env vars and CLI flags
that are no longer valid in ESLint v10.
Removing eslint-env comments without also migrating the declared
globals causes rules like no-unused-vars to fire on globals that
were previously provided by the comment. Per DMartens review,
the safe approach is to leave the comments untouched and let
ESLint v10 report them so users can manually convert each comment
to languageOptions.globals in eslint.config.js.

- Remove remove-eslint-env-comments.ts script and workflow step
- Remove eslint-env-basic and eslint-env-only-line test fixtures
- Remove test:eslint-env script from package.json
- Update README with manual migration guidance and example
- Update codemod.yaml and changeset descriptions
@khadatkarrohit
khadatkarrohit force-pushed the feat/v9-to-v10-config branch from cc8bdee to 9280bcc Compare June 7, 2026 09:52
@khadatkarrohit

Copy link
Copy Markdown
Contributor Author

Force push was needed because the branch was rebased onto origin/main.

Comment thread codemods/v10/config/scripts/legacy-flag-transforms.ts
Comment thread .lintstagedrc.mjs Outdated
@alexbit-codemod
alexbit-codemod merged commit 1748ff9 into eslint:main Jun 9, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants