eslint-plugin-jsdoc 60.4.0 → 60.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "url": "http://gajus.com"
6
6
  },
7
7
  "dependencies": {
8
- "@es-joy/jsdoccomment": "~0.61.0",
8
+ "@es-joy/jsdoccomment": "~0.62.0",
9
9
  "are-docs-informative": "^0.0.2",
10
10
  "comment-parser": "1.4.1",
11
11
  "debug": "^4.4.3",
@@ -27,8 +27,8 @@
27
27
  "@babel/plugin-syntax-class-properties": "^7.12.13",
28
28
  "@babel/plugin-transform-flow-strip-types": "^7.27.1",
29
29
  "@babel/preset-env": "^7.28.3",
30
- "@es-joy/escodegen": "^3.5.1",
31
- "@es-joy/jsdoc-eslint-parser": "^0.23.0",
30
+ "@es-joy/escodegen": "^4.0.3",
31
+ "@es-joy/jsdoc-eslint-parser": "^0.24.0",
32
32
  "@eslint/core": "^0.16.0",
33
33
  "@hkdobrev/run-if-changed": "^0.6.3",
34
34
  "@semantic-release/commit-analyzer": "^13.0.1",
@@ -50,7 +50,7 @@
50
50
  "babel-plugin-transform-import-meta": "^2.3.3",
51
51
  "c8": "^10.1.3",
52
52
  "camelcase": "^8.0.0",
53
- "chai": "^6.0.1",
53
+ "chai": "^6.2.0",
54
54
  "decamelize": "^6.0.1",
55
55
  "eslint": "9.36.0",
56
56
  "eslint-config-canonical": "^45.0.0",
@@ -58,7 +58,7 @@
58
58
  "glob": "^11.0.3",
59
59
  "globals": "^16.4.0",
60
60
  "husky": "^9.1.7",
61
- "jsdoc-type-pratt-parser": "^5.8.0",
61
+ "jsdoc-type-pratt-parser": "^5.9.2",
62
62
  "json-schema": "^0.4.0",
63
63
  "json-schema-to-typescript": "^15.0.4",
64
64
  "lint-staged": "^16.2.1",
@@ -178,5 +178,5 @@
178
178
  "test-cov": "TIMING=1 c8 --reporter text pnpm run test-no-cov",
179
179
  "test-index": "pnpm run test-no-cov test/rules/index.js"
180
180
  },
181
- "version": "60.4.0"
181
+ "version": "60.5.0"
182
182
  }
package/src/index-cjs.js CHANGED
@@ -188,6 +188,17 @@ index.rules = {
188
188
  'require-returns-type': requireReturnsType,
189
189
  'require-tags': requireTags,
190
190
  'require-template': requireTemplate,
191
+ 'require-template-description': buildForbidRuleDefinition({
192
+ contexts: [
193
+ {
194
+ comment: 'JsdocBlock:has(JsdocTag[tag=template]:not([description!=""]))',
195
+ context: 'any',
196
+ message: '@template should have a description',
197
+ },
198
+ ],
199
+ description: 'Requires a description for `@template` tags',
200
+ url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-template-description.md#repos-sticky-header',
201
+ }),
191
202
  'require-throws': requireThrows,
192
203
  'require-throws-description': buildForbidRuleDefinition({
193
204
  contexts: [
@@ -316,6 +327,7 @@ const createRecommendedRuleset = (warnOrError, flatName) => {
316
327
  'jsdoc/require-returns-type': warnOrError,
317
328
  'jsdoc/require-tags': 'off',
318
329
  'jsdoc/require-template': 'off',
330
+ 'jsdoc/require-template-description': 'off',
319
331
  'jsdoc/require-throws': 'off',
320
332
  'jsdoc/require-throws-description': 'off',
321
333
  'jsdoc/require-throws-type': warnOrError,
package/src/index-esm.js CHANGED
@@ -16,7 +16,6 @@ import {
16
16
  export default index;
17
17
  // END REPLACE
18
18
 
19
- /* eslint-disable jsdoc/valid-types -- Bug */
20
19
  /**
21
20
  * @type {((
22
21
  * cfg?: import('eslint').Linter.Config & {
@@ -54,7 +53,6 @@ export default index;
54
53
  * ) => import('eslint').Linter.Config)}
55
54
  */
56
55
  export const jsdoc = function (cfg) {
57
- /* eslint-enable jsdoc/valid-types -- Bug */
58
56
  /** @type {import('eslint').Linter.Config} */
59
57
  let outputConfig = {
60
58
  plugins: {
package/src/index.js CHANGED
@@ -194,6 +194,17 @@ index.rules = {
194
194
  'require-returns-type': requireReturnsType,
195
195
  'require-tags': requireTags,
196
196
  'require-template': requireTemplate,
197
+ 'require-template-description': buildForbidRuleDefinition({
198
+ contexts: [
199
+ {
200
+ comment: 'JsdocBlock:has(JsdocTag[tag=template]:not([description!=""]))',
201
+ context: 'any',
202
+ message: '@template should have a description',
203
+ },
204
+ ],
205
+ description: 'Requires a description for `@template` tags',
206
+ url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-template-description.md#repos-sticky-header',
207
+ }),
197
208
  'require-throws': requireThrows,
198
209
  'require-throws-description': buildForbidRuleDefinition({
199
210
  contexts: [
@@ -322,6 +333,7 @@ const createRecommendedRuleset = (warnOrError, flatName) => {
322
333
  'jsdoc/require-returns-type': warnOrError,
323
334
  'jsdoc/require-tags': 'off',
324
335
  'jsdoc/require-template': 'off',
336
+ 'jsdoc/require-template-description': 'off',
325
337
  'jsdoc/require-throws': 'off',
326
338
  'jsdoc/require-throws-description': 'off',
327
339
  'jsdoc/require-throws-type': warnOrError,
@@ -694,7 +706,6 @@ index.configs['flat/recommended-mixed'] = [
694
706
 
695
707
  export default index;
696
708
 
697
- /* eslint-disable jsdoc/valid-types -- Bug */
698
709
  /**
699
710
  * @type {((
700
711
  * cfg?: import('eslint').Linter.Config & {
@@ -732,7 +743,6 @@ export default index;
732
743
  * ) => import('eslint').Linter.Config)}
733
744
  */
734
745
  export const jsdoc = function (cfg) {
735
- /* eslint-enable jsdoc/valid-types -- Bug */
736
746
  /** @type {import('eslint').Linter.Config} */
737
747
  let outputConfig = {
738
748
  plugins: {
@@ -436,6 +436,14 @@ import esquery from 'esquery';
436
436
  * import('@es-joy/jsdoccomment').JsdocInlineTagNoType)[]}
437
437
  */
438
438
 
439
+ /**
440
+ * @callback getInlineTags
441
+ * @returns {(import('comment-parser').Spec|
442
+ * import('@es-joy/jsdoccomment').JsdocInlineTagNoType & {
443
+ * line?: number | undefined; column?: number | undefined;
444
+ * })[]}
445
+ */
446
+
439
447
  /**
440
448
  * @callback GetTagsByType
441
449
  * @param {import('comment-parser').Spec[]} tags
@@ -533,6 +541,7 @@ import esquery from 'esquery';
533
541
  * getPresentTags: GetPresentTags,
534
542
  * filterTags: FilterTags,
535
543
  * filterAllTags: FilterAllTags,
544
+ * getInlineTags: getInlineTags,
536
545
  * getTagsByType: GetTagsByType,
537
546
  * hasOptionTag: HasOptionTag,
538
547
  * getClassNode: GetClassNode,
@@ -1641,6 +1650,10 @@ const getUtils = (
1641
1650
  });
1642
1651
  };
1643
1652
 
1653
+ utils.getInlineTags = () => {
1654
+ return jsdocUtils.getInlineTags(jsdoc);
1655
+ };
1656
+
1644
1657
  /** @type {GetTagsByType} */
1645
1658
  utils.getTagsByType = (tags) => {
1646
1659
  return jsdocUtils.getTagsByType(context, mode, tags);
package/src/jsdocUtils.js CHANGED
@@ -836,14 +836,15 @@ const forEachPreferredTag = (
836
836
  };
837
837
 
838
838
  /**
839
- * Get all tags, inline tags and inline tags in tags
839
+ * Get all inline tags and inline tags in tags
840
840
  * @param {import('./iterateJsdoc.js').JsdocBlockWithInline} jsdoc
841
841
  * @returns {(import('comment-parser').Spec|
842
- * import('@es-joy/jsdoccomment').JsdocInlineTagNoType)[]}
842
+ * import('@es-joy/jsdoccomment').JsdocInlineTagNoType & {
843
+ * line?: number | undefined; column?: number | undefined;
844
+ * })[]}
843
845
  */
844
- const getAllTags = (jsdoc) => {
846
+ const getInlineTags = (jsdoc) => {
845
847
  return [
846
- ...jsdoc.tags,
847
848
  ...jsdoc.inlineTags.map((inlineTag) => {
848
849
  // Tags don't have source or line numbers, so add before returning
849
850
  let line = -1;
@@ -863,18 +864,6 @@ const getAllTags = (jsdoc) => {
863
864
  return inlineTag;
864
865
  }),
865
866
  ...jsdoc.tags.flatMap((tag) => {
866
- let tagBegins = -1;
867
- for (const {
868
- tokens: {
869
- tag: tg,
870
- },
871
- } of jsdoc.source) {
872
- tagBegins++;
873
- if (tg) {
874
- break;
875
- }
876
- }
877
-
878
867
  for (const inlineTag of tag.inlineTags) {
879
868
  /** @type {import('./iterateJsdoc.js').Integer} */
880
869
  let line = 0;
@@ -890,7 +879,7 @@ const getAllTags = (jsdoc) => {
890
879
  }
891
880
  }
892
881
 
893
- inlineTag.line = tagBegins + line - 1;
882
+ inlineTag.line = line;
894
883
  }
895
884
 
896
885
  return (
@@ -906,6 +895,21 @@ const getAllTags = (jsdoc) => {
906
895
  ];
907
896
  };
908
897
 
898
+ /**
899
+ * Get all tags, inline tags and inline tags in tags
900
+ * @param {import('./iterateJsdoc.js').JsdocBlockWithInline} jsdoc
901
+ * @returns {(import('comment-parser').Spec|
902
+ * import('@es-joy/jsdoccomment').JsdocInlineTagNoType & {
903
+ * line?: number | undefined; column?: number | undefined;
904
+ * })[]}
905
+ */
906
+ const getAllTags = (jsdoc) => {
907
+ return [
908
+ ...jsdoc.tags,
909
+ ...getInlineTags(jsdoc),
910
+ ];
911
+ };
912
+
909
913
  /**
910
914
  * @param {import('./iterateJsdoc.js').JsdocBlockWithInline} jsdoc
911
915
  * @param {string[]} targetTagNames
@@ -1853,6 +1857,7 @@ export {
1853
1857
  getContextObject,
1854
1858
  getFunctionParameterNames,
1855
1859
  getIndent,
1860
+ getInlineTags,
1856
1861
  getJsdocTagsDeep,
1857
1862
  getPreferredTagName,
1858
1863
  getPreferredTagNameSimple,
@@ -75,11 +75,15 @@ export default iterateJsdoc(({
75
75
  * @type {{
76
76
  * definedTags: string[],
77
77
  * enableFixer: boolean,
78
+ * inlineTags: string[],
78
79
  * jsxTags: boolean,
79
80
  * typed: boolean
80
81
  }} */ {
81
82
  definedTags = [],
82
83
  enableFixer = true,
84
+ inlineTags = [
85
+ 'link', 'linkcode', 'linkplain', 'tutorial',
86
+ ],
83
87
  jsxTags,
84
88
  typed,
85
89
  } = context.options[0] || {};
@@ -278,6 +282,12 @@ export default iterateJsdoc(({
278
282
  report(`Invalid JSDoc tag name "${tagName}".`, null, jsdocTag);
279
283
  }
280
284
  }
285
+
286
+ for (const inlineTag of utils.getInlineTags()) {
287
+ if (!inlineTags.includes(inlineTag.tag)) {
288
+ report(`Invalid JSDoc inline tag name "${inlineTag.tag}"`, null, inlineTag);
289
+ }
290
+ }
281
291
  }, {
282
292
  iterateAllJsdocs: true,
283
293
  meta: {
@@ -308,6 +318,15 @@ The format is as follows:
308
318
  description: 'Set to `false` to disable auto-removal of types that are redundant with the [`typed` option](#typed).',
309
319
  type: 'boolean',
310
320
  },
321
+ inlineTags: {
322
+ description: `List of tags to allow inline.
323
+
324
+ Defaults to array of \`'link', 'linkcode', 'linkplain', 'tutorial'\``,
325
+ items: {
326
+ type: 'string',
327
+ },
328
+ type: 'array',
329
+ },
311
330
  jsxTags: {
312
331
  description: `If this is set to \`true\`, all of the following tags used to control JSX output are allowed:
313
332
 
@@ -139,7 +139,13 @@ export default iterateJsdoc(({
139
139
  return doc.tags.filter(({
140
140
  tag,
141
141
  }) => {
142
- return utils.isNamepathDefiningTag(tag);
142
+ return utils.isNamepathDefiningTag(tag) && ![
143
+ 'arg',
144
+ 'argument',
145
+ 'param',
146
+ 'prop',
147
+ 'property',
148
+ ].includes(tag);
143
149
  });
144
150
  });
145
151
 
@@ -175,7 +175,10 @@ export default iterateJsdoc(({
175
175
  * newAdd?: boolean
176
176
  * })[]} jsdocTags
177
177
  * @param {import('../iterateJsdoc.js').Integer} indexAtFunctionParams
178
- * @returns {import('../iterateJsdoc.js').Integer}
178
+ * @returns {{
179
+ * foundIndex: import('../iterateJsdoc.js').Integer,
180
+ * tagLineCount: import('../iterateJsdoc.js').Integer,
181
+ * }}
179
182
  */
180
183
  const findExpectedIndex = (jsdocTags, indexAtFunctionParams) => {
181
184
  const remainingRoots = functionParameterNames.slice(indexAtFunctionParams || 0);
@@ -225,7 +228,10 @@ export default iterateJsdoc(({
225
228
  }
226
229
  }
227
230
 
228
- return tagLineCount;
231
+ return {
232
+ foundIndex,
233
+ tagLineCount,
234
+ };
229
235
  };
230
236
 
231
237
  let [
@@ -486,8 +492,22 @@ export default iterateJsdoc(({
486
492
  if (remove) {
487
493
  createTokens(functionParameterIdx, offset + functionParameterIdx, 1);
488
494
  } else {
489
- const expectedIdx = findExpectedIndex(jsdoc.tags, functionParameterIdx);
490
- createTokens(expectedIdx, offset + expectedIdx, 0);
495
+ const {
496
+ foundIndex,
497
+ tagLineCount: expectedIdx,
498
+ } =
499
+ findExpectedIndex(jsdoc.tags, functionParameterIdx);
500
+
501
+ const firstParamLine = jsdoc.source.findIndex(({
502
+ tokens,
503
+ }) => {
504
+ return tokens.tag === `@${preferredTagName}`;
505
+ });
506
+ const baseOffset = foundIndex > -1 || firstParamLine === -1 ?
507
+ offset :
508
+ firstParamLine;
509
+
510
+ createTokens(expectedIdx, baseOffset + expectedIdx, 0);
491
511
  }
492
512
  };
493
513
 
package/src/rules.d.ts CHANGED
@@ -261,6 +261,12 @@ export interface Rules {
261
261
  * Set to `false` to disable auto-removal of types that are redundant with the [`typed` option](#typed).
262
262
  */
263
263
  enableFixer?: boolean;
264
+ /**
265
+ * List of tags to allow inline.
266
+ *
267
+ * Defaults to array of `'link', 'linkcode', 'linkplain', 'tutorial'`
268
+ */
269
+ inlineTags?: string[];
264
270
  /**
265
271
  * If this is set to `true`, all of the following tags used to control JSX output are allowed:
266
272
  *
@@ -2444,6 +2450,9 @@ export interface Rules {
2444
2450
  }
2445
2451
  ];
2446
2452
 
2453
+ /** Requires a description for `@template` tags */
2454
+ "jsdoc/require-template-description": [];
2455
+
2447
2456
  /** Requires that throw statements are documented with `@throws` tags. */
2448
2457
  "jsdoc/require-throws":
2449
2458
  | []