UNPKG

eslint-plugin-jsdoc

Version:
1 lines 90.4 kB
{"version":3,"file":"jsdocUtils.cjs","names":["_getDefaultTagStructureForMode","_interopRequireDefault","require","_tagNames","_hasReturnValue","_WarnSettings","_jsdoccomment","e","__esModule","default","tagStructure","setTagStructure","mode","getDefaultTagStructureForMode","exports","flattenRoots","params","root","hasRestElement","hasPropertyRest","rests","names","reduce","acc","cur","Array","isArray","nms","flattened","inner","filter","Boolean","push","concat","isRestProperty","restElement","String","name","getPropertiesFromPropertySignature","propSignature","type","undefined","typeAnnotation","key","members","map","member","getFunctionParameterNames","functionNode","checkDefaultObjects","_functionNode$value","getParamName","param","isProperty","_param$left2","hasLeftTypeAnnotation","left","_typeAnnotation$typeA","propertyNames","annotationParamName","hasLeftName","_param$left","properties","roots","prop","value","elements","idx","right","raw","_param$left3","includes","_param$argument","argument","parameter","Error","hasParams","length","getJsdocTagsDeep","jsdoc","targetTagName","ret","tag","tags","entries","modeWarnSettings","WarnSettings","getTagNamesForMode","context","jsdocTags","typeScriptTags","closureTags","hasBeenWarned","report","loc","end","column","line","start","message","markSettingAsWarned","getTagDescription","tg","returnArray","descriptions","source","some","tokens","lineEnd","postDelimiter","postTag","description","desc","slice","join","getPreferredTagNameSimple","tagPreference","_Object$entries$find","prefValues","Object","values","prefVal","replacement","tagPreferenceFixed","fromEntries","replace","prototype","hasOwnProperty","call","tagNames","preferredTagName","find","aliases","isValidTag","definedTags","validTagNames","keys","flat","additionalTags","allTags","hasTag","targetTagLower","toLowerCase","doc","filterTags","getTags","tagName","item","getPreferredTagName","tagNamePreference","skipReportingBlockedTag","allowObjectReturn","defaultMessage","isObject","blocked","forEachPreferredTag","arrayHandler","matchingJsdocTags","matchingJsdocTag","getAllTags","inlineTags","inlineTag","flatMap","tagBegins","number","hasATag","targetTagNames","mayBeUndefinedTypeTag","tagType","trim","parsedTypes","tryParse","elem","ensureMap","has","set","Map","get","overrideTagStructure","structuredTags","tagMap","required","tagStruct","requiredName","requiredType","typeOrNameRequired","getTagStructureForMode","isNamepathDefiningTag","isNamepathReferencingTag","isNamepathOrUrlReferencingTag","tagMustHaveTypePosition","tagMightHaveTypePosition","namepathTypes","Set","tagMightHaveNamePosition","tagMightHaveNamepath","nampathRole","tagMustHaveNamePosition","tagMightHaveEitherTypeOrNamePosition","tagMustHaveEitherTypeOrNamePosition","tagMissingRequiredTypeOrNamepath","mustHaveTypePosition","mightHaveTypePosition","hasTypePosition","hasNameOrNamepathPosition","mustHaveEither","hasEither","hasNonFunctionYield","node","checkYieldReturnValue","body","bodyNode","arguments","element","expression","test","consequent","alternate","block","handler","finalizer","cases","someCase","nde","declarations","id","init","expressions","subExpression","property","computed","quasi","object","parent","hasYieldValue","generator","hasThrowValue","innerFunction","async","parseClosureTemplateTag","split","enforcedContexts","defaultContexts","settings","_context$options$","contexts","options","getContextObject","checkJsdoc","selInfo","lastIndex","selector","comment","bind","old","tagsWithNamesAndDescriptions","getTagsByType","tagsWithoutNames","tagsWithNames","tagWithName","getIndent","sourceCode","_sourceCode$text$matc","text","match","isConstructor","_node$parent","kind","isGetter","_node$parent2","isSetter","_node$parent3","hasAccessorPair","sourceKind","sourceName","oppositeKind","sibling","child","ky","exemptSpeciaMethods","schema","hasSchemaOption","_context$options$2","schemaProperties","checkGetters","checkSetters","dropPathSegmentQuotes","str","replaceAll","comparePaths","otherPathName","pathDoesNotBeginWith","startsWith","getRegexFromString","regexString","requiredFlags","flags","regex","uniqueFlags","RegExp"],"sources":["../src/jsdocUtils.js"],"sourcesContent":["import getDefaultTagStructureForMode from './getDefaultTagStructureForMode.js';\nimport {\n closureTags,\n jsdocTags,\n typeScriptTags,\n} from './tagNames.js';\nimport {\n hasReturnValue,\n hasValueOrExecutorHasNonEmptyResolveValue,\n} from './utils/hasReturnValue.js';\nimport WarnSettings from './WarnSettings.js';\nimport {\n tryParse,\n} from '@es-joy/jsdoccomment';\n\n/**\n * @typedef {number} Integer\n */\n/**\n * @typedef {import('./utils/hasReturnValue.js').ESTreeOrTypeScriptNode} ESTreeOrTypeScriptNode\n */\n\n/**\n * @typedef {\"jsdoc\"|\"typescript\"|\"closure\"|\"permissive\"} ParserMode\n */\n\n/**\n * @type {import('./getDefaultTagStructureForMode.js').TagStructure}\n */\nlet tagStructure;\n\n/**\n * @param {ParserMode} mode\n * @returns {void}\n */\nconst setTagStructure = (mode) => {\n tagStructure = getDefaultTagStructureForMode(mode);\n};\n\n/**\n * @typedef {undefined|string|{\n * name: Integer,\n * restElement: boolean\n * }|{\n * isRestProperty: boolean|undefined,\n * name: string,\n * restElement: boolean\n * }|{\n * name: string,\n * restElement: boolean\n * }} ParamCommon\n */\n/**\n * @typedef {ParamCommon|[string|undefined, (FlattendRootInfo & {\n * annotationParamName?: string,\n * })]|NestedParamInfo} ParamNameInfo\n */\n\n/**\n * @typedef {{\n * hasPropertyRest: boolean,\n * hasRestElement: boolean,\n * names: string[],\n * rests: boolean[],\n * }} FlattendRootInfo\n */\n/**\n * @typedef {[string, (string[]|ParamInfo[])]} NestedParamInfo\n */\n/**\n * @typedef {ParamCommon|\n * [string|undefined, (FlattendRootInfo & {\n * annotationParamName?: string\n * })]|\n * NestedParamInfo} ParamInfo\n */\n\n/**\n * Given a nested array of property names, reduce them to a single array,\n * appending the name of the root element along the way if present.\n * @callback FlattenRoots\n * @param {ParamInfo[]} params\n * @param {string} [root]\n * @returns {FlattendRootInfo}\n */\n\n/** @type {FlattenRoots} */\nconst flattenRoots = (params, root = '') => {\n let hasRestElement = false;\n let hasPropertyRest = false;\n\n /**\n * @type {boolean[]}\n */\n const rests = [];\n\n const names = params.reduce(\n /**\n * @param {string[]} acc\n * @param {ParamInfo} cur\n * @returns {string[]}\n */\n (acc, cur) => {\n if (Array.isArray(cur)) {\n let nms;\n if (Array.isArray(cur[1])) {\n nms = cur[1];\n } else {\n if (cur[1].hasRestElement) {\n hasRestElement = true;\n }\n\n if (cur[1].hasPropertyRest) {\n hasPropertyRest = true;\n }\n\n nms = cur[1].names;\n }\n\n const flattened = flattenRoots(nms, root ? `${root}.${cur[0]}` : cur[0]);\n if (flattened.hasRestElement) {\n hasRestElement = true;\n }\n\n if (flattened.hasPropertyRest) {\n hasPropertyRest = true;\n }\n\n const inner = /** @type {string[]} */ ([\n root ? `${root}.${cur[0]}` : cur[0],\n ...flattened.names,\n ].filter(Boolean));\n rests.push(false, ...flattened.rests);\n\n return acc.concat(inner);\n }\n\n if (typeof cur === 'object') {\n if ('isRestProperty' in cur && cur.isRestProperty) {\n hasPropertyRest = true;\n rests.push(true);\n } else {\n rests.push(false);\n }\n\n if ('restElement' in cur && cur.restElement) {\n hasRestElement = true;\n }\n\n acc.push(root ? `${root}.${String(cur.name)}` : String(cur.name));\n } else if (typeof cur !== 'undefined') {\n rests.push(false);\n acc.push(root ? `${root}.${cur}` : cur);\n }\n\n return acc;\n }, [],\n );\n\n return {\n hasPropertyRest,\n hasRestElement,\n names,\n rests,\n };\n};\n\n/**\n * @param {import('@typescript-eslint/types').TSESTree.TSIndexSignature|\n * import('@typescript-eslint/types').TSESTree.TSConstructSignatureDeclaration|\n * import('@typescript-eslint/types').TSESTree.TSCallSignatureDeclaration|\n * import('@typescript-eslint/types').TSESTree.TSPropertySignature} propSignature\n * @returns {undefined|string|[string, string[]]}\n */\nconst getPropertiesFromPropertySignature = (propSignature) => {\n if (\n propSignature.type === 'TSIndexSignature' ||\n propSignature.type === 'TSConstructSignatureDeclaration' ||\n propSignature.type === 'TSCallSignatureDeclaration'\n ) {\n return undefined;\n }\n\n if (propSignature.typeAnnotation && propSignature.typeAnnotation.typeAnnotation.type === 'TSTypeLiteral') {\n return [\n /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */ (\n propSignature.key\n ).name,\n propSignature.typeAnnotation.typeAnnotation.members.map((member) => {\n return /** @type {string} */ (\n getPropertiesFromPropertySignature(\n /** @type {import('@typescript-eslint/types').TSESTree.TSPropertySignature} */ (\n member\n ),\n )\n );\n }),\n ];\n }\n\n return /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */ (\n propSignature.key\n ).name;\n};\n\n/**\n * @param {ESTreeOrTypeScriptNode|null} functionNode\n * @param {boolean} [checkDefaultObjects]\n * @throws {Error}\n * @returns {ParamNameInfo[]}\n */\nconst getFunctionParameterNames = (\n functionNode, checkDefaultObjects,\n) => {\n /* eslint-disable complexity -- Temporary */\n /**\n * @param {import('estree').Identifier|import('estree').AssignmentPattern|\n * import('estree').ObjectPattern|import('estree').Property|\n * import('estree').RestElement|import('estree').ArrayPattern|\n * import('@typescript-eslint/types').TSESTree.TSParameterProperty|\n * import('@typescript-eslint/types').TSESTree.Property|\n * import('@typescript-eslint/types').TSESTree.RestElement|\n * import('@typescript-eslint/types').TSESTree.Identifier|\n * import('@typescript-eslint/types').TSESTree.ObjectPattern|\n * import('@typescript-eslint/types').TSESTree.BindingName|\n * import('@typescript-eslint/types').TSESTree.Parameter\n * } param\n * @param {boolean} [isProperty]\n * @returns {ParamNameInfo|[string, ParamNameInfo[]]}\n */\n const getParamName = (param, isProperty) => {\n /* eslint-enable complexity -- Temporary */\n const hasLeftTypeAnnotation = 'left' in param && 'typeAnnotation' in param.left;\n\n if ('typeAnnotation' in param || hasLeftTypeAnnotation) {\n const typeAnnotation = hasLeftTypeAnnotation ?\n /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */ (\n param.left\n ).typeAnnotation :\n /** @type {import('@typescript-eslint/types').TSESTree.Identifier|import('@typescript-eslint/types').TSESTree.ObjectPattern} */\n (param).typeAnnotation;\n\n if (typeAnnotation?.typeAnnotation?.type === 'TSTypeLiteral') {\n const propertyNames = typeAnnotation.typeAnnotation.members.map((member) => {\n return getPropertiesFromPropertySignature(\n /** @type {import('@typescript-eslint/types').TSESTree.TSPropertySignature} */\n (member),\n );\n });\n\n const flattened = {\n ...flattenRoots(propertyNames),\n annotationParamName: 'name' in param ? param.name : undefined,\n };\n const hasLeftName = 'left' in param && 'name' in param.left;\n\n if ('name' in param || hasLeftName) {\n return [\n hasLeftName ?\n /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */ (\n param.left\n ).name :\n /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */ (\n param\n ).name,\n flattened,\n ];\n }\n\n return [\n undefined, flattened,\n ];\n }\n }\n\n if ('name' in param) {\n return param.name;\n }\n\n if ('left' in param && 'name' in param.left) {\n return param.left.name;\n }\n\n if (\n param.type === 'ObjectPattern' ||\n ('left' in param &&\n (\n param\n ).left.type === 'ObjectPattern')\n ) {\n const properties = /** @type {import('@typescript-eslint/types').TSESTree.ObjectPattern} */ (\n param\n ).properties ||\n /** @type {import('estree').ObjectPattern} */\n (\n /** @type {import('@typescript-eslint/types').TSESTree.AssignmentPattern} */ (\n param\n ).left\n )?.properties;\n const roots = properties.map((prop) => {\n return getParamName(prop, true);\n });\n\n return [\n undefined, flattenRoots(roots),\n ];\n }\n\n if (param.type === 'Property') {\n switch (param.value.type) {\n case 'ArrayPattern': {\n return [\n /** @type {import('estree').Identifier} */\n (param.key).name,\n /** @type {import('estree').ArrayPattern} */ (\n param.value\n ).elements.map((prop, idx) => {\n return {\n name: idx,\n restElement: prop?.type === 'RestElement',\n };\n }),\n ];\n }\n\n case 'ObjectPattern': {\n return [\n /** @type {import('estree').Identifier} */ (param.key).name,\n /** @type {import('estree').ObjectPattern} */ (\n param.value\n ).properties.map((prop) => {\n return /** @type {string|[string, string[]]} */ (getParamName(prop, isProperty));\n }),\n ];\n }\n\n case 'AssignmentPattern': {\n switch (param.value.left.type) {\n case 'Identifier':\n // Default parameter\n if (checkDefaultObjects && param.value.right.type === 'ObjectExpression') {\n return [\n /** @type {import('estree').Identifier} */ (\n param.key\n ).name,\n /** @type {import('estree').AssignmentPattern} */ (\n param.value\n ).right.properties.map((prop) => {\n return /** @type {string} */ (getParamName(\n /** @type {import('estree').Property} */\n (prop),\n isProperty,\n ));\n }),\n ];\n }\n\n break;\n case 'ObjectPattern':\n return [\n /** @type {import('estree').Identifier} */\n (param.key).name,\n /** @type {import('estree').ObjectPattern} */ (\n param.value.left\n ).properties.map((prop) => {\n return getParamName(prop, isProperty);\n }),\n ];\n case 'ArrayPattern':\n return [\n /** @type {import('estree').Identifier} */\n (param.key).name,\n /** @type {import('estree').ArrayPattern} */ (\n param.value.left\n ).elements.map((prop, idx) => {\n return {\n name: idx,\n restElement: prop?.type === 'RestElement',\n };\n }),\n ];\n }\n }\n }\n\n switch (param.key.type) {\n case 'Identifier':\n return param.key.name;\n\n // The key of an object could also be a string or number\n case 'Literal':\n /* c8 ignore next 2 -- `raw` may not be present in all parsers */\n return /** @type {string} */ (param.key.raw ||\n param.key.value);\n\n // case 'MemberExpression':\n default:\n // Todo: We should really create a structure (and a corresponding\n // option analogous to `checkRestProperty`) which allows for\n // (and optionally requires) dynamic properties to have a single\n // line of documentation\n return undefined;\n }\n }\n\n if (\n param.type === 'ArrayPattern' ||\n /** @type {import('estree').AssignmentPattern} */ (\n param\n ).left?.type === 'ArrayPattern'\n ) {\n const elements = /** @type {import('estree').ArrayPattern} */ (\n param\n ).elements || /** @type {import('estree').ArrayPattern} */ (\n /** @type {import('estree').AssignmentPattern} */ (\n param\n ).left\n )?.elements;\n const roots = elements.map((prop, idx) => {\n return {\n name: `\"${idx}\"`,\n restElement: prop?.type === 'RestElement',\n };\n });\n\n return [\n undefined, flattenRoots(roots),\n ];\n }\n\n if ([\n 'RestElement', 'ExperimentalRestProperty',\n ].includes(param.type)) {\n return {\n isRestProperty: isProperty,\n name: /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */ (\n /** @type {import('@typescript-eslint/types').TSESTree.RestElement} */ (\n param\n // @ts-expect-error Ok\n ).argument).name ?? param?.argument?.elements?.map(({name}) => {\n return name;\n }),\n restElement: true,\n };\n }\n\n if (param.type === 'TSParameterProperty') {\n return getParamName(\n /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */ (\n /** @type {import('@typescript-eslint/types').TSESTree.TSParameterProperty} */ (\n param\n ).parameter\n ),\n true,\n );\n }\n\n throw new Error(`Unsupported function signature format: \\`${param.type}\\`.`);\n };\n\n if (!functionNode) {\n return [];\n }\n\n return (/** @type {import('@typescript-eslint/types').TSESTree.FunctionDeclaration} */ (\n functionNode\n ).params || /** @type {import('@typescript-eslint/types').TSESTree.MethodDefinition} */ (\n functionNode\n ).value?.params || []).map((param) => {\n return getParamName(param);\n });\n};\n\n/**\n * @param {ESTreeOrTypeScriptNode} functionNode\n * @returns {Integer}\n */\nconst hasParams = (functionNode) => {\n // Should also check `functionNode.value.params` if supporting `MethodDefinition`\n return /** @type {import('@typescript-eslint/types').TSESTree.FunctionDeclaration} */ (\n functionNode\n ).params.length;\n};\n\n/**\n * Gets all names of the target type, including those that refer to a path, e.g.\n * `foo` or `foo.bar`.\n * @param {import('comment-parser').Block} jsdoc\n * @param {string} targetTagName\n * @returns {{\n * idx: Integer,\n * name: string,\n * type: string\n * }[]}\n */\nconst getJsdocTagsDeep = (jsdoc, targetTagName) => {\n const ret = [];\n for (const [\n idx,\n {\n name,\n tag,\n type,\n },\n ] of jsdoc.tags.entries()) {\n if (tag !== targetTagName) {\n continue;\n }\n\n ret.push({\n idx,\n name,\n type,\n });\n }\n\n return ret;\n};\n\nconst modeWarnSettings = WarnSettings();\n\n/**\n * @param {ParserMode|undefined} mode\n * @param {Reporter} context\n * @returns {import('./tagNames.js').AliasedTags}\n */\nconst getTagNamesForMode = (mode, context) => {\n switch (mode) {\n case 'jsdoc':\n return jsdocTags;\n case 'typescript':\n return typeScriptTags;\n case 'closure': case 'permissive':\n return closureTags;\n default:\n if (!modeWarnSettings.hasBeenWarned(context, 'mode')) {\n context.report({\n loc: {\n end: {\n column: 1,\n line: 1,\n },\n start: {\n column: 1,\n line: 1,\n },\n },\n message: `Unrecognized value \\`${mode}\\` for \\`settings.jsdoc.mode\\`.`,\n });\n modeWarnSettings.markSettingAsWarned(context, 'mode');\n }\n\n // We'll avoid breaking too many other rules\n return jsdocTags;\n }\n};\n\n/**\n * @param {import('comment-parser').Spec} tg\n * @param {boolean} [returnArray]\n * @returns {string[]|string}\n */\nconst getTagDescription = (tg, returnArray) => {\n /**\n * @type {string[]}\n */\n const descriptions = [];\n tg.source.some(({\n tokens: {\n end,\n lineEnd,\n postDelimiter,\n tag,\n postTag,\n name,\n type,\n description,\n },\n }) => {\n const desc = (\n tag && postTag ||\n !tag && !name && !type && postDelimiter || ''\n\n // Remove space\n ).slice(1) +\n (description || '') + (lineEnd || '');\n\n if (end) {\n if (desc) {\n descriptions.push(desc);\n }\n\n return true;\n }\n\n descriptions.push(desc);\n\n return false;\n });\n\n return returnArray ? descriptions : descriptions.join('\\n');\n};\n\n/**\n * @typedef {{\n * report: (descriptor: import('eslint').Rule.ReportDescriptor) => void\n * }} Reporter\n */\n\n/**\n * @param {string} name\n * @param {ParserMode|undefined} mode\n * @param {TagNamePreference} tagPreference\n * @param {Reporter} context\n * @returns {string|false|{\n * message: string;\n * replacement?: string|undefined;\n * }}\n */\nconst getPreferredTagNameSimple = (\n name,\n mode,\n tagPreference = {},\n context = {\n report () {\n // No-op\n }\n },\n) => {\n const prefValues = Object.values(tagPreference);\n if (prefValues.includes(name) || prefValues.some((prefVal) => {\n return prefVal && typeof prefVal === 'object' && prefVal.replacement === name;\n })) {\n return name;\n }\n\n // Allow keys to have a 'tag ' prefix to avoid upstream bug in ESLint\n // that disallows keys that conflict with Object.prototype,\n // e.g. 'tag constructor' for 'constructor':\n // https://github.com/eslint/eslint/issues/13289\n // https://github.com/gajus/eslint-plugin-jsdoc/issues/537\n const tagPreferenceFixed = Object.fromEntries(\n Object\n .entries(tagPreference)\n .map(([\n key,\n value,\n ]) => {\n return [\n key.replace(/^tag /u, ''), value,\n ];\n }),\n );\n\n if (Object.prototype.hasOwnProperty.call(tagPreferenceFixed, name)) {\n return tagPreferenceFixed[name];\n }\n\n const tagNames = getTagNamesForMode(mode, context);\n\n const preferredTagName = Object.entries(tagNames).find(([\n , aliases,\n ]) => {\n return aliases.includes(name);\n })?.[0];\n if (preferredTagName) {\n return preferredTagName;\n }\n\n return name;\n};\n\n/**\n * @param {import('eslint').Rule.RuleContext} context\n * @param {ParserMode|undefined} mode\n * @param {string} name\n * @param {string[]} definedTags\n * @returns {boolean}\n */\nconst isValidTag = (\n context,\n mode,\n name,\n definedTags,\n) => {\n const tagNames = getTagNamesForMode(mode, context);\n\n const validTagNames = Object.keys(tagNames).concat(Object.values(tagNames).flat());\n const additionalTags = definedTags;\n const allTags = validTagNames.concat(additionalTags);\n\n return allTags.includes(name);\n};\n\n/**\n * @param {import('./iterateJsdoc.js').JsdocBlockWithInline} jsdoc\n * @param {string} targetTagName\n * @returns {boolean}\n */\nconst hasTag = (jsdoc, targetTagName) => {\n const targetTagLower = targetTagName.toLowerCase();\n\n return jsdoc.tags.some((doc) => {\n return doc.tag.toLowerCase() === targetTagLower;\n });\n};\n\n/**\n * @param {import('./iterateJsdoc.js').JsdocBlockWithInline} jsdoc\n * @param {(tag: import('@es-joy/jsdoccomment').JsdocTagWithInline) => boolean} filter\n * @returns {import('@es-joy/jsdoccomment').JsdocTagWithInline[]}\n */\nconst filterTags = (jsdoc, filter) => {\n return jsdoc.tags.filter((tag) => {\n return filter(tag);\n });\n};\n\n/**\n * @param {import('./iterateJsdoc.js').JsdocBlockWithInline} jsdoc\n * @param {string} tagName\n * @returns {import('comment-parser').Spec[]}\n */\nconst getTags = (jsdoc, tagName) => {\n return filterTags(jsdoc, (item) => {\n return item.tag === tagName;\n });\n};\n\n/**\n * @param {import('./iterateJsdoc.js').JsdocBlockWithInline} jsdoc\n * @param {{\n * tagName: string,\n * context?: import('eslint').Rule.RuleContext,\n * mode?: ParserMode,\n * report?: import('./iterateJsdoc.js').Report\n * tagNamePreference?: TagNamePreference\n * skipReportingBlockedTag?: boolean,\n * allowObjectReturn?: boolean,\n * defaultMessage?: string,\n * }} cfg\n * @returns {string|undefined|false|{\n * message: string;\n * replacement?: string|undefined;\n * }|{\n * blocked: true,\n * tagName: string\n * }}\n */\nconst getPreferredTagName = (jsdoc, {\n tagName,\n context, mode,\n tagNamePreference,\n report = () => {},\n skipReportingBlockedTag = false,\n allowObjectReturn = false,\n defaultMessage = `Unexpected tag \\`@${tagName}\\``,\n}) => {\n const ret = getPreferredTagNameSimple(tagName, mode, tagNamePreference, context);\n const isObject = ret && typeof ret === 'object';\n if (hasTag(jsdoc, tagName) && (ret === false || isObject && !ret.replacement)) {\n if (skipReportingBlockedTag) {\n return {\n blocked: true,\n tagName,\n };\n }\n\n const message = isObject && ret.message || defaultMessage;\n report(message, null, getTags(jsdoc, tagName)[0]);\n\n return false;\n }\n\n return isObject && !allowObjectReturn ? ret.replacement : ret;\n};\n\n/**\n * @param {import('./iterateJsdoc.js').JsdocBlockWithInline} jsdoc\n * @param {string} tagName\n * @param {(\n * matchingJsdocTag: import('@es-joy/jsdoccomment').JsdocTagWithInline,\n * targetTagName: string\n * ) => void} arrayHandler\n * @param {object} cfg\n * @param {import('eslint').Rule.RuleContext} [cfg.context]\n * @param {ParserMode} [cfg.mode]\n * @param {import('./iterateJsdoc.js').Report} [cfg.report]\n * @param {TagNamePreference} [cfg.tagNamePreference]\n * @param {boolean} [cfg.skipReportingBlockedTag]\n * @returns {void}\n */\nconst forEachPreferredTag = (\n jsdoc, tagName, arrayHandler,\n {\n context, mode, report,\n tagNamePreference,\n skipReportingBlockedTag = false,\n } = {}\n) => {\n const targetTagName = /** @type {string|false} */ (\n getPreferredTagName(jsdoc, {\n skipReportingBlockedTag,\n tagName,\n context, mode, report, tagNamePreference\n })\n );\n if (!targetTagName ||\n skipReportingBlockedTag && targetTagName && typeof targetTagName === 'object'\n ) {\n return;\n }\n\n const matchingJsdocTags = jsdoc.tags.filter(({\n tag,\n }) => {\n return tag === targetTagName;\n });\n\n for (const matchingJsdocTag of matchingJsdocTags) {\n arrayHandler(\n /**\n * @type {import('@es-joy/jsdoccomment').JsdocTagWithInline}\n */ (\n matchingJsdocTag\n ), targetTagName,\n );\n }\n};\n\n/**\n * Get all tags, inline tags and inline tags in tags\n * @param {import('./iterateJsdoc.js').JsdocBlockWithInline} jsdoc\n * @returns {(import('comment-parser').Spec|\n * import('@es-joy/jsdoccomment').JsdocInlineTagNoType)[]}\n */\nconst getAllTags = (jsdoc) => {\n return [\n ...jsdoc.tags,\n ...jsdoc.inlineTags.map((inlineTag) => {\n // Tags don't have source or line numbers, so add before returning\n let line = -1;\n for (const {\n tokens: {\n description,\n },\n } of jsdoc.source) {\n line++;\n if (description && description.includes(`{@${inlineTag.tag}`)) {\n break;\n }\n }\n\n inlineTag.line = line;\n\n return inlineTag;\n }),\n ...jsdoc.tags.flatMap((tag) => {\n let tagBegins = -1;\n for (const {\n tokens: {\n tag: tg,\n },\n } of jsdoc.source) {\n tagBegins++;\n if (tg) {\n break;\n }\n }\n\n for (const inlineTag of tag.inlineTags) {\n /** @type {import('./iterateJsdoc.js').Integer} */\n let line = 0;\n for (const {\n number,\n tokens: {\n description,\n },\n } of tag.source) {\n if (description && description.includes(`{@${inlineTag.tag}`)) {\n line = number;\n break;\n }\n }\n\n inlineTag.line = tagBegins + line - 1;\n }\n\n return (\n /**\n * @type {import('comment-parser').Spec & {\n * inlineTags: import('@es-joy/jsdoccomment').JsdocInlineTagNoType[]\n * }}\n */ (\n tag\n ).inlineTags\n );\n }),\n ];\n};\n\n/**\n * @param {import('./iterateJsdoc.js').JsdocBlockWithInline} jsdoc\n * @param {string[]} targetTagNames\n * @returns {boolean}\n */\nconst hasATag = (jsdoc, targetTagNames) => {\n return targetTagNames.some((targetTagName) => {\n return hasTag(jsdoc, targetTagName);\n });\n};\n\n/**\n * Checks if the JSDoc comment has an undefined type.\n * @param {import('comment-parser').Spec|null|undefined} tag\n * the tag which should be checked.\n * @param {ParserMode} mode\n * @returns {boolean}\n * true in case a defined type is undeclared; otherwise false.\n */\nconst mayBeUndefinedTypeTag = (tag, mode) => {\n // The function should not continue in the event the type is not defined...\n if (typeof tag === 'undefined' || tag === null) {\n return true;\n }\n\n // .. same applies if it declares an `{undefined}` or `{void}` type\n const tagType = tag.type.trim();\n\n // Exit early if matching\n if (\n tagType === 'undefined' || tagType === 'void' ||\n tagType === '*' || tagType === 'any'\n ) {\n return true;\n }\n\n let parsedTypes;\n try {\n parsedTypes = tryParse(\n tagType,\n mode === 'permissive' ? undefined : [\n mode,\n ],\n );\n } catch {\n // Ignore\n }\n\n if (\n // We do not traverse deeply as it could be, e.g., `Promise<void>`\n parsedTypes &&\n parsedTypes.type === 'JsdocTypeUnion' &&\n parsedTypes.elements.find((elem) => {\n return elem.type === 'JsdocTypeUndefined' ||\n elem.type === 'JsdocTypeName' && elem.value === 'void';\n })) {\n return true;\n }\n\n // In any other case, a type is present\n return false;\n};\n\n/**\n * @param {import('./getDefaultTagStructureForMode.js').TagStructure} map\n * @param {string} tag\n * @returns {Map<string, string|string[]|boolean|undefined>}\n */\nconst ensureMap = (map, tag) => {\n if (!map.has(tag)) {\n map.set(tag, new Map());\n }\n\n return /** @type {Map<string, string | boolean>} */ (map.get(tag));\n};\n\n/**\n * @param {import('./iterateJsdoc.js').StructuredTags} structuredTags\n * @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap\n * @returns {void}\n */\nconst overrideTagStructure = (structuredTags, tagMap = tagStructure) => {\n for (const [\n tag,\n {\n name,\n type,\n required = [],\n },\n ] of Object.entries(structuredTags)) {\n const tagStruct = ensureMap(tagMap, tag);\n\n tagStruct.set('namepathRole', name);\n tagStruct.set('typeAllowed', type);\n\n const requiredName = required.includes('name');\n if (requiredName && name === false) {\n throw new Error('Cannot add \"name\" to `require` with the tag\\'s `name` set to `false`');\n }\n\n tagStruct.set('nameRequired', requiredName);\n\n const requiredType = required.includes('type');\n if (requiredType && type === false) {\n throw new Error('Cannot add \"type\" to `require` with the tag\\'s `type` set to `false`');\n }\n\n tagStruct.set('typeRequired', requiredType);\n\n const typeOrNameRequired = required.includes('typeOrNameRequired');\n if (typeOrNameRequired && name === false) {\n throw new Error('Cannot add \"typeOrNameRequired\" to `require` with the tag\\'s `name` set to `false`');\n }\n\n if (typeOrNameRequired && type === false) {\n throw new Error('Cannot add \"typeOrNameRequired\" to `require` with the tag\\'s `type` set to `false`');\n }\n\n tagStruct.set('typeOrNameRequired', typeOrNameRequired);\n }\n};\n\n/**\n * @param {ParserMode} mode\n * @param {import('./iterateJsdoc.js').StructuredTags} structuredTags\n * @returns {import('./getDefaultTagStructureForMode.js').TagStructure}\n */\nconst getTagStructureForMode = (mode, structuredTags) => {\n const tagStruct = getDefaultTagStructureForMode(mode);\n\n try {\n overrideTagStructure(structuredTags, tagStruct);\n /* c8 ignore next 3 */\n } catch {\n //\n }\n\n return tagStruct;\n};\n\n/**\n * @param {string} tag\n * @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap\n * @returns {boolean}\n */\nconst isNamepathDefiningTag = (tag, tagMap = tagStructure) => {\n const tagStruct = ensureMap(tagMap, tag);\n\n return tagStruct.get('namepathRole') === 'namepath-defining';\n};\n\n/**\n * @param {string} tag\n * @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap\n * @returns {boolean}\n */\nconst isNamepathReferencingTag = (tag, tagMap = tagStructure) => {\n const tagStruct = ensureMap(tagMap, tag);\n return tagStruct.get('namepathRole') === 'namepath-referencing';\n};\n\n/**\n * @param {string} tag\n * @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap\n * @returns {boolean}\n */\nconst isNamepathOrUrlReferencingTag = (tag, tagMap = tagStructure) => {\n const tagStruct = ensureMap(tagMap, tag);\n return tagStruct.get('namepathRole') === 'namepath-or-url-referencing';\n};\n\n/**\n * @param {string} tag\n * @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap\n * @returns {boolean|undefined}\n */\nconst tagMustHaveTypePosition = (tag, tagMap = tagStructure) => {\n const tagStruct = ensureMap(tagMap, tag);\n\n return /** @type {boolean|undefined} */ (tagStruct.get('typeRequired'));\n};\n\n/**\n * @param {string} tag\n * @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap\n * @returns {boolean|string}\n */\nconst tagMightHaveTypePosition = (tag, tagMap = tagStructure) => {\n if (tagMustHaveTypePosition(tag, tagMap)) {\n return true;\n }\n\n const tagStruct = ensureMap(tagMap, tag);\n\n const ret = /** @type {boolean|undefined} */ (tagStruct.get('typeAllowed'));\n\n return ret === undefined ? true : ret;\n};\n\nconst namepathTypes = new Set([\n 'namepath-defining', 'namepath-referencing',\n]);\n\n/**\n * @param {string} tag\n * @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap\n * @returns {boolean}\n */\nconst tagMightHaveNamePosition = (tag, tagMap = tagStructure) => {\n const tagStruct = ensureMap(tagMap, tag);\n\n const ret = tagStruct.get('namepathRole');\n\n return ret === undefined ? true : Boolean(ret);\n};\n\n/**\n * @param {string} tag\n * @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap\n * @returns {boolean}\n */\nconst tagMightHaveNamepath = (tag, tagMap = tagStructure) => {\n const tagStruct = ensureMap(tagMap, tag);\n\n const nampathRole = tagStruct.get('namepathRole');\n\n return nampathRole !== false &&\n namepathTypes.has(/** @type {string} */ (nampathRole));\n};\n\n/**\n * @param {string} tag\n * @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap\n * @returns {boolean|undefined}\n */\nconst tagMustHaveNamePosition = (tag, tagMap = tagStructure) => {\n const tagStruct = ensureMap(tagMap, tag);\n\n return /** @type {boolean|undefined} */ (tagStruct.get('nameRequired'));\n};\n\n/**\n * @param {string} tag\n * @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap\n * @returns {boolean}\n */\nconst tagMightHaveEitherTypeOrNamePosition = (tag, tagMap) => {\n return Boolean(tagMightHaveTypePosition(tag, tagMap)) || tagMightHaveNamepath(tag, tagMap);\n};\n\n/**\n * @param {string} tag\n * @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap\n * @returns {boolean|undefined}\n */\nconst tagMustHaveEitherTypeOrNamePosition = (tag, tagMap) => {\n const tagStruct = ensureMap(tagMap, tag);\n\n return /** @type {boolean} */ (tagStruct.get('typeOrNameRequired'));\n};\n\n/**\n * @param {import('comment-parser').Spec} tag\n * @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap\n * @returns {boolean|undefined}\n */\nconst tagMissingRequiredTypeOrNamepath = (tag, tagMap = tagStructure) => {\n const mustHaveTypePosition = tagMustHaveTypePosition(tag.tag, tagMap);\n const mightHaveTypePosition = tagMightHaveTypePosition(tag.tag, tagMap);\n const hasTypePosition = mightHaveTypePosition && Boolean(tag.type);\n const hasNameOrNamepathPosition = (\n tagMustHaveNamePosition(tag.tag, tagMap) ||\n tagMightHaveNamepath(tag.tag, tagMap)\n ) && Boolean(tag.name);\n const mustHaveEither = tagMustHaveEitherTypeOrNamePosition(tag.tag, tagMap);\n const hasEither = tagMightHaveEitherTypeOrNamePosition(tag.tag, tagMap) &&\n (hasTypePosition || hasNameOrNamepathPosition);\n\n return mustHaveEither && !hasEither && !mustHaveTypePosition;\n};\n\n/* eslint-disable complexity -- Temporary */\n/**\n * @param {ESTreeOrTypeScriptNode|null|undefined} node\n * @param {boolean} [checkYieldReturnValue]\n * @returns {boolean}\n */\nconst hasNonFunctionYield = (node, checkYieldReturnValue) => {\n /* eslint-enable complexity -- Temporary */\n if (!node) {\n return false;\n }\n\n switch (node.type) {\n case 'BlockStatement': {\n return node.body.some((bodyNode) => {\n return ![\n 'ArrowFunctionExpression',\n 'FunctionDeclaration',\n 'FunctionExpression',\n ].includes(bodyNode.type) && hasNonFunctionYield(\n bodyNode, checkYieldReturnValue,\n );\n });\n }\n\n /* c8 ignore next 2 -- In Babel? */\n // @ts-expect-error In Babel?\n case 'OptionalCallExpression':\n case 'CallExpression':\n return node.arguments.some((element) => {\n return hasNonFunctionYield(element, checkYieldReturnValue);\n });\n case 'ChainExpression':\n case 'ExpressionStatement': {\n return hasNonFunctionYield(node.expression, checkYieldReturnValue);\n }\n\n case 'LabeledStatement':\n case 'WhileStatement':\n case 'DoWhileStatement':\n case 'ForStatement':\n case 'ForInStatement':\n case 'ForOfStatement':\n case 'WithStatement': {\n return hasNonFunctionYield(node.body, checkYieldReturnValue);\n }\n\n case 'ConditionalExpression':\n case 'IfStatement': {\n return hasNonFunctionYield(node.test, checkYieldReturnValue) ||\n hasNonFunctionYield(node.consequent, checkYieldReturnValue) ||\n hasNonFunctionYield(node.alternate, checkYieldReturnValue);\n }\n\n case 'TryStatement': {\n return hasNonFunctionYield(node.block, checkYieldReturnValue) ||\n hasNonFunctionYield(\n node.handler && node.handler.body, checkYieldReturnValue,\n ) ||\n hasNonFunctionYield(\n /** @type {import('@typescript-eslint/types').TSESTree.BlockStatement} */\n (node.finalizer),\n checkYieldReturnValue,\n );\n }\n\n case 'SwitchStatement': {\n return node.cases.some(\n (someCase) => {\n return someCase.consequent.some((nde) => {\n return hasNonFunctionYield(nde, checkYieldReturnValue);\n });\n },\n );\n }\n\n case 'ArrayPattern':\n case 'ArrayExpression':\n return node.elements.some((element) => {\n return hasNonFunctionYield(element, checkYieldReturnValue);\n });\n case 'AssignmentPattern':\n return hasNonFunctionYield(node.right, checkYieldReturnValue);\n\n case 'VariableDeclaration': {\n return node.declarations.some((nde) => {\n return hasNonFunctionYield(nde, checkYieldReturnValue);\n });\n }\n\n case 'VariableDeclarator': {\n return hasNonFunctionYield(node.id, checkYieldReturnValue) ||\n hasNonFunctionYield(node.init, checkYieldReturnValue);\n }\n\n case 'AssignmentExpression':\n case 'BinaryExpression':\n case 'LogicalExpression': {\n return hasNonFunctionYield(node.left, checkYieldReturnValue) ||\n hasNonFunctionYield(node.right, checkYieldReturnValue);\n }\n\n // Comma\n case 'SequenceExpression':\n case 'TemplateLiteral':\n return node.expressions.some((subExpression) => {\n return hasNonFunctionYield(subExpression, checkYieldReturnValue);\n });\n\n case 'ObjectPattern':\n case 'ObjectExpression':\n return node.properties.some((property) => {\n return hasNonFunctionYield(property, checkYieldReturnValue);\n });\n\n /* c8 ignore next -- In Babel? */\n case 'PropertyDefinition':\n /* eslint-disable no-fallthrough */\n /* c8 ignore next 2 -- In Babel? */\n // @ts-expect-error In Babel?\n case 'ObjectProperty':\n /* c8 ignore next 2 -- In Babel? */\n // @ts-expect-error In Babel?\n case 'ClassProperty':\n case 'Property':\n /* eslint-enable no-fallthrough */\n return node.computed && hasNonFunctionYield(node.key, checkYieldReturnValue) ||\n hasNonFunctionYield(node.value, checkYieldReturnValue);\n /* c8 ignore next 2 -- In Babel? */\n // @ts-expect-error In Babel?\n case 'ObjectMethod':\n /* c8 ignore next 6 -- In Babel? */\n // @ts-expect-error In Babel?\n return node.computed && hasNonFunctionYield(node.key, checkYieldReturnValue) ||\n // @ts-expect-error In Babel?\n node.arguments.some((nde) => {\n return hasNonFunctionYield(nde, checkYieldReturnValue);\n });\n\n case 'SpreadElement':\n case 'UnaryExpression':\n return hasNonFunctionYield(node.argument, checkYieldReturnValue);\n\n case 'TaggedTemplateExpression':\n return hasNonFunctionYield(node.quasi, checkYieldReturnValue);\n\n // ?.\n /* c8 ignore next 2 -- In Babel? */\n // @ts-expect-error In Babel?\n case 'OptionalMemberExpression':\n case 'MemberExpression':\n return hasNonFunctionYield(node.object, checkYieldReturnValue) ||\n hasNonFunctionYield(node.property, checkYieldReturnValue);\n\n /* c8 ignore next 2 -- In Babel? */\n // @ts-expect-error In Babel?\n case 'Import':\n case 'ImportExpression':\n return hasNonFunctionYield(node.source, checkYieldReturnValue);\n\n case 'ReturnStatement': {\n if (node.argument === null) {\n return false;\n }\n\n return hasNonFunctionYield(node.argument, checkYieldReturnValue);\n }\n\n case 'YieldExpression': {\n if (checkYieldReturnValue) {\n if (\n /** @type {import('eslint').Rule.Node} */ (\n node\n ).parent.type === 'VariableDeclarator'\n ) {\n return true;\n }\n\n return false;\n }\n\n // void return does not count.\n if (node.argument === null) {\n return false;\n }\n\n return true;\n }\n\n default: {\n return false;\n }\n }\n};\n\n/**\n * Checks if a node has a return statement. Void return does not count.\n * @param {ESTreeOrTypeScriptNode} node\n * @param {boolean} [checkYieldReturnValue]\n * @returns {boolean}\n */\nconst hasYieldValue = (node, checkYieldReturnValue) => {\n return /** @type {import('@typescript-eslint/types').TSESTree.FunctionDeclaration} */ (\n node\n ).generator && (\n /** @type {import('@typescript-eslint/types').TSESTree.FunctionDeclaration} */ (\n node\n ).expression || hasNonFunctionYield(\n /** @type {import('@typescript-eslint/types').TSESTree.FunctionDeclaration} */\n (node).body,\n checkYieldReturnValue,\n )\n );\n};\n\n/**\n * Checks if a node has a throws statement.\n * @param {ESTreeOrTypeScriptNode|null|undefined} node\n * @param {boolean} [innerFunction]\n * @returns {boolean}\n */\n// eslint-disable-next-line complexity\nconst hasThrowValue = (node, innerFunction) => {\n if (!node) {\n return false;\n }\n\n // There are cases where a function may execute its inner function which\n // throws, but we're treating functions atomically rather than trying to\n // follow them\n switch (node.type) {\n case 'FunctionExpression':\n case 'FunctionDeclaration':\n case 'ArrowFunctionExpression': {\n return !innerFunction && !node.async && hasThrowValue(node.body, true);\n }\n\n case 'BlockStatement': {\n return node.body.some((bodyNode) => {\n return bodyNode.type !== 'FunctionDeclaration' && hasThrowValue(bodyNode);\n });\n }\n\n case 'LabeledStatement':\n case 'WhileStatement':\n case 'DoWhileStatement':\n case 'ForStatement':\n case 'ForInStatement':\n case 'ForOfStatement':\n case 'WithStatement': {\n return hasThrowValue(node.body);\n }\n\n case 'IfStatement': {\n return hasThrowValue(node.consequent) || hasThrowValue(node.alternate);\n }\n\n // We only consider it to throw an error if the catch or finally blocks throw an error.\n case 'TryStatement': {\n return hasThrowValue(node.handler && node.handler.body) ||\n hasThrowValue(node.finalizer);\n }\n\n case 'SwitchStatement': {\n return node.cases.some(\n (someCase) => {\n return someCase.consequent.some((nde) => {\n return hasThrowValue(nde);\n });\n },\n );\n }\n\n case 'ThrowStatement': {\n return true;\n }\n\n default: {\n return false;\n }\n }\n};\n\n/**\n * @param {string} tag\n */\n/*\nconst isInlineTag = (tag) => {\n return /^(@link|@linkcode|@linkplain|@tutorial) /u.test(tag);\n};\n*/\n\n/**\n * Parses GCC Generic/Template types\n * @see {@link https://github.com/google/closure-compiler/wiki/Generic-Types}\n * @see {@link https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#template}\n * @param {import('comment-parser').Spec} tag\n * @returns {string[]}\n */\nconst parseClosureTemplateTag = (tag) => {\n return tag.name\n .split(',')\n .map((type) => {\n return type.trim().replace(/^\\[(?<name>.*?)=.*\\]$/u, '$<name>');\n });\n};\n\n/**\n * @typedef {true|string[]} DefaultContexts\n */\n\n/**\n * Checks user option for `contexts` array, defaulting to\n * contexts designated by the rule. Returns an array of\n * ESTree AST types, indicating allowable contexts.\n * @param {import('eslint').Rule.RuleContext} context\n * @param {DefaultContexts|undefined} defaultContexts\n * @param {{\n * contexts?: import('./iterateJsdoc.js').Context[]\n * }} settings\n * @returns {(string|import('./iterateJsdoc.js').ContextObject)[]}\n */\nconst enforcedContexts = (context, defaultContexts, settings) => {\n const contexts = context.options[0]?.contexts || settings.contexts || (defaultContexts === true ? [\n 'ArrowFunctionExpression',\n 'FunctionDeclaration',\n 'FunctionExpression',\n 'TSDeclareFunction',\n ] : defaultContexts);\n\n return contexts;\n};\n\n/**\n * @param {import('./iterateJsdoc.js').Context[]} contexts\n * @param {import('./iterateJsdoc.js').CheckJsdoc} checkJsdoc\n * @param {import('@es-joy/jsdoccomment').CommentHandler} [handler]\n * @returns {import('eslint').Rule.RuleListener}\n */\nconst getContextObject = (contexts, checkJsdoc, handler) => {\n /** @type {import('eslint').Rule.RuleListener} */\n const properties = {};\n\n for (const [\n idx,\n prop,\n ] of contexts.entries()) {\n /** @type {string} */\n let property;\n\n /** @type {(node: import('eslint').Rule.Node) => void} */\n let value;\n\n if (typeof prop === 'object') {\n const selInfo = {\n lastIndex: idx,\n selector: prop.context,\n };\n if (prop.comment) {\n property = /** @type {string} */ (prop.context);\n value = checkJsdoc.bind(\n null,\n {\n ...selInfo,\n comment: prop.comment,\n },\n /**\n * @type {(jsdoc: import('@es-joy/jsdoccomment').JsdocBlockWithInline) => boolean}\n */\n (/** @type {import('@es-joy/jsdoccomment').CommentHandler} */ (\n handler\n ).bind(null, prop.comment)),\n );\n } else {\n property = /** @type {string} */ (prop.context);\n value = checkJsdoc.bind(null, selInfo, null);\n }\n } else {\n const selInfo = {\n lastIndex: idx,\n selector: prop,\n };\n property = prop;\n value = checkJsdoc.bind(null, selInfo, null);\n }\n\n const old = /**\n * @type {((node: import('eslint').Rule.Node) => void)}\n */ (properties[property]);\n properties[property] = old ?\n /**\n * @type {((node: import('eslint').Rule.Node) => void)}\n */\n function (node) {\n old(node);\n value(node);\n } :\n value;\n }\n\n return properties;\n};\n\nconst tagsWithNamesAndDescriptions = new Set([\n 'param', 'arg', 'argument', 'property', 'prop',\n 'template',\n\n // These two are parsed by our custom parser as though having a `name`\n 'returns', 'return',\n]);\n\n/**\n * @typedef {{\n * [key: string]: false|string|\n * {message: string, replacement?: string}\n * }} TagNamePreference\n */\n\n/**\n * @param {import('eslint').Rule.RuleContext} context\n * @param {ParserMode|undefined} mode\n * @param {import('comment-parser').Spec[]} tags\n * @returns {{\n * tagsWithNames: import('comment-parser').Spec[],\n * tagsWithoutNames: import('comment-parser').Spec[]\n * }}\n */\nconst getTagsByType = (context, mode, tags) => {\n /**\n * @type {import('comment-parser').Spec[]}\n */\n const tagsWithoutNames = [];\n const tagsWithNames = tags.filter((tag) => {\n const {\n tag: tagName,\n } = tag;\n const tagWithName = tagsWithNamesAndDescriptions.has(tagName);\n if (!tagWithName) {\n tagsWithoutNames.push(tag);\n }\n\n return tagWithName;\n });\n\n return {\n tagsWithNames,\n tagsWithoutNames,\n };\n};\n\n/**\n * @param {import('eslint').SourceCode|{\n * text: string\n * }} sourceCode\n * @returns {string}\n */\nconst getIndent = (sourceCode) => {\n return (sourceCode.text.match(/^\\n*([ \\t]+)/u)?.[