eslint-plugin-ts-ban-snippets
Version:
Ban snippets of TypeScript from your project, using a custom eslint rule 'ts-ban-snippets'.
159 lines (158 loc) • 6.02 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const experimental_utils_1 = require("@typescript-eslint/experimental-utils");
// ref = https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin-tslint/src/rules/config.ts
const createRule = experimental_utils_1.ESLintUtils.RuleCreator((name) => {
return `https://github.com/mrseanryan/eslint-plugin-ts-ban-snippets/blob/main/docs/${name}.md`;
});
const BannedSnippetMessage = "'{{name}}' is a banned code snippet - {{message}} [{{ruleName}}]";
function isFileInPaths(filePath, paths) {
return paths.some((path) => filePath.indexOf(path) >= 0);
}
const isRegexMatch = (regexRaw, text) => {
const regex = new RegExp(regexRaw);
return regex.test(text);
};
const filterSnippets = (bannedSnippetsRaw, isRegex, text) => {
const filterFun = (s) => {
if (isRegex)
return isRegexMatch(s, text);
return text.startsWith(s);
};
return bannedSnippetsRaw.filter(filterFun);
};
const analyzeNodeFor = (node, banned, context, alreadyReported) => {
const fileName = node.getSourceFile().fileName;
if (!!banned.includePaths && !isFileInPaths(fileName, banned.includePaths))
return;
if (!!banned.excludePaths && isFileInPaths(fileName, banned.excludePaths))
return;
const text = node.getText();
node.getStart();
node.getEnd();
node.getSourceFile().getLineAndCharacterOfPosition(node.pos);
if (!!banned.snippets && !!banned.regexSnippets) {
throw new Error("Invalid config: 'snippets' and 'regexSnippets' are mutually exclusive.");
}
const bannedSnippetsRaw = banned.snippets || banned.regexSnippets || [];
const isRegex = !!banned.regexSnippets;
const bannedSnippets = filterSnippets(bannedSnippetsRaw, isRegex, text);
if (bannedSnippets.length > 0) {
const pos = node.getSourceFile().getLineAndCharacterOfPosition(node.pos);
reportOnlyOnce(context, pos, bannedSnippets[0], banned, alreadyReported);
}
node.forEachChild((c) => analyzeNodeFor(c, banned, context, alreadyReported));
};
exports.default = createRule({
name: "ts-ban-snippets",
meta: {
type: "problem",
docs: {
description: "This TypeScript snippet is banned and should not be used.",
category: "Possible Errors",
recommended: "error",
},
fixable: "code",
messages: {
BannedSnippetMessage,
},
schema: [
{
type: "object",
properties: {
banned: {
type: "array",
items: {
type: "object",
properties: {
snippets: {
type: "array",
items: {
type: "string",
},
},
// regexSnippets and snippets are mutually exclusive
regexSnippets: {
type: "array",
items: {
type: "string",
},
},
message: {
type: "string",
},
includePaths: {
type: "array",
items: {
type: "string",
},
},
excludePaths: {
type: "array",
items: {
type: "string",
},
},
},
additionalProperties: false,
},
},
},
additionalProperties: false,
},
],
},
defaultOptions: [{ banned: [] }],
create: (context) => {
if (!context.options[0]) {
return {
Program: () => {
// do nothing
},
};
}
const parserServices = experimental_utils_1.ESLintUtils.getParserServices(context);
/**
* The TSLint rules configuration passed in by the user
*/
const banned = context.options[0].banned;
return {
Program: (node) => {
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
// prevent duplicate errors:
const alreadyReported = [];
tsNode.forEachChild((child) => {
banned.forEach((b) => analyzeNodeFor(child, b, context, alreadyReported));
});
},
};
},
});
function clone(obj) {
return JSON.parse(JSON.stringify(obj, null, 0));
}
function isAlreadyReported(id, alreadyReported) {
const problemClone = clone(id);
const json = JSON.stringify(problemClone, null, 0);
if (alreadyReported.includes(json))
return true;
alreadyReported.push(json);
return false;
}
function reportOnlyOnce(context, pos, bannedSnippet, banned, alreadyReported) {
const loc = {
line: pos.line,
column: pos.character,
};
const problem = {
loc: loc,
messageId: "BannedSnippetMessage",
data: {
name: bannedSnippet,
message: banned.message,
ruleName: "ts-ban-snippets",
},
};
if (!isAlreadyReported(loc, alreadyReported))
context.report(problem);
}