Skip to content

Commit 4de9d43

Browse files
authoredOct 26, 2024··
fix: permit appropriate computed member expressions and prototype access (#535)
Permits appropriate computed member expressions such as `Promise['all']` and suitable prototype access (e.g., `Promise.prototype.catch`) but errs on detectable problems (e.g., `Promise.prototype.done`).
1 parent 05c8a93 commit 4de9d43

File tree

5 files changed

+67
-14
lines changed

5 files changed

+67
-14
lines changed
 

‎__tests__/spec-only.js

+22
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ ruleTester.run('spec-only', rule, {
99
'Promise.resolve()',
1010
'Promise.reject()',
1111
'Promise.all()',
12+
'Promise["all"]',
13+
'Promise[method];',
14+
'Promise.prototype;',
15+
'Promise.prototype[method];',
16+
'Promise.prototype["then"];',
1217
'Promise.race()',
18+
'var ctch = Promise.prototype.catch',
1319
'Promise.withResolvers()',
1420
'new Promise(function (resolve, reject) {})',
1521
'SomeClass.resolve()',
@@ -22,6 +28,14 @@ ruleTester.run('spec-only', rule, {
2228
},
2329
],
2430
},
31+
{
32+
code: 'Promise.prototype.permittedInstanceMethod',
33+
options: [
34+
{
35+
allowedMethods: ['permittedInstanceMethod'],
36+
},
37+
],
38+
},
2539
],
2640
invalid: [
2741
{
@@ -53,5 +67,13 @@ ruleTester.run('spec-only', rule, {
5367
`,
5468
errors: [{ message: "Avoid using non-standard 'Promise.done'" }],
5569
},
70+
{
71+
code: `var done = Promise.prototype.done`,
72+
errors: [{ message: "Avoid using non-standard 'Promise.prototype'" }],
73+
},
74+
{
75+
code: `Promise["done"];`,
76+
errors: [{ message: "Avoid using non-standard 'Promise.done'" }],
77+
},
5678
],
5779
})

‎rules/lib/is-promise.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ function isPromise(expression) {
2929
expression.callee.type === 'MemberExpression' &&
3030
expression.callee.object.type === 'Identifier' &&
3131
expression.callee.object.name === 'Promise' &&
32-
PROMISE_STATICS[expression.callee.property.name] &&
32+
PROMISE_STATICS.has(expression.callee.property.name) &&
3333
expression.callee.property.name !== 'withResolvers')
3434
)
3535
}

‎rules/lib/promise-statics.js

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
'use strict'
22

3-
module.exports = {
4-
all: true,
5-
allSettled: true,
6-
any: true,
7-
race: true,
8-
reject: true,
9-
resolve: true,
10-
withResolvers: true,
11-
}
3+
module.exports = new Set([
4+
'all',
5+
'allSettled',
6+
'any',
7+
'race',
8+
'reject',
9+
'resolve',
10+
'withResolvers',
11+
])

‎rules/no-new-statics.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ module.exports = {
2222
if (
2323
node.callee.type === 'MemberExpression' &&
2424
node.callee.object.name === 'Promise' &&
25-
PROMISE_STATICS[node.callee.property.name]
25+
PROMISE_STATICS.has(node.callee.property.name)
2626
) {
2727
context.report({
2828
node,

‎rules/spec-only.js

+34-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,30 @@
33
const PROMISE_STATICS = require('./lib/promise-statics')
44
const getDocsUrl = require('./lib/get-docs-url')
55

6+
const PROMISE_INSTANCE_METHODS = new Set(['then', 'catch', 'finally'])
7+
8+
function isPermittedProperty(expression, standardSet, allowedMethods) {
9+
// istanbul ignore if
10+
if (expression.type !== 'MemberExpression') return false
11+
12+
if (expression.property.type === 'Literal')
13+
return (
14+
standardSet.has(expression.property.value) ||
15+
allowedMethods.includes(expression.property.value)
16+
)
17+
18+
// istanbul ignore else
19+
if (expression.property.type === 'Identifier')
20+
return (
21+
expression.computed ||
22+
standardSet.has(expression.property.name) ||
23+
allowedMethods.includes(expression.property.name)
24+
)
25+
26+
// istanbul ignore next
27+
return false
28+
}
29+
630
module.exports = {
731
meta: {
832
type: 'problem',
@@ -36,13 +60,20 @@ module.exports = {
3660
if (
3761
node.object.type === 'Identifier' &&
3862
node.object.name === 'Promise' &&
39-
!(node.property.name in PROMISE_STATICS) &&
40-
!allowedMethods.includes(node.property.name)
63+
((node.property.name !== 'prototype' &&
64+
!isPermittedProperty(node, PROMISE_STATICS, allowedMethods)) ||
65+
(node.property.name === 'prototype' &&
66+
node.parent.type === 'MemberExpression' &&
67+
!isPermittedProperty(
68+
node.parent,
69+
PROMISE_INSTANCE_METHODS,
70+
allowedMethods,
71+
)))
4172
) {
4273
context.report({
4374
node,
4475
messageId: 'avoidNonStandard',
45-
data: { name: node.property.name },
76+
data: { name: node.property.name ?? node.property.value },
4677
})
4778
}
4879
},

0 commit comments

Comments
 (0)
Please sign in to comment.