@sapphire/eslint-plugin-result
Version:
A TypeScript ESLint plugin for @sapphire/result
157 lines (149 loc) • 5.72 kB
JavaScript
;
var utils = require('@typescript-eslint/utils');
var tsutils = require('tsutils');
var ts = require('typescript');
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
var ts__default = /*#__PURE__*/_interopDefault(ts);
var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
}) : x)(function(x) {
if (typeof require !== "undefined")
return require.apply(this, arguments);
throw Error('Dynamic require of "' + x + '" is not supported');
});
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __commonJS = (cb, mod) => function __require2() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
// src/configs/recommended.ts
var recommendedConfig;
var init_recommended = __esm({
"src/configs/recommended.ts"() {
recommendedConfig = {
plugins: ["@sapphire/eslint-plugin-result"],
rules: {
"@sapphire/result/no-discard-result": "error"
}
};
}
});
function getSapphireResultType(service, checker) {
const file = service.program?.getSourceFile(resultPath);
const resultNode = file?.statements.find((node) => ts__default.default.isTypeAliasDeclaration(node) && node.name.getText() === "Result");
if (resultNode) {
return checker.getTypeAtLocation(resultNode);
}
return null;
}
function unwrapPotentialPromiseType(checker, node, type = checker.getTypeAtLocation(node)) {
if (tsutils.isUnionType(type)) {
const { ...copy } = type;
copy.types = type.types.filter((subtype) => Boolean(subtype));
if (copy.types.length === 1) {
return unwrapPotentialPromiseType(checker, node, copy.types[0]);
}
copy.types = copy.types.map((subtype) => unwrapPotentialPromiseType(checker, node, subtype));
return copy;
}
if (tsutils.isThenableType(checker, node)) {
return checker.getTypeArguments(type)[0];
}
return type;
}
function functionHasResultLikeReturnType(service, checker, node) {
const functionDeclaration = checker.getResolvedSignature(node);
if (!functionDeclaration) {
return false;
}
const returnType = unwrapPotentialPromiseType(checker, node);
const resultType = getSapphireResultType(service, checker);
if (!returnType || !returnType.aliasSymbol || !resultType?.aliasSymbol) {
return false;
}
return Reflect.get(returnType.aliasSymbol, "id") === Reflect.get(resultType.aliasSymbol, "id");
}
function isDiscardedResult(callExpressionNode) {
if (callExpressionNode.parent?.type === utils.AST_NODE_TYPES.UnaryExpression && callExpressionNode.parent.operator === "void") {
return false;
}
if (callExpressionNode.parent?.type === utils.AST_NODE_TYPES.VariableDeclarator) {
return false;
}
if (callExpressionNode.parent?.type === utils.AST_NODE_TYPES.AssignmentExpression) {
return false;
}
if (callExpressionNode.parent?.type === utils.AST_NODE_TYPES.CallExpression) {
return false;
}
if (callExpressionNode.parent?.type === utils.AST_NODE_TYPES.SequenceExpression) {
return isDiscardedResult(callExpressionNode.parent);
}
if (callExpressionNode.parent?.type === utils.AST_NODE_TYPES.AwaitExpression) {
return isDiscardedResult(callExpressionNode.parent);
}
if (callExpressionNode.parent?.type === utils.AST_NODE_TYPES.ConditionalExpression) {
return isDiscardedResult(callExpressionNode.parent);
}
if (callExpressionNode.parent?.type === utils.AST_NODE_TYPES.LogicalExpression) {
return isDiscardedResult(callExpressionNode.parent);
}
return true;
}
var messages, resultPath, noDiscardResult;
var init_no_discard_result = __esm({
"src/rules/no-discard-result.ts"() {
messages = {
discardedResult: "This function returns a Result, but its return value is being discarded."
};
resultPath = __require.resolve("@sapphire/result").split("/").slice(0, -1).concat("index.d.ts").join("/");
__name(getSapphireResultType, "getSapphireResultType");
__name(unwrapPotentialPromiseType, "unwrapPotentialPromiseType");
__name(functionHasResultLikeReturnType, "functionHasResultLikeReturnType");
__name(isDiscardedResult, "isDiscardedResult");
noDiscardResult = utils.ESLintUtils.RuleCreator.withoutDocs({
meta: {
messages,
type: "problem",
schema: []
},
defaultOptions: [],
create: (context) => {
return {
CallExpression(callExpressionNode) {
const service = utils.ESLintUtils.getParserServices(context);
const checker = service.program.getTypeChecker();
if (!functionHasResultLikeReturnType(service, checker, service.esTreeNodeToTSNodeMap.get(callExpressionNode))) {
return;
}
if (isDiscardedResult(callExpressionNode)) {
context.report({ messageId: "discardedResult", node: callExpressionNode });
}
}
};
}
});
}
});
// src/index.ts
var require_src = __commonJS({
"src/index.ts"(exports, module) {
init_recommended();
init_no_discard_result();
var eslintPluginResult = {
rules: {
"no-discard-result": noDiscardResult
},
configs: {
recommended: recommendedConfig
}
};
module.exports = eslintPluginResult;
}
});
var index = require_src();
module.exports = index;