hereby
Version:
A simple task runner
87 lines (86 loc) • 4.13 kB
JavaScript
import * as style from "./style.js";
import { compareTaskNames } from "./utils.js";
export function formatTasks(format, tasks, defaultTask, columns) {
const visibleTasks = [...tasks].filter(isTaskVisible).sort(compareTaskNames);
if (format === "simple") {
return visibleTasks.map((task) => task.options.name).join("\n");
}
const rows = visibleTasks.map((task) => {
var _a;
const name = task === defaultTask
? `${style.green(task.options.name)} (default)`
: style.blue(task.options.name);
const parts = task.options.description ? [task.options.description] : [];
const deps = (_a = task.options.dependencies) === null || _a === void 0 ? void 0 : _a.filter(isTaskVisible).sort(compareTaskNames);
if (deps === null || deps === void 0 ? void 0 : deps.length) {
parts.push(`Depends on: ${deps.map((dep) => style.blue(dep.options.name)).join(", ")}`);
}
return { name, description: parts.join("\n") };
});
// There's a 2 space indent plus 3 spaces between columns, hence take away 5
// padding spaces from the available width. Keep widths positive so narrow
// terminals still wrap safely.
const maxTotalWidth = Math.max(1, columns - 5);
const maxNameWidth = Math.max(1, ...rows.map((row) => visibleLength(row.name)));
// Check the name doesn't take up more than half the space
const nameWidth = Math.min(maxNameWidth, Math.max(1, maxTotalWidth >> 1));
const descriptionWidth = Math.max(1, maxTotalWidth - nameWidth);
const formatted = rows.map((row) => formatAsColumns(" ", row.name, nameWidth, row.description, descriptionWidth));
return `
${style.bold(style.underline("Available tasks"))}
${formatted.join("")}`;
}
function isTaskVisible(task) {
return !task.options.hiddenFromTaskList;
}
function formatAsColumns(indent, leftText, leftWidth, rightText, rightWidth) {
var _a, _b;
const leftLines = wrapText(leftText, leftWidth);
const rightLines = wrapText(rightText, rightWidth);
const maxLines = Math.max(leftLines.length, rightLines.length);
let result = "";
for (let i = 0; i < maxLines; i++) {
const leftPart = (_a = leftLines[i]) !== null && _a !== void 0 ? _a : "";
const rightPart = (_b = rightLines[i]) !== null && _b !== void 0 ? _b : "";
// Only pad the left column when there's something to the right of it,
// so we don't emit trailing whitespace. The clamp is needed because
// wrapText chunks long tokens by code point, while visibleLength counts
// UTF-16 code units; astral characters can overshoot leftWidth.
const paddedLeft = leftPart + " ".repeat(Math.max(0, leftWidth - visibleLength(leftPart)));
result += rightPart ? `${indent}${paddedLeft} ${rightPart}\n` : `${indent}${leftPart}\n`;
}
return result;
}
// oxlint-disable-next-line no-control-regex
const ANSI_REGEX = /\u001B\[([0-9]{1,2})m/g;
function visibleLength(str) {
return str.replace(ANSI_REGEX, "").length;
}
const TOKEN_REGEX = /[^\s-]+?-\b|\S+|\s+/g;
function wrapText(text, maxWidth) {
var _a;
const result = [];
for (const line of text.split(/\r?\n/)) {
let current = "";
for (const token of (_a = line.match(TOKEN_REGEX)) !== null && _a !== void 0 ? _a : []) {
if (visibleLength(current) + visibleLength(token) > maxWidth && current) {
result.push(current.trim());
current = "";
}
if (visibleLength(token) > maxWidth) {
// oxlint-disable-next-line typescript/no-misused-spread
const chars = [...token];
while (chars.length > 0)
result.push(chars.splice(0, maxWidth).join(""));
}
else {
// Drop whitespace tokens that would otherwise start a line.
current = current ? current + token : token.trimStart();
}
}
if (current)
result.push(current.trim());
}
return result;
}
//# sourceMappingURL=formatTasks.js.map