Skip to content

feat: Add meta.languages support to rules#20571

Merged
fasttime merged 21 commits intomainfrom
copilot/allow-rules-to-specify-languages
Mar 26, 2026
Merged

feat: Add meta.languages support to rules#20571
fasttime merged 21 commits intomainfrom
copilot/allow-rules-to-specify-languages

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 27, 2026

Implements RFC 2025-rule-languages: adds a meta.languages array to rule metadata, validated at config time against the active language.

Changes

  • lib/config/config.js

    • Added doesRuleSupportLanguage(ruleLangs, configLanguageName, validPluginNames) helper with full matching logic
    • Added defensive Array.isArray() and typeof === "string" guards for invalid meta.languages values
    • Updated validateRulesConfig() to collect rules whose meta.languages don't match the config language and throw a descriptive TypeError listing all offenders
    • Normalizes @/ to js/ in the language name shown in error messages (e.g. "js/js" instead of "@/js")
    • Validates meta.languages structure (must be an array of strings) inside validateRulesConfig(), only for enabled rules
    • Attaches messageTemplate = "rule-unsupported-language" and messageData to the thrown error, enabling both verbose terminal output and clear inline-config error messages
  • lib/config/flat-config-schema.js

    • No longer validates meta.languages at Config construction time; validation is deferred to validateRulesConfig() so rules in plugins with invalid meta.languages do not cause errors unless the rule is actually enabled
  • lib/linter/linter.js

    • Added special handling for the rule-unsupported-language message template: when a rule enabled via inline config (e.g. /* eslint my-plugin/my-rule: 2 */) doesn't support the active language, the lint error now clearly explains why and tells the user to scope the rule with files or disable it, instead of showing an incomplete message
  • messages/rule-unsupported-language.js (new)

    • Verbose error message template shown in the terminal when ESLint throws for unsupported-language rules, listing the offending rule IDs, the active language, and actionable remediation steps (files scoping or "off")
  • tests/lib/config/config.js — Extended meta.languages validation test suite covering all matching paths:

    • js/* wildcard matching @ plugin
    • Namespace match with plugin/* wildcard
    • @/jsjs/js normalization in error messages
    • Both rule IDs appear in the error when multiple rules are unsupported
    • Invalid meta.languages (non-array or non-string entries) throws at validateRulesConfig() time only when the rule is enabled
  • docs/src/extend/custom-rules.md — Added meta.languages property documentation with usage examples and matching semantics (removed internal @/js implementation detail)

  • docs/src/extend/custom-rule-tutorial.md and docs/_examples/custom-rule-tutorial-code/enforce-foo-bar.js — Updated tutorial example to include languages: ["js/js"]

Matching semantics

// Rule supports any language
meta: { languages: ["*"] }

// Rule supports only JS
meta: { languages: ["js/js"] }

// Rule supports any language from the "markdown" plugin
meta: { languages: ["markdown/*"] }

// Rule supports a specific language; also resolves via plugin.meta.namespace
meta: { languages: ["markdown/gfm"] }

// No meta.languages → backward compatible, works with all languages

Rules without meta.languages continue to work with every language. Rules that declare meta.languages will cause validateRulesConfig() to throw if the active language isn't in the list. Invalid meta.languages values (non-array or containing non-strings) are only validated when the rule is actually enabled — consistent with how ESLint skips other rule validation (e.g. rule existence checks) when a rule is set to "off".

Error messages

When a rule doesn't support the active language, the error message now advises the user to either scope the rule with the files option or disable it, for example:

The following rules do not support the language "json/json":
    - "no-var"

To fix this error:
- Use the "files" option to apply the rule only to files where the language is supported:
  { files: ["**/*.js"], rules: { "no-var": "error" } }
- Or disable the rule with "off" or 0.

For rules enabled inline (e.g. /* eslint no-var: 2 */), the lint error message is similarly clear:

1:1  error  Inline configuration for rule "no-var" is invalid:
              Rule does not support the language "json/json". Use a config block
              with "files" to apply the rule only to supported files, or disable it.

📱 Kick off Copilot coding agent tasks wherever you are with GitHub Mobile, available on iOS and Android.

@eslint-github-bot
Copy link
Copy Markdown

Hi @Copilot!, thanks for the Pull Request

The pull request title isn't properly formatted. We ask that you update the pull request title to match this format, as we use it to generate changelogs and automate releases.

  • The commit message tag wasn't recognized. Did you mean "docs", "fix", or "feat"?
  • There should be a space following the initial tag and colon, for example 'feat: Message'.
  • The first letter of the tag should be in lowercase

To Fix: You can fix this problem by clicking 'Edit' next to the pull request title at the top of this page.

Read more about contributing to ESLint here

@netlify
Copy link
Copy Markdown

netlify Bot commented Feb 27, 2026

Deploy Preview for docs-eslint canceled.

Name Link
🔨 Latest commit bcfe9f9
🔍 Latest deploy log https://app.netlify.com/projects/docs-eslint/deploys/69c560a13d7ddf00080d9f10

@eslint-github-bot
Copy link
Copy Markdown

Hi @Copilot!, thanks for the Pull Request

The pull request title isn't properly formatted. We ask that you update the pull request title to match this format, as we use it to generate changelogs and automate releases.

  • The commit message tag wasn't recognized. Did you mean "docs", "fix", or "feat"?
  • There should be a space following the initial tag and colon, for example 'feat: Message'.
  • The first letter of the tag should be in lowercase

To Fix: You can fix this problem by clicking 'Edit' next to the pull request title at the top of this page.

Read more about contributing to ESLint here

@eslint-github-bot
Copy link
Copy Markdown

Hi @Copilot!, thanks for the Pull Request

The pull request title isn't properly formatted. We ask that you update the pull request title to match this format, as we use it to generate changelogs and automate releases.

  • The commit message tag wasn't recognized. Did you mean "docs", "fix", or "feat"?
  • There should be a space following the initial tag and colon, for example 'feat: Message'.
  • The first letter of the tag should be in lowercase

To Fix: You can fix this problem by clicking 'Edit' next to the pull request title at the top of this page.

Read more about contributing to ESLint here

1 similar comment
@eslint-github-bot
Copy link
Copy Markdown

Hi @Copilot!, thanks for the Pull Request

The pull request title isn't properly formatted. We ask that you update the pull request title to match this format, as we use it to generate changelogs and automate releases.

  • The commit message tag wasn't recognized. Did you mean "docs", "fix", or "feat"?
  • There should be a space following the initial tag and colon, for example 'feat: Message'.
  • The first letter of the tag should be in lowercase

To Fix: You can fix this problem by clicking 'Edit' next to the pull request title at the top of this page.

Read more about contributing to ESLint here

Copilot AI changed the title [WIP] Add support for language and dialect properties in rules feat: Add meta.languages support to rules for language-aware validation Feb 27, 2026
@eslint-github-bot
Copy link
Copy Markdown

Hi @Copilot!, thanks for the Pull Request

The pull request title isn't properly formatted. We ask that you update the pull request title to match this format, as we use it to generate changelogs and automate releases.

  • The length of the commit message must be less than or equal to 72

To Fix: You can fix this problem by clicking 'Edit' next to the pull request title at the top of this page.

Read more about contributing to ESLint here

Copy link
Copy Markdown
Member

@nzakas nzakas left a comment

Choose a reason for hiding this comment

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

@nzakas nzakas changed the title feat: Add meta.languages support to rules for language-aware validation feat: Add meta.languages support to rules Mar 9, 2026
@eslint-github-bot eslint-github-bot Bot added the feature This change adds a new feature to ESLint label Mar 9, 2026
@nzakas nzakas moved this from Needs Triage to Implementing in Triage Mar 9, 2026
@nzakas nzakas marked this pull request as ready for review March 9, 2026 20:34
@nzakas nzakas requested a review from a team as a code owner March 9, 2026 20:34
@github-actions github-actions Bot added the core Relates to ESLint's core APIs and features label Mar 9, 2026
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 9, 2026

@copilot The code looks good. We now need to update these docs pages to explain how to use this feature:

https://eslint.org/docs/latest/extend/custom-rules...

Done in commit 3c2483e. Updated both docs pages:

  • docs/src/extend/custom-rules.md: Added meta.languages property documentation with usage examples and a description of the full matching semantics (wildcard "*", "plugin/*", direct match, "js/js" special case, and namespace-based matching).
  • docs/src/extend/custom-rule-tutorial.md + docs/_examples/custom-rule-tutorial-code/enforce-foo-bar.js: Added languages: ["js/js"] to the example rule and a short explanation of why it's set.

Copy link
Copy Markdown
Member

@nzakas nzakas left a comment

Choose a reason for hiding this comment

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

LGTM. Would like another review before merging.

@nzakas nzakas moved this from Implementing to Second Review Needed in Triage Mar 9, 2026
Comment thread lib/config/config.js
Comment thread lib/config/config.js
}

const { pluginName: rulePluginPart, objectName: ruleLangPart } =
splitPluginIdentifier(langEntry);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The value for the language may be a non-string which would lead to a type error here as splitPluginIdentifier uses String#split.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in commit 5645358. Added a typeof langEntry !== "string" guard in the loop that skips non-string entries. The pluginsSchema.validate now also throws at config construction time if any entry is not a string, so the runtime defense is a belt-and-suspenders safeguard for any code paths that might bypass schema validation.

Copy link
Copy Markdown
Member

@mdjermanovic mdjermanovic left a comment

Choose a reason for hiding this comment

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

For rules enabled inline (e.g., /* eslint my-plugin/my-rule: 2 */), the logic in Linter cuts off the "The following rules do not support the language..." part, leaving a lint error message that doesn't really explain why the config is invalid:

1:1  error  Inline configuration for rule "my-plugin/my-rule" is invalid:
        - "my-plugin/my-rule"
  my-plugin/my-rule

Comment thread lib/config/config.js

if (unsupportedLanguageRules.length > 0) {
throw new TypeError(
`Key "rules": The following rules do not support the language "${normalizedLanguageName}":\n${unsupportedLanguageRules.map(ruleId => `\t- "${ruleId}"`).join("\n")}`,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The main problem is likely that users activate the JavaScript rules for all files and then will be struck with this error when using an additional language (e.g. JSON).
So users reading this error message should know to solve this problem (e.g. by turning off the rule or using files to only activate the rule for matching files).

@nzakas
Copy link
Copy Markdown
Member

nzakas commented Mar 24, 2026

@copilot address the concerns brought up by @mdjermanovic and @DMartens.

For the error message, we can use an error message template file to create a more verbose and helpful error message to display to the user.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 24, 2026

@copilot address the concerns brought up by @mdjermanovic and @DMartens.

For the error message, we can use an error message template file to create a more verbose and helpful err...

Done in commit 239a0a7:

  • messages/rule-unsupported-language.js: New verbose template explaining the problem and guiding users to fix it (use files to scope the rule, or set it to "off").
  • lib/config/config.js: The TypeError from validateRulesConfig now has messageTemplate = "rule-unsupported-language" and messageData = { ruleIds, language }, enabling the verbose terminal output.
  • lib/linter/linter.js: Added a dedicated handler for rule-unsupported-language errors in the inline config catch block. Instead of the confusing truncated message (- "rule-id"), users now see:

    Inline configuration for rule "my-plugin/my-rule" is invalid: Rule does not support the language "js/js". Use a config block with "files" to apply the rule only to supported files, or disable it.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • download.cypress.io
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node index.js --exec install (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

DMartens
DMartens previously approved these changes Mar 24, 2026
Copy link
Copy Markdown
Contributor

@DMartens DMartens left a comment

Choose a reason for hiding this comment

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

Changes LGTM, thanks.

Copy link
Copy Markdown
Member

@mdjermanovic mdjermanovic left a comment

Choose a reason for hiding this comment

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

To fix formatting errors.

Comment thread tests/lib/linter/linter.js Outdated
Comment thread docs/src/extend/custom-rules.md Outdated
Co-authored-by: Milos Djermanovic <[email protected]>
Comment thread docs/src/extend/custom-rules.md Outdated
mdjermanovic
mdjermanovic previously approved these changes Mar 26, 2026
Copy link
Copy Markdown
Member

@mdjermanovic mdjermanovic left a comment

Choose a reason for hiding this comment

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

LGTM. Leaving open for @fasttime to verify changes after his approval.

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.

We should add a test for this message template, similar to the existing ones in tests/messages. @copilot

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added in commit bcfe9f9. Added a describe("rule-unsupported-language", ...) block to tests/messages/messages.js with two tests: one for a single rule ID (verifying the language name, rule ID, files example, and help URL appear in the output) and one for multiple rule IDs (verifying both IDs appear).

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 26, 2026

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • download.cypress.io
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node index.js --exec install (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI requested a review from fasttime March 26, 2026 16:38
Copy link
Copy Markdown
Member

@fasttime fasttime left a comment

Choose a reason for hiding this comment

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

LGTM.

@fasttime fasttime merged commit 586ec2f into main Mar 26, 2026
41 checks passed
@fasttime fasttime deleted the copilot/allow-rules-to-specify-languages branch March 26, 2026 17:00
@github-project-automation github-project-automation Bot moved this from Second Review Needed to Complete in Triage Mar 26, 2026
},
fixable: "code",
schema: [],
languages: ["js/js"],
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

js/js is honestly pretty confusing.

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

Labels

accepted There is consensus among the team that this change meets the criteria for inclusion core Relates to ESLint's core APIs and features feature This change adds a new feature to ESLint

Projects

Status: Complete

Development

Successfully merging this pull request may close these issues.

6 participants