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
35 changes: 35 additions & 0 deletions src/commands/gen-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { getAdapter } from "../adapters";
import { generateAndWriteTypes } from "../lib/codegen";
import { createCommand, type CommandConfig } from "../lib/command";
import { relativePathname } from "../lib/url";
import { findProjectRoot } from "../project";

const FILENAME = "prismicio-types.d.ts";

const config = {
name: "prismic gen types",
description: `
Generate TypeScript types from local custom type and slice models.

Reads models from the customtypes/ and slices directories, then writes
a prismicio-types.d.ts file at the project root.
`,
} satisfies CommandConfig;

export default createCommand(config, async () => {
const adapter = await getAdapter();
const slices = await adapter.getSlices();
const customTypes = await adapter.getCustomTypes();
const projectRoot = await findProjectRoot();

const output = new URL(FILENAME, projectRoot);
const relativeOutput = relativePathname(projectRoot, output);

await generateAndWriteTypes({
customTypes: customTypes.map((customType) => customType.model),
slices: slices.map((slice) => slice.model),
output,
});

console.info(`Generated types at ${relativeOutput}`);
});
13 changes: 13 additions & 0 deletions src/commands/gen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { createCommandRouter } from "../lib/command";
import genTypes from "./gen-types";

export default createCommandRouter({
name: "prismic gen",
description: "Generate files from local Prismic models.",
commands: {
types: {
handler: genTypes,
description: "Generate TypeScript types from local models",
},
},
});
3 changes: 2 additions & 1 deletion src/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,11 @@ export default createCommand(config, async ({ values }) => {
const slices = await adapter.getSlices();
const customTypes = await adapter.getCustomTypes();
const projectRoot = await findProjectRoot();
const output = new URL("prismicio-types.d.ts", projectRoot);
await generateAndWriteTypes({
customTypes: customTypes.map((customType) => customType.model),
slices: slices.map((slice) => slice.model),
projectRoot,
output,
});

console.info(`\nInitialized Prismic for repository "${repo}".`);
Expand Down
3 changes: 2 additions & 1 deletion src/commands/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,10 @@ async function regenerateTypes(adapter: Adapter): Promise<void> {
const slices = await adapter.getSlices();
const customTypes = await adapter.getCustomTypes();
const projectRoot = await findProjectRoot();
const output = new URL("prismicio-types.d.ts", projectRoot);
await generateAndWriteTypes({
customTypes: customTypes.map((customType) => customType.model),
slices: slices.map((slice) => slice.model),
projectRoot,
output,
});
}
5 changes: 5 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import packageJson from "../package.json" with { type: "json" };
import { getAdapter, NoSupportedFrameworkError } from "./adapters";
import { getHost, refreshToken } from "./auth";
import { getProfile } from "./clients/user";
import gen from "./commands/gen";
import init from "./commands/init";
import locale from "./commands/locale";
import login from "./commands/login";
Expand Down Expand Up @@ -45,6 +46,10 @@ const router = createCommandRouter({
handler: init,
description: "Initialize a Prismic project",
},
gen: {
handler: gen,
description: "Generate files from local models",
},
sync: {
handler: sync,
description: "Sync types and slices from Prismic",
Expand Down
11 changes: 6 additions & 5 deletions src/lib/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,19 @@ import { generateTypes } from "prismic-ts-codegen";
export async function generateAndWriteTypes(args: {
customTypes: CustomType[];
slices: SharedSlice[];
projectRoot: URL;
output: URL;
}): Promise<void> {
const { customTypes, slices, output } = args;

const types = generateTypes({
customTypeModels: args.customTypes,
sharedSliceModels: args.slices,
customTypeModels: customTypes,
sharedSliceModels: slices,
clientIntegration: {
includeContentNamespace: true,
includeCreateClientInterface: true,
},
cache: true,
typesProvider: "@prismicio/client",
});
const outputPath = new URL("prismicio-types.d.ts", args.projectRoot);
await writeFile(outputPath, types);
await writeFile(output, types);
}
7 changes: 7 additions & 0 deletions src/lib/url.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { relative } from "node:path";
import { fileURLToPath } from "node:url";

export function appendTrailingSlash(url: string | URL): URL {
const newURL = new URL(url);
if (!newURL.pathname.endsWith("/")) newURL.pathname += "/";
return newURL;
}

export function relativePathname(a: URL, b: URL): string {
return relative(fileURLToPath(a), fileURLToPath(b));
}
69 changes: 69 additions & 0 deletions test/gen-types.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import type { CustomType, SharedSlice } from "@prismicio/types-internal/lib/customtypes";

import { mkdir, writeFile } from "node:fs/promises";

import { it } from "./it";

it("supports --help", async ({ expect, prismic }) => {
const { stdout, exitCode } = await prismic("gen", ["types", "--help"]);
expect(exitCode).toBe(0);
expect(stdout).toContain("prismic gen types [options]");
});

it("generates types from local models", async ({ expect, project, prismic }) => {
const customType = buildCustomType();
const customTypePath = new URL(`customtypes/${customType.id}/index.json`, project);
await mkdir(new URL(".", customTypePath), { recursive: true });
await writeFile(customTypePath, JSON.stringify(customType));

const slice = buildSlice();
const slicePath = new URL(`slices/${slice.name}/model.json`, project);
await mkdir(new URL(".", slicePath), { recursive: true });
await writeFile(slicePath, JSON.stringify(slice));

const { exitCode, stdout } = await prismic("gen", ["types"]);
expect(exitCode).toBe(0);
expect(stdout).toContain("Generated types");

await expect(project).toHaveFile("prismicio-types.d.ts", {
contains: customType.id,
});
});

it("generates types with no models", async ({ expect, project, prismic }) => {
const { exitCode, stdout } = await prismic("gen", ["types"]);
expect(exitCode).toBe(0);
expect(stdout).toContain("Generated types");

await expect(project).toHaveFile("prismicio-types.d.ts");
});

function buildCustomType(): CustomType {
const id = crypto.randomUUID().split("-")[0];
return {
id: `type-T${id}`,
label: `TypeT${id}`,
repeatable: true,
status: true,
json: {},
};
}

function buildSlice(): SharedSlice {
const id = crypto.randomUUID().split("-")[0];
return {
id: `slice-S${id}`,
type: "SharedSlice",
name: `SliceS${id}`,
variations: [
{
id: "default",
name: "Default",
docURL: "",
version: "initial",
description: "Default",
imageUrl: "",
},
],
};
}
13 changes: 13 additions & 0 deletions test/gen.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { it } from "./it";

it("supports --help", async ({ expect, prismic }) => {
const { stdout, exitCode } = await prismic("gen", ["--help"]);
expect(exitCode).toBe(0);
expect(stdout).toContain("prismic gen <command> [options]");
});

it("prints help by default", async ({ expect, prismic }) => {
const { stdout, exitCode } = await prismic("gen");
expect(exitCode).toBe(0);
expect(stdout).toContain("prismic gen <command> [options]");
});
3 changes: 2 additions & 1 deletion vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ export default defineConfig({
globalSetup: ["./test/setup.global.ts"],
forceRerunTriggers: ["**/dist/index.mjs"],
typecheck: { enabled: true },
testTimeout: 10_000,
retry: 1,
projects: [
{
Expand All @@ -15,6 +14,7 @@ export default defineConfig({
include: ["./test/**/*.test.ts"],
exclude: ["./test/*.serial.test.ts"],
sequence: { concurrent: true },
testTimeout: 10_000,
},
},
{
Expand All @@ -24,6 +24,7 @@ export default defineConfig({
include: ["./test/*.serial.test.ts"],
sequence: { concurrent: false },
fileParallelism: false,
testTimeout: 10_000,
},
},
],
Expand Down