eslint-plugin-jsdoc 65.0.2 → 65.2.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
@@ -494,7 +494,7 @@ non-default-recommended fixer).
494
494
  ||:wrench:| [check-line-alignment](./docs/rules/check-line-alignment.md#readme) | Reports invalid alignment of JSDoc block lines. |
495
495
  |:heavy_check_mark:|:wrench:| [check-param-names](./docs/rules/check-param-names.md#readme) | Checks for dupe `@param` names, that nested param names have roots, and that parameter names in function declarations match JSDoc param names. |
496
496
  |:heavy_check_mark:|:wrench:| [check-property-names](./docs/rules/check-property-names.md#readme) | Ensures that property names in JSDoc are not duplicated on the same block and that nested properties have defined roots. |
497
- ||| [check-syntax](./docs/rules/check-syntax.md#readme) | Reports against syntax not valid for the mode (e.g., Google Closure Compiler in non-Closure mode). |
497
+ ||:wrench:| [check-syntax](./docs/rules/check-syntax.md#readme) | Reports against syntax not valid for the mode (e.g., Google Closure Compiler in non-Closure mode). |
498
498
  |:heavy_check_mark:|:wrench:| [check-tag-names](./docs/rules/check-tag-names.md#readme) | Reports invalid block tag names. |
499
499
  ||| [check-template-names](./docs/rules/check-template-names.md#readme) | Checks that any `@template` names are actually used in the connected `@typedef` or type alias. |
500
500
  |:heavy_check_mark:|:wrench:| [check-types](./docs/rules/check-types.md#readme) | Reports types deemed invalid (customizable and with defaults, for preventing and/or recommending replacements). |
package/dist/rules.d.ts CHANGED
@@ -7,6 +7,10 @@ export interface Rules {
7
7
  | []
8
8
  | [
9
9
  {
10
+ /**
11
+ * Whether to ignore empty and whitespace-only lines (e.g., to allow a formatter to handle)
12
+ */
13
+ ignoreEmptyLines?: boolean;
10
14
  /**
11
15
  * Set to 0 if you wish to avoid the normal requirement for an inner indentation of
12
16
  * one space. Defaults to 1 (one space of normal inner indentation).
@@ -266,7 +270,19 @@ export interface Rules {
266
270
  ];
267
271
 
268
272
  /** Reports against syntax not valid for the mode (e.g., Google Closure Compiler in non-Closure mode). */
269
- "jsdoc/check-syntax": [];
273
+ "jsdoc/check-syntax":
274
+ | []
275
+ | [
276
+ {
277
+ /**
278
+ * Whether to enable the fixer to replace the Closure Compiler style `{type=}`
279
+ * with `{type} [name]` on `@param` and `@property` tags, and with
280
+ * `{type|undefined}` on other tags.
281
+ * Defaults to `false`.
282
+ */
283
+ enableFixer?: boolean;
284
+ }
285
+ ];
270
286
 
271
287
  /** Reports invalid block tag names. */
272
288
  "jsdoc/check-tag-names":
@@ -3301,7 +3317,7 @@ export interface Rules {
3301
3317
  */
3302
3318
  stringQuotes?: "double" | "single";
3303
3319
  /**
3304
- * If `objectFieldSeparatorTrailingPunctuation` is set, this will determine whether the trailing puncutation is only added when the type is multiline
3320
+ * If `objectFieldSeparatorTrailingPunctuation` is set, this will determine whether the trailing punctuation is only added when the type is multiline
3305
3321
  */
3306
3322
  trailingPunctuationMultilineOnly?: boolean;
3307
3323
  /**
package/package.json CHANGED
@@ -163,5 +163,5 @@
163
163
  "test-cov": "TIMING=1 c8 --reporter text pnpm run test-no-cov",
164
164
  "test-index": "pnpm run test-no-cov test/rules/index.js"
165
165
  },
166
- "version": "65.0.2"
166
+ "version": "65.2.0"
167
167
  }
@@ -8,6 +8,7 @@ export default iterateJsdoc(({
8
8
  sourceCode,
9
9
  }) => {
10
10
  const {
11
+ ignoreEmptyLines = false,
11
12
  innerIndent = 1,
12
13
  } = context.options[0] || {};
13
14
 
@@ -15,6 +16,9 @@ export default iterateJsdoc(({
15
16
  const indentLevel = indent.length + innerIndent;
16
17
  const sourceLines = sourceCode.getText(jsdocNode).split('\n')
17
18
  .slice(1)
19
+ .filter((line) => {
20
+ return !ignoreEmptyLines || line.trim();
21
+ })
18
22
  .map((line, number) => {
19
23
  return {
20
24
  line: line.split('*')[0],
@@ -67,6 +71,10 @@ export default iterateJsdoc(({
67
71
  {
68
72
  additionalProperties: false,
69
73
  properties: {
74
+ ignoreEmptyLines: {
75
+ description: 'Whether to ignore empty and whitespace-only lines (e.g., to allow a formatter to handle)',
76
+ type: 'boolean',
77
+ },
70
78
  innerIndent: {
71
79
  default: 1,
72
80
  description: `Set to 0 if you wish to avoid the normal requirement for an inner indentation of
@@ -118,8 +118,8 @@ export default iterateJsdoc(({
118
118
  hasSeenContent = true;
119
119
  }
120
120
 
121
- // Reset section indent when we encounter a tag
122
- if (/@\w+/v.test(line)) {
121
+ // Reset section indent when we encounter a block tag
122
+ if (/^(?:\/?\**|[\t ]*)\*[ \t]*@\w+/v.test(line)) {
123
123
  currentSectionIndent = null;
124
124
  }
125
125
  }
@@ -1,20 +1,116 @@
1
1
  import iterateJsdoc from '../iterateJsdoc.js';
2
2
 
3
+ // Closure's `{type=} name` is written `{type} [name]` in JSDoc/TypeScript
4
+ const namedTags = new Set([
5
+ 'arg', 'argument', 'param', 'prop', 'property',
6
+ ]);
7
+
8
+ // Types that do not need parentheses before `|undefined`
9
+ const simpleType = /^[\w$.]+$/v;
10
+
11
+ /**
12
+ * Builds the fix for a tag with a Closure style optional type (`{type=}`), if
13
+ * there is one.
14
+ *
15
+ * Named tags (`@param {type=} name`) become `{type} [name]`; for other tags
16
+ * (or a missing or defaulted name), the type is joined with `undefined`.
17
+ * @param {import('comment-parser').Spec} tag
18
+ * @returns {() => void}
19
+ */
20
+ const getFix = (tag) => {
21
+ const {
22
+ source,
23
+ } = tag;
24
+
25
+ // For a type over several lines, the end of the type is on the line that has the name
26
+ const lastTypeLine = /** @type {import('comment-parser').Line} */ (source.findLast(({
27
+ tokens,
28
+ }) => {
29
+ return tokens.type.endsWith('=}');
30
+ }));
31
+
32
+ const firstTypeLine = /** @type {import('comment-parser').Line} */ (source.find(({
33
+ tokens,
34
+ }) => {
35
+ return tokens.type;
36
+ }));
37
+
38
+ const nameTokens = source.find(({
39
+ tokens,
40
+ }) => {
41
+ return tokens.name;
42
+ })?.tokens;
43
+
44
+ // A name with a default (`[foo=bar]`) is already bracketed; a bare `foo=bar` is not a valid name
45
+ if (namedTags.has(tag.tag) && nameTokens && (!nameTokens.name.includes('=') || nameTokens.name.startsWith('['))) {
46
+ return () => {
47
+ lastTypeLine.tokens.type = `${lastTypeLine.tokens.type.slice(0, -2)}}`;
48
+ if (!nameTokens.name.startsWith('[')) {
49
+ nameTokens.name = `[${nameTokens.name}]`;
50
+ }
51
+
52
+ tag.optional = true;
53
+ };
54
+ }
55
+
56
+ const isSingleLine = firstTypeLine === lastTypeLine;
57
+ const inner = lastTypeLine.tokens.type.slice(isSingleLine ? 1 : 0, -2);
58
+ if (isSingleLine && simpleType.test(inner)) {
59
+ return () => {
60
+ lastTypeLine.tokens.type = `{${inner}|undefined}`;
61
+ };
62
+ }
63
+
64
+ return () => {
65
+ firstTypeLine.tokens.type = `{(${firstTypeLine.tokens.type.slice(1)}`;
66
+ lastTypeLine.tokens.type = `${lastTypeLine.tokens.type.slice(0, -2)})|undefined}`;
67
+ };
68
+ };
69
+
3
70
  export default iterateJsdoc(({
71
+ context,
4
72
  jsdoc,
5
73
  report,
6
74
  settings,
75
+ utils,
7
76
  }) => {
77
+ const {
78
+ enableFixer = false,
79
+ } = context.options[0] || {};
80
+
8
81
  const {
9
82
  mode,
10
83
  } = settings;
11
84
 
12
85
  // Don't check for "permissive" and "closure"
13
86
  if (mode === 'jsdoc' || mode === 'typescript') {
87
+ /** @type {import('comment-parser').Spec|undefined} */
88
+ let firstTag;
89
+ /** @type {(() => void)[]} */
90
+ const fixes = [];
91
+
14
92
  for (const tag of jsdoc.tags) {
15
- if (tag.type.slice(-1) === '=') {
16
- report('Syntax should not be Google Closure Compiler style.', null, tag);
17
- break;
93
+ if (tag.type.slice(-1) !== '=') {
94
+ continue;
95
+ }
96
+
97
+ firstTag ??= tag;
98
+
99
+ if (enableFixer) {
100
+ fixes.push(getFix(tag));
101
+ }
102
+ }
103
+
104
+ if (firstTag) {
105
+ const message = 'Syntax should not be Google Closure Compiler style.';
106
+ if (fixes.length) {
107
+ utils.reportJSDoc(message, firstTag, () => {
108
+ for (const fix of fixes) {
109
+ fix();
110
+ }
111
+ });
112
+ } else {
113
+ report(message, null, firstTag);
18
114
  }
19
115
  }
20
116
  }
@@ -25,6 +121,22 @@ export default iterateJsdoc(({
25
121
  description: 'Reports against syntax not valid for the mode (e.g., Google Closure Compiler in non-Closure mode).',
26
122
  url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-syntax.md#repos-sticky-header',
27
123
  },
124
+ fixable: 'code',
125
+ schema: [
126
+ {
127
+ additionalProperties: false,
128
+ properties: {
129
+ enableFixer: {
130
+ description: `Whether to enable the fixer to replace the Closure Compiler style \`{type=}\`
131
+ with \`{type} [name]\` on \`@param\` and \`@property\` tags, and with
132
+ \`{type|undefined}\` on other tags.
133
+ Defaults to \`false\`.`,
134
+ type: 'boolean',
135
+ },
136
+ },
137
+ type: 'object',
138
+ },
139
+ ],
28
140
  type: 'suggestion',
29
141
  },
30
142
  });
@@ -668,7 +668,7 @@ or \`double\`. Defaults to 'double'.`,
668
668
  type: 'string',
669
669
  },
670
670
  trailingPunctuationMultilineOnly: {
671
- description: 'If `objectFieldSeparatorTrailingPunctuation` is set, this will determine whether the trailing puncutation is only added when the type is multiline',
671
+ description: 'If `objectFieldSeparatorTrailingPunctuation` is set, this will determine whether the trailing punctuation is only added when the type is multiline',
672
672
  type: 'boolean',
673
673
  },
674
674
  typeBracketSpacing: {
package/src/rules.d.ts CHANGED
@@ -7,6 +7,10 @@ export interface Rules {
7
7
  | []
8
8
  | [
9
9
  {
10
+ /**
11
+ * Whether to ignore empty and whitespace-only lines (e.g., to allow a formatter to handle)
12
+ */
13
+ ignoreEmptyLines?: boolean;
10
14
  /**
11
15
  * Set to 0 if you wish to avoid the normal requirement for an inner indentation of
12
16
  * one space. Defaults to 1 (one space of normal inner indentation).
@@ -266,7 +270,19 @@ export interface Rules {
266
270
  ];
267
271
 
268
272
  /** Reports against syntax not valid for the mode (e.g., Google Closure Compiler in non-Closure mode). */
269
- "jsdoc/check-syntax": [];
273
+ "jsdoc/check-syntax":
274
+ | []
275
+ | [
276
+ {
277
+ /**
278
+ * Whether to enable the fixer to replace the Closure Compiler style `{type=}`
279
+ * with `{type} [name]` on `@param` and `@property` tags, and with
280
+ * `{type|undefined}` on other tags.
281
+ * Defaults to `false`.
282
+ */
283
+ enableFixer?: boolean;
284
+ }
285
+ ];
270
286
 
271
287
  /** Reports invalid block tag names. */
272
288
  "jsdoc/check-tag-names":
@@ -3301,7 +3317,7 @@ export interface Rules {
3301
3317
  */
3302
3318
  stringQuotes?: "double" | "single";
3303
3319
  /**
3304
- * If `objectFieldSeparatorTrailingPunctuation` is set, this will determine whether the trailing puncutation is only added when the type is multiline
3320
+ * If `objectFieldSeparatorTrailingPunctuation` is set, this will determine whether the trailing punctuation is only added when the type is multiline
3305
3321
  */
3306
3322
  trailingPunctuationMultilineOnly?: boolean;
3307
3323
  /**