mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-09 12:57:46 +00:00
[cli] Unify table formatting output (#11387)
This removes 'text-table' as a dependency in favor of using 'cli-table3', and simplifies table formatting logic.
This commit is contained in:
@@ -83,7 +83,6 @@
|
||||
"@types/qs": "6.9.7",
|
||||
"@types/semver": "6.0.1",
|
||||
"@types/tar-fs": "1.16.1",
|
||||
"@types/text-table": "0.2.0",
|
||||
"@types/title": "3.4.1",
|
||||
"@types/universal-analytics": "0.4.2",
|
||||
"@types/update-notifier": "5.1.0",
|
||||
@@ -162,7 +161,6 @@
|
||||
"strip-ansi": "6.0.1",
|
||||
"supports-hyperlinks": "3.0.0",
|
||||
"tar-fs": "1.16.3",
|
||||
"text-table": "0.2.0",
|
||||
"title": "3.4.1",
|
||||
"tmp-promise": "1.0.3",
|
||||
"tree-kill": "1.2.2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import chalk from 'chalk';
|
||||
import ms from 'ms';
|
||||
import table from 'text-table';
|
||||
import table from '../../util/output/table';
|
||||
import Client from '../../util/client';
|
||||
import getAliases from '../../util/alias/get-aliases';
|
||||
import getScope from '../../util/get-scope';
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
getPaginationOpts,
|
||||
} from '../../util/get-pagination-opts';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import strlen from '../../util/strlen';
|
||||
import getCommandFlags from '../../util/get-command-flags';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
import type { Alias } from '@vercel-internals/types';
|
||||
@@ -78,10 +77,6 @@ function printAliasTable(aliases: Alias[]) {
|
||||
ms(Date.now() - a.createdAt),
|
||||
]),
|
||||
],
|
||||
{
|
||||
align: ['l', 'l', 'r'],
|
||||
hsep: ' '.repeat(4),
|
||||
stringLength: strlen,
|
||||
}
|
||||
{ align: ['l', 'l', 'r'], hsep: 4 }
|
||||
).replace(/^/gm, ' ')}\n\n`;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import chalk from 'chalk';
|
||||
import ms from 'ms';
|
||||
import table from 'text-table';
|
||||
import table from '../../util/output/table';
|
||||
import Client from '../../util/client';
|
||||
import getScope from '../../util/get-scope';
|
||||
import removeAliasById from '../../util/alias/remove-alias-by-id';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import strlen from '../../util/strlen';
|
||||
import confirm from '../../util/input/confirm';
|
||||
import findAliasByAliasOrId from '../../util/alias/find-alias-by-alias-or-id';
|
||||
|
||||
@@ -84,11 +83,7 @@ async function confirmAliasRemove(client: Client, alias: Alias) {
|
||||
chalk.gray(`${ms(Date.now() - alias.createdAt)} ago`),
|
||||
],
|
||||
],
|
||||
{
|
||||
align: ['l', 'l', 'r'],
|
||||
hsep: ' '.repeat(4),
|
||||
stringLength: strlen,
|
||||
}
|
||||
{ hsep: 4 }
|
||||
);
|
||||
|
||||
client.output.log(`The following alias will be removed permanently`);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import chalk from 'chalk';
|
||||
import ms from 'ms';
|
||||
import table from 'text-table';
|
||||
import table from '../../util/output/table';
|
||||
import Client from '../../util/client';
|
||||
import getScope from '../../util/get-scope';
|
||||
import {
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
} from '../../util/get-pagination-opts';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import getCerts from '../../util/certs/get-certs';
|
||||
import strlen from '../../util/strlen';
|
||||
import type { Cert } from '@vercel-internals/types';
|
||||
import getCommandFlags from '../../util/get-command-flags';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
@@ -70,11 +69,7 @@ async function ls(
|
||||
function formatCertsTable(certsList: Cert[]) {
|
||||
return `${table(
|
||||
[formatCertsTableHead(), ...formatCertsTableBody(certsList)],
|
||||
{
|
||||
align: ['l', 'l', 'r', 'c', 'r'],
|
||||
hsep: ' '.repeat(2),
|
||||
stringLength: strlen,
|
||||
}
|
||||
{ align: ['l', 'l', 'r', 'c', 'r'], hsep: 2 }
|
||||
).replace(/^(.*)/gm, ' $1')}\n`;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import chalk from 'chalk';
|
||||
import ms from 'ms';
|
||||
import plural from 'pluralize';
|
||||
import table from 'text-table';
|
||||
import table from '../../util/output/table';
|
||||
import type { Cert } from '@vercel-internals/types';
|
||||
import * as ERRORS from '../../util/errors-ts';
|
||||
import { Output } from '../../util/output';
|
||||
@@ -98,7 +98,7 @@ function readConfirmation(output: Output, msg: string, certs: Cert[]) {
|
||||
output.print(
|
||||
`${table(certs.map(formatCertRow), {
|
||||
align: ['l', 'r', 'l'],
|
||||
hsep: ' '.repeat(6),
|
||||
hsep: 6,
|
||||
}).replace(/^(.*)/gm, ' $1')}\n`
|
||||
);
|
||||
output.print(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import chalk from 'chalk';
|
||||
import ms from 'ms';
|
||||
import table from 'text-table';
|
||||
import table from '../../util/output/table';
|
||||
import type { DNSRecord } from '@vercel-internals/types';
|
||||
import { Output } from '../../util/output';
|
||||
import Client from '../../util/client';
|
||||
@@ -71,7 +71,7 @@ function readConfirmation(
|
||||
output.print(
|
||||
`${table([getDeleteTableRow(domainName, record)], {
|
||||
align: ['l', 'r', 'l'],
|
||||
hsep: ' '.repeat(6),
|
||||
hsep: 6,
|
||||
}).replace(/^(.*)/gm, ' $1')}\n`
|
||||
);
|
||||
output.print(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import chalk from 'chalk';
|
||||
import { LOGO, NAME } from '@vercel-internals/constants';
|
||||
import Table, { CellOptions } from 'cli-table3';
|
||||
import { noBorderChars } from '../util/output/table';
|
||||
|
||||
const INDENT = ' '.repeat(2);
|
||||
const NEWLINE = '\n';
|
||||
@@ -39,23 +40,7 @@ type _CellOptions = CellOptions & {
|
||||
};
|
||||
|
||||
const tableOptions = {
|
||||
chars: {
|
||||
top: '',
|
||||
'top-mid': '',
|
||||
'top-left': '',
|
||||
'top-right': '',
|
||||
bottom: '',
|
||||
'bottom-mid': '',
|
||||
'bottom-left': '',
|
||||
'bottom-right': '',
|
||||
left: '',
|
||||
'left-mid': '',
|
||||
mid: '',
|
||||
'mid-mid': '',
|
||||
right: '',
|
||||
'right-mid': '',
|
||||
middle: '',
|
||||
},
|
||||
chars: noBorderChars,
|
||||
style: {
|
||||
'padding-left': 0,
|
||||
'padding-right': 0,
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import chalk from 'chalk';
|
||||
import ms from 'ms';
|
||||
import table from 'text-table';
|
||||
import table from '../../util/output/table';
|
||||
import title from 'title';
|
||||
import Now from '../../util';
|
||||
import getArgs from '../../util/get-args';
|
||||
import { handleError } from '../../util/error';
|
||||
import elapsed from '../../util/output/elapsed';
|
||||
import strlen from '../../util/strlen';
|
||||
import toHost from '../../util/to-host';
|
||||
import parseMeta from '../../util/parse-meta';
|
||||
import { isValidName } from '../../util/is-valid-name';
|
||||
@@ -275,11 +274,7 @@ export default async function list(client: Client) {
|
||||
app === null ? filterUniqueApps() : () => true
|
||||
),
|
||||
],
|
||||
{
|
||||
align: ['l', 'l', 'l', 'l', 'l'],
|
||||
hsep: ' '.repeat(5),
|
||||
stringLength: strlen,
|
||||
}
|
||||
{ hsep: 5 }
|
||||
).replace(/^/gm, ' ')}\n\n`
|
||||
);
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import chalk from 'chalk';
|
||||
import ms from 'ms';
|
||||
import table from 'text-table';
|
||||
import table from '../../util/output/table';
|
||||
import type { Project } from '@vercel-internals/types';
|
||||
import Client from '../../util/client';
|
||||
import getCommandFlags from '../../util/get-command-flags';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
import strlen from '../../util/strlen';
|
||||
import { NODE_VERSIONS } from '@vercel/build-utils';
|
||||
|
||||
export default async function list(
|
||||
@@ -100,11 +99,7 @@ export default async function list(
|
||||
])
|
||||
.flat(),
|
||||
],
|
||||
{
|
||||
align: ['l', 'l', 'l'],
|
||||
hsep: ' '.repeat(3),
|
||||
stringLength: strlen,
|
||||
}
|
||||
{ hsep: 3 }
|
||||
).replace(/^/gm, ' ');
|
||||
output.print(`\n${tablePrint}\n\n`);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import chalk from 'chalk';
|
||||
import ms from 'ms';
|
||||
import plural from 'pluralize';
|
||||
import table from 'text-table';
|
||||
import table from '../../util/output/table';
|
||||
import Now from '../../util';
|
||||
import getAliases from '../../util/alias/get-aliases';
|
||||
import elapsed from '../../util/output/elapsed';
|
||||
@@ -245,7 +245,7 @@ function readConfirmation(
|
||||
const url = depl.url ? chalk.underline(`https://${depl.url}`) : '';
|
||||
return [` ${depl.id}`, url, time];
|
||||
}),
|
||||
{ align: ['l', 'r', 'l'], hsep: ' '.repeat(6) }
|
||||
{ align: ['l', 'r', 'l'], hsep: 6 }
|
||||
);
|
||||
output.print(`${deploymentTable}\n`);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import isErrnoException from '@vercel/error-utils';
|
||||
import chalk from 'chalk';
|
||||
import table from 'text-table';
|
||||
import table from '../../util/output/table';
|
||||
import ms from 'ms';
|
||||
import strlen from '../../util/strlen';
|
||||
import { handleError, error } from '../../util/error';
|
||||
import NowSecrets from '../../util/secrets';
|
||||
import getScope from '../../util/get-scope';
|
||||
@@ -124,11 +123,7 @@ async function run({ output, contextName, currentTeam, client }) {
|
||||
chalk.gray(`${ms(cur - new Date(secret.created))} ago`),
|
||||
])
|
||||
),
|
||||
{
|
||||
align: ['l', 'l', 'l'],
|
||||
hsep: ' '.repeat(2),
|
||||
stringLength: strlen,
|
||||
}
|
||||
{ hsep: 2 }
|
||||
);
|
||||
|
||||
if (out) {
|
||||
@@ -285,7 +280,7 @@ async function readConfirmation(client, output, secret, contextName) {
|
||||
const time = chalk.gray(`${ms(new Date() - new Date(secret.created))} ago`);
|
||||
const tbl = table([[chalk.bold(secret.name), time]], {
|
||||
align: ['r', 'l'],
|
||||
hsep: ' '.repeat(6),
|
||||
hsep: 6,
|
||||
});
|
||||
|
||||
output.print(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import chars from '../../util/output/chars';
|
||||
import table from '../../util/output/table';
|
||||
import { gray } from 'chalk';
|
||||
import getUser from '../../util/get-user';
|
||||
import getTeams from '../../util/teams/get-teams';
|
||||
import { packageName } from '../../util/pkg-name';
|
||||
@@ -53,7 +54,7 @@ export default async function list(client: Client): Promise<number> {
|
||||
id,
|
||||
name,
|
||||
value: slug,
|
||||
current: id === currentTeam ? chars.tick : '',
|
||||
prefix: id === currentTeam ? chars.tick : ' ',
|
||||
}));
|
||||
|
||||
if (user.version !== 'northstar') {
|
||||
@@ -61,7 +62,7 @@ export default async function list(client: Client): Promise<number> {
|
||||
id: user.id,
|
||||
name: user.email,
|
||||
value: user.username || user.email,
|
||||
current: accountIsCurrent ? chars.tick : '',
|
||||
prefix: accountIsCurrent ? chars.tick : ' ',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -76,14 +77,22 @@ export default async function list(client: Client): Promise<number> {
|
||||
output.stopSpinner();
|
||||
client.stdout.write('\n'); // empty line
|
||||
|
||||
table(
|
||||
['', 'id', 'email / name'],
|
||||
teamList.map(team => [team.current, team.value, team.name]),
|
||||
[1, 5],
|
||||
(str: string) => {
|
||||
client.stdout.write(str);
|
||||
}
|
||||
const teamTable = table(
|
||||
[
|
||||
['id', 'email / name'].map(str => gray(str)),
|
||||
...teamList.map(team => [team.value, team.name]),
|
||||
],
|
||||
{ hsep: 5 }
|
||||
);
|
||||
client.stderr.write(
|
||||
currentTeam
|
||||
? teamTable
|
||||
.split('\n')
|
||||
.map((line, i) => `${i > 0 ? teamList[i - 1].prefix : ' '} ${line}`)
|
||||
.join('\n')
|
||||
: teamTable
|
||||
);
|
||||
client.stderr.write('\n');
|
||||
|
||||
if (pagination?.count === 20) {
|
||||
const flags = getCommandFlags(argv, ['_', '--next', '-N', '-d']);
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
import chalk from 'chalk';
|
||||
import table from 'text-table';
|
||||
import strlen from './strlen';
|
||||
import table from './output/table';
|
||||
import { gray } from 'chalk';
|
||||
|
||||
const HEADER = ['name', 'type', 'value'].map(v => chalk.gray(v));
|
||||
const HEADER = ['name', 'type', 'value'].map(v => gray(v));
|
||||
|
||||
export default function formatDNSTable(
|
||||
rows: string[][],
|
||||
{ extraSpace = '' } = {}
|
||||
) {
|
||||
return table([HEADER, ...rows], {
|
||||
align: ['l', 'l', 'l'],
|
||||
hsep: ' '.repeat(8),
|
||||
stringLength: strlen,
|
||||
}).replace(/^(.*)/gm, `${extraSpace}$1`);
|
||||
export default function formatDNSTable(rows: string[][]) {
|
||||
return table([HEADER, ...rows], { hsep: 8 });
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import chalk from 'chalk';
|
||||
import table from 'text-table';
|
||||
import strlen from './strlen';
|
||||
import table from './output/table';
|
||||
import chars from './output/chars';
|
||||
|
||||
export default function formatNSTable(
|
||||
@@ -35,10 +34,6 @@ export default function formatNSTable(
|
||||
],
|
||||
...rows,
|
||||
],
|
||||
{
|
||||
align: ['l', 'l', 'l', 'l'],
|
||||
hsep: ' '.repeat(4),
|
||||
stringLength: strlen,
|
||||
}
|
||||
{ hsep: 4 }
|
||||
).replace(/^(.*)/gm, `${extraSpace}$1`);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import chalk from 'chalk';
|
||||
import table from 'text-table';
|
||||
import table from './output/table';
|
||||
import strlen from './strlen';
|
||||
|
||||
// header:
|
||||
@@ -19,9 +19,8 @@ import strlen from './strlen';
|
||||
// ]
|
||||
export default function formatTable(
|
||||
header: string[],
|
||||
align: Array<'l' | 'r' | 'c' | '.'>,
|
||||
blocks: { name?: string; rows: string[][] }[],
|
||||
hsep = ' '
|
||||
align: Array<'l' | 'r' | 'c'>,
|
||||
blocks: { name?: string; rows: string[][] }[]
|
||||
) {
|
||||
const nrCols = header.length;
|
||||
const padding = [];
|
||||
@@ -57,7 +56,7 @@ export default function formatTable(
|
||||
rows[i][j] = al === 'l' ? col + pad : pad + col;
|
||||
}
|
||||
}
|
||||
out += table(rows, { align, hsep, stringLength: strlen });
|
||||
out += table(rows, { align, hsep: 4 });
|
||||
}
|
||||
out += '\n\n';
|
||||
}
|
||||
|
||||
@@ -1,38 +1,52 @@
|
||||
import chalk from 'chalk';
|
||||
import Table from 'cli-table3';
|
||||
|
||||
const printLine = (data: string[], sizes: number[]) =>
|
||||
data.reduce((line, col, i) => line + col.padEnd(sizes[i]), '');
|
||||
const defaultStyle = {
|
||||
'padding-left': 0,
|
||||
'padding-right': 2,
|
||||
};
|
||||
|
||||
export const noBorderChars = {
|
||||
top: '',
|
||||
'top-mid': '',
|
||||
'top-left': '',
|
||||
'top-right': '',
|
||||
bottom: '',
|
||||
'bottom-mid': '',
|
||||
'bottom-left': '',
|
||||
'bottom-right': '',
|
||||
left: '',
|
||||
'left-mid': '',
|
||||
mid: '',
|
||||
'mid-mid': '',
|
||||
right: '',
|
||||
'right-mid': '',
|
||||
middle: '',
|
||||
};
|
||||
|
||||
const alignMap = {
|
||||
l: 'left',
|
||||
c: 'center',
|
||||
r: 'right',
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Print a table.
|
||||
*/
|
||||
export default function table(
|
||||
fieldNames: string[] = [],
|
||||
data: string[][] = [],
|
||||
margins: number[] = [],
|
||||
print: (str: string) => void
|
||||
rows: string[][],
|
||||
opts?: { hsep?: number; align?: ('l' | 'c' | 'r')[] }
|
||||
) {
|
||||
// Compute size of each column
|
||||
const sizes = data
|
||||
.reduce(
|
||||
(acc, row) =>
|
||||
row.map((col, i) => {
|
||||
const currentMaxColSize = acc[i] || 0;
|
||||
const colSize = (col && col.length) || 0;
|
||||
return Math.max(currentMaxColSize, colSize);
|
||||
}),
|
||||
fieldNames.map(col => col.length)
|
||||
const table = new Table({
|
||||
style: {
|
||||
...defaultStyle,
|
||||
'padding-right': opts?.hsep ?? defaultStyle['padding-right'],
|
||||
},
|
||||
chars: noBorderChars,
|
||||
});
|
||||
table.push(
|
||||
...rows.map(row =>
|
||||
row.map((cell, i) => ({
|
||||
content: cell,
|
||||
hAlign: alignMap[opts?.align?.[i] ?? 'l'],
|
||||
}))
|
||||
)
|
||||
// Add margin to all columns except the last
|
||||
.map((size, i) => (i < margins.length && size + margins[i]) || size);
|
||||
|
||||
// Print header
|
||||
print(chalk.grey(printLine(fieldNames, sizes)));
|
||||
print('\n');
|
||||
|
||||
// Print content
|
||||
for (const row of data) {
|
||||
print(printLine(row, sizes));
|
||||
print('\n');
|
||||
}
|
||||
);
|
||||
return table.toString();
|
||||
}
|
||||
|
||||
2
packages/cli/test/integration-3.test.ts
vendored
2
packages/cli/test/integration-3.test.ts
vendored
@@ -369,7 +369,7 @@ test('list the scopes', async () => {
|
||||
expect(exitCode, formatOutput({ stdout, stderr })).toBe(0);
|
||||
|
||||
const include = new RegExp(`✔ ${contextName}\\s+${email}`);
|
||||
expect(stdout).toMatch(include);
|
||||
expect(stderr).toMatch(include);
|
||||
});
|
||||
|
||||
test('domains inspect', async () => {
|
||||
|
||||
@@ -11,7 +11,7 @@ describe('teams', () => {
|
||||
const user = useUser();
|
||||
useTeams(undefined, { apiVersion: 2 });
|
||||
const exitCodePromise = teamsList(client);
|
||||
await expect(client.stdout).toOutput(user.username);
|
||||
await expect(client.stderr).toOutput(user.username);
|
||||
await expect(exitCodePromise).resolves.toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user