From 12fca71fdc7761d4affb52bf5fe4be25eddabc5b Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Wed, 31 Jul 2024 04:09:10 +0800 Subject: [PATCH] fix(`check-template-names`): check for param and returns tags; fixes #1286 --- docs/rules/check-template-names.md | 12 ++++ src/rules/checkTemplateNames.js | 80 +++++++++++++-------- test/rules/assertions/checkTemplateNames.js | 18 +++++ 3 files changed, 81 insertions(+), 29 deletions(-) diff --git a/docs/rules/check-template-names.md b/docs/rules/check-template-names.md index a990ca89..996895d0 100644 --- a/docs/rules/check-template-names.md +++ b/docs/rules/check-template-names.md @@ -315,5 +315,17 @@ export default class { zeroValue: NumType; add: (x: NumType, y: NumType) => NumType; } + +/** + * Uses the provided callback to group the given array into the keys of a map. + * Based on the array grouping proposal: https://github.com/tc39/proposal-array-grouping/ + * + * @template T + * @param {T[]} array + * @param {(value: T, index: number) => string} callbackFn + * @returns {Map} + */ +export function mapGroupBy(array, callbackFn) { +} ```` diff --git a/src/rules/checkTemplateNames.js b/src/rules/checkTemplateNames.js index 455c97da..28b2c180 100644 --- a/src/rules/checkTemplateNames.js +++ b/src/rules/checkTemplateNames.js @@ -19,18 +19,60 @@ export default iterateJsdoc(({ const templateTags = utils.getTags('template'); const usedNames = new Set(); + /** + * @param {string} potentialType + */ + const checkForUsedTypes = (potentialType) => { + let parsedType; + try { + parsedType = mode === 'permissive' ? + tryParseType(/** @type {string} */ (potentialType)) : + parseType(/** @type {string} */ (potentialType), mode); + } catch { + return; + } + + traverse(parsedType, (nde) => { + const { + type, + value, + } = /** @type {import('jsdoc-type-pratt-parser').NameResult} */ (nde); + if (type === 'JsdocTypeName' && (/^[A-Z]$/).test(value)) { + usedNames.add(value); + } + }); + }; + /** * @param {import('@typescript-eslint/types').TSESTree.FunctionDeclaration| * import('@typescript-eslint/types').TSESTree.ClassDeclaration| * import('@typescript-eslint/types').TSESTree.TSInterfaceDeclaration| * import('@typescript-eslint/types').TSESTree.TSTypeAliasDeclaration} aliasDeclaration + * @param {boolean} [checkParamsAndReturns] */ - const checkParameters = (aliasDeclaration) => { + const checkParameters = (aliasDeclaration, checkParamsAndReturns) => { /* c8 ignore next -- Guard */ const {params} = aliasDeclaration.typeParameters ?? {params: []}; for (const {name: {name}} of params) { usedNames.add(name); } + if (checkParamsAndReturns) { + const paramName = /** @type {string} */ (utils.getPreferredTagName({ + tagName: 'param', + })); + const paramTags = utils.getTags(paramName); + for (const paramTag of paramTags) { + checkForUsedTypes(paramTag.type); + } + + const returnsName = /** @type {string} */ (utils.getPreferredTagName({ + tagName: 'returns', + })); + const returnsTags = utils.getTags(returnsName); + for (const returnsTag of returnsTags) { + checkForUsedTypes(returnsTag.type); + } + } for (const tag of templateTags) { const {name} = tag; const names = name.split(/,\s*/); @@ -53,16 +95,20 @@ export default iterateJsdoc(({ case 'ExportDefaultDeclaration': case 'ExportNamedDeclaration': switch (nde.declaration?.type) { - case 'ClassDeclaration': case 'FunctionDeclaration': + checkParameters(nde.declaration, true); + break; + case 'ClassDeclaration': case 'TSTypeAliasDeclaration': case 'TSInterfaceDeclaration': checkParameters(nde.declaration); break; } break; - case 'ClassDeclaration': case 'FunctionDeclaration': + checkParameters(nde, true); + break; + case 'ClassDeclaration': case 'TSTypeAliasDeclaration': case 'TSInterfaceDeclaration': checkParameters(nde); @@ -76,37 +122,13 @@ export default iterateJsdoc(({ return; } - /** - * @param {string} potentialType - */ - const checkForUsedTypes = (potentialType) => { - let parsedType; - try { - parsedType = mode === 'permissive' ? - tryParseType(/** @type {string} */ (potentialType)) : - parseType(/** @type {string} */ (potentialType), mode); - } catch { - return; - } - - traverse(parsedType, (nde) => { - const { - type, - value, - } = /** @type {import('jsdoc-type-pratt-parser').NameResult} */ (nde); - if (type === 'JsdocTypeName' && (/^[A-Z]$/).test(value)) { - usedNames.add(value); - } - }); - }; - const potentialTypedefType = typedefTags[0].type; checkForUsedTypes(potentialTypedefType); - const tagName = /** @type {string} */ (utils.getPreferredTagName({ + const propertyName = /** @type {string} */ (utils.getPreferredTagName({ tagName: 'property', })); - const propertyTags = utils.getTags(tagName); + const propertyTags = utils.getTags(propertyName); for (const propertyTag of propertyTags) { checkForUsedTypes(propertyTag.type); } diff --git a/test/rules/assertions/checkTemplateNames.js b/test/rules/assertions/checkTemplateNames.js index 87a05a1c..639448ad 100644 --- a/test/rules/assertions/checkTemplateNames.js +++ b/test/rules/assertions/checkTemplateNames.js @@ -606,5 +606,23 @@ export default { parser: typescriptEslintParser }, }, + { + code: ` + /** + * Uses the provided callback to group the given array into the keys of a map. + * Based on the array grouping proposal: https://github.com/tc39/proposal-array-grouping/ + * + * @template T + * @param {T[]} array + * @param {(value: T, index: number) => string} callbackFn + * @returns {Map} + */ + export function mapGroupBy(array, callbackFn) { + } + `, + languageOptions: { + parser: typescriptEslintParser + }, + }, ], };