From 395add25f1e31626cadf83100125537ef68e5e21 Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Wed, 1 Apr 2026 11:42:31 -0600 Subject: [PATCH 1/3] Fix TS package not to override base no-restricted-syntax selectors Both the `eslint-config` and `eslint-config-typescript` packages list the `no-restricted-syntax` rule. But `eslint-config-typescript` overwrites the selectors that `eslint-config` specifies, meaning that some invalid syntax is not detected in TypeScript files that would ordinarily be detected in JavaScript files. This commit ensures that the two are always kept in sync. --- packages/typescript/CHANGELOG.md | 4 +++ packages/typescript/rules-snapshot.json | 12 +++++++++ packages/typescript/src/index.mjs | 34 ++++++++++++++++++++++++- 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/packages/typescript/CHANGELOG.md b/packages/typescript/CHANGELOG.md index 2c1ac42..fc3a384 100644 --- a/packages/typescript/CHANGELOG.md +++ b/packages/typescript/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- **BREAKING:** Restrict usage of `with`, `in`, and sequence expressions, which should have been inherited from the base config but were overwritten + ## [15.0.0] ### Changed diff --git a/packages/typescript/rules-snapshot.json b/packages/typescript/rules-snapshot.json index a153a46..39eb028 100644 --- a/packages/typescript/rules-snapshot.json +++ b/packages/typescript/rules-snapshot.json @@ -226,6 +226,18 @@ "no-redeclare": "off", "no-restricted-syntax": [ "error", + { + "selector": "WithStatement", + "message": "With statements are not allowed" + }, + { + "selector": "BinaryExpression[operator='in']", + "message": "The \"in\" operator is not allowed" + }, + { + "selector": "SequenceExpression", + "message": "Sequence expressions are not allowed" + }, { "selector": "PropertyDefinition[accessibility='private'], MethodDefinition[accessibility='private'], TSParameterProperty[accessibility='private']", "message": "Use a hash name instead." diff --git a/packages/typescript/src/index.mjs b/packages/typescript/src/index.mjs index 85297a3..8bc44f9 100644 --- a/packages/typescript/src/index.mjs +++ b/packages/typescript/src/index.mjs @@ -1,4 +1,4 @@ -import { createConfig } from '@metamask/eslint-config'; +import base, { createConfig } from '@metamask/eslint-config'; import * as resolver from 'eslint-import-resolver-typescript'; import importX from 'eslint-plugin-import-x'; import jsdoc from 'eslint-plugin-jsdoc'; @@ -6,6 +6,37 @@ import jsdoc from 'eslint-plugin-jsdoc'; // eslint-disable-next-line import-x/no-unresolved import typescript from 'typescript-eslint'; +/** + * Collects all options for a given array-valued rule across one or more flat + * config arrays, excluding the leading severity element. + * + * ESLint flat config does not merge array-valued rules across config objects — + * a later config silently replaces earlier ones. This helper makes it possible + * to extend an upstream rule configuration rather than copy-pasting its options. + * + * @param {string} ruleName - The rule to collect options for. + * @param {import('eslint').Linter.Config[][]} configs - Flat config arrays to + * collect options from. + * @returns {unknown[]} The options from all matching rule entries, with the + * leading severity element omitted. + */ +function collectExistingRuleOptions(ruleName, configs) { + return configs.flat().flatMap((config) => { + const rule = config.rules?.[ruleName]; + if (!Array.isArray(rule)) { + return []; + } + // Rule entries are ['error' | 'warn' | number, ...options]. + // Skip the first element (severity) and collect the rest. + return rule.slice(1); + }); +} + +const baseNoRestrictedSyntaxOptions = collectExistingRuleOptions( + 'no-restricted-syntax', + base, +); + const config = createConfig({ name: '@metamask/eslint-config-typescript', @@ -234,6 +265,7 @@ const config = createConfig({ // Prefer hash names over TypeScript's `private` modifier. 'no-restricted-syntax': [ 'error', + ...baseNoRestrictedSyntaxOptions, { selector: "PropertyDefinition[accessibility='private'], MethodDefinition[accessibility='private'], TSParameterProperty[accessibility='private']", From 72744d4ec36abb1c5cf8c7f88a6823bb220c3e1e Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Wed, 1 Apr 2026 12:14:14 -0600 Subject: [PATCH 2/3] Add PR link to changelog --- packages/typescript/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/typescript/CHANGELOG.md b/packages/typescript/CHANGELOG.md index fc3a384..f403d1c 100644 --- a/packages/typescript/CHANGELOG.md +++ b/packages/typescript/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- **BREAKING:** Restrict usage of `with`, `in`, and sequence expressions, which should have been inherited from the base config but were overwritten +- **BREAKING:** Restrict usage of `with`, `in`, and sequence expressions, which should have been inherited from the base config but were overwritten ([#436](https://github.com/MetaMask/eslint-config/pull/436)) ## [15.0.0] From 107bcfea099610b571fb8681159a8f009baa50c8 Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Wed, 1 Apr 2026 12:15:16 -0600 Subject: [PATCH 3/3] Update changelog --- packages/typescript/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/typescript/CHANGELOG.md b/packages/typescript/CHANGELOG.md index f403d1c..7f89209 100644 --- a/packages/typescript/CHANGELOG.md +++ b/packages/typescript/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- **BREAKING:** Restrict usage of `with`, `in`, and sequence expressions, which should have been inherited from the base config but were overwritten ([#436](https://github.com/MetaMask/eslint-config/pull/436)) +- **BREAKING:** Restrict usage of `with`, `in`, and sequence expressions, which should have been inherited from the base config but were mistakenly overridden ([#436](https://github.com/MetaMask/eslint-config/pull/436)) ## [15.0.0]