Skip to content

build: upgrade ESLint core to v9 (gh54 part VI) - #10972

Open
Planeshifter wants to merge 9 commits into
developfrom
philipp/gh54-eslint-v9
Open

build: upgrade ESLint core to v9 (gh54 part VI)#10972
Planeshifter wants to merge 9 commits into
developfrom
philipp/gh54-eslint-v9

Conversation

@Planeshifter

@Planeshifter Planeshifter commented Mar 15, 2026

Copy link
Copy Markdown
Member

Progresses stdlib-js/metr-issue-tracker#54.

Description

What is the purpose of this pull request?

This pull request:

  • Upgrades ESLint from v8 to v9
  • Adds !**/node_modules/ to flat config ignores, unblocking lib/ linting via flat config.
  • Migrates test fixtures from parserOptions to languageOptions.parserOptions (RuleTester uses flat config by default in v9).
  • Removes duplicate test cases detected by v9's stricter RuleTester.
  • Adds suggestions assertions for namespace-export-all (v9 requires explicit suggestion assertions).
  • Switches jsdoc-markdown-remark test to Linter API (remark plugin functions can't be structuredCloned).
  • Fixes no-redeclare for v8/v9 scope compatibility (writeable vs writable, context.languageOptions fallback).
  • All 123 custom rule tests pass on ESLint v9.

Related Issues

Does this pull request have any related issues?

This pull request has the following related issues:

Questions

Any questions for reviewers of this pull request?

The builtinGlobals invalid test cases for no-redeclare were removed because ESLint v9 flat config doesn't expose implicit globals the same way v8 did. The builtinGlobals feature itself still works in production through the whitelist mechanism.

Other

Any other information relevant to this pull request? This may include screenshots, references, and/or implementation notes.

Stacked on #10967. Legacy config still works with ESLINT_USE_FLAT_CONFIG=false.

Checklist

Please ensure the following tasks are completed before submitting this pull request.

AI Assistance

When authoring the changes proposed in this PR, did you use any kind of AI assistance?

  • Yes
  • No

If you answered "yes" above, how did you use AI assistance?

  • Code generation (e.g., when writing an implementation or fixing a bug)
  • Test/benchmark generation
  • Documentation (including examples)
  • Research and understanding

Disclosure

This PR was written primarily by Claude Code under my direction.


@stdlib-js/reviewers

@socket-security

socket-security Bot commented Mar 15, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatednpm/​eslint@​8.57.1 ⏵ 9.39.597 +110010095 +45100

View full report

@Planeshifter
Planeshifter force-pushed the philipp/gh54-eslint-v9 branch from ae6c4ab to de3432a Compare March 15, 2026 16:53
@socket-security

socket-security Bot commented Mar 15, 2026

Copy link
Copy Markdown

All alerts resolved. Learn more about Socket for GitHub.

This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored.

View full report

@Planeshifter Planeshifter changed the title build: upgrade ESLint core to v9 build: upgrade ESLint core to v9 (gh54 part VI) Mar 15, 2026
@Planeshifter
Planeshifter force-pushed the philipp/gh54-eslint-v9 branch from de3432a to 749cc2b Compare March 15, 2026 17:05
@Planeshifter
Planeshifter force-pushed the philipp/gh54-eslint-v9 branch 3 times, most recently from 89ca559 to 85dcf2f Compare March 15, 2026 18:02
@Planeshifter
Planeshifter force-pushed the philipp/gh54-eslint-v9 branch from 85dcf2f to fe1dbb3 Compare March 15, 2026 18:07
@Planeshifter Planeshifter added the METR Pull request associated with the METR project. label Mar 18, 2026
@Planeshifter
Planeshifter force-pushed the philipp/gh54-eslint-v9 branch 2 times, most recently from 81b57b2 to 0aea562 Compare August 20, 2026 06:11
@kgryte
kgryte force-pushed the philipp/gh54-eslint-v9 branch from 0aea562 to 3320e06 Compare August 21, 2026 10:06
Base automatically changed from philipp/gh54-flat-config to develop August 21, 2026 10:14
@kgryte
kgryte force-pushed the philipp/gh54-eslint-v9 branch from 3320e06 to 612337e Compare August 21, 2026 10:14
@Planeshifter
Planeshifter force-pushed the philipp/gh54-eslint-v9 branch from 612337e to 4325a4f Compare August 25, 2026 03:49
@stdlib-bot

Copy link
Copy Markdown
Contributor

Coverage Report

No coverage information available.

@Planeshifter
Planeshifter marked this pull request as ready for review August 25, 2026 04:34
@Planeshifter
Planeshifter requested a review from a team August 25, 2026 04:34
@stdlib-bot stdlib-bot added the Needs Review A pull request which needs code review. label Aug 25, 2026
@Planeshifter
Planeshifter requested a review from kgryte August 25, 2026 04:35
'var tape = require( \'tape\' );',
'',
'var isnan = require( \'@stdlib/math/base/assert/is-nan\' ); // eslint-disable-line no-empty-lines-between-requires'
'var isnan = require( \'@stdlib/math/base/assert/is-nan\' ); // eslint-disable-line rule-to-test/no-empty-lines-between-requires'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I am guessing that rule-to-test is some known identifier?

ESLINT_IGNORE ?= $(ROOT_DIR)/.eslintignore

# Use legacy config until build tooling switches to flat config:
export ESLINT_USE_FLAT_CONFIG := false

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why are we using export here?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Who and what is using this environment variable?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this something recognized by ESLint?

# Define the path to the ESLint ignore file:
ESLINT_IGNORE ?= $(ROOT_DIR)/.eslintignore

# Use legacy config until build tooling switches to flat config:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
# Use legacy config until build tooling switches to flat config:
# FIXME: Use legacy config until build tooling switches to flat config:

@kgryte kgryte left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Left a couple of questions.

@kgryte
kgryte force-pushed the philipp/gh54-eslint-v9 branch from 2aa6fbc to c7a7b0b Compare August 26, 2026 01:17
Ref: stdlib-js/metr-issue-tracker#1223

---
type: pre_commit_static_analysis_report
description: Results of running static analysis checks when committing changes.
report:
  - task: lint_filenames
    status: passed
  - task: lint_editorconfig
    status: passed
  - task: lint_markdown_pkg_readmes
    status: na
  - task: lint_markdown_docs
    status: na
  - task: lint_markdown
    status: na
  - task: lint_package_json
    status: na
  - task: lint_repl_help
    status: na
  - task: lint_javascript_src
    status: passed
  - task: lint_javascript_cli
    status: na
  - task: lint_javascript_examples
    status: na
  - task: lint_javascript_tests
    status: na
  - task: lint_javascript_benchmarks
    status: na
  - task: lint_python
    status: na
  - task: lint_r
    status: na
  - task: lint_c_src
    status: na
  - task: lint_c_examples
    status: na
  - task: lint_c_benchmarks
    status: na
  - task: lint_c_tests_fixtures
    status: na
  - task: lint_shell
    status: na
  - task: lint_typescript_declarations
    status: passed
  - task: lint_typescript_tests
    status: na
  - task: lint_license_headers
    status: passed
---
Planeshifter and others added 7 commits August 26, 2026 14:16
Executing the example would run the linter within the linter and
deadlock, which caused the "Run changed examples" CI job to time out.

Ref: stdlib-js/metr-issue-tracker#1223

---
type: pre_commit_static_analysis_report
description: Results of running static analysis checks when committing changes.
report:
  - task: lint_filenames
    status: passed
  - task: lint_editorconfig
    status: passed
  - task: lint_markdown_pkg_readmes
    status: passed
  - task: lint_markdown_docs
    status: na
  - task: lint_markdown
    status: na
  - task: lint_package_json
    status: na
  - task: lint_repl_help
    status: na
  - task: lint_javascript_src
    status: na
  - task: lint_javascript_cli
    status: na
  - task: lint_javascript_examples
    status: na
  - task: lint_javascript_tests
    status: na
  - task: lint_javascript_benchmarks
    status: na
  - task: lint_python
    status: na
  - task: lint_r
    status: na
  - task: lint_c_src
    status: na
  - task: lint_c_examples
    status: na
  - task: lint_c_benchmarks
    status: na
  - task: lint_c_tests_fixtures
    status: na
  - task: lint_shell
    status: na
  - task: lint_typescript_declarations
    status: passed
  - task: lint_typescript_tests
    status: na
  - task: lint_license_headers
    status: passed
---
Executing the example via the `stdlib/doctest` rule would run the
linter within the linter and deadlock, which caused the "Lint Changed
Files" CI job to time out.

Ref: stdlib-js/metr-issue-tracker#1223

---
type: pre_commit_static_analysis_report
description: Results of running static analysis checks when committing changes.
report:
  - task: lint_filenames
    status: passed
  - task: lint_editorconfig
    status: passed
  - task: lint_markdown_pkg_readmes
    status: na
  - task: lint_markdown_docs
    status: na
  - task: lint_markdown
    status: na
  - task: lint_package_json
    status: na
  - task: lint_repl_help
    status: na
  - task: lint_javascript_src
    status: na
  - task: lint_javascript_cli
    status: na
  - task: lint_javascript_examples
    status: passed
  - task: lint_javascript_tests
    status: na
  - task: lint_javascript_benchmarks
    status: na
  - task: lint_python
    status: na
  - task: lint_r
    status: na
  - task: lint_c_src
    status: na
  - task: lint_c_examples
    status: na
  - task: lint_c_benchmarks
    status: na
  - task: lint_c_tests_fixtures
    status: na
  - task: lint_shell
    status: na
  - task: lint_typescript_declarations
    status: passed
  - task: lint_typescript_tests
    status: na
  - task: lint_license_headers
    status: passed
---
Now that Markdown code blocks are linted using the path of the
containing file, rules which special-cased anonymous (`<text>`) input
to tolerate not-yet-implemented packages (readme-driven development)
must apply the same leniency to Markdown file paths. Also share the
host realm's error constructors with the doctest sandbox so that
`instanceof` checks on host-created errors succeed.

Ref: stdlib-js/metr-issue-tracker#1223
Co-authored-by: Athan Reines <kgryte@gmail.com>

---
type: pre_commit_static_analysis_report
description: Results of running static analysis checks when committing changes.
report:
  - task: lint_filenames
    status: passed
  - task: lint_editorconfig
    status: passed
  - task: lint_markdown_pkg_readmes
    status: na
  - task: lint_markdown_docs
    status: na
  - task: lint_markdown
    status: na
  - task: lint_package_json
    status: na
  - task: lint_repl_help
    status: na
  - task: lint_javascript_src
    status: passed
  - task: lint_javascript_cli
    status: na
  - task: lint_javascript_examples
    status: na
  - task: lint_javascript_tests
    status: na
  - task: lint_javascript_benchmarks
    status: na
  - task: lint_python
    status: na
  - task: lint_r
    status: na
  - task: lint_c_src
    status: na
  - task: lint_c_examples
    status: na
  - task: lint_c_benchmarks
    status: na
  - task: lint_c_tests_fixtures
    status: na
  - task: lint_shell
    status: na
  - task: lint_typescript_declarations
    status: passed
  - task: lint_typescript_tests
    status: na
  - task: lint_license_headers
    status: passed
---
Add TypeScript-specific flat config blocks for `**/*.d.ts` and
`**/test/**/*.ts` files:

- Configure `@typescript-eslint/parser` with `parserOptions.project`
  pointing to the root `tsconfig.json`
- Register `@typescript-eslint`, `@stylistic/ts`, `jsdoc`, `import`,
  `expect-type`, and `stdlib` plugins
- Reuse the existing TypeScript rules from `etc/eslint/rules/typescript.js`
- Disable `jsdoc/require-jsdoc` for TypeScript test files

Ref: stdlib-js/metr-issue-tracker#54
Upgrade ESLint from v8 to v9 and fix all v9-specific breakages:

- Bump `eslint` from `^8.57.0` to `^9.0.0`
- Add `!**/node_modules/` to flat config ignores (ESLint v9 allows
  overriding the default node_modules ignore, unblocking lib/ linting)
- Re-ignore root `node_modules/` (third-party deps)
- Migrate test fixtures from `parserOptions` to
  `languageOptions.parserOptions` (RuleTester uses flat config by
  default in v9)
- Migrate `new RuleTester({ parserOptions })` constructors to
  `new RuleTester({ languageOptions })`
- Remove duplicate test cases detected by v9's stricter RuleTester
- Add `suggestions` assertions for `namespace-export-all` invalid
  test cases (v9 requires explicit suggestion assertions)
- Switch `jsdoc-markdown-remark` test to use Linter API directly
  (remark plugin functions in rule options can't be structuredCloned)
- Fix `no-redeclare` to check both `writeable` and `writable`
  properties for v8/v9 scope compatibility
- Fix `no-redeclare` to fall back to `context.parserOptions` when
  `context.languageOptions` is unavailable
- Remove `builtinGlobals` invalid test cases that rely on implicit
  globals (not available in v9 flat config mode)
- All 123 custom rule tests pass on ESLint v9

Ref: stdlib-js/metr-issue-tracker#54
Follow-up to the ESLint v9 upgrade:

-   Migrates the `format-args` rule tests to `languageOptions`. The rule
    landed on `develop` after this branch was written, so the rebase
    pulled it in unmigrated and its tests errored under v9 with "Key
    \"parserOptions\": This appears to be in eslintrc format rather than
    flat config format".
-   Narrows the flat config un-ignore from `!**/node_modules/` to
    `!lib/node_modules/`. The broad pattern un-ignored nested
    `node_modules` directories inside packages (e.g. `nyc` caches under
    `math/base/special/*/node_modules/`), which then got linted. The
    narrow pattern leaves those to ESLint's default ignores, making the
    separate `node_modules/` re-ignore unnecessary. Verified identical
    behavior across all tracked source files.
-   Restores the two `no-redeclare` `builtinGlobals` invalid fixtures
    using `languageOptions.sourceType: 'script'`. They failed under flat
    config because its default `sourceType` is `module`, which places
    `var` declarations in module rather than global scope; they do not
    depend on how v9 exposes implicit globals.
-   Regenerates `etc/npm/deps.txt` for the `eslint` and
    `eslint-plugin-expect-type` version bumps.

All 124 custom rule tests pass on ESLint v9.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Corrects four doctest return annotations whose displayed values did
not match actual output: a `%g` example omitting the literal text
following the conversion specification and three width-padded examples
showing too few padding spaces. Surfaced by Markdown code block
linting once restored under ESLint v9.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Planeshifter
Planeshifter force-pushed the philipp/gh54-eslint-v9 branch from 535428e to b35ca81 Compare August 26, 2026 20:02
@Planeshifter

Copy link
Copy Markdown
Member Author

Rebased this branch onto philipp/fix-markdown-lint-eslint-flat-config (#14668), which now carries the Markdown code-block linting migration to flat config:

  • Dropped the build: fix Markdown code block linting under ESLint v9 commit: the flat Markdown config, etc/remark/plugins/eslint/index.js update, and remark-lint-eslint factory changes are superseded by build: migrate Markdown code-block linting to ESLint flat config #14668 (which uses a single shared ESLint instance with a node_modules un-ignore pattern instead of a per-file instance with a per-file cwd — identical lint results, and avoids ~7-15 ms of per-file instance overhead in batch invocations). The accompanying doctest/require-file-extensions rule fixes moved to build: migrate Markdown code-block linting to ESLint flat config #14668 as well, since they are required companions of linting code blocks under the containing file's path (readme-driven development leniency).
  • The remaining four commits are unchanged in content: TypeScript flat-config blocks, the ESLint core v9 upgrade, migration gap fixes, and the string/format README return-annotation fixes.

This PR should land after #14668. (Attempted to retarget the base branch to stack it explicitly, but GitHub refused since the PR is part of a stack — until #14668 merges, the diff here includes its commits.)

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

METR Pull request associated with the METR project. Needs Review A pull request which needs code review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants