Skip to content

Commit bc2e405

Browse files
authored
chore(release): 1.4.16
* feat(cms): extend CMS support to all frontend frameworks Sanity, Strapi, and TinaCMS now work with all frontend frameworks: Next.js, Astro, Nuxt, SvelteKit, TanStack Router, React Router, React Vite, and TanStack Start. Payload stays Next.js-only (v3 architecture requires @payloadcms/next). - Rewrite CMS handler with variant detection (analytics.ts pattern) - Framework-aware dependency installation (@sanity/client for non-Next) - Framework-appropriate env var prefixes (VITE_, PUBLIC_, NUXT_PUBLIC_) - 81 new template files across 4 variants per CMS - Payload disabled in web builder for non-Next.js frontends - 43 CMS tests passing (13 new multi-framework tests) * fix: remove hardcoded Next.js fallbacks from TinaCMS script wrapping
1 parent db189f7 commit bc2e405

87 files changed

Lines changed: 3837 additions & 128 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/cli/test/cms.test.ts

Lines changed: 144 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -465,56 +465,183 @@ describe("CMS Options", () => {
465465
});
466466
});
467467

468-
describe("CMS not supported with non-Next.js frontends", () => {
469-
test("payload without Next.js should still work (cms deps skipped)", async () => {
468+
describe("Payload stays Next.js-only", () => {
469+
test("payload without Next.js still creates project (cms skipped)", async () => {
470470
const result = await runTRPCTest(
471471
createCustomConfig({
472-
projectName: "payload-tanstack-router",
472+
projectName: "payload-tanstack-skip",
473473
frontend: ["tanstack-router"],
474474
cms: "payload",
475475
}),
476476
);
477-
// Payload requires Next.js, but the project should still be created
478-
// The CMS deps processor will skip adding Payload deps for non-Next.js
479477
expectSuccess(result);
480478
});
479+
});
481480

482-
test("sanity without Next.js should still work (cms deps skipped)", async () => {
481+
describe("Sanity with non-Next.js frameworks", () => {
482+
test("sanity with TanStack Router", async () => {
483483
const result = await runTRPCTest(
484484
createCustomConfig({
485-
projectName: "sanity-tanstack-router",
485+
projectName: "sanity-tanstack",
486486
frontend: ["tanstack-router"],
487487
cms: "sanity",
488488
}),
489489
);
490-
// Sanity requires Next.js for optimal integration, but the project should still be created
491-
// The CMS deps processor will skip adding Sanity deps for non-Next.js
492490
expectSuccess(result);
493491
});
494492

495-
test("strapi without Next.js should still work (cms deps skipped)", async () => {
493+
test("sanity with Astro", async () => {
494+
const result = await runTRPCTest(
495+
createCustomConfig({
496+
projectName: "sanity-astro",
497+
frontend: ["astro"],
498+
astroIntegration: "react",
499+
backend: "hono",
500+
runtime: "bun",
501+
api: "trpc",
502+
cms: "sanity",
503+
}),
504+
);
505+
expectSuccess(result);
506+
});
507+
508+
test("sanity with Nuxt", async () => {
509+
const result = await runTRPCTest(
510+
createCustomConfig({
511+
projectName: "sanity-nuxt",
512+
frontend: ["nuxt"],
513+
backend: "self",
514+
runtime: "none",
515+
api: "orpc",
516+
cms: "sanity",
517+
}),
518+
);
519+
expectSuccess(result);
520+
});
521+
522+
test("sanity with SvelteKit", async () => {
523+
const result = await runTRPCTest(
524+
createCustomConfig({
525+
projectName: "sanity-svelte",
526+
frontend: ["svelte"],
527+
backend: "self",
528+
runtime: "none",
529+
api: "orpc",
530+
cms: "sanity",
531+
}),
532+
);
533+
expectSuccess(result);
534+
});
535+
});
536+
537+
describe("Strapi with non-Next.js frameworks", () => {
538+
test("strapi with TanStack Router", async () => {
496539
const result = await runTRPCTest(
497540
createCustomConfig({
498-
projectName: "strapi-tanstack-router",
541+
projectName: "strapi-tanstack",
499542
frontend: ["tanstack-router"],
500543
cms: "strapi",
501544
}),
502545
);
503-
// Strapi client requires Next.js for our templates, but the project should still be created
504-
// The CMS deps processor will skip adding Strapi deps for non-Next.js
505546
expectSuccess(result);
506547
});
507548

508-
test("tinacms without Next.js should still work (cms deps skipped)", async () => {
549+
test("strapi with Astro", async () => {
550+
const result = await runTRPCTest(
551+
createCustomConfig({
552+
projectName: "strapi-astro",
553+
frontend: ["astro"],
554+
astroIntegration: "react",
555+
backend: "hono",
556+
runtime: "bun",
557+
api: "trpc",
558+
cms: "strapi",
559+
}),
560+
);
561+
expectSuccess(result);
562+
});
563+
564+
test("strapi with Nuxt", async () => {
509565
const result = await runTRPCTest(
510566
createCustomConfig({
511-
projectName: "tinacms-tanstack-router",
567+
projectName: "strapi-nuxt",
568+
frontend: ["nuxt"],
569+
backend: "self",
570+
runtime: "none",
571+
api: "orpc",
572+
cms: "strapi",
573+
}),
574+
);
575+
expectSuccess(result);
576+
});
577+
578+
test("strapi with SvelteKit", async () => {
579+
const result = await runTRPCTest(
580+
createCustomConfig({
581+
projectName: "strapi-svelte",
582+
frontend: ["svelte"],
583+
backend: "self",
584+
runtime: "none",
585+
api: "orpc",
586+
cms: "strapi",
587+
}),
588+
);
589+
expectSuccess(result);
590+
});
591+
});
592+
593+
describe("TinaCMS with non-Next.js frameworks", () => {
594+
test("tinacms with TanStack Router", async () => {
595+
const result = await runTRPCTest(
596+
createCustomConfig({
597+
projectName: "tinacms-tanstack",
512598
frontend: ["tanstack-router"],
513599
cms: "tinacms",
514600
}),
515601
);
516-
// TinaCMS requires Next.js for our templates, but the project should still be created
517-
// The CMS deps processor will skip adding TinaCMS deps for non-Next.js
602+
expectSuccess(result);
603+
});
604+
605+
test("tinacms with Astro", async () => {
606+
const result = await runTRPCTest(
607+
createCustomConfig({
608+
projectName: "tinacms-astro",
609+
frontend: ["astro"],
610+
astroIntegration: "react",
611+
backend: "hono",
612+
runtime: "bun",
613+
api: "trpc",
614+
cms: "tinacms",
615+
}),
616+
);
617+
expectSuccess(result);
618+
});
619+
620+
test("tinacms with Nuxt", async () => {
621+
const result = await runTRPCTest(
622+
createCustomConfig({
623+
projectName: "tinacms-nuxt",
624+
frontend: ["nuxt"],
625+
backend: "self",
626+
runtime: "none",
627+
api: "orpc",
628+
cms: "tinacms",
629+
}),
630+
);
631+
expectSuccess(result);
632+
});
633+
634+
test("tinacms with SvelteKit", async () => {
635+
const result = await runTRPCTest(
636+
createCustomConfig({
637+
projectName: "tinacms-svelte",
638+
frontend: ["svelte"],
639+
backend: "self",
640+
runtime: "none",
641+
api: "orpc",
642+
cms: "tinacms",
643+
}),
644+
);
518645
expectSuccess(result);
519646
});
520647
});

packages/template-generator/src/processors/cms-deps.ts

Lines changed: 54 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -6,92 +6,84 @@ import { addPackageDependency, type AvailableDependencies } from "../utils/add-d
66

77
export function processCMSDeps(vfs: VirtualFileSystem, config: ProjectConfig): void {
88
const { cms, frontend, database } = config;
9-
10-
// Skip if not selected or set to "none"
119
if (!cms || cms === "none") return;
1210

13-
// Both Payload and Sanity require Next.js for optimal integration
1411
const hasNext = frontend.includes("next");
12+
const hasWebFrontend =
13+
frontend.includes("next") ||
14+
frontend.includes("astro") ||
15+
frontend.includes("nuxt") ||
16+
frontend.includes("svelte") ||
17+
frontend.some((f) =>
18+
["tanstack-router", "react-router", "react-vite", "tanstack-start"].includes(f),
19+
);
1520

1621
if (cms === "payload") {
17-
// Payload is a Next.js-only CMS in v3
1822
if (!hasNext) return;
1923

2024
const webPath = "apps/web/package.json";
2125
if (vfs.exists(webPath)) {
22-
const deps = getPayloadDeps(database);
23-
if (deps.length > 0) {
24-
addPackageDependency({
25-
vfs,
26-
packagePath: webPath,
27-
dependencies: deps,
28-
});
29-
}
26+
addPackageDependency({
27+
vfs,
28+
packagePath: webPath,
29+
dependencies: getPayloadDeps(database),
30+
});
3031
}
32+
return;
3133
}
3234

33-
if (cms === "sanity") {
34-
// Sanity works best with Next.js due to next-sanity integration
35-
if (!hasNext) return;
35+
if (!hasWebFrontend) return;
3636

37-
const webPath = "apps/web/package.json";
38-
if (vfs.exists(webPath)) {
39-
const deps = getSanityDeps();
40-
if (deps.length > 0) {
41-
addPackageDependency({
42-
vfs,
43-
packagePath: webPath,
44-
dependencies: deps,
45-
});
46-
}
47-
}
37+
const webPath = "apps/web/package.json";
38+
if (!vfs.exists(webPath)) return;
39+
40+
if (cms === "sanity") {
41+
addPackageDependency({
42+
vfs,
43+
packagePath: webPath,
44+
dependencies: hasNext
45+
? ["sanity", "next-sanity", "@sanity/image-url", "@sanity/vision"]
46+
: ["sanity", "@sanity/client", "@sanity/image-url"],
47+
});
4848
}
4949

5050
if (cms === "strapi") {
51-
// Strapi client works with any frontend but we provide Next.js templates
52-
if (!hasNext) return;
53-
54-
const webPath = "apps/web/package.json";
55-
if (vfs.exists(webPath)) {
56-
const deps = getStrapiDeps();
57-
if (deps.length > 0) {
58-
addPackageDependency({
59-
vfs,
60-
packagePath: webPath,
61-
dependencies: deps,
62-
});
63-
}
64-
}
51+
addPackageDependency({
52+
vfs,
53+
packagePath: webPath,
54+
dependencies: ["@strapi/client", "qs"],
55+
});
6556
}
6657

6758
if (cms === "tinacms") {
68-
if (!hasNext) return;
69-
70-
const webPath = "apps/web/package.json";
71-
if (vfs.exists(webPath)) {
72-
addPackageDependency({
73-
vfs,
74-
packagePath: webPath,
75-
dependencies: ["tinacms"],
76-
devDependencies: ["@tinacms/cli"],
77-
});
78-
79-
const pkgJson = vfs.readJson<{
80-
scripts?: Record<string, string>;
81-
[key: string]: unknown;
82-
}>(webPath);
83-
if (pkgJson?.scripts) {
84-
const existingDev = pkgJson.scripts.dev || "next dev --port 3001";
59+
addPackageDependency({
60+
vfs,
61+
packagePath: webPath,
62+
dependencies: ["tinacms"],
63+
devDependencies: ["@tinacms/cli"],
64+
});
65+
66+
const pkgJson = vfs.readJson<{
67+
scripts?: Record<string, string>;
68+
[key: string]: unknown;
69+
}>(webPath);
70+
if (pkgJson?.scripts) {
71+
const existingDev = pkgJson.scripts.dev;
72+
if (existingDev) {
8573
pkgJson.scripts.dev = `tinacms dev -c "${existingDev}"`;
74+
}
8675

87-
const existingBuild = pkgJson.scripts.build || "next build";
76+
const existingBuild = pkgJson.scripts.build;
77+
if (existingBuild) {
8878
pkgJson.scripts.build = `tinacms build && ${existingBuild}`;
79+
}
8980

90-
const existingCheckTypes = pkgJson.scripts["check-types"] || "tsc --noEmit";
81+
const existingCheckTypes = pkgJson.scripts["check-types"];
82+
if (existingCheckTypes) {
9183
pkgJson.scripts["check-types"] = `tinacms build && ${existingCheckTypes}`;
92-
93-
vfs.writeJson(webPath, pkgJson);
9484
}
85+
86+
vfs.writeJson(webPath, pkgJson);
9587
}
9688

9789
const gitignorePath = "apps/web/.gitignore";
@@ -106,37 +98,19 @@ export function processCMSDeps(vfs: VirtualFileSystem, config: ProjectConfig): v
10698
}
10799

108100
function getPayloadDeps(database: ProjectConfig["database"]): AvailableDependencies[] {
109-
const deps: AvailableDependencies[] = [
110-
"payload",
111-
"@payloadcms/next",
112-
"@payloadcms/richtext-lexical",
113-
];
101+
const deps: AvailableDependencies[] = ["payload", "@payloadcms/next", "@payloadcms/richtext-lexical"];
114102

115-
// Add appropriate database adapter based on selected database
116103
switch (database) {
117104
case "postgres":
118105
deps.push("@payloadcms/db-postgres");
119106
break;
120107
case "mongodb":
121108
deps.push("@payloadcms/db-mongodb");
122109
break;
123-
case "sqlite":
124-
deps.push("@payloadcms/db-sqlite");
125-
break;
126110
default:
127-
// Default to SQLite for simplicity
128111
deps.push("@payloadcms/db-sqlite");
129112
break;
130113
}
131114

132115
return deps;
133116
}
134-
135-
function getSanityDeps(): AvailableDependencies[] {
136-
return ["sanity", "next-sanity", "@sanity/image-url", "@sanity/vision"];
137-
}
138-
139-
function getStrapiDeps(): AvailableDependencies[] {
140-
return ["@strapi/client", "qs"];
141-
}
142-

0 commit comments

Comments
 (0)