Skip to content

fix: type compatibility issue between Markdown v8 and ESLint v9.39.x#648

Open
lumirlumir wants to merge 15 commits intomainfrom
fix/resolve-eslint-markdown-v8-and-eslint-v9-compatibility-issue
Open

fix: type compatibility issue between Markdown v8 and ESLint v9.39.x#648
lumirlumir wants to merge 15 commits intomainfrom
fix/resolve-eslint-markdown-v8-and-eslint-v9-compatibility-issue

Conversation

@lumirlumir
Copy link
Copy Markdown
Member

@lumirlumir lumirlumir commented Apr 21, 2026

Prerequisites checklist

AI acknowledgment

  • I did not use AI to generate this PR.
  • (If the above is not checked) I have reviewed the AI-generated content before submitting.

What is the purpose of this pull request?

This PR fixes a type compatibility issue that makes Markdown plugin v8 incompatible with ESLint v9.39.x, which is now in maintenance mode.

Cause of the type incompatibility problem

Currently, the incompatibility comes from the export { rules }; statement in dist/index.d.ts (line 17).

image

This happens because rules is exposed such as Record<RuleId, MarkdownRuleDefinition>, and MarkdownRuleDefinition depends on CustomRuleDefinitionType, CustomRuleTypeDefinitions, and CustomRuleVisitorWithExit. Those types were changed as part of ESLint v10’s breaking changes, so some type signatures no longer match, making Markdown plugin v8 incompatible with ESLint v9.39.x.

Interestingly, if we avoid coercing the rule types to MarkdownRuleDefinition and keep them in their original object form instead, there are no type errors. That is because the rule objects themselves have not changed between versions.

I tried a number of different approaches to preserve the original rule object form while still keeping the benefits of type restrictions. In the end, the simplest approach was to coerce the built src/build/rules.js object to Record<RuleId, any>.

This lets us keep type restrictions when writing the code, while preventing end users from running into type incompatibilities caused by major ESLint version bumps.

Potential downsides of this change

Since the user-facing rule type changes from Record<RuleId, MarkdownRuleDefinition> to Record<RuleId, any>, directly accessing rule metadata through the index file becomes less strictly typed.

However, because directly accessing rules is considered an unstable API and is not treated as a breaking change under our policy, this seems acceptable.

Ref: https://github.com/eslint/eslint/blob/main/lib/unsupported-api.js

Compatibility with ESLint versions earlier than v9.39.0?

For ESLint versions earlier than v9.39.0, type errors occur not only with MarkdownRuleDefinition but also in other parts, such as processor.

I'm not sure there is an easy way to make this work with ESLint < v9.39.0. If there is, I'd be happy to follow it.

What changes did you make? (Give an overview)

ci.yml, types.test.ts, and README.md

Updated tests to ensure type compatibility with ESLint v9.39.0, v9.39.x, and v10.x.

src/index.js, and src/processor.js

Updated the types to use the ones from @eslint/core.

Before this change, @eslint/markdown depended on types exported from the eslint package. This is problematic because those types do not come from a direct dependency; instead, they effectively come from the peer-like eslint package.

First, eslint is currently not declared as a peer dependency, which can cause type errors when @eslint/markdown is used without the eslint package. For example, in Code Explorer, we use eslint-linter-browserify instead of eslint.

Second, the ESLint types come from the host package and depend on the version installed by the user, making the behavior unpredictable. This is especially problematic because some internal implementation code uses types from @eslint/core, while types from eslint are also mixed in.

tools/build-rules.js

Updated the auto-generated src/build/rules.js file to use the type Record<RuleId, any>. I believe this will also help avoid type inconsistency errors when updating the RuleDefinition type in the future.

Related Issues

Ref: eslint/json#213

Is there anything you'd like reviewers to focus on?

N/A

@eslintbot eslintbot added this to Triage Apr 21, 2026
@github-project-automation github-project-automation Bot moved this to Needs Triage in Triage Apr 21, 2026
@lumirlumir lumirlumir moved this from Needs Triage to Implementing in Triage Apr 21, 2026
@lumirlumir lumirlumir force-pushed the fix/resolve-eslint-markdown-v8-and-eslint-v9-compatibility-issue branch 2 times, most recently from b8b4ffd to 0917752 Compare April 21, 2026 06:30
@lumirlumir lumirlumir force-pushed the fix/resolve-eslint-markdown-v8-and-eslint-v9-compatibility-issue branch from 0917752 to 38fd019 Compare April 21, 2026 06:32
@lumirlumir lumirlumir changed the title fix: resolve type compatibility issue between Markdown v8 and ESLint v9 fix: type compatibility issue between Markdown v8 and ESLint v9.39.x Apr 21, 2026
@lumirlumir lumirlumir moved this from Implementing to Needs Triage in Triage Apr 26, 2026
@lumirlumir lumirlumir marked this pull request as ready for review April 26, 2026 14:45
@lumirlumir lumirlumir requested a review from Copilot April 26, 2026 14:46
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes TypeScript type compatibility between @eslint/markdown v8 and ESLint v9.39.x by decoupling the published rule types from ESLint’s evolving internal rule-definition typings, while expanding CI/type checks across supported ESLint versions.

Changes:

  • Switch JSDoc typing in runtime JS (src/index.js, src/processor.js) to use types from @eslint/core instead of eslint.
  • Update rule build generation to emit a Record<RuleId, any>-typed rules map to avoid cross-major ESLint type incompatibilities.
  • Expand type-compatibility testing (CI matrix + tests/types) and clarify README version guidance.

Reviewed changes

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

Show a summary per file
File Description
tools/build-rules.js Generates a typed RuleId union and coerces exported rules map to a looser type to avoid ESLint type coupling.
tests/types/types.test.ts Adds type assertions intended to validate compatibility with ESLint v9.39.x/v9.x/v10.x and rule key presence.
src/processor.js Replaces eslint-sourced types with @eslint/core types in JSDoc for fixes/messages/ranges.
src/index.js Replaces eslint-sourced config/rules typing with @eslint/core equivalents for generated declarations.
README.md Updates installation note to clarify which ESLint versions are type-compatible.
.github/workflows/ci.yml Adds an ESLint version matrix for the type-checking job and installs matching ESLint versions in CI.
Comments suppressed due to low confidence (1)

src/processor.js:445

  • excludeUnsatisfiableRules() is used after group.map(adjust) where adjust can return null, but the parameter is typed as LintMessage. Consider typing the parameter as LintMessage | null and/or making this a type guard so postprocess() is correctly typed as returning LintMessage[].
/**
 * Excludes unsatisfiable rules from the list of messages.
 * @param {LintMessage} message A message from the linter.
 * @returns {boolean} True if the message should be included in output.
 */
function excludeUnsatisfiableRules(message) {
	return message && !UNSATISFIABLE_RULES.has(message.ruleId);
}

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

Comment thread tests/types/types.test.ts
Comment thread tools/build-rules.js Outdated
Comment thread tools/build-rules.js
Comment thread src/processor.js
…solve-eslint-markdown-v8-and-eslint-v9-compatibility-issue
@lumirlumir lumirlumir marked this pull request as draft April 28, 2026 10:22
@lumirlumir lumirlumir moved this from Needs Triage to Implementing in Triage Apr 28, 2026
@lumirlumir lumirlumir marked this pull request as ready for review April 28, 2026 10:25
@lumirlumir lumirlumir moved this from Implementing to Needs Triage in Triage Apr 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Needs Triage

Development

Successfully merging this pull request may close these issues.

3 participants