eslint-plugin-jsdoc 61.2.1 → 61.4.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.
@@ -0,0 +1,242 @@
1
+ import iterateJsdoc from '../iterateJsdoc.js';
2
+
3
+ /**
4
+ * Checks if a node or its children contain Promise rejection patterns
5
+ * @param {import('eslint').Rule.Node} node
6
+ * @param {boolean} [innerFunction]
7
+ * @returns {boolean}
8
+ */
9
+ // eslint-disable-next-line complexity -- Temporary
10
+ const hasRejectValue = (node, innerFunction) => {
11
+ if (!node) {
12
+ return false;
13
+ }
14
+
15
+ switch (node.type) {
16
+ case 'ArrowFunctionExpression':
17
+ case 'FunctionDeclaration':
18
+ case 'FunctionExpression': {
19
+ // For inner functions in async contexts, check if they throw
20
+ // (they could be called and cause rejection)
21
+ if (innerFunction) {
22
+ // Check the inner function's body for throw statements
23
+ return hasRejectValue(/** @type {import('eslint').Rule.Node} */ (node.body), false);
24
+ }
25
+
26
+ return hasRejectValue(/** @type {import('eslint').Rule.Node} */ (node.body), true);
27
+ }
28
+
29
+ case 'BlockStatement': {
30
+ return node.body.some((bodyNode) => {
31
+ return hasRejectValue(/** @type {import('eslint').Rule.Node} */ (bodyNode), innerFunction);
32
+ });
33
+ }
34
+
35
+ case 'CallExpression': {
36
+ // Check for Promise.reject()
37
+ if (node.callee.type === 'MemberExpression' &&
38
+ node.callee.object.type === 'Identifier' &&
39
+ node.callee.object.name === 'Promise' &&
40
+ node.callee.property.type === 'Identifier' &&
41
+ node.callee.property.name === 'reject') {
42
+ return true;
43
+ }
44
+
45
+ // Check for reject() call (in Promise executor context)
46
+ if (node.callee.type === 'Identifier' && node.callee.name === 'reject') {
47
+ return true;
48
+ }
49
+
50
+ // Check if this is calling an inner function that might reject
51
+ if (innerFunction && node.callee.type === 'Identifier') {
52
+ // We found a function call inside - check if it could be calling a function that rejects
53
+ // We'll handle this in function body traversal
54
+ return false;
55
+ }
56
+
57
+ return false;
58
+ }
59
+
60
+ case 'DoWhileStatement':
61
+ case 'ForInStatement':
62
+ case 'ForOfStatement':
63
+ case 'ForStatement':
64
+ case 'LabeledStatement':
65
+ case 'WhileStatement':
66
+
67
+ case 'WithStatement': {
68
+ return hasRejectValue(/** @type {import('eslint').Rule.Node} */ (node.body), innerFunction);
69
+ }
70
+
71
+ case 'ExpressionStatement': {
72
+ return hasRejectValue(/** @type {import('eslint').Rule.Node} */ (node.expression), innerFunction);
73
+ }
74
+
75
+ case 'IfStatement': {
76
+ return hasRejectValue(/** @type {import('eslint').Rule.Node} */ (node.consequent), innerFunction) || hasRejectValue(/** @type {import('eslint').Rule.Node} */ (node.alternate), innerFunction);
77
+ }
78
+
79
+ case 'NewExpression': {
80
+ // Check for new Promise((resolve, reject) => { reject(...) })
81
+ if (node.callee.type === 'Identifier' && node.callee.name === 'Promise' && node.arguments.length > 0) {
82
+ const executor = node.arguments[0];
83
+ if (executor.type === 'ArrowFunctionExpression' || executor.type === 'FunctionExpression') {
84
+ // Check if the executor has reject() calls
85
+ return hasRejectValue(/** @type {import('eslint').Rule.Node} */ (executor.body), false);
86
+ }
87
+ }
88
+
89
+ return false;
90
+ }
91
+
92
+ case 'ReturnStatement': {
93
+ if (node.argument) {
94
+ return hasRejectValue(/** @type {import('eslint').Rule.Node} */ (node.argument), innerFunction);
95
+ }
96
+
97
+ return false;
98
+ }
99
+
100
+ case 'SwitchStatement': {
101
+ return node.cases.some(
102
+ (someCase) => {
103
+ return someCase.consequent.some((nde) => {
104
+ return hasRejectValue(/** @type {import('eslint').Rule.Node} */ (nde), innerFunction);
105
+ });
106
+ },
107
+ );
108
+ }
109
+
110
+ // Throw statements in async functions become rejections
111
+ case 'ThrowStatement': {
112
+ return true;
113
+ }
114
+
115
+ case 'TryStatement': {
116
+ return hasRejectValue(/** @type {import('eslint').Rule.Node} */ (node.handler && node.handler.body), innerFunction) ||
117
+ hasRejectValue(/** @type {import('eslint').Rule.Node} */ (node.finalizer), innerFunction);
118
+ }
119
+
120
+ default: {
121
+ return false;
122
+ }
123
+ }
124
+ };
125
+
126
+ /**
127
+ * We can skip checking for a rejects value, in case the documentation is inherited
128
+ * or the method is abstract.
129
+ * @param {import('../iterateJsdoc.js').Utils} utils
130
+ * @returns {boolean}
131
+ */
132
+ const canSkip = (utils) => {
133
+ return utils.hasATag([
134
+ 'abstract',
135
+ 'virtual',
136
+ 'type',
137
+ ]) ||
138
+ utils.avoidDocs();
139
+ };
140
+
141
+ export default iterateJsdoc(({
142
+ node,
143
+ report,
144
+ utils,
145
+ }) => {
146
+ if (canSkip(utils)) {
147
+ return;
148
+ }
149
+
150
+ const tagName = /** @type {string} */ (utils.getPreferredTagName({
151
+ tagName: 'rejects',
152
+ }));
153
+ if (!tagName) {
154
+ return;
155
+ }
156
+
157
+ const tags = utils.getTags(tagName);
158
+ const iteratingFunction = utils.isIteratingFunction();
159
+
160
+ const [
161
+ tag,
162
+ ] = tags;
163
+ const missingRejectsTag = typeof tag === 'undefined' || tag === null;
164
+
165
+ const shouldReport = () => {
166
+ if (!missingRejectsTag) {
167
+ return false;
168
+ }
169
+
170
+ // Check if this is an async function or returns a Promise
171
+ const isAsync = utils.isAsync();
172
+ if (!isAsync && !iteratingFunction) {
173
+ return false;
174
+ }
175
+
176
+ // For async functions, check for throw statements
177
+ // For regular functions, check for Promise.reject or reject calls
178
+ return hasRejectValue(/** @type {import('eslint').Rule.Node} */ (node));
179
+ };
180
+
181
+ if (shouldReport()) {
182
+ report('Promise-rejecting function requires `@reject` tag');
183
+ }
184
+ }, {
185
+ contextDefaults: true,
186
+ meta: {
187
+ docs: {
188
+ description: 'Requires that Promise rejections are documented with `@rejects` tags.',
189
+ url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-rejects.md#repos-sticky-header',
190
+ },
191
+ schema: [
192
+ {
193
+ additionalProperties: false,
194
+ properties: {
195
+ contexts: {
196
+ description: `Set this to an array of strings representing the AST context
197
+ (or objects with optional \`context\` and \`comment\` properties) where you wish
198
+ the rule to be applied.
199
+
200
+ \`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
201
+
202
+ Overrides the default contexts (\`ArrowFunctionExpression\`, \`FunctionDeclaration\`,
203
+ \`FunctionExpression\`).`,
204
+ items: {
205
+ anyOf: [
206
+ {
207
+ type: 'string',
208
+ },
209
+ {
210
+ additionalProperties: false,
211
+ properties: {
212
+ comment: {
213
+ type: 'string',
214
+ },
215
+ context: {
216
+ type: 'string',
217
+ },
218
+ },
219
+ type: 'object',
220
+ },
221
+ ],
222
+ },
223
+ type: 'array',
224
+ },
225
+ exemptedBy: {
226
+ description: `Array of tags (e.g., \`['type']\`) whose presence on the
227
+ document block avoids the need for a \`@rejects\`. Defaults to an array
228
+ with \`abstract\`, \`virtual\`, and \`type\`. If you set this array, it will overwrite the default,
229
+ so be sure to add back those tags if you wish their presence to cause
230
+ exemption of the rule.`,
231
+ items: {
232
+ type: 'string',
233
+ },
234
+ type: 'array',
235
+ },
236
+ },
237
+ type: 'object',
238
+ },
239
+ ],
240
+ type: 'suggestion',
241
+ },
242
+ });
package/src/rules.d.ts CHANGED
@@ -47,6 +47,10 @@ export interface Rules {
47
47
  | []
48
48
  | [
49
49
  {
50
+ /**
51
+ * Allows indentation of nested sections on subsequent lines (like bullet lists)
52
+ */
53
+ allowIndentedSections?: boolean;
50
54
  /**
51
55
  * Array of tags (e.g., `['example', 'description']`) whose content will be
52
56
  * "hidden" from the `check-indentation` rule. Defaults to `['example']`.
@@ -2192,6 +2196,39 @@ export interface Rules {
2192
2196
  /** Requires that each `@property` tag has a type value (in curly brackets). */
2193
2197
  "jsdoc/require-property-type": [];
2194
2198
 
2199
+ /** Requires that Promise rejections are documented with `@rejects` tags. */
2200
+ "jsdoc/require-rejects":
2201
+ | []
2202
+ | [
2203
+ {
2204
+ /**
2205
+ * Set this to an array of strings representing the AST context
2206
+ * (or objects with optional `context` and `comment` properties) where you wish
2207
+ * the rule to be applied.
2208
+ *
2209
+ * `context` defaults to `any` and `comment` defaults to no specific comment context.
2210
+ *
2211
+ * Overrides the default contexts (`ArrowFunctionExpression`, `FunctionDeclaration`,
2212
+ * `FunctionExpression`).
2213
+ */
2214
+ contexts?: (
2215
+ | string
2216
+ | {
2217
+ comment?: string;
2218
+ context?: string;
2219
+ }
2220
+ )[];
2221
+ /**
2222
+ * Array of tags (e.g., `['type']`) whose presence on the
2223
+ * document block avoids the need for a `@rejects`. Defaults to an array
2224
+ * with `abstract`, `virtual`, and `type`. If you set this array, it will overwrite the default,
2225
+ * so be sure to add back those tags if you wish their presence to cause
2226
+ * exemption of the rule.
2227
+ */
2228
+ exemptedBy?: string[];
2229
+ }
2230
+ ];
2231
+
2195
2232
  /** Requires that returns are documented with `@returns`. */
2196
2233
  "jsdoc/require-returns":
2197
2234
  | []