diff --git a/.README/rules/require-param.md b/.README/rules/require-param.md index 6fc671384..cf85459a9 100644 --- a/.README/rules/require-param.md +++ b/.README/rules/require-param.md @@ -369,6 +369,11 @@ documentation). Defaults to `true`. Set to `true` if you wish to expect documentation of properties on objects supplied as default values. Defaults to `false`. +### `ignoreWhenAllParamsMissing` + +Set to `true` to ignore reporting when all params are missing. Defaults to +`false`. + ## Context and settings | | | @@ -377,7 +382,7 @@ supplied as default values. Defaults to `false`. | Tags | `param` | | Aliases | `arg`, `argument` | |Recommended | true| -| Options |`autoIncrementBase`, `checkConstructors`, `checkDestructured`, `checkDestructuredRoots`, `checkGetters`, `checkRestProperty`, `checkSetters`, `checkTypesPattern`, `contexts`, `enableFixer`, `enableRestElementFixer`, `enableRootFixer`, `exemptedBy`, `unnamedRootBase`, `useDefaultObjectProperties`| +| Options |`autoIncrementBase`, `checkConstructors`, `checkDestructured`, `checkDestructuredRoots`, `checkGetters`, `checkRestProperty`, `checkSetters`, `checkTypesPattern`, `contexts`, `enableFixer`, `enableRestElementFixer`, `enableRootFixer`, `exemptedBy`, `ignoreWhenAllParamsMissing`, `unnamedRootBase`, `useDefaultObjectProperties`| | Settings | `ignoreReplacesDocs`, `overrideReplacesDocs`, `augmentsExtendsReplacesDocs`, `implementsReplacesDocs`| ## Failing examples diff --git a/docs/rules/require-jsdoc.md b/docs/rules/require-jsdoc.md index 411f596f3..627b97dbf 100644 --- a/docs/rules/require-jsdoc.md +++ b/docs/rules/require-jsdoc.md @@ -1020,6 +1020,14 @@ export type LoginOptions = CmdOptions<{ }>; // "jsdoc/require-jsdoc": ["error"|"warn", {"publicOnly":{"ancestorsOnly":true},"contexts":["TSTypeAliasDeclaration","TSInterfaceDeclaration","TSMethodSignature","TSPropertySignature"]}] // Message: Missing JSDoc comment. + +type Props = { + variant: string +} + +export type { Props as ComponentProps }; +// "jsdoc/require-jsdoc": ["error"|"warn", {"publicOnly":{"esm":true},"require":{"FunctionDeclaration":true,"FunctionExpression":true,"ArrowFunctionExpression":true,"ClassDeclaration":true,"ClassExpression":true,"MethodDefinition":true},"contexts":["VariableDeclaration","TSTypeAliasDeclaration","TSPropertySignature","TSInterfaceDeclaration","TSMethodSignature","TSEnumDeclaration"],"enableFixer":true}] +// Message: Missing JSDoc comment. ```` diff --git a/docs/rules/require-param.md b/docs/rules/require-param.md index fa6006553..7e757b103 100644 --- a/docs/rules/require-param.md +++ b/docs/rules/require-param.md @@ -23,6 +23,7 @@ * [`checkDestructured`](#user-content-require-param-options-checkdestructured) * [`checkDestructuredRoots`](#user-content-require-param-options-checkdestructuredroots) * [`useDefaultObjectProperties`](#user-content-require-param-options-usedefaultobjectproperties) + * [`ignoreWhenAllParamsMissing`](#user-content-require-param-options-ignorewhenallparamsmissing) * [Context and settings](#user-content-require-param-context-and-settings) * [Failing examples](#user-content-require-param-failing-examples) * [Passing examples](#user-content-require-param-passing-examples) @@ -437,6 +438,13 @@ documentation). Defaults to `true`. Set to `true` if you wish to expect documentation of properties on objects supplied as default values. Defaults to `false`. + + +### ignoreWhenAllParamsMissing + +Set to `true` to ignore reporting when all params are missing. Defaults to +`false`. + ## Context and settings @@ -447,7 +455,7 @@ supplied as default values. Defaults to `false`. | Tags | `param` | | Aliases | `arg`, `argument` | |Recommended | true| -| Options |`autoIncrementBase`, `checkConstructors`, `checkDestructured`, `checkDestructuredRoots`, `checkGetters`, `checkRestProperty`, `checkSetters`, `checkTypesPattern`, `contexts`, `enableFixer`, `enableRestElementFixer`, `enableRootFixer`, `exemptedBy`, `unnamedRootBase`, `useDefaultObjectProperties`| +| Options |`autoIncrementBase`, `checkConstructors`, `checkDestructured`, `checkDestructuredRoots`, `checkGetters`, `checkRestProperty`, `checkSetters`, `checkTypesPattern`, `contexts`, `enableFixer`, `enableRestElementFixer`, `enableRootFixer`, `exemptedBy`, `ignoreWhenAllParamsMissing`, `unnamedRootBase`, `useDefaultObjectProperties`| | Settings | `ignoreReplacesDocs`, `overrideReplacesDocs`, `augmentsExtendsReplacesDocs`, `implementsReplacesDocs`| @@ -1162,6 +1170,14 @@ class A { } } // Message: Missing JSDoc @param "root1" declaration. + +/** + * Some desc. + * @param a + */ +function quux (a, b) {} +// "jsdoc/require-param": ["error"|"warn", {"ignoreWhenAllParamsMissing":true}] +// Message: Missing JSDoc @param "b" declaration. ```` @@ -1813,5 +1829,11 @@ const inner = (c: number, d: string): void => { console.log(d); }; // Settings: {"jsdoc":{"contexts":["VariableDeclaration"]}} + +/** + * Some desc. + */ +function quux (a, b) {} +// "jsdoc/require-param": ["error"|"warn", {"ignoreWhenAllParamsMissing":true}] ```` diff --git a/docs/rules/require-returns-check.md b/docs/rules/require-returns-check.md index 2ff2b6c19..fab817d67 100644 --- a/docs/rules/require-returns-check.md +++ b/docs/rules/require-returns-check.md @@ -992,5 +992,17 @@ function foo() { } } } + +/** + * @returns {number} + */ +function foo() { + for (;;) { + const n = Math.random(); + if (n < 0.5) { + return n; + } + } +} ```` diff --git a/package.json b/package.json index dc5737166..be0a963a3 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@es-joy/jsdoc-eslint-parser": "^0.21.1", "@hkdobrev/run-if-changed": "^0.6.0", "@semantic-release/commit-analyzer": "^13.0.0", - "@semantic-release/github": "^10.1.4", + "@semantic-release/github": "^11.0.0", "@semantic-release/npm": "^12.0.1", "@types/chai": "^4.3.17", "@types/debug": "^4.1.12", @@ -67,7 +67,7 @@ "open-editor": "^5.0.0", "replace": "^1.2.2", "rimraf": "^5.0.7", - "semantic-release": "^24.0.0", + "semantic-release": "^24.1.1", "typescript": "5.5.x", "typescript-eslint": "^8.1.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3f804c6fa..ec572d128 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,13 +74,13 @@ importers: version: 0.6.0(typescript@5.5.3) '@semantic-release/commit-analyzer': specifier: ^13.0.0 - version: 13.0.0(semantic-release@24.0.0(typescript@5.5.3)) + version: 13.0.0(semantic-release@24.1.1(typescript@5.5.3)) '@semantic-release/github': - specifier: ^10.1.4 - version: 10.1.4(semantic-release@24.0.0(typescript@5.5.3)) + specifier: ^11.0.0 + version: 11.0.0(semantic-release@24.1.1(typescript@5.5.3)) '@semantic-release/npm': specifier: ^12.0.1 - version: 12.0.1(semantic-release@24.0.0(typescript@5.5.3)) + version: 12.0.1(semantic-release@24.1.1(typescript@5.5.3)) '@types/chai': specifier: ^4.3.17 version: 4.3.17 @@ -187,8 +187,8 @@ importers: specifier: ^5.0.7 version: 5.0.7 semantic-release: - specifier: ^24.0.0 - version: 24.0.0(typescript@5.5.3) + specifier: ^24.1.1 + version: 24.1.1(typescript@5.5.3) typescript: specifier: 5.5.x version: 5.5.3 @@ -1306,6 +1306,12 @@ packages: peerDependencies: semantic-release: '>=20.1.0' + '@semantic-release/github@11.0.0': + resolution: {integrity: sha512-Uon6G6gJD8U1JNvPm7X0j46yxNRJ8Ui6SgK4Zw5Ktu8RgjEft3BGn+l/RX1TTzhhO3/uUcKuqM+/9/ETFxWS/Q==} + engines: {node: '>=20.8.1'} + peerDependencies: + semantic-release: '>=24.1.0' + '@semantic-release/npm@12.0.1': resolution: {integrity: sha512-/6nntGSUGK2aTOI0rHPwY3ZjgY9FkXmEHbW9Kr+62NVOsyqpKKeP0lrCH+tphv+EsNdJNmqqwijTEnVWUMQ2Nw==} engines: {node: '>=20.8.1'} @@ -3156,6 +3162,10 @@ packages: resolution: {integrity: sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==} engines: {node: ^16.14.0 || >=18.0.0} + hosted-git-info@8.0.0: + resolution: {integrity: sha512-4nw3vOVR+vHUOT8+U4giwe2tcGv+R3pwwRidUe67DoMBTjhrfr6rZYJVVwdkBE+Um050SG+X9tf0Jo4fOpn01w==} + engines: {node: ^18.17.0 || >=20.5.0} + html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} @@ -4590,8 +4600,8 @@ packages: resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==} engines: {node: ^14.0.0 || >=16.0.0} - semantic-release@24.0.0: - resolution: {integrity: sha512-v46CRPw+9eI3ZuYGF2oAjqPqsfbnfFTwLBgQsv/lch4goD09ytwOTESMN4QIrx/wPLxUGey60/NMx+ANQtWRsA==} + semantic-release@24.1.1: + resolution: {integrity: sha512-4Ax2GxD411jUe9IdhOjMLuN+6wAj+aKjvOGngByrpD/iKL+UKN/2puQglhyI4gxNyy9XzEBMzBwbqpnEwbXGEg==} engines: {node: '>=20.8.1'} hasBin: true @@ -6856,7 +6866,7 @@ snapshots: '@sec-ant/readable-stream@0.4.1': {} - '@semantic-release/commit-analyzer@13.0.0(semantic-release@24.0.0(typescript@5.5.3))': + '@semantic-release/commit-analyzer@13.0.0(semantic-release@24.1.1(typescript@5.5.3))': dependencies: conventional-changelog-angular: 8.0.0 conventional-changelog-writer: 8.0.0 @@ -6866,13 +6876,13 @@ snapshots: import-from-esm: 1.3.3 lodash-es: 4.17.21 micromatch: 4.0.7 - semantic-release: 24.0.0(typescript@5.5.3) + semantic-release: 24.1.1(typescript@5.5.3) transitivePeerDependencies: - supports-color '@semantic-release/error@4.0.0': {} - '@semantic-release/github@10.1.4(semantic-release@24.0.0(typescript@5.5.3))': + '@semantic-release/github@10.1.4(semantic-release@24.1.1(typescript@5.5.3))': dependencies: '@octokit/core': 6.1.1 '@octokit/plugin-paginate-rest': 11.3.0(@octokit/core@6.1.1) @@ -6889,12 +6899,34 @@ snapshots: lodash-es: 4.17.21 mime: 4.0.1 p-filter: 4.1.0 - semantic-release: 24.0.0(typescript@5.5.3) + semantic-release: 24.1.1(typescript@5.5.3) url-join: 5.0.0 transitivePeerDependencies: - supports-color - '@semantic-release/npm@12.0.1(semantic-release@24.0.0(typescript@5.5.3))': + '@semantic-release/github@11.0.0(semantic-release@24.1.1(typescript@5.5.3))': + dependencies: + '@octokit/core': 6.1.1 + '@octokit/plugin-paginate-rest': 11.3.0(@octokit/core@6.1.1) + '@octokit/plugin-retry': 7.1.0(@octokit/core@6.1.1) + '@octokit/plugin-throttling': 9.1.0(@octokit/core@6.1.1) + '@semantic-release/error': 4.0.0 + aggregate-error: 5.0.0 + debug: 4.3.6(supports-color@8.1.1) + dir-glob: 3.0.1 + globby: 14.0.0 + http-proxy-agent: 7.0.0 + https-proxy-agent: 7.0.2 + issue-parser: 7.0.0 + lodash-es: 4.17.21 + mime: 4.0.1 + p-filter: 4.1.0 + semantic-release: 24.1.1(typescript@5.5.3) + url-join: 5.0.0 + transitivePeerDependencies: + - supports-color + + '@semantic-release/npm@12.0.1(semantic-release@24.1.1(typescript@5.5.3))': dependencies: '@semantic-release/error': 4.0.0 aggregate-error: 5.0.0 @@ -6907,11 +6939,11 @@ snapshots: rc: 1.2.8 read-pkg: 9.0.1 registry-auth-token: 5.0.2 - semantic-release: 24.0.0(typescript@5.5.3) + semantic-release: 24.1.1(typescript@5.5.3) semver: 7.6.3 tempy: 3.1.0 - '@semantic-release/release-notes-generator@14.0.1(semantic-release@24.0.0(typescript@5.5.3))': + '@semantic-release/release-notes-generator@14.0.1(semantic-release@24.1.1(typescript@5.5.3))': dependencies: conventional-changelog-angular: 8.0.0 conventional-changelog-writer: 8.0.0 @@ -6923,7 +6955,7 @@ snapshots: into-stream: 7.0.0 lodash-es: 4.17.21 read-package-up: 11.0.0 - semantic-release: 24.0.0(typescript@5.5.3) + semantic-release: 24.1.1(typescript@5.5.3) transitivePeerDependencies: - supports-color @@ -9250,6 +9282,10 @@ snapshots: dependencies: lru-cache: 10.2.0 + hosted-git-info@8.0.0: + dependencies: + lru-cache: 10.2.0 + html-escaper@2.0.2: {} htmlparser2@3.10.1: @@ -10651,24 +10687,24 @@ snapshots: refa: 0.12.1 regexp-ast-analysis: 0.7.1 - semantic-release@24.0.0(typescript@5.5.3): + semantic-release@24.1.1(typescript@5.5.3): dependencies: - '@semantic-release/commit-analyzer': 13.0.0(semantic-release@24.0.0(typescript@5.5.3)) + '@semantic-release/commit-analyzer': 13.0.0(semantic-release@24.1.1(typescript@5.5.3)) '@semantic-release/error': 4.0.0 - '@semantic-release/github': 10.1.4(semantic-release@24.0.0(typescript@5.5.3)) - '@semantic-release/npm': 12.0.1(semantic-release@24.0.0(typescript@5.5.3)) - '@semantic-release/release-notes-generator': 14.0.1(semantic-release@24.0.0(typescript@5.5.3)) + '@semantic-release/github': 10.1.4(semantic-release@24.1.1(typescript@5.5.3)) + '@semantic-release/npm': 12.0.1(semantic-release@24.1.1(typescript@5.5.3)) + '@semantic-release/release-notes-generator': 14.0.1(semantic-release@24.1.1(typescript@5.5.3)) aggregate-error: 5.0.0 cosmiconfig: 9.0.0(typescript@5.5.3) debug: 4.3.6(supports-color@8.1.1) env-ci: 11.0.0 - execa: 9.1.0 + execa: 9.3.0 figures: 6.1.0 find-versions: 6.0.0 get-stream: 6.0.1 git-log-parser: 1.2.0 hook-std: 3.0.0 - hosted-git-info: 7.0.1 + hosted-git-info: 8.0.0 import-from-esm: 1.3.3 lodash-es: 4.17.21 marked: 12.0.1 diff --git a/src/exportParser.js b/src/exportParser.js index b9dff88cf..85957e54c 100644 --- a/src/exportParser.js +++ b/src/exportParser.js @@ -14,7 +14,7 @@ const debug = debugModule('requireExportJsdoc'); /** * @typedef {{ * type?: string, - * value?: ValueObject|import('eslint').Rule.Node, + * value?: ValueObject|import('eslint').Rule.Node|import('@typescript-eslint/types').TSESTree.Node, * props: { * [key: string]: CreatedNode|null, * }, @@ -93,7 +93,7 @@ const getIdentifier = function (node, globals, scope, opts) { * @callback CreateSymbol * @param {import('eslint').Rule.Node|null} node * @param {CreatedNode} globals - * @param {import('eslint').Rule.Node|null} value + * @param {import('eslint').Rule.Node|import('@typescript-eslint/types').TSESTree.Node|null} value * @param {CreatedNode} [scope] * @param {boolean|SymbolOptions} [isGlobal] * @returns {CreatedNode|null} @@ -112,7 +112,7 @@ let createSymbol; // eslint-disable-line prefer-const /** * - * @param {import('eslint').Rule.Node} node + * @param {import('eslint').Rule.Node|import('@typescript-eslint/types').TSESTree.Node} node * @param {CreatedNode} globals * @param {CreatedNode} scope * @param {SymbolOptions} [opt] @@ -177,13 +177,10 @@ const getSymbol = function (node, globals, scope, opt) { ); } - /* c8 ignore next 7 -- No longer needed? */ - // @ts-expect-error TS OK + /* c8 ignore next 4 -- No longer needed? */ case 'TSTypeAliasDeclaration': - // @ts-expect-error TS OK // Fallthrough case 'TSEnumDeclaration': - // @ts-expect-error TS OK case 'TSInterfaceDeclaration': case 'ClassDeclaration': case 'FunctionExpression': case 'FunctionDeclaration': @@ -473,7 +470,7 @@ const initVariables = function (node, globals, opts) { /** * Populates variable maps using AST - * @param {import('eslint').Rule.Node} node + * @param {import('eslint').Rule.Node|import('@typescript-eslint/types').TSESTree.Node} node * @param {CreatedNode} globals * @param {import('./rules/requireJsdoc.js').RequireJsdocOpts} opt * @param {true} [isExport] @@ -543,6 +540,7 @@ const mapVariables = function (node, globals, opt, isExport) { break; } + case 'TSTypeAliasDeclaration': case 'FunctionDeclaration': { /* c8 ignore next 10 */ if (/** @type {import('estree').Identifier} */ (node.id).type === 'Identifier') { @@ -655,9 +653,9 @@ const mapVariables = function (node, globals, opt, isExport) { * * @param {import('eslint').Rule.Node} node * @param {CreatedNode|ValueObject|string|undefined| - * import('eslint').Rule.Node} block + * import('eslint').Rule.Node|import('@typescript-eslint/types').TSESTree.Node} block * @param {(CreatedNode|ValueObject|string| - * import('eslint').Rule.Node)[]} [cache] + * import('eslint').Rule.Node|import('@typescript-eslint/types').TSESTree.Node)[]} [cache] * @returns {boolean} */ const findNode = function (node, block, cache) { diff --git a/src/rules/requireParam.js b/src/rules/requireParam.js index 33f50db77..28e0b66ee 100644 --- a/src/rules/requireParam.js +++ b/src/rules/requireParam.js @@ -60,6 +60,7 @@ export default iterateJsdoc(({ 'root', ], useDefaultObjectProperties = false, + ignoreWhenAllParamsMissing = false, } = context.options[0] || {}; const preferredTagName = /** @type {string} */ (utils.getPreferredTagName({ @@ -83,6 +84,10 @@ export default iterateJsdoc(({ * }[]} */ (utils.getJsdocTagsDeep(preferredTagName)); + if (ignoreWhenAllParamsMissing && !jsdocParameterNames.length) { + return; + } + const shallowJsdocParameterNames = jsdocParameterNames.filter((tag) => { return !tag.name.includes('.'); }).map((tag, idx) => { @@ -571,6 +576,9 @@ export default iterateJsdoc(({ }, type: 'array', }, + ignoreWhenAllParamsMissing: { + type: 'boolean', + }, unnamedRootBase: { items: { type: 'string', diff --git a/src/utils/hasReturnValue.js b/src/utils/hasReturnValue.js index 1809cabc1..a481ee17f 100644 --- a/src/utils/hasReturnValue.js +++ b/src/utils/hasReturnValue.js @@ -177,8 +177,13 @@ const allBrancheshaveReturnValues = (node, promFilter) => { } // Fallthrough - case 'LabeledStatement': case 'ForStatement': + if (node.test === null) { + // If this is an infinite loop, we assume only one branch + // is needed to provide a return + return hasReturnValue(node.body, false, promFilter); + } + case 'LabeledStatement': case 'ForInStatement': case 'ForOfStatement': case 'WithStatement': { diff --git a/test/rules/assertions/requireJsdoc.js b/test/rules/assertions/requireJsdoc.js index e8ab9b0f8..9991abf9b 100644 --- a/test/rules/assertions/requireJsdoc.js +++ b/test/rules/assertions/requireJsdoc.js @@ -4196,6 +4196,57 @@ function quux (foo) { parser: typescriptEslintParser, }, }, + { + code: ` + type Props = { + variant: string + } + + export type { Props as ComponentProps }; + `, + errors: [ + { + line: 2, + message: 'Missing JSDoc comment.', + }, + ], + languageOptions: { + parser: typescriptEslintParser, + }, + options: [ + { + publicOnly: { esm: true }, + require: { + FunctionDeclaration: true, + FunctionExpression: true, + ArrowFunctionExpression: true, + ClassDeclaration: true, + ClassExpression: true, + MethodDefinition: true, + }, + contexts: [ + "VariableDeclaration", + "TSTypeAliasDeclaration", + // Encourage documenting React prop types + "TSPropertySignature", + "TSInterfaceDeclaration", + "TSMethodSignature", + "TSEnumDeclaration" + ], + enableFixer: true, + }, + ], + output: ` + /** + * + */ + type Props = { + variant: string + } + + export type { Props as ComponentProps }; + `, + }, ], valid: [ { @@ -6312,6 +6363,6 @@ function quux (foo) { } } ], - } + }, ], }; diff --git a/test/rules/assertions/requireParam.js b/test/rules/assertions/requireParam.js index 666cb4ab0..71f8c32dd 100644 --- a/test/rules/assertions/requireParam.js +++ b/test/rules/assertions/requireParam.js @@ -2527,6 +2527,33 @@ export default { } `, }, + { + code: ` + /** + * Some desc. + * @param a + */ + function quux (a, b) {} + `, + errors: [ + { + message: 'Missing JSDoc @param "b" declaration.', + }, + ], + options: [ + { + ignoreWhenAllParamsMissing: true, + } + ], + output: ` + /** + * Some desc. + * @param a + * @param b + */ + function quux (a, b) {} + `, + }, ], valid: [ { @@ -3604,5 +3631,18 @@ export default { } }, }, + { + code: ` + /** + * Some desc. + */ + function quux (a, b) {} + `, + options: [ + { + ignoreWhenAllParamsMissing: true, + } + ], + }, ], }; diff --git a/test/rules/assertions/requireReturnsCheck.js b/test/rules/assertions/requireReturnsCheck.js index 025d6ab12..f95472812 100755 --- a/test/rules/assertions/requireReturnsCheck.js +++ b/test/rules/assertions/requireReturnsCheck.js @@ -1558,5 +1558,20 @@ export default { } `, }, + { + code: ` + /** + * @returns {number} + */ + function foo() { + for (;;) { + const n = Math.random(); + if (n < 0.5) { + return n; + } + } + } + `, + }, ], };