@storm-software/eslint
Version:
⚡ A package containing the base ESLint configuration used by Storm Software across many projects.
327 lines (324 loc) • 10.4 kB
JavaScript
import {
getFileBanner
} from "./chunk-XNCBYAYL.js";
import {
GLOB_SRC
} from "./chunk-SRHSKJB5.js";
import {
__name
} from "./chunk-SHUYVCID.js";
// src/utils/banner-plugin.ts
import { ESLintUtils } from "@typescript-eslint/utils";
import os from "node:os";
function match(actual, expected) {
if (expected.test) {
return expected.test(actual);
} else {
return expected === actual;
}
}
__name(match, "match");
function excludeShebangs(comments) {
return comments.filter((comment) => {
return comment.type !== "Shebang";
});
}
__name(excludeShebangs, "excludeShebangs");
function getLeadingComments(context, node) {
const all = excludeShebangs(context.getSourceCode().getAllComments(node.body.length ? node.body[0] : node));
if (all[0].type.toLowerCase() === "block") {
return [
all[0]
];
}
let i = 1;
for (i = 1; i < all.length; ++i) {
const txt = context.getSourceCode().getText().slice(all[i - 1].range[1], all[i].range[0]);
if (!txt.match(/^(\r\n|\r|\n)$/)) {
break;
}
}
return all.slice(0, i);
}
__name(getLeadingComments, "getLeadingComments");
function genCommentBody(commentType, textArray, eol, numNewlines) {
const eols = eol.repeat(numNewlines);
if (commentType === "block") {
return "/*" + textArray.join(eol) + "*/" + eols;
} else {
return "//" + textArray.join(eol + "//") + eols;
}
}
__name(genCommentBody, "genCommentBody");
function genCommentsRange(context, comments, eol) {
const start = comments[0].range[0];
let end = comments.slice(-1)[0].range[1];
if (context.getSourceCode().text[end] === eol) {
end += eol.length;
}
return [
start,
end
];
}
__name(genCommentsRange, "genCommentsRange");
function genPrependFixer(commentType, node, bannerLines, eol, numNewlines) {
return function(fixer) {
return fixer.insertTextBefore(node, genCommentBody(commentType, bannerLines, eol, numNewlines));
};
}
__name(genPrependFixer, "genPrependFixer");
function genReplaceFixer(commentType, context, leadingComments, bannerLines, eol, numNewlines) {
return function(fixer) {
return fixer.replaceTextRange(genCommentsRange(context, leadingComments, eol), genCommentBody(commentType, bannerLines, eol, numNewlines));
};
}
__name(genReplaceFixer, "genReplaceFixer");
function getEOL(options) {
if (options.lineEndings === "unix") {
return "\n";
}
if (options.lineEndings === "windows") {
return "\r\n";
}
return os.EOL;
}
__name(getEOL, "getEOL");
function hasBanner(commentType = "block", src, eol) {
if (src.startsWith("#!")) {
const bannerLines = src.split(eol);
if (bannerLines && bannerLines.length > 1) {
bannerLines.shift();
while (bannerLines.length && bannerLines[0] && !bannerLines[0].trim()) {
bannerLines.shift();
}
if (bannerLines.length) {
src = bannerLines.join(eol);
} else {
return false;
}
}
}
return commentType === "block" && src.startsWith("/*") || commentType === "line" && src.startsWith("//") || commentType !== "block" && commentType !== "line" && commentType && src.startsWith(commentType);
}
__name(hasBanner, "hasBanner");
function matchesLineEndings(src, num) {
for (let j = 0; j < num; ++j) {
const m = src.match(/^(\r\n|\r|\n)/);
if (m) {
src = src.slice(m.index + m[0].length);
} else {
return false;
}
}
return true;
}
__name(matchesLineEndings, "matchesLineEndings");
var bannerRule = ESLintUtils.RuleCreator(() => `https://docs.stormsoftware.com/eslint/rules/banner`)({
name: "banner",
meta: {
docs: {
description: "Ensures the file has a organization specific banner at the top of source code files"
},
schema: [
{
type: "object",
properties: {
banner: {
type: "string",
description: "The banner to enforce at the top of the file. If not provided, the banner will be read from the file specified in the commentStart option"
},
repositoryName: {
type: "string",
description: "The name of the repository to use when reading the banner from a file."
},
commentType: {
type: "string",
description: "The comment token to use for the banner. Defaults to block ('/* <banner> */')"
},
numNewlines: {
type: "number",
description: "The number of newlines to use after the banner. Defaults to 1"
},
lineEndings: {
type: "string",
enum: [
"unix",
"windows"
],
description: "The type of line endings to use. Defaults to the system default"
}
},
additionalProperties: false
}
],
type: "layout",
messages: {
missingBanner: "Missing banner",
incorrectComment: "Banner should use the {{commentType}} comment type",
incorrectBanner: "Incorrect banner",
noNewlineAfterBanner: "No newline after banner"
},
fixable: "whitespace"
},
defaultOptions: [
{
repositoryName: "",
commentType: "block",
numNewlines: 2,
lineEndings: "unix"
}
],
create(context, [{ banner, repositoryName = "", commentType = "block", numNewlines = 2, lineEndings = "unix" }]) {
if (!banner) {
banner = getFileBanner(repositoryName);
}
const options = context.options;
const eol = getEOL({
lineEndings,
...options
});
const canFix = true;
const bannerLines = banner.split(/\r?\n/);
let fixLines = bannerLines;
return {
Program: /* @__PURE__ */ __name(function(node) {
if (!hasBanner(commentType, context.sourceCode.getText(), eol)) {
context.report({
loc: node.loc,
messageId: "missingBanner",
fix: genPrependFixer(commentType, node, fixLines, eol, numNewlines)
});
} else {
const leadingComments = getLeadingComments(context, node);
if (!leadingComments.length) {
context.report({
loc: node.loc,
messageId: "missingBanner",
fix: canFix ? genPrependFixer(commentType, node, fixLines, eol, numNewlines) : null
});
} else if (leadingComments[0].type.toLowerCase() !== commentType) {
context.report({
loc: node.loc,
messageId: "incorrectComment",
data: {
commentType
},
fix: canFix ? genReplaceFixer(commentType, context, leadingComments, fixLines, eol, numNewlines) : null
});
} else {
if (commentType === "line") {
if (leadingComments.length < bannerLines.length) {
context.report({
loc: node.loc,
messageId: "missingBanner",
fix: canFix ? genReplaceFixer(commentType, context, leadingComments, fixLines, eol, numNewlines) : null
});
return;
}
for (let i = 0; i < bannerLines.length; i++) {
if (!match(leadingComments[i].value, bannerLines[i])) {
context.report({
loc: node.loc,
messageId: "incorrectBanner",
fix: canFix ? genReplaceFixer(commentType, context, leadingComments, fixLines, eol, numNewlines) : null
});
return;
}
}
const postLineBanner = context.getSourceCode().text.substr(leadingComments[bannerLines.length - 1].range[1], (numNewlines ?? 1) * 2);
if (!matchesLineEndings(postLineBanner, numNewlines)) {
context.report({
loc: node.loc,
messageId: "noNewlineAfterBanner",
fix: canFix ? genReplaceFixer(commentType, context, leadingComments, fixLines, eol, numNewlines) : null
});
}
} else {
let leadingLines = [
leadingComments[0].value
];
if (bannerLines.length > 1) {
leadingLines = leadingComments[0].value.split(/\r?\n/);
}
let hasError = false;
if (leadingLines.length > bannerLines.length) {
hasError = true;
}
for (let i = 0; !hasError && i < bannerLines.length; i++) {
if (!match(leadingLines[i], bannerLines[i])) {
hasError = true;
}
}
if (hasError) {
if (canFix && bannerLines.length > 1) {
fixLines = [
fixLines.join(eol)
];
}
context.report({
loc: node.loc,
messageId: "incorrectBanner",
fix: canFix ? genReplaceFixer(commentType, context, leadingComments, fixLines, eol, numNewlines) : null
});
} else {
const postBlockBanner = context.getSourceCode().text.substr(leadingComments[0].range[1], (numNewlines ?? 1) * 2);
if (!matchesLineEndings(postBlockBanner, numNewlines)) {
context.report({
loc: node.loc,
messageId: "noNewlineAfterBanner",
fix: canFix ? genReplaceFixer(commentType, context, leadingComments, fixLines, eol, numNewlines) : null
});
}
}
}
}
}
}, "Program")
};
}
});
var plugin = {
meta: {
name: "eslint-plugin-banner",
version: "0.0.1"
},
configs: {},
rules: {
banner: bannerRule
},
processors: {}
};
plugin.configs && (plugin.configs.recommended = {
name: "banner/recommended",
plugins: {
banner: plugin
},
files: [
GLOB_SRC
],
ignores: [
"!**/docs/**/*",
"!**/crates/**/*",
"!**/tmp/**/*",
"!**/dist/**/*",
"!**/coverage/**/*",
"!**/node_modules/**/*",
"!**/.cache/**/*",
"!**/.nx/**/*",
"!**/.storm/**/*"
],
rules: {
"banner/banner": [
"error",
{
commentType: "block",
numNewlines: 2
}
]
}
});
var banner_plugin_default = plugin;
export {
banner_plugin_default
};