@depup/eslint-plugin-jsdoc 64.3.8-depup.0 → 64.3.10-depup.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/README.md CHANGED
@@ -13,8 +13,8 @@ npm install @depup/eslint-plugin-jsdoc
13
13
 
14
14
  | Field | Value |
15
15
  |-------|-------|
16
- | Original | [eslint-plugin-jsdoc](https://www.npmjs.com/package/eslint-plugin-jsdoc) @ 64.3.8 |
17
- | Processed | 2026-09-09 |
16
+ | Original | [eslint-plugin-jsdoc](https://www.npmjs.com/package/eslint-plugin-jsdoc) @ 64.3.10 |
17
+ | Processed | 2026-09-12 |
18
18
  | Smoke test | passed |
19
19
  | Deps updated | 2 |
20
20
 
package/changes.json CHANGED
@@ -9,6 +9,6 @@
9
9
  "to": "^1.4.9"
10
10
  }
11
11
  },
12
- "timestamp": "2026-09-09T16:10:08.502Z",
12
+ "timestamp": "2026-09-12T16:08:52.008Z",
13
13
  "totalUpdated": 2
14
14
  }
package/package.json CHANGED
@@ -162,7 +162,7 @@
162
162
  "test-cov": "TIMING=1 c8 --reporter text pnpm run test-no-cov",
163
163
  "test-index": "pnpm run test-no-cov test/rules/index.js"
164
164
  },
165
- "version": "64.3.8-depup.0",
165
+ "version": "64.3.10-depup.0",
166
166
  "depup": {
167
167
  "changes": {
168
168
  "@typescript-eslint/utils": {
@@ -176,8 +176,8 @@
176
176
  },
177
177
  "depsUpdated": 2,
178
178
  "originalPackage": "eslint-plugin-jsdoc",
179
- "originalVersion": "64.3.8",
180
- "processedAt": "2026-09-09T16:10:35.569Z",
179
+ "originalVersion": "64.3.10",
180
+ "processedAt": "2026-09-12T16:09:18.335Z",
181
181
  "smokeTest": "passed"
182
182
  }
183
183
  }
@@ -163,8 +163,11 @@ export default {
163
163
  * @param {import('eslint').Rule.Node} node
164
164
  * @param {AddComment} addComment
165
165
  * @param {import('../iterateJsdoc.js').Context[]} ctxts
166
+ * @param {Token} [locComment] Comment to anchor the report location to,
167
+ * e.g., the first comment of a stacked group of line comments, so the
168
+ * report does not read as though only the last line were at fault.
166
169
  */
167
- const reportings = (comment, node, addComment, ctxts) => {
170
+ const reportings = (comment, node, addComment, ctxts, locComment) => {
168
171
  const fixer = getFixer(node, comment, addComment, ctxts);
169
172
 
170
173
  if (comment.type === 'Block') {
@@ -181,33 +184,89 @@ export default {
181
184
  return;
182
185
  }
183
186
 
184
- report('lineCommentsJsdocStyle', comment, node, fixer);
187
+ report('lineCommentsJsdocStyle', locComment ?? comment, node, fixer);
185
188
  }
186
189
  };
187
190
 
191
+ /**
192
+ * Walks backward from a `//` comment which immediately precedes a node,
193
+ * collecting any directly adjacent (no intervening blank line) `//`
194
+ * comments stacked above it, so that the fixer can convert the whole
195
+ * run into a single JSDoc block instead of leaving all but the last
196
+ * line behind.
197
+ * @param {Token} comment
198
+ * @returns {Token[]}
199
+ */
200
+ const getPrecedingLineCommentGroup = (comment) => {
201
+ const group = [
202
+ comment,
203
+ ];
204
+ let current = comment;
205
+
206
+ for (;;) {
207
+ const prev = /** @type {Token|null} */ (
208
+ sourceCode.getTokenBefore(
209
+ /** @type {import('eslint').AST.Token} */ (current),
210
+ {
211
+ includeComments: true,
212
+ },
213
+ )
214
+ );
215
+
216
+ if (
217
+ !prev ||
218
+ prev.type !== 'Line' ||
219
+ // @ts-expect-error Ok
220
+ prev.loc.end.line !== current.loc.start.line - 1 ||
221
+ /** @type {string[]} */
222
+ (allowedPrefixes).some((prefix) => {
223
+ return prev.value.trimStart().startsWith(prefix);
224
+ })
225
+ ) {
226
+ break;
227
+ }
228
+
229
+ group.unshift(prev);
230
+ current = prev;
231
+ }
232
+
233
+ return group;
234
+ };
235
+
188
236
  /**
189
237
  * Builds the opening portion of the JSDoc comment, i.e. everything before
190
- * the closing delimiter.
238
+ * the closing delimiter. When more than one comment is supplied (e.g., a
239
+ * stacked group of `//` lines), each is rendered on its own JSDoc line.
191
240
  * @param {string} indent
192
- * @param {Token} comment
241
+ * @param {Token[]} comments
193
242
  * @param {boolean|undefined} inlineCommentBlock
194
243
  * @returns {string}
195
244
  */
196
- const getCommentOpening = (indent, comment, inlineCommentBlock) => {
245
+ const getCommentOpening = (indent, comments, inlineCommentBlock) => {
197
246
  if (inlineCommentBlock || enforceJsdocLineStyle === 'single') {
198
- return `/** ${comment.value.trim()} `;
247
+ return `/** ${comments.map((comment) => {
248
+ return comment.value.trim();
249
+ }).join(' ')} `;
199
250
  }
200
251
 
201
- const body = comment.value.trimEnd();
252
+ if (comments.length === 1) {
253
+ const body = comments[0].value.trimEnd();
254
+
255
+ // When the comment's text already begins on its own line (e.g. a
256
+ // multi-line block comment), there is no need for the fixer to add a
257
+ // leading blank `*` line.
258
+ if ((/^[ \t]*\n/v).test(body)) {
259
+ return `/**${body.replace(/^[ \t]+/v, '')}\n${indent}`;
260
+ }
202
261
 
203
- // When the comment's text already begins on its own line (e.g. a
204
- // multi-line block comment), there is no need for the fixer to add a
205
- // leading blank `*` line.
206
- if ((/^[ \t]*\n/v).test(body)) {
207
- return `/**${body.replace(/^[ \t]+/v, '')}\n${indent}`;
262
+ return `/**\n${indent}*${body}\n${indent}`;
208
263
  }
209
264
 
210
- return `/**\n${indent}*${body}\n${indent}`;
265
+ const inner = comments.map((comment) => {
266
+ return `${indent}*${comment.value.trimEnd()}`;
267
+ }).join('\n');
268
+
269
+ return `/**\n${inner}\n${indent}`;
211
270
  };
212
271
 
213
272
  /**
@@ -228,19 +287,29 @@ export default {
228
287
 
229
288
  reportingNonJsdoc = true;
230
289
 
290
+ const commentGroup = comment.type === 'Line' ?
291
+ getPrecedingLineCommentGroup(/** @type {Token} */ (comment)) :
292
+ [
293
+ /** @type {Token} */ (comment),
294
+ ];
295
+
231
296
  /** @type {AddComment} */
232
297
  const addComment = (inlineCommentBlock, commentToAdd, indent, lines, fixer) => {
233
- const insertion = getCommentOpening(indent, commentToAdd, inlineCommentBlock) +
298
+ const insertion = getCommentOpening(indent, commentGroup, inlineCommentBlock) +
234
299
  `*/${'\n'.repeat((lines || 1) - 1)}`;
235
300
 
236
- return fixer.replaceText(
237
- /** @type {import('eslint').AST.Token} */
238
- (commentToAdd),
301
+ return fixer.replaceTextRange(
302
+ [
303
+ /* c8 ignore next -- Guard */
304
+ commentGroup[0].range?.[0] ?? 0,
305
+ /* c8 ignore next -- Guard */
306
+ commentToAdd.range?.[1] ?? 0,
307
+ ],
239
308
  insertion,
240
309
  );
241
310
  };
242
311
 
243
- reportings(comment, node, addComment, contexts);
312
+ reportings(comment, node, addComment, contexts, commentGroup[0]);
244
313
  };
245
314
 
246
315
  /**
@@ -263,7 +332,9 @@ export default {
263
332
 
264
333
  /** @type {AddComment} */
265
334
  const addComment = (inlineCommentBlock, commentToAdd, indent, lines, fixer) => {
266
- const insertion = getCommentOpening(indent, commentToAdd, inlineCommentBlock) +
335
+ const insertion = getCommentOpening(indent, [
336
+ /** @type {Token} */ (commentToAdd),
337
+ ], inlineCommentBlock) +
267
338
  `*/${'\n'.repeat((lines || 1) - 1)}${lines ? `\n${indent.slice(1)}` : ' '}`;
268
339
 
269
340
  return [
@@ -483,32 +483,34 @@ export default iterateJsdoc(({
483
483
  .concat(/** @type {string[]} */ (definedPreferredTypes))
484
484
  .concat((() => {
485
485
  // Other class members are not in scope, but we need them (e.g., for a
486
- // sibling property or method referenced by `{@link}`), and we grab
487
- // them here
486
+ // sibling property or method referenced by `{@link}`, or a member
487
+ // referenced by `{@link}` from the class's own JSDoc block), and we
488
+ // grab them here
489
+ /** @type {import('estree').ClassBody|undefined} */
490
+ let classBody;
491
+ /** @type {string|undefined} */
492
+ let className;
488
493
  if (node?.type === 'MethodDefinition' || node?.type === 'PropertyDefinition') {
489
- return /** @type {import('estree').ClassBody} */ (node.parent).body.flatMap((methodOrProp) => {
490
- if (methodOrProp.type === 'MethodDefinition') {
491
- // eslint-disable-next-line unicorn/no-lonely-if -- Pattern
492
- if (methodOrProp.key.type === 'Identifier') {
493
- return [
494
- methodOrProp.key.name,
495
- `${/** @type {import('estree').ClassDeclaration} */ (
496
- node.parent?.parent
497
- )?.id?.name}.${methodOrProp.key.name}`,
498
- ];
499
- }
500
- }
494
+ classBody = /** @type {import('estree').ClassBody} */ (node.parent);
495
+ className = /** @type {import('estree').ClassDeclaration} */ (
496
+ node.parent?.parent
497
+ )?.id?.name;
498
+ } else if (node?.type === 'ClassDeclaration' || node?.type === 'ClassExpression') {
499
+ classBody = node.body;
500
+ className = node.id?.name;
501
+ }
501
502
 
502
- if (methodOrProp.type === 'PropertyDefinition') {
503
- // eslint-disable-next-line unicorn/no-lonely-if -- Pattern
504
- if (methodOrProp.key.type === 'Identifier') {
505
- return [
506
- methodOrProp.key.name,
507
- `${/** @type {import('estree').ClassDeclaration} */ (
508
- node.parent?.parent
509
- )?.id?.name}.${methodOrProp.key.name}`,
510
- ];
511
- }
503
+ if (classBody) {
504
+ return classBody.body.flatMap((methodOrProp) => {
505
+ if (
506
+ (methodOrProp.type === 'MethodDefinition' ||
507
+ methodOrProp.type === 'PropertyDefinition') &&
508
+ methodOrProp.key.type === 'Identifier'
509
+ ) {
510
+ return [
511
+ methodOrProp.key.name,
512
+ `${className}.${methodOrProp.key.name}`,
513
+ ];
512
514
  }
513
515
  /* c8 ignore next 2 -- Not yet built */
514
516