diff --git a/.babelrc.json b/.babelrc.json index 6f7519256..b4a7d5a8b 100644 --- a/.babelrc.json +++ b/.babelrc.json @@ -1,7 +1,5 @@ { "plugins": [ - "@babel/plugin-transform-flow-strip-types", - "@babel/plugin-syntax-class-properties", "add-module-exports" ], "presets": [ diff --git a/.eslintrc.json b/.eslintrc.json index 5a63b3dba..c1efd3633 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -22,6 +22,11 @@ } } ], + "settings": { + "jsdoc": { + "mode": "typescript" + } + }, "root": true, "rules": { "array-element-newline": 0, diff --git a/src/iterateJsdoc.js b/src/iterateJsdoc.js index 5b0f92d75..4830ecced 100644 --- a/src/iterateJsdoc.js +++ b/src/iterateJsdoc.js @@ -871,6 +871,7 @@ const makeReport = (context, commentNode) => { return report; }; +/* eslint-disable jsdoc/no-undefined-types -- Need to build this in; see https://www.typescriptlang.org/docs/handbook/utility-types.html */ /** * @typedef {ReturnType} Utils * @typedef {ReturnType} Settings @@ -888,6 +889,7 @@ const makeReport = (context, commentNode) => { * } * ) => any } JsdocVisitor */ +/* eslint-enable jsdoc/no-undefined-types -- Need to build this in */ const iterate = ( info, diff --git a/src/jsdocUtils.js b/src/jsdocUtils.js index 179f77122..498b04c0c 100644 --- a/src/jsdocUtils.js +++ b/src/jsdocUtils.js @@ -8,7 +8,9 @@ import { typeScriptTags, } from './tagNames'; -type ParserMode = "jsdoc"|"typescript"|"closure"; +/** + * @typedef {"jsdoc"|"typescript"|"closure"} ParserMode + */ let tagStructure; @@ -87,8 +89,11 @@ const flattenRoots = (params, root = '') => { }; }; -type T = string | [?string, T]; -const getPropertiesFromPropertySignature = (propSignature): T => { +/** + * @param {object} propSignature + * @returns {undefined|Array|string} + */ +const getPropertiesFromPropertySignature = (propSignature) => { if ( propSignature.type === 'TSIndexSignature' || propSignature.type === 'TSConstructSignatureDeclaration' || @@ -108,9 +113,14 @@ const getPropertiesFromPropertySignature = (propSignature): T => { return propSignature.key.name; }; +/** + * @param {object} functionNode + * @param {boolean} checkDefaultObjects + * @returns {Array} + */ const getFunctionParameterNames = ( - functionNode : Object, checkDefaultObjects: Boolean, -) : Array => { + functionNode, checkDefaultObjects, +) => { // eslint-disable-next-line complexity const getParamName = (param, isProperty) => { const hasLeftTypeAnnotation = 'left' in param && 'typeAnnotation' in param.left; @@ -266,6 +276,10 @@ const getFunctionParameterNames = ( }); }; +/** + * @param {Node} functionNode + * @returns {Integer} + */ const hasParams = (functionNode) => { // Should also check `functionNode.value.params` if supporting `MethodDefinition` return functionNode.params.length; @@ -274,8 +288,12 @@ const hasParams = (functionNode) => { /** * Gets all names of the target type, including those that refer to a path, e.g. * "@param foo; @param foo.bar". + * + * @param {object} jsdoc + * @param {string} targetTagName + * @returns {Array} */ -const getJsdocTagsDeep = (jsdoc : Object, targetTagName : string) : Array => { +const getJsdocTagsDeep = (jsdoc, targetTagName) => { const ret = []; for (const [ idx, @@ -301,6 +319,10 @@ const getJsdocTagsDeep = (jsdoc : Object, targetTagName : string) : Array { switch (mode) { case 'jsdoc': @@ -328,12 +350,19 @@ const getTagNamesForMode = (mode, context) => { } }; +/** + * @param context + * @param {ParserMode} mode + * @param {string} name + * @param {object} tagPreference + * @returns {string|object} + */ const getPreferredTagName = ( context, - mode : ParserMode, - name : string, - tagPreference : Object = {}, -) : string|Object => { + mode, + name, + tagPreference = {}, +) => { const prefValues = Object.values(tagPreference); if (prefValues.includes(name) || prefValues.some((prefVal) => { return prefVal && typeof prefVal === 'object' && prefVal.replacement === name; @@ -377,12 +406,19 @@ const getPreferredTagName = ( return name; }; +/** + * @param context + * @param {ParserMode} mode + * @param {string} name + * @param {Array} definedTags + * @returns {boolean} + */ const isValidTag = ( context, - mode : ParserMode, - name : string, - definedTags : Array, -) : boolean => { + mode, + name, + definedTags, +) => { const tagNames = getTagNamesForMode(mode, context); const validTagNames = Object.keys(tagNames).concat(Object.values(tagNames).flat()); @@ -392,15 +428,25 @@ const isValidTag = ( return allTags.includes(name); }; -const hasTag = (jsdoc : Object, targetTagName : string) : boolean => { +/** + * @param {object} jsdoc + * @param {string} targetTagName + * @returns {boolean} + */ +const hasTag = (jsdoc, targetTagName) => { const targetTagLower = targetTagName.toLowerCase(); - return jsdoc.tags.some((doc : Object) => { + return jsdoc.tags.some((doc) => { return doc.tag.toLowerCase() === targetTagLower; }); }; -const hasATag = (jsdoc : Object, targetTagNames : Array) : boolean => { +/** + * @param {object} jsdoc + * @param {Array} targetTagNames + * @returns {boolean} + */ +const hasATag = (jsdoc, targetTagNames) => { return targetTagNames.some((targetTagName) => { return hasTag(jsdoc, targetTagName); }); @@ -430,6 +476,11 @@ const hasDefinedTypeTag = (tag) => { return true; }; +/** + * @param map + * @param tag + * @returns {Map} + */ const ensureMap = (map, tag) => { if (!map.has(tag)) { map.set(tag, new Map()); @@ -438,6 +489,10 @@ const ensureMap = (map, tag) => { return map.get(tag); }; +/** + * @param structuredTags + * @param tagMap + */ const overrideTagStructure = (structuredTags, tagMap = tagStructure) => { for (const [ tag, @@ -479,6 +534,11 @@ const overrideTagStructure = (structuredTags, tagMap = tagStructure) => { } }; +/** + * @param mode + * @param structuredTags + * @returns {Map} + */ const getTagStructureForMode = (mode, structuredTags) => { const tagStruct = getDefaultTagStructureForMode(mode); @@ -491,18 +551,33 @@ const getTagStructureForMode = (mode, structuredTags) => { return tagStruct; }; +/** + * @param tag + * @param {Map} tagMap + * @returns {boolean} + */ const isNamepathDefiningTag = (tag, tagMap = tagStructure) => { const tagStruct = ensureMap(tagMap, tag); return tagStruct.get('nameContents') === 'namepath-defining'; }; +/** + * @param tag + * @param {Map} tagMap + * @returns {boolean} + */ const tagMustHaveTypePosition = (tag, tagMap = tagStructure) => { const tagStruct = ensureMap(tagMap, tag); return tagStruct.get('typeRequired'); }; +/** + * @param tag + * @param {Map} tagMap + * @returns {boolean} + */ const tagMightHaveTypePosition = (tag, tagMap = tagStructure) => { if (tagMustHaveTypePosition(tag, tagMap)) { return true; @@ -519,6 +594,11 @@ const namepathTypes = new Set([ 'namepath-defining', 'namepath-referencing', ]); +/** + * @param tag + * @param {Map} tagMap + * @returns {boolean} + */ const tagMightHaveNamePosition = (tag, tagMap = tagStructure) => { const tagStruct = ensureMap(tagMap, tag); @@ -527,28 +607,53 @@ const tagMightHaveNamePosition = (tag, tagMap = tagStructure) => { return ret === undefined ? true : Boolean(ret); }; +/** + * @param tag + * @param {Map} tagMap + * @returns {boolean} + */ const tagMightHaveNamepath = (tag, tagMap = tagStructure) => { const tagStruct = ensureMap(tagMap, tag); return namepathTypes.has(tagStruct.get('nameContents')); }; +/** + * @param tag + * @param {Map} tagMap + * @returns {boolean} + */ const tagMustHaveNamePosition = (tag, tagMap = tagStructure) => { const tagStruct = ensureMap(tagMap, tag); return tagStruct.get('nameRequired'); }; +/** + * @param tag + * @param {Map} tagMap + * @returns {boolean} + */ const tagMightHaveEitherTypeOrNamePosition = (tag, tagMap) => { return tagMightHaveTypePosition(tag, tagMap) || tagMightHaveNamepath(tag, tagMap); }; +/** + * @param tag + * @param {Map} tagMap + * @returns {boolean} + */ const tagMustHaveEitherTypeOrNamePosition = (tag, tagMap) => { const tagStruct = ensureMap(tagMap, tag); return tagStruct.get('typeOrNameRequired'); }; +/** + * @param tag + * @param {Map} tagMap + * @returns {boolean} + */ const tagMissingRequiredTypeOrNamepath = (tag, tagMap = tagStructure) => { const mustHaveTypePosition = tagMustHaveTypePosition(tag.tag, tagMap); const mightHaveTypePosition = tagMightHaveTypePosition(tag.tag, tagMap); @@ -1368,6 +1473,10 @@ const dropPathSegmentQuotes = (str) => { return str.replace(/\.(['"])(.*)\1/gu, '.$2'); }; +/** + * @param {string} name + * @returns {(otherPathName: string) => void} + */ const comparePaths = (name) => { return (otherPathName) => { return otherPathName === name || @@ -1375,11 +1484,21 @@ const comparePaths = (name) => { }; }; +/** + * @param {string} name + * @param {string} otherPathName + * @returns {boolean} + */ const pathDoesNotBeginWith = (name, otherPathName) => { return !name.startsWith(otherPathName) && !dropPathSegmentQuotes(name).startsWith(dropPathSegmentQuotes(otherPathName)); }; +/** + * @param {string} regexString + * @param {string} requiredFlags + * @returns {RegExp} + */ const getRegexFromString = (regexString, requiredFlags) => { const match = regexString.match(/^\/(.*)\/([gimyus]*)$/us); let flags = 'u'; diff --git a/src/rules/checkParamNames.js b/src/rules/checkParamNames.js index 534930fa3..94cced865 100644 --- a/src/rules/checkParamNames.js +++ b/src/rules/checkParamNames.js @@ -1,14 +1,29 @@ import iterateJsdoc from '../iterateJsdoc'; +/** + * @param {string} targetTagName + * @param {boolean} allowExtraTrailingParamDocs + * @param {boolean} checkDestructured + * @param {boolean} checkRestProperty + * @param {RegExp} checkTypesRegex + * @param {boolean} disableExtraPropertyReporting + * @param {boolean} enableFixer + * @param {Array} functionParameterNames + * @param jsdoc + * @param _jsdocNode + * @param utils + * @param report + * @returns {boolean} + */ const validateParameterNames = ( - targetTagName : string, - allowExtraTrailingParamDocs: boolean, - checkDestructured : boolean, - checkRestProperty : boolean, - checkTypesRegex : RegExp, + targetTagName, + allowExtraTrailingParamDocs, + checkDestructured, + checkRestProperty, + checkTypesRegex, disableExtraPropertyReporting, - enableFixer: boolean, - functionParameterNames : Array, jsdoc, _jsdocNode, utils, report, + enableFixer, + functionParameterNames, jsdoc, _jsdocNode, utils, report, ) => { const paramTags = Object.entries(jsdoc.tags).filter(([ , tag, @@ -228,9 +243,17 @@ const validateParameterNames = ( }); }; +/** + * @param {string} targetTagName + * @param {boolean} _allowExtraTrailingParamDocs + * @param {Array} jsdocParameterNames + * @param jsdoc + * @param {Function} report + * @returns {boolean} + */ const validateParameterNamesDeep = ( - targetTagName : string, _allowExtraTrailingParamDocs: boolean, - jsdocParameterNames : Array, jsdoc, report : Function, + targetTagName, _allowExtraTrailingParamDocs, + jsdocParameterNames, jsdoc, report, ) => { let lastRealParameter; diff --git a/src/rules/checkPropertyNames.js b/src/rules/checkPropertyNames.js index 10bf56144..c275e1fcb 100644 --- a/src/rules/checkPropertyNames.js +++ b/src/rules/checkPropertyNames.js @@ -1,8 +1,16 @@ import iterateJsdoc from '../iterateJsdoc'; +/** + * @param {string} targetTagName + * @param {boolean} enableFixer + * @param jsdoc + * @param jsdocNode + * @param utils + * @returns {boolean} + */ const validatePropertyNames = ( - targetTagName : string, - enableFixer : boolean, + targetTagName, + enableFixer, jsdoc, jsdocNode, utils, ) => { const propertyTags = Object.entries(jsdoc.tags).filter(([ @@ -35,9 +43,15 @@ const validatePropertyNames = ( }); }; +/** + * @param {string} targetTagName + * @param {string[]} jsdocPropertyNames + * @param jsdoc + * @param {Function} report + */ const validatePropertyNamesDeep = ( - targetTagName : string, - jsdocPropertyNames : Array, jsdoc, report : Function, + targetTagName, + jsdocPropertyNames, jsdoc, report, ) => { let lastRealProperty; diff --git a/src/rules/requireParam.js b/src/rules/requireParam.js index 83cb929a8..98ad5aa8c 100644 --- a/src/rules/requireParam.js +++ b/src/rules/requireParam.js @@ -1,7 +1,12 @@ import iterateJsdoc from '../iterateJsdoc'; -type T = [string, () => T]; -const rootNamer = (desiredRoots: string[], currentIndex: number): T => { +/** + * @template T + * @param {string[]} desiredRoots + * @param {number} currentIndex + * @returns {[string, boolean, () => T]} + */ +const rootNamer = (desiredRoots, currentIndex) => { let name; let idx = currentIndex; const incremented = desiredRoots.length <= 1; diff --git a/test/rules/assertions/requireJsdoc.js b/test/rules/assertions/requireJsdoc.js index b59ca1aba..28e8c2f61 100644 --- a/test/rules/assertions/requireJsdoc.js +++ b/test/rules/assertions/requireJsdoc.js @@ -2197,6 +2197,32 @@ function quux (foo) { } `, parser: require.resolve('@babel/eslint-parser'), + parserOptions: { + babelOptions: { + env: { + test: { + plugins: [ + 'istanbul', + ], + }, + }, + plugins: [ + '@babel/plugin-transform-flow-strip-types', + '@babel/plugin-syntax-class-properties', + 'add-module-exports', + ], + presets: [ + [ + '@babel/preset-env', + { + targets: { + node: 12, + }, + }, + ], + ], + }, + }, }, { code: `