UNPKG

@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
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 };