From 77bcf7f235abb0336faa4535f852f93def3491b9 Mon Sep 17 00:00:00 2001 From: lihbr Date: Tue, 23 Jun 2026 15:28:22 +0900 Subject: [PATCH 1/2] fix: create new slices in the first configured library When prismic.config.json defines slice libraries, pull and slice create were writing new slices to the framework default path instead of the first listed library, causing them to be missed on subsequent push/pull cycles. Co-authored-by: Cursor --- src/adapters/index.ts | 2 +- test/it.ts | 32 +++++++++++++++++++++++++------- test/pull.test.ts | 30 ++++++++++++++++++++++++++++++ test/setup.ts | 25 +++++++++++++++++++++---- test/slice-create.test.ts | 22 ++++++++++++++++++++++ 5 files changed, 99 insertions(+), 12 deletions(-) diff --git a/src/adapters/index.ts b/src/adapters/index.ts index d2c2aea..bd54e1e 100644 --- a/src/adapters/index.ts +++ b/src/adapters/index.ts @@ -106,7 +106,7 @@ export abstract class Adapter { } async createSlice(model: SharedSlice, library?: URL): Promise { - library ??= await this.getDefaultSliceLibrary(); + library ??= (await this.getSliceLibraries())[0]; const sliceDirectoryName = pascalCase(model.name); const sliceDirectory = new URL(sliceDirectoryName, appendTrailingSlash(library)); const modelPath = new URL("model.json", appendTrailingSlash(sliceDirectory)); diff --git a/test/it.ts b/test/it.ts index 8b912ae..7dff17d 100644 --- a/test/it.ts +++ b/test/it.ts @@ -197,8 +197,21 @@ export async function readLocalCustomTypes(project: URL): Promise return result; } +export async function getSliceLibraryPaths(project: URL): Promise { + const configPath = new URL("prismic.config.json", project); + try { + const config: { libraries?: string[] } = JSON.parse(await readFile(configPath, "utf8")); + const libraries = config.libraries; + if (libraries && libraries.length >= 1) { + return libraries.map((library) => library.replace(/\\/g, "").replace(/^\.\//, "")); + } + } catch {} + return ["slices"]; +} + export async function writeLocalSlice(project: URL, model: SharedSlice): Promise { - const path = new URL(`slices/${pascalCase(model.name)}/model.json`, project); + const [library] = await getSliceLibraryPaths(project); + const path = new URL(`${library}/${pascalCase(model.name)}/model.json`, project); await mkdir(new URL(".", path), { recursive: true }); await writeFile(path, JSON.stringify(model, null, 2)); } @@ -209,13 +222,18 @@ export async function readLocalSlice(project: URL, id: string): Promise { - const dir = new URL("slices/", project); - const entries = await readdir(dir).catch(() => [] as string[]); + const libraries = await getSliceLibraryPaths(project); const result: SharedSlice[] = []; - for (const name of entries) { - try { - result.push(JSON.parse(await readFile(new URL(`${name}/model.json`, dir), "utf8"))); - } catch {} + + for (const library of libraries) { + const dir = new URL(`${library}/`, project); + const entries = await readdir(dir).catch(() => [] as string[]); + for (const name of entries) { + try { + result.push(JSON.parse(await readFile(new URL(`${name}/model.json`, dir), "utf8"))); + } catch {} + } } + return result; } diff --git a/test/pull.test.ts b/test/pull.test.ts index c3eb567..af3bbb6 100644 --- a/test/pull.test.ts +++ b/test/pull.test.ts @@ -1,6 +1,7 @@ import { writeFile, mkdir } from "node:fs/promises"; import { sep } from "node:path"; import { fileURLToPath } from "node:url"; +import { pascalCase } from "change-case"; import { x } from "tinyexec"; import { buildCustomType, buildSlice, it } from "./it"; @@ -101,6 +102,35 @@ it.sequential("adds new slice to existing library on re-pull", async ({ await expect(project).toContainSlice(sliceB); }); +it.sequential("pulls new slices into the first configured library", async ({ + expect, + project, + prismic, + repo, + token, + host, +}) => { + const slice = buildSlice(); + + await writeFile( + new URL("prismic.config.json", project), + JSON.stringify({ + repositoryName: repo, + libraries: ["./slices/blog", "./slices/features"], + }), + ); + + await insertSlice(slice, { repo, token, host }); + + const { exitCode } = await prismic("pull", ["--repo", repo]); + expect(exitCode).toBe(0); + + const sliceDirectoryName = pascalCase(slice.name); + await expect(project).toContainSlice(slice); + await expect(project).toHaveFile(`slices/blog/${sliceDirectoryName}/model.json`); + await expect(project).not.toHaveFile(`slices/${sliceDirectoryName}/model.json`); +}); + it.sequential("removes deleted slice and updates index on re-pull", async ({ expect, project, diff --git a/test/setup.ts b/test/setup.ts index 6bac5c3..51acc52 100644 --- a/test/setup.ts +++ b/test/setup.ts @@ -1,8 +1,11 @@ import type { CustomType, SharedSlice } from "@prismicio/types-internal/lib/customtypes"; -import { readFile } from "node:fs/promises"; +import { pascalCase } from "change-case"; +import { access, readFile } from "node:fs/promises"; import { expect } from "vitest"; +import { getSliceLibraryPaths } from "./it"; + declare module "vitest" { // oxlint-disable-next-line no-explicit-any interface Matchers { @@ -38,8 +41,22 @@ expect.extend({ async toContainSlice(project, slice) { const problems: string[] = []; + const libraries = await getSliceLibraryPaths(project); + const sliceDirectoryName = pascalCase(slice.name); + let sliceLibrary: string | undefined; + + for (const library of libraries) { + const sliceDirectory = new URL(`${library}/${sliceDirectoryName}/`, project); + try { + await access(sliceDirectory); + sliceLibrary = library; + break; + } catch {} + } + + const library = sliceLibrary ?? libraries[0]!; - const sliceIndexPath = new URL("slices/index.js", project); + const sliceIndexPath = new URL(`${library}/index.js`, project); try { const sliceIndex = await readFile(sliceIndexPath, "utf8"); if (!new RegExp(`\\b${slice.id}: `).test(sliceIndex)) { @@ -51,7 +68,7 @@ expect.extend({ problems.push(`slice library index (${sliceIndexPath.href}) does not exist`); } - const modelPath = new URL(`slices/${slice.name}/model.json`, project); + const modelPath = new URL(`${library}/${sliceDirectoryName}/model.json`, project); try { const model: SharedSlice = JSON.parse(await readFile(modelPath, "utf8")); if (model.id !== slice.id) { @@ -61,7 +78,7 @@ expect.extend({ problems.push(`slice model file (${modelPath.href}) does not exist`); } - const componentPath = new URL(`slices/${slice.name}/index.jsx`, project); + const componentPath = new URL(`${library}/${sliceDirectoryName}/index.jsx`, project); try { const componentFile = await readFile(componentPath, "utf-8"); if (!componentFile.includes(slice.name)) { diff --git a/test/slice-create.test.ts b/test/slice-create.test.ts index 1fa38af..2daa89d 100644 --- a/test/slice-create.test.ts +++ b/test/slice-create.test.ts @@ -1,4 +1,5 @@ import { snakeCase } from "change-case"; +import { writeFile } from "node:fs/promises"; import { buildSlice, it, readLocalSlice } from "./it"; @@ -32,3 +33,24 @@ it("creates a slice with a custom id", async ({ expect, prismic, project }) => { const created = await readLocalSlice(project, id); expect(created).toBeDefined(); }); + +it("creates a slice in the first configured library", async ({ expect, prismic, project, repo }) => { + await writeFile( + new URL("prismic.config.json", project), + JSON.stringify({ + repositoryName: repo, + libraries: ["./slices/blog", "./slices/features"], + }), + ); + + const { name } = buildSlice(); + const id = snakeCase(name); + + const { exitCode } = await prismic("slice", ["create", name]); + expect(exitCode).toBe(0); + + const created = await readLocalSlice(project, id); + expect(created).toBeDefined(); + await expect(project).toHaveFile(`slices/blog/${name}/model.json`); + await expect(project).not.toHaveFile(`slices/${name}/model.json`); +}); From 20bbcef8e138e6c7b49fbf5dddac92ec2ca6e572 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Tue, 23 Jun 2026 12:26:31 -1000 Subject: [PATCH 2/2] refactor: PR 199 (#200) --- test/it.ts | 33 ++++++++++------------ test/setup.ts | 76 ++++++++++++++++++++++++--------------------------- 2 files changed, 51 insertions(+), 58 deletions(-) diff --git a/test/it.ts b/test/it.ts index 7dff17d..f071994 100644 --- a/test/it.ts +++ b/test/it.ts @@ -197,21 +197,15 @@ export async function readLocalCustomTypes(project: URL): Promise return result; } -export async function getSliceLibraryPaths(project: URL): Promise { - const configPath = new URL("prismic.config.json", project); - try { - const config: { libraries?: string[] } = JSON.parse(await readFile(configPath, "utf8")); - const libraries = config.libraries; - if (libraries && libraries.length >= 1) { - return libraries.map((library) => library.replace(/\\/g, "").replace(/^\.\//, "")); - } - } catch {} - return ["slices"]; +export async function getSliceLibraries(project: URL): Promise { + const { libraries = [] } = await getConfig(project); + if (libraries.length < 1) return [new URL("slices/", project)]; + return libraries.map((library) => new URL(library.replace(/^\//, "") + "/", project)); } export async function writeLocalSlice(project: URL, model: SharedSlice): Promise { - const [library] = await getSliceLibraryPaths(project); - const path = new URL(`${library}/${pascalCase(model.name)}/model.json`, project); + const [library] = await getSliceLibraries(project); + const path = new URL(`${pascalCase(model.name)}/model.json`, library); await mkdir(new URL(".", path), { recursive: true }); await writeFile(path, JSON.stringify(model, null, 2)); } @@ -222,18 +216,21 @@ export async function readLocalSlice(project: URL, id: string): Promise { - const libraries = await getSliceLibraryPaths(project); + const libraries = await getSliceLibraries(project); const result: SharedSlice[] = []; - for (const library of libraries) { - const dir = new URL(`${library}/`, project); - const entries = await readdir(dir).catch(() => [] as string[]); + const entries = await readdir(library).catch(() => []); for (const name of entries) { try { - result.push(JSON.parse(await readFile(new URL(`${name}/model.json`, dir), "utf8"))); + result.push(JSON.parse(await readFile(new URL(`${name}/model.json`, library), "utf8"))); } catch {} } } - return result; } + +async function getConfig(project: URL): Promise<{ libraries?: string[] }> { + const path = new URL("prismic.config.json", project); + const raw = await readFile(path, "utf8"); + return JSON.parse(raw); +} diff --git a/test/setup.ts b/test/setup.ts index 51acc52..c453fd0 100644 --- a/test/setup.ts +++ b/test/setup.ts @@ -1,10 +1,11 @@ import type { CustomType, SharedSlice } from "@prismicio/types-internal/lib/customtypes"; -import { pascalCase } from "change-case"; -import { access, readFile } from "node:fs/promises"; +import { readFile } from "node:fs/promises"; +import { pathToFileURL } from "node:url"; +import { glob } from "tinyglobby"; import { expect } from "vitest"; -import { getSliceLibraryPaths } from "./it"; +import { getSliceLibraries } from "./it"; declare module "vitest" { // oxlint-disable-next-line no-explicit-any @@ -41,51 +42,46 @@ expect.extend({ async toContainSlice(project, slice) { const problems: string[] = []; - const libraries = await getSliceLibraryPaths(project); - const sliceDirectoryName = pascalCase(slice.name); - let sliceLibrary: string | undefined; + const libraries = await getSliceLibraries(project); + let sliceDirectory; for (const library of libraries) { - const sliceDirectory = new URL(`${library}/${sliceDirectoryName}/`, project); - try { - await access(sliceDirectory); - sliceLibrary = library; - break; - } catch {} - } - - const library = sliceLibrary ?? libraries[0]!; - - const sliceIndexPath = new URL(`${library}/index.js`, project); - try { - const sliceIndex = await readFile(sliceIndexPath, "utf8"); - if (!new RegExp(`\\b${slice.id}: `).test(sliceIndex)) { - problems.push( - `slice "${slice.id}" not found in slice library index (${sliceIndexPath.href})`, - ); + const sliceModelPaths = Array.from( + await glob("*/model.json", { absolute: true, cwd: library }), + (path) => pathToFileURL(path), + ); + for (const sliceModelPath of sliceModelPaths) { + const model: SharedSlice = JSON.parse(await readFile(sliceModelPath, "utf8")); + if (model.id === slice.id) { + sliceDirectory = new URL(".", sliceModelPath); + } } - } catch { - problems.push(`slice library index (${sliceIndexPath.href}) does not exist`); } - const modelPath = new URL(`${library}/${sliceDirectoryName}/model.json`, project); - try { - const model: SharedSlice = JSON.parse(await readFile(modelPath, "utf8")); - if (model.id !== slice.id) { - problems.push(`slice model file (${modelPath.href}) has wrong ID`); + if (!sliceDirectory) { + problems.push(`slice model file does not exist`); + } else { + const sliceIndexPath = new URL("../index.js", sliceDirectory); + try { + const sliceIndex = await readFile(sliceIndexPath, "utf8"); + if (!new RegExp(`\\b${slice.id}: `).test(sliceIndex)) { + problems.push( + `slice "${slice.id}" not found in slice library index (${sliceIndexPath.href})`, + ); + } + } catch { + problems.push(`slice library index (${sliceIndexPath.href}) does not exist`); } - } catch { - problems.push(`slice model file (${modelPath.href}) does not exist`); - } - const componentPath = new URL(`${library}/${sliceDirectoryName}/index.jsx`, project); - try { - const componentFile = await readFile(componentPath, "utf-8"); - if (!componentFile.includes(slice.name)) { - problems.push(`slice component file (${componentPath.href}) does not contain slice name`); + const componentPath = new URL("index.jsx", sliceDirectory); + try { + const componentFile = await readFile(componentPath, "utf-8"); + if (!componentFile.includes(slice.name)) { + problems.push(`slice component file (${componentPath.href}) does not contain slice name`); + } + } catch { + problems.push(`slice component file (${componentPath.href}) does not exist`); } - } catch { - problems.push(`slice component file (${componentPath.href}) does not exist`); } return {