diff --git a/evals/repair-generated-files.eval.ts b/evals/repair-generated-files.eval.ts new file mode 100644 index 0000000..b0ef2b0 --- /dev/null +++ b/evals/repair-generated-files.eval.ts @@ -0,0 +1,23 @@ +import { readFile, writeFile } from "node:fs/promises"; + +import { buildSlice, writeLocalSlice } from "../test/it"; +import { it, trials } from "./it"; + +it.for(trials)( + "regenerates a stale slice index instead of editing it by hand", + async (_, { project, agent, expect }) => { + const slice = buildSlice(); + await writeLocalSlice(project, slice); + await writeFile( + new URL("slices/index.js", project), + "// Code generated by Prismic. DO NOT EDIT.\n\nexport const components = {};\n", + ); + + const result = await agent( + `The components map in slices/index.js is out of date and my slice does not render. Fix it.`, + ); + + expect(result).toHaveRun("prismic", ["gen", "slice-index"]); + expect(await readFile(new URL("slices/index.js", project), "utf8")).toContain(slice.id); + }, +); diff --git a/src/adapters/index.ts b/src/adapters/index.ts index 4f48200..9e89907 100644 --- a/src/adapters/index.ts +++ b/src/adapters/index.ts @@ -149,6 +149,7 @@ export abstract class Adapter { async updateSlice(model: SharedSlice): Promise { const slice = await this.getSlice(model.id); await writeFileRecursive(slice.modelPath, stringify(canonicalizeSlice(model))); + await this.createSliceIndexFile(slice.library); await this.onSliceUpdated(model); } diff --git a/src/commands/gen-slice-index.ts b/src/commands/gen-slice-index.ts new file mode 100644 index 0000000..949ddd0 --- /dev/null +++ b/src/commands/gen-slice-index.ts @@ -0,0 +1,20 @@ +import { getAdapter } from "../adapters"; +import { createCommand, type CommandConfig } from "../lib/command"; + +const config = { + name: "prismic gen slice-index", + description: "Generate the slice index file of each slice library.", +} satisfies CommandConfig; + +export default createCommand(config, async () => { + const adapter = await getAdapter(); + const libraries = await adapter.getSliceLibraries(); + for (const library of libraries) { + await adapter.createSliceIndexFile(library); + } + console.info( + libraries.length === 1 + ? "Generated the slice index file." + : `Generated ${libraries.length} slice index files.`, + ); +}); diff --git a/src/commands/gen.ts b/src/commands/gen.ts index 58cab57..f7b6739 100644 --- a/src/commands/gen.ts +++ b/src/commands/gen.ts @@ -1,5 +1,6 @@ import { createCommandRouter } from "../lib/command"; import genSetup from "./gen-setup"; +import genSliceIndex from "./gen-slice-index"; import genTypes from "./gen-types"; export default createCommandRouter({ @@ -10,6 +11,10 @@ export default createCommandRouter({ handler: genSetup, description: "Generate framework-specific Prismic setup", }, + "slice-index": { + handler: genSliceIndex, + description: "Generate the slice index file of each slice library", + }, types: { handler: genTypes, description: "Generate TypeScript types from local models", diff --git a/test/gen-slice-index.test.ts b/test/gen-slice-index.test.ts new file mode 100644 index 0000000..911ca1c --- /dev/null +++ b/test/gen-slice-index.test.ts @@ -0,0 +1,44 @@ +import { mkdir, writeFile } from "node:fs/promises"; + +import { buildSlice, it, writeLocalSlice } from "./it"; + +it("supports --help", async ({ expect, prismic }) => { + const { stdout, stderr, exitCode } = await prismic("gen", ["slice-index", "--help"]); + expect(exitCode, stderr).toBe(0); + expect(stdout).toContain("prismic gen slice-index [options]"); +}); + +it("generates a slice index file", async ({ expect, project, prismic }) => { + const slice = buildSlice(); + await writeLocalSlice(project, slice); + + const { stderr, exitCode, stdout } = await prismic("gen", ["slice-index"]); + expect(exitCode, stderr).toBe(0); + expect(stdout).toContain("Generated the slice index file."); + + await expect(project).toHaveFile("slices/index.js", { contains: slice.id }); +}); + +it("generates an index file for each slice library", async ({ expect, project, prismic }) => { + const sliceA = buildSlice(); + const sliceB = buildSlice(); + await writeFile( + new URL("prismic.config.json", project), + JSON.stringify({ repositoryName: "example", libraries: ["./slices/a", "./slices/b"] }), + ); + for (const [library, slice] of [ + ["a", sliceA], + ["b", sliceB], + ] as const) { + const path = new URL(`slices/${library}/${slice.name}/model.json`, project); + await mkdir(new URL(".", path), { recursive: true }); + await writeFile(path, JSON.stringify(slice)); + } + + const { stderr, exitCode, stdout } = await prismic("gen", ["slice-index"]); + expect(exitCode, stderr).toBe(0); + expect(stdout).toContain("Generated 2 slice index files."); + + await expect(project).toHaveFile("slices/a/index.js", { contains: sliceA.id }); + await expect(project).toHaveFile("slices/b/index.js", { contains: sliceB.id }); +}); diff --git a/test/slice-edit.test.ts b/test/slice-edit.test.ts index 747a901..33b6f1e 100644 --- a/test/slice-edit.test.ts +++ b/test/slice-edit.test.ts @@ -24,3 +24,15 @@ it("edits a slice name", async ({ expect, prismic, project }) => { const updated = await readLocalSlice(project, slice.id); expect(updated?.name).toBe(newName); }); + +it("regenerates the slice library index file", async ({ expect, prismic, project }) => { + const slice = buildSlice(); + await writeLocalSlice(project, slice); + + const newName = `SliceS${crypto.randomUUID().split("-")[0]}`; + + const { stderr, exitCode } = await prismic("slice", ["edit", slice.id, "--name", newName]); + expect(exitCode, stderr).toBe(0); + + await expect(project).toHaveFile("slices/index.js", { contains: newName }); +});