avoid-new = function (context) {
return {
NewExpression: function (node) {
if (node.callee.name === 'Promise') {
context.report(node, 'Avoid creating new promises.')
}
}
}
}n/a
no-callback-in-promise = function (context) {
return {
CallExpression: function (node) {
if (!isCallback(node)) {
// in general we send you packing if you're not a callback
// but we also need to watch out for whatever.then(cb)
if (hasPromiseCallback(node)) {
var name = node.arguments && node.arguments[0] && node.arguments[0].name
if (name === 'callback' || name === 'cb' || name === 'next' || name === 'done') {
context.report(node.arguments[0], 'Avoid calling back inside of a promise.')
}
}
return
}
if (context.getAncestors().some(isInsidePromise)) {
context.report(node, 'Avoid calling back inside of a promise.')
}
}
}
}n/a
no-nesting = function (context) {
return {
CallExpression: function (node) {
if (!hasPromiseCallback(node)) return
if (context.getAncestors().some(isInsidePromise)) {
context.report(node, 'Avoid nesting promises.')
}
}
}
}n/a
no-promise-in-callback = function (context) {
return {
CallExpression: function (node) {
if (!isPromise(node)) return
// if i'm returning the promise, it's probably not really a callback
// function, and I should be okay....
if (node.parent.type === 'ReturnStatement') return
// what about if the parent is an ArrowFunctionExpression
// would that imply an implicit return?
if (context.getAncestors().some(isInsideCallback)) {
context.report(node.callee, 'Avoid using promises inside of callbacks.')
}
}
}
}n/a
prefer-await-to-callbacks = function (context) {
function checkLastParamsForCallback (node) {
var len = node.params.length - 1
var lastParam = node.params[len]
if (lastParam && (lastParam.name === 'callback' || lastParam.name === 'cb')) {
context.report(lastParam, errorMessage)
}
}
function isInsideYieldOrAwait () {
return context.getAncestors().some(function (parent) {
return parent.type === 'AwaitExpression' || parent.type === 'YieldExpression'
})
}
return {
CallExpression: function (node) {
// callbacks aren't allowed
if (node.callee.name === 'cb' || node.callee.name === 'callback') {
context.report(node, errorMessage)
return
}
// thennables aren't allowed either
var args = node.arguments
var num = args.length - 1
var arg = num > -1 && node.arguments && node.arguments[num]
if (arg && arg.type === 'FunctionExpression' || arg.type === 'ArrowFunctionExpression') {
if (arg.params && arg.params[0] && arg.params[0].name === 'err') {
if (!isInsideYieldOrAwait()) {
context.report(arg, errorMessage)
}
}
}
},
FunctionDeclaration: checkLastParamsForCallback,
FunctionExpression: checkLastParamsForCallback,
ArrowFunctionExpression: checkLastParamsForCallback
}
}n/a
prefer-await-to-then = function (context) {
return {
MemberExpression: function (node) {
// you can then() if you are inside of a yield or await
if (context.getAncestors().some(function (parent) {
return parent.type === 'AwaitExpression' || parent.type === 'YieldExpression'
})) {
return
}
// if you're a then expression then you're probably a promise
if (node.property && node.property.name === 'then') {
context.report(node.property, 'Prefer await to then().')
}
}
}
}n/a
create = function (context) {
// funcInfoStack is a stack representing the stack of currently executing
// functions
// funcInfoStack[i].branchIDStack is a stack representing the currently
// executing branches ("codePathSegment"s) within the given function
// funcInfoStack[i].branchInfoMap is an object representing information
// about all branches within the given function
// funcInfoStack[i].branchInfoMap[j].good is a boolean representing whether
// the given branch explictly `return`s or `throw`s. It starts as `false`
// for every branch and is updated to `true` if a `return` or `throw`
// statement is found
// funcInfoStack[i].branchInfoMap[j].loc is a eslint SourceLocation object
// for the given branch
// example:
// funcInfoStack = [ { branchIDStack: [ 's1_1' ],
// branchInfoMap:
// { s1_1:
// { good: false,
// loc: <loc> } } },
// { branchIDStack: ['s2_1', 's2_4'],
// branchInfoMap:
// { s2_1:
// { good: false,
// loc: <loc> },
// s2_2:
// { good: true,
// loc: <loc> },
// s2_4:
// { good: false,
// loc: <loc> } } } ]
var funcInfoStack = []
function markCurrentBranchAsGood () {
var funcInfo = peek(funcInfoStack)
var currentBranchID = peek(funcInfo.branchIDStack)
if (funcInfo.branchInfoMap[currentBranchID]) {
funcInfo.branchInfoMap[currentBranchID].good = true
}
// else unreachable code
}
return {
ReturnStatement: markCurrentBranchAsGood,
ThrowStatement: markCurrentBranchAsGood,
onCodePathSegmentStart: function (segment, node) {
var funcInfo = peek(funcInfoStack)
funcInfo.branchIDStack.push(segment.id)
funcInfo.branchInfoMap[segment.id] = {good: false, node: node}
},
onCodePathSegmentEnd: function (segment, node) {
var funcInfo = peek(funcInfoStack)
funcInfo.branchIDStack.pop()
},
onCodePathStart: function (path, node) {
funcInfoStack.push({
branchIDStack: [],
branchInfoMap: {}
})
},
onCodePathEnd: function (path, node) {
var funcInfo = funcInfoStack.pop()
if (!isInlineThenFunctionExpression(node)) {
return
}
path.finalSegments.forEach((segment) => {
var id = segment.id
var branch = funcInfo.branchInfoMap[id]
if (!branch.good) {
if (hasParentReturnStatement(branch.node)) {
return
}
// check shortcircuit syntax like `x && x()` and `y || x()``
var prevSegments = segment.prevSegments
for (var ii = prevSegments.length - 1; ii >= 0; --ii) {
var prevSegment = prevSegments[ii]
if (funcInfo.branchInfoMap[prevSegment.id].good) return
}
context.report({
message: 'Each then() should return a value or throw',
node: branch.node
})
}
})
}
}
}n/a
create = function (context) {
var options = context.options[0] || {}
var allowThen = options.allowThen
var terminationMethod = options.terminationMethod || 'catch'
if (typeof terminationMethod === 'string') {
terminationMethod = [terminationMethod]
}
return {
ExpressionStatement: function (node) {
if (!isPromise(node.expression)) {
return
}
// somePromise.then(a, b)
if (allowThen &&
node.expression.type === 'CallExpression' &&
node.expression.callee.type === 'MemberExpression' &&
node.expression.callee.property.name === 'then' &&
node.expression.arguments.length === 2
) {
return
}
// somePromise.catch()
if (node.expression.type === 'CallExpression' &&
node.expression.callee.type === 'MemberExpression' &&
terminationMethod.indexOf(node.expression.callee.property.name) !== -1
) {
return
}
context.report(node, 'Expected ' + terminationMethod + '() or return')
}
}
}n/a
create = function (context) {
var MESSAGE = '"function eslint-plugin-promise.rules.no-native.create" is not defined.'
/**
* Checks for and reports reassigned constants
*
* @param {Scope} scope - an escope Scope object
* @returns {void}
* @private
*/
return {
'Program:exit': function () {
var scope = context.getScope()
scope.implicit.left.forEach(function (ref) {
if (ref.identifier.name !== 'Promise') {
return
}
if (!isDeclared(scope, ref)) {
context.report(ref.identifier, MESSAGE, { name: ref.identifier.name })
}
})
}
}
}n/a
create = function (context) {
return {
ReturnStatement: function (node) {
if (isInPromise(context)) {
if (node.argument) {
if (node.argument.type === 'CallExpression') {
if (node.argument.callee.type === 'MemberExpression') {
if (node.argument.callee.object.name === 'Promise') {
if (node.argument.callee.property.name === 'resolve') {
context.report(node, resolveMessage)
} else if (node.argument.callee.property.name === 'reject') {
context.report(node, rejectMessage)
}
}
}
}
}
}
}
}
}n/a
create = function (context) {
return {
NewExpression: function (node) {
if (node.callee.name === 'Promise' && node.arguments.length === 1) {
var params = node.arguments[0].params
if (!params || !params.length) { return }
if (params[0].name !== 'resolve') {
return context.report(node, 'Promise constructor parameters must be named resolve, reject')
}
if (params[1] && params[1].name !== 'reject') {
return context.report(node, 'Promise constructor parameters must be named resolve, reject')
}
}
}
}
}n/a