@cmssy/cli 9.4.0 → 9.6.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/env.example +7 -0
- package/assets/init/astro/src/cmssy/blocks.ts +12 -0
- package/assets/init/astro/src/cmssy/editor.tsx +9 -0
- package/assets/init/astro/src/cmssy/hero.tsx +17 -0
- package/assets/init/astro/src/cmssy.config.ts +9 -0
- package/assets/init/astro/src/components/Blocks.tsx +37 -0
- package/assets/init/astro/src/middleware.ts +6 -0
- package/assets/init/astro/src/pages/[...path].astro +38 -0
- package/assets/init/astro/src/pages/cmssy-edit/[...path].astro +33 -0
- package/assets/init/astro/src/pages/robots.txt.ts +5 -0
- package/assets/init/astro/src/pages/sitemap.xml.ts +5 -0
- package/assets/init/next/app/[[...path]]/page.tsx +16 -0
- package/assets/init/next/app/api/draft/route.ts +4 -0
- package/assets/init/next/app/cmssy-edit/[[...path]]/page.tsx +13 -0
- package/assets/init/next/app/robots.ts +4 -0
- package/assets/init/next/app/sitemap.ts +7 -0
- package/assets/init/next/blocks/hero/Hero.tsx +17 -0
- package/assets/init/next/blocks/hero/block.ts +17 -0
- package/assets/init/next/cmssy/blocks.ts +5 -0
- package/assets/init/next/cmssy/editable-layout.tsx +12 -0
- package/assets/init/next/cmssy/editor.tsx +10 -0
- package/assets/init/next/cmssy.config.ts +14 -0
- package/assets/init/next/env.example +7 -0
- package/assets/init/next/proxy.ts +13 -0
- package/assets/init/remix/app/cmssy/blocks.ts +12 -0
- package/assets/init/remix/app/cmssy/editor.tsx +14 -0
- package/assets/init/remix/app/cmssy/hero.tsx +17 -0
- package/assets/init/remix/app/routes/page.tsx +67 -0
- package/assets/init/remix/app/routes/robots.ts +4 -0
- package/assets/init/remix/app/routes/sitemap.ts +4 -0
- package/assets/init/remix/app/routes.ts +10 -0
- package/assets/init/remix/cmssy.config.ts +9 -0
- package/assets/init/remix/env.example +7 -0
- package/dist/index.js +333 -52
- package/package.json +6 -4
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineBlock } from "@cmssy/react";
|
|
2
|
+
import { Hero, heroProps } from "./hero";
|
|
3
|
+
|
|
4
|
+
export const heroBlock = defineBlock({
|
|
5
|
+
type: "hero",
|
|
6
|
+
label: "Hero",
|
|
7
|
+
component: Hero,
|
|
8
|
+
props: heroProps,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export const blocks = [heroBlock];
|
|
12
|
+
export default blocks;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { CmssyLazyEditor } from "@cmssy/react/client";
|
|
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
|
+
export default function CmssyEditor(props: Record<string, unknown>) {
|
|
8
|
+
return <CmssyLazyEditor {...(props as never)} load={() => import("./blocks")} />;
|
|
9
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { fields, type BlockProps } from "@cmssy/react";
|
|
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
|
+
export const heroProps = {
|
|
6
|
+
title: fields.text({ label: "Title", required: true }),
|
|
7
|
+
subtitle: fields.text({ label: "Subtitle" }),
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function Hero({ content }: BlockProps<typeof heroProps>) {
|
|
11
|
+
return (
|
|
12
|
+
<section className="hero">
|
|
13
|
+
<h1>{content.title}</h1>
|
|
14
|
+
{content.subtitle ? <p>{content.subtitle}</p> : null}
|
|
15
|
+
</section>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { defineCmssyConfig } from "@cmssy/astro";
|
|
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
|
+
export const cmssy = defineCmssyConfig({
|
|
6
|
+
org: process.env.CMSSY_ORG_SLUG,
|
|
7
|
+
workspaceSlug: process.env.CMSSY_WORKSPACE_SLUG,
|
|
8
|
+
draftSecret: process.env.CMSSY_DRAFT_SECRET,
|
|
9
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { CmssyBlock, buildBlockMap, buildBlockContext } from "@cmssy/react";
|
|
2
|
+
import type { CmssyPageData } from "@cmssy/core";
|
|
3
|
+
import { blocks } from "../cmssy/blocks";
|
|
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
|
+
export function Blocks({
|
|
10
|
+
page,
|
|
11
|
+
locale,
|
|
12
|
+
defaultLocale,
|
|
13
|
+
enabledLocales,
|
|
14
|
+
}: {
|
|
15
|
+
page: CmssyPageData;
|
|
16
|
+
locale: string;
|
|
17
|
+
defaultLocale: string;
|
|
18
|
+
enabledLocales: string[];
|
|
19
|
+
}) {
|
|
20
|
+
const blockMap = buildBlockMap(blocks);
|
|
21
|
+
const context = buildBlockContext(locale, defaultLocale, enabledLocales);
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<>
|
|
25
|
+
{(page.blocks ?? []).map((block) => (
|
|
26
|
+
<CmssyBlock
|
|
27
|
+
key={block.id}
|
|
28
|
+
block={block}
|
|
29
|
+
blockMap={blockMap}
|
|
30
|
+
locale={locale}
|
|
31
|
+
defaultLocale={defaultLocale}
|
|
32
|
+
context={context}
|
|
33
|
+
/>
|
|
34
|
+
))}
|
|
35
|
+
</>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { cmssyMiddleware } from "@cmssy/astro";
|
|
2
|
+
import { cmssy } from "./cmssy.config";
|
|
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
|
+
export const onRequest = cmssyMiddleware(cmssy);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { loadCmssyPage } from "@cmssy/astro";
|
|
3
|
+
import { cmssy } from "../cmssy.config";
|
|
4
|
+
import { Blocks } from "../components/Blocks";
|
|
5
|
+
|
|
6
|
+
export const prerender = false;
|
|
7
|
+
|
|
8
|
+
const { page, locale, defaultLocale, enabledLocales } = await loadCmssyPage(
|
|
9
|
+
cmssy,
|
|
10
|
+
Astro.request,
|
|
11
|
+
Astro.url,
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
if (!page) {
|
|
15
|
+
Astro.response.status = 404;
|
|
16
|
+
}
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
<html lang={locale}>
|
|
20
|
+
<head>
|
|
21
|
+
<meta charset="utf-8" />
|
|
22
|
+
<title>{page?.seoTitle?.[locale] ?? page?.displayName?.[locale] ?? "cmssy on Astro"}</title>
|
|
23
|
+
</head>
|
|
24
|
+
<body>
|
|
25
|
+
{
|
|
26
|
+
page ? (
|
|
27
|
+
<Blocks
|
|
28
|
+
page={page}
|
|
29
|
+
locale={locale}
|
|
30
|
+
defaultLocale={defaultLocale}
|
|
31
|
+
enabledLocales={enabledLocales}
|
|
32
|
+
/>
|
|
33
|
+
) : (
|
|
34
|
+
<main><h1>Not found</h1></main>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
</body>
|
|
38
|
+
</html>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { loadCmssyPage } from "@cmssy/astro";
|
|
3
|
+
import { cmssy } from "../../cmssy.config";
|
|
4
|
+
import CmssyEditor from "../../cmssy/editor";
|
|
5
|
+
|
|
6
|
+
// The editor iframe lands here (the middleware rewrites verified requests). It
|
|
7
|
+
// must be dynamic: a prerendered page never sees the query string.
|
|
8
|
+
export const prerender = false;
|
|
9
|
+
|
|
10
|
+
const { page, locale, defaultLocale, enabledLocales } = await loadCmssyPage(
|
|
11
|
+
cmssy,
|
|
12
|
+
Astro.request,
|
|
13
|
+
Astro.url,
|
|
14
|
+
);
|
|
15
|
+
const editorOrigin = cmssy.editorOrigin ?? "*";
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<html lang={locale}>
|
|
19
|
+
<head>
|
|
20
|
+
<meta charset="utf-8" />
|
|
21
|
+
<title>cmssy editor</title>
|
|
22
|
+
</head>
|
|
23
|
+
<body>
|
|
24
|
+
<CmssyEditor
|
|
25
|
+
client:load
|
|
26
|
+
page={page}
|
|
27
|
+
locale={locale}
|
|
28
|
+
defaultLocale={defaultLocale}
|
|
29
|
+
enabledLocales={enabledLocales}
|
|
30
|
+
edit={{ editorOrigin }}
|
|
31
|
+
/>
|
|
32
|
+
</body>
|
|
33
|
+
</html>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { buildCmssyMetadata, createCmssyPage } from "@cmssy/next/server";
|
|
2
|
+
import { cmssy } from "@/cmssy.config";
|
|
3
|
+
import { blocks } from "@/cmssy/blocks";
|
|
4
|
+
import { CmssyEditor } from "@/cmssy/editor";
|
|
5
|
+
|
|
6
|
+
type PageProps = { params: Promise<{ path?: string[] }> };
|
|
7
|
+
|
|
8
|
+
export async function generateMetadata({ params }: PageProps) {
|
|
9
|
+
const { path } = await params;
|
|
10
|
+
// As routed, prefix and all: the prefix IS the language. Strip it and every
|
|
11
|
+
// translated page gets the default language's title - and a canonical
|
|
12
|
+
// pointing at the default language's URL, which reads as "duplicate".
|
|
13
|
+
return buildCmssyMetadata(cmssy, path);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default createCmssyPage(cmssy, blocks, { editor: CmssyEditor });
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createCmssyEditPage } from "@cmssy/next/server";
|
|
2
|
+
import { cmssy } from "@/cmssy.config";
|
|
3
|
+
import { blocks } from "@/cmssy/blocks";
|
|
4
|
+
import { CmssyEditor } from "@/cmssy/editor";
|
|
5
|
+
|
|
6
|
+
// The route the middleware rewrites a verified editor request onto. The public
|
|
7
|
+
// pages stay static, and a static page never sees the query string that would
|
|
8
|
+
// put it in edit mode - which is why the editor needs a route of its own.
|
|
9
|
+
//
|
|
10
|
+
// Delete this file and the editor preview goes blank.
|
|
11
|
+
export const dynamic = "force-dynamic";
|
|
12
|
+
|
|
13
|
+
export default createCmssyEditPage(cmssy, blocks, { editor: CmssyEditor });
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createCmssySitemap } from "@cmssy/next/server";
|
|
2
|
+
import { cmssy } from "@/cmssy.config";
|
|
3
|
+
|
|
4
|
+
// One entry per language version of every published page, with hreflang and
|
|
5
|
+
// x-default. Rendering products or categories from model records? They are not
|
|
6
|
+
// pages - add them through `extra`.
|
|
7
|
+
export default createCmssySitemap(cmssy);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { fields, type BlockProps } from "@cmssy/react";
|
|
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
|
+
export const heroProps = {
|
|
6
|
+
heading: fields.text({ label: "Heading", required: true }),
|
|
7
|
+
text: fields.textarea({ label: "Text" }),
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default function Hero({ content }: BlockProps<typeof heroProps>) {
|
|
11
|
+
return (
|
|
12
|
+
<section>
|
|
13
|
+
<h1>{content.heading}</h1>
|
|
14
|
+
{content.text ? <p>{content.text}</p> : null}
|
|
15
|
+
</section>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineBlock } from "@cmssy/react";
|
|
2
|
+
import Hero, { heroProps } from "./Hero";
|
|
3
|
+
|
|
4
|
+
export const heroBlock = defineBlock({
|
|
5
|
+
type: "hero",
|
|
6
|
+
label: "Hero",
|
|
7
|
+
component: Hero,
|
|
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
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
CmssyLazyLayout,
|
|
5
|
+
type CmssyLazyLayoutProps,
|
|
6
|
+
} from "@cmssy/react/client";
|
|
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
|
+
export function EditableLayout(props: Omit<CmssyLazyLayoutProps, "load">) {
|
|
11
|
+
return <CmssyLazyLayout {...props} load={() => import("./blocks")} />;
|
|
12
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { CmssyLazyEditor } from "@cmssy/react/client";
|
|
4
|
+
import type { CmssyEditorProps } from "@cmssy/next";
|
|
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
|
+
export function CmssyEditor(props: CmssyEditorProps) {
|
|
9
|
+
return <CmssyLazyEditor {...props} load={() => import("./blocks")} />;
|
|
10
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineCmssyConfig } from "@cmssy/next";
|
|
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
|
+
export const cmssy = defineCmssyConfig({
|
|
11
|
+
org: process.env.CMSSY_ORG_SLUG,
|
|
12
|
+
workspaceSlug: process.env.CMSSY_WORKSPACE_SLUG,
|
|
13
|
+
draftSecret: process.env.CMSSY_DRAFT_SECRET,
|
|
14
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# `npx @cmssy/cli link --token cs_...` writes all three into .env.local for you.
|
|
2
|
+
# Your organization slug (Settings -> Headless in the cmssy dashboard)
|
|
3
|
+
CMSSY_ORG_SLUG=
|
|
4
|
+
# Your workspace slug (Settings -> Headless in the cmssy dashboard)
|
|
5
|
+
CMSSY_WORKSPACE_SLUG=
|
|
6
|
+
# Draft/preview secret, generated per workspace (Settings -> Headless)
|
|
7
|
+
CMSSY_DRAFT_SECRET=
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createCmssyProxy } from "@cmssy/next/middleware";
|
|
2
|
+
import { cmssy } from "@/cmssy.config";
|
|
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
|
+
export const proxy = createCmssyProxy(cmssy);
|
|
8
|
+
|
|
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
|
+
export const config = {
|
|
12
|
+
matcher: ["/((?!_next/|api/|.*\\..*).*)"],
|
|
13
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineBlock } from "@cmssy/react";
|
|
2
|
+
import { Hero, heroProps } from "./hero";
|
|
3
|
+
|
|
4
|
+
export const heroBlock = defineBlock({
|
|
5
|
+
type: "hero",
|
|
6
|
+
label: "Hero",
|
|
7
|
+
component: Hero,
|
|
8
|
+
props: heroProps,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export const blocks = [heroBlock];
|
|
12
|
+
export default blocks;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { CmssyLazyEditor } from "@cmssy/react/client";
|
|
2
|
+
import type { CmssyPageData } from "@cmssy/core";
|
|
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
|
+
export function CmssyEditor(props: {
|
|
7
|
+
page: CmssyPageData | null;
|
|
8
|
+
locale: string;
|
|
9
|
+
defaultLocale: string;
|
|
10
|
+
enabledLocales: string[];
|
|
11
|
+
edit: { editorOrigin: string | string[] };
|
|
12
|
+
}) {
|
|
13
|
+
return <CmssyLazyEditor {...props} load={() => import("./blocks")} />;
|
|
14
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { fields, type BlockProps } from "@cmssy/react";
|
|
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
|
+
export const heroProps = {
|
|
6
|
+
title: fields.text({ label: "Title", required: true }),
|
|
7
|
+
subtitle: fields.text({ label: "Subtitle" }),
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function Hero({ content }: BlockProps<typeof heroProps>) {
|
|
11
|
+
return (
|
|
12
|
+
<section>
|
|
13
|
+
<h1>{content.title}</h1>
|
|
14
|
+
{content.subtitle ? <p>{content.subtitle}</p> : null}
|
|
15
|
+
</section>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { CmssyBlock, buildBlockContext, buildBlockMap } from "@cmssy/react";
|
|
2
|
+
import { createCmssyHeaders, createCmssyLoader } from "@cmssy/remix";
|
|
3
|
+
import { cmssy } from "../../cmssy.config";
|
|
4
|
+
import { blocks } from "../cmssy/blocks";
|
|
5
|
+
import { CmssyEditor } from "../cmssy/editor";
|
|
6
|
+
import type { Route } from "./+types/page";
|
|
7
|
+
|
|
8
|
+
export const loader = createCmssyLoader(cmssy);
|
|
9
|
+
|
|
10
|
+
// Without these the admin cannot frame the site, and the editor shows an empty
|
|
11
|
+
// box with no error anywhere.
|
|
12
|
+
export const headers = createCmssyHeaders(cmssy);
|
|
13
|
+
|
|
14
|
+
export default function CmssyPage({ loaderData }: Route.ComponentProps) {
|
|
15
|
+
const {
|
|
16
|
+
page,
|
|
17
|
+
locale,
|
|
18
|
+
defaultLocale,
|
|
19
|
+
enabledLocales,
|
|
20
|
+
isEdit,
|
|
21
|
+
editorOrigin,
|
|
22
|
+
diagnostics,
|
|
23
|
+
} = loaderData;
|
|
24
|
+
|
|
25
|
+
if (diagnostics) {
|
|
26
|
+
return <div dangerouslySetInnerHTML={{ __html: diagnostics }} />;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// A verified editor request renders the same page through the edit bridge.
|
|
30
|
+
// No separate route: a React Router page always sees its query string.
|
|
31
|
+
if (isEdit) {
|
|
32
|
+
return (
|
|
33
|
+
<CmssyEditor
|
|
34
|
+
page={page}
|
|
35
|
+
locale={locale}
|
|
36
|
+
defaultLocale={defaultLocale}
|
|
37
|
+
enabledLocales={enabledLocales}
|
|
38
|
+
edit={{ editorOrigin }}
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!page)
|
|
44
|
+
return (
|
|
45
|
+
<main>
|
|
46
|
+
<h1>Not found</h1>
|
|
47
|
+
</main>
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const blockMap = buildBlockMap(blocks);
|
|
51
|
+
const context = buildBlockContext(locale, defaultLocale, enabledLocales);
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<main>
|
|
55
|
+
{(page.blocks ?? []).map((block) => (
|
|
56
|
+
<CmssyBlock
|
|
57
|
+
key={block.id}
|
|
58
|
+
block={block}
|
|
59
|
+
blockMap={blockMap}
|
|
60
|
+
locale={locale}
|
|
61
|
+
defaultLocale={defaultLocale}
|
|
62
|
+
context={context}
|
|
63
|
+
/>
|
|
64
|
+
))}
|
|
65
|
+
</main>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type RouteConfig, index, route } from "@react-router/dev/routes";
|
|
2
|
+
|
|
3
|
+
// The splat does not match "/", so the homepage needs its own entry - it is the
|
|
4
|
+
// same route module, mounted twice.
|
|
5
|
+
export default [
|
|
6
|
+
route("sitemap.xml", "routes/sitemap.ts"),
|
|
7
|
+
route("robots.txt", "routes/robots.ts"),
|
|
8
|
+
index("routes/page.tsx"),
|
|
9
|
+
route("*", "routes/page.tsx", { id: "cmssy-catch-all" }),
|
|
10
|
+
] satisfies RouteConfig;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { defineCmssyConfig } from "@cmssy/remix";
|
|
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
|
+
export const cmssy = defineCmssyConfig({
|
|
6
|
+
org: process.env.CMSSY_ORG_SLUG,
|
|
7
|
+
workspaceSlug: process.env.CMSSY_WORKSPACE_SLUG,
|
|
8
|
+
draftSecret: process.env.CMSSY_DRAFT_SECRET,
|
|
9
|
+
});
|
package/dist/index.js
CHANGED
|
@@ -3,14 +3,16 @@
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { createInterface } from "readline/promises";
|
|
5
5
|
|
|
6
|
-
// src/
|
|
7
|
-
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
8
|
-
import { join } from "path";
|
|
6
|
+
// src/init.ts
|
|
9
7
|
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
copyFileSync,
|
|
9
|
+
existsSync,
|
|
10
|
+
mkdirSync,
|
|
11
|
+
readFileSync,
|
|
12
|
+
writeFileSync
|
|
13
|
+
} from "fs";
|
|
14
|
+
import { dirname, join, resolve } from "path";
|
|
15
|
+
import { fileURLToPath } from "url";
|
|
14
16
|
|
|
15
17
|
// src/admin-client.ts
|
|
16
18
|
var DEFAULT_ADMIN_API_URL = "https://api.cmssy.io/graphql";
|
|
@@ -120,6 +122,298 @@ async function setPreviewUrl(previewUrl, options) {
|
|
|
120
122
|
);
|
|
121
123
|
}
|
|
122
124
|
|
|
125
|
+
// src/format.ts
|
|
126
|
+
var GREEN = "\x1B[32m";
|
|
127
|
+
var RED = "\x1B[31m";
|
|
128
|
+
var YELLOW = "\x1B[33m";
|
|
129
|
+
var CYAN = "\x1B[36m";
|
|
130
|
+
var RESET = "\x1B[0m";
|
|
131
|
+
function useColor(env = process.env, isTty = Boolean(process.stdout.isTTY)) {
|
|
132
|
+
return isTty && env.NO_COLOR === void 0;
|
|
133
|
+
}
|
|
134
|
+
function paint(text, color, colored) {
|
|
135
|
+
return colored ? `${color}${text}${RESET}` : text;
|
|
136
|
+
}
|
|
137
|
+
function formatResult(result, colored = useColor()) {
|
|
138
|
+
if (result.status === "ok") {
|
|
139
|
+
return `${paint("\u2713", GREEN, colored)} ${result.message}`;
|
|
140
|
+
}
|
|
141
|
+
if (result.status === "unknown") {
|
|
142
|
+
return `${paint("?", YELLOW, colored)} ${result.message}`;
|
|
143
|
+
}
|
|
144
|
+
const fix = result.fix ? `
|
|
145
|
+
fix: ${result.fix}` : "";
|
|
146
|
+
return `${paint("\u2717", RED, colored)} ${result.message}${fix}`;
|
|
147
|
+
}
|
|
148
|
+
function formatEditorLink(url, colored = useColor()) {
|
|
149
|
+
return `
|
|
150
|
+
Edit this site visually:
|
|
151
|
+
${paint(url, CYAN, colored)}
|
|
152
|
+
`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// src/init.ts
|
|
156
|
+
var ASSETS_DIR = fileURLToPath(new URL("../assets/init", import.meta.url));
|
|
157
|
+
var CLI_PACKAGE_JSON = fileURLToPath(
|
|
158
|
+
new URL("../package.json", import.meta.url)
|
|
159
|
+
);
|
|
160
|
+
var FRAMEWORKS = [
|
|
161
|
+
{
|
|
162
|
+
name: "next",
|
|
163
|
+
label: "Next.js",
|
|
164
|
+
createCommand: "npx create-next-app@latest",
|
|
165
|
+
detect: ["next"],
|
|
166
|
+
dependencies: ["@cmssy/next", "@cmssy/react"],
|
|
167
|
+
files: [
|
|
168
|
+
"cmssy.config.ts",
|
|
169
|
+
"proxy.ts",
|
|
170
|
+
"cmssy/blocks.ts",
|
|
171
|
+
"cmssy/editor.tsx",
|
|
172
|
+
"cmssy/editable-layout.tsx",
|
|
173
|
+
"blocks/hero/block.ts",
|
|
174
|
+
"blocks/hero/Hero.tsx",
|
|
175
|
+
"app/[[...path]]/page.tsx",
|
|
176
|
+
"app/cmssy-edit/[[...path]]/page.tsx",
|
|
177
|
+
"app/api/draft/route.ts",
|
|
178
|
+
"app/robots.ts",
|
|
179
|
+
"app/sitemap.ts"
|
|
180
|
+
]
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
name: "astro",
|
|
184
|
+
label: "Astro",
|
|
185
|
+
createCommand: "npm create astro@latest",
|
|
186
|
+
detect: ["astro"],
|
|
187
|
+
dependencies: ["@cmssy/astro", "@cmssy/react", "@cmssy/core"],
|
|
188
|
+
files: [
|
|
189
|
+
"src/cmssy.config.ts",
|
|
190
|
+
"src/middleware.ts",
|
|
191
|
+
"src/cmssy/blocks.ts",
|
|
192
|
+
"src/cmssy/editor.tsx",
|
|
193
|
+
"src/cmssy/hero.tsx",
|
|
194
|
+
"src/components/Blocks.tsx",
|
|
195
|
+
"src/pages/[...path].astro",
|
|
196
|
+
"src/pages/cmssy-edit/[...path].astro",
|
|
197
|
+
"src/pages/robots.txt.ts",
|
|
198
|
+
"src/pages/sitemap.xml.ts"
|
|
199
|
+
]
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: "remix",
|
|
203
|
+
label: "React Router 7 / Remix",
|
|
204
|
+
createCommand: "npx create-react-router@latest",
|
|
205
|
+
detect: ["react-router", "@react-router/dev", "@remix-run/react"],
|
|
206
|
+
dependencies: ["@cmssy/remix", "@cmssy/react", "@cmssy/core"],
|
|
207
|
+
files: [
|
|
208
|
+
"cmssy.config.ts",
|
|
209
|
+
"app/routes.ts",
|
|
210
|
+
"app/cmssy/blocks.ts",
|
|
211
|
+
"app/cmssy/editor.tsx",
|
|
212
|
+
"app/cmssy/hero.tsx",
|
|
213
|
+
"app/routes/page.tsx",
|
|
214
|
+
"app/routes/robots.ts",
|
|
215
|
+
"app/routes/sitemap.ts"
|
|
216
|
+
]
|
|
217
|
+
}
|
|
218
|
+
];
|
|
219
|
+
function readPackageJson(root) {
|
|
220
|
+
const path = join(root, "package.json");
|
|
221
|
+
if (!existsSync(path)) {
|
|
222
|
+
throw new CliError(
|
|
223
|
+
`no package.json in ${root}`,
|
|
224
|
+
"cmssy init wires cmssy into an EXISTING app - create one first, then rerun inside it"
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
try {
|
|
228
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
229
|
+
} catch {
|
|
230
|
+
throw new CliError(`${path} is not valid JSON`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function detectFramework(pkg) {
|
|
234
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
235
|
+
const match = FRAMEWORKS.find(
|
|
236
|
+
(framework) => framework.detect.some((name) => deps[name] !== void 0)
|
|
237
|
+
);
|
|
238
|
+
if (!match) {
|
|
239
|
+
const hints = FRAMEWORKS.map(
|
|
240
|
+
(framework) => `${framework.label}: create the app with \`${framework.createCommand}\`, then rerun cmssy init inside it`
|
|
241
|
+
);
|
|
242
|
+
throw new CliError(
|
|
243
|
+
"no supported framework in package.json (looked for next, astro, react-router)",
|
|
244
|
+
hints.join("\n ")
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
return match;
|
|
248
|
+
}
|
|
249
|
+
function frameworkFiles(framework, root) {
|
|
250
|
+
const srcPrefix = framework.name === "next" && existsSync(join(root, "src", "app")) ? "src/" : "";
|
|
251
|
+
return [
|
|
252
|
+
{ asset: "env.example", target: ".env.example" },
|
|
253
|
+
...framework.files.map((path) => ({
|
|
254
|
+
asset: path,
|
|
255
|
+
target: framework.name === "next" ? `${srcPrefix}${path}` : path
|
|
256
|
+
}))
|
|
257
|
+
];
|
|
258
|
+
}
|
|
259
|
+
function cliVersion() {
|
|
260
|
+
const pkg = JSON.parse(readFileSync(CLI_PACKAGE_JSON, "utf8"));
|
|
261
|
+
return pkg.version;
|
|
262
|
+
}
|
|
263
|
+
function addDependencies(root, pkg, framework) {
|
|
264
|
+
const present = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
265
|
+
const missing = framework.dependencies.filter(
|
|
266
|
+
(name) => present[name] === void 0
|
|
267
|
+
);
|
|
268
|
+
if (missing.length === 0) return [];
|
|
269
|
+
const range = `^${cliVersion()}`;
|
|
270
|
+
const dependencies = { ...pkg.dependencies };
|
|
271
|
+
for (const name of missing) dependencies[name] = range;
|
|
272
|
+
pkg.dependencies = Object.fromEntries(
|
|
273
|
+
Object.entries(dependencies).sort(([a], [b]) => a.localeCompare(b))
|
|
274
|
+
);
|
|
275
|
+
writeFileSync(
|
|
276
|
+
join(root, "package.json"),
|
|
277
|
+
`${JSON.stringify(pkg, null, 2)}
|
|
278
|
+
`
|
|
279
|
+
);
|
|
280
|
+
return missing.map((name) => `${name}@${range}`);
|
|
281
|
+
}
|
|
282
|
+
function detectInstallCommand(root) {
|
|
283
|
+
if (existsSync(join(root, "pnpm-lock.yaml")) || existsSync(join(root, "pnpm-workspace.yaml"))) {
|
|
284
|
+
return "pnpm install";
|
|
285
|
+
}
|
|
286
|
+
if (existsSync(join(root, "yarn.lock"))) return "yarn";
|
|
287
|
+
if (existsSync(join(root, "bun.lock")) || existsSync(join(root, "bun.lockb"))) {
|
|
288
|
+
return "bun install";
|
|
289
|
+
}
|
|
290
|
+
return "npm install";
|
|
291
|
+
}
|
|
292
|
+
function frameworkNotes(framework, root, skipped) {
|
|
293
|
+
const notes = [];
|
|
294
|
+
if (framework.name === "next") {
|
|
295
|
+
const srcPrefix = existsSync(join(root, "src", "app")) ? "src/" : "";
|
|
296
|
+
const home = `${srcPrefix}app/page.tsx`;
|
|
297
|
+
if (existsSync(join(root, home))) {
|
|
298
|
+
notes.push({
|
|
299
|
+
status: "unknown",
|
|
300
|
+
message: `${home} conflicts with the cmssy catch-all route - delete it and the cmssy page serves /`
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (framework.name === "astro") {
|
|
305
|
+
notes.push({
|
|
306
|
+
status: "unknown",
|
|
307
|
+
message: "the cmssy wiring needs the React integration and a server adapter - run: npx astro add react node"
|
|
308
|
+
});
|
|
309
|
+
if (existsSync(join(root, "src/pages/index.astro"))) {
|
|
310
|
+
notes.push({
|
|
311
|
+
status: "unknown",
|
|
312
|
+
message: "src/pages/index.astro shadows the cmssy catch-all for / - delete it and the cmssy page serves /"
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (framework.name === "remix" && skipped.includes("app/routes.ts")) {
|
|
317
|
+
notes.push({
|
|
318
|
+
status: "unknown",
|
|
319
|
+
message: "app/routes.ts already existed - mount routes/page.tsx (index + splat), routes/robots.ts and routes/sitemap.ts there yourself, or rerun with --force"
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
return notes;
|
|
323
|
+
}
|
|
324
|
+
function runInit(options, deps) {
|
|
325
|
+
const { log } = deps;
|
|
326
|
+
try {
|
|
327
|
+
const root = resolve(deps.cwd, options.dir ?? ".");
|
|
328
|
+
if (!existsSync(root)) {
|
|
329
|
+
throw new CliError(
|
|
330
|
+
`${root} does not exist`,
|
|
331
|
+
"pass --dir with the app's directory, or run cmssy init inside it"
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
const pkg = readPackageJson(root);
|
|
335
|
+
const framework = detectFramework(pkg);
|
|
336
|
+
log(
|
|
337
|
+
formatResult({
|
|
338
|
+
status: "ok",
|
|
339
|
+
message: `detected ${framework.label} - wiring cmssy into ${root}`
|
|
340
|
+
})
|
|
341
|
+
);
|
|
342
|
+
const written = [];
|
|
343
|
+
const skipped = [];
|
|
344
|
+
for (const file of frameworkFiles(framework, root)) {
|
|
345
|
+
const target = join(root, file.target);
|
|
346
|
+
if (existsSync(target) && !options.force) {
|
|
347
|
+
skipped.push(file.target);
|
|
348
|
+
log(
|
|
349
|
+
formatResult({
|
|
350
|
+
status: "unknown",
|
|
351
|
+
message: `${file.target} exists, skipped (--force overwrites)`
|
|
352
|
+
})
|
|
353
|
+
);
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
357
|
+
copyFileSync(join(ASSETS_DIR, framework.name, file.asset), target);
|
|
358
|
+
written.push(file.target);
|
|
359
|
+
log(formatResult({ status: "ok", message: `wrote ${file.target}` }));
|
|
360
|
+
}
|
|
361
|
+
const added = addDependencies(root, pkg, framework);
|
|
362
|
+
if (added.length > 0) {
|
|
363
|
+
log(
|
|
364
|
+
formatResult({
|
|
365
|
+
status: "ok",
|
|
366
|
+
message: `added ${added.join(", ")} to package.json`
|
|
367
|
+
})
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
for (const note of frameworkNotes(framework, root, skipped)) {
|
|
371
|
+
log(formatResult(note));
|
|
372
|
+
}
|
|
373
|
+
log("");
|
|
374
|
+
log(
|
|
375
|
+
`${written.length} file${written.length === 1 ? "" : "s"} written, ${skipped.length} skipped.`
|
|
376
|
+
);
|
|
377
|
+
log("");
|
|
378
|
+
log("Next steps:");
|
|
379
|
+
let step = 1;
|
|
380
|
+
if (added.length > 0) log(` ${step++}. ${detectInstallCommand(root)}`);
|
|
381
|
+
log(` ${step++}. npx @cmssy/cli link --token cs_...`);
|
|
382
|
+
const registry = frameworkFiles(framework, root).find(
|
|
383
|
+
(file) => file.target.endsWith("cmssy/blocks.ts")
|
|
384
|
+
);
|
|
385
|
+
log(` ${step}. add your blocks to ${registry?.target} and publish a page`);
|
|
386
|
+
return 0;
|
|
387
|
+
} catch (error) {
|
|
388
|
+
if (error instanceof CliError) {
|
|
389
|
+
log(
|
|
390
|
+
formatResult({
|
|
391
|
+
status: "fail",
|
|
392
|
+
message: error.message,
|
|
393
|
+
fix: error.fix
|
|
394
|
+
})
|
|
395
|
+
);
|
|
396
|
+
return 1;
|
|
397
|
+
}
|
|
398
|
+
log(
|
|
399
|
+
formatResult({
|
|
400
|
+
status: "fail",
|
|
401
|
+
message: error instanceof Error ? error.message : String(error)
|
|
402
|
+
})
|
|
403
|
+
);
|
|
404
|
+
return 1;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// src/link.ts
|
|
409
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
410
|
+
import { join as join2 } from "path";
|
|
411
|
+
import {
|
|
412
|
+
buildEditorUrl,
|
|
413
|
+
checkDraftSecret,
|
|
414
|
+
checkWorkspaceReachable
|
|
415
|
+
} from "@cmssy/core/preflight";
|
|
416
|
+
|
|
123
417
|
// src/env-file.ts
|
|
124
418
|
function parseEnvFile(content) {
|
|
125
419
|
const vars = {};
|
|
@@ -179,43 +473,13 @@ function mergeEnvContent(existing, updates) {
|
|
|
179
473
|
`;
|
|
180
474
|
}
|
|
181
475
|
|
|
182
|
-
// src/format.ts
|
|
183
|
-
var GREEN = "\x1B[32m";
|
|
184
|
-
var RED = "\x1B[31m";
|
|
185
|
-
var YELLOW = "\x1B[33m";
|
|
186
|
-
var CYAN = "\x1B[36m";
|
|
187
|
-
var RESET = "\x1B[0m";
|
|
188
|
-
function useColor(env = process.env, isTty = Boolean(process.stdout.isTTY)) {
|
|
189
|
-
return isTty && env.NO_COLOR === void 0;
|
|
190
|
-
}
|
|
191
|
-
function paint(text, color, colored) {
|
|
192
|
-
return colored ? `${color}${text}${RESET}` : text;
|
|
193
|
-
}
|
|
194
|
-
function formatResult(result, colored = useColor()) {
|
|
195
|
-
if (result.status === "ok") {
|
|
196
|
-
return `${paint("\u2713", GREEN, colored)} ${result.message}`;
|
|
197
|
-
}
|
|
198
|
-
if (result.status === "unknown") {
|
|
199
|
-
return `${paint("?", YELLOW, colored)} ${result.message}`;
|
|
200
|
-
}
|
|
201
|
-
const fix = result.fix ? `
|
|
202
|
-
fix: ${result.fix}` : "";
|
|
203
|
-
return `${paint("\u2717", RED, colored)} ${result.message}${fix}`;
|
|
204
|
-
}
|
|
205
|
-
function formatEditorLink(url, colored = useColor()) {
|
|
206
|
-
return `
|
|
207
|
-
Edit this site visually:
|
|
208
|
-
${paint(url, CYAN, colored)}
|
|
209
|
-
`;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
476
|
// src/link.ts
|
|
213
477
|
var ENV_FILES = [".env.local", ".env"];
|
|
214
478
|
function loadEnvFiles(cwd, env) {
|
|
215
479
|
for (const file of ENV_FILES) {
|
|
216
|
-
const path =
|
|
217
|
-
if (!
|
|
218
|
-
applyEnv(parseEnvFile(
|
|
480
|
+
const path = join2(cwd, file);
|
|
481
|
+
if (!existsSync2(path)) continue;
|
|
482
|
+
applyEnv(parseEnvFile(readFileSync2(path, "utf8")), env);
|
|
219
483
|
}
|
|
220
484
|
}
|
|
221
485
|
function resolveToken(options, deps) {
|
|
@@ -298,9 +562,9 @@ function resolvePreviewUrl(options) {
|
|
|
298
562
|
return origin;
|
|
299
563
|
}
|
|
300
564
|
function writeEnvLocal(cwd, updates) {
|
|
301
|
-
const path =
|
|
302
|
-
const existing =
|
|
303
|
-
|
|
565
|
+
const path = join2(cwd, ".env.local");
|
|
566
|
+
const existing = existsSync2(path) ? readFileSync2(path, "utf8") : null;
|
|
567
|
+
writeFileSync2(path, mergeEnvContent(existing, updates));
|
|
304
568
|
}
|
|
305
569
|
async function runLink(options, deps) {
|
|
306
570
|
const { cwd, env, log } = deps;
|
|
@@ -395,22 +659,23 @@ async function runLink(options, deps) {
|
|
|
395
659
|
}
|
|
396
660
|
|
|
397
661
|
// src/index.ts
|
|
398
|
-
var USAGE =
|
|
662
|
+
var USAGE = [
|
|
663
|
+
"usage: cmssy <command>",
|
|
664
|
+
" cmssy init [--dir <path>] [--force]",
|
|
665
|
+
" cmssy link [--token <cs_...>] [--workspace <slug>] [--preview-url <url>]"
|
|
666
|
+
].join("\n");
|
|
399
667
|
function flagValue(args, name) {
|
|
400
668
|
const index = args.findIndex((arg) => arg === name);
|
|
401
669
|
if (index !== -1) return args[index + 1];
|
|
402
670
|
return args.find((arg) => arg.startsWith(`${name}=`))?.slice(name.length + 1);
|
|
403
671
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
process.exitCode = command === void 0 || command === "--help" ? 0 : 1;
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
672
|
+
function hasFlag(args, name) {
|
|
673
|
+
return args.includes(name);
|
|
674
|
+
}
|
|
675
|
+
async function runLinkCommand(args) {
|
|
411
676
|
const rl = process.stdin.isTTY ? createInterface({ input: process.stdin, output: process.stdout }) : null;
|
|
412
677
|
try {
|
|
413
|
-
|
|
678
|
+
return await runLink(
|
|
414
679
|
{
|
|
415
680
|
token: flagValue(args, "--token"),
|
|
416
681
|
workspace: flagValue(args, "--workspace"),
|
|
@@ -429,6 +694,22 @@ async function main() {
|
|
|
429
694
|
rl?.close();
|
|
430
695
|
}
|
|
431
696
|
}
|
|
697
|
+
async function main() {
|
|
698
|
+
const [command, ...args] = process.argv.slice(2);
|
|
699
|
+
if (command === "init") {
|
|
700
|
+
process.exitCode = runInit(
|
|
701
|
+
{ dir: flagValue(args, "--dir"), force: hasFlag(args, "--force") },
|
|
702
|
+
{ cwd: process.cwd(), log: (line) => console.log(line) }
|
|
703
|
+
);
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
if (command === "link") {
|
|
707
|
+
process.exitCode = await runLinkCommand(args);
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
console.error(USAGE);
|
|
711
|
+
process.exitCode = command === void 0 || command === "--help" ? 0 : 1;
|
|
712
|
+
}
|
|
432
713
|
main().catch((error) => {
|
|
433
714
|
console.error(
|
|
434
715
|
`cmssy: ${error instanceof Error ? error.message : String(error)}`
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cmssy/cli",
|
|
3
|
-
"version": "9.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "9.6.0",
|
|
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",
|
|
7
7
|
"cli",
|
|
8
|
+
"init",
|
|
8
9
|
"link"
|
|
9
10
|
],
|
|
10
11
|
"homepage": "https://cmssy.com",
|
|
@@ -22,10 +23,11 @@
|
|
|
22
23
|
"cmssy": "./dist/index.js"
|
|
23
24
|
},
|
|
24
25
|
"files": [
|
|
25
|
-
"dist"
|
|
26
|
+
"dist",
|
|
27
|
+
"assets"
|
|
26
28
|
],
|
|
27
29
|
"dependencies": {
|
|
28
|
-
"@cmssy/core": "9.
|
|
30
|
+
"@cmssy/core": "9.6.0"
|
|
29
31
|
},
|
|
30
32
|
"devDependencies": {
|
|
31
33
|
"@types/node": "^20.0.0",
|