eslint-plugin-jsdoc 48.0.0 → 48.0.1

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.
Files changed (64) hide show
  1. package/package.json +1 -1
  2. package/src/WarnSettings.js +34 -0
  3. package/src/alignTransform.js +356 -0
  4. package/src/defaultTagOrder.js +168 -0
  5. package/src/exportParser.js +957 -0
  6. package/src/getDefaultTagStructureForMode.js +969 -0
  7. package/src/index.js +266 -0
  8. package/src/iterateJsdoc.js +2555 -0
  9. package/src/jsdocUtils.js +1693 -0
  10. package/src/rules/checkAccess.js +45 -0
  11. package/src/rules/checkAlignment.js +63 -0
  12. package/src/rules/checkExamples.js +594 -0
  13. package/src/rules/checkIndentation.js +75 -0
  14. package/src/rules/checkLineAlignment.js +364 -0
  15. package/src/rules/checkParamNames.js +404 -0
  16. package/src/rules/checkPropertyNames.js +152 -0
  17. package/src/rules/checkSyntax.js +30 -0
  18. package/src/rules/checkTagNames.js +314 -0
  19. package/src/rules/checkTypes.js +535 -0
  20. package/src/rules/checkValues.js +220 -0
  21. package/src/rules/emptyTags.js +88 -0
  22. package/src/rules/implementsOnClasses.js +64 -0
  23. package/src/rules/importsAsDependencies.js +131 -0
  24. package/src/rules/informativeDocs.js +182 -0
  25. package/src/rules/matchDescription.js +286 -0
  26. package/src/rules/matchName.js +147 -0
  27. package/src/rules/multilineBlocks.js +333 -0
  28. package/src/rules/noBadBlocks.js +109 -0
  29. package/src/rules/noBlankBlockDescriptions.js +69 -0
  30. package/src/rules/noBlankBlocks.js +53 -0
  31. package/src/rules/noDefaults.js +85 -0
  32. package/src/rules/noMissingSyntax.js +195 -0
  33. package/src/rules/noMultiAsterisks.js +134 -0
  34. package/src/rules/noRestrictedSyntax.js +91 -0
  35. package/src/rules/noTypes.js +73 -0
  36. package/src/rules/noUndefinedTypes.js +328 -0
  37. package/src/rules/requireAsteriskPrefix.js +189 -0
  38. package/src/rules/requireDescription.js +161 -0
  39. package/src/rules/requireDescriptionCompleteSentence.js +333 -0
  40. package/src/rules/requireExample.js +118 -0
  41. package/src/rules/requireFileOverview.js +154 -0
  42. package/src/rules/requireHyphenBeforeParamDescription.js +178 -0
  43. package/src/rules/requireJsdoc.js +629 -0
  44. package/src/rules/requireParam.js +592 -0
  45. package/src/rules/requireParamDescription.js +89 -0
  46. package/src/rules/requireParamName.js +55 -0
  47. package/src/rules/requireParamType.js +89 -0
  48. package/src/rules/requireProperty.js +48 -0
  49. package/src/rules/requirePropertyDescription.js +25 -0
  50. package/src/rules/requirePropertyName.js +25 -0
  51. package/src/rules/requirePropertyType.js +25 -0
  52. package/src/rules/requireReturns.js +238 -0
  53. package/src/rules/requireReturnsCheck.js +141 -0
  54. package/src/rules/requireReturnsDescription.js +59 -0
  55. package/src/rules/requireReturnsType.js +51 -0
  56. package/src/rules/requireThrows.js +111 -0
  57. package/src/rules/requireYields.js +216 -0
  58. package/src/rules/requireYieldsCheck.js +208 -0
  59. package/src/rules/sortTags.js +557 -0
  60. package/src/rules/tagLines.js +359 -0
  61. package/src/rules/textEscaping.js +146 -0
  62. package/src/rules/validTypes.js +368 -0
  63. package/src/tagNames.js +234 -0
  64. package/src/utils/hasReturnValue.js +549 -0
@@ -0,0 +1,314 @@
1
+ import iterateJsdoc from '../iterateJsdoc.js';
2
+ import escapeStringRegexp from 'escape-string-regexp';
3
+
4
+ // https://babeljs.io/docs/en/babel-plugin-transform-react-jsx/
5
+ const jsxTagNames = new Set([
6
+ 'jsx',
7
+ 'jsxFrag',
8
+ 'jsxImportSource',
9
+ 'jsxRuntime',
10
+ ]);
11
+
12
+ const typedTagsAlwaysUnnecessary = new Set([
13
+ 'augments',
14
+ 'callback',
15
+ 'class',
16
+ 'enum',
17
+ 'implements',
18
+ 'private',
19
+ 'property',
20
+ 'protected',
21
+ 'public',
22
+ 'readonly',
23
+ 'this',
24
+ 'type',
25
+ 'typedef',
26
+ ]);
27
+
28
+ const typedTagsNeedingName = new Set([
29
+ 'template',
30
+ ]);
31
+
32
+ const typedTagsUnnecessaryOutsideDeclare = new Set([
33
+ 'abstract',
34
+ 'access',
35
+ 'class',
36
+ 'constant',
37
+ 'constructs',
38
+ 'default',
39
+ 'enum',
40
+ 'export',
41
+ 'exports',
42
+ 'function',
43
+ 'global',
44
+ 'inherits',
45
+ 'instance',
46
+ 'interface',
47
+ 'member',
48
+ 'memberof',
49
+ 'memberOf',
50
+ 'method',
51
+ 'mixes',
52
+ 'mixin',
53
+ 'module',
54
+ 'name',
55
+ 'namespace',
56
+ 'override',
57
+ 'property',
58
+ 'requires',
59
+ 'static',
60
+ 'this',
61
+ ]);
62
+
63
+ export default iterateJsdoc(({
64
+ sourceCode,
65
+ jsdoc,
66
+ report,
67
+ utils,
68
+ context,
69
+ node,
70
+ settings,
71
+ jsdocNode,
72
+ }) => {
73
+ const
74
+ /**
75
+ * @type {{
76
+ * definedTags: string[],
77
+ * enableFixer: boolean,
78
+ * jsxTags: boolean,
79
+ * typed: boolean
80
+ }} */ {
81
+ definedTags = [],
82
+ enableFixer = true,
83
+ jsxTags,
84
+ typed,
85
+ } = context.options[0] || {};
86
+
87
+ /** @type {(string|undefined)[]} */
88
+ let definedPreferredTags = [];
89
+ const {
90
+ tagNamePreference,
91
+ structuredTags,
92
+ } = settings;
93
+ const definedStructuredTags = Object.keys(structuredTags);
94
+ const definedNonPreferredTags = Object.keys(tagNamePreference);
95
+ if (definedNonPreferredTags.length) {
96
+ definedPreferredTags = Object.values(tagNamePreference).map((preferredTag) => {
97
+ if (typeof preferredTag === 'string') {
98
+ // May become an empty string but will be filtered out below
99
+ return preferredTag;
100
+ }
101
+
102
+ if (!preferredTag) {
103
+ return undefined;
104
+ }
105
+
106
+ if (typeof preferredTag !== 'object') {
107
+ utils.reportSettings(
108
+ 'Invalid `settings.jsdoc.tagNamePreference`. Values must be falsy, a string, or an object.',
109
+ );
110
+ }
111
+
112
+ return preferredTag.replacement;
113
+ })
114
+ .filter(Boolean);
115
+ }
116
+
117
+ /**
118
+ * @param {import('eslint').Rule.Node} subNode
119
+ * @returns {boolean}
120
+ */
121
+ const isInAmbientContext = (subNode) => {
122
+ return subNode.type === 'Program' ?
123
+ context.getFilename().endsWith('.d.ts') :
124
+ Boolean(
125
+ /** @type {import('@typescript-eslint/types').TSESTree.VariableDeclaration} */ (
126
+ subNode
127
+ ).declare,
128
+ ) || isInAmbientContext(subNode.parent);
129
+ };
130
+
131
+ /**
132
+ * @param {import('comment-parser').Spec} jsdocTag
133
+ * @returns {boolean}
134
+ */
135
+ const tagIsRedundantWhenTyped = (jsdocTag) => {
136
+ if (!typedTagsUnnecessaryOutsideDeclare.has(jsdocTag.tag)) {
137
+ return false;
138
+ }
139
+
140
+ if (jsdocTag.tag === 'default') {
141
+ return false;
142
+ }
143
+
144
+ if (node === null) {
145
+ return false;
146
+ }
147
+
148
+ if (context.getFilename().endsWith('.d.ts') && [
149
+ 'Program', null, undefined,
150
+ ].includes(node?.parent?.type)) {
151
+ return false;
152
+ }
153
+
154
+ if (isInAmbientContext(/** @type {import('eslint').Rule.Node} */ (node))) {
155
+ return false;
156
+ }
157
+
158
+ return true;
159
+ };
160
+
161
+ /**
162
+ * @param {string} message
163
+ * @param {import('comment-parser').Spec} jsdocTag
164
+ * @param {import('../iterateJsdoc.js').Integer} tagIndex
165
+ * @param {Partial<import('comment-parser').Tokens>} [additionalTagChanges]
166
+ * @returns {void}
167
+ */
168
+ const reportWithTagRemovalFixer = (message, jsdocTag, tagIndex, additionalTagChanges) => {
169
+ utils.reportJSDoc(message, jsdocTag, enableFixer ? () => {
170
+ if (jsdocTag.description.trim()) {
171
+ utils.changeTag(jsdocTag, {
172
+ postType: '',
173
+ type: '',
174
+ ...additionalTagChanges,
175
+ });
176
+ } else {
177
+ utils.removeTag(tagIndex, {
178
+ removeEmptyBlock: true,
179
+ });
180
+ }
181
+ } : null, true);
182
+ };
183
+
184
+ /**
185
+ * @param {import('comment-parser').Spec} jsdocTag
186
+ * @param {import('../iterateJsdoc.js').Integer} tagIndex
187
+ * @returns {boolean}
188
+ */
189
+ const checkTagForTypedValidity = (jsdocTag, tagIndex) => {
190
+ if (typedTagsAlwaysUnnecessary.has(jsdocTag.tag)) {
191
+ reportWithTagRemovalFixer(
192
+ `'@${jsdocTag.tag}' is redundant when using a type system.`,
193
+ jsdocTag,
194
+ tagIndex,
195
+ {
196
+ postTag: '',
197
+ tag: '',
198
+ },
199
+ );
200
+ return true;
201
+ }
202
+
203
+ if (tagIsRedundantWhenTyped(jsdocTag)) {
204
+ reportWithTagRemovalFixer(
205
+ `'@${jsdocTag.tag}' is redundant outside of ambient (\`declare\`/\`.d.ts\`) contexts when using a type system.`,
206
+ jsdocTag,
207
+ tagIndex,
208
+ );
209
+ return true;
210
+ }
211
+
212
+ if (typedTagsNeedingName.has(jsdocTag.tag) && !jsdocTag.name) {
213
+ reportWithTagRemovalFixer(
214
+ `'@${jsdocTag.tag}' without a name is redundant when using a type system.`,
215
+ jsdocTag,
216
+ tagIndex,
217
+ );
218
+ return true;
219
+ }
220
+
221
+ return false;
222
+ };
223
+
224
+ for (let tagIndex = 0; tagIndex < jsdoc.tags.length; tagIndex += 1) {
225
+ const jsdocTag = jsdoc.tags[tagIndex];
226
+ const tagName = jsdocTag.tag;
227
+ if (jsxTags && jsxTagNames.has(tagName)) {
228
+ continue;
229
+ }
230
+
231
+ if (typed && checkTagForTypedValidity(jsdocTag, tagIndex)) {
232
+ continue;
233
+ }
234
+
235
+ const validTags = [
236
+ ...definedTags,
237
+ ...(/** @type {string[]} */ (definedPreferredTags)),
238
+ ...definedNonPreferredTags,
239
+ ...definedStructuredTags,
240
+ ...typed ? typedTagsNeedingName : [],
241
+ ];
242
+
243
+ if (utils.isValidTag(tagName, validTags)) {
244
+ let preferredTagName = utils.getPreferredTagName({
245
+ allowObjectReturn: true,
246
+ defaultMessage: `Blacklisted tag found (\`@${tagName}\`)`,
247
+ tagName,
248
+ });
249
+ if (!preferredTagName) {
250
+ continue;
251
+ }
252
+
253
+ let message;
254
+ if (typeof preferredTagName === 'object') {
255
+ ({
256
+ message,
257
+ replacement: preferredTagName,
258
+ } = /** @type {{message: string; replacement?: string | undefined;}} */ (
259
+ preferredTagName
260
+ ));
261
+ }
262
+
263
+ if (!message) {
264
+ message = `Invalid JSDoc tag (preference). Replace "${tagName}" JSDoc tag with "${preferredTagName}".`;
265
+ }
266
+
267
+ if (preferredTagName !== tagName) {
268
+ report(message, (fixer) => {
269
+ const replacement = sourceCode.getText(jsdocNode).replace(
270
+ new RegExp(`@${escapeStringRegexp(tagName)}\\b`, 'u'),
271
+ `@${preferredTagName}`,
272
+ );
273
+
274
+ return fixer.replaceText(jsdocNode, replacement);
275
+ }, jsdocTag);
276
+ }
277
+ } else {
278
+ report(`Invalid JSDoc tag name "${tagName}".`, null, jsdocTag);
279
+ }
280
+ }
281
+ }, {
282
+ iterateAllJsdocs: true,
283
+ meta: {
284
+ docs: {
285
+ description: 'Reports invalid block tag names.',
286
+ url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-tag-names.md#repos-sticky-header',
287
+ },
288
+ fixable: 'code',
289
+ schema: [
290
+ {
291
+ additionalProperties: false,
292
+ properties: {
293
+ definedTags: {
294
+ items: {
295
+ type: 'string',
296
+ },
297
+ type: 'array',
298
+ },
299
+ enableFixer: {
300
+ type: 'boolean',
301
+ },
302
+ jsxTags: {
303
+ type: 'boolean',
304
+ },
305
+ typed: {
306
+ type: 'boolean',
307
+ },
308
+ },
309
+ type: 'object',
310
+ },
311
+ ],
312
+ type: 'suggestion',
313
+ },
314
+ });