@cmssy/cli 11.6.0 → 11.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/init/astro/src/cmssy/editor.tsx +0 -3
- package/assets/init/astro/src/cmssy/hero.tsx +0 -2
- package/assets/init/astro/src/cmssy/layout-slot.tsx +0 -16
- package/assets/init/astro/src/cmssy.config.ts +0 -2
- package/assets/init/astro/src/components/Blocks.tsx +0 -4
- package/assets/init/astro/src/middleware.ts +0 -2
- package/assets/init/astro/src/pages/[...path].astro +0 -2
- package/assets/init/astro/src/pages/cmssy-edit/[...path].astro +0 -4
- package/assets/init/next/app/[[...path]]/page.tsx +0 -9
- package/assets/init/next/app/cmssy-edit/[[...path]]/page.tsx +0 -11
- package/assets/init/next/blocks/hero/Hero.tsx +0 -2
- package/assets/init/next/blocks/hero/block.ts +0 -8
- package/assets/init/next/cmssy/blocks.ts +0 -2
- package/assets/init/next/cmssy/editable-layout.tsx +0 -2
- package/assets/init/next/cmssy/editor.tsx +0 -2
- package/assets/init/next/cmssy.config.ts +0 -7
- package/assets/init/next/proxy.ts +0 -5
- package/assets/init/next/services/pages.ts +0 -15
- package/assets/init/remix/app/cmssy/editor.tsx +0 -2
- package/assets/init/remix/app/cmssy/hero.tsx +0 -2
- package/assets/init/remix/app/cmssy/layout-slot.tsx +0 -13
- package/assets/init/remix/app/routes/page.tsx +0 -6
- package/assets/init/remix/app/routes.ts +0 -2
- package/assets/init/remix/cmssy.config.ts +0 -2
- package/dist/index.js +146 -35
- package/package.json +2 -2
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { CmssyLazyEditor } from "@cmssy/react/client";
|
|
3
3
|
|
|
4
|
-
// The edit bridge is a client island: the editor talks to the page over
|
|
5
|
-
// postMessage, and that protocol lives in @cmssy/core - not in React and not in
|
|
6
|
-
// Next. That is why the same bridge works here.
|
|
7
4
|
export default function CmssyEditor(props: Record<string, unknown>) {
|
|
8
5
|
return <CmssyLazyEditor {...(props as never)} load={() => import("./blocks")} />;
|
|
9
6
|
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { fields, type BlockProps } from "@cmssy/react";
|
|
2
2
|
|
|
3
|
-
// The schema lives next to the component that reads it, and types it. Rename a
|
|
4
|
-
// field here and this file stops compiling - it cannot render an empty block.
|
|
5
3
|
export const heroProps = {
|
|
6
4
|
title: fields.text({ label: "Title", required: true }),
|
|
7
5
|
subtitle: fields.text({ label: "Subtitle" }),
|
|
@@ -13,27 +13,11 @@ export interface LayoutSlotProps {
|
|
|
13
13
|
locale: string;
|
|
14
14
|
defaultLocale: string;
|
|
15
15
|
enabledLocales: string[];
|
|
16
|
-
/** Set on a VERIFIED editor request; the loader reports it as `isEdit`. */
|
|
17
16
|
edit?: { editorOrigin: string | string[] };
|
|
18
|
-
/**
|
|
19
|
-
* The editor's block data, both halves, straight from the loader. Without
|
|
20
|
-
* `resolvedContent` the canvas shows a relation field as the raw ids it
|
|
21
|
-
* stores - the site looks right and the editor cannot fill the header.
|
|
22
|
-
*/
|
|
23
17
|
data?: Record<string, unknown>;
|
|
24
18
|
resolvedContent?: Record<string, Record<string, unknown>>;
|
|
25
19
|
}
|
|
26
20
|
|
|
27
|
-
/**
|
|
28
|
-
* The header and footer, which are layout blocks rather than markup you own.
|
|
29
|
-
*
|
|
30
|
-
* A React island: Astro renders it on the server for visitors (no client JS),
|
|
31
|
-
* and with `client:load` on the edit route, where the bridge needs the browser.
|
|
32
|
-
*
|
|
33
|
-
* Two renders, one per mode, and the difference is the point: server-rendered
|
|
34
|
-
* for visitors, and through the edit bridge for the editor. A header still
|
|
35
|
-
* server-rendered in edit mode is one the editor can select and cannot fill.
|
|
36
|
-
*/
|
|
37
21
|
export default function LayoutSlot({
|
|
38
22
|
groups,
|
|
39
23
|
position,
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { defineCmssyConfig } from "@cmssy/astro";
|
|
2
2
|
|
|
3
|
-
// Pass the env raw: a `?? ""` fallback turns a missing variable into an empty
|
|
4
|
-
// one, and the error then surfaces somewhere unrelated.
|
|
5
3
|
export const cmssy = defineCmssyConfig({
|
|
6
4
|
org: process.env.CMSSY_ORG_SLUG,
|
|
7
5
|
workspaceSlug: process.env.CMSSY_WORKSPACE_SLUG,
|
|
@@ -2,10 +2,6 @@ import { CmssyBlock, buildBlockMap, buildBlockContext } from "@cmssy/react";
|
|
|
2
2
|
import type { CmssyPageData } from "@cmssy/core";
|
|
3
3
|
import { blocks } from "../cmssy/blocks";
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
* The public page: React blocks rendered on the server by Astro, with no client
|
|
7
|
-
* JS at all. The visitor gets HTML; the framework stays out of the render path.
|
|
8
|
-
*/
|
|
9
5
|
export function Blocks({
|
|
10
6
|
page,
|
|
11
7
|
locale,
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { cmssyMiddleware } from "@cmssy/astro";
|
|
2
2
|
import { cmssy } from "./cmssy.config";
|
|
3
3
|
|
|
4
|
-
// The whole adapter. It resolves the language, routes a verified editor request
|
|
5
|
-
// to /cmssy-edit, and applies the CSP that lets the admin frame this site.
|
|
6
4
|
export const onRequest = cmssyMiddleware(cmssy);
|
|
@@ -7,8 +7,6 @@ import LayoutSlot from "../cmssy/layout-slot";
|
|
|
7
7
|
|
|
8
8
|
export const prerender = false;
|
|
9
9
|
|
|
10
|
-
// `blocks` is what lets the loader resolve the editor's layout data. Drop it
|
|
11
|
-
// and the site still renders - the editor just cannot fill the header.
|
|
12
10
|
const { page, layouts, locale, defaultLocale, enabledLocales } = await loadCmssyPage(
|
|
13
11
|
cmssy,
|
|
14
12
|
Astro.request,
|
|
@@ -5,12 +5,8 @@ import { blocks } from "../../cmssy/blocks";
|
|
|
5
5
|
import CmssyEditor from "../../cmssy/editor";
|
|
6
6
|
import LayoutSlot from "../../cmssy/layout-slot";
|
|
7
7
|
|
|
8
|
-
// The editor iframe lands here (the middleware rewrites verified requests). It
|
|
9
|
-
// must be dynamic: a prerendered page never sees the query string.
|
|
10
8
|
export const prerender = false;
|
|
11
9
|
|
|
12
|
-
// `blocks` is what resolves the editor's layout data. Without it the editor
|
|
13
|
-
// can select the header and not fill it, and a relation field shows raw ids.
|
|
14
10
|
const { page, layouts, locale, defaultLocale, enabledLocales, editorData } =
|
|
15
11
|
await loadCmssyPage(cmssy, Astro.request, Astro.url, { blocks });
|
|
16
12
|
const editorOrigin = resolveEditorOrigin(cmssy.editorOrigin);
|
|
@@ -10,11 +10,6 @@ type PageProps = {
|
|
|
10
10
|
searchParams: Promise<Record<string, string | string[] | undefined>>;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
// Three lines that decide whether this site is cached at all. Without
|
|
14
|
-
// generateStaticParams a catch-all is rendered on demand every time and
|
|
15
|
-
// `revalidate` is ignored - the build prints a blank Revalidate column, which is
|
|
16
|
-
// the only warning you get. `dynamicParams` keeps pages published after the
|
|
17
|
-
// build working: first request renders them, and they cache from then on.
|
|
18
13
|
export const revalidate = 3600;
|
|
19
14
|
export const dynamicParams = true;
|
|
20
15
|
|
|
@@ -24,10 +19,6 @@ export function generateStaticParams() {
|
|
|
24
19
|
|
|
25
20
|
const CmssyPage = createCmssyPage(cmssy, blocks, { editor: CmssyEditor });
|
|
26
21
|
|
|
27
|
-
// The header and footer are layout blocks. They are mounted here rather than in
|
|
28
|
-
// app/layout.tsx because this route knows its path, and the language prefix in
|
|
29
|
-
// that path is what says which language to render them in - reading it from the
|
|
30
|
-
// request header instead would make every page dynamic.
|
|
31
22
|
export default async function Page(props: PageProps) {
|
|
32
23
|
const { path } = await props.params;
|
|
33
24
|
const slot = (position: "header" | "footer") => (
|
|
@@ -8,11 +8,6 @@ import { blocks } from "@/cmssy/blocks";
|
|
|
8
8
|
import { CmssyEditor } from "@/cmssy/editor";
|
|
9
9
|
import { EditableLayout } from "@/cmssy/editable-layout";
|
|
10
10
|
|
|
11
|
-
// The route the middleware rewrites a verified editor request onto. The public
|
|
12
|
-
// pages stay static, and a static page never sees the query string that would
|
|
13
|
-
// put it in edit mode - which is why the editor needs a route of its own.
|
|
14
|
-
//
|
|
15
|
-
// Delete this file and the editor preview goes blank.
|
|
16
11
|
export const dynamic = "force-dynamic";
|
|
17
12
|
|
|
18
13
|
type PageProps = {
|
|
@@ -24,15 +19,9 @@ const CmssyEditPage = createCmssyEditPage(cmssy, blocks, {
|
|
|
24
19
|
editor: CmssyEditor,
|
|
25
20
|
});
|
|
26
21
|
|
|
27
|
-
// The same chrome the public route renders - the editor frames THIS page, so a
|
|
28
|
-
// header missing here is a header missing in the editor.
|
|
29
22
|
export default async function EditPage(props: PageProps) {
|
|
30
23
|
const { path } = await props.params;
|
|
31
24
|
|
|
32
|
-
// Not a hard `true`: this route is reachable directly, and only the proxy's
|
|
33
|
-
// verified rewrite sets the edit header. Hard-coding it would fetch layouts
|
|
34
|
-
// with the draft secret for anyone who typed the URL. Reading a header costs
|
|
35
|
-
// nothing here - the route is force-dynamic either way.
|
|
36
25
|
const editMode = await isCmssyEditMode();
|
|
37
26
|
|
|
38
27
|
const slot = (position: "header" | "footer") => (
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { fields, type BlockProps } from "@cmssy/react";
|
|
2
2
|
|
|
3
|
-
// The schema lives next to the component that reads it, and types it. Rename a
|
|
4
|
-
// field here and this file stops compiling - it cannot render an empty block.
|
|
5
3
|
export const heroProps = {
|
|
6
4
|
heading: fields.text({ label: "Heading", required: true }),
|
|
7
5
|
text: fields.textarea({ label: "Text" }),
|
|
@@ -6,12 +6,4 @@ export const heroBlock = defineBlock({
|
|
|
6
6
|
label: "Hero",
|
|
7
7
|
component: Hero,
|
|
8
8
|
props: heroProps,
|
|
9
|
-
// A loader runs SERVER-SIDE before the block renders, so a block can read data
|
|
10
|
-
// (products, posts) the same way a page can. Import it dynamically: it may
|
|
11
|
-
// touch the cmssy config, which must never reach the browser.
|
|
12
|
-
//
|
|
13
|
-
// loader: async () => {
|
|
14
|
-
// const { loadPosts } = await import("./load-posts");
|
|
15
|
-
// return { posts: await loadPosts() };
|
|
16
|
-
// },
|
|
17
9
|
});
|
|
@@ -5,8 +5,6 @@ import {
|
|
|
5
5
|
type CmssyLazyLayoutProps,
|
|
6
6
|
} from "@cmssy/react/client";
|
|
7
7
|
|
|
8
|
-
// Mounts the layout blocks through the edit bridge, so the header and the footer
|
|
9
|
-
// are editable in the editor rather than markup it can select and cannot fill.
|
|
10
8
|
export function EditableLayout(props: Omit<CmssyLazyLayoutProps, "load">) {
|
|
11
9
|
return <CmssyLazyLayout {...props} load={() => import("./blocks")} />;
|
|
12
10
|
}
|
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
import { CmssyLazyEditor } from "@cmssy/react/client";
|
|
4
4
|
import type { CmssyEditorProps } from "@cmssy/next";
|
|
5
5
|
|
|
6
|
-
// The block registry is loaded lazily ON THE CLIENT, so block loaders - which
|
|
7
|
-
// run server-side and read the config - never reach the browser bundle.
|
|
8
6
|
export function CmssyEditor(props: CmssyEditorProps) {
|
|
9
7
|
return <CmssyLazyEditor {...props} load={() => import("./blocks")} />;
|
|
10
8
|
}
|
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
import { defineCmssyConfig } from "@cmssy/next";
|
|
2
2
|
|
|
3
|
-
// Pass process.env raw: defineCmssyConfig validates at startup and names any
|
|
4
|
-
// variable you are missing. A `?? ""` fallback would hide that, and the error
|
|
5
|
-
// would surface later, somewhere unrelated.
|
|
6
|
-
//
|
|
7
|
-
// This module reads server env. Never import a VALUE from it (or from a module
|
|
8
|
-
// that imports it) in a "use client" component - types are erased, values drag
|
|
9
|
-
// process.env into the browser bundle.
|
|
10
3
|
export const cmssy = defineCmssyConfig({
|
|
11
4
|
org: process.env.CMSSY_ORG_SLUG,
|
|
12
5
|
workspaceSlug: process.env.CMSSY_WORKSPACE_SLUG,
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import { createCmssyProxy } from "@cmssy/next/middleware";
|
|
2
2
|
import { cmssy } from "@/cmssy.config";
|
|
3
3
|
|
|
4
|
-
// Resolves the language, sends verified editor traffic to /cmssy-edit (carrying
|
|
5
|
-
// that language and the edit flag), and lets the cmssy admin frame this site.
|
|
6
|
-
// The order matters, which is why it lives in the preset and not here.
|
|
7
4
|
export const proxy = createCmssyProxy(cmssy);
|
|
8
5
|
|
|
9
|
-
// Next parses this at compile time, so the matcher has to be a literal - an
|
|
10
|
-
// imported constant is rejected. Skips Next internals, API routes and files.
|
|
11
6
|
export const config = {
|
|
12
7
|
matcher: ["/((?!_next/|api/|.*\\..*).*)"],
|
|
13
8
|
};
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { createCmssyClient } from "@cmssy/core";
|
|
2
2
|
import { cmssy } from "@/cmssy.config";
|
|
3
3
|
|
|
4
|
-
// Your query, in your repo. The SDK does not mirror the graph - it gives you a
|
|
5
|
-
// client and gets out of the way, so adding a field here is your call and not a
|
|
6
|
-
// release of ours.
|
|
7
4
|
const PUBLISHED_PAGES = `query PublishedPages($workspaceSlug: String!) {
|
|
8
5
|
public {
|
|
9
6
|
page {
|
|
@@ -27,24 +24,12 @@ const client = createCmssyClient({
|
|
|
27
24
|
workspaceSlug: cmssy.workspaceSlug,
|
|
28
25
|
});
|
|
29
26
|
|
|
30
|
-
/**
|
|
31
|
-
* The paths to prerender, as `generateStaticParams` wants them.
|
|
32
|
-
*
|
|
33
|
-
* A catch-all route with no params generated is served on demand and cached by
|
|
34
|
-
* nothing, whatever `revalidate` says - so this function is what makes the
|
|
35
|
-
* route cacheable at all, not an optimisation on top of it.
|
|
36
|
-
*/
|
|
37
27
|
export async function publishedPaths(): Promise<{ path: string[] }[]> {
|
|
38
28
|
const data = await client.query<PublishedPages>(
|
|
39
29
|
PUBLISHED_PAGES,
|
|
40
30
|
{ workspaceSlug: cmssy.workspaceSlug },
|
|
41
31
|
{
|
|
42
|
-
// `public` routes through the org-scoped delivery path. Without it the
|
|
43
|
-
// request lands on the base endpoint, where an unauthenticated workspace
|
|
44
|
-
// lookup is by slug alone - across every org.
|
|
45
32
|
public: true,
|
|
46
|
-
// A build reads this once. A single 429 from the delivery API would fail
|
|
47
|
-
// the whole deploy, so retry - it is a read, nothing to double-apply.
|
|
48
33
|
retry: {},
|
|
49
34
|
},
|
|
50
35
|
);
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { CmssyLazyEditor } from "@cmssy/react/client";
|
|
2
2
|
import type { CmssyPageData } from "@cmssy/core";
|
|
3
3
|
|
|
4
|
-
// The edit bridge: the editor talks to the page over postMessage, and that
|
|
5
|
-
// protocol lives in @cmssy/core - not in React and not in a framework.
|
|
6
4
|
export function CmssyEditor(props: {
|
|
7
5
|
page: CmssyPageData | null;
|
|
8
6
|
locale: string;
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { fields, type BlockProps } from "@cmssy/react";
|
|
2
2
|
|
|
3
|
-
// The schema lives next to the component that reads it, and types it. Rename a
|
|
4
|
-
// field here and this file stops compiling - it cannot render an empty block.
|
|
5
3
|
export const heroProps = {
|
|
6
4
|
title: fields.text({ label: "Title", required: true }),
|
|
7
5
|
subtitle: fields.text({ label: "Subtitle" }),
|
|
@@ -13,24 +13,11 @@ export interface LayoutSlotProps {
|
|
|
13
13
|
locale: string;
|
|
14
14
|
defaultLocale: string;
|
|
15
15
|
enabledLocales: string[];
|
|
16
|
-
/** Set on a VERIFIED editor request; the loader reports it as `isEdit`. */
|
|
17
16
|
edit?: { editorOrigin: string | string[] };
|
|
18
|
-
/**
|
|
19
|
-
* The editor's block data, both halves, straight from the loader. Without
|
|
20
|
-
* `resolvedContent` the canvas shows a relation field as the raw ids it
|
|
21
|
-
* stores - the site looks right and the editor cannot fill the header.
|
|
22
|
-
*/
|
|
23
17
|
data?: Record<string, unknown>;
|
|
24
18
|
resolvedContent?: Record<string, Record<string, unknown>>;
|
|
25
19
|
}
|
|
26
20
|
|
|
27
|
-
/**
|
|
28
|
-
* The header and footer, which are layout blocks rather than markup you own.
|
|
29
|
-
*
|
|
30
|
-
* Two renders, one per mode, and the difference is the point: server-rendered
|
|
31
|
-
* for visitors, and through the edit bridge for the editor. A header still
|
|
32
|
-
* server-rendered in edit mode is one the editor can select and cannot fill.
|
|
33
|
-
*/
|
|
34
21
|
export function LayoutSlot({
|
|
35
22
|
groups,
|
|
36
23
|
position,
|
|
@@ -6,12 +6,8 @@ import { CmssyEditor } from "../cmssy/editor";
|
|
|
6
6
|
import { LayoutSlot } from "../cmssy/layout-slot";
|
|
7
7
|
import type { Route } from "./+types/page";
|
|
8
8
|
|
|
9
|
-
// `blocks` is what lets the loader resolve the editor's layout data. Drop it
|
|
10
|
-
// and the site still renders - the editor just cannot fill the header.
|
|
11
9
|
export const loader = createCmssyLoader(cmssy, { blocks });
|
|
12
10
|
|
|
13
|
-
// Without these the admin cannot frame the site, and the editor shows an empty
|
|
14
|
-
// box with no error anywhere.
|
|
15
11
|
export const headers = createCmssyHeaders(cmssy);
|
|
16
12
|
|
|
17
13
|
export default function CmssyPage({ loaderData }: Route.ComponentProps) {
|
|
@@ -31,8 +27,6 @@ export default function CmssyPage({ loaderData }: Route.ComponentProps) {
|
|
|
31
27
|
return <div dangerouslySetInnerHTML={{ __html: diagnostics }} />;
|
|
32
28
|
}
|
|
33
29
|
|
|
34
|
-
// A verified editor request renders the same page through the edit bridge.
|
|
35
|
-
// No separate route: a React Router page always sees its query string.
|
|
36
30
|
const slot = (position: "header" | "footer") => (
|
|
37
31
|
<LayoutSlot
|
|
38
32
|
groups={layouts}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { type RouteConfig, index, route } from "@react-router/dev/routes";
|
|
2
2
|
|
|
3
|
-
// The splat does not match "/", so the homepage needs its own entry - it is the
|
|
4
|
-
// same route module, mounted twice.
|
|
5
3
|
export default [
|
|
6
4
|
index("routes/page.tsx"),
|
|
7
5
|
route("*", "routes/page.tsx", { id: "cmssy-catch-all" }),
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { defineCmssyConfig } from "@cmssy/remix";
|
|
2
2
|
|
|
3
|
-
// Pass the env raw: a `?? ""` fallback turns a missing variable into an empty
|
|
4
|
-
// one, and the error then surfaces somewhere unrelated.
|
|
5
3
|
export const cmssy = defineCmssyConfig({
|
|
6
4
|
org: process.env.CMSSY_ORG_SLUG,
|
|
7
5
|
workspaceSlug: process.env.CMSSY_WORKSPACE_SLUG,
|
package/dist/index.js
CHANGED
|
@@ -163,19 +163,63 @@ var FRAMEWORKS = [
|
|
|
163
163
|
detect: ["next"],
|
|
164
164
|
dependencies: ["@cmssy/next", "@cmssy/react", "@cmssy/core"],
|
|
165
165
|
files: [
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
166
|
+
{
|
|
167
|
+
path: "cmssy.config.ts",
|
|
168
|
+
purpose: "reads server env and validates it at startup; server-only, never import a value from it in a client component"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
path: "proxy.ts",
|
|
172
|
+
purpose: "resolves the language, rewrites verified editor traffic to /cmssy-edit, lets the admin frame the site; its matcher must stay a literal"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
path: "services/pages.ts",
|
|
176
|
+
purpose: "your delivery queries, in your repo; generateStaticParams lives here and is what makes the catch-all cacheable at all"
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
path: "cmssy/blocks.ts",
|
|
180
|
+
purpose: "every block the app renders - the server renders from this list, the editor loads it lazily"
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
path: "cmssy/editor.tsx",
|
|
184
|
+
purpose: "loads the block registry on the client, so server-side block loaders never reach the browser bundle"
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
path: "cmssy/editable-layout.tsx",
|
|
188
|
+
purpose: "mounts header and footer through the edit bridge; without it the editor can select them and cannot fill them"
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
path: "blocks/hero/block.ts",
|
|
192
|
+
purpose: "an example block: schema beside the component that reads it (loaders: docs/building-blocks/server-loaders.md)"
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
path: "blocks/hero/Hero.tsx",
|
|
196
|
+
purpose: "its component - renaming a schema field stops this compiling"
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
path: "app/[[...path]]/layout.tsx",
|
|
200
|
+
purpose: "the public root layout; <html lang> comes from the route, so put your global CSS and metadata here"
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
path: "app/[[...path]]/page.tsx",
|
|
204
|
+
purpose: "the public catch-all: generateStaticParams plus dynamicParams decide whether this site is cached at all"
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
path: "app/cmssy-edit/[[...path]]/layout.tsx",
|
|
208
|
+
purpose: "the editor's root layout - a separate root, so CSS and metadata have to be repeated here"
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
path: "app/cmssy-edit/[[...path]]/page.tsx",
|
|
212
|
+
purpose: "the route the proxy rewrites a verified editor request onto; delete it and the editor preview goes blank"
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
path: "app/api/draft/route.ts",
|
|
216
|
+
purpose: "draft preview without the editor - enters draft mode for a verified secret"
|
|
217
|
+
}
|
|
218
|
+
],
|
|
219
|
+
warnings: [
|
|
220
|
+
'pass process.env values raw into defineCmssyConfig - a `?? ""` fallback turns a missing variable into an empty one and the error surfaces somewhere unrelated',
|
|
221
|
+
"the two root layouts are separate: global CSS or metadata added to one and not the other shows up as an editor preview with no styles",
|
|
222
|
+
"a page without generateStaticParams renders on demand and ignores revalidate; the build's blank Revalidate column is the only warning you get"
|
|
179
223
|
]
|
|
180
224
|
},
|
|
181
225
|
{
|
|
@@ -185,15 +229,46 @@ var FRAMEWORKS = [
|
|
|
185
229
|
detect: ["astro"],
|
|
186
230
|
dependencies: ["@cmssy/astro", "@cmssy/react", "@cmssy/core"],
|
|
187
231
|
files: [
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
232
|
+
{
|
|
233
|
+
path: "src/cmssy.config.ts",
|
|
234
|
+
purpose: "reads env and validates it at startup; pass the values raw so a missing one is named here"
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
path: "src/middleware.ts",
|
|
238
|
+
purpose: "the whole adapter: resolves the language, rewrites verified editor requests to /cmssy-edit, applies the framing CSP"
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
path: "src/cmssy/blocks.ts",
|
|
242
|
+
purpose: "every block the app renders"
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
path: "src/cmssy/editor.tsx",
|
|
246
|
+
purpose: "the edit bridge as a client island - the postMessage protocol lives in @cmssy/core, which is why it works outside React frameworks"
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
path: "src/cmssy/layout-slot.tsx",
|
|
250
|
+
purpose: "header and footer: server-rendered for visitors, through the edit bridge on the edit route; resolvedContent is what stops a relation field showing raw ids"
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
path: "src/cmssy/hero.tsx",
|
|
254
|
+
purpose: "an example block - schema beside the component that reads it"
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
path: "src/components/Blocks.tsx",
|
|
258
|
+
purpose: "renders React blocks on the server with no client JS - the visitor gets HTML"
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
path: "src/pages/[...path].astro",
|
|
262
|
+
purpose: "the public catch-all; its `blocks` prop is what lets the loader resolve the editor's layout data"
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
path: "src/pages/cmssy-edit/[...path].astro",
|
|
266
|
+
purpose: "where the editor iframe lands - must stay dynamic, because a prerendered page never sees the query string"
|
|
267
|
+
}
|
|
268
|
+
],
|
|
269
|
+
warnings: [
|
|
270
|
+
'pass env values raw into defineCmssyConfig - a `?? ""` fallback hides a missing variable until it breaks somewhere unrelated',
|
|
271
|
+
"the `blocks` prop on both page routes is not decoration: without it the site still renders and the editor can select a header it cannot fill"
|
|
197
272
|
]
|
|
198
273
|
},
|
|
199
274
|
{
|
|
@@ -203,14 +278,42 @@ var FRAMEWORKS = [
|
|
|
203
278
|
detect: ["react-router", "@react-router/dev", "@remix-run/react"],
|
|
204
279
|
dependencies: ["@cmssy/remix", "@cmssy/react", "@cmssy/core"],
|
|
205
280
|
files: [
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
281
|
+
{
|
|
282
|
+
path: "cmssy.config.ts",
|
|
283
|
+
purpose: "reads env and validates it at startup; pass the values raw so a missing one is named here"
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
path: "app/root.tsx",
|
|
287
|
+
purpose: "the root layout - <html lang> comes from useCmssyLocale(), so a prefixed URL declares the language it renders"
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
path: "app/routes.ts",
|
|
291
|
+
purpose: 'mounts the page module twice: the splat does not match "/", so the homepage needs its own entry'
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
path: "app/cmssy/blocks.ts",
|
|
295
|
+
purpose: "every block the app renders"
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
path: "app/cmssy/editor.tsx",
|
|
299
|
+
purpose: "the edit bridge - the postMessage protocol lives in @cmssy/core, not in the framework"
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
path: "app/cmssy/layout-slot.tsx",
|
|
303
|
+
purpose: "header and footer: server-rendered for visitors, through the edit bridge for the editor; resolvedContent is what stops a relation field showing raw ids"
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
path: "app/cmssy/hero.tsx",
|
|
307
|
+
purpose: "an example block - schema beside the component that reads it"
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
path: "app/routes/page.tsx",
|
|
311
|
+
purpose: "one route for both modes - a React Router page always sees its query string, so there is no separate edit route; its headers are what let the admin frame the site"
|
|
312
|
+
}
|
|
313
|
+
],
|
|
314
|
+
warnings: [
|
|
315
|
+
'pass env values raw into defineCmssyConfig - a `?? ""` fallback hides a missing variable until it breaks somewhere unrelated',
|
|
316
|
+
"the route's `blocks` prop and its CSP headers are load-bearing: drop either and the site renders while the editor shows an empty box with no error"
|
|
214
317
|
]
|
|
215
318
|
}
|
|
216
319
|
];
|
|
@@ -511,10 +614,15 @@ var CLI_PACKAGE_JSON = fileURLToPath(
|
|
|
511
614
|
function frameworkFiles(framework, root) {
|
|
512
615
|
const srcPrefix = framework.name === "next" ? nextSrcPrefix(root) : "";
|
|
513
616
|
const files = [
|
|
514
|
-
{
|
|
515
|
-
|
|
617
|
+
{
|
|
618
|
+
asset: "env.example",
|
|
619
|
+
target: ".env.example",
|
|
620
|
+
purpose: "the variables the config reads - copy it to .env.local and fill them in"
|
|
621
|
+
},
|
|
622
|
+
...framework.files.map(({ path, purpose }) => ({
|
|
516
623
|
asset: path,
|
|
517
|
-
target: framework.name === "next" ? `${srcPrefix}${path}` : path
|
|
624
|
+
target: framework.name === "next" ? `${srcPrefix}${path}` : path,
|
|
625
|
+
purpose
|
|
518
626
|
}))
|
|
519
627
|
];
|
|
520
628
|
if (framework.name === "next" && existingFile(root, `${srcPrefix}app/layout`)) {
|
|
@@ -648,6 +756,7 @@ function runInit(options, deps) {
|
|
|
648
756
|
copyFileSync(join3(ASSETS_DIR, framework.name, file.asset), target);
|
|
649
757
|
written.push(file.target);
|
|
650
758
|
log(formatResult({ status: "ok", message: `wrote ${file.target}` }));
|
|
759
|
+
log(` ${file.purpose}`);
|
|
651
760
|
}
|
|
652
761
|
const added = addDependencies(root, pkg, framework);
|
|
653
762
|
if (added.length > 0) {
|
|
@@ -666,6 +775,10 @@ function runInit(options, deps) {
|
|
|
666
775
|
`${written.length} file${written.length === 1 ? "" : "s"} written, ${skipped.length} skipped.`
|
|
667
776
|
);
|
|
668
777
|
log("");
|
|
778
|
+
log("What not to break:");
|
|
779
|
+
for (const warning of framework.warnings) log(` - ${warning}`);
|
|
780
|
+
log(" full wiring, and what each file is doing: docs/wiring.md");
|
|
781
|
+
log("");
|
|
669
782
|
log("Next steps:");
|
|
670
783
|
let step = 1;
|
|
671
784
|
if (added.length > 0) log(` ${step++}. ${detectInstallCommand(root)}`);
|
|
@@ -1237,8 +1350,6 @@ function generateOperationsFile() {
|
|
|
1237
1350
|
""
|
|
1238
1351
|
].join("\n");
|
|
1239
1352
|
const documents = CMSSY_DELIVERY_OPERATIONS.map(
|
|
1240
|
-
// A newline in `purpose` would emit bare prose into the document and break
|
|
1241
|
-
// parsing for every consumer.
|
|
1242
1353
|
(operation) => `# ${operation.purpose.replace(/\s+/g, " ").trim()}
|
|
1243
1354
|
${operation.document}`
|
|
1244
1355
|
).join("\n\n");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cmssy/cli",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.8.0",
|
|
4
4
|
"description": "The cmssy CLI: `cmssy init` wires cmssy into an existing Next.js, Astro or React Router app; `cmssy link` connects it to a workspace and verifies the editor wiring.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cmssy",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"assets"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@cmssy/core": "11.
|
|
30
|
+
"@cmssy/core": "11.8.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^20.0.0",
|