eslint-plugin-etc
Version:
More general-purpose ESLint rules
154 lines (153 loc) • 5.92 kB
JavaScript
"use strict";
const experimental_utils_1 = require("@typescript-eslint/experimental-utils");
const eslint_etc_1 = require("eslint-etc");
const utils_1 = require("../utils");
function isParenthesised(sourceCode, node) {
const before = sourceCode.getTokenBefore(node);
const after = sourceCode.getTokenAfter(node);
return (before &&
after &&
before.value === "(" &&
before.range[1] <= node.range[0] &&
after.value === ")" &&
after.range[0] >= node.range[1]);
}
const defaultOptions = [];
const rule = (0, utils_1.ruleCreator)({
defaultOptions,
meta: {
docs: {
description: "Forbids implicit `any` error parameters in promise rejections.",
recommended: false,
suggestion: true,
},
fixable: "code",
hasSuggestions: true,
messages: {
explicitAny: "Explicit `any` in promise rejection.",
implicitAny: "Implicit `any` in promise rejection.",
narrowed: "Error type must be `unknown` or `any`.",
suggestExplicitUnknown: "Use `unknown` instead, this will force you to explicitly and safely assert the type is correct.",
},
schema: [
{
additionalProperties: false,
properties: {
allowExplicitAny: {
type: "boolean",
},
},
type: "object",
},
],
type: "suggestion",
},
name: "no-implicit-any-catch",
create: (context, unused) => {
const { couldBeType } = (0, eslint_etc_1.getTypeServices)(context);
const [config = {}] = context.options;
const { allowExplicitAny = false } = config;
const sourceCode = context.getSourceCode();
function checkRejectionCallback(callExpression, callback) {
if (!(0, eslint_etc_1.isArrowFunctionExpression)(callback) &&
!(0, eslint_etc_1.isFunctionExpression)(callback)) {
return;
}
const [param] = callback.params;
if (!param) {
return;
}
if ((0, eslint_etc_1.hasTypeAnnotation)(param)) {
const { typeAnnotation } = param;
const { typeAnnotation: { type }, } = typeAnnotation;
if (type === experimental_utils_1.AST_NODE_TYPES.TSAnyKeyword) {
if (allowExplicitAny) {
return;
}
if (!isPromiseCall(callExpression)) {
return;
}
function fix(fixer) {
return fixer.replaceText(typeAnnotation, ": unknown");
}
context.report({
fix,
messageId: "explicitAny",
node: param,
suggest: [
{
messageId: "suggestExplicitUnknown",
fix,
},
],
});
}
else if (type !== experimental_utils_1.AST_NODE_TYPES.TSUnknownKeyword) {
if (!isPromiseCall(callExpression)) {
return;
}
function fix(fixer) {
return fixer.replaceText(typeAnnotation, ": unknown");
}
context.report({
messageId: "narrowed",
node: param,
suggest: [
{
messageId: "suggestExplicitUnknown",
fix,
},
],
});
}
}
else {
if (!isPromiseCall(callExpression)) {
return;
}
function fix(fixer) {
if (isParenthesised(sourceCode, param)) {
return fixer.insertTextAfter(param, ": unknown");
}
return [
fixer.insertTextBefore(param, "("),
fixer.insertTextAfter(param, ": unknown)"),
];
}
context.report({
fix,
messageId: "implicitAny",
node: param,
suggest: [
{
messageId: "suggestExplicitUnknown",
fix,
},
],
});
}
}
function isPromiseCall(callExpression) {
const { callee } = callExpression;
if (!(0, eslint_etc_1.isMemberExpression)(callee)) {
return false;
}
return couldBeType(callee.object, "Promise");
}
return {
"CallExpression[callee.property.name='catch']": (callExpression) => {
const [callback] = callExpression.arguments;
if (callback) {
checkRejectionCallback(callExpression, callback);
}
},
"CallExpression[callee.property.name='then']": (callExpression) => {
const [, callback] = callExpression.arguments;
if (callback) {
checkRejectionCallback(callExpression, callback);
}
},
};
},
});
module.exports = rule;