iterateJsdoc.js 61 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = iterateJsdoc;
  6. exports.getSettings = void 0;
  7. Object.defineProperty(exports, "parseComment", {
  8. enumerable: true,
  9. get: function () {
  10. return _jsdoccomment.parseComment;
  11. }
  12. });
  13. var _jsdocUtils = _interopRequireDefault(require("./jsdocUtils.js"));
  14. var _jsdoccomment = require("@es-joy/jsdoccomment");
  15. var _commentParser = require("comment-parser");
  16. var _esquery = _interopRequireDefault(require("esquery"));
  17. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  18. /**
  19. * @typedef {number} Integer
  20. */
  21. /**
  22. * @typedef {import('@es-joy/jsdoccomment').JsdocBlockWithInline} JsdocBlockWithInline
  23. */
  24. /**
  25. * @typedef {{
  26. * disallowName?: string,
  27. * allowName?: string,
  28. * context?: string,
  29. * comment?: string,
  30. * tags?: string[],
  31. * replacement?: string,
  32. * minimum?: Integer,
  33. * message?: string,
  34. * forceRequireReturn?: boolean
  35. * }} ContextObject
  36. */
  37. /**
  38. * @typedef {string|ContextObject} Context
  39. */
  40. /**
  41. * @callback CheckJsdoc
  42. * @param {{
  43. * lastIndex?: Integer,
  44. * isFunctionContext?: boolean,
  45. * selector?: string,
  46. * comment?: string
  47. * }} info
  48. * @param {null|((jsdoc: import('@es-joy/jsdoccomment').JsdocBlockWithInline) => boolean|undefined)} handler
  49. * @param {import('eslint').Rule.Node} node
  50. * @returns {void}
  51. */
  52. /**
  53. * @callback ForEachPreferredTag
  54. * @param {string} tagName
  55. * @param {(
  56. * matchingJsdocTag: import('@es-joy/jsdoccomment').JsdocTagWithInline,
  57. * targetTagName: string
  58. * ) => void} arrayHandler
  59. * @param {boolean} [skipReportingBlockedTag]
  60. * @returns {void}
  61. */
  62. /**
  63. * @callback ReportSettings
  64. * @param {string} message
  65. * @returns {void}
  66. */
  67. /**
  68. * @callback ParseClosureTemplateTag
  69. * @param {import('comment-parser').Spec} tag
  70. * @returns {string[]}
  71. */
  72. /**
  73. * @callback GetPreferredTagNameObject
  74. * @param {{
  75. * tagName: string
  76. * }} cfg
  77. * @returns {string|false|{
  78. * message: string;
  79. * replacement?: string|undefined
  80. * }|{
  81. * blocked: true,
  82. * tagName: string
  83. * }}
  84. */
  85. /**
  86. * @typedef {{
  87. * forEachPreferredTag: ForEachPreferredTag,
  88. * reportSettings: ReportSettings,
  89. * parseClosureTemplateTag: ParseClosureTemplateTag,
  90. * getPreferredTagNameObject: GetPreferredTagNameObject,
  91. * pathDoesNotBeginWith: import('./jsdocUtils.js').PathDoesNotBeginWith
  92. * }} BasicUtils
  93. */
  94. /**
  95. * @callback IsIteratingFunction
  96. * @returns {boolean}
  97. */
  98. /**
  99. * @callback IsVirtualFunction
  100. * @returns {boolean}
  101. */
  102. /**
  103. * @callback Stringify
  104. * @param {import('comment-parser').Block} tagBlock
  105. * @param {boolean} [specRewire]
  106. * @returns {string}
  107. */
  108. /**
  109. * @callback ReportJSDoc
  110. * @param {string} msg
  111. * @param {null|import('comment-parser').Spec|{line: Integer, column?: Integer}} [tag]
  112. * @param {(() => void)|null} [handler]
  113. * @param {boolean} [specRewire]
  114. * @param {undefined|{
  115. * [key: string]: string
  116. * }} [data]
  117. */
  118. /**
  119. * @callback GetRegexFromString
  120. * @param {string} str
  121. * @param {string} [requiredFlags]
  122. * @returns {RegExp}
  123. */
  124. /**
  125. * @callback GetTagDescription
  126. * @param {import('comment-parser').Spec} tg
  127. * @param {boolean} [returnArray]
  128. * @returns {string[]|string}
  129. */
  130. /**
  131. * @callback SetTagDescription
  132. * @param {import('comment-parser').Spec} tg
  133. * @param {RegExp} matcher
  134. * @param {(description: string) => string} setter
  135. * @returns {Integer}
  136. */
  137. /**
  138. * @callback GetDescription
  139. * @returns {{
  140. * description: string,
  141. * descriptions: string[],
  142. * lastDescriptionLine: Integer
  143. * }}
  144. */
  145. /**
  146. * @callback SetBlockDescription
  147. * @param {(
  148. * info: {
  149. * delimiter: string,
  150. * postDelimiter: string,
  151. * start: string
  152. * },
  153. * seedTokens: (
  154. * tokens?: Partial<import('comment-parser').Tokens>
  155. * ) => import('comment-parser').Tokens,
  156. * descLines: string[]
  157. * ) => import('comment-parser').Line[]} setter
  158. * @returns {void}
  159. */
  160. /**
  161. * @callback SetDescriptionLines
  162. * @param {RegExp} matcher
  163. * @param {(description: string) => string} setter
  164. * @returns {Integer}
  165. */
  166. /**
  167. * @callback ChangeTag
  168. * @param {import('comment-parser').Spec} tag
  169. * @param {...Partial<import('comment-parser').Tokens>} tokens
  170. * @returns {void}
  171. */
  172. /**
  173. * @callback SetTag
  174. * @param {import('comment-parser').Spec & {
  175. * line: Integer
  176. * }} tag
  177. * @param {Partial<import('comment-parser').Tokens>} [tokens]
  178. * @returns {void}
  179. */
  180. /**
  181. * @callback RemoveTag
  182. * @param {Integer} tagIndex
  183. * @param {{
  184. * removeEmptyBlock?: boolean,
  185. * tagSourceOffset?: Integer
  186. * }} [cfg]
  187. * @returns {void}
  188. */
  189. /**
  190. * @callback AddTag
  191. * @param {string} targetTagName
  192. * @param {Integer} [number]
  193. * @param {import('comment-parser').Tokens|{}} [tokens]
  194. * @returns {void}
  195. */
  196. /**
  197. * @callback GetFirstLine
  198. * @returns {Integer|undefined}
  199. */
  200. /**
  201. * @typedef {(
  202. * tokens?: Partial<import('comment-parser').Tokens> | undefined
  203. * ) => import('comment-parser').Tokens} SeedTokens
  204. */
  205. /**
  206. * Sets tokens to empty string.
  207. * @callback EmptyTokens
  208. * @param {import('comment-parser').Tokens} tokens
  209. * @returns {void}
  210. */
  211. /**
  212. * @callback AddLine
  213. * @param {Integer} sourceIndex
  214. * @param {Partial<import('comment-parser').Tokens>} tokens
  215. * @returns {void}
  216. */
  217. /**
  218. * @callback AddLines
  219. * @param {Integer} tagIndex
  220. * @param {Integer} tagSourceOffset
  221. * @param {Integer} numLines
  222. * @returns {void}
  223. */
  224. /**
  225. * @callback MakeMultiline
  226. * @returns {void}
  227. */
  228. /**
  229. * @callback GetFunctionParameterNames
  230. * @param {boolean} [useDefaultObjectProperties]
  231. * @returns {import('./jsdocUtils.js').ParamNameInfo[]}
  232. */
  233. /**
  234. * @callback HasParams
  235. * @returns {Integer}
  236. */
  237. /**
  238. * @callback IsGenerator
  239. * @returns {boolean}
  240. */
  241. /**
  242. * @callback IsConstructor
  243. * @returns {boolean}
  244. */
  245. /**
  246. * @callback GetJsdocTagsDeep
  247. * @param {string} tagName
  248. * @returns {false|{
  249. * idx: Integer,
  250. * name: string,
  251. * type: string
  252. * }[]}
  253. */
  254. /**
  255. * @callback GetPreferredTagName
  256. * @param {{
  257. * tagName: string,
  258. * skipReportingBlockedTag?: boolean,
  259. * allowObjectReturn?: boolean,
  260. * defaultMessage?: string
  261. * }} cfg
  262. * @returns {string|undefined|false|{
  263. * message: string;
  264. * replacement?: string|undefined;
  265. * }|{
  266. * blocked: true,
  267. * tagName: string
  268. * }}
  269. */
  270. /**
  271. * @callback IsValidTag
  272. * @param {string} name
  273. * @param {string[]} definedTags
  274. * @returns {boolean}
  275. */
  276. /**
  277. * @callback HasATag
  278. * @param {string[]} names
  279. * @returns {boolean}
  280. */
  281. /**
  282. * @callback HasTag
  283. * @param {string} name
  284. * @returns {boolean}
  285. */
  286. /**
  287. * @callback ComparePaths
  288. * @param {string} name
  289. * @returns {(otherPathName: string) => boolean}
  290. */
  291. /**
  292. * @callback DropPathSegmentQuotes
  293. * @param {string} name
  294. * @returns {string}
  295. */
  296. /**
  297. * @callback AvoidDocs
  298. * @returns {boolean}
  299. */
  300. /**
  301. * @callback TagMightHaveNamePositionTypePosition
  302. * @param {string} tagName
  303. * @param {import('./getDefaultTagStructureForMode.js').
  304. * TagStructure[]} [otherModeMaps]
  305. * @returns {boolean|{otherMode: true}}
  306. */
  307. /**
  308. * @callback TagMustHave
  309. * @param {string} tagName
  310. * @param {import('./getDefaultTagStructureForMode.js').
  311. * TagStructure[]} otherModeMaps
  312. * @returns {boolean|{
  313. * otherMode: false
  314. * }}
  315. */
  316. /**
  317. * @callback TagMissingRequiredTypeOrNamepath
  318. * @param {import('comment-parser').Spec} tag
  319. * @param {import('./getDefaultTagStructureForMode.js').
  320. * TagStructure[]} otherModeMaps
  321. * @returns {boolean|{
  322. * otherMode: false
  323. * }}
  324. */
  325. /**
  326. * @callback IsNamepathX
  327. * @param {string} tagName
  328. * @returns {boolean}
  329. */
  330. /**
  331. * @callback GetTagStructureForMode
  332. * @param {import('./jsdocUtils.js').ParserMode} mde
  333. * @returns {import('./getDefaultTagStructureForMode.js').TagStructure}
  334. */
  335. /**
  336. * @callback MayBeUndefinedTypeTag
  337. * @param {import('comment-parser').Spec} tag
  338. * @returns {boolean}
  339. */
  340. /**
  341. * @callback HasValueOrExecutorHasNonEmptyResolveValue
  342. * @param {boolean} anyPromiseAsReturn
  343. * @param {boolean} [allBranches]
  344. * @returns {boolean}
  345. */
  346. /**
  347. * @callback HasYieldValue
  348. * @returns {boolean}
  349. */
  350. /**
  351. * @callback HasYieldReturnValue
  352. * @returns {boolean}
  353. */
  354. /**
  355. * @callback HasThrowValue
  356. * @returns {boolean}
  357. */
  358. /**
  359. * @callback IsAsync
  360. * @returns {boolean|undefined}
  361. */
  362. /**
  363. * @callback GetTags
  364. * @param {string} tagName
  365. * @returns {import('comment-parser').Spec[]}
  366. */
  367. /**
  368. * @callback GetPresentTags
  369. * @param {string[]} tagList
  370. * @returns {import('@es-joy/jsdoccomment').JsdocTagWithInline[]}
  371. */
  372. /**
  373. * @callback FilterTags
  374. * @param {(tag: import('@es-joy/jsdoccomment').JsdocTagWithInline) => boolean} filter
  375. * @returns {import('@es-joy/jsdoccomment').JsdocTagWithInline[]}
  376. */
  377. /**
  378. * @callback FilterAllTags
  379. * @param {(tag: (import('comment-parser').Spec|
  380. * import('@es-joy/jsdoccomment').JsdocInlineTagNoType)) => boolean} filter
  381. * @returns {(import('comment-parser').Spec|
  382. * import('@es-joy/jsdoccomment').JsdocInlineTagNoType)[]}
  383. */
  384. /**
  385. * @callback GetTagsByType
  386. * @param {import('comment-parser').Spec[]} tags
  387. * @returns {{
  388. * tagsWithNames: import('comment-parser').Spec[],
  389. * tagsWithoutNames: import('comment-parser').Spec[]
  390. * }}
  391. */
  392. /**
  393. * @callback HasOptionTag
  394. * @param {string} tagName
  395. * @returns {boolean}
  396. */
  397. /**
  398. * @callback GetClassNode
  399. * @returns {Node|null}
  400. */
  401. /**
  402. * @callback GetClassJsdoc
  403. * @returns {null|JsdocBlockWithInline}
  404. */
  405. /**
  406. * @callback ClassHasTag
  407. * @param {string} tagName
  408. * @returns {boolean}
  409. */
  410. /**
  411. * @callback FindContext
  412. * @param {Context[]} contexts
  413. * @param {string|undefined} comment
  414. * @returns {{
  415. * foundContext: Context|undefined,
  416. * contextStr: string
  417. * }}
  418. */
  419. /**
  420. * @typedef {BasicUtils & {
  421. * isIteratingFunction: IsIteratingFunction,
  422. * isVirtualFunction: IsVirtualFunction,
  423. * stringify: Stringify,
  424. * reportJSDoc: ReportJSDoc,
  425. * getRegexFromString: GetRegexFromString,
  426. * getTagDescription: GetTagDescription,
  427. * setTagDescription: SetTagDescription,
  428. * getDescription: GetDescription,
  429. * setBlockDescription: SetBlockDescription,
  430. * setDescriptionLines: SetDescriptionLines,
  431. * changeTag: ChangeTag,
  432. * setTag: SetTag,
  433. * removeTag: RemoveTag,
  434. * addTag: AddTag,
  435. * getFirstLine: GetFirstLine,
  436. * seedTokens: SeedTokens,
  437. * emptyTokens: EmptyTokens,
  438. * addLine: AddLine,
  439. * addLines: AddLines,
  440. * makeMultiline: MakeMultiline,
  441. * flattenRoots: import('./jsdocUtils.js').FlattenRoots,
  442. * getFunctionParameterNames: GetFunctionParameterNames,
  443. * hasParams: HasParams,
  444. * isGenerator: IsGenerator,
  445. * isConstructor: IsConstructor,
  446. * getJsdocTagsDeep: GetJsdocTagsDeep,
  447. * getPreferredTagName: GetPreferredTagName,
  448. * isValidTag: IsValidTag,
  449. * hasATag: HasATag,
  450. * hasTag: HasTag,
  451. * comparePaths: ComparePaths,
  452. * dropPathSegmentQuotes: DropPathSegmentQuotes,
  453. * avoidDocs: AvoidDocs,
  454. * tagMightHaveNamePosition: TagMightHaveNamePositionTypePosition,
  455. * tagMightHaveTypePosition: TagMightHaveNamePositionTypePosition,
  456. * tagMustHaveNamePosition: TagMustHave,
  457. * tagMustHaveTypePosition: TagMustHave,
  458. * tagMissingRequiredTypeOrNamepath: TagMissingRequiredTypeOrNamepath,
  459. * isNamepathDefiningTag: IsNamepathX,
  460. * isNamepathReferencingTag: IsNamepathX,
  461. * isNamepathOrUrlReferencingTag: IsNamepathX,
  462. * tagMightHaveNamepath: IsNamepathX,
  463. * getTagStructureForMode: GetTagStructureForMode,
  464. * mayBeUndefinedTypeTag: MayBeUndefinedTypeTag,
  465. * hasValueOrExecutorHasNonEmptyResolveValue: HasValueOrExecutorHasNonEmptyResolveValue,
  466. * hasYieldValue: HasYieldValue,
  467. * hasYieldReturnValue: HasYieldReturnValue,
  468. * hasThrowValue: HasThrowValue,
  469. * isAsync: IsAsync,
  470. * getTags: GetTags,
  471. * getPresentTags: GetPresentTags,
  472. * filterTags: FilterTags,
  473. * filterAllTags: FilterAllTags,
  474. * getTagsByType: GetTagsByType,
  475. * hasOptionTag: HasOptionTag,
  476. * getClassNode: GetClassNode,
  477. * getClassJsdoc: GetClassJsdoc,
  478. * classHasTag: ClassHasTag,
  479. * findContext: FindContext
  480. * }} Utils
  481. */
  482. const {
  483. rewireSpecs,
  484. seedTokens
  485. } = _commentParser.util;
  486. // todo: Change these `any` types once importing types properly.
  487. /**
  488. * Should use ESLint rule's typing.
  489. * @typedef {import('eslint').Rule.RuleMetaData} EslintRuleMeta
  490. */
  491. /**
  492. * A plain object for tracking state as needed by rules across iterations.
  493. * @typedef {{
  494. * globalTags: {},
  495. * hasDuplicates: {
  496. * [key: string]: boolean
  497. * },
  498. * selectorMap: {
  499. * [selector: string]: {
  500. * [comment: string]: Integer
  501. * }
  502. * },
  503. * hasTag: {
  504. * [key: string]: boolean
  505. * },
  506. * hasNonComment: number,
  507. * hasNonCommentBeforeTag: {
  508. * [key: string]: boolean|number
  509. * }
  510. * }} StateObject
  511. */
  512. /**
  513. * The Node AST as supplied by the parser.
  514. * @typedef {import('eslint').Rule.Node} Node
  515. */
  516. /*
  517. const {
  518. align as commentAlign,
  519. flow: commentFlow,
  520. indent: commentIndent,
  521. } = transforms;
  522. */
  523. const globalState = new Map();
  524. /**
  525. * @param {import('eslint').Rule.RuleContext} context
  526. * @param {{
  527. * tagNamePreference?: import('./jsdocUtils.js').TagNamePreference,
  528. * mode?: import('./jsdocUtils.js').ParserMode
  529. * }} cfg
  530. * @returns {BasicUtils}
  531. */
  532. const getBasicUtils = (context, {
  533. tagNamePreference,
  534. mode
  535. }) => {
  536. /** @type {BasicUtils} */
  537. const utils = {};
  538. /** @type {ReportSettings} */
  539. utils.reportSettings = message => {
  540. context.report({
  541. loc: {
  542. end: {
  543. column: 1,
  544. line: 1
  545. },
  546. start: {
  547. column: 1,
  548. line: 1
  549. }
  550. },
  551. message
  552. });
  553. };
  554. /** @type {ParseClosureTemplateTag} */
  555. utils.parseClosureTemplateTag = tag => {
  556. return _jsdocUtils.default.parseClosureTemplateTag(tag);
  557. };
  558. utils.pathDoesNotBeginWith = _jsdocUtils.default.pathDoesNotBeginWith;
  559. /** @type {GetPreferredTagNameObject} */
  560. utils.getPreferredTagNameObject = ({
  561. tagName
  562. }) => {
  563. const ret = _jsdocUtils.default.getPreferredTagName(context, /** @type {import('./jsdocUtils.js').ParserMode} */mode, tagName, tagNamePreference);
  564. const isObject = ret && typeof ret === 'object';
  565. if (ret === false || isObject && !ret.replacement) {
  566. return {
  567. blocked: true,
  568. tagName
  569. };
  570. }
  571. return ret;
  572. };
  573. return utils;
  574. };
  575. /**
  576. * @callback Report
  577. * @param {string} message
  578. * @param {import('eslint').Rule.ReportFixer|null} [fix]
  579. * @param {null|
  580. * {line?: Integer, column?: Integer}|
  581. * import('comment-parser').Spec & {line?: Integer}
  582. * } [jsdocLoc]
  583. * @param {undefined|{
  584. * [key: string]: string
  585. * }} [data]
  586. * @returns {void}
  587. */
  588. /**
  589. * @param {Node|null} node
  590. * @param {JsdocBlockWithInline} jsdoc
  591. * @param {import('eslint').AST.Token} jsdocNode
  592. * @param {Settings} settings
  593. * @param {Report} report
  594. * @param {import('eslint').Rule.RuleContext} context
  595. * @param {import('eslint').SourceCode} sc
  596. * @param {boolean|undefined} iteratingAll
  597. * @param {RuleConfig} ruleConfig
  598. * @param {string} indent
  599. * @returns {Utils}
  600. */
  601. const getUtils = (node, jsdoc, jsdocNode, settings, report, context, sc, iteratingAll, ruleConfig, indent) => {
  602. /* istanbul ignore next */
  603. const ancestors = /** @type {import('eslint').Rule.Node[]} */node ? sc.getAncestors ? sc.getAncestors(node) : context.getAncestors() : [];
  604. // istanbul ignore next -- Fallback to deprecated method
  605. const {
  606. sourceCode = context.getSourceCode()
  607. } = context;
  608. const utils = /** @type {Utils} */getBasicUtils(context, settings);
  609. const {
  610. tagNamePreference,
  611. overrideReplacesDocs,
  612. ignoreReplacesDocs,
  613. implementsReplacesDocs,
  614. augmentsExtendsReplacesDocs,
  615. maxLines,
  616. minLines,
  617. mode
  618. } = settings;
  619. /** @type {IsIteratingFunction} */
  620. utils.isIteratingFunction = () => {
  621. return !iteratingAll || ['MethodDefinition', 'ArrowFunctionExpression', 'FunctionDeclaration', 'FunctionExpression'].includes(String(node && node.type));
  622. };
  623. /** @type {IsVirtualFunction} */
  624. utils.isVirtualFunction = () => {
  625. return Boolean(iteratingAll) && utils.hasATag(['callback', 'function', 'func', 'method']);
  626. };
  627. /** @type {Stringify} */
  628. utils.stringify = (tagBlock, specRewire) => {
  629. let block;
  630. if (specRewire) {
  631. block = rewireSpecs(tagBlock);
  632. }
  633. return (0, _commentParser.stringify)( /** @type {import('comment-parser').Block} */
  634. specRewire ? block : tagBlock);
  635. };
  636. /** @type {ReportJSDoc} */
  637. utils.reportJSDoc = (msg, tag, handler, specRewire, data) => {
  638. report(msg, handler ? /** @type {import('eslint').Rule.ReportFixer} */fixer => {
  639. handler();
  640. const replacement = utils.stringify(jsdoc, specRewire);
  641. if (!replacement) {
  642. const text = sourceCode.getText();
  643. const lastLineBreakPos = text.slice(0, jsdocNode.range[0]).search(/\n[ \t]*$/u);
  644. if (lastLineBreakPos > -1) {
  645. return fixer.removeRange([lastLineBreakPos, jsdocNode.range[1]]);
  646. }
  647. return fixer.removeRange(/\s/u.test(text.charAt(jsdocNode.range[1])) ? [jsdocNode.range[0], jsdocNode.range[1] + 1] : jsdocNode.range);
  648. }
  649. return fixer.replaceText(jsdocNode, replacement);
  650. } : null, tag, data);
  651. };
  652. /** @type {GetRegexFromString} */
  653. utils.getRegexFromString = (str, requiredFlags) => {
  654. return _jsdocUtils.default.getRegexFromString(str, requiredFlags);
  655. };
  656. /** @type {GetTagDescription} */
  657. utils.getTagDescription = (tg, returnArray) => {
  658. /**
  659. * @type {string[]}
  660. */
  661. const descriptions = [];
  662. tg.source.some(({
  663. tokens: {
  664. end,
  665. lineEnd,
  666. postDelimiter,
  667. tag,
  668. postTag,
  669. name,
  670. type,
  671. description
  672. }
  673. }) => {
  674. const desc = (tag && postTag || !tag && !name && !type && postDelimiter || ''
  675. // Remove space
  676. ).slice(1) + (description || '') + (lineEnd || '');
  677. if (end) {
  678. if (desc) {
  679. descriptions.push(desc);
  680. }
  681. return true;
  682. }
  683. descriptions.push(desc);
  684. return false;
  685. });
  686. return returnArray ? descriptions : descriptions.join('\n');
  687. };
  688. /** @type {SetTagDescription} */
  689. utils.setTagDescription = (tg, matcher, setter) => {
  690. let finalIdx = 0;
  691. tg.source.some(({
  692. tokens: {
  693. description
  694. }
  695. }, idx) => {
  696. if (description && matcher.test(description)) {
  697. tg.source[idx].tokens.description = setter(description);
  698. finalIdx = idx;
  699. return true;
  700. }
  701. return false;
  702. });
  703. return finalIdx;
  704. };
  705. /** @type {GetDescription} */
  706. utils.getDescription = () => {
  707. /** @type {string[]} */
  708. const descriptions = [];
  709. let lastDescriptionLine = 0;
  710. let tagsBegun = false;
  711. jsdoc.source.some(({
  712. tokens: {
  713. description,
  714. tag,
  715. end
  716. }
  717. }, idx) => {
  718. if (tag) {
  719. tagsBegun = true;
  720. }
  721. if (idx && (tag || end)) {
  722. lastDescriptionLine = idx - 1;
  723. if (!tagsBegun && description) {
  724. descriptions.push(description);
  725. }
  726. return true;
  727. }
  728. if (!tagsBegun && (idx || description)) {
  729. descriptions.push(description || (descriptions.length ? '' : '\n'));
  730. }
  731. return false;
  732. });
  733. return {
  734. description: descriptions.join('\n'),
  735. descriptions,
  736. lastDescriptionLine
  737. };
  738. };
  739. /** @type {SetBlockDescription} */
  740. utils.setBlockDescription = setter => {
  741. /** @type {string[]} */
  742. const descLines = [];
  743. /**
  744. * @type {undefined|Integer}
  745. */
  746. let startIdx;
  747. /**
  748. * @type {undefined|Integer}
  749. */
  750. let endIdx;
  751. /**
  752. * @type {undefined|{
  753. * delimiter: string,
  754. * postDelimiter: string,
  755. * start: string
  756. * }}
  757. */
  758. let info;
  759. jsdoc.source.some(({
  760. tokens: {
  761. description,
  762. start,
  763. delimiter,
  764. postDelimiter,
  765. tag,
  766. end
  767. }
  768. }, idx) => {
  769. if (delimiter === '/**') {
  770. return false;
  771. }
  772. if (startIdx === undefined) {
  773. startIdx = idx;
  774. info = {
  775. delimiter,
  776. postDelimiter,
  777. start
  778. };
  779. }
  780. if (tag || end) {
  781. endIdx = idx;
  782. return true;
  783. }
  784. descLines.push(description);
  785. return false;
  786. });
  787. /* istanbul ignore else -- Won't be called if missing */
  788. if (descLines.length) {
  789. jsdoc.source.splice( /** @type {Integer} */startIdx, /** @type {Integer} */endIdx - ( /** @type {Integer} */startIdx), ...setter(
  790. /**
  791. * @type {{
  792. * delimiter: string,
  793. * postDelimiter: string,
  794. * start: string
  795. * }}
  796. */
  797. info, seedTokens, descLines));
  798. }
  799. };
  800. /** @type {SetDescriptionLines} */
  801. utils.setDescriptionLines = (matcher, setter) => {
  802. let finalIdx = 0;
  803. jsdoc.source.some(({
  804. tokens: {
  805. description,
  806. tag,
  807. end
  808. }
  809. }, idx) => {
  810. // istanbul ignore if -- Already checked
  811. if (idx && (tag || end)) {
  812. return true;
  813. }
  814. if (description && matcher.test(description)) {
  815. jsdoc.source[idx].tokens.description = setter(description);
  816. finalIdx = idx;
  817. return true;
  818. }
  819. return false;
  820. });
  821. return finalIdx;
  822. };
  823. /** @type {ChangeTag} */
  824. utils.changeTag = (tag, ...tokens) => {
  825. for (const [idx, src] of tag.source.entries()) {
  826. src.tokens = {
  827. ...src.tokens,
  828. ...tokens[idx]
  829. };
  830. }
  831. };
  832. /** @type {SetTag} */
  833. utils.setTag = (tag, tokens) => {
  834. tag.source = [{
  835. number: tag.line,
  836. // Or tag.source[0].number?
  837. source: '',
  838. tokens: seedTokens({
  839. delimiter: '*',
  840. postDelimiter: ' ',
  841. start: indent + ' ',
  842. tag: '@' + tag.tag,
  843. ...tokens
  844. })
  845. }];
  846. };
  847. /** @type {RemoveTag} */
  848. utils.removeTag = (tagIndex, {
  849. removeEmptyBlock = false,
  850. tagSourceOffset = 0
  851. } = {}) => {
  852. const {
  853. source: tagSource
  854. } = jsdoc.tags[tagIndex];
  855. /** @type {Integer|undefined} */
  856. let lastIndex;
  857. const firstNumber = jsdoc.source[0].number;
  858. tagSource.some(({
  859. number
  860. }, tagIdx) => {
  861. const sourceIndex = jsdoc.source.findIndex(({
  862. number: srcNumber
  863. }) => {
  864. return number === srcNumber;
  865. });
  866. // istanbul ignore else
  867. if (sourceIndex > -1) {
  868. let spliceCount = 1;
  869. tagSource.slice(tagIdx + 1).some(({
  870. tokens: {
  871. tag,
  872. end: ending
  873. }
  874. }) => {
  875. if (!tag && !ending) {
  876. spliceCount++;
  877. return false;
  878. }
  879. return true;
  880. });
  881. const spliceIdx = sourceIndex + tagSourceOffset;
  882. const {
  883. delimiter,
  884. end
  885. } = jsdoc.source[spliceIdx].tokens;
  886. /* istanbul ignore if -- Currently want to clear entirely if removing tags */
  887. if (spliceIdx === 0 && jsdoc.tags.length >= 2 || !removeEmptyBlock && (end || delimiter === '/**')) {
  888. const {
  889. tokens
  890. } = jsdoc.source[spliceIdx];
  891. for (const item of ['postDelimiter', 'tag', 'postTag', 'type', 'postType', 'name', 'postName', 'description']) {
  892. tokens[(
  893. /**
  894. * @type {"postDelimiter"|"tag"|"type"|"postType"|
  895. * "postTag"|"name"|"postName"|"description"}
  896. */
  897. item)] = '';
  898. }
  899. } else {
  900. jsdoc.source.splice(spliceIdx, spliceCount - tagSourceOffset + (spliceIdx ? 0 : jsdoc.source.length));
  901. tagSource.splice(tagIdx + tagSourceOffset, spliceCount - tagSourceOffset + (spliceIdx ? 0 : jsdoc.source.length));
  902. }
  903. lastIndex = sourceIndex;
  904. return true;
  905. }
  906. // istanbul ignore next
  907. return false;
  908. });
  909. for (const [idx, src] of jsdoc.source.slice(lastIndex).entries()) {
  910. src.number = firstNumber + ( /** @type {Integer} */lastIndex) + idx;
  911. }
  912. // Todo: Once rewiring of tags may be fixed in comment-parser to reflect
  913. // missing tags, this step should be added here (so that, e.g.,
  914. // if accessing `jsdoc.tags`, such as to add a new tag, the
  915. // correct information will be available)
  916. };
  917. /** @type {AddTag} */
  918. utils.addTag = (targetTagName, number = ((_jsdoc$tags => (_jsdoc$tags = jsdoc.tags[jsdoc.tags.length - 1]) === null || _jsdoc$tags === void 0 || (_jsdoc$tags = _jsdoc$tags.source[0]) === null || _jsdoc$tags === void 0 ? void 0 : _jsdoc$tags.number)() ?? jsdoc.source.findIndex(({
  919. tokens: {
  920. tag
  921. }
  922. }) => {
  923. return tag;
  924. }) - 1) + 1, tokens = {}) => {
  925. jsdoc.source.splice(number, 0, {
  926. number,
  927. source: '',
  928. tokens: seedTokens({
  929. delimiter: '*',
  930. postDelimiter: ' ',
  931. start: indent + ' ',
  932. tag: `@${targetTagName}`,
  933. ...tokens
  934. })
  935. });
  936. for (const src of jsdoc.source.slice(number + 1)) {
  937. src.number++;
  938. }
  939. };
  940. /** @type {GetFirstLine} */
  941. utils.getFirstLine = () => {
  942. let firstLine;
  943. for (const {
  944. number,
  945. tokens: {
  946. tag
  947. }
  948. } of jsdoc.source) {
  949. if (tag) {
  950. firstLine = number;
  951. break;
  952. }
  953. }
  954. return firstLine;
  955. };
  956. /** @type {SeedTokens} */
  957. utils.seedTokens = seedTokens;
  958. /** @type {EmptyTokens} */
  959. utils.emptyTokens = tokens => {
  960. for (const prop of ['start', 'postDelimiter', 'tag', 'type', 'postType', 'postTag', 'name', 'postName', 'description', 'end', 'lineEnd']) {
  961. tokens[(
  962. /**
  963. * @type {"start"|"postDelimiter"|"tag"|"type"|"postType"|
  964. * "postTag"|"name"|"postName"|"description"|"end"|"lineEnd"}
  965. */
  966. prop)] = '';
  967. }
  968. };
  969. /** @type {AddLine} */
  970. utils.addLine = (sourceIndex, tokens) => {
  971. var _jsdoc$source;
  972. const number = (((_jsdoc$source = jsdoc.source[sourceIndex - 1]) === null || _jsdoc$source === void 0 ? void 0 : _jsdoc$source.number) || 0) + 1;
  973. jsdoc.source.splice(sourceIndex, 0, {
  974. number,
  975. source: '',
  976. tokens: seedTokens(tokens)
  977. });
  978. for (const src of jsdoc.source.slice(number + 1)) {
  979. src.number++;
  980. }
  981. // If necessary, we can rewire the tags (misnamed method)
  982. // rewireSource(jsdoc);
  983. };
  984. /** @type {AddLines} */
  985. utils.addLines = (tagIndex, tagSourceOffset, numLines) => {
  986. const {
  987. source: tagSource
  988. } = jsdoc.tags[tagIndex];
  989. /** @type {Integer|undefined} */
  990. let lastIndex;
  991. const firstNumber = jsdoc.source[0].number;
  992. tagSource.some(({
  993. number
  994. }) => {
  995. const makeLine = () => {
  996. return {
  997. number,
  998. source: '',
  999. tokens: seedTokens({
  1000. delimiter: '*',
  1001. start: indent + ' '
  1002. })
  1003. };
  1004. };
  1005. const makeLines = () => {
  1006. return Array.from({
  1007. length: numLines
  1008. }, makeLine);
  1009. };
  1010. const sourceIndex = jsdoc.source.findIndex(({
  1011. number: srcNumber,
  1012. tokens: {
  1013. end
  1014. }
  1015. }) => {
  1016. return number === srcNumber && !end;
  1017. });
  1018. // istanbul ignore else
  1019. if (sourceIndex > -1) {
  1020. const lines = makeLines();
  1021. jsdoc.source.splice(sourceIndex + tagSourceOffset, 0, ...lines);
  1022. // tagSource.splice(tagIdx + 1, 0, ...makeLines());
  1023. lastIndex = sourceIndex;
  1024. return true;
  1025. }
  1026. // istanbul ignore next
  1027. return false;
  1028. });
  1029. for (const [idx, src] of jsdoc.source.slice(lastIndex).entries()) {
  1030. src.number = firstNumber + ( /** @type {Integer} */lastIndex) + idx;
  1031. }
  1032. };
  1033. /** @type {MakeMultiline} */
  1034. utils.makeMultiline = () => {
  1035. const {
  1036. source: [{
  1037. tokens
  1038. }]
  1039. } = jsdoc;
  1040. const {
  1041. postDelimiter,
  1042. description,
  1043. lineEnd,
  1044. tag,
  1045. name,
  1046. type
  1047. } = tokens;
  1048. let {
  1049. tokens: {
  1050. postName,
  1051. postTag,
  1052. postType
  1053. }
  1054. } = jsdoc.source[0];
  1055. // Strip trailing leftovers from single line ending
  1056. if (!description) {
  1057. if (postName) {
  1058. postName = '';
  1059. } else if (postType) {
  1060. postType = '';
  1061. } else /* istanbul ignore else -- `comment-parser` prevents empty blocks currently per https://github.com/syavorsky/comment-parser/issues/128 */if (postTag) {
  1062. postTag = '';
  1063. }
  1064. }
  1065. utils.emptyTokens(tokens);
  1066. utils.addLine(1, {
  1067. delimiter: '*',
  1068. // If a description were present, it may have whitespace attached
  1069. // due to being at the end of the single line
  1070. description: description.trimEnd(),
  1071. name,
  1072. postDelimiter,
  1073. postName,
  1074. postTag,
  1075. postType,
  1076. start: indent + ' ',
  1077. tag,
  1078. type
  1079. });
  1080. utils.addLine(2, {
  1081. end: '*/',
  1082. lineEnd,
  1083. start: indent + ' '
  1084. });
  1085. };
  1086. /**
  1087. * @type {import('./jsdocUtils.js').FlattenRoots}
  1088. */
  1089. utils.flattenRoots = _jsdocUtils.default.flattenRoots;
  1090. /** @type {GetFunctionParameterNames} */
  1091. utils.getFunctionParameterNames = useDefaultObjectProperties => {
  1092. return _jsdocUtils.default.getFunctionParameterNames(node, useDefaultObjectProperties);
  1093. };
  1094. /** @type {HasParams} */
  1095. utils.hasParams = () => {
  1096. return _jsdocUtils.default.hasParams( /** @type {Node} */node);
  1097. };
  1098. /** @type {IsGenerator} */
  1099. utils.isGenerator = () => {
  1100. return node !== null && Boolean(
  1101. /**
  1102. * @type {import('estree').FunctionDeclaration|
  1103. * import('estree').FunctionExpression}
  1104. */
  1105. node.generator || node.type === 'MethodDefinition' && node.value.generator || ['ExportNamedDeclaration', 'ExportDefaultDeclaration'].includes(node.type) && /** @type {import('estree').FunctionDeclaration} */
  1106. (
  1107. /**
  1108. * @type {import('estree').ExportNamedDeclaration|
  1109. * import('estree').ExportDefaultDeclaration}
  1110. */
  1111. node.declaration).generator);
  1112. };
  1113. /** @type {IsConstructor} */
  1114. utils.isConstructor = () => {
  1115. return _jsdocUtils.default.isConstructor( /** @type {Node} */node);
  1116. };
  1117. /** @type {GetJsdocTagsDeep} */
  1118. utils.getJsdocTagsDeep = tagName => {
  1119. const name = /** @type {string|false} */utils.getPreferredTagName({
  1120. tagName
  1121. });
  1122. if (!name) {
  1123. return false;
  1124. }
  1125. return _jsdocUtils.default.getJsdocTagsDeep(jsdoc, name);
  1126. };
  1127. /** @type {GetPreferredTagName} */
  1128. utils.getPreferredTagName = ({
  1129. tagName,
  1130. skipReportingBlockedTag = false,
  1131. allowObjectReturn = false,
  1132. defaultMessage = `Unexpected tag \`@${tagName}\``
  1133. }) => {
  1134. const ret = _jsdocUtils.default.getPreferredTagName(context, mode, tagName, tagNamePreference);
  1135. const isObject = ret && typeof ret === 'object';
  1136. if (utils.hasTag(tagName) && (ret === false || isObject && !ret.replacement)) {
  1137. if (skipReportingBlockedTag) {
  1138. return {
  1139. blocked: true,
  1140. tagName
  1141. };
  1142. }
  1143. const message = isObject && ret.message || defaultMessage;
  1144. report(message, null, utils.getTags(tagName)[0]);
  1145. return false;
  1146. }
  1147. return isObject && !allowObjectReturn ? ret.replacement : ret;
  1148. };
  1149. /** @type {IsValidTag} */
  1150. utils.isValidTag = (name, definedTags) => {
  1151. return _jsdocUtils.default.isValidTag(context, mode, name, definedTags);
  1152. };
  1153. /** @type {HasATag} */
  1154. utils.hasATag = names => {
  1155. return _jsdocUtils.default.hasATag(jsdoc, names);
  1156. };
  1157. /** @type {HasTag} */
  1158. utils.hasTag = name => {
  1159. return _jsdocUtils.default.hasTag(jsdoc, name);
  1160. };
  1161. /** @type {ComparePaths} */
  1162. utils.comparePaths = name => {
  1163. return _jsdocUtils.default.comparePaths(name);
  1164. };
  1165. /** @type {DropPathSegmentQuotes} */
  1166. utils.dropPathSegmentQuotes = name => {
  1167. return _jsdocUtils.default.dropPathSegmentQuotes(name);
  1168. };
  1169. /** @type {AvoidDocs} */
  1170. utils.avoidDocs = () => {
  1171. var _context$options$;
  1172. if (ignoreReplacesDocs !== false && (utils.hasTag('ignore') || utils.classHasTag('ignore')) || overrideReplacesDocs !== false && (utils.hasTag('override') || utils.classHasTag('override')) || implementsReplacesDocs !== false && (utils.hasTag('implements') || utils.classHasTag('implements')) || augmentsExtendsReplacesDocs && (utils.hasATag(['augments', 'extends']) || utils.classHasTag('augments') || utils.classHasTag('extends'))) {
  1173. return true;
  1174. }
  1175. if (_jsdocUtils.default.exemptSpeciaMethods(jsdoc, node, context, /** @type {import('json-schema').JSONSchema4|import('json-schema').JSONSchema4[]} */
  1176. ruleConfig.meta.schema)) {
  1177. return true;
  1178. }
  1179. const exemptedBy = ((_context$options$ = context.options[0]) === null || _context$options$ === void 0 ? void 0 : _context$options$.exemptedBy) ?? ['inheritDoc', ...(mode === 'closure' ? [] : ['inheritdoc'])];
  1180. if (exemptedBy.length && utils.getPresentTags(exemptedBy).length) {
  1181. return true;
  1182. }
  1183. return false;
  1184. };
  1185. for (const method of ['tagMightHaveNamePosition', 'tagMightHaveTypePosition']) {
  1186. /** @type {TagMightHaveNamePositionTypePosition} */
  1187. utils[( /** @type {"tagMightHaveNamePosition"|"tagMightHaveTypePosition"} */
  1188. method)] = (tagName, otherModeMaps) => {
  1189. const result = _jsdocUtils.default[( /** @type {"tagMightHaveNamePosition"|"tagMightHaveTypePosition"} */
  1190. method)](tagName);
  1191. if (result) {
  1192. return true;
  1193. }
  1194. if (!otherModeMaps) {
  1195. return false;
  1196. }
  1197. const otherResult = otherModeMaps.some(otherModeMap => {
  1198. return _jsdocUtils.default[( /** @type {"tagMightHaveNamePosition"|"tagMightHaveTypePosition"} */
  1199. method)](tagName, otherModeMap);
  1200. });
  1201. return otherResult ? {
  1202. otherMode: true
  1203. } : false;
  1204. };
  1205. }
  1206. /** @type {TagMissingRequiredTypeOrNamepath} */
  1207. utils.tagMissingRequiredTypeOrNamepath = (tagName, otherModeMaps) => {
  1208. const result = _jsdocUtils.default.tagMissingRequiredTypeOrNamepath(tagName);
  1209. if (!result) {
  1210. return false;
  1211. }
  1212. const otherResult = otherModeMaps.every(otherModeMap => {
  1213. return _jsdocUtils.default.tagMissingRequiredTypeOrNamepath(tagName, otherModeMap);
  1214. });
  1215. return otherResult ? true : {
  1216. otherMode: false
  1217. };
  1218. };
  1219. for (const method of ['tagMustHaveNamePosition', 'tagMustHaveTypePosition']) {
  1220. /** @type {TagMustHave} */
  1221. utils[( /** @type {"tagMustHaveNamePosition"|"tagMustHaveTypePosition"} */
  1222. method)] = (tagName, otherModeMaps) => {
  1223. const result = _jsdocUtils.default[( /** @type {"tagMustHaveNamePosition"|"tagMustHaveTypePosition"} */
  1224. method)](tagName);
  1225. if (!result) {
  1226. return false;
  1227. }
  1228. // if (!otherModeMaps) { return true; }
  1229. const otherResult = otherModeMaps.every(otherModeMap => {
  1230. return _jsdocUtils.default[( /** @type {"tagMustHaveNamePosition"|"tagMustHaveTypePosition"} */
  1231. method)](tagName, otherModeMap);
  1232. });
  1233. return otherResult ? true : {
  1234. otherMode: false
  1235. };
  1236. };
  1237. }
  1238. for (const method of ['isNamepathDefiningTag', 'isNamepathReferencingTag', 'isNamepathOrUrlReferencingTag', 'tagMightHaveNamepath']) {
  1239. /** @type {IsNamepathX} */
  1240. utils[( /** @type {"isNamepathDefiningTag"|"isNamepathReferencingTag"|"isNamepathOrUrlReferencingTag"|"tagMightHaveNamepath"} */
  1241. method)] = tagName => {
  1242. return _jsdocUtils.default[( /** @type {"isNamepathDefiningTag"|"isNamepathReferencingTag"|"isNamepathOrUrlReferencingTag"|"tagMightHaveNamepath"} */
  1243. method)](tagName);
  1244. };
  1245. }
  1246. /** @type {GetTagStructureForMode} */
  1247. utils.getTagStructureForMode = mde => {
  1248. return _jsdocUtils.default.getTagStructureForMode(mde, settings.structuredTags);
  1249. };
  1250. /** @type {MayBeUndefinedTypeTag} */
  1251. utils.mayBeUndefinedTypeTag = tag => {
  1252. return _jsdocUtils.default.mayBeUndefinedTypeTag(tag, settings.mode);
  1253. };
  1254. /** @type {HasValueOrExecutorHasNonEmptyResolveValue} */
  1255. utils.hasValueOrExecutorHasNonEmptyResolveValue = (anyPromiseAsReturn, allBranches) => {
  1256. return _jsdocUtils.default.hasValueOrExecutorHasNonEmptyResolveValue( /** @type {Node} */node, anyPromiseAsReturn, allBranches);
  1257. };
  1258. /** @type {HasYieldValue} */
  1259. utils.hasYieldValue = () => {
  1260. if (['ExportNamedDeclaration', 'ExportDefaultDeclaration'].includes( /** @type {Node} */node.type)) {
  1261. return _jsdocUtils.default.hasYieldValue( /** @type {import('estree').Declaration|import('estree').Expression} */
  1262. /** @type {import('estree').ExportNamedDeclaration|import('estree').ExportDefaultDeclaration} */
  1263. node.declaration);
  1264. }
  1265. return _jsdocUtils.default.hasYieldValue( /** @type {Node} */node);
  1266. };
  1267. /** @type {HasYieldReturnValue} */
  1268. utils.hasYieldReturnValue = () => {
  1269. return _jsdocUtils.default.hasYieldValue( /** @type {Node} */node, true);
  1270. };
  1271. /** @type {HasThrowValue} */
  1272. utils.hasThrowValue = () => {
  1273. return _jsdocUtils.default.hasThrowValue(node);
  1274. };
  1275. /** @type {IsAsync} */
  1276. utils.isAsync = () => {
  1277. return 'async' in ( /** @type {Node} */node) && node.async;
  1278. };
  1279. /** @type {GetTags} */
  1280. utils.getTags = tagName => {
  1281. return utils.filterTags(item => {
  1282. return item.tag === tagName;
  1283. });
  1284. };
  1285. /** @type {GetPresentTags} */
  1286. utils.getPresentTags = tagList => {
  1287. return utils.filterTags(tag => {
  1288. return tagList.includes(tag.tag);
  1289. });
  1290. };
  1291. /** @type {FilterTags} */
  1292. utils.filterTags = filter => {
  1293. return jsdoc.tags.filter(tag => {
  1294. return filter(tag);
  1295. });
  1296. };
  1297. /** @type {FilterAllTags} */
  1298. utils.filterAllTags = filter => {
  1299. const tags = _jsdocUtils.default.getAllTags(jsdoc);
  1300. return tags.filter(tag => {
  1301. return filter(tag);
  1302. });
  1303. };
  1304. /** @type {GetTagsByType} */
  1305. utils.getTagsByType = tags => {
  1306. return _jsdocUtils.default.getTagsByType(context, mode, tags);
  1307. };
  1308. /** @type {HasOptionTag} */
  1309. utils.hasOptionTag = tagName => {
  1310. const {
  1311. tags
  1312. } = context.options[0] ?? {};
  1313. return Boolean(tags && tags.includes(tagName));
  1314. };
  1315. /** @type {GetClassNode} */
  1316. utils.getClassNode = () => {
  1317. return [...ancestors, node].reverse().find(parent => {
  1318. return parent && ['ClassDeclaration', 'ClassExpression'].includes(parent.type);
  1319. }) ?? null;
  1320. };
  1321. /** @type {GetClassJsdoc} */
  1322. utils.getClassJsdoc = () => {
  1323. const classNode = utils.getClassNode();
  1324. if (!classNode) {
  1325. return null;
  1326. }
  1327. const classJsdocNode = (0, _jsdoccomment.getJSDocComment)(sourceCode, classNode, {
  1328. maxLines,
  1329. minLines
  1330. });
  1331. if (classJsdocNode) {
  1332. return (0, _jsdoccomment.parseComment)(classJsdocNode, '');
  1333. }
  1334. return null;
  1335. };
  1336. /** @type {ClassHasTag} */
  1337. utils.classHasTag = tagName => {
  1338. const classJsdoc = utils.getClassJsdoc();
  1339. return classJsdoc !== null && _jsdocUtils.default.hasTag(classJsdoc, tagName);
  1340. };
  1341. /** @type {ForEachPreferredTag} */
  1342. utils.forEachPreferredTag = (tagName, arrayHandler, skipReportingBlockedTag = false) => {
  1343. const targetTagName = /** @type {string|false} */
  1344. utils.getPreferredTagName({
  1345. skipReportingBlockedTag,
  1346. tagName
  1347. });
  1348. if (!targetTagName || skipReportingBlockedTag && targetTagName && typeof targetTagName === 'object') {
  1349. return;
  1350. }
  1351. const matchingJsdocTags = jsdoc.tags.filter(({
  1352. tag
  1353. }) => {
  1354. return tag === targetTagName;
  1355. });
  1356. for (const matchingJsdocTag of matchingJsdocTags) {
  1357. arrayHandler(
  1358. /**
  1359. * @type {import('@es-joy/jsdoccomment').JsdocTagWithInline}
  1360. */
  1361. matchingJsdocTag, targetTagName);
  1362. }
  1363. };
  1364. /** @type {FindContext} */
  1365. utils.findContext = (contexts, comment) => {
  1366. const foundContext = contexts.find(cntxt => {
  1367. return typeof cntxt === 'string' ? _esquery.default.matches( /** @type {Node} */node, _esquery.default.parse(cntxt), undefined, {
  1368. visitorKeys: sourceCode.visitorKeys
  1369. }) : (!cntxt.context || cntxt.context === 'any' || _esquery.default.matches( /** @type {Node} */node, _esquery.default.parse(cntxt.context), undefined, {
  1370. visitorKeys: sourceCode.visitorKeys
  1371. })) && comment === cntxt.comment;
  1372. });
  1373. const contextStr = typeof foundContext === 'object' ? foundContext.context ?? 'any' : String(foundContext);
  1374. return {
  1375. contextStr,
  1376. foundContext
  1377. };
  1378. };
  1379. return utils;
  1380. };
  1381. /**
  1382. * @typedef {{
  1383. * [key: string]: false|string|{
  1384. * message: string,
  1385. * replacement?: false|string
  1386. * skipRootChecking?: boolean
  1387. * }
  1388. * }} PreferredTypes
  1389. */
  1390. /**
  1391. * @typedef {{
  1392. * [key: string]: {
  1393. * name?: "text"|"namepath-defining"|"namepath-referencing"|false,
  1394. * type?: boolean|string[],
  1395. * required?: ("name"|"type"|"typeOrNameRequired")[]
  1396. * }
  1397. * }} StructuredTags
  1398. */
  1399. /**
  1400. * Settings from ESLint types.
  1401. * @typedef {{
  1402. * maxLines: Integer,
  1403. * minLines: Integer,
  1404. * tagNamePreference: import('./jsdocUtils.js').TagNamePreference,
  1405. * mode: import('./jsdocUtils.js').ParserMode,
  1406. * preferredTypes: PreferredTypes,
  1407. * structuredTags: StructuredTags,
  1408. * [name: string]: any,
  1409. * contexts?: Context[]
  1410. * }} Settings
  1411. */
  1412. /**
  1413. * @param {import('eslint').Rule.RuleContext} context
  1414. * @returns {Settings|false}
  1415. */
  1416. const getSettings = context => {
  1417. var _context$settings$jsd, _context$settings$jsd2, _context$settings$jsd3, _context$settings$jsd4, _context$settings$jsd5, _context$settings$jsd6, _context$settings$jsd7, _context$settings$jsd8, _context$settings$jsd9, _context$settings$jsd10, _context$settings$jsd11, _context$settings$jsd12, _context$settings$jsd13, _context$settings$jsd14;
  1418. /* dslint-disable canonical/sort-keys */
  1419. const settings = {
  1420. // All rules
  1421. ignorePrivate: Boolean((_context$settings$jsd = context.settings.jsdoc) === null || _context$settings$jsd === void 0 ? void 0 : _context$settings$jsd.ignorePrivate),
  1422. ignoreInternal: Boolean((_context$settings$jsd2 = context.settings.jsdoc) === null || _context$settings$jsd2 === void 0 ? void 0 : _context$settings$jsd2.ignoreInternal),
  1423. maxLines: Number(((_context$settings$jsd3 = context.settings.jsdoc) === null || _context$settings$jsd3 === void 0 ? void 0 : _context$settings$jsd3.maxLines) ?? 1),
  1424. minLines: Number(((_context$settings$jsd4 = context.settings.jsdoc) === null || _context$settings$jsd4 === void 0 ? void 0 : _context$settings$jsd4.minLines) ?? 0),
  1425. // `check-tag-names` and many returns/param rules
  1426. tagNamePreference: ((_context$settings$jsd5 = context.settings.jsdoc) === null || _context$settings$jsd5 === void 0 ? void 0 : _context$settings$jsd5.tagNamePreference) ?? {},
  1427. // `check-types` and `no-undefined-types`
  1428. preferredTypes: ((_context$settings$jsd6 = context.settings.jsdoc) === null || _context$settings$jsd6 === void 0 ? void 0 : _context$settings$jsd6.preferredTypes) ?? {},
  1429. // `check-types`, `no-undefined-types`, `valid-types`
  1430. structuredTags: ((_context$settings$jsd7 = context.settings.jsdoc) === null || _context$settings$jsd7 === void 0 ? void 0 : _context$settings$jsd7.structuredTags) ?? {},
  1431. // `require-param`, `require-description`, `require-example`,
  1432. // `require-returns`, `require-throw`, `require-yields`
  1433. overrideReplacesDocs: (_context$settings$jsd8 = context.settings.jsdoc) === null || _context$settings$jsd8 === void 0 ? void 0 : _context$settings$jsd8.overrideReplacesDocs,
  1434. ignoreReplacesDocs: (_context$settings$jsd9 = context.settings.jsdoc) === null || _context$settings$jsd9 === void 0 ? void 0 : _context$settings$jsd9.ignoreReplacesDocs,
  1435. implementsReplacesDocs: (_context$settings$jsd10 = context.settings.jsdoc) === null || _context$settings$jsd10 === void 0 ? void 0 : _context$settings$jsd10.implementsReplacesDocs,
  1436. augmentsExtendsReplacesDocs: (_context$settings$jsd11 = context.settings.jsdoc) === null || _context$settings$jsd11 === void 0 ? void 0 : _context$settings$jsd11.augmentsExtendsReplacesDocs,
  1437. // `require-param-type`, `require-param-description`
  1438. exemptDestructuredRootsFromChecks: (_context$settings$jsd12 = context.settings.jsdoc) === null || _context$settings$jsd12 === void 0 ? void 0 : _context$settings$jsd12.exemptDestructuredRootsFromChecks,
  1439. // Many rules, e.g., `check-tag-names`
  1440. mode: ((_context$settings$jsd13 = context.settings.jsdoc) === null || _context$settings$jsd13 === void 0 ? void 0 : _context$settings$jsd13.mode) ?? 'typescript',
  1441. // Many rules
  1442. contexts: (_context$settings$jsd14 = context.settings.jsdoc) === null || _context$settings$jsd14 === void 0 ? void 0 : _context$settings$jsd14.contexts
  1443. };
  1444. /* dslint-enable canonical/sort-keys */
  1445. _jsdocUtils.default.setTagStructure(settings.mode);
  1446. try {
  1447. _jsdocUtils.default.overrideTagStructure(settings.structuredTags);
  1448. } catch (error) {
  1449. context.report({
  1450. loc: {
  1451. end: {
  1452. column: 1,
  1453. line: 1
  1454. },
  1455. start: {
  1456. column: 1,
  1457. line: 1
  1458. }
  1459. },
  1460. message: /** @type {Error} */error.message
  1461. });
  1462. return false;
  1463. }
  1464. return settings;
  1465. };
  1466. /**
  1467. * Create the report function
  1468. * @callback MakeReport
  1469. * @param {import('eslint').Rule.RuleContext} context
  1470. * @param {import('estree').Node} commentNode
  1471. * @returns {Report}
  1472. */
  1473. /** @type {MakeReport} */
  1474. exports.getSettings = getSettings;
  1475. const makeReport = (context, commentNode) => {
  1476. /** @type {Report} */
  1477. const report = (message, fix = null, jsdocLoc = null, data = undefined) => {
  1478. let loc;
  1479. if (jsdocLoc) {
  1480. if (!('line' in jsdocLoc)) {
  1481. jsdocLoc.line = /** @type {import('comment-parser').Spec & {line?: Integer}} */jsdocLoc.source[0].number;
  1482. }
  1483. const lineNumber = /** @type {import('eslint').AST.SourceLocation} */commentNode.loc.start.line + ( /** @type {Integer} */jsdocLoc.line);
  1484. loc = {
  1485. end: {
  1486. column: 0,
  1487. line: lineNumber
  1488. },
  1489. start: {
  1490. column: 0,
  1491. line: lineNumber
  1492. }
  1493. };
  1494. // Todo: Remove ignore once `check-examples` can be restored for ESLint 8+
  1495. // istanbul ignore if
  1496. if ('column' in jsdocLoc && typeof jsdocLoc.column === 'number') {
  1497. const colNumber = /** @type {import('eslint').AST.SourceLocation} */commentNode.loc.start.column + jsdocLoc.column;
  1498. loc.end.column = colNumber;
  1499. loc.start.column = colNumber;
  1500. }
  1501. }
  1502. context.report({
  1503. data,
  1504. fix,
  1505. loc,
  1506. message,
  1507. node: commentNode
  1508. });
  1509. };
  1510. return report;
  1511. };
  1512. /**
  1513. * @typedef {(
  1514. * arg: {
  1515. * context: import('eslint').Rule.RuleContext,
  1516. * sourceCode: import('eslint').SourceCode,
  1517. * indent?: string,
  1518. * info?: {
  1519. * comment?: string|undefined,
  1520. * lastIndex?: Integer|undefined
  1521. * },
  1522. * state?: StateObject,
  1523. * globalState?: Map<string, Map<string, string>>,
  1524. * jsdoc?: JsdocBlockWithInline,
  1525. * jsdocNode?: import('eslint').Rule.Node & {
  1526. * range: [number, number]
  1527. * },
  1528. * node?: Node,
  1529. * allComments?: import('estree').Node[]
  1530. * report?: Report,
  1531. * makeReport?: MakeReport,
  1532. * settings: Settings,
  1533. * utils: BasicUtils,
  1534. * }
  1535. * ) => any } JsdocVisitorBasic
  1536. */
  1537. /**
  1538. * @typedef {(
  1539. * arg: {
  1540. * context: import('eslint').Rule.RuleContext,
  1541. * sourceCode: import('eslint').SourceCode,
  1542. * indent: string,
  1543. * info: {
  1544. * comment?: string|undefined,
  1545. * lastIndex?: Integer|undefined
  1546. * },
  1547. * state: StateObject,
  1548. * globalState: Map<string, Map<string, string>>,
  1549. * jsdoc: JsdocBlockWithInline,
  1550. * jsdocNode: import('eslint').Rule.Node & {
  1551. * range: [number, number]
  1552. * },
  1553. * node: Node|null,
  1554. * allComments?: import('estree').Node[]
  1555. * report: Report,
  1556. * makeReport?: MakeReport,
  1557. * settings: Settings,
  1558. * utils: Utils,
  1559. * }
  1560. * ) => any } JsdocVisitor
  1561. */
  1562. /**
  1563. * @param {{
  1564. * comment?: string,
  1565. * lastIndex?: Integer,
  1566. * selector?: string,
  1567. * isFunctionContext?: boolean,
  1568. * }} info
  1569. * @param {string} indent
  1570. * @param {JsdocBlockWithInline} jsdoc
  1571. * @param {RuleConfig} ruleConfig
  1572. * @param {import('eslint').Rule.RuleContext} context
  1573. * @param {import('@es-joy/jsdoccomment').Token} jsdocNode
  1574. * @param {Node|null} node
  1575. * @param {Settings} settings
  1576. * @param {import('eslint').SourceCode} sourceCode
  1577. * @param {JsdocVisitor} iterator
  1578. * @param {StateObject} state
  1579. * @param {boolean} [iteratingAll]
  1580. * @returns {void}
  1581. */
  1582. const iterate = (info, indent, jsdoc, ruleConfig, context, jsdocNode, node, settings, sourceCode, iterator, state, iteratingAll) => {
  1583. const jsdocNde = /** @type {unknown} */jsdocNode;
  1584. const report = makeReport(context, /** @type {import('estree').Node} */
  1585. jsdocNde);
  1586. const utils = getUtils(node, jsdoc, /** @type {import('eslint').AST.Token} */
  1587. jsdocNode, settings, report, context, sourceCode, iteratingAll, ruleConfig, indent);
  1588. if (!ruleConfig.checkInternal && settings.ignoreInternal && utils.hasTag('internal')) {
  1589. return;
  1590. }
  1591. if (!ruleConfig.checkPrivate && settings.ignorePrivate && (utils.hasTag('private') || jsdoc.tags.filter(({
  1592. tag
  1593. }) => {
  1594. return tag === 'access';
  1595. }).some(({
  1596. description
  1597. }) => {
  1598. return description === 'private';
  1599. }))) {
  1600. return;
  1601. }
  1602. iterator({
  1603. context,
  1604. globalState,
  1605. indent,
  1606. info,
  1607. jsdoc,
  1608. jsdocNode: (
  1609. /**
  1610. * @type {import('eslint').Rule.Node & {
  1611. * range: [number, number];}}
  1612. */
  1613. jsdocNde),
  1614. node,
  1615. report,
  1616. settings,
  1617. sourceCode,
  1618. state,
  1619. utils
  1620. });
  1621. };
  1622. /**
  1623. * @param {string[]} lines
  1624. * @param {import('estree').Comment} jsdocNode
  1625. * @returns {[indent: string, jsdoc: JsdocBlockWithInline]}
  1626. */
  1627. const getIndentAndJSDoc = function (lines, jsdocNode) {
  1628. const sourceLine = lines[/** @type {import('estree').SourceLocation} */
  1629. jsdocNode.loc.start.line - 1];
  1630. const indnt = sourceLine.charAt(0).repeat( /** @type {import('estree').SourceLocation} */
  1631. jsdocNode.loc.start.column);
  1632. const jsdc = (0, _jsdoccomment.parseComment)(jsdocNode, '');
  1633. return [indnt, jsdc];
  1634. };
  1635. /**
  1636. *
  1637. * @typedef {{node: Node & {
  1638. * range: [number, number]
  1639. * }, state: StateObject}} NonCommentArgs
  1640. */
  1641. /**
  1642. * @typedef {object} RuleConfig
  1643. * @property {EslintRuleMeta} meta ESLint rule meta
  1644. * @property {import('./jsdocUtils.js').DefaultContexts} [contextDefaults] Any default contexts
  1645. * @property {true} [contextSelected] Whether to force a `contexts` check
  1646. * @property {true} [iterateAllJsdocs] Whether to iterate all JSDoc blocks by default
  1647. * regardless of context
  1648. * @property {true} [checkPrivate] Whether to check `@private` blocks (normally exempted)
  1649. * @property {true} [checkInternal] Whether to check `@internal` blocks (normally exempted)
  1650. * @property {true} [checkFile] Whether to iterates over all JSDoc blocks regardless of attachment
  1651. * @property {true} [nonGlobalSettings] Whether to avoid relying on settings for global contexts
  1652. * @property {true} [noTracking] Whether to disable the tracking of visited comment nodes (as
  1653. * non-tracked may conduct further actions)
  1654. * @property {true} [matchContext] Whether the rule expects contexts to be based on a match option
  1655. * @property {(args: {
  1656. * context: import('eslint').Rule.RuleContext,
  1657. * state: StateObject,
  1658. * settings: Settings,
  1659. * utils: BasicUtils
  1660. * }) => void} [exit] Handler to be executed upon exiting iteration of program AST
  1661. * @property {(nca: NonCommentArgs) => void} [nonComment] Handler to be executed if rule wishes
  1662. * to be supplied nodes without comments
  1663. */
  1664. /**
  1665. * Create an eslint rule that iterates over all JSDocs, regardless of whether
  1666. * they are attached to a function-like node.
  1667. * @param {JsdocVisitor} iterator
  1668. * @param {RuleConfig} ruleConfig The rule's configuration
  1669. * @param {ContextObject[]|null} [contexts] The `contexts` containing relevant `comment` info.
  1670. * @param {boolean} [additiveCommentContexts] If true, will have a separate
  1671. * iteration for each matching comment context. Otherwise, will iterate
  1672. * once if there is a single matching comment context.
  1673. * @returns {import('eslint').Rule.RuleModule}
  1674. */
  1675. const iterateAllJsdocs = (iterator, ruleConfig, contexts, additiveCommentContexts) => {
  1676. const trackedJsdocs = new Set();
  1677. /** @type {import('@es-joy/jsdoccomment').CommentHandler} */
  1678. let handler;
  1679. /** @type {Settings|false} */
  1680. let settings;
  1681. /**
  1682. * @param {import('eslint').Rule.RuleContext} context
  1683. * @param {Node|null} node
  1684. * @param {import('estree').Comment[]} jsdocNodes
  1685. * @param {StateObject} state
  1686. * @param {boolean} [lastCall]
  1687. * @returns {void}
  1688. */
  1689. const callIterator = (context, node, jsdocNodes, state, lastCall) => {
  1690. // istanbul ignore next -- Fallback to deprecated method
  1691. const {
  1692. sourceCode = context.getSourceCode()
  1693. } = context;
  1694. const {
  1695. lines
  1696. } = sourceCode;
  1697. const utils = getBasicUtils(context, /** @type {Settings} */settings);
  1698. for (const jsdocNode of jsdocNodes) {
  1699. const jsdocNde = /** @type {unknown} */jsdocNode;
  1700. if (!/^\/\*\*\s/u.test(sourceCode.getText( /** @type {import('estree').Node} */
  1701. jsdocNde))) {
  1702. continue;
  1703. }
  1704. const [indent, jsdoc] = getIndentAndJSDoc(lines, jsdocNode);
  1705. if (additiveCommentContexts) {
  1706. for (const [idx, {
  1707. comment
  1708. }] of /** @type {ContextObject[]} */contexts.entries()) {
  1709. if (comment && handler(comment, jsdoc) === false) {
  1710. continue;
  1711. }
  1712. iterate({
  1713. comment,
  1714. lastIndex: idx,
  1715. selector: node === null || node === void 0 ? void 0 : node.type
  1716. }, indent, jsdoc, ruleConfig, context, jsdocNode, /** @type {Node} */
  1717. node, /** @type {Settings} */
  1718. settings, sourceCode, iterator, state, true);
  1719. }
  1720. continue;
  1721. }
  1722. let lastComment;
  1723. let lastIndex;
  1724. // eslint-disable-next-line no-loop-func
  1725. if (contexts && contexts.every(({
  1726. comment
  1727. }, idx) => {
  1728. lastComment = comment;
  1729. lastIndex = idx;
  1730. return comment && handler(comment, jsdoc) === false;
  1731. })) {
  1732. continue;
  1733. }
  1734. iterate(lastComment ? {
  1735. comment: lastComment,
  1736. lastIndex,
  1737. selector: node === null || node === void 0 ? void 0 : node.type
  1738. } : {
  1739. lastIndex,
  1740. selector: node === null || node === void 0 ? void 0 : node.type
  1741. }, indent, jsdoc, ruleConfig, context, jsdocNode, node, /** @type {Settings} */
  1742. settings, sourceCode, iterator, state, true);
  1743. }
  1744. const settngs = /** @type {Settings} */settings;
  1745. if (lastCall && ruleConfig.exit) {
  1746. ruleConfig.exit({
  1747. context,
  1748. settings: settngs,
  1749. state,
  1750. utils
  1751. });
  1752. }
  1753. };
  1754. return {
  1755. // @ts-expect-error ESLint accepts
  1756. create(context) {
  1757. // istanbul ignore next -- Fallback to deprecated method
  1758. const {
  1759. sourceCode = context.getSourceCode()
  1760. } = context;
  1761. settings = getSettings(context);
  1762. if (!settings) {
  1763. return {};
  1764. }
  1765. if (contexts) {
  1766. handler = (0, _jsdoccomment.commentHandler)(settings);
  1767. }
  1768. const state = {};
  1769. return {
  1770. /**
  1771. * @param {import('eslint').Rule.Node & {
  1772. * range: [Integer, Integer];
  1773. * }} node
  1774. * @returns {void}
  1775. */
  1776. '*:not(Program)'(node) {
  1777. const commentNode = (0, _jsdoccomment.getJSDocComment)(sourceCode, node, /** @type {Settings} */settings);
  1778. if (!ruleConfig.noTracking && trackedJsdocs.has(commentNode)) {
  1779. return;
  1780. }
  1781. if (!commentNode) {
  1782. if (ruleConfig.nonComment) {
  1783. const ste = /** @type {StateObject} */state;
  1784. ruleConfig.nonComment({
  1785. node,
  1786. state: ste
  1787. });
  1788. }
  1789. return;
  1790. }
  1791. trackedJsdocs.add(commentNode);
  1792. callIterator(context, node, [( /** @type {import('estree').Comment} */
  1793. commentNode)], /** @type {StateObject} */state);
  1794. },
  1795. 'Program:exit'() {
  1796. const allComments = sourceCode.getAllComments();
  1797. const untrackedJSdoc = allComments.filter(node => {
  1798. return !trackedJsdocs.has(node);
  1799. });
  1800. callIterator(context, null, untrackedJSdoc, /** @type {StateObject} */
  1801. state, true);
  1802. }
  1803. };
  1804. },
  1805. meta: ruleConfig.meta
  1806. };
  1807. };
  1808. /**
  1809. * Create an eslint rule that iterates over all JSDocs, regardless of whether
  1810. * they are attached to a function-like node.
  1811. * @param {JsdocVisitorBasic} iterator
  1812. * @param {RuleConfig} ruleConfig
  1813. * @returns {import('eslint').Rule.RuleModule}
  1814. */
  1815. const checkFile = (iterator, ruleConfig) => {
  1816. return {
  1817. create(context) {
  1818. // istanbul ignore next -- Fallback to deprecated method
  1819. const {
  1820. sourceCode = context.getSourceCode()
  1821. } = context;
  1822. const settings = getSettings(context);
  1823. if (!settings) {
  1824. return {};
  1825. }
  1826. return {
  1827. 'Program:exit'() {
  1828. const allComms = /** @type {unknown} */sourceCode.getAllComments();
  1829. const utils = getBasicUtils(context, settings);
  1830. iterator({
  1831. allComments: ( /** @type {import('estree').Node[]} */allComms),
  1832. context,
  1833. makeReport,
  1834. settings,
  1835. sourceCode,
  1836. utils
  1837. });
  1838. }
  1839. };
  1840. },
  1841. meta: ruleConfig.meta
  1842. };
  1843. };
  1844. /**
  1845. * @param {JsdocVisitor} iterator
  1846. * @param {RuleConfig} ruleConfig
  1847. * @returns {import('eslint').Rule.RuleModule}
  1848. */
  1849. function iterateJsdoc(iterator, ruleConfig) {
  1850. var _ruleConfig$meta;
  1851. const metaType = ruleConfig === null || ruleConfig === void 0 || (_ruleConfig$meta = ruleConfig.meta) === null || _ruleConfig$meta === void 0 ? void 0 : _ruleConfig$meta.type;
  1852. if (!metaType || !['problem', 'suggestion', 'layout'].includes(metaType)) {
  1853. throw new TypeError('Rule must include `meta.type` option (with value "problem", "suggestion", or "layout")');
  1854. }
  1855. if (typeof iterator !== 'function') {
  1856. throw new TypeError('The iterator argument must be a function.');
  1857. }
  1858. if (ruleConfig.checkFile) {
  1859. return checkFile( /** @type {JsdocVisitorBasic} */iterator, ruleConfig);
  1860. }
  1861. if (ruleConfig.iterateAllJsdocs) {
  1862. return iterateAllJsdocs(iterator, ruleConfig);
  1863. }
  1864. /** @type {import('eslint').Rule.RuleModule} */
  1865. return {
  1866. /**
  1867. * The entrypoint for the JSDoc rule.
  1868. * @param {import('eslint').Rule.RuleContext} context
  1869. * a reference to the context which hold all important information
  1870. * like settings and the sourcecode to check.
  1871. * @returns {import('eslint').Rule.RuleListener}
  1872. * a listener with parser callback function.
  1873. */
  1874. create(context) {
  1875. const settings = getSettings(context);
  1876. if (!settings) {
  1877. return {};
  1878. }
  1879. /**
  1880. * @type {Context[]|undefined}
  1881. */
  1882. let contexts;
  1883. if (ruleConfig.contextDefaults || ruleConfig.contextSelected || ruleConfig.matchContext) {
  1884. var _context$options$2, _contexts, _contexts2;
  1885. contexts = ruleConfig.matchContext && (_context$options$2 = context.options[0]) !== null && _context$options$2 !== void 0 && _context$options$2.match ? context.options[0].match : _jsdocUtils.default.enforcedContexts(context, ruleConfig.contextDefaults, ruleConfig.nonGlobalSettings ? {} : settings);
  1886. if (contexts) {
  1887. contexts = contexts.map(obj => {
  1888. if (typeof obj === 'object' && !obj.context) {
  1889. return {
  1890. ...obj,
  1891. context: 'any'
  1892. };
  1893. }
  1894. return obj;
  1895. });
  1896. }
  1897. const hasPlainAny = (_contexts = contexts) === null || _contexts === void 0 ? void 0 : _contexts.includes('any');
  1898. const hasObjectAny = !hasPlainAny && ((_contexts2 = contexts) === null || _contexts2 === void 0 ? void 0 : _contexts2.find(ctxt => {
  1899. if (typeof ctxt === 'string') {
  1900. return false;
  1901. }
  1902. return (ctxt === null || ctxt === void 0 ? void 0 : ctxt.context) === 'any';
  1903. }));
  1904. if (hasPlainAny || hasObjectAny) {
  1905. return iterateAllJsdocs(iterator, ruleConfig, hasObjectAny ? ( /** @type {ContextObject[]} */contexts) : null, ruleConfig.matchContext).create(context);
  1906. }
  1907. }
  1908. // istanbul ignore next -- Fallback to deprecated method
  1909. const {
  1910. sourceCode = context.getSourceCode()
  1911. } = context;
  1912. const {
  1913. lines
  1914. } = sourceCode;
  1915. /** @type {Partial<StateObject>} */
  1916. const state = {};
  1917. /** @type {CheckJsdoc} */
  1918. const checkJsdoc = (info, handler, node) => {
  1919. const jsdocNode = (0, _jsdoccomment.getJSDocComment)(sourceCode, node, settings);
  1920. if (!jsdocNode) {
  1921. return;
  1922. }
  1923. const [indent, jsdoc] = getIndentAndJSDoc(lines, /** @type {import('estree').Comment} */
  1924. jsdocNode);
  1925. if (
  1926. // Note, `handler` should already be bound in its first argument
  1927. // with these only to be called after the value of
  1928. // `comment`
  1929. handler && handler(jsdoc) === false) {
  1930. return;
  1931. }
  1932. iterate(info, indent, jsdoc, ruleConfig, context, jsdocNode, node, settings, sourceCode, iterator, /** @type {StateObject} */
  1933. state);
  1934. };
  1935. /** @type {import('eslint').Rule.RuleListener} */
  1936. let contextObject = {};
  1937. if (contexts && (ruleConfig.contextDefaults || ruleConfig.contextSelected || ruleConfig.matchContext)) {
  1938. contextObject = _jsdocUtils.default.getContextObject(contexts, checkJsdoc, (0, _jsdoccomment.commentHandler)(settings));
  1939. } else {
  1940. for (const prop of ['ArrowFunctionExpression', 'FunctionDeclaration', 'FunctionExpression', 'TSDeclareFunction']) {
  1941. contextObject[prop] = checkJsdoc.bind(null, {
  1942. selector: prop
  1943. }, null);
  1944. }
  1945. }
  1946. if (typeof ruleConfig.exit === 'function') {
  1947. contextObject['Program:exit'] = () => {
  1948. const ste = /** @type {StateObject} */state;
  1949. // @ts-expect-error `utils` not needed at this point
  1950. /** @type {Required<RuleConfig>} */
  1951. ruleConfig.exit({
  1952. context,
  1953. settings,
  1954. state: ste
  1955. });
  1956. };
  1957. }
  1958. return contextObject;
  1959. },
  1960. meta: ruleConfig.meta
  1961. };
  1962. }
  1963. //# sourceMappingURL=iterateJsdoc.js.map