forked from FoundKeyGang/FoundKey
120 lines
4 KiB
JavaScript
120 lines
4 KiB
JavaScript
const dbFunctions = ["find", "findBy", "findOne", "findOneBy", "findOneOrFail", "findOneByOrFail", "getOne", "getMany", "getRawOne", "getRawMany"];
|
|
|
|
module.exports = {
|
|
meta: {
|
|
type: "suggestion",
|
|
},
|
|
create(context) {
|
|
return {
|
|
"Program:exit"(programNode) {
|
|
const isNull = (node) => node.type === "Literal" && node.value === null;
|
|
|
|
const scopes = [context.getScope()];
|
|
while (scopes.length > 0) {
|
|
const s = scopes.pop();
|
|
s.childScopes.forEach(x => scopes.push(x));
|
|
|
|
const variables = s.variables
|
|
.filter(x =>
|
|
x.references
|
|
.filter((ref) => ref.isRead())
|
|
.length > 0
|
|
&& x.defs.length > 0
|
|
);
|
|
|
|
for (const v of variables) {
|
|
let findValueAssign = null;
|
|
// if the find value was not read, there will already be an unused variable lint
|
|
// or maybe this was inside an if/else so we dont want to cause a false positive
|
|
let read = false;
|
|
for (const ref of v.references) {
|
|
if (
|
|
ref.isWrite()
|
|
) {
|
|
if (!ref.writeExpr) continue;
|
|
let writeExpr = ref.writeExpr;
|
|
// unwrap write expression, order matters to correctly unwrap
|
|
// something like "await Promise.all([a, b])".
|
|
// Basically a poor mans data flow analysis.
|
|
if (writeExpr.type === "AwaitExpression")
|
|
writeExpr = writeExpr.argument;
|
|
if (
|
|
writeExpr.type === "CallExpression"
|
|
&& writeExpr.callee.type === "MemberExpression"
|
|
&& writeExpr.callee.object.type === "Identifier"
|
|
&& writeExpr.callee.object.name === "Promise"
|
|
&& writeExpr.callee.property.type === "Identifier"
|
|
&& writeExpr.callee.property.name === "all"
|
|
)
|
|
writeExpr = writeExpr.arguments[0];
|
|
if (
|
|
writeExpr.type === "ArrayExpression"
|
|
&& ref.identifier.parent.type === "ArrayPattern"
|
|
) {
|
|
// use same index as the index of the identifier in the array pattern
|
|
const index = ref.identifier.parent.elements.indexOf(ref.identifier);
|
|
if (index > -1)
|
|
writeExpr = writeExpr.elements[index];
|
|
}
|
|
// check if this is a DB find thingy
|
|
if (
|
|
writeExpr.type === "CallExpression"
|
|
&& writeExpr.callee.type === "MemberExpression"
|
|
&& writeExpr.callee.property.type === "Identifier"
|
|
&& dbFunctions.includes(writeExpr.callee.property.name)
|
|
) {
|
|
if (findValueAssign && read) {
|
|
context.report({
|
|
node: findValueAssign,
|
|
message: "The returned object(s) are not read from, use `count` or `countBy` instead."
|
|
});
|
|
}
|
|
findValueAssign = writeExpr;
|
|
read = false;
|
|
}
|
|
}
|
|
// must be a read
|
|
else if (findValueAssign) {
|
|
const node = ref.identifier;
|
|
if(!(
|
|
(
|
|
// explicit null check
|
|
node.parent.type === "BinaryExpression" &&
|
|
["==", "===", "!=", "!=="].includes(node.parent.operator) &&
|
|
(
|
|
(isNull(node.parent.left) && node.parent.right === node)
|
|
||
|
|
(isNull(node.parent.right) && node.parent.left === node)
|
|
)
|
|
) || (
|
|
// implicit null check
|
|
["IfStatement", "ConditionalExpression"].includes(node.parent.type)
|
|
) || (
|
|
// different implicit null check
|
|
node.parent.type === "UnaryExpression" &&
|
|
node.parent.operator === "!"
|
|
) || (
|
|
// length check
|
|
node.parent.type === "MemberExpression" &&
|
|
node.parent.object === node &&
|
|
node.parent.property.type === "Identifier" &&
|
|
node.parent.property.name === "length"
|
|
)
|
|
)) {
|
|
// other use, dont report
|
|
findValueAssign = null;
|
|
}
|
|
}
|
|
}
|
|
if (findValueAssign) {
|
|
context.report({
|
|
node: findValueAssign,
|
|
message: "The returned object(s) are not read from, use `count` or `countBy` instead."
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
};
|