eslint-plugin-jsdoc 48.0.0 → 48.0.2

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 +2 -2
  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
package/package.json CHANGED
@@ -84,7 +84,7 @@
84
84
  "*.js": "npm run lint-arg -- --fix"
85
85
  },
86
86
  "type": "module",
87
- "main": "./dist/index.js",
87
+ "main": "./dist/index.cjs",
88
88
  "types": "./dist/index.d.ts",
89
89
  "exports": {
90
90
  "types": "./dist/index.d.ts",
@@ -141,5 +141,5 @@
141
141
  "test-cov": "cross-env TIMING=1 c8 --reporter text npm run test-no-cov",
142
142
  "test-index": "npm run test-no-cov -- test/rules/index.js"
143
143
  },
144
- "version": "48.0.0"
144
+ "version": "48.0.2"
145
145
  }
@@ -0,0 +1,34 @@
1
+ const WarnSettings = function () {
2
+ /** @type {WeakMap<object, Set<string>>} */
3
+ const warnedSettings = new WeakMap();
4
+
5
+ return {
6
+ /**
7
+ * Warn only once for each context and setting
8
+ * @param {import('eslint').Rule.RuleContext} context
9
+ * @param {string} setting
10
+ * @returns {boolean}
11
+ */
12
+ hasBeenWarned (context, setting) {
13
+ return warnedSettings.has(context) && /** @type {Set<string>} */ (
14
+ warnedSettings.get(context)
15
+ ).has(setting);
16
+ },
17
+
18
+ /**
19
+ * @param {import('eslint').Rule.RuleContext} context
20
+ * @param {string} setting
21
+ * @returns {void}
22
+ */
23
+ markSettingAsWarned (context, setting) {
24
+ // c8 ignore else
25
+ if (!warnedSettings.has(context)) {
26
+ warnedSettings.set(context, new Set());
27
+ }
28
+
29
+ /** @type {Set<string>} */ (warnedSettings.get(context)).add(setting);
30
+ },
31
+ };
32
+ };
33
+
34
+ export default WarnSettings;
@@ -0,0 +1,356 @@
1
+ /**
2
+ * Transform based on https://github.com/syavorsky/comment-parser/blob/master/src/transforms/align.ts
3
+ *
4
+ * It contains some customizations to align based on the tags, and some custom options.
5
+ */
6
+
7
+ import {
8
+ // `comment-parser/primitives` export
9
+ util,
10
+ } from 'comment-parser';
11
+
12
+ /**
13
+ * @typedef {{
14
+ * hasNoTypes: boolean,
15
+ * maxNamedTagLength: import('./iterateJsdoc.js').Integer,
16
+ * maxUnnamedTagLength: import('./iterateJsdoc.js').Integer
17
+ * }} TypelessInfo
18
+ */
19
+
20
+ const {
21
+ rewireSource,
22
+ } = util;
23
+
24
+ /**
25
+ * @typedef {{
26
+ * name: import('./iterateJsdoc.js').Integer,
27
+ * start: import('./iterateJsdoc.js').Integer,
28
+ * tag: import('./iterateJsdoc.js').Integer,
29
+ * type: import('./iterateJsdoc.js').Integer
30
+ * }} Width
31
+ */
32
+
33
+ /** @type {Width} */
34
+ const zeroWidth = {
35
+ name: 0,
36
+ start: 0,
37
+ tag: 0,
38
+ type: 0,
39
+ };
40
+
41
+ /**
42
+ * @param {string[]} tags
43
+ * @param {import('./iterateJsdoc.js').Integer} index
44
+ * @param {import('comment-parser').Line[]} source
45
+ * @returns {boolean}
46
+ */
47
+ const shouldAlign = (tags, index, source) => {
48
+ const tag = source[index].tokens.tag.replace('@', '');
49
+ const includesTag = tags.includes(tag);
50
+
51
+ if (includesTag) {
52
+ return true;
53
+ }
54
+
55
+ if (tag !== '') {
56
+ return false;
57
+ }
58
+
59
+ for (let iterator = index; iterator >= 0; iterator--) {
60
+ const previousTag = source[iterator].tokens.tag.replace('@', '');
61
+
62
+ if (previousTag !== '') {
63
+ if (tags.includes(previousTag)) {
64
+ return true;
65
+ }
66
+
67
+ return false;
68
+ }
69
+ }
70
+
71
+ return true;
72
+ };
73
+
74
+ /**
75
+ * @param {string[]} tags
76
+ * @returns {(
77
+ * width: Width,
78
+ * line: {
79
+ * tokens: import('comment-parser').Tokens
80
+ * },
81
+ * index: import('./iterateJsdoc.js').Integer,
82
+ * source: import('comment-parser').Line[]
83
+ * ) => Width}
84
+ */
85
+ const getWidth = (tags) => {
86
+ return (width, {
87
+ tokens,
88
+ }, index, source) => {
89
+ if (!shouldAlign(tags, index, source)) {
90
+ return width;
91
+ }
92
+
93
+ return {
94
+ name: Math.max(width.name, tokens.name.length),
95
+ start: tokens.delimiter === '/**' ? tokens.start.length : width.start,
96
+ tag: Math.max(width.tag, tokens.tag.length),
97
+ type: Math.max(width.type, tokens.type.length),
98
+ };
99
+ };
100
+ };
101
+
102
+ /**
103
+ * @param {{
104
+ * description: string;
105
+ * tags: import('comment-parser').Spec[];
106
+ * problems: import('comment-parser').Problem[];
107
+ * }} fields
108
+ * @returns {TypelessInfo}
109
+ */
110
+ const getTypelessInfo = (fields) => {
111
+ const hasNoTypes = fields.tags.every(({
112
+ type,
113
+ }) => {
114
+ return !type;
115
+ });
116
+ const maxNamedTagLength = Math.max(...fields.tags.map(({
117
+ tag,
118
+ name,
119
+ }) => {
120
+ return name.length === 0 ? -1 : tag.length;
121
+ }).filter((length) => {
122
+ return length !== -1;
123
+ })) + 1;
124
+ const maxUnnamedTagLength = Math.max(...fields.tags.map(({
125
+ tag,
126
+ name,
127
+ }) => {
128
+ return name.length === 0 ? tag.length : -1;
129
+ }).filter((length) => {
130
+ return length !== -1;
131
+ })) + 1;
132
+ return {
133
+ hasNoTypes,
134
+ maxNamedTagLength,
135
+ maxUnnamedTagLength,
136
+ };
137
+ };
138
+
139
+ /**
140
+ * @param {import('./iterateJsdoc.js').Integer} len
141
+ * @returns {string}
142
+ */
143
+ const space = (len) => {
144
+ return ''.padStart(len, ' ');
145
+ };
146
+
147
+ /**
148
+ * @param {{
149
+ * customSpacings: import('../src/rules/checkLineAlignment.js').CustomSpacings,
150
+ * tags: string[],
151
+ * indent: string,
152
+ * preserveMainDescriptionPostDelimiter: boolean,
153
+ * wrapIndent: string,
154
+ * }} cfg
155
+ * @returns {(
156
+ * block: import('comment-parser').Block
157
+ * ) => import('comment-parser').Block}
158
+ */
159
+ const alignTransform = ({
160
+ customSpacings,
161
+ tags,
162
+ indent,
163
+ preserveMainDescriptionPostDelimiter,
164
+ wrapIndent,
165
+ }) => {
166
+ let intoTags = false;
167
+ /** @type {Width} */
168
+ let width;
169
+
170
+ /**
171
+ * @param {import('comment-parser').Tokens} tokens
172
+ * @param {TypelessInfo} typelessInfo
173
+ * @returns {import('comment-parser').Tokens}
174
+ */
175
+ const alignTokens = (tokens, typelessInfo) => {
176
+ const nothingAfter = {
177
+ delim: false,
178
+ name: false,
179
+ tag: false,
180
+ type: false,
181
+ };
182
+
183
+ if (tokens.description === '') {
184
+ nothingAfter.name = true;
185
+ tokens.postName = '';
186
+
187
+ if (tokens.name === '') {
188
+ nothingAfter.type = true;
189
+ tokens.postType = '';
190
+
191
+ if (tokens.type === '') {
192
+ nothingAfter.tag = true;
193
+ tokens.postTag = '';
194
+
195
+ /* c8 ignore next: Never happens because the !intoTags return. But it's here for consistency with the original align transform */
196
+ if (tokens.tag === '') {
197
+ nothingAfter.delim = true;
198
+ }
199
+ }
200
+ }
201
+ }
202
+
203
+ let untypedNameAdjustment = 0;
204
+ let untypedTypeAdjustment = 0;
205
+ if (typelessInfo.hasNoTypes) {
206
+ nothingAfter.tag = true;
207
+ tokens.postTag = '';
208
+ if (tokens.name === '') {
209
+ untypedNameAdjustment = typelessInfo.maxNamedTagLength - tokens.tag.length;
210
+ } else {
211
+ untypedNameAdjustment = typelessInfo.maxNamedTagLength > typelessInfo.maxUnnamedTagLength ? 0 :
212
+ Math.max(0, typelessInfo.maxUnnamedTagLength - (tokens.tag.length + tokens.name.length + 1));
213
+ untypedTypeAdjustment = typelessInfo.maxNamedTagLength - tokens.tag.length;
214
+ }
215
+ }
216
+
217
+ // Todo: Avoid fixing alignment of blocks with multiline wrapping of type
218
+ if (tokens.tag === '' && tokens.type) {
219
+ return tokens;
220
+ }
221
+
222
+ const spacings = {
223
+ postDelimiter: customSpacings?.postDelimiter || 1,
224
+ postName: customSpacings?.postName || 1,
225
+ postTag: customSpacings?.postTag || 1,
226
+ postType: customSpacings?.postType || 1,
227
+ };
228
+
229
+ tokens.postDelimiter = nothingAfter.delim ? '' : space(spacings.postDelimiter);
230
+
231
+ if (!nothingAfter.tag) {
232
+ tokens.postTag = space(width.tag - tokens.tag.length + spacings.postTag);
233
+ }
234
+
235
+ if (!nothingAfter.type) {
236
+ tokens.postType = space(width.type - tokens.type.length + spacings.postType + untypedTypeAdjustment);
237
+ }
238
+
239
+ if (!nothingAfter.name) {
240
+ // If post name is empty for all lines (name width 0), don't add post name spacing.
241
+ tokens.postName = width.name === 0 ? '' : space(width.name - tokens.name.length + spacings.postName + untypedNameAdjustment);
242
+ }
243
+
244
+ return tokens;
245
+ };
246
+
247
+ /**
248
+ * @param {import('comment-parser').Line} line
249
+ * @param {import('./iterateJsdoc.js').Integer} index
250
+ * @param {import('comment-parser').Line[]} source
251
+ * @param {TypelessInfo} typelessInfo
252
+ * @param {string|false} indentTag
253
+ * @returns {import('comment-parser').Line}
254
+ */
255
+ const update = (line, index, source, typelessInfo, indentTag) => {
256
+ /** @type {import('comment-parser').Tokens} */
257
+ const tokens = {
258
+ ...line.tokens,
259
+ };
260
+
261
+ if (tokens.tag !== '') {
262
+ intoTags = true;
263
+ }
264
+
265
+ const isEmpty =
266
+ tokens.tag === '' &&
267
+ tokens.name === '' &&
268
+ tokens.type === '' &&
269
+ tokens.description === '';
270
+
271
+ // dangling '*/'
272
+ if (tokens.end === '*/' && isEmpty) {
273
+ tokens.start = indent + ' ';
274
+
275
+ return {
276
+ ...line,
277
+ tokens,
278
+ };
279
+ }
280
+
281
+ switch (tokens.delimiter) {
282
+ case '/**':
283
+ tokens.start = indent;
284
+ break;
285
+ case '*':
286
+ tokens.start = indent + ' ';
287
+ break;
288
+ default:
289
+ tokens.delimiter = '';
290
+
291
+ // compensate delimiter
292
+ tokens.start = indent + ' ';
293
+ }
294
+
295
+ if (!intoTags) {
296
+ if (tokens.description === '') {
297
+ tokens.postDelimiter = '';
298
+ } else if (!preserveMainDescriptionPostDelimiter) {
299
+ tokens.postDelimiter = ' ';
300
+ }
301
+
302
+ return {
303
+ ...line,
304
+ tokens,
305
+ };
306
+ }
307
+
308
+ const postHyphenSpacing = customSpacings?.postHyphen ?? 1;
309
+ const hyphenSpacing = /^\s*-\s+/u;
310
+ tokens.description = tokens.description.replace(
311
+ hyphenSpacing, '-' + ''.padStart(postHyphenSpacing, ' '),
312
+ );
313
+
314
+ // Not align.
315
+ if (shouldAlign(tags, index, source)) {
316
+ alignTokens(tokens, typelessInfo);
317
+ if (indentTag) {
318
+ tokens.postDelimiter += wrapIndent;
319
+ }
320
+ }
321
+
322
+ return {
323
+ ...line,
324
+ tokens,
325
+ };
326
+ };
327
+
328
+ return ({
329
+ source,
330
+ ...fields
331
+ }) => {
332
+ width = source.reduce(getWidth(tags), {
333
+ ...zeroWidth,
334
+ });
335
+
336
+ const typelessInfo = getTypelessInfo(fields);
337
+
338
+ let tagIndentMode = false;
339
+
340
+ return rewireSource({
341
+ ...fields,
342
+ source: source.map((line, index) => {
343
+ const indentTag = tagIndentMode && !line.tokens.tag && line.tokens.description;
344
+ const ret = update(line, index, source, typelessInfo, indentTag);
345
+
346
+ if (line.tokens.tag) {
347
+ tagIndentMode = true;
348
+ }
349
+
350
+ return ret;
351
+ }),
352
+ });
353
+ };
354
+ };
355
+
356
+ export default alignTransform;
@@ -0,0 +1,168 @@
1
+ const defaultTagOrder = [
2
+ {
3
+ tags: [
4
+ // Brief descriptions
5
+ 'summary',
6
+ 'typeSummary',
7
+
8
+ // Module/file-level
9
+ 'module',
10
+ 'exports',
11
+ 'file',
12
+ 'fileoverview',
13
+ 'overview',
14
+
15
+ // Identifying (name, type)
16
+ 'typedef',
17
+ 'interface',
18
+ 'record',
19
+ 'template',
20
+ 'name',
21
+ 'kind',
22
+ 'type',
23
+ 'alias',
24
+ 'external',
25
+ 'host',
26
+ 'callback',
27
+ 'func',
28
+ 'function',
29
+ 'method',
30
+ 'class',
31
+ 'constructor',
32
+
33
+ // Relationships
34
+ 'modifies',
35
+ 'mixes',
36
+ 'mixin',
37
+ 'mixinClass',
38
+ 'mixinFunction',
39
+ 'namespace',
40
+ 'borrows',
41
+ 'constructs',
42
+ 'lends',
43
+ 'implements',
44
+ 'requires',
45
+
46
+ // Long descriptions
47
+ 'desc',
48
+ 'description',
49
+ 'classdesc',
50
+ 'tutorial',
51
+ 'copyright',
52
+ 'license',
53
+
54
+ // Simple annotations
55
+
56
+ // TypeScript
57
+ 'internal',
58
+ 'overload',
59
+
60
+ 'const',
61
+ 'constant',
62
+ 'final',
63
+ 'global',
64
+ 'readonly',
65
+ 'abstract',
66
+ 'virtual',
67
+ 'var',
68
+ 'member',
69
+ 'memberof',
70
+ 'memberof!',
71
+ 'inner',
72
+ 'instance',
73
+ 'inheritdoc',
74
+ 'inheritDoc',
75
+ 'override',
76
+ 'hideconstructor',
77
+
78
+ // Core function/object info
79
+ 'param',
80
+ 'arg',
81
+ 'argument',
82
+ 'prop',
83
+ 'property',
84
+ 'return',
85
+ 'returns',
86
+
87
+ // Important behavior details
88
+ 'async',
89
+ 'generator',
90
+ 'default',
91
+ 'defaultvalue',
92
+ 'enum',
93
+ 'augments',
94
+ 'extends',
95
+ 'throws',
96
+ 'exception',
97
+ 'yield',
98
+ 'yields',
99
+ 'event',
100
+ 'fires',
101
+ 'emits',
102
+ 'listens',
103
+ 'this',
104
+
105
+ // TypeScript
106
+ 'satisfies',
107
+
108
+ // Access
109
+ 'static',
110
+ 'private',
111
+ 'protected',
112
+ 'public',
113
+ 'access',
114
+ 'package',
115
+
116
+ '-other',
117
+
118
+ // Supplementary descriptions
119
+ 'see',
120
+ 'example',
121
+
122
+ // METADATA
123
+
124
+ // Other Closure (undocumented) metadata
125
+ 'closurePrimitive',
126
+ 'customElement',
127
+ 'expose',
128
+ 'hidden',
129
+ 'idGenerator',
130
+ 'meaning',
131
+ 'ngInject',
132
+ 'owner',
133
+ 'wizaction',
134
+
135
+ // Other Closure (documented) metadata
136
+ 'define',
137
+ 'dict',
138
+ 'export',
139
+ 'externs',
140
+ 'implicitCast',
141
+ 'noalias',
142
+ 'nocollapse',
143
+ 'nocompile',
144
+ 'noinline',
145
+ 'nosideeffects',
146
+ 'polymer',
147
+ 'polymerBehavior',
148
+ 'preserve',
149
+ 'struct',
150
+ 'suppress',
151
+ 'unrestricted',
152
+
153
+ // @homer0/prettier-plugin-jsdoc metadata
154
+ 'category',
155
+
156
+ // Non-Closure metadata
157
+ 'ignore',
158
+ 'author',
159
+ 'version',
160
+ 'variation',
161
+ 'since',
162
+ 'deprecated',
163
+ 'todo',
164
+ ],
165
+ },
166
+ ];
167
+
168
+ export default defaultTagOrder;