packages/cli/src/components/version-table/index.ts
import type { RefinePackageInstalledVersionData } from "@definitions/package";
import Table from "cli-table3";
import chalk from "chalk";
import { removeANSIColors } from "@utils/text";
const columns = {
name: "name",
current: "current",
wanted: "wanted",
latest: "latest",
changelog: "changelog",
} as const;
const orderedColumns: (typeof columns)[keyof typeof columns][] = [
columns.name,
columns.current,
columns.wanted,
columns.latest,
columns.changelog,
];
export const getVersionTable = (
packages: RefinePackageInstalledVersionData[] = [],
) => {
const tableHead = Object.keys(packages?.[0] || {});
if (!packages || !tableHead.length) return { table: "", width: 0 };
const terminalWidth = process.stdout.columns || 80;
const nameColumnWidth =
Math.max(...packages.map((row) => row.name.length)) + 2;
const versionColumnWidth = 7 + 2;
const bordersWidth = 6;
const changelogColumnWidth = Math.min(
35,
terminalWidth - nameColumnWidth - versionColumnWidth * 3 - bordersWidth,
);
const columnWidths = {
name: nameColumnWidth,
current: versionColumnWidth,
wanted: versionColumnWidth,
latest: versionColumnWidth,
changelog: changelogColumnWidth,
} as const;
const table = new Table({
head: orderedColumns,
wordWrap: false,
wrapOnWordBoundary: true,
colWidths: orderedColumns.map((column) => columnWidths[column]),
style: {
head: ["blue"],
},
});
const ellipsisFromCenter = (text: string, length: number) => {
// if text is longer than length, cut it from the center to fit the length (add ellipsis)
if (text.length > length) {
const fitLength = length - 3;
const start = text.slice(0, Math.floor(fitLength / 2));
const end = text.slice(-Math.ceil(fitLength / 2));
return `${start}...${end}`;
}
return text;
};
packages.forEach((row) => {
table.push(
orderedColumns.map((column) => {
const columnValue = row[column];
if (!columnValue) return columnValue;
if (column === "latest" || column === "wanted") {
const installedVersion = parseVersions(row.current);
const latestVersion = parseVersions(columnValue);
const colors = getColorsByVersionDiffrence(
installedVersion,
latestVersion,
);
const textMajor = chalk[colors.major](latestVersion.major);
const textMinor = chalk[colors.minor](latestVersion.minor);
const textPatch = chalk[colors.patch](latestVersion.patch);
return `${textMajor}.${textMinor}.${textPatch}`;
}
if (column === "changelog") {
return chalk.blueBright.underline(columnValue);
}
return columnValue;
}),
);
});
const tableString = table.toString();
const tableWidth = removeANSIColors(
tableString.split("\n")?.[0] || "",
).length;
return { table: tableString, width: tableWidth };
};
const parseVersions = (text: string) => {
const versions = text.split(".");
return {
major: versions[0],
minor: versions[1],
patch: versions[2],
};
};
const getColorsByVersionDiffrence = (
installedVersion: ReturnType<typeof parseVersions>,
nextVersion: ReturnType<typeof parseVersions>,
) => {
const isMajorDiffrence =
installedVersion.major.trim() !== nextVersion.major.trim();
if (isMajorDiffrence)
return {
major: "red",
minor: "red",
patch: "red",
} as const;
const isMinorDiffrence =
installedVersion.minor.trim() !== nextVersion.minor.trim();
if (isMinorDiffrence)
return {
major: "white",
minor: "yellow",
patch: "yellow",
} as const;
const isPatchDiffrence =
installedVersion.patch.trim() !== nextVersion.patch.trim();
if (isPatchDiffrence)
return {
major: "white",
minor: "white",
patch: "green",
} as const;
return {
major: "white",
minor: "white",
patch: "white",
} as const;
};