Files
NapCatQQ/.pnpm-store/v3/files/01/4d191b0d034f1c2913885cf5b628cc6e137e078c61fde9a49f4ce6a0a8b819b317306fb0f7cd186403efaf79ebdb906f13a036739c5c278df4d7db9ded2ac4
TanTan e311ca3275
Build NapCat Artifacts / Type Check & Test (push) Waiting to run
docs: 仓库链接指向本镜像仓库
2026-11-13 09:25:58 +08:00

124 lines
3.2 KiB
Plaintext

/**
* Rule: no-nesting
* Avoid nesting your promises.
*/
'use strict'
const { getScope } = require('./lib/eslint-compat')
const getDocsUrl = require('./lib/get-docs-url')
const hasPromiseCallback = require('./lib/has-promise-callback')
const isInsidePromise = require('./lib/is-inside-promise')
module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'Disallow nested `then()` or `catch()` statements.',
url: getDocsUrl('no-nesting'),
},
schema: [],
messages: {
avoidNesting: 'Avoid nesting promises.',
},
},
create(context) {
/**
* Array of callback function scopes.
* Scopes are in order closest to the current node.
* @type {import('eslint').Scope.Scope[]}
*/
const callbackScopes = []
/**
* @param {import('eslint').Scope.Scope} scope
* @returns {Iterable<import('eslint').Scope.Reference>}
*/
function* iterateDefinedReferences(scope) {
for (const variable of scope.variables) {
for (const reference of variable.references) {
yield reference
}
}
}
return {
':function'(node) {
if (isInsidePromise(node)) {
callbackScopes.unshift(getScope(context, node))
}
},
':function:exit'(node) {
if (isInsidePromise(node)) {
callbackScopes.shift()
}
},
CallExpression(node) {
if (!hasPromiseCallback(node)) return
if (!callbackScopes.length) {
// The node is not in the callback function.
return
}
// Checks if the argument callback uses variables defined in the closest callback function scope.
//
// e.g.
// ```
// doThing()
// .then(a => getB(a)
// .then(b => getC(a, b))
// )
// ```
//
// In the above case, Since the variables it references are undef,
// we cannot refactor the nesting like following:
// ```
// doThing()
// .then(a => getB(a))
// .then(b => getC(a, b))
// ```
//
// However, `getD` can be refactored in the following:
// ```
// doThing()
// .then(a => getB(a)
// .then(b => getC(a, b)
// .then(c => getD(a, c))
// )
// )
// ```
// ↓
// ```
// doThing()
// .then(a => getB(a)
// .then(b => getC(a, b))
// .then(c => getD(a, c))
// )
// ```
// This is why we only check the closest callback function scope.
//
const closestCallbackScope = callbackScopes[0]
for (const reference of iterateDefinedReferences(
closestCallbackScope,
)) {
if (
node.arguments.some(
(arg) =>
arg.range[0] <= reference.identifier.range[0] &&
reference.identifier.range[1] <= arg.range[1],
)
) {
// Argument callbacks refer to variables defined in the callback function.
return
}
}
context.report({
node: node.callee.property,
messageId: 'avoidNesting',
})
},
}
},
}