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
5 changes: 5 additions & 0 deletions .changeset/fix-export-seed-with-content-all.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"emdash": patch
---

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.
17 changes: 10 additions & 7 deletions packages/core/src/cli/commands/export-seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,16 @@ export async function exportSeed(db: Kysely<Database>, withContent?: string): Pr

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

seed.content = await exportContent(
db,
Expand Down
88 changes: 88 additions & 0 deletions packages/core/tests/unit/seed/export-seed-with-content-all.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import type { Kysely } from "kysely";
import { afterEach, beforeEach, describe, expect, it } from "vitest";

import { exportSeed } from "../../../src/cli/commands/export-seed.js";
import { ContentRepository } from "../../../src/database/repositories/content.js";
import type { Database } from "../../../src/database/types.js";
import { setI18nConfig } from "../../../src/i18n/config.js";
import { SchemaRegistry } from "../../../src/schema/registry.js";
import { setupTestDatabase, teardownTestDatabase } from "../../utils/test-db.js";

/**
* Regression for #1329: `emdash export-seed --with-content all` exported
* nothing because the literal string "all" was treated as a collection name.
* Only the bare flag and `--with-content=true` were honoured as the
* "include every collection" sentinel, contradicting the args help text:
*
* "with-content": {
* description: "Include content (all or comma-separated collection names)",
* }
*/
describe("exportSeed: --with-content sentinel handling", () => {
let db: Kysely<Database>;

beforeEach(async () => {
setI18nConfig(null);
db = await setupTestDatabase();

const registry = new SchemaRegistry(db);
await registry.createCollection({ slug: "posts", label: "Posts" });
await registry.createField("posts", { slug: "title", label: "Title", type: "string" });
await registry.createCollection({ slug: "pages", label: "Pages" });
await registry.createField("pages", { slug: "title", label: "Title", type: "string" });

const contentRepo = new ContentRepository(db);
await contentRepo.create({
type: "posts",
slug: "hello-post",
status: "published",
data: { title: "Hello Post" },
});
await contentRepo.create({
type: "pages",
slug: "hello-page",
status: "published",
data: { title: "Hello Page" },
});
});

afterEach(async () => {
await teardownTestDatabase(db);
setI18nConfig(null);
});

it("treats `all` as a synonym for include-every-collection", async () => {
const seed = await exportSeed(db, "all");

expect(seed.content).toBeDefined();
expect(Object.keys(seed.content ?? {}).toSorted()).toEqual(["pages", "posts"]);
expect(seed.content?.posts?.[0]?.slug).toBe("hello-post");
expect(seed.content?.pages?.[0]?.slug).toBe("hello-page");
});

it("matches the bare flag's behaviour (empty string)", async () => {
const seedAll = await exportSeed(db, "all");
const seedBare = await exportSeed(db, "");

expect(Object.keys(seedAll.content ?? {}).toSorted()).toEqual(
Object.keys(seedBare.content ?? {}).toSorted(),
);
});

it("matches the explicit `true` sentinel's behaviour", async () => {
const seedAll = await exportSeed(db, "all");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] The test description says "comma-separated list", but the value passed is "posts" (a single collection name, no comma). Consider updating the description to match the data, or passing an actual comma-separated value like "posts,pages" to exercise the .split(",") path.

const seedTrue = await exportSeed(db, "true");

expect(Object.keys(seedAll.content ?? {}).toSorted()).toEqual(
Object.keys(seedTrue.content ?? {}).toSorted(),
);
});

it("still treats a comma-separated list as a collection-name filter", async () => {
const seed = await exportSeed(db, "posts");

expect(seed.content).toBeDefined();
expect(Object.keys(seed.content ?? {})).toEqual(["posts"]);
expect(seed.content?.posts?.[0]?.slug).toBe("hello-post");
});
});
Loading