From 85a02f6f6b31b5d18f9e5db17249f50c0f0851fb Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Fri, 19 Dec 2025 15:46:34 -0700 Subject: [PATCH 01/10] Loosen JSDoc rules Currently, all arrow functions and function expressions are required to be documented with JSDoc, and object types in return types are required to documented as well. This means: ``` typescript // The arrow function here is now required to be documented foo(() => { // ... }) foo({ // The arrow function here is now required to be documented bar: () => { // ... } }) function foo(): { // This is now required to be documented bar: string; } { // ... } ``` This commit loosens the rules so that: - Instead of restricting all arrow functions and function expressions, restrict only those that are not contained within plain objects or are not arguments to functions or methods - Instead of restricting all interfaces or type aliases, restrict only those that do not appear in `declare` blocks (ambient declarations) - Instead of restricting object types in return types, restrict object types in "root" types --- packages/base/src/index.mjs | 17 +++++++++----- packages/typescript/src/index.mjs | 37 ++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/packages/base/src/index.mjs b/packages/base/src/index.mjs index 8d6219fb..e0055c9d 100644 --- a/packages/base/src/index.mjs +++ b/packages/base/src/index.mjs @@ -397,17 +397,22 @@ const rules = createConfig({ 'error', { require: { - ArrowFunctionExpression: true, + // Classes ClassDeclaration: true, + // Function declarations FunctionDeclaration: true, - FunctionExpression: true, + // Methods MethodDefinition: true, }, contexts: [ - 'TSInterfaceDeclaration', - 'TSTypeAliasDeclaration', - 'TSEnumDeclaration', - 'TSPropertySignature', + // Arrow functions that are not contained within plain objects or + // are not arguments to functions or methods + ':not(Property, NewExpression, CallExpression) > ArrowFunctionExpression', + // Function expressions that are not contained within plain objects + // or are not arguments to functions or methods + ':not(Property, NewExpression, CallExpression) > FunctionExpression', + // Exported variables + 'ExportNamedDeclaration:has(> VariableDeclaration)', ], }, ], diff --git a/packages/typescript/src/index.mjs b/packages/typescript/src/index.mjs index 3903bd02..af961202 100644 --- a/packages/typescript/src/index.mjs +++ b/packages/typescript/src/index.mjs @@ -42,7 +42,12 @@ const config = createConfig({ '@typescript-eslint/array-type': 'error', '@typescript-eslint/consistent-type-assertions': 'error', '@typescript-eslint/consistent-type-definitions': ['error', 'type'], - '@typescript-eslint/explicit-function-return-type': 'error', + '@typescript-eslint/explicit-function-return-type': [ + 'error', + { + allowExpressions: true, + }, + ], '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-namespace': [ 'error', @@ -215,6 +220,36 @@ const config = createConfig({ // Use TypeScript types rather than JSDoc types. 'jsdoc/no-types': 'error', + // Override rule defined in base config to require JSDoc for + // TypeScript-specific symbols. + 'jsdoc/require-jsdoc': [ + 'error', + { + require: { + // Classes + ClassDeclaration: true, + // Function declarations + FunctionDeclaration: true, + // Methods + MethodDefinition: true, + }, + contexts: [ + // Arrow functions that are not contained within plain objects or + // are not arguments to functions or methods + ':not(Property, NewExpression, CallExpression) > ArrowFunctionExpression', + // Function expressions that are not contained within plain objects + // or are not arguments to functions or methods + ':not(Property, NewExpression, CallExpression) > FunctionExpression', + // Type interfaces that are not defined within `declare` blocks + ':not(TSModuleBlock) > TSInterfaceDeclaration', + // Type aliases + 'TSTypeAliasDeclaration', + // Enums + 'TSEnumDeclaration', + ], + }, + ], + // These all conflict with `jsdoc/no-types`. 'jsdoc/require-param-type': 'off', 'jsdoc/require-property-type': 'off', From 59a95d9f91ee4e53f9d3e2756991e2a5652a6840 Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Wed, 1 Apr 2026 11:36:59 -0600 Subject: [PATCH 02/10] Ensure we don't override rules from base; regenerate snapshots --- packages/base/rules-snapshot.json | 9 ++---- packages/typescript/rules-snapshot.json | 24 +++++++++++++-- packages/typescript/src/index.mjs | 41 +++++++++++++++++++------ 3 files changed, 57 insertions(+), 17 deletions(-) diff --git a/packages/base/rules-snapshot.json b/packages/base/rules-snapshot.json index e301a5a8..746413c6 100644 --- a/packages/base/rules-snapshot.json +++ b/packages/base/rules-snapshot.json @@ -180,17 +180,14 @@ "error", { "require": { - "ArrowFunctionExpression": true, "ClassDeclaration": true, "FunctionDeclaration": true, - "FunctionExpression": true, "MethodDefinition": true }, "contexts": [ - "TSInterfaceDeclaration", - "TSTypeAliasDeclaration", - "TSEnumDeclaration", - "TSPropertySignature" + ":not(Property, NewExpression, CallExpression) > ArrowFunctionExpression", + ":not(Property, NewExpression, CallExpression) > FunctionExpression", + "ExportNamedDeclaration:has(> VariableDeclaration)" ] } ], diff --git a/packages/typescript/rules-snapshot.json b/packages/typescript/rules-snapshot.json index afb709c3..03f0ce64 100644 --- a/packages/typescript/rules-snapshot.json +++ b/packages/typescript/rules-snapshot.json @@ -6,7 +6,10 @@ "@typescript-eslint/consistent-type-definitions": ["error", "type"], "@typescript-eslint/consistent-type-exports": "error", "@typescript-eslint/default-param-last": "error", - "@typescript-eslint/explicit-function-return-type": "error", + "@typescript-eslint/explicit-function-return-type": [ + "error", + { "allowExpressions": true } + ], "@typescript-eslint/naming-convention": [ "error", { @@ -192,7 +195,24 @@ "jsdoc/require-example": "off", "jsdoc/require-file-overview": "off", "jsdoc/require-hyphen-before-param-description": "off", - "jsdoc/require-jsdoc": "error", + "jsdoc/require-jsdoc": [ + "error", + { + "require": { + "ClassDeclaration": true, + "FunctionDeclaration": true, + "MethodDefinition": true + }, + "contexts": [ + ":not(Property, NewExpression, CallExpression) > ArrowFunctionExpression", + ":not(Property, NewExpression, CallExpression) > FunctionExpression", + "ExportNamedDeclaration:has(> VariableDeclaration)", + ":not(TSModuleBlock) > TSInterfaceDeclaration", + ":not(TSModuleBlock) > TSTypeAliasDeclaration", + "TSEnumDeclaration" + ] + } + ], "jsdoc/require-param": "error", "jsdoc/require-param-description": "error", "jsdoc/require-param-name": "error", diff --git a/packages/typescript/src/index.mjs b/packages/typescript/src/index.mjs index af961202..285b6dec 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,32 @@ 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 config = createConfig({ name: '@metamask/eslint-config-typescript', @@ -234,16 +260,13 @@ const config = createConfig({ MethodDefinition: true, }, contexts: [ - // Arrow functions that are not contained within plain objects or - // are not arguments to functions or methods - ':not(Property, NewExpression, CallExpression) > ArrowFunctionExpression', - // Function expressions that are not contained within plain objects - // or are not arguments to functions or methods - ':not(Property, NewExpression, CallExpression) > FunctionExpression', + ...collectExistingRuleOptions('jsdoc/require-jsdoc', base)[0] + .contexts, + // Type interfaces that are not defined within `declare` blocks ':not(TSModuleBlock) > TSInterfaceDeclaration', - // Type aliases - 'TSTypeAliasDeclaration', + // Type aliases that are not defined within `declare` blocks + ':not(TSModuleBlock) > TSTypeAliasDeclaration', // Enums 'TSEnumDeclaration', ], From 75c9356398dedd18b4cedd521cab2d7488c3e8c7 Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Wed, 1 Apr 2026 11:39:20 -0600 Subject: [PATCH 03/10] Also deduplicate 'require' option --- packages/typescript/src/index.mjs | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/packages/typescript/src/index.mjs b/packages/typescript/src/index.mjs index 285b6dec..c9cdfe10 100644 --- a/packages/typescript/src/index.mjs +++ b/packages/typescript/src/index.mjs @@ -32,6 +32,11 @@ function collectExistingRuleOptions(ruleName, configs) { }); } +const existingJsdocRuleOptions = collectExistingRuleOptions( + 'jsdoc/require-jsdoc', + base, +); + const config = createConfig({ name: '@metamask/eslint-config-typescript', @@ -246,23 +251,14 @@ const config = createConfig({ // Use TypeScript types rather than JSDoc types. 'jsdoc/no-types': 'error', - // Override rule defined in base config to require JSDoc for + // Extend rule defined in base config to require JSDoc for // TypeScript-specific symbols. 'jsdoc/require-jsdoc': [ 'error', { - require: { - // Classes - ClassDeclaration: true, - // Function declarations - FunctionDeclaration: true, - // Methods - MethodDefinition: true, - }, + require: existingJsdocRuleOptions[0].require, contexts: [ - ...collectExistingRuleOptions('jsdoc/require-jsdoc', base)[0] - .contexts, - + ...existingJsdocRuleOptions[0].contexts, // Type interfaces that are not defined within `declare` blocks ':not(TSModuleBlock) > TSInterfaceDeclaration', // Type aliases that are not defined within `declare` blocks From 19b7bdb38c12d4fa084e32a880cce1ce7fe9fd3d Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Wed, 1 Apr 2026 12:30:12 -0600 Subject: [PATCH 04/10] Update changelog --- packages/base/CHANGELOG.md | 12 ++++++++++++ packages/typescript/CHANGELOG.md | 17 +++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/packages/base/CHANGELOG.md b/packages/base/CHANGELOG.md index cfe6067f..9c26910a 100644 --- a/packages/base/CHANGELOG.md +++ b/packages/base/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Update `jsdoc/require-jsdoc` to loosen requirements for various kinds of symbols ([#433](https://github.com/MetaMask/eslint-config/pull/433)) + - Instead of requiring JSDoc for all arrow functions and function expressions, require only arrow functions not contained within plain objects or are not arguments to functions or methods. + ## [14.1.0] ### Changed @@ -32,6 +37,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - ESLint 9 supports ESM out-of-the-box, so this change updates the package to be pure ESM. This means that the package can no longer be used with CommonJS `require` syntax. +- **BREAKING:** Update `jsdoc/require-jsdoc` to require documentation for more things ([#394](https://github.com/MetaMask/eslint-config/pull/394)) + - These kinds of symbols are now required to have JSDoc: + - Arrow functions + - Class declarations + - Enum declarations + - Function expressions + - Method definitions ## [13.0.0] diff --git a/packages/typescript/CHANGELOG.md b/packages/typescript/CHANGELOG.md index 29091f05..65d54ac6 100644 --- a/packages/typescript/CHANGELOG.md +++ b/packages/typescript/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Update `jsdoc/require-jsdoc` to loosen requirements for various kinds of symbols ([#433](https://github.com/MetaMask/eslint-config/pull/433)) + - Instead of requiring JSDoc for all arrow functions and function expressions, require only arrow functions not contained within plain objects or are not arguments to functions or methods. + - Instead of requiring JSDoc for all interfaces or type aliases, require only those that do not appear in declare blocks (ambient declarations). + - Instead of requiring JSDoc for all object types in return types, require only object types in "root" types. + ## [14.1.0] ### Added @@ -40,6 +47,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - ESLint 9 supports ESM out-of-the-box, so this change updates the package to be pure ESM. This means that the package can no longer be used with CommonJS `require` syntax. +- **BREAKING:** Update `jsdoc/require-jsdoc` to require documentation for more things ([#394](https://github.com/MetaMask/eslint-config/pull/394)) + - These kinds of symbols are now required to have JSDoc: + - Arrow functions + - Class declarations + - Enum declarations + - Function expressions + - Method definitions + - TypeScript interface declarations + - TypeScript type alias declarations + - TypeScript property signatures ### Removed From 09f7e4d814d0e5efbdfdeed62dfca63f5e2f81f0 Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Wed, 1 Apr 2026 12:30:45 -0600 Subject: [PATCH 05/10] Don't require that exported variables are documented --- packages/base/src/index.mjs | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/base/src/index.mjs b/packages/base/src/index.mjs index e0055c9d..7afa0937 100644 --- a/packages/base/src/index.mjs +++ b/packages/base/src/index.mjs @@ -411,8 +411,6 @@ const rules = createConfig({ // Function expressions that are not contained within plain objects // or are not arguments to functions or methods ':not(Property, NewExpression, CallExpression) > FunctionExpression', - // Exported variables - 'ExportNamedDeclaration:has(> VariableDeclaration)', ], }, ], From 58469e5fb38ad7477b671f1b2a0067badcac94d8 Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Wed, 1 Apr 2026 12:30:52 -0600 Subject: [PATCH 06/10] Update name --- packages/typescript/src/index.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/typescript/src/index.mjs b/packages/typescript/src/index.mjs index c9cdfe10..8f39eb69 100644 --- a/packages/typescript/src/index.mjs +++ b/packages/typescript/src/index.mjs @@ -32,7 +32,7 @@ function collectExistingRuleOptions(ruleName, configs) { }); } -const existingJsdocRuleOptions = collectExistingRuleOptions( +const baseJsdocRuleOptions = collectExistingRuleOptions( 'jsdoc/require-jsdoc', base, ); @@ -256,9 +256,9 @@ const config = createConfig({ 'jsdoc/require-jsdoc': [ 'error', { - require: existingJsdocRuleOptions[0].require, + require: baseJsdocRuleOptions[0].require, contexts: [ - ...existingJsdocRuleOptions[0].contexts, + ...baseJsdocRuleOptions[0].contexts, // Type interfaces that are not defined within `declare` blocks ':not(TSModuleBlock) > TSInterfaceDeclaration', // Type aliases that are not defined within `declare` blocks From bc6ebecbae85db4016cc68a243cb8f66d765344a Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Wed, 1 Apr 2026 12:32:11 -0600 Subject: [PATCH 07/10] Update rules snapshots --- packages/base/rules-snapshot.json | 3 +-- packages/typescript/rules-snapshot.json | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/base/rules-snapshot.json b/packages/base/rules-snapshot.json index 746413c6..a0020da6 100644 --- a/packages/base/rules-snapshot.json +++ b/packages/base/rules-snapshot.json @@ -186,8 +186,7 @@ }, "contexts": [ ":not(Property, NewExpression, CallExpression) > ArrowFunctionExpression", - ":not(Property, NewExpression, CallExpression) > FunctionExpression", - "ExportNamedDeclaration:has(> VariableDeclaration)" + ":not(Property, NewExpression, CallExpression) > FunctionExpression" ] } ], diff --git a/packages/typescript/rules-snapshot.json b/packages/typescript/rules-snapshot.json index 03f0ce64..962eb0aa 100644 --- a/packages/typescript/rules-snapshot.json +++ b/packages/typescript/rules-snapshot.json @@ -206,7 +206,6 @@ "contexts": [ ":not(Property, NewExpression, CallExpression) > ArrowFunctionExpression", ":not(Property, NewExpression, CallExpression) > FunctionExpression", - "ExportNamedDeclaration:has(> VariableDeclaration)", ":not(TSModuleBlock) > TSInterfaceDeclaration", ":not(TSModuleBlock) > TSTypeAliasDeclaration", "TSEnumDeclaration" @@ -265,6 +264,7 @@ "no-use-before-define": "off", "no-useless-constructor": "off", "no-var": "error", + "no-with": "off", "prefer-const": "error", "prefer-promise-reject-errors": "off", "prefer-rest-params": "error", From ec0cee3f65b1c8680fb637adf570cf974cc66482 Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Wed, 1 Apr 2026 12:40:31 -0600 Subject: [PATCH 08/10] Revert change to explicit-function-return-type --- packages/typescript/rules-snapshot.json | 6 +----- packages/typescript/src/index.mjs | 7 +------ 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/packages/typescript/rules-snapshot.json b/packages/typescript/rules-snapshot.json index b8cbf629..6a8d3d56 100644 --- a/packages/typescript/rules-snapshot.json +++ b/packages/typescript/rules-snapshot.json @@ -6,10 +6,7 @@ "@typescript-eslint/consistent-type-definitions": ["error", "type"], "@typescript-eslint/consistent-type-exports": "error", "@typescript-eslint/default-param-last": "error", - "@typescript-eslint/explicit-function-return-type": [ - "error", - { "allowExpressions": true } - ], + "@typescript-eslint/explicit-function-return-type": "error", "@typescript-eslint/naming-convention": [ "error", { @@ -262,7 +259,6 @@ "no-use-before-define": "off", "no-useless-constructor": "off", "no-var": "error", - "no-with": "off", "prefer-const": "error", "prefer-promise-reject-errors": "off", "prefer-rest-params": "error", diff --git a/packages/typescript/src/index.mjs b/packages/typescript/src/index.mjs index c9f022aa..993f676c 100644 --- a/packages/typescript/src/index.mjs +++ b/packages/typescript/src/index.mjs @@ -73,12 +73,7 @@ const config = createConfig({ '@typescript-eslint/array-type': 'error', '@typescript-eslint/consistent-type-assertions': 'error', '@typescript-eslint/consistent-type-definitions': ['error', 'type'], - '@typescript-eslint/explicit-function-return-type': [ - 'error', - { - allowExpressions: true, - }, - ], + '@typescript-eslint/explicit-function-return-type': 'error', '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-namespace': [ 'error', From bd1d326ffcf3ae6c618d81cb35a9e2b507bab975 Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Wed, 1 Apr 2026 12:43:32 -0600 Subject: [PATCH 09/10] Tweak changelog --- packages/typescript/CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/typescript/CHANGELOG.md b/packages/typescript/CHANGELOG.md index 86b5e901..704af2bd 100644 --- a/packages/typescript/CHANGELOG.md +++ b/packages/typescript/CHANGELOG.md @@ -10,9 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Update `jsdoc/require-jsdoc` to loosen requirements for various kinds of symbols ([#433](https://github.com/MetaMask/eslint-config/pull/433)) - - Instead of requiring JSDoc for all arrow functions and function expressions, require only arrow functions not contained within plain objects or are not arguments to functions or methods. - - Instead of requiring JSDoc for all interfaces or type aliases, require only those that do not appear in declare blocks (ambient declarations). - - Instead of requiring JSDoc for all object types in return types, require only object types in "root" types. + - Require JSDoc not for all arrow functions, but only those not contained within plain objects or are not arguments to functions or methods. + - Require JSDoc not for all interfaces or type aliases, but only those that do not appear in declare blocks (ambient declarations). + - Require JSDoc not for all object types in return types, but only those in "root" types. ## [15.0.0] From 140fdf58e0303b1902fe22c150cc018426b82c60 Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Wed, 1 Apr 2026 12:46:39 -0600 Subject: [PATCH 10/10] Add this back to satisfy the config validation --- packages/typescript/rules-snapshot.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/typescript/rules-snapshot.json b/packages/typescript/rules-snapshot.json index 6a8d3d56..7ef9fd13 100644 --- a/packages/typescript/rules-snapshot.json +++ b/packages/typescript/rules-snapshot.json @@ -259,6 +259,7 @@ "no-use-before-define": "off", "no-useless-constructor": "off", "no-var": "error", + "no-with": "off", "prefer-const": "error", "prefer-promise-reject-errors": "off", "prefer-rest-params": "error",