diff --git a/packages/base/CHANGELOG.md b/packages/base/CHANGELOG.md index 400d12cf..178207db 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. + ## [15.0.0] ### Changed @@ -15,12 +20,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - New things that now require documentation are: - Arrow functions - Class declarations - - TypeScript enum declarations - Function expressions - - TypeScript interface declarations - Method definitions - - TypeScript type alias declarations - - TypeScript property signatures - **BREAKING:** Convert various rules from `warn` to `error` ([#424](https://github.com/MetaMask/eslint-config/pull/424)) - The rules impacted are: - `promise/no-callback-in-promise` diff --git a/packages/base/rules-snapshot.json b/packages/base/rules-snapshot.json index 89238702..5995c5ee 100644 --- a/packages/base/rules-snapshot.json +++ b/packages/base/rules-snapshot.json @@ -180,17 +180,13 @@ "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" ] } ], diff --git a/packages/base/src/index.mjs b/packages/base/src/index.mjs index f4bfd3da..88f33e84 100644 --- a/packages/base/src/index.mjs +++ b/packages/base/src/index.mjs @@ -397,17 +397,20 @@ 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', ], }, ], diff --git a/packages/typescript/CHANGELOG.md b/packages/typescript/CHANGELOG.md index 2c1ac42a..704af2bd 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)) + - 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] ### Changed @@ -17,6 +24,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **BREAKING:** Update type import specifier rules ([#381](https://github.com/MetaMask/eslint-config/pull/381)) - `@typescript-eslint/consistent-type-imports` has been replaced with `import-x/consistent-type-specifier-style` - The rule now prefers "top-level" type imports over inline. e.g. `import type { a } from 'x'` over `import { type a } from 'x'` +- **BREAKING:** Update `jsdoc/require-jsdoc` to require documentation for more things ([#394](https://github.com/MetaMask/eslint-config/pull/394)) + - New things that now require documentation are: + - Arrow functions + - Class declarations + - Function expressions + - Method definitions + - TypeScript enum declarations + - TypeScript interface declarations + - TypeScript type alias declarations + - TypeScript property signatures - Disable `@typescript-eslint/no-unnecessary-type-arguments` ([#426](https://github.com/MetaMask/eslint-config/pull/426)) - We decided that "unnecessary" type arguments make types easier to read sometimes, so we should allow them. - Disable `promise/valid-params` because it's redundant in type-checked projects ([#425](https://github.com/MetaMask/eslint-config/pull/425)) diff --git a/packages/typescript/rules-snapshot.json b/packages/typescript/rules-snapshot.json index a153a46d..7ef9fd13 100644 --- a/packages/typescript/rules-snapshot.json +++ b/packages/typescript/rules-snapshot.json @@ -190,7 +190,23 @@ "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", + ":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 85297a35..993f676c 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 baseJsdocRuleOptions = collectExistingRuleOptions( + 'jsdoc/require-jsdoc', + base, +); + const config = createConfig({ name: '@metamask/eslint-config-typescript', @@ -225,6 +256,24 @@ const config = createConfig({ // Use TypeScript types rather than JSDoc types. 'jsdoc/no-types': 'error', + // Extend rule defined in base config to require JSDoc for + // TypeScript-specific symbols. + 'jsdoc/require-jsdoc': [ + 'error', + { + require: baseJsdocRuleOptions[0].require, + 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 + ':not(TSModuleBlock) > TSTypeAliasDeclaration', + // Enums + 'TSEnumDeclaration', + ], + }, + ], + // These all conflict with `jsdoc/no-types`. 'jsdoc/require-param-type': 'off', 'jsdoc/require-property-type': 'off',