Skip to content

Commit 53be970

Browse files
authoredOct 16, 2024··
feat(catch-or-return): add allowThenStrict option (#522)
1 parent bbd048b commit 53be970

File tree

3 files changed

+167
-11
lines changed

3 files changed

+167
-11
lines changed
 

‎__tests__/catch-or-return.js

+107-6
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,10 @@ ruleTester.run('catch-or-return', rule, {
5353
options: [{ allowThen: true }],
5454
},
5555

56-
// allowThen - .then(null, fn)
57-
{ code: 'frank().then(a, b)', options: [{ allowThen: true }] },
5856
{
5957
code: 'frank().then(a).then(b).then(null, c)',
6058
options: [{ allowThen: true }],
6159
},
62-
{
63-
code: 'frank().then(a).then(b).then(c, d)',
64-
options: [{ allowThen: true }],
65-
},
6660
{
6761
code: 'frank().then(a).then(b).then().then().then(null, doIt)',
6862
options: [{ allowThen: true }],
@@ -73,10 +67,15 @@ ruleTester.run('catch-or-return', rule, {
7367
},
7468

7569
// allowThen - .then(fn, fn)
70+
{ code: 'frank().then(a, b)', options: [{ allowThen: true }] },
7671
{
7772
code: 'frank().then(go).then(zam, doIt)',
7873
options: [{ allowThen: true }],
7974
},
75+
{
76+
code: 'frank().then(a).then(b).then(c, d)',
77+
options: [{ allowThen: true }],
78+
},
8079
{
8180
code: 'frank().then(go).then().then().then().then(wham, doIt)',
8281
options: [{ allowThen: true }],
@@ -90,6 +89,37 @@ ruleTester.run('catch-or-return', rule, {
9089
options: [{ allowThen: true }],
9190
},
9291

92+
// allowThenStrict - .then(null, fn)
93+
{
94+
code: 'frank().then(go).then(null, doIt)',
95+
options: [{ allowThenStrict: true }],
96+
},
97+
{
98+
code: 'frank().then(go).then().then().then().then(null, doIt)',
99+
options: [{ allowThenStrict: true }],
100+
},
101+
{
102+
code: 'frank().then(go).then().then(null, function() { /* why bother */ })',
103+
options: [{ allowThenStrict: true }],
104+
},
105+
{
106+
code: 'frank.then(go).then(to).then(null, jail)',
107+
options: [{ allowThenStrict: true }],
108+
},
109+
110+
{
111+
code: 'frank().then(a).then(b).then(null, c)',
112+
options: [{ allowThenStrict: true }],
113+
},
114+
{
115+
code: 'frank().then(a).then(b).then().then().then(null, doIt)',
116+
options: [{ allowThenStrict: true }],
117+
},
118+
{
119+
code: 'frank().then(a).then(b).then(null, function() { /* why bother */ })',
120+
options: [{ allowThenStrict: true }],
121+
},
122+
93123
// allowFinally - .finally(fn)
94124
{
95125
code: 'frank().then(go).catch(doIt).finally(fn)',
@@ -234,5 +264,76 @@ ruleTester.run('catch-or-return', rule, {
234264
code: 'frank().catch(go).someOtherMethod()',
235265
errors: [{ message: catchMessage }],
236266
},
267+
268+
// .then(null, fn)
269+
{
270+
code: 'frank().then(a).then(b).then(null, c)',
271+
errors: [{ message: catchMessage }],
272+
},
273+
{
274+
code: 'frank().then(a).then(b).then().then().then(null, doIt)',
275+
errors: [{ message: catchMessage }],
276+
},
277+
{
278+
code: 'frank().then(a).then(b).then(null, function() { /* why bother */ })',
279+
errors: [{ message: catchMessage }],
280+
},
281+
282+
// .then(fn, fn)
283+
{
284+
code: 'frank().then(a, b)',
285+
errors: [{ message: catchMessage }],
286+
},
287+
{
288+
code: 'frank().then(go).then(zam, doIt)',
289+
errors: [{ message: catchMessage }],
290+
},
291+
{
292+
code: 'frank().then(a).then(b).then(c, d)',
293+
errors: [{ message: catchMessage }],
294+
},
295+
{
296+
code: 'frank().then(go).then().then().then().then(wham, doIt)',
297+
errors: [{ message: catchMessage }],
298+
},
299+
{
300+
code: 'frank().then(go).then().then(function() {}, function() { /* why bother */ })',
301+
errors: [{ message: catchMessage }],
302+
},
303+
{
304+
code: 'frank.then(go).then(to).then(pewPew, jail)',
305+
errors: [{ message: catchMessage }],
306+
},
307+
308+
{
309+
code: 'frank().then(a, b)',
310+
errors: [{ message: catchMessage }],
311+
options: [{ allowThenStrict: true }],
312+
},
313+
{
314+
code: 'frank().then(go).then(zam, doIt)',
315+
errors: [{ message: catchMessage }],
316+
options: [{ allowThenStrict: true }],
317+
},
318+
{
319+
code: 'frank().then(a).then(b).then(c, d)',
320+
errors: [{ message: catchMessage }],
321+
options: [{ allowThenStrict: true }],
322+
},
323+
{
324+
code: 'frank().then(go).then().then().then().then(wham, doIt)',
325+
errors: [{ message: catchMessage }],
326+
options: [{ allowThenStrict: true }],
327+
},
328+
{
329+
code: 'frank().then(go).then().then(function() {}, function() { /* why bother */ })',
330+
errors: [{ message: catchMessage }],
331+
options: [{ allowThenStrict: true }],
332+
},
333+
{
334+
code: 'frank.then(go).then(to).then(pewPew, jail)',
335+
errors: [{ message: catchMessage }],
336+
options: [{ allowThenStrict: true }],
337+
},
237338
],
238339
})

‎docs/rules/catch-or-return.md

+50-3
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,56 @@ function doSomethingElse() {
3232

3333
##### `allowThen`
3434

35-
You can pass an `{ allowThen: true }` as an option to this rule to allow for
36-
`.then(null, fn)` to be used instead of `catch()` at the end of the promise
37-
chain.
35+
The second argument to `then()` can also be used to handle a promise rejection,
36+
but it won't catch any errors from the first argument callback. Because of this,
37+
this rule reports usage of `then()` with two arguments without `catch()` by
38+
default.
39+
40+
However, you can use `{ allowThen: true }` to allow using `then()` with two
41+
arguments instead of `catch()` to handle promise rejections.
42+
43+
Examples of **incorrect** code for the default `{ allowThen: false }` option:
44+
45+
```js
46+
myPromise.then(doSomething, handleErrors)
47+
myPromise.then(null, handleErrors)
48+
```
49+
50+
Examples of **correct** code for the `{ allowThen: true }` option:
51+
52+
```js
53+
myPromise.then(doSomething, handleErrors)
54+
myPromise.then(null, handleErrors)
55+
myPromise.then(doSomething).catch(handleErrors)
56+
```
57+
58+
##### `allowThenStrict`
59+
60+
`allowThenStrict` is similar to `allowThen` but it only permits `then` when the
61+
fulfillment handler is `null`. This option ensures that the final rejected
62+
handler works like a `catch` and can handle any uncaught errors from the final
63+
`then`.
64+
65+
Examples of **incorrect** code for the default `{ allowThenStrict: false }`
66+
option:
67+
68+
```js
69+
myPromise.then(doSomething, handleErrors)
70+
myPromise.then(null, handleErrors)
71+
```
72+
73+
Examples of **correct** code for the `{ allowThenStrict: true }` option:
74+
75+
```js
76+
myPromise.then(null, handleErrors)
77+
myPromise.then(doSomething).catch(handleErrors)
78+
```
79+
80+
Examples of **incorrect** code for the `{ allowThenStrict: true }` option:
81+
82+
```js
83+
myPromise.then(doSomething, handleErrors)
84+
```
3885

3986
##### `allowFinally`
4087

‎rules/catch-or-return.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ module.exports = {
3030
allowThen: {
3131
type: 'boolean',
3232
},
33+
allowThenStrict: {
34+
type: 'boolean',
35+
},
3336
terminationMethod: {
3437
oneOf: [
3538
{ type: 'string' },
@@ -49,6 +52,7 @@ module.exports = {
4952
create(context) {
5053
const options = context.options[0] || {}
5154
const allowThen = options.allowThen
55+
const allowThenStrict = options.allowThenStrict
5256
const allowFinally = options.allowFinally
5357
let terminationMethod = options.terminationMethod || 'catch'
5458

@@ -59,11 +63,15 @@ module.exports = {
5963
function isAllowedPromiseTermination(expression) {
6064
// somePromise.then(a, b)
6165
if (
62-
allowThen &&
66+
(allowThen || allowThenStrict) &&
6367
expression.type === 'CallExpression' &&
6468
expression.callee.type === 'MemberExpression' &&
6569
expression.callee.property.name === 'then' &&
66-
expression.arguments.length === 2
70+
expression.arguments.length === 2 &&
71+
// somePromise.then(null, b)
72+
((allowThen && !allowThenStrict) ||
73+
(expression.arguments[0].type === 'Literal' &&
74+
expression.arguments[0].value === null))
6775
) {
6876
return true
6977
}

0 commit comments

Comments
 (0)
Please sign in to comment.