eslint-plugin-jsdoc 64.4.0 → 64.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/README.md CHANGED
@@ -552,6 +552,7 @@ non-default-recommended fixer).
552
552
  ||:wrench:| [sort-tags](./docs/rules/sort-tags.md#readme) | Sorts tags by a specified sequence according to tag name, optionally adding line breaks between tag groups. |
553
553
  |:heavy_check_mark:|:wrench:| [tag-lines](./docs/rules/tag-lines.md#readme) | Enforces lines (or no lines) before, after, or between tags. |
554
554
  ||:wrench:| [text-escaping](./docs/rules/text-escaping.md#readme) | Auto-escape certain characters that are input within block and tag descriptions. |
555
+ |Off in TS; On in TS flavor|| [ts-ban-ts-comment](./docs/rules/ts-ban-ts-comment.md#readme) | Disallows (or requires descriptions for) `@ts-<directive>` comments, mirroring `@typescript-eslint/ban-ts-comment`. |
555
556
  ||:wrench:| [ts-method-signature-style](./docs/rules/ts-method-signature-style.md#readme) | Prefers either function properties or method signatures |
556
557
  |:heavy_check_mark:|| [ts-no-empty-object-type](./docs/rules/ts-no-empty-object-type.md#readme) | Warns against use of the empty object type |
557
558
  ||:wrench:| [ts-no-unnecessary-template-expression](./docs/rules/ts-no-unnecessary-template-expression.md#readme) | Catches unnecessary template expressions such as string expressions within a template literal. |
@@ -0,0 +1,13 @@
1
+ declare const _default: import("eslint").Rule.RuleModule;
2
+ export default _default;
3
+ export type DirectiveConfig = boolean | "allow-with-description" | {
4
+ descriptionFormat: string;
5
+ };
6
+ export type OptionsShape = {
7
+ minimumDescriptionLength?: number;
8
+ "ts-check"?: DirectiveConfig;
9
+ "ts-expect-error"?: DirectiveConfig;
10
+ "ts-ignore"?: DirectiveConfig;
11
+ "ts-nocheck"?: DirectiveConfig;
12
+ };
13
+ //# sourceMappingURL=tsBanTsComment.d.ts.map
package/dist/rules.d.ts CHANGED
@@ -3065,6 +3065,66 @@ export interface Rules {
3065
3065
  }
3066
3066
  ];
3067
3067
 
3068
+ /** Disallows (or requires descriptions for) `@ts-<directive>` comments, mirroring `@typescript-eslint/ban-ts-comment`. */
3069
+ "jsdoc/ts-ban-ts-comment":
3070
+ | []
3071
+ | [
3072
+ {
3073
+ /**
3074
+ * A minimum character length for descriptions when `allow-with-description` is enabled. Defaults to `3`.
3075
+ */
3076
+ minimumDescriptionLength?: number;
3077
+ /**
3078
+ * Whether (and how) to allow `@ts-check` directives.
3079
+ */
3080
+ "ts-check"?:
3081
+ | boolean
3082
+ | "allow-with-description"
3083
+ | {
3084
+ /**
3085
+ * A regular expression indicating the format the directive should follow
3086
+ */
3087
+ descriptionFormat?: string;
3088
+ };
3089
+ /**
3090
+ * Whether (and how) to allow `@ts-expect-error` directives.
3091
+ */
3092
+ "ts-expect-error"?:
3093
+ | boolean
3094
+ | "allow-with-description"
3095
+ | {
3096
+ /**
3097
+ * A regular expression indicating the format the directive should follow
3098
+ */
3099
+ descriptionFormat?: string;
3100
+ };
3101
+ /**
3102
+ * Whether (and how) to allow `@ts-ignore` directives.
3103
+ */
3104
+ "ts-ignore"?:
3105
+ | boolean
3106
+ | "allow-with-description"
3107
+ | {
3108
+ /**
3109
+ * A regular expression indicating the format the directive should follow
3110
+ */
3111
+ descriptionFormat?: string;
3112
+ };
3113
+ /**
3114
+ * Whether (and how) to allow `@ts-nocheck` directives.
3115
+ */
3116
+ "ts-nocheck"?:
3117
+ | boolean
3118
+ | "allow-with-description"
3119
+ | {
3120
+ /**
3121
+ * A regular expression indicating the format the directive should follow
3122
+ */
3123
+ descriptionFormat?: string;
3124
+ };
3125
+ }
3126
+ ];
3127
+
3068
3128
  /** Prefers either function properties or method signatures */
3069
3129
  "jsdoc/ts-method-signature-style":
3070
3130
  | []
package/package.json CHANGED
@@ -157,5 +157,5 @@
157
157
  "test-cov": "TIMING=1 c8 --reporter text pnpm run test-no-cov",
158
158
  "test-index": "pnpm run test-no-cov test/rules/index.js"
159
159
  },
160
- "version": "64.4.0"
160
+ "version": "64.5.0"
161
161
  }
package/src/index.js CHANGED
@@ -69,6 +69,7 @@ import requireYieldsCheck from './rules/requireYieldsCheck.js';
69
69
  import sortTags from './rules/sortTags.js';
70
70
  import tagLines from './rules/tagLines.js';
71
71
  import textEscaping from './rules/textEscaping.js';
72
+ import tsBanTsComment from './rules/tsBanTsComment.js';
72
73
  import tsMethodSignatureStyle from './rules/tsMethodSignatureStyle.js';
73
74
  import tsNoEmptyObjectType from './rules/tsNoEmptyObjectType.js';
74
75
  import tsNoUnnecessaryTemplateExpression from './rules/tsNoUnnecessaryTemplateExpression.js';
@@ -270,6 +271,7 @@ index.rules = {
270
271
  'sort-tags': sortTags,
271
272
  'tag-lines': tagLines,
272
273
  'text-escaping': textEscaping,
274
+ 'ts-ban-ts-comment': tsBanTsComment,
273
275
  'ts-method-signature-style': tsMethodSignatureStyle,
274
276
  'ts-no-empty-object-type': tsNoEmptyObjectType,
275
277
  'ts-no-unnecessary-template-expression': tsNoUnnecessaryTemplateExpression,
@@ -367,6 +369,7 @@ const createRecommendedRuleset = (warnOrError, flatName) => {
367
369
  'jsdoc/sort-tags': 'off',
368
370
  'jsdoc/tag-lines': warnOrError,
369
371
  'jsdoc/text-escaping': 'off',
372
+ 'jsdoc/ts-ban-ts-comment': 'off',
370
373
  'jsdoc/ts-method-signature-style': 'off',
371
374
  'jsdoc/ts-no-empty-object-type': warnOrError,
372
375
  'jsdoc/ts-no-unnecessary-template-expression': 'off',
@@ -440,6 +443,7 @@ const createRecommendedTypeScriptFlavorRuleset = (warnOrError, flatName) => {
440
443
  ...ruleset.rules,
441
444
  /* eslint-disable @stylistic/indent -- Extra indent to avoid use by auto-rule-editing */
442
445
  'jsdoc/no-undefined-types': 'off',
446
+ 'jsdoc/ts-ban-ts-comment': warnOrError,
443
447
  /* eslint-enable @stylistic/indent */
444
448
  },
445
449
  };
@@ -0,0 +1,295 @@
1
+ import iterateJsdoc from '../iterateJsdoc.js';
2
+
3
+ /**
4
+ * @typedef {boolean|'allow-with-description'|{descriptionFormat: string}} DirectiveConfig
5
+ */
6
+
7
+ /**
8
+ * @typedef {{
9
+ * minimumDescriptionLength?: number,
10
+ * 'ts-check'?: DirectiveConfig,
11
+ * 'ts-expect-error'?: DirectiveConfig,
12
+ * 'ts-ignore'?: DirectiveConfig,
13
+ * 'ts-nocheck'?: DirectiveConfig,
14
+ * }} OptionsShape
15
+ */
16
+
17
+ const defaultMinimumDescriptionLength = 3;
18
+
19
+ // https://github.com/microsoft/TypeScript/blob/main/src/compiler/parser.ts
20
+ const singleLinePragmaRegExp =
21
+ /^\/\/\/?\s*@ts-(?<directive>check|nocheck)(?<description>.*)$/v;
22
+
23
+ // https://github.com/microsoft/TypeScript/blob/main/src/compiler/scanner.ts
24
+ const commentDirectiveRegExpSingleLine =
25
+ /^\/*\s*@ts-(?<directive>expect-error|ignore)(?<description>.*)/v;
26
+ const commentDirectiveRegExpMultiLine =
27
+ /^\s*(?:\/|\*)*\s*@ts-(?<directive>expect-error|ignore)(?<description>.*)/v;
28
+
29
+ const lineBreakRegExp = /\r\n|\r|\n/v;
30
+
31
+ /**
32
+ * @param {RegExp} regExp
33
+ * @param {string} str
34
+ * @returns {{description: string, directive: string}|null}
35
+ */
36
+ const execDirectiveRegExp = (regExp, str) => {
37
+ const match = regExp.exec(str);
38
+ if (!match?.groups) {
39
+ return null;
40
+ }
41
+
42
+ return {
43
+ description: /** @type {string} */ (match.groups.description),
44
+ directive: /** @type {string} */ (match.groups.directive),
45
+ };
46
+ };
47
+
48
+ /**
49
+ * @param {import('estree').Comment} comment
50
+ * @returns {{description: string, directive: string}|null}
51
+ */
52
+ const findDirectiveInComment = (comment) => {
53
+ if (comment.type === 'Line') {
54
+ const matchedPragma = execDirectiveRegExp(singleLinePragmaRegExp, `//${comment.value}`);
55
+ if (matchedPragma) {
56
+ return matchedPragma;
57
+ }
58
+
59
+ return execDirectiveRegExp(commentDirectiveRegExpSingleLine, comment.value);
60
+ }
61
+
62
+ const commentLines = comment.value.split(lineBreakRegExp);
63
+
64
+ return execDirectiveRegExp(
65
+ commentDirectiveRegExpMultiLine,
66
+ /** @type {string} */ (commentLines.at(-1)),
67
+ );
68
+ };
69
+
70
+ export default iterateJsdoc(({
71
+ allComments,
72
+ context,
73
+ makeReport,
74
+ sourceCode,
75
+ }) => {
76
+ const [
77
+ {
78
+ minimumDescriptionLength = defaultMinimumDescriptionLength,
79
+ 'ts-check': tsCheck = false,
80
+ 'ts-expect-error': tsExpectError = 'allow-with-description',
81
+ 'ts-ignore': tsIgnore = true,
82
+ 'ts-nocheck': tsNoCheck = true,
83
+ } = /** @type {OptionsShape} */ ({}),
84
+ ] = /** @type {[OptionsShape]} */ (context.options);
85
+
86
+ /** @type {{[key: string]: DirectiveConfig}} */
87
+ const directiveOptions = {
88
+ 'ts-check': tsCheck,
89
+ 'ts-expect-error': tsExpectError,
90
+ 'ts-ignore': tsIgnore,
91
+ 'ts-nocheck': tsNoCheck,
92
+ };
93
+
94
+ const firstStatement = sourceCode.ast.body.at(0);
95
+
96
+ for (const comment of /** @type {import('estree').Comment[]} */ (
97
+ /** @type {unknown} */ (allComments)
98
+ )) {
99
+ const match = findDirectiveInComment(comment);
100
+ if (!match) {
101
+ continue;
102
+ }
103
+
104
+ const {
105
+ description,
106
+ directive,
107
+ } = match;
108
+
109
+ if (directive === 'nocheck' && firstStatement) {
110
+ const firstStatementLine = /** @type {import('eslint').AST.SourceLocation} */ (
111
+ firstStatement.loc
112
+ ).start.line;
113
+ const commentLine = /** @type {import('eslint').AST.SourceLocation} */ (
114
+ comment.loc
115
+ ).start.line;
116
+ if (firstStatementLine <= commentLine) {
117
+ continue;
118
+ }
119
+ }
120
+
121
+ const option = directiveOptions[`ts-${directive}`];
122
+
123
+ const report = /** @type {import('../iterateJsdoc.js').MakeReport} */ (
124
+ makeReport
125
+ )(context, /** @type {import('estree').Node} */ (/** @type {unknown} */ (comment)));
126
+
127
+ if (option === true) {
128
+ if (directive === 'ignore') {
129
+ report(
130
+ 'Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free.',
131
+ null,
132
+ null,
133
+ undefined,
134
+ [
135
+ {
136
+ desc: 'Replace "@ts-ignore" with "@ts-expect-error".',
137
+ fix (fixer) {
138
+ const commentText = comment.value.replace('@ts-ignore', '@ts-expect-error');
139
+
140
+ return fixer.replaceText(
141
+ /** @type {import('estree').Node & {range: [number, number]}} */ (
142
+ /** @type {unknown} */ (comment)
143
+ ),
144
+ comment.type === 'Line' ? `//${commentText}` : `/*${commentText}*/`,
145
+ );
146
+ },
147
+ },
148
+ ],
149
+ );
150
+ } else {
151
+ report(`Do not use "@ts-${directive}" because it alters compilation errors.`);
152
+ }
153
+ } else if (
154
+ option === 'allow-with-description' ||
155
+ (typeof option === 'object' && option.descriptionFormat)
156
+ ) {
157
+ const trimmedDescription = description.trim();
158
+ if (trimmedDescription.length < minimumDescriptionLength) {
159
+ report(
160
+ `Include a description after the "@ts-${directive}" directive to explain why the @ts-${directive} is necessary. The description must be ${minimumDescriptionLength} characters or longer.`,
161
+ );
162
+ } else if (
163
+ typeof option === 'object' &&
164
+ option.descriptionFormat &&
165
+ !new RegExp(option.descriptionFormat, 'v').test(description)
166
+ ) {
167
+ report(
168
+ `The description for the "@ts-${directive}" directive must match the ${option.descriptionFormat} format.`,
169
+ );
170
+ }
171
+ }
172
+ }
173
+ }, {
174
+ checkFile: true,
175
+ meta: {
176
+ docs: {
177
+ description: 'Disallows (or requires descriptions for) `@ts-<directive>` comments, mirroring `@typescript-eslint/ban-ts-comment`.',
178
+ url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/ts-ban-ts-comment.md#repos-sticky-header',
179
+ },
180
+ hasSuggestions: true,
181
+ schema: [
182
+ {
183
+ additionalProperties: false,
184
+ properties: {
185
+ minimumDescriptionLength: {
186
+ description: 'A minimum character length for descriptions when `allow-with-description` is enabled. Defaults to `3`.',
187
+ type: 'integer',
188
+ },
189
+ 'ts-check': {
190
+ description: 'Whether (and how) to allow `@ts-check` directives.',
191
+ oneOf: [
192
+ {
193
+ type: 'boolean',
194
+ },
195
+ {
196
+ description: 'Whether to allow the directive if a description is present',
197
+ enum: [
198
+ 'allow-with-description',
199
+ ],
200
+ type: 'string',
201
+ },
202
+ {
203
+ additionalProperties: false,
204
+ properties: {
205
+ descriptionFormat: {
206
+ description: 'A regular expression indicating the format the directive should follow',
207
+ type: 'string',
208
+ },
209
+ },
210
+ type: 'object',
211
+ },
212
+ ],
213
+ },
214
+ 'ts-expect-error': {
215
+ description: 'Whether (and how) to allow `@ts-expect-error` directives.',
216
+ oneOf: [
217
+ {
218
+ type: 'boolean',
219
+ },
220
+ {
221
+ description: 'Whether to allow the directive if a description is present',
222
+ enum: [
223
+ 'allow-with-description',
224
+ ],
225
+ type: 'string',
226
+ },
227
+ {
228
+ additionalProperties: false,
229
+ properties: {
230
+ descriptionFormat: {
231
+ description: 'A regular expression indicating the format the directive should follow',
232
+ type: 'string',
233
+ },
234
+ },
235
+ type: 'object',
236
+ },
237
+ ],
238
+ },
239
+ 'ts-ignore': {
240
+ description: 'Whether (and how) to allow `@ts-ignore` directives.',
241
+ oneOf: [
242
+ {
243
+ type: 'boolean',
244
+ },
245
+ {
246
+ description: 'Whether to allow the directive if a description is present',
247
+ enum: [
248
+ 'allow-with-description',
249
+ ],
250
+ type: 'string',
251
+ },
252
+ {
253
+ additionalProperties: false,
254
+ properties: {
255
+ descriptionFormat: {
256
+ description: 'A regular expression indicating the format the directive should follow',
257
+ type: 'string',
258
+ },
259
+ },
260
+ type: 'object',
261
+ },
262
+ ],
263
+ },
264
+ 'ts-nocheck': {
265
+ description: 'Whether (and how) to allow `@ts-nocheck` directives.',
266
+ oneOf: [
267
+ {
268
+ type: 'boolean',
269
+ },
270
+ {
271
+ description: 'Whether to allow the directive if a description is present',
272
+ enum: [
273
+ 'allow-with-description',
274
+ ],
275
+ type: 'string',
276
+ },
277
+ {
278
+ additionalProperties: false,
279
+ properties: {
280
+ descriptionFormat: {
281
+ description: 'A regular expression indicating the format the directive should follow',
282
+ type: 'string',
283
+ },
284
+ },
285
+ type: 'object',
286
+ },
287
+ ],
288
+ },
289
+ },
290
+ type: 'object',
291
+ },
292
+ ],
293
+ type: 'suggestion',
294
+ },
295
+ });
package/src/rules.d.ts CHANGED
@@ -3065,6 +3065,66 @@ export interface Rules {
3065
3065
  }
3066
3066
  ];
3067
3067
 
3068
+ /** Disallows (or requires descriptions for) `@ts-<directive>` comments, mirroring `@typescript-eslint/ban-ts-comment`. */
3069
+ "jsdoc/ts-ban-ts-comment":
3070
+ | []
3071
+ | [
3072
+ {
3073
+ /**
3074
+ * A minimum character length for descriptions when `allow-with-description` is enabled. Defaults to `3`.
3075
+ */
3076
+ minimumDescriptionLength?: number;
3077
+ /**
3078
+ * Whether (and how) to allow `@ts-check` directives.
3079
+ */
3080
+ "ts-check"?:
3081
+ | boolean
3082
+ | "allow-with-description"
3083
+ | {
3084
+ /**
3085
+ * A regular expression indicating the format the directive should follow
3086
+ */
3087
+ descriptionFormat?: string;
3088
+ };
3089
+ /**
3090
+ * Whether (and how) to allow `@ts-expect-error` directives.
3091
+ */
3092
+ "ts-expect-error"?:
3093
+ | boolean
3094
+ | "allow-with-description"
3095
+ | {
3096
+ /**
3097
+ * A regular expression indicating the format the directive should follow
3098
+ */
3099
+ descriptionFormat?: string;
3100
+ };
3101
+ /**
3102
+ * Whether (and how) to allow `@ts-ignore` directives.
3103
+ */
3104
+ "ts-ignore"?:
3105
+ | boolean
3106
+ | "allow-with-description"
3107
+ | {
3108
+ /**
3109
+ * A regular expression indicating the format the directive should follow
3110
+ */
3111
+ descriptionFormat?: string;
3112
+ };
3113
+ /**
3114
+ * Whether (and how) to allow `@ts-nocheck` directives.
3115
+ */
3116
+ "ts-nocheck"?:
3117
+ | boolean
3118
+ | "allow-with-description"
3119
+ | {
3120
+ /**
3121
+ * A regular expression indicating the format the directive should follow
3122
+ */
3123
+ descriptionFormat?: string;
3124
+ };
3125
+ }
3126
+ ];
3127
+
3068
3128
  /** Prefers either function properties or method signatures */
3069
3129
  "jsdoc/ts-method-signature-style":
3070
3130
  | []