@storm-software/eslint
Version:
A package containing the base ESLint configuration used by Storm Software across many projects.
459 lines (444 loc) • 14.7 kB
JavaScript
;
var utils = require('@typescript-eslint/utils');
var os = require('os');
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
var os__default = /*#__PURE__*/_interopDefault(os);
// src/utils/banner-plugin.ts
// src/utils/constants.ts
var ACRONYMS_LIST = [
"API",
"ASCII",
"CPU",
"CSS",
"DNS",
"EOF",
"GUID",
"HTML",
"HTTP",
"HTTPS",
"ID",
"IP",
"JSON",
"LHS",
"OEM",
"PP",
"QA",
"RAM",
"RHS",
"RPC",
"RSS",
"SLA",
"SMTP",
"SQL",
"SSH",
"SSL",
"TCP",
"TLS",
"TTL",
"UDP",
"UI",
"UID",
"UUID",
"URI",
"URL",
"UTF",
"VM",
"XML",
"XSS"
];
var GLOB_SRC_EXT = "?([cm])[jt]s?(x)";
var GLOB_SRC_FILE = `*.${GLOB_SRC_EXT}`;
var GLOB_SRC = `**/${GLOB_SRC_FILE}`;
// src/utils/get-file-banner.ts
var getFileBanner = (name = "", workspaceConfig) => {
if (!name) {
name = process.env.STORM_NAME || "";
}
let padding = " ";
for (let i = 0; i < name.length + 2 && padding.length > 4; i++) {
padding = padding.slice(0, -1);
}
let titleName = name || workspaceConfig?.name;
if (titleName) {
if (titleName?.startsWith("@")) {
titleName = titleName.slice(1);
}
titleName = (titleName.charAt(0).toUpperCase() + titleName.slice(1)).split("-").filter((word) => word && word.length > 0).map((word) => {
if (ACRONYMS_LIST.includes(word.toUpperCase())) {
return word.toUpperCase();
}
return word.charAt(0).toUpperCase() + word.slice(1);
}).join(" ");
}
const license = (process.env.STORM_LICENSE || workspaceConfig?.license || "Apache-2.0").split(" ").filter((word) => word && word.toLowerCase() !== "license").join(" ");
const organization = process.env.STORM_ORG_NAME || process.env.STORM_ORGANIZATION_NAME || process.env.STORM_ORG || process.env.STORM_ORGANIZATION || (void 0) || "storm-software";
return ` -------------------------------------------------------------------
${padding}\u26A1 ${(organization.charAt(0).toUpperCase() + organization.slice(1)).split("-").filter((word) => word && word.length > 0).map((word) => {
if (ACRONYMS_LIST.includes(word.toUpperCase())) {
return word.toUpperCase();
}
return word.charAt(0).toUpperCase() + word.slice(1);
}).join(" ")} ${titleName ? `- ${titleName}` : ""}
This code was released as part of ${titleName ? `the ${titleName}` : `a ${(organization.charAt(0).toUpperCase() + organization.slice(1)).split("-").filter((word) => word && word.length > 0).map((word) => {
if (ACRONYMS_LIST.includes(word.toUpperCase())) {
return word.toUpperCase();
}
return word.charAt(0).toUpperCase() + word.slice(1);
}).join(" ")}`} project. ${titleName ? titleName : "The project"}
is maintained by ${(organization.charAt(0).toUpperCase() + organization.slice(1)).split("-").filter((word) => word && word.length > 0).map((word) => {
if (ACRONYMS_LIST.includes(word.toUpperCase())) {
return word.toUpperCase();
}
return word.charAt(0).toUpperCase() + word.slice(1);
}).join(" ")} under the ${license} license, and is
free for commercial and private use. For more information, please visit
our licensing page at ${process.env.STORM_LICENSING?.replace(/\/$/, "") || workspaceConfig?.licensing?.replace(/\/$/, "") || "https://stormsoftware.com/licenses"}/${name ? `projects/${name}` : ""}.
Website: ${process.env.STORM_HOMEPAGE || workspaceConfig?.homepage || "https://stormsoftware.com"}
Repository: ${process.env.STORM_REPOSITORY || workspaceConfig?.repository || `https://github.com/${organization}${name ? `/${name}` : ""}`}
Documentation: ${process.env.STORM_DOCS || workspaceConfig?.docs || `https://docs.stormsoftware.com${name ? `/projects/${name}` : ""}`}
Contact: ${(process.env.STORM_HOMEPAGE || workspaceConfig?.homepage || "https://stormsoftware.com").endsWith("/") ? (process.env.STORM_HOMEPAGE || workspaceConfig?.homepage || "https://stormsoftware.com").slice(-1) : process.env.STORM_HOMEPAGE || workspaceConfig?.homepage || "https://stormsoftware.com"}/contact
SPDX-License-Identifier: ${license}
------------------------------------------------------------------- `;
};
// src/utils/banner-plugin.ts
function match(actual, expected) {
if (expected.test) {
return expected.test(actual);
} else {
return expected === actual;
}
}
function excludeShebangs(comments) {
return comments.filter((comment) => {
return comment.type !== "Shebang";
});
}
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);
}
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;
}
}
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];
}
function genPrependFixer(commentType, node, bannerLines, eol, numNewlines) {
return function(fixer) {
return fixer.insertTextBefore(
node,
genCommentBody(commentType, bannerLines, eol, numNewlines)
);
};
}
function genReplaceFixer(commentType, context, leadingComments, bannerLines, eol, numNewlines) {
return function(fixer) {
return fixer.replaceTextRange(
genCommentsRange(context, leadingComments, eol),
genCommentBody(commentType, bannerLines, eol, numNewlines)
);
};
}
function getEOL(options) {
if (options.lineEndings === "unix") {
return "\n";
}
if (options.lineEndings === "windows") {
return "\r\n";
}
return os__default.default.EOL;
}
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);
}
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;
}
var bannerRule = utils.ESLintUtils.RuleCreator(
() => `https://developer.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"
},
name: {
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: [
{
name: "",
commentType: "block",
numNewlines: 2,
lineEndings: "unix"
}
],
create(context, [
{
banner,
name = "",
commentType = "block",
numNewlines = 2,
lineEndings = "unix"
}
]) {
if (!banner) {
banner = getFileBanner(name);
}
const options = context.options;
const eol = getEOL({ lineEndings, ...options });
const bannerLines = banner.split(/\r?\n/);
let fixLines = bannerLines;
return {
Program: 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: genPrependFixer(commentType, node, fixLines, eol, numNewlines)
});
} else if (leadingComments[0].type.toLowerCase() !== commentType) {
context.report({
loc: node.loc,
messageId: "incorrectComment",
data: {
commentType
},
fix: genReplaceFixer(
commentType,
context,
leadingComments,
fixLines,
eol,
numNewlines
)
});
} else {
if (commentType === "line") {
if (leadingComments.length < bannerLines.length) {
context.report({
loc: node.loc,
messageId: "missingBanner",
fix: genReplaceFixer(
commentType,
context,
leadingComments,
fixLines,
eol,
numNewlines
)
});
return;
}
for (let i = 0; i < bannerLines.length; i++) {
if (!match(leadingComments[i].value, bannerLines[i])) {
context.report({
loc: node.loc,
messageId: "incorrectBanner",
fix: genReplaceFixer(
commentType,
context,
leadingComments,
fixLines,
eol,
numNewlines
)
});
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: genReplaceFixer(
commentType,
context,
leadingComments,
fixLines,
eol,
numNewlines
)
});
}
} 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 (bannerLines.length > 1) {
fixLines = [fixLines.join(eol)];
}
context.report({
loc: node.loc,
messageId: "incorrectBanner",
fix: genReplaceFixer(
commentType,
context,
leadingComments,
fixLines,
eol,
numNewlines
)
});
} 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: genReplaceFixer(
commentType,
context,
leadingComments,
fixLines,
eol,
numNewlines
)
});
}
}
}
}
}
}
};
}
});
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;
module.exports = banner_plugin_default;
module.exports = exports.default;