Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
969451b
feat(sveltekit): install the @prismicio/svelte major that fits the pr…
claude Oct 14, 2026
b23e382
refactor(sveltekit): support only @prismicio/svelte v3
claude Oct 14, 2026
06163b0
refactor(sveltekit): remove code that only served Svelte 4
claude Oct 14, 2026
e3cbe28
fix(sveltekit): import from #lib instead of $lib
claude Oct 14, 2026
e1f5e1d
feat(sveltekit): declare the Prismic environment in src/env
claude Oct 14, 2026
d67f1d4
feat(env): print setup steps after prismic env set
claude Oct 14, 2026
f1a96ee
refactor(sveltekit): pass the file extension to templates
claude Oct 14, 2026
4ec76cb
feat(sveltekit): keep SvelteKit 2 projects on @prismicio/svelte v2
claude Oct 14, 2026
9b90320
refactor(sveltekit): derive SvelteKit 2 templates from SvelteKit 3
claude Oct 14, 2026
e7f8998
refactor(adapters): read the declared range in getInstalledMajor
claude Oct 14, 2026
b332d3e
refactor: move getInstalledMajor to lib/packageJson
claude Oct 14, 2026
9b0653b
test(sveltekit): cover the setup logic in the SvelteKit 2 adapter
claude Oct 14, 2026
977df0f
test(sveltekit): merge two SvelteKit 2 server layout tests
claude Oct 14, 2026
3c5d817
refactor(sveltekit): stop generating Svelte 4 code
claude Oct 14, 2026
abb64ec
refactor: read the declared version when a package is not installed
claude Oct 14, 2026
d990dd9
feat(env): print open setup steps after prismic env set
claude Oct 14, 2026
4ce6471
feat(sveltekit): support SvelteKit 3 and @prismicio/svelte v3
claude Oct 14, 2026
c0aa3f7
chore: keep the earlier history of this branch
claude Oct 14, 2026
b2e7ccd
test(env): rename the setup instructions test and drop a comment
claude Oct 14, 2026
45ecde3
chore: merge the env set test rename
claude Oct 14, 2026
02ff55f
fix: read peer dependencies in getInstalledMajor
claude Oct 14, 2026
cbc7e51
chore: merge the getInstalledMajor review fixes
claude Oct 14, 2026
b45689c
chore: merge the getInstalledMajor review fixes
claude Oct 14, 2026
a7d1ee0
refactor: remove a comment in getInstalledMajor
claude Oct 14, 2026
d799218
chore: merge the getInstalledMajor comment removal
claude Oct 14, 2026
f5c5e2c
chore: merge the getInstalledMajor comment removal
claude Oct 14, 2026
ec6e180
test: split the Next.js declared version test
claude Oct 14, 2026
58c6597
chore: merge the Next.js test split
claude Oct 14, 2026
d520f17
chore: merge the Next.js test split
claude Oct 14, 2026
3fa2665
test: remove a comment in the Next.js 15 test
claude Oct 14, 2026
5e28f10
chore: merge the Next.js test comment removal
claude Oct 14, 2026
eab44a2
chore: merge the Next.js test comment removal
claude Oct 14, 2026
be4fb2f
chore: merge main
claude Oct 14, 2026
7ede5b2
chore: merge main
claude Oct 14, 2026
cbef55a
chore: merge main
claude Oct 14, 2026
e202308
chore: merge main
claude Oct 14, 2026
2a2cb8c
chore: merge main
claude Oct 14, 2026
128a537
chore: merge main
claude Oct 14, 2026
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
4 changes: 3 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ console output.
## `src/adapters/`

Framework-specific integrations. One file per framework, with a sibling
`*.templates.ts` file for generated file content.
`*.templates.ts` file for generated file content. When an older framework major
needs different generated code, it gets its own adapter that extends the current
one, such as `sveltekit2.ts`. Delete it when support for that major ends.

## `src/subprocesses/`

Expand Down
2 changes: 1 addition & 1 deletion evals/set-up-previews.eval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ it.for(trials)(
await mkdir(new URL("src/routes/", project), { recursive: true });
await writeFile(
new URL("src/routes/+layout.svelte", project),
"<script>\n\timport favicon from '$lib/assets/favicon.svg';\n\n" +
"<script>\n\timport favicon from '#lib/assets/favicon.svg';\n\n" +
"\tlet { children } = $props();\n</script>\n\n" +
'<svelte:head>\n\t<link rel="icon" href={favicon} />\n</svelte:head>\n\n' +
"<p>KEEP-ME</p>\n{@render children()}\n",
Expand Down
6 changes: 5 additions & 1 deletion src/adapters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
writeFileRecursive,
} from "../lib/file";
import { stringify } from "../lib/json";
import { readPackageJson } from "../lib/packageJson";
import { getInstalledMajor, readPackageJson } from "../lib/packageJson";
import {
addPreview,
getPreviews,
Expand Down Expand Up @@ -63,6 +63,10 @@ export async function getAdapter(): Promise<Adapter> {
return new NuxtAdapter();
}
if ("@sveltejs/kit" in allDependencies) {
if ((await getInstalledMajor("@sveltejs/kit")) < 3) {
const { SvelteKit2Adapter } = await import("./sveltekit2");
return new SvelteKit2Adapter();
}
const { SvelteKitAdapter } = await import("./sveltekit");
return new SvelteKitAdapter();
}
Expand Down
58 changes: 40 additions & 18 deletions src/adapters/sveltekit.templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ export function prismicIOFileTemplate({ typescript }: { typescript: boolean }):
return dedent`
import { createClient as baseCreateClient } from "@prismicio/client";
import { type CreateClientConfig, enableAutoPreviews } from '@prismicio/svelte/kit';
import { env } from '$env/dynamic/public';
import { PUBLIC_PRISMIC_ENVIRONMENT } from '$app/env/public';
import prismicConfig from "../../prismic.config.json";

/**
* The project's Prismic repository name.
*/
export const repositoryName =
env.PUBLIC_PRISMIC_ENVIRONMENT || prismicConfig.repositoryName;
PUBLIC_PRISMIC_ENVIRONMENT || prismicConfig.repositoryName;

/**
* Creates a Prismic client for the project's repository. The client is used to
Expand All @@ -39,14 +39,14 @@ export function prismicIOFileTemplate({ typescript }: { typescript: boolean }):
return dedent`
import { createClient as baseCreateClient } from "@prismicio/client";
import { enableAutoPreviews } from '@prismicio/svelte/kit';
import { env } from '$env/dynamic/public';
import { PUBLIC_PRISMIC_ENVIRONMENT } from '$app/env/public';
import prismicConfig from "../../prismic.config.json";

/**
* The project's Prismic repository name.
*/
export const repositoryName =
env.PUBLIC_PRISMIC_ENVIRONMENT || prismicConfig.repositoryName;
PUBLIC_PRISMIC_ENVIRONMENT || prismicConfig.repositoryName;

/**
* Creates a Prismic client for the project's repository. The client is used to
Expand All @@ -67,15 +67,37 @@ export function prismicIOFileTemplate({ typescript }: { typescript: boolean }):
`;
}

export function sliceSimulatorPageTemplate(): string {
export function envVarTemplate(): string {
return dedent`
PUBLIC_PRISMIC_ENVIRONMENT: {
public: true,
schema: (value) => value,
description: "The Prismic environment to use instead of the production repository.",
},
`;
}

export function envFileTemplate(): string {
return dedent`
import { defineEnvVars } from "@sveltejs/kit/env";

export const variables = defineEnvVars({
${envVarTemplate()}
});
`;
}

export function sliceSimulatorPageTemplate({ extension }: { extension: string }): string {
return dedent`
<script>
import { SliceSimulator, SliceZone } from '@prismicio/svelte';
import { components } from '$lib/slices';
import { components } from '#lib/slices/index.${extension}';
</script>

<SliceSimulator let:slices>
<SliceZone {slices} {components} />
<SliceSimulator>
{#snippet children({ slices })}
<SliceZone {slices} {components} />
{/snippet}
</SliceSimulator>
`;
}
Expand All @@ -84,7 +106,7 @@ export function previewAPIRouteTemplate({ typescript }: { typescript: boolean })
if (typescript) {
return dedent`
import { redirectToPreviewURL } from '@prismicio/svelte/kit';
import { createClient } from '$lib/prismicio';
import { createClient } from '#lib/prismicio.ts';
import type { RequestHandler } from "./$types";

export const GET: RequestHandler = async ({ fetch, request, cookies }) => {
Expand All @@ -97,7 +119,7 @@ export function previewAPIRouteTemplate({ typescript }: { typescript: boolean })

return dedent`
import { redirectToPreviewURL } from '@prismicio/svelte/kit';
import { createClient } from '$lib/prismicio';
import { createClient } from '#lib/prismicio.js';

/* @type {import("./$types").RequestHandler} */
export async function GET({ fetch, request, cookies }) {
Expand All @@ -108,9 +130,9 @@ export function previewAPIRouteTemplate({ typescript }: { typescript: boolean })
`;
}

export function layoutServerTemplate(): string {
export function layoutServerTemplate({ extension }: { extension: string }): string {
return dedent`
import { repositoryName } from "$lib/prismicio";
import { repositoryName } from "#lib/prismicio.${extension}";

export const prerender = "auto";

Expand Down Expand Up @@ -151,7 +173,7 @@ export function pageTemplate({ typescript }: { typescript: boolean }): string {
return dedent`
<script lang="ts">
import { SliceZone } from "@prismicio/svelte";
import { components } from "$lib/slices";
import { components } from "#lib/slices/index.ts";
import type { PageProps } from "./$types";

const { data }: PageProps = $props();
Expand All @@ -164,7 +186,7 @@ export function pageTemplate({ typescript }: { typescript: boolean }): string {
return dedent`
<script>
import { SliceZone } from "@prismicio/svelte";
import { components } from "$lib/slices";
import { components } from "#lib/slices/index.js";

const { data } = $props();
</script>
Expand All @@ -184,7 +206,7 @@ export function pageServerTemplate({
if (typescript) {
return dedent`
import type { PageServerLoad } from "./$types";
import { createClient } from "$lib/prismicio";
import { createClient } from "#lib/prismicio.ts";

export const load: PageServerLoad = async ({ params, fetch, cookies }) => {
const client = createClient({ fetch, cookies });
Expand All @@ -196,7 +218,7 @@ export function pageServerTemplate({
}

return dedent`
import { createClient } from "$lib/prismicio";
import { createClient } from "#lib/prismicio.js";

/** @type {import("./$types").PageServerLoad} */
export async function load({ params, fetch, cookies }) {
Expand All @@ -211,7 +233,7 @@ export function pageServerTemplate({
if (typescript) {
return dedent`
import type { PageServerLoad } from "./$types";
import { createClient } from "$lib/prismicio";
import { createClient } from "#lib/prismicio.ts";

export const load: PageServerLoad = async ({ fetch, cookies }) => {
const client = createClient({ fetch, cookies });
Expand All @@ -223,7 +245,7 @@ export function pageServerTemplate({
}

return dedent`
import { createClient } from "$lib/prismicio";
import { createClient } from "#lib/prismicio.js";

/* @type {import("./$types").PageServerLoad} */
export async function load({ fetch, cookies }) {
Expand Down
48 changes: 38 additions & 10 deletions src/adapters/sveltekit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { addDependencies, getNpmPackageVersion } from "../lib/packageJson";
import { dedent, formatObjectKey } from "../lib/string";
import { checkIsTypeScriptProject, findProjectRoot } from "../project";
import {
envFileTemplate,
envVarTemplate,
layoutServerTemplate,
pageServerTemplate,
pageTemplate,
Expand Down Expand Up @@ -58,9 +60,12 @@ export class SvelteKitAdapter extends Adapter {
new URL(`src/lib/prismicio.${extension}`, projectRoot),
prismicIOFileTemplate({ typescript }),
);
if (!(await findSourceFile("src/env"))) {
await writeFileRecursive(new URL(`src/env.${extension}`, projectRoot), envFileTemplate());
}
await writeFileIfMissing(
new URL("src/routes/slice-simulator/+page.svelte", projectRoot),
sliceSimulatorPageTemplate(),
sliceSimulatorPageTemplate({ extension }),
);
await writeFileIfMissing(
new URL(`src/params/preview.${extension}`, projectRoot),
Expand All @@ -87,11 +92,11 @@ export class SvelteKitAdapter extends Adapter {
See <https://prismic.io/docs/svelte-preview> for more information.
`,
);
const serverLayout = await findServerLayout();
const serverLayout = await findSourceFile("src/routes/+layout.server");
if (!serverLayout) {
await writeFileRecursive(
new URL(`src/routes/+layout.server.${extension}`, projectRoot),
layoutServerTemplate(),
layoutServerTemplate({ extension }),
);
}
// The generated layout reads repositoryName from the server layout. When an existing
Expand Down Expand Up @@ -124,22 +129,45 @@ export class SvelteKitAdapter extends Adapter {
from it first.
`;

return [await this.getPreviewStep(), homePageStep].filter(Boolean).join("\n\n") || undefined;
return (
[await this.getEnvStep(), await this.getPreviewStep(), homePageStep]
.filter(Boolean)
.join("\n\n") || undefined
);
}

// The generated Prismic client imports PUBLIC_PRISMIC_ENVIRONMENT, which SvelteKit only
// provides when the project's env file declares it.
private async getEnvStep(): Promise<string | undefined> {
const envFile = await findSourceFile("src/env");
if (!envFile) return;
const contents = await readFile(new URL(envFile, await findProjectRoot()), "utf8");
if (contents.includes("PUBLIC_PRISMIC_ENVIRONMENT")) return;

return dedent`
Action required: add PUBLIC_PRISMIC_ENVIRONMENT to ${envFile}.

Your project does not build until you do this. Add these lines to
defineEnvVars({ ... }):

${envVarTemplate().replace(/^/gm, " ")}
`;
}

private async getPreviewStep(): Promise<string | undefined> {
if (await checkSourceContains("PrismicPreview")) return;

// The layout reads repositoryName from the server layout's data.
const serverLayoutPath =
(await findServerLayout()) ?? `src/routes/+layout.server.${await getJsFileExtension()}`;
(await findSourceFile("src/routes/+layout.server")) ??
`src/routes/+layout.server.${await getJsFileExtension()}`;
const serverLayout = await readFile(
new URL(serverLayoutPath, await findProjectRoot()),
"utf8",
).catch(() => "");
const serverLayoutStep =
!serverLayout.includes("repositoryName") &&
`Return repositoryName from load in ${serverLayoutPath}, creating the file if needed:\n\n${layoutServerTemplate().replace(/^/gm, " ")}`;
`Return repositoryName from load in ${serverLayoutPath}, creating the file if needed:\n\n${layoutServerTemplate({ extension: serverLayoutPath.split(".").pop()! }).replace(/^/gm, " ")}`;

return [
dedent`
Expand Down Expand Up @@ -219,17 +247,17 @@ export class SvelteKitAdapter extends Adapter {
}
}

async function deleteStarterHomePage(): Promise<void> {
export async function deleteStarterHomePage(): Promise<void> {
const path = new URL("src/routes/+page.svelte", await findProjectRoot());
const contents = await readFile(path, "utf8").catch(() => undefined);
if (!STARTER_HOME_PAGES.includes(contents?.replace(/\s/g, "") ?? "")) return;

await rm(path);
}

async function findServerLayout(): Promise<string | undefined> {
export async function findSourceFile(path: string): Promise<string | undefined> {
const projectRoot = await findProjectRoot();
for (const path of ["src/routes/+layout.server.js", "src/routes/+layout.server.ts"]) {
if (await exists(new URL(path, projectRoot))) return path;
for (const extension of ["js", "ts"]) {
if (await exists(new URL(`${path}.${extension}`, projectRoot))) return `${path}.${extension}`;
}
}
56 changes: 56 additions & 0 deletions src/adapters/sveltekit2.templates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import type { DynamicCustomTypeModel } from "@prismicio/types-internal";

import { dedent } from "../lib/string";
import * as sveltekit from "./sveltekit.templates";

// SvelteKit 2 has no #lib, so it imports from $lib without file extensions.
function useLib(code: string): string {
return code.replace(/#lib\/(\w+)(?:\/index)?\.\w+/g, "$lib/$1");
}

// SvelteKit 2 has no $app/env.
export function prismicIOFileTemplate({ typescript }: { typescript: boolean }): string {
return sveltekit
.prismicIOFileTemplate({ typescript })
.replace(
"{ PUBLIC_PRISMIC_ENVIRONMENT } from '$app/env/public'",
"{ env } from '$env/dynamic/public'",
)
.replace("PUBLIC_PRISMIC_ENVIRONMENT ||", "env.PUBLIC_PRISMIC_ENVIRONMENT ||");
}

// @prismicio/svelte v2 passes slices through a slot.
export function sliceSimulatorPageTemplate(): string {
return dedent`
<script>
import { SliceSimulator, SliceZone } from '@prismicio/svelte';
import { components } from '$lib/slices';
</script>

<SliceSimulator let:slices>
<SliceZone {slices} {components} />
</SliceSimulator>
`;
}

export function previewAPIRouteTemplate({ typescript }: { typescript: boolean }): string {
return useLib(sveltekit.previewAPIRouteTemplate({ typescript }));
}

export function layoutServerTemplate(): string {
return useLib(sveltekit.layoutServerTemplate({ extension: "js" }));
}

export function pageTemplate({ typescript }: { typescript: boolean }): string {
return useLib(sveltekit.pageTemplate({ typescript }));
}

export function pageServerTemplate({
model,
typescript,
}: {
model: DynamicCustomTypeModel;
typescript: boolean;
}): string {
return useLib(sveltekit.pageServerTemplate({ model, typescript }));
}
Loading
Loading