@depup/eslint-plugin-jsdoc 64.3.4-depup.0 → 64.3.6-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,17 +13,10 @@ 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.4 |
17
- | Processed | 2026-09-02 |
16
+ | Original | [eslint-plugin-jsdoc](https://www.npmjs.com/package/eslint-plugin-jsdoc) @ 64.3.6 |
17
+ | Processed | 2026-09-06 |
18
18
  | Smoke test | passed |
19
- | Deps updated | 2 |
20
-
21
- ## Dependency Changes
22
-
23
- | Dependency | From | To |
24
- |------------|------|-----|
25
- | @es-joy/jsdoccomment | ~0.95.1 | ^0.96.0 |
26
- | @typescript-eslint/utils | ^8.68.0 | ^8.69.0 |
19
+ | Deps updated | 0 |
27
20
 
28
21
  ---
29
22
 
package/changes.json CHANGED
@@ -1,14 +1,5 @@
1
1
  {
2
- "bumped": {
3
- "@es-joy/jsdoccomment": {
4
- "from": "~0.95.1",
5
- "to": "^0.96.0"
6
- },
7
- "@typescript-eslint/utils": {
8
- "from": "^8.68.0",
9
- "to": "^8.69.0"
10
- }
11
- },
12
- "timestamp": "2026-09-02T00:33:20.653Z",
13
- "totalUpdated": 2
2
+ "bumped": {},
3
+ "timestamp": "2026-09-06T00:59:03.732Z",
4
+ "totalUpdated": 0
14
5
  }
package/dist/rules.d.ts CHANGED
@@ -1324,6 +1324,10 @@ export interface Rules {
1324
1324
  * Whether to enable the fixer that removes the redundant `@type` tag (and the JSDoc block if it becomes empty). Defaults to `true`.
1325
1325
  */
1326
1326
  enableFixer?: boolean;
1327
+ /**
1328
+ * Whether to report a non-`const` literal-tuple assertion on an array literal (e.g. `/** @type {['foo']} * / (['foo'])`) and fix it to the equivalent, more concise `/** @type {const} * /` assertion. Defaults to `false`.
1329
+ */
1330
+ preferConstToLiteralTuples?: boolean;
1327
1331
  /**
1328
1332
  * Whether to treat `any` type casts as redundant
1329
1333
  */
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "url": "http://gajus.com"
6
6
  },
7
7
  "dependencies": {
8
- "@es-joy/jsdoccomment": "^0.96.0",
8
+ "@es-joy/jsdoccomment": "~0.97.0",
9
9
  "@es-joy/resolve.exports": "1.2.0",
10
10
  "@typescript-eslint/utils": "^8.69.0",
11
11
  "are-docs-informative": "^0.1.1",
@@ -41,36 +41,36 @@
41
41
  "@types/estree": "^1.0.9",
42
42
  "@types/json-schema": "^7.0.15",
43
43
  "@types/mocha": "^10.0.10",
44
- "@types/node": "^26.2.0",
44
+ "@types/node": "^26.4.1",
45
45
  "@types/semver": "^7.8.0",
46
46
  "@types/spdx-expression-parse": "^4.0.0",
47
- "@typescript-eslint/types": "8.67.0",
47
+ "@typescript-eslint/types": "8.69.0",
48
48
  "babel-plugin-add-module-exports": "^1.0.4",
49
49
  "babel-plugin-transform-import-meta": "^3.0.0",
50
50
  "c8": "^12.0.0",
51
51
  "camelcase": "^9.0.0",
52
52
  "chai": "^6.2.2",
53
53
  "decamelize": "^6.0.1",
54
- "eslint": "10.8.1",
54
+ "eslint": "10.10.0",
55
55
  "eslint-config-canonical": "^47.4.2",
56
56
  "gitdown": "^4.1.1",
57
57
  "glob": "^13.0.6",
58
- "globals": "^17.11.0",
58
+ "globals": "^17.12.0",
59
59
  "husky": "^9.1.7",
60
- "jsdoc-type-pratt-parser": "^9.1.2",
60
+ "jsdoc-type-pratt-parser": "^9.2.1",
61
61
  "json-schema": "^0.4.0",
62
- "json-schema-to-typescript": "^15.0.4",
63
- "lint-staged": "^17.3.0",
64
- "mocha": "^11.8.0",
62
+ "json-schema-to-typescript": "^16.0.0",
63
+ "lint-staged": "^17.5.0",
64
+ "mocha": "^12.0.0",
65
65
  "open-editor": "^6.0.0",
66
- "playwright": "^1.62.1",
66
+ "playwright": "^1.63.0",
67
67
  "replace": "^1.2.2",
68
68
  "rimraf": "^6.1.3",
69
69
  "semantic-release": "^25.0.9",
70
70
  "sinon": "^22.1.0",
71
71
  "ts-api-utils": "^2.5.0",
72
72
  "typescript": "6.0.3",
73
- "typescript-eslint": "8.67.0"
73
+ "typescript-eslint": "8.69.0"
74
74
  },
75
75
  "engines": {
76
76
  "node": "^22.22.2 || >=24.15.0"
@@ -162,22 +162,13 @@
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.4-depup.0",
165
+ "version": "64.3.6-depup.0",
166
166
  "depup": {
167
- "changes": {
168
- "@es-joy/jsdoccomment": {
169
- "from": "~0.95.1",
170
- "to": "^0.96.0"
171
- },
172
- "@typescript-eslint/utils": {
173
- "from": "^8.68.0",
174
- "to": "^8.69.0"
175
- }
176
- },
177
- "depsUpdated": 2,
167
+ "changes": {},
168
+ "depsUpdated": 0,
178
169
  "originalPackage": "eslint-plugin-jsdoc",
179
- "originalVersion": "64.3.4",
180
- "processedAt": "2026-09-02T00:33:49.226Z",
170
+ "originalVersion": "64.3.6",
171
+ "processedAt": "2026-09-06T00:59:25.028Z",
181
172
  "smokeTest": "passed"
182
173
  }
183
174
  }
@@ -87,6 +87,7 @@ export default iterateJsdoc(({
87
87
  // https://typescript-eslint.io/rules/no-unnecessary-type-assertion/
88
88
  checkLiteralConstAssertions = false,
89
89
  enableFixer = true,
90
+ preferConstToLiteralTuples = false,
90
91
  treatAnyAsRedundant = false,
91
92
  typesToIgnore = [],
92
93
  } = context.options[0] ?? {};
@@ -154,6 +155,24 @@ export default iterateJsdoc(({
154
155
  'The @type tag declaring "{{ type }}" is redundant as TypeScript infers it automatically for literals.' :
155
156
  'The @type tag declaring "{{ type }}" is redundant as TypeScript infers it automatically.';
156
157
 
158
+ const preferConstMessage =
159
+ 'The @type tag declaring "{{ type }}" is better written as the "const" assertion `/** @type {const} */` (TypeScript 4.5+).';
160
+
161
+ /**
162
+ * Rewrites the reported `@type` tag's type to `const`.
163
+ * @returns {void}
164
+ */
165
+ const convertToConst = () => {
166
+ for (const {
167
+ tokens,
168
+ } of /** @type {import('comment-parser').Spec} */ (types[0]).source) {
169
+ if (tokens.type) {
170
+ tokens.type = '{const}';
171
+ break;
172
+ }
173
+ }
174
+ };
175
+
157
176
  /**
158
177
  * Whether `inferredType` is a generic reference carrying `any` type arguments
159
178
  * that `assertedType` replaces with concrete ones (e.g. an untyped
@@ -327,6 +346,97 @@ export default iterateJsdoc(({
327
346
  checker.isTypeAssignableTo(assertedBaseType, inferredBaseType);
328
347
  };
329
348
 
349
+ /**
350
+ * A non-`const` tuple type annotation on an array literal (asserting `['foo']`
351
+ * onto `['foo']`) turns the literal's inferred `T[]` into a tuple; since the
352
+ * literal only takes that tuple type *from* the assertion, the assertion is
353
+ * never genuinely redundant even though the contextually-typed expression
354
+ * echoes the asserted tuple straight back. It is the pre-TypeScript-4.5
355
+ * stand-in for a `const` assertion.
356
+ * @param {any} assertedType `ts.Type`
357
+ * @param {import('@typescript-eslint/utils').TSESTree.Node | null | undefined} operand
358
+ * @returns {boolean}
359
+ */
360
+ const isTupleConversionIdiom = (assertedType, operand) => {
361
+ return assertedTypeStr !== 'const' &&
362
+ operand?.type === 'ArrayExpression' &&
363
+ checker.isTupleType(assertedType);
364
+ };
365
+
366
+ /**
367
+ * An array-literal annotation whose element type is a literal (or union of
368
+ * literals) — `type {ViewType[]}` on `['single-panel', 'user']` —
369
+ * narrows the element type that the bare literal would widen (`string[]` ->
370
+ * `ViewType[]`). Like the tuple idiom, the literal takes that element type
371
+ * only *from* the annotation, so the contextually-typed initializer echoes
372
+ * the asserted array straight back and the genuine narrowing looks redundant.
373
+ * @param {any} assertedType `ts.Type`
374
+ * @param {import('@typescript-eslint/utils').TSESTree.Node | null | undefined} operand
375
+ * @returns {boolean}
376
+ */
377
+ const narrowsArrayLiteralElement = (assertedType, operand) => {
378
+ if (operand?.type !== 'ArrayExpression' || !checker.isArrayType(assertedType)) {
379
+ return false;
380
+ }
381
+
382
+ const [
383
+ elementType,
384
+ ] = checker.getTypeArguments(assertedType);
385
+ return isLiteralType(elementType);
386
+ };
387
+
388
+ /**
389
+ * Whether `operand` is an object literal carrying a method or function-valued
390
+ * property. Under a surrounding `@type` object annotation such a function is
391
+ * contextually typed by the annotation — its otherwise-implicit-`any`
392
+ * parameters (and hence its signature) come straight from it — so
393
+ * `getTypeAtLocation` echoes the asserted object shape back and a genuine
394
+ * typing looks redundant. As elsewhere in this rule the uncontaminated type
395
+ * cannot be recovered here, so such literals are left alone (at the cost of
396
+ * missing the rare object annotation that only restates a fully
397
+ * self-evident method signature).
398
+ * @param {import('@typescript-eslint/utils').TSESTree.Node | null | undefined} operand
399
+ * @returns {boolean}
400
+ */
401
+ const hasContextuallyTypedFunctionMember = (operand) => {
402
+ return operand?.type === 'ObjectExpression' &&
403
+ operand.properties.some((property) => {
404
+ return property.type === 'Property' && (
405
+ property.value.type === 'FunctionExpression' ||
406
+ property.value.type === 'ArrowFunctionExpression'
407
+ );
408
+ });
409
+ };
410
+
411
+ /**
412
+ * Whether every element of `tupleType` is a literal type, so a `const`
413
+ * assertion would reproduce the same element types (`['foo', 1]` but not
414
+ * `[string]`).
415
+ * @param {any} tupleType `ts.Type`
416
+ * @returns {boolean}
417
+ */
418
+ const tupleElementsAllLiteral = (tupleType) => {
419
+ const elements = checker.getTypeArguments(tupleType);
420
+ return elements.length > 0 && elements.every(isLiteralType);
421
+ };
422
+
423
+ /**
424
+ * Reports an array-literal tuple assertion, offering (under `enableFixer`) a
425
+ * rewrite to the equivalent, more concise `const` assertion.
426
+ * @returns {void}
427
+ */
428
+ const reportPreferConst = () => {
429
+ utils.reportJSDoc(
430
+ preferConstMessage,
431
+ types[0],
432
+ enableFixer ? convertToConst : null,
433
+ false,
434
+ {
435
+ type: assertedTypeStr,
436
+ },
437
+ );
438
+ };
439
+
330
440
  // Positions where a bare expression of any precedence is valid and equivalent
331
441
  // to the parenthesized form, so a redundant `/** @type {T} */ (expr)` cast can
332
442
  // be unwrapped to `expr` without changing meaning.
@@ -372,6 +482,21 @@ export default iterateJsdoc(({
372
482
  const declInferredType = checker.getTypeAtLocation(declInitTsNode);
373
483
  const declAssertedType = checker.getTypeFromTypeNode(jsdocTypeNode);
374
484
 
485
+ if (isTupleConversionIdiom(declAssertedType, decl.init)) {
486
+ if (preferConstToLiteralTuples && tupleElementsAllLiteral(declAssertedType)) {
487
+ reportPreferConst();
488
+ }
489
+
490
+ return;
491
+ }
492
+
493
+ if (
494
+ narrowsArrayLiteralElement(declAssertedType, decl.init) ||
495
+ hasContextuallyTypedFunctionMember(decl.init)
496
+ ) {
497
+ return;
498
+ }
499
+
375
500
  if (isRedundantAssertion(declInferredType, declAssertedType)) {
376
501
  utils.reportJSDoc(message, types[0], fixer, true, {
377
502
  type: assertedTypeStr,
@@ -441,6 +566,21 @@ export default iterateJsdoc(({
441
566
  checker.getTypeAtLocation(exprTsNode);
442
567
  const castAssertedType = checker.getTypeFromTypeNode(typeTag.typeExpression.type);
443
568
 
569
+ if (isTupleConversionIdiom(castAssertedType, node)) {
570
+ if (preferConstToLiteralTuples && tupleElementsAllLiteral(castAssertedType)) {
571
+ reportPreferConst();
572
+ }
573
+
574
+ return;
575
+ }
576
+
577
+ if (
578
+ narrowsArrayLiteralElement(castAssertedType, node) ||
579
+ hasContextuallyTypedFunctionMember(node)
580
+ ) {
581
+ return;
582
+ }
583
+
444
584
  if (!isRedundantAssertion(castInferredType, castAssertedType)) {
445
585
  return;
446
586
  }
@@ -503,6 +643,10 @@ export default iterateJsdoc(({
503
643
  description: 'Whether to enable the fixer that removes the redundant `@type` tag (and the JSDoc block if it becomes empty). Defaults to `true`.',
504
644
  type: 'boolean',
505
645
  },
646
+ preferConstToLiteralTuples: {
647
+ description: 'Whether to report a non-`const` literal-tuple assertion on an array literal (e.g. `/** @type {[\'foo\']} */ ([\'foo\'])`) and fix it to the equivalent, more concise `/** @type {const} */` assertion. Defaults to `false`.',
648
+ type: 'boolean',
649
+ },
506
650
  treatAnyAsRedundant: {
507
651
  description: 'Whether to treat `any` type casts as redundant',
508
652
  type: 'boolean',
@@ -34,7 +34,6 @@ import {
34
34
  /** @type {import('json-schema').JSONSchema4} */
35
35
  const OPTIONS_SCHEMA = {
36
36
  additionalProperties: false,
37
- description: 'Has the following optional keys.\n',
38
37
  properties: {
39
38
  checkAllFunctionExpressions: {
40
39
  default: false,
package/src/rules.d.ts CHANGED
@@ -1324,6 +1324,10 @@ export interface Rules {
1324
1324
  * Whether to enable the fixer that removes the redundant `@type` tag (and the JSDoc block if it becomes empty). Defaults to `true`.
1325
1325
  */
1326
1326
  enableFixer?: boolean;
1327
+ /**
1328
+ * Whether to report a non-`const` literal-tuple assertion on an array literal (e.g. `/** @type {['foo']} * / (['foo'])`) and fix it to the equivalent, more concise `/** @type {const} * /` assertion. Defaults to `false`.
1329
+ */
1330
+ preferConstToLiteralTuples?: boolean;
1327
1331
  /**
1328
1332
  * Whether to treat `any` type casts as redundant
1329
1333
  */