eslint-plugin-promise
Advanced tools
Comparing version 2.0.1 to 3.0.0
{ | ||
"name": "eslint-plugin-promise", | ||
"version": "2.0.1", | ||
"version": "3.0.0", | ||
"description": "Enforce best practices for JavaScript promises", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -64,2 +64,3 @@ # eslint-plugin-promise | ||
myPromise.then(doSomething); // could be either | ||
myPromise.then((b) => { if (b) { return "yes" } else { return "no" } }); | ||
``` | ||
@@ -72,2 +73,3 @@ | ||
myPromise.then(() => { doSomething(); }); | ||
myPromise.then((b) => { if (b) { return "yes" } else { forgotToReturn(); } }); | ||
``` | ||
@@ -74,0 +76,0 @@ |
@@ -11,26 +11,108 @@ function isFunctionWithBlockStatement (node) { | ||
function isReturnOrThrowStatement (node) { | ||
return node.type === 'ReturnStatement' || node.type === 'ThrowStatement' | ||
function isThenCallExpression (node) { | ||
return ( | ||
node.type === 'CallExpression' && | ||
node.callee.type === 'MemberExpression' && | ||
node.callee.property.name === 'then' | ||
) | ||
} | ||
function isFirstArgument (node) { | ||
return ( | ||
node.parent && | ||
node.parent.arguments && | ||
node.parent.arguments[0] === node | ||
) | ||
} | ||
function isInlineThenFunctionExpression (node) { | ||
return ( | ||
isFunctionWithBlockStatement(node) && | ||
isThenCallExpression(node.parent) && | ||
isFirstArgument(node) | ||
) | ||
} | ||
function peek (arr) { | ||
return arr[arr.length - 1] | ||
} | ||
module.exports = { | ||
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) | ||
funcInfo.branchInfoMap[currentBranchID].good = true | ||
} | ||
return { | ||
MemberExpression: function (node) { | ||
var firstArg, body, lastStatement | ||
ReturnStatement: markCurrentBranchAsGood, | ||
ThrowStatement: markCurrentBranchAsGood, | ||
if (node.property.name !== 'then' || node.parent.type !== 'CallExpression') { | ||
return | ||
} | ||
onCodePathSegmentStart: function (segment, node) { | ||
var funcInfo = peek(funcInfoStack) | ||
funcInfo.branchIDStack.push(segment.id) | ||
funcInfo.branchInfoMap[segment.id] = {good: false, loc: node.loc} | ||
}, | ||
firstArg = node.parent.arguments[0] | ||
if (!firstArg || !isFunctionWithBlockStatement(firstArg)) { | ||
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 | ||
} | ||
body = firstArg.body.body | ||
lastStatement = body[body.length - 1] | ||
if (!lastStatement || !isReturnOrThrowStatement(lastStatement)) { | ||
context.report(node, 'Each then() should return a value or throw') | ||
} | ||
var finalBranchIDs = path.finalSegments.map(x => x.id) | ||
finalBranchIDs.forEach((id) => { | ||
var branch = funcInfo.branchInfoMap[id] | ||
if (!branch.good) { | ||
context.report({ | ||
message: 'Each then() should return a value or throw', | ||
loc: branch.loc | ||
}) | ||
} | ||
}) | ||
} | ||
@@ -37,0 +119,0 @@ } |
@@ -12,2 +12,3 @@ 'use strict' | ||
{ code: 'hey.then(x => ({}))', parserOptions: parserOptions }, | ||
{ code: 'hey.then(x => { return; })', parserOptions: parserOptions }, | ||
{ code: 'hey.then(x => { return x * 10 })', parserOptions: parserOptions }, | ||
@@ -23,3 +24,7 @@ { code: 'hey.then(function() { return 42; })', parserOptions: parserOptions }, | ||
{ code: 'hey.then(x => { if (!x) { throw new Error("no x"); } return x; })', parserOptions: parserOptions }, | ||
{ code: 'hey.then(x => { if (x) { return x; } throw new Error("no x"); })', parserOptions: parserOptions } | ||
{ code: 'hey.then(x => { if (x) { return x; } throw new Error("no x"); })', parserOptions: parserOptions }, | ||
{ code: 'hey.then(x => { var f = function() { }; return f; })', parserOptions: parserOptions }, | ||
{ code: 'hey.then(x => { if (x) { return x; } else { return x; } })', parserOptions: parserOptions }, | ||
{ code: 'hey.then(x => { return x; var y = "unreachable"; })', parserOptions: parserOptions }, | ||
{ code: 'hey.then(x => { return; }, err=>{ log(err); })', parserOptions: parserOptions } | ||
], | ||
@@ -46,6 +51,26 @@ | ||
{ | ||
code: 'hey.then(function() { return; }).then(function() { })', | ||
errors: [ { message: message } ] | ||
}, | ||
{ | ||
code: 'hey.then(function() { doSomethingWicked(); })', | ||
errors: [ { message: message } ] | ||
}, | ||
{ | ||
code: 'hey.then(function() { if (x) { return x; } })', | ||
errors: [ { message: message } ] | ||
}, | ||
{ | ||
code: 'hey.then(function() { if (x) { return x; } else { }})', | ||
errors: [ { message: message } ] | ||
}, | ||
{ | ||
code: 'hey.then(function() { if (x) { } else { return x; }})', | ||
errors: [ { message: message } ] | ||
}, | ||
{ | ||
code: 'hey.then(function() { if (x) { return you.then(function() { return x; }); } })', | ||
errors: [ { message: message } ] | ||
} | ||
] | ||
}) |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
24290
508
157