UNPKG

2.98 kBJavaScriptView Raw
1/**
2 * Rule: catch-or-return
3 * Ensures that promises either include a catch() handler
4 * or are returned (to be handled upstream)
5 */
6
7'use strict'
8
9const getDocsUrl = require('./lib/get-docs-url')
10const isPromise = require('./lib/is-promise')
11
12module.exports = {
13 meta: {
14 type: 'problem',
15 docs: {
16 url: getDocsUrl('catch-or-return'),
17 },
18 messages: {
19 terminationMethod: 'Expected {{ terminationMethod }}() or return',
20 },
21 schema: [
22 {
23 type: 'object',
24 properties: {
25 allowFinally: {
26 type: 'boolean',
27 },
28 allowThen: {
29 type: 'boolean',
30 },
31 terminationMethod: {
32 oneOf: [
33 { type: 'string' },
34 {
35 type: 'array',
36 items: {
37 type: 'string',
38 },
39 },
40 ],
41 },
42 },
43 additionalProperties: false,
44 },
45 ],
46 },
47 create(context) {
48 const options = context.options[0] || {}
49 const allowThen = options.allowThen
50 const allowFinally = options.allowFinally
51 let terminationMethod = options.terminationMethod || 'catch'
52
53 if (typeof terminationMethod === 'string') {
54 terminationMethod = [terminationMethod]
55 }
56
57 function isAllowedPromiseTermination(expression) {
58 // somePromise.then(a, b)
59 if (
60 allowThen &&
61 expression.type === 'CallExpression' &&
62 expression.callee.type === 'MemberExpression' &&
63 expression.callee.property.name === 'then' &&
64 expression.arguments.length === 2
65 ) {
66 return true
67 }
68
69 // somePromise.catch().finally(fn)
70 if (
71 allowFinally &&
72 expression.type === 'CallExpression' &&
73 expression.callee.type === 'MemberExpression' &&
74 expression.callee.property.name === 'finally' &&
75 isPromise(expression.callee.object) &&
76 isAllowedPromiseTermination(expression.callee.object)
77 ) {
78 return true
79 }
80
81 // somePromise.catch()
82 if (
83 expression.type === 'CallExpression' &&
84 expression.callee.type === 'MemberExpression' &&
85 terminationMethod.indexOf(expression.callee.property.name) !== -1
86 ) {
87 return true
88 }
89
90 // somePromise['catch']()
91 if (
92 expression.type === 'CallExpression' &&
93 expression.callee.type === 'MemberExpression' &&
94 expression.callee.property.type === 'Literal' &&
95 expression.callee.property.value === 'catch'
96 ) {
97 return true
98 }
99
100 return false
101 }
102
103 return {
104 ExpressionStatement(node) {
105 if (!isPromise(node.expression)) {
106 return
107 }
108
109 if (isAllowedPromiseTermination(node.expression)) {
110 return
111 }
112
113 context.report({
114 node,
115 messageId: 'terminationMethod',
116 data: { terminationMethod },
117 })
118 },
119 }
120 },
121}