Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions src/commands/docs-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getDocsIndex, getDocsPageIndex } from "../clients/docs";
import { CommandError, createCommand, type CommandConfig } from "../lib/command";
import { stringify } from "../lib/json";
import { NotFoundRequestError, UnknownRequestError } from "../lib/request";
import { formatTable } from "../lib/string";

const config = {
name: "prismic docs list",
Expand Down Expand Up @@ -52,9 +53,8 @@ export default createCommand(config, async ({ positionals, values }) => {
return;
}

for (const anchor of entry.anchors) {
console.info(`${path}#${anchor.slug}: ${anchor.excerpt}`);
}
const rows = entry.anchors.map((anchor) => [`${path}#${anchor.slug}`, anchor.excerpt]);
console.info(formatTable(rows, { headers: ["PATH", "EXCERPT"] }));
} else {
let pages;
try {
Expand All @@ -79,9 +79,7 @@ export default createCommand(config, async ({ positionals, values }) => {
return;
}

for (const page of pages) {
const description = page.description ? ` — ${page.description}` : "";
console.info(`${page.path}: ${page.title}${description}`);
}
const rows = pages.map((page) => [page.path, page.title, page.description ?? ""]);
console.info(formatTable(rows, { headers: ["PATH", "TITLE", "DESCRIPTION"] }));
}
});
8 changes: 5 additions & 3 deletions src/commands/locale-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getHost, getToken } from "../auth";
import { getLocales } from "../clients/locale";
import { createCommand, type CommandConfig } from "../lib/command";
import { stringify } from "../lib/json";
import { formatTable } from "../lib/string";
import { getRepositoryName } from "../project";

const config = {
Expand Down Expand Up @@ -36,8 +37,9 @@ export default createCommand(config, async ({ values }) => {
return;
}

for (const locale of locales) {
const rows = locales.map((locale) => {
const masterLabel = locale.isMaster ? " (master)" : "";
console.info(`${locale.id} ${locale.label}${masterLabel}`);
}
return [locale.id, `${locale.label}${masterLabel}`];
});
console.info(formatTable(rows, { headers: ["ID", "LABEL"] }));
});
11 changes: 8 additions & 3 deletions src/commands/preview-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getHost, getToken } from "../auth";
import { getPreviews, getSimulatorUrl } from "../clients/core";
import { createCommand, type CommandConfig } from "../lib/command";
import { stringify } from "../lib/json";
import { formatTable } from "../lib/string";
import { getRepositoryName } from "../project";

const config = {
Expand Down Expand Up @@ -44,11 +45,15 @@ export default createCommand(config, async ({ values }) => {
return;
}

for (const preview of previews) {
console.info(`${preview.url} ${preview.label}`);
if (previews.length > 0) {
const rows = previews.map((preview) => [preview.url, preview.label]);
console.info(formatTable(rows, { headers: ["URL", "NAME"] }));
}

if (simulatorUrl) {
console.info(`\nSimulator: ${simulatorUrl}`);
if (previews.length > 0) {
console.info("");
}
console.info(`Simulator: ${simulatorUrl}`);
}
});
9 changes: 5 additions & 4 deletions src/commands/repo-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { getProfile } from "../clients/user";
import { CommandError, createCommand, type CommandConfig } from "../lib/command";
import { stringify } from "../lib/json";
import { UnknownRequestError } from "../lib/request";
import { formatTable } from "../lib/string";

const config = {
name: "prismic repo list",
Expand Down Expand Up @@ -50,9 +51,9 @@ export default createCommand(config, async ({ values }) => {
return;
}

for (const repo of repos) {
const rows = repos.map((repo) => {
const name = repo.name || "(no name)";
const role = repo.role ? ` ${repo.role}` : "";
console.info(`${repo.domain} ${name}${role}`);
}
return [repo.domain, name, repo.role ?? ""];
});
console.info(formatTable(rows, { headers: ["DOMAIN", "NAME", "ROLE"] }));
});
6 changes: 3 additions & 3 deletions src/commands/slice-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getHost, getToken } from "../auth";
import { getSlices } from "../clients/custom-types";
import { createCommand, type CommandConfig } from "../lib/command";
import { stringify } from "../lib/json";
import { formatTable } from "../lib/string";
import { getRepositoryName } from "../project";

const config = {
Expand Down Expand Up @@ -30,7 +31,6 @@ export default createCommand(config, async ({ values }) => {
return;
}

for (const slice of slices) {
console.info(`${slice.name} (id: ${slice.id})`);
}
const rows = slices.map((slice) => [slice.name, slice.id]);
console.info(formatTable(rows, { headers: ["NAME", "ID"] }));
});
8 changes: 5 additions & 3 deletions src/commands/slice-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getHost, getToken } from "../auth";
import { getSlices } from "../clients/custom-types";
import { CommandError, createCommand, type CommandConfig } from "../lib/command";
import { stringify } from "../lib/json";
import { formatTable } from "../lib/string";
import { getRepositoryName } from "../project";

const config = {
Expand Down Expand Up @@ -44,12 +45,13 @@ export default createCommand(config, async ({ positionals, values }) => {
if (entries.length === 0) {
console.info(" (no fields)");
} else {
for (const [id, field] of entries) {
const rows = entries.map(([id, field]) => {
const config = field.config as Record<string, unknown> | undefined;
const label = (config?.label as string) || "";
const placeholder = config?.placeholder ? `"${config.placeholder}"` : "";
console.info(` ${[id, field.type, label, placeholder].filter(Boolean).join(" ")}`);
}
return [` ${id}`, field.type, label, placeholder];
});
console.info(formatTable(rows));
}
}
});
15 changes: 8 additions & 7 deletions src/commands/token-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getHost, getToken } from "../auth";
import { getOAuthApps, getWriteTokens } from "../clients/wroom";
import { createCommand, type CommandConfig } from "../lib/command";
import { stringify } from "../lib/json";
import { formatTable } from "../lib/string";
import { getRepositoryName } from "../project";

const config = {
Expand Down Expand Up @@ -46,9 +47,8 @@ export default createCommand(config, async ({ values }) => {

if (accessTokens.length > 0) {
console.info("ACCESS TOKENS");
for (const accessToken of accessTokens) {
console.info(` ${accessToken.name} ${accessToken.scope} ${accessToken.token} ${accessToken.createdAt}`);
}
const rows = accessTokens.map((t) => [` ${t.name}`, t.scope, t.token, t.createdAt]);
console.info(formatTable(rows));
} else {
console.info("ACCESS TOKENS (none)");
}
Expand All @@ -57,10 +57,11 @@ export default createCommand(config, async ({ values }) => {

if (writeTokens.length > 0) {
console.info("WRITE TOKENS");
for (const writeToken of writeTokens) {
const date = new Date(writeToken.timestamp * 1000).toISOString().split("T")[0];
console.info(` ${writeToken.app_name} ${writeToken.token} ${date}`);
}
const rows = writeTokens.map((t) => {
const date = new Date(t.timestamp * 1000).toISOString().split("T")[0];
return [` ${t.app_name}`, t.token, date];
});
console.info(formatTable(rows));
} else {
console.info("WRITE TOKENS (none)");
}
Expand Down
8 changes: 5 additions & 3 deletions src/commands/type-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getHost, getToken } from "../auth";
import { getCustomTypes } from "../clients/custom-types";
import { createCommand, type CommandConfig } from "../lib/command";
import { stringify } from "../lib/json";
import { formatTable } from "../lib/string";
import { getRepositoryName } from "../project";

const config = {
Expand Down Expand Up @@ -31,8 +32,9 @@ export default createCommand(config, async ({ values }) => {
return;
}

for (const type of types) {
const rows = types.map((type) => {
const label = type.label || "(no name)";
console.info(`${label} (id: ${type.id}, format: ${type.format})`);
}
return [label, type.id, type.format ?? ""];
});
console.info(formatTable(rows, { headers: ["NAME", "ID", "FORMAT"] }));
});
8 changes: 5 additions & 3 deletions src/commands/type-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getHost, getToken } from "../auth";
import { getCustomTypes } from "../clients/custom-types";
import { CommandError, createCommand, type CommandConfig } from "../lib/command";
import { stringify } from "../lib/json";
import { formatTable } from "../lib/string";
import { getRepositoryName } from "../project";

const config = {
Expand Down Expand Up @@ -46,12 +47,13 @@ export default createCommand(config, async ({ positionals, values }) => {
if (entries.length === 0) {
console.info(" (no fields)");
} else {
for (const [id, field] of entries) {
const rows = entries.map(([id, field]) => {
const config = field.config as Record<string, unknown> | undefined;
const label = (config?.label as string) || "";
const placeholder = config?.placeholder ? `"${config.placeholder}"` : "";
console.info(` ${[id, field.type, label, placeholder].filter(Boolean).join(" ")}`);
}
return [` ${id}`, field.type, label, placeholder];
});
console.info(formatTable(rows));
}
}
});
8 changes: 5 additions & 3 deletions src/commands/webhook-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getHost, getToken } from "../auth";
import { getWebhooks } from "../clients/wroom";
import { createCommand, type CommandConfig } from "../lib/command";
import { stringify } from "../lib/json";
import { formatTable } from "../lib/string";
import { getRepositoryName } from "../project";

const config = {
Expand Down Expand Up @@ -35,9 +36,10 @@ export default createCommand(config, async ({ values }) => {
return;
}

for (const webhook of webhooks) {
const rows = webhooks.map((webhook) => {
const status = webhook.config.active ? "enabled" : "disabled";
const name = webhook.config.name ? ` (${webhook.config.name})` : "";
console.info(`${webhook.config.url}${name} [${status}]`);
}
return [`${webhook.config.url}${name}`, `[${status}]`];
});
console.info(formatTable(rows, { headers: ["URL", "STATUS"] }));
});
27 changes: 8 additions & 19 deletions src/lib/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { ParseArgsOptionDescriptor } from "node:util";

import { parseArgs } from "node:util";

import { dedent } from "./string";
import { dedent, formatTable } from "./string";

export type CommandConfig = {
name: string;
Expand Down Expand Up @@ -89,16 +89,13 @@ function buildCommandHelp(config: CommandConfig): string {
if (positionalNames.length > 0) {
lines.push("");
lines.push("ARGUMENTS");
const maxNameLength = Math.max(
...positionalNames.map((positionalName) => `<${positionalName}>`.length),
);
const rows: string[][] = [];
for (const positionalName in positionals) {
const formattedName = `<${positionalName}>`;
const paddedName = formattedName.padEnd(maxNameLength);
const positional = positionals[positionalName];
const description = positional.description + (positional.required ? " (required)" : "");
lines.push(` ${paddedName} ${description}`);
rows.push([` <${positionalName}>`, description]);
}
lines.push(formatTable(rows));
}

lines.push("");
Expand All @@ -116,11 +113,8 @@ function buildCommandHelp(config: CommandConfig): string {
}
}
optionEntries.push({ left: "-h, --help", description: "Show help for command" });
const maxOptionLength = Math.max(...optionEntries.map((optionEntry) => optionEntry.left.length));
for (const optionEntry of optionEntries) {
const paddedLeft = optionEntry.left.padEnd(maxOptionLength);
lines.push(` ${paddedLeft} ${optionEntry.description}`);
}
const optionRows = optionEntries.map((entry) => [` ${entry.left}`, entry.description]);
lines.push(formatTable(optionRows));

if (sections) {
for (const sectionName in sections) {
Expand Down Expand Up @@ -190,13 +184,8 @@ function buildRouterHelp(config: CreateCommandRouterConfig): string {

lines.push("");
lines.push("COMMANDS");
const commandNames = Object.keys(commands);
const maxNameLength = Math.max(...commandNames.map((commandName) => commandName.length));
for (const commandName of commandNames) {
const paddedName = commandName.padEnd(maxNameLength);
const description = commands[commandName].description;
lines.push(` ${paddedName} ${description}`);
}
const commandRows = Object.entries(commands).map(([name, cmd]) => [` ${name}`, cmd.description]);
lines.push(formatTable(commandRows));

lines.push("");
lines.push("OPTIONS");
Expand Down
23 changes: 23 additions & 0 deletions src/lib/string.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
import baseDedent from "dedent";

export const dedent = baseDedent.withOptions({ alignValues: true });

export function formatTable(
rows: string[][],
config?: { headers?: string[]; separator?: string },
): string {
const separator = config?.separator ?? " ";
const allRows = config?.headers ? [config.headers, ...rows] : rows;
const columnWidths: number[] = [];
for (const row of allRows) {
for (let i = 0; i < row.length; i++) {
columnWidths[i] = Math.max(columnWidths[i] ?? 0, row[i].length);
}
}
return allRows
.map((row) => {
const line = row
.map((cell, i) => (i < row.length - 1 ? cell.padEnd(columnWidths[i]) : cell))
.join(separator)
.trimEnd();
return line;
})
.join("\n");
}
2 changes: 1 addition & 1 deletion test/slice-list.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ it("lists slices", async ({ expect, prismic, repo, token, host }) => {

const { stdout, exitCode } = await prismic("slice", ["list"]);
expect(exitCode).toBe(0);
expect(stdout).toContain(`${slice.name} (id: ${slice.id})`);
expect(stdout).toMatch(new RegExp(`${slice.name}\\s+${slice.id}`));
});

it("lists slices as JSON", async ({ expect, prismic, repo, token, host }) => {
Expand Down
7 changes: 3 additions & 4 deletions test/slice-view.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,10 @@ it("shows fields per variation", async ({ expect, prismic, repo, token, host })
const { stdout, exitCode } = await prismic("slice", ["view", slice.name]);
expect(exitCode).toBe(0);
expect(stdout).toContain("default:");
expect(stdout).toContain("title StructuredText Title");
expect(stdout).toContain('"Enter title"');
expect(stdout).toContain("is_active Boolean Is Active");
expect(stdout).toMatch(/title\s+StructuredText\s+Title\s+"Enter title"/);
expect(stdout).toMatch(/is_active\s+Boolean\s+Is Active/);
expect(stdout).toContain("withImage:");
expect(stdout).toContain("image Image Image");
expect(stdout).toMatch(/image\s+Image\s+Image/);
});

it("views a slice as JSON", async ({ expect, prismic, repo, token, host }) => {
Expand Down
4 changes: 2 additions & 2 deletions test/type-list.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ it("lists all types", async ({ expect, prismic, repo, token, host }) => {

const { stdout, exitCode } = await prismic("type", ["list"]);
expect(exitCode).toBe(0);
expect(stdout).toContain(`${customType.label} (id: ${customType.id}, format: custom)`);
expect(stdout).toContain(`${pageType.label} (id: ${pageType.id}, format: page)`);
expect(stdout).toMatch(new RegExp(`${customType.label}\\s+${customType.id}\\s+custom`));
expect(stdout).toMatch(new RegExp(`${pageType.label}\\s+${pageType.id}\\s+page`));
});

it("lists types as JSON", async ({ expect, prismic, repo, token, host }) => {
Expand Down
7 changes: 3 additions & 4 deletions test/type-view.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,10 @@ it("shows fields per tab", async ({ expect, prismic, repo, token, host }) => {
const { stdout, exitCode } = await prismic("type", ["view", customType.label!]);
expect(exitCode).toBe(0);
expect(stdout).toContain("Main:");
expect(stdout).toContain("title StructuredText Title");
expect(stdout).toContain('"Enter title"');
expect(stdout).toContain("is_active Boolean Is Active");
expect(stdout).toMatch(/title\s+StructuredText\s+Title\s+"Enter title"/);
expect(stdout).toMatch(/is_active\s+Boolean\s+Is Active/);
expect(stdout).toContain("SEO:");
expect(stdout).toContain("meta_title Text Meta Title");
expect(stdout).toMatch(/meta_title\s+Text\s+Meta Title/);
});

it("views a type as JSON", async ({ expect, prismic, repo, token, host }) => {
Expand Down