@dropsh/plugin-table 0.5.2 → 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,31 @@
1
+ const PREFERRED = ["title", "name", "label", "status"];
2
+ const MAX_CELL = 40;
3
+ const MAX_COLS = 5;
4
+ function isScalar(v) {
5
+ return typeof v === "string" || typeof v === "number" || typeof v === "boolean";
6
+ }
7
+ export function pickColumns(res) {
8
+ const attrs = res.attributes ?? {};
9
+ const scalars = Object.entries(attrs)
10
+ .filter(([, v]) => isScalar(v))
11
+ .map(([k]) => k);
12
+ const preferred = PREFERRED.filter((k) => scalars.includes(k));
13
+ const rest = scalars.filter((k) => !preferred.includes(k));
14
+ return ["id", ...preferred, ...rest].slice(0, MAX_COLS);
15
+ }
16
+ export function cell(v) {
17
+ const s = v === undefined || v === null ? "" : String(v);
18
+ return s.length > MAX_CELL ? `${s.slice(0, MAX_CELL - 1)}…` : s;
19
+ }
20
+ export function formatTable(headers, rows) {
21
+ const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length), 0));
22
+ const bar = (l, m, r) => `${l}${widths.map((w) => "─".repeat(w + 2)).join(m)}${r}`;
23
+ const line = (cells) => `│ ${cells.map((c, i) => (c ?? "").padEnd(widths[i] ?? 0)).join(" │ ")} │`;
24
+ return [
25
+ bar("┌", "┬", "┐"),
26
+ line(headers),
27
+ bar("├", "┼", "┤"),
28
+ ...rows.map(line),
29
+ bar("└", "┴", "┘"),
30
+ ].join("\n");
31
+ }
@@ -0,0 +1,12 @@
1
+ import { renderTable } from "./render-table.js";
2
+ export { renderTable };
3
+ export function tablePlugin() {
4
+ return {
5
+ id: "table",
6
+ requiredModules: [],
7
+ async extendSchema(_entityType, _bundle, schema) {
8
+ return schema;
9
+ },
10
+ renderers: [{ id: "table", render: renderTable }],
11
+ };
12
+ }
@@ -0,0 +1,27 @@
1
+ import { cell, formatTable, pickColumns } from "./columns.js";
2
+ export { formatTable, pickColumns };
3
+ function isScalar(v) {
4
+ return typeof v === "string" || typeof v === "number" || typeof v === "boolean";
5
+ }
6
+ export function renderTable(doc, _ctx) {
7
+ const data = doc.data;
8
+ if (Array.isArray(data)) {
9
+ if (data.length === 0)
10
+ return "(0 rows)";
11
+ const first = data[0];
12
+ if (!first)
13
+ return "(0 rows)";
14
+ const cols = pickColumns(first);
15
+ const rows = data.map((res) => cols.map((c) => cell(c === "id" ? res.id : (res.attributes ?? {})[c])));
16
+ return formatTable(cols, rows);
17
+ }
18
+ const rows = [
19
+ ["type", data.type],
20
+ ["id", data.id],
21
+ ];
22
+ for (const [k, v] of Object.entries(data.attributes ?? {})) {
23
+ if (isScalar(v))
24
+ rows.push([k, cell(v)]);
25
+ }
26
+ return formatTable(["field", "value"], rows);
27
+ }
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "@dropsh/plugin-table",
3
- "version": "0.5.2",
3
+ "version": "0.5.5",
4
4
  "type": "module",
5
5
  "main": "./dist/plugins/table/src/index.js",
6
6
  "exports": {
7
7
  ".": "./dist/plugins/table/src/index.js",
8
8
  "./render": "./dist/plugins/table/src/render-table.js"
9
9
  },
10
+ "files": [
11
+ "dist/plugins/table/src"
12
+ ],
10
13
  "publishConfig": {
11
14
  "access": "public"
12
15
  },
@@ -16,7 +19,7 @@
16
19
  "devDependencies": {
17
20
  "typescript": "^5.6.0",
18
21
  "vitest": "^2.1.0",
19
- "dropsh": "0.5.2"
22
+ "dropsh": "0.5.5"
20
23
  },
21
24
  "scripts": {
22
25
  "build": "tsc",
package/src/columns.ts DELETED
@@ -1,41 +0,0 @@
1
- import type { JsonApiResource } from "dropsh/plugin";
2
-
3
- const PREFERRED = ["title", "name", "label", "status"];
4
- const MAX_CELL = 40;
5
- const MAX_COLS = 5;
6
-
7
- function isScalar(v: unknown): boolean {
8
- return typeof v === "string" || typeof v === "number" || typeof v === "boolean";
9
- }
10
-
11
- export function pickColumns(res: JsonApiResource): string[] {
12
- const attrs = res.attributes ?? {};
13
- const scalars = Object.entries(attrs)
14
- .filter(([, v]) => isScalar(v))
15
- .map(([k]) => k);
16
- const preferred = PREFERRED.filter((k) => scalars.includes(k));
17
- const rest = scalars.filter((k) => !preferred.includes(k));
18
- return ["id", ...preferred, ...rest].slice(0, MAX_COLS);
19
- }
20
-
21
- export function cell(v: unknown): string {
22
- const s = v === undefined || v === null ? "" : String(v);
23
- return s.length > MAX_CELL ? `${s.slice(0, MAX_CELL - 1)}…` : s;
24
- }
25
-
26
- export function formatTable(headers: string[], rows: string[][]): string {
27
- const widths = headers.map((h, i) =>
28
- Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length), 0),
29
- );
30
- const bar = (l: string, m: string, r: string) =>
31
- `${l}${widths.map((w) => "─".repeat(w + 2)).join(m)}${r}`;
32
- const line = (cells: string[]) =>
33
- `│ ${cells.map((c, i) => (c ?? "").padEnd(widths[i] ?? 0)).join(" │ ")} │`;
34
- return [
35
- bar("┌", "┬", "┐"),
36
- line(headers),
37
- bar("├", "┼", "┤"),
38
- ...rows.map(line),
39
- bar("└", "┴", "┘"),
40
- ].join("\n");
41
- }
package/src/index.ts DELETED
@@ -1,15 +0,0 @@
1
- import type { DropSHPlugin } from "dropsh/plugin";
2
- import { renderTable } from "./render-table.js";
3
-
4
- export { renderTable };
5
-
6
- export function tablePlugin(): DropSHPlugin {
7
- return {
8
- id: "table",
9
- requiredModules: [],
10
- async extendSchema(_entityType, _bundle, schema) {
11
- return schema;
12
- },
13
- renderers: [{ id: "table", render: renderTable }],
14
- };
15
- }
@@ -1,30 +0,0 @@
1
- import type { JsonApiDocument, RenderContext } from "dropsh/plugin";
2
- import { cell, formatTable, pickColumns } from "./columns.js";
3
-
4
- export { formatTable, pickColumns };
5
-
6
- function isScalar(v: unknown): boolean {
7
- return typeof v === "string" || typeof v === "number" || typeof v === "boolean";
8
- }
9
-
10
- export function renderTable(doc: JsonApiDocument, _ctx: RenderContext): string {
11
- const data = doc.data;
12
- if (Array.isArray(data)) {
13
- if (data.length === 0) return "(0 rows)";
14
- const first = data[0];
15
- if (!first) return "(0 rows)";
16
- const cols = pickColumns(first);
17
- const rows = data.map((res) =>
18
- cols.map((c) => cell(c === "id" ? res.id : (res.attributes ?? {})[c])),
19
- );
20
- return formatTable(cols, rows);
21
- }
22
- const rows: string[][] = [
23
- ["type", data.type],
24
- ["id", data.id],
25
- ];
26
- for (const [k, v] of Object.entries(data.attributes ?? {})) {
27
- if (isScalar(v)) rows.push([k, cell(v)]);
28
- }
29
- return formatTable(["field", "value"], rows);
30
- }
@@ -1,53 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { pickColumns, renderTable } from "../../src/render-table.js";
3
-
4
- const ctx = { command: "search" as const };
5
-
6
- describe("pickColumns", () => {
7
- it("puts id first and prefers title/status", () => {
8
- const cols = pickColumns({
9
- type: "node--article",
10
- id: "u1",
11
- attributes: { body: "x", status: true, title: "T" },
12
- });
13
- expect(cols[0]).toBe("id");
14
- expect(cols).toContain("title");
15
- expect(cols).toContain("status");
16
- });
17
- });
18
-
19
- describe("renderTable", () => {
20
- it("renders a collection as an aligned box table", () => {
21
- const out = renderTable(
22
- {
23
- data: [
24
- { type: "node--article", id: "u1", attributes: { title: "Alpha", status: true } },
25
- { type: "node--article", id: "u2", attributes: { title: "Beta", status: false } },
26
- ],
27
- },
28
- ctx,
29
- );
30
- const lines = out.split("\n");
31
- // biome-ignore lint/style/noNonNullAssertion: split() on a non-empty string always yields at least one element
32
- expect(lines[0]!.startsWith("┌")).toBe(true);
33
- expect(out).toContain("u1");
34
- expect(out).toContain("Alpha");
35
- // every rendered line is the same visual width
36
- expect(new Set(lines.map((l) => [...l].length)).size).toBe(1);
37
- });
38
-
39
- it("renders an empty collection as (0 rows)", () => {
40
- expect(renderTable({ data: [] }, ctx)).toBe("(0 rows)");
41
- });
42
-
43
- it("renders a single resource as a key/value table", () => {
44
- const out = renderTable(
45
- { data: { type: "node--article", id: "u1", attributes: { title: "Solo" } } },
46
- { command: "read" },
47
- );
48
- expect(out).toContain("field");
49
- expect(out).toContain("value");
50
- expect(out).toContain("title");
51
- expect(out).toContain("Solo");
52
- });
53
- });
package/tsconfig.json DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "dist",
5
- "noEmit": false
6
- },
7
- "include": ["src", "tests"]
8
- }
package/vitest.config.ts DELETED
@@ -1,14 +0,0 @@
1
- import { resolve } from "node:path";
2
- import { defineConfig } from "vitest/config";
3
-
4
- export default defineConfig({
5
- resolve: {
6
- alias: {
7
- "dropsh/plugin": resolve(__dirname, "../../src/plugin-api.ts"),
8
- },
9
- },
10
- test: {
11
- include: ["tests/unit/**/*.test.ts"],
12
- environment: "node",
13
- },
14
- });