Skip to content

Commit 9ebb47e

Browse files
authored
fix(cli): treat --with-content=all as include-all sentinel (#1389)
The args help text for `emdash export-seed --with-content` documents `all` as a valid value, but the implementation only treated empty and `true` as the include-every-collection sentinel. The literal string `all` was passed through to the collection-name filter, matched no collection, and produced an empty `content` block. Treat `all` as a synonym for the existing sentinels so the CLI matches its own help text. Closes #1329
1 parent cc28843 commit 9ebb47e

3 files changed

Lines changed: 103 additions & 7 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"emdash": patch
3+
---
4+
5+
Fixes `emdash export-seed --with-content=all` so it exports content from every collection, matching the documented behaviour. Previously the literal string `"all"` was treated as a collection name and matched none, producing an empty `content` block. The bare flag and `--with-content=true` were the only sentinels honoured.

packages/core/src/cli/commands/export-seed.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,16 @@ export async function exportSeed(db: Kysely<Database>, withContent?: string): Pr
153153

154154
// 7. Export content (if requested)
155155
if (withContent !== undefined) {
156-
const collections =
157-
withContent === "" || withContent === "true"
158-
? null // all collections
159-
: withContent
160-
.split(",")
161-
.map((s) => s.trim())
162-
.filter(Boolean);
156+
// Treat "all" as a synonym for the bare flag and "true". The args help
157+
// text documents `all` as a valid value, but without this the literal
158+
// string is read as a collection name and matches no collection (#1329).
159+
const includeAll = withContent === "" || withContent === "true" || withContent === "all";
160+
const collections = includeAll
161+
? null // all collections
162+
: withContent
163+
.split(",")
164+
.map((s) => s.trim())
165+
.filter(Boolean);
163166

164167
seed.content = await exportContent(
165168
db,
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import type { Kysely } from "kysely";
2+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
3+
4+
import { exportSeed } from "../../../src/cli/commands/export-seed.js";
5+
import { ContentRepository } from "../../../src/database/repositories/content.js";
6+
import type { Database } from "../../../src/database/types.js";
7+
import { setI18nConfig } from "../../../src/i18n/config.js";
8+
import { SchemaRegistry } from "../../../src/schema/registry.js";
9+
import { setupTestDatabase, teardownTestDatabase } from "../../utils/test-db.js";
10+
11+
/**
12+
* Regression for #1329: `emdash export-seed --with-content all` exported
13+
* nothing because the literal string "all" was treated as a collection name.
14+
* Only the bare flag and `--with-content=true` were honoured as the
15+
* "include every collection" sentinel, contradicting the args help text:
16+
*
17+
* "with-content": {
18+
* description: "Include content (all or comma-separated collection names)",
19+
* }
20+
*/
21+
describe("exportSeed: --with-content sentinel handling", () => {
22+
let db: Kysely<Database>;
23+
24+
beforeEach(async () => {
25+
setI18nConfig(null);
26+
db = await setupTestDatabase();
27+
28+
const registry = new SchemaRegistry(db);
29+
await registry.createCollection({ slug: "posts", label: "Posts" });
30+
await registry.createField("posts", { slug: "title", label: "Title", type: "string" });
31+
await registry.createCollection({ slug: "pages", label: "Pages" });
32+
await registry.createField("pages", { slug: "title", label: "Title", type: "string" });
33+
34+
const contentRepo = new ContentRepository(db);
35+
await contentRepo.create({
36+
type: "posts",
37+
slug: "hello-post",
38+
status: "published",
39+
data: { title: "Hello Post" },
40+
});
41+
await contentRepo.create({
42+
type: "pages",
43+
slug: "hello-page",
44+
status: "published",
45+
data: { title: "Hello Page" },
46+
});
47+
});
48+
49+
afterEach(async () => {
50+
await teardownTestDatabase(db);
51+
setI18nConfig(null);
52+
});
53+
54+
it("treats `all` as a synonym for include-every-collection", async () => {
55+
const seed = await exportSeed(db, "all");
56+
57+
expect(seed.content).toBeDefined();
58+
expect(Object.keys(seed.content ?? {}).toSorted()).toEqual(["pages", "posts"]);
59+
expect(seed.content?.posts?.[0]?.slug).toBe("hello-post");
60+
expect(seed.content?.pages?.[0]?.slug).toBe("hello-page");
61+
});
62+
63+
it("matches the bare flag's behaviour (empty string)", async () => {
64+
const seedAll = await exportSeed(db, "all");
65+
const seedBare = await exportSeed(db, "");
66+
67+
expect(Object.keys(seedAll.content ?? {}).toSorted()).toEqual(
68+
Object.keys(seedBare.content ?? {}).toSorted(),
69+
);
70+
});
71+
72+
it("matches the explicit `true` sentinel's behaviour", async () => {
73+
const seedAll = await exportSeed(db, "all");
74+
const seedTrue = await exportSeed(db, "true");
75+
76+
expect(Object.keys(seedAll.content ?? {}).toSorted()).toEqual(
77+
Object.keys(seedTrue.content ?? {}).toSorted(),
78+
);
79+
});
80+
81+
it("still treats a comma-separated list as a collection-name filter", async () => {
82+
const seed = await exportSeed(db, "posts");
83+
84+
expect(seed.content).toBeDefined();
85+
expect(Object.keys(seed.content ?? {})).toEqual(["posts"]);
86+
expect(seed.content?.posts?.[0]?.slug).toBe("hello-post");
87+
});
88+
});

0 commit comments

Comments
 (0)