eslint-plugin-etc
Version:
More general-purpose ESLint rules
143 lines (142 loc) • 5.2 kB
JavaScript
const utils_1 = require("../utils");
const rule = (0, utils_1.ruleCreator)({
defaultOptions: [],
meta: {
docs: {
description: "Forbids commented-out code.",
recommended: false,
},
fixable: undefined,
hasSuggestions: false,
messages: {
forbidden: "Commented-out code is forbidden.",
},
schema: [],
type: "problem",
},
name: "no-commented-out-code",
create: (context) => {
const { parse } = require(context.parserPath);
const { project, ...parserOptions } = context.parserOptions;
const sourceCode = context.getSourceCode();
return {
Program: () => {
const comments = context.getSourceCode().getAllComments();
const blocks = toBlocks(comments);
for (const block of blocks) {
const { content, loc } = block;
if (isRegionComment(content)) {
continue;
}
try {
const program = parse(content, parserOptions);
if (!hasEmptyBody(program) &&
!hasExpressionBody(program) &&
!hasLabeledStatementBody(program)) {
context.report({
loc,
messageId: "forbidden",
});
}
continue;
}
catch (error) { }
const index = sourceCode.getIndexFromLoc(loc.start);
const node = sourceCode.getNodeByRangeIndex(index);
const wrappedContent = wrapContent(content, node);
if (wrappedContent) {
try {
parse(wrappedContent, parserOptions);
context.report({
loc,
messageId: "forbidden",
});
}
catch (error) { }
}
}
},
};
},
});
function hasEmptyBody(program) {
return program.type === "Program" && program.body.length === 0;
}
function hasExpressionBody(program) {
return (program.type === "Program" &&
program.body.every((statement) => statement.type === "ExpressionStatement" &&
isExpressionOrIdentifierOrLiteral(statement.expression)));
}
function hasLabeledStatementBody(program) {
return (program.type === "Program" &&
program.body.length === 1 &&
program.body[0].type === "LabeledStatement");
}
function isExpressionOrIdentifierOrLiteral(node) {
if (node.type === "Identifier") {
return true;
}
if (node.type === "Literal") {
return true;
}
if (node.type === "BinaryExpression") {
return (isExpressionOrIdentifierOrLiteral(node.left) &&
isExpressionOrIdentifierOrLiteral(node.right));
}
return false;
}
function isRegionComment(content) {
return /\s*#(end)?region/.test(content);
}
function toBlocks(comments) {
const blocks = [];
let prevLine;
for (const comment of comments) {
if (comment.type === "Block") {
blocks.push({
content: comment.value.replace(/^\s*\*/, "").replace(/\n\s*\*/g, "\n"),
loc: { ...comment.loc },
});
prevLine = undefined;
}
else if (comment.type === "Line") {
if (prevLine && prevLine.loc.start.line === comment.loc.start.line - 1) {
const prevBlock = blocks[blocks.length - 1];
prevBlock.content += `\n${comment.value}`;
prevBlock.loc.end = comment.loc.end;
}
else {
blocks.push({
content: comment.value,
loc: { ...comment.loc },
});
}
prevLine = comment;
}
}
return blocks;
}
function wrapContent(content, node) {
switch (node === null || node === void 0 ? void 0 : node.type) {
case "ArrayExpression":
return `let wrapper = [${content}]`;
case "ClassBody":
return `class Wrapper { ${content} }`;
case "ImportDeclaration":
return `import { ${content} } from "wrapper"`;
case "ObjectExpression":
return `let wrapper = { ${content} }`;
case "FunctionDeclaration":
return `function wrapper(${content}) {}`;
case "SwitchStatement":
return `switch (wrapper) { ${content} }`;
case "TSInterfaceBody":
return `interface Wrapper { ${content} }`;
case "TSTypeLiteral":
return `type Wrapper = { ${content} }`;
default:
return undefined;
}
}
module.exports = rule;
;