@cmssy/cli 0.22.3 → 1.0.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/README.md +49 -650
- package/dist/cli.js +706 -328
- package/package.json +26 -73
- package/templates/block/Component.module.css.tpl +12 -0
- package/templates/block/Component.tsx.tpl +15 -0
- package/templates/block/block.ts.tpl +11 -0
- package/templates/init/app/[[...path]]/page.tsx +19 -0
- package/templates/init/app/api/draft/route.ts +5 -0
- package/templates/init/blocks/hero/Hero.module.css +131 -0
- package/templates/init/blocks/hero/Hero.tsx +100 -0
- package/templates/init/blocks/hero/block.ts +21 -0
- package/templates/init/cmssy/blocks.ts +5 -0
- package/templates/init/cmssy/editor.tsx +10 -0
- package/templates/init/cmssy.config.ts +8 -0
- package/templates/init/env.example +4 -0
- package/templates/init/next.config.mjs +8 -0
- package/templates/init/proxy.ts +32 -0
- package/config.d.ts +0 -3
- package/config.js +0 -4
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/commands/add-source.d.ts +0 -13
- package/dist/commands/add-source.d.ts.map +0 -1
- package/dist/commands/add-source.js +0 -273
- package/dist/commands/add-source.js.map +0 -1
- package/dist/commands/build.d.ts +0 -7
- package/dist/commands/build.d.ts.map +0 -1
- package/dist/commands/build.js +0 -127
- package/dist/commands/build.js.map +0 -1
- package/dist/commands/codegen.d.ts +0 -7
- package/dist/commands/codegen.d.ts.map +0 -1
- package/dist/commands/codegen.js +0 -150
- package/dist/commands/codegen.js.map +0 -1
- package/dist/commands/configure.d.ts +0 -6
- package/dist/commands/configure.d.ts.map +0 -1
- package/dist/commands/configure.js +0 -39
- package/dist/commands/configure.js.map +0 -1
- package/dist/commands/create.d.ts +0 -18
- package/dist/commands/create.d.ts.map +0 -1
- package/dist/commands/create.js +0 -403
- package/dist/commands/create.js.map +0 -1
- package/dist/commands/dev.d.ts +0 -6
- package/dist/commands/dev.d.ts.map +0 -1
- package/dist/commands/dev.js +0 -144
- package/dist/commands/dev.js.map +0 -1
- package/dist/commands/doctor.d.ts +0 -2
- package/dist/commands/doctor.d.ts.map +0 -1
- package/dist/commands/doctor.js +0 -230
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/init.d.ts +0 -6
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js +0 -414
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/lib.d.ts +0 -17
- package/dist/commands/lib.d.ts.map +0 -1
- package/dist/commands/lib.js +0 -132
- package/dist/commands/lib.js.map +0 -1
- package/dist/commands/link.d.ts +0 -8
- package/dist/commands/link.d.ts.map +0 -1
- package/dist/commands/link.js +0 -116
- package/dist/commands/link.js.map +0 -1
- package/dist/commands/migrate.d.ts +0 -2
- package/dist/commands/migrate.d.ts.map +0 -1
- package/dist/commands/migrate.js +0 -231
- package/dist/commands/migrate.js.map +0 -1
- package/dist/commands/package.d.ts +0 -14
- package/dist/commands/package.d.ts.map +0 -1
- package/dist/commands/package.js +0 -137
- package/dist/commands/package.js.map +0 -1
- package/dist/commands/publish-block-buildtime.d.ts +0 -8
- package/dist/commands/publish-block-buildtime.d.ts.map +0 -1
- package/dist/commands/publish-block-buildtime.js +0 -290
- package/dist/commands/publish-block-buildtime.js.map +0 -1
- package/dist/commands/publish-template.d.ts +0 -22
- package/dist/commands/publish-template.d.ts.map +0 -1
- package/dist/commands/publish-template.js +0 -345
- package/dist/commands/publish-template.js.map +0 -1
- package/dist/commands/publish.d.ts +0 -15
- package/dist/commands/publish.d.ts.map +0 -1
- package/dist/commands/publish.js +0 -1003
- package/dist/commands/publish.js.map +0 -1
- package/dist/commands/skills.d.ts +0 -11
- package/dist/commands/skills.d.ts.map +0 -1
- package/dist/commands/skills.js +0 -178
- package/dist/commands/skills.js.map +0 -1
- package/dist/commands/sync.d.ts +0 -6
- package/dist/commands/sync.d.ts.map +0 -1
- package/dist/commands/sync.js +0 -208
- package/dist/commands/sync.js.map +0 -1
- package/dist/commands/test.d.ts +0 -8
- package/dist/commands/test.d.ts.map +0 -1
- package/dist/commands/test.js +0 -60
- package/dist/commands/test.js.map +0 -1
- package/dist/commands/upload.d.ts +0 -11
- package/dist/commands/upload.d.ts.map +0 -1
- package/dist/commands/upload.js +0 -128
- package/dist/commands/upload.js.map +0 -1
- package/dist/commands/workspaces.d.ts +0 -2
- package/dist/commands/workspaces.d.ts.map +0 -1
- package/dist/commands/workspaces.js +0 -62
- package/dist/commands/workspaces.js.map +0 -1
- package/dist/dev-ui/app.js +0 -1284
- package/dist/dev-ui/index.html +0 -1511
- package/dist/dev-ui-react/App.tsx +0 -164
- package/dist/dev-ui-react/__tests__/previewData.test.ts +0 -193
- package/dist/dev-ui-react/components/BlocksList.tsx +0 -232
- package/dist/dev-ui-react/components/Editor.tsx +0 -469
- package/dist/dev-ui-react/components/Preview.tsx +0 -146
- package/dist/dev-ui-react/hooks/useBlocks.ts +0 -80
- package/dist/dev-ui-react/index.html +0 -13
- package/dist/dev-ui-react/main.tsx +0 -8
- package/dist/dev-ui-react/styles.css +0 -856
- package/dist/dev-ui-react/types.ts +0 -45
- package/dist/skills/claude/cmssy-block.md +0 -454
- package/dist/skills/claude/cmssy-mcp-content.md +0 -231
- package/dist/test-helpers/index.d.ts +0 -23
- package/dist/test-helpers/index.d.ts.map +0 -1
- package/dist/test-helpers/index.js +0 -50
- package/dist/test-helpers/index.js.map +0 -1
- package/dist/types/block-config.d.ts +0 -6
- package/dist/types/block-config.d.ts.map +0 -1
- package/dist/types/block-config.js +0 -12
- package/dist/types/block-config.js.map +0 -1
- package/dist/types/publish-job.d.ts +0 -61
- package/dist/types/publish-job.d.ts.map +0 -1
- package/dist/types/publish-job.js +0 -13
- package/dist/types/publish-job.js.map +0 -1
- package/dist/utils/api-error.d.ts +0 -10
- package/dist/utils/api-error.d.ts.map +0 -1
- package/dist/utils/api-error.js +0 -55
- package/dist/utils/api-error.js.map +0 -1
- package/dist/utils/block-config.d.ts +0 -87
- package/dist/utils/block-config.d.ts.map +0 -1
- package/dist/utils/block-config.js +0 -214
- package/dist/utils/block-config.js.map +0 -1
- package/dist/utils/blocks-meta-cache.d.ts +0 -28
- package/dist/utils/blocks-meta-cache.d.ts.map +0 -1
- package/dist/utils/blocks-meta-cache.js +0 -72
- package/dist/utils/blocks-meta-cache.js.map +0 -1
- package/dist/utils/builder.d.ts +0 -34
- package/dist/utils/builder.d.ts.map +0 -1
- package/dist/utils/builder.js +0 -140
- package/dist/utils/builder.js.map +0 -1
- package/dist/utils/cmssy-config.d.ts +0 -16
- package/dist/utils/cmssy-config.d.ts.map +0 -1
- package/dist/utils/cmssy-config.js +0 -19
- package/dist/utils/cmssy-config.js.map +0 -1
- package/dist/utils/config.d.ts +0 -9
- package/dist/utils/config.d.ts.map +0 -1
- package/dist/utils/config.js +0 -51
- package/dist/utils/config.js.map +0 -1
- package/dist/utils/dependency-check.d.ts +0 -21
- package/dist/utils/dependency-check.d.ts.map +0 -1
- package/dist/utils/dependency-check.js +0 -56
- package/dist/utils/dependency-check.js.map +0 -1
- package/dist/utils/dev-generator/api-routes/blocks.d.ts +0 -3
- package/dist/utils/dev-generator/api-routes/blocks.d.ts.map +0 -1
- package/dist/utils/dev-generator/api-routes/blocks.js +0 -194
- package/dist/utils/dev-generator/api-routes/blocks.js.map +0 -1
- package/dist/utils/dev-generator/api-routes/config.d.ts +0 -2
- package/dist/utils/dev-generator/api-routes/config.d.ts.map +0 -1
- package/dist/utils/dev-generator/api-routes/config.js +0 -107
- package/dist/utils/dev-generator/api-routes/config.js.map +0 -1
- package/dist/utils/dev-generator/api-routes/preview.d.ts +0 -2
- package/dist/utils/dev-generator/api-routes/preview.d.ts.map +0 -1
- package/dist/utils/dev-generator/api-routes/preview.js +0 -50
- package/dist/utils/dev-generator/api-routes/preview.js.map +0 -1
- package/dist/utils/dev-generator/api-routes/workspaces.d.ts +0 -2
- package/dist/utils/dev-generator/api-routes/workspaces.d.ts.map +0 -1
- package/dist/utils/dev-generator/api-routes/workspaces.js +0 -128
- package/dist/utils/dev-generator/api-routes/workspaces.js.map +0 -1
- package/dist/utils/dev-generator/helpers.d.ts +0 -23
- package/dist/utils/dev-generator/helpers.d.ts.map +0 -1
- package/dist/utils/dev-generator/helpers.js +0 -128
- package/dist/utils/dev-generator/helpers.js.map +0 -1
- package/dist/utils/dev-generator/home-page.d.ts +0 -2
- package/dist/utils/dev-generator/home-page.d.ts.map +0 -1
- package/dist/utils/dev-generator/home-page.js +0 -643
- package/dist/utils/dev-generator/home-page.js.map +0 -1
- package/dist/utils/dev-generator/index.d.ts +0 -11
- package/dist/utils/dev-generator/index.d.ts.map +0 -1
- package/dist/utils/dev-generator/index.js +0 -85
- package/dist/utils/dev-generator/index.js.map +0 -1
- package/dist/utils/dev-generator/layout.d.ts +0 -3
- package/dist/utils/dev-generator/layout.d.ts.map +0 -1
- package/dist/utils/dev-generator/layout.js +0 -63
- package/dist/utils/dev-generator/layout.js.map +0 -1
- package/dist/utils/dev-generator/next-config.d.ts +0 -3
- package/dist/utils/dev-generator/next-config.d.ts.map +0 -1
- package/dist/utils/dev-generator/next-config.js +0 -111
- package/dist/utils/dev-generator/next-config.js.map +0 -1
- package/dist/utils/dev-generator/preview-pages.d.ts +0 -3
- package/dist/utils/dev-generator/preview-pages.d.ts.map +0 -1
- package/dist/utils/dev-generator/preview-pages.js +0 -833
- package/dist/utils/dev-generator/preview-pages.js.map +0 -1
- package/dist/utils/dev-generator.d.ts +0 -12
- package/dist/utils/dev-generator.d.ts.map +0 -1
- package/dist/utils/dev-generator.js +0 -1752
- package/dist/utils/dev-generator.js.map +0 -1
- package/dist/utils/field-schema.d.ts +0 -13
- package/dist/utils/field-schema.d.ts.map +0 -1
- package/dist/utils/field-schema.js +0 -182
- package/dist/utils/field-schema.js.map +0 -1
- package/dist/utils/graphql.d.ts +0 -39
- package/dist/utils/graphql.d.ts.map +0 -1
- package/dist/utils/graphql.js +0 -138
- package/dist/utils/graphql.js.map +0 -1
- package/dist/utils/lib-manifest.d.ts +0 -7
- package/dist/utils/lib-manifest.d.ts.map +0 -1
- package/dist/utils/lib-manifest.js +0 -59
- package/dist/utils/lib-manifest.js.map +0 -1
- package/dist/utils/publish-helpers.d.ts +0 -43
- package/dist/utils/publish-helpers.d.ts.map +0 -1
- package/dist/utils/publish-helpers.js +0 -215
- package/dist/utils/publish-helpers.js.map +0 -1
- package/dist/utils/publish-helpers.test.d.ts +0 -2
- package/dist/utils/publish-helpers.test.d.ts.map +0 -1
- package/dist/utils/publish-helpers.test.js +0 -24
- package/dist/utils/publish-helpers.test.js.map +0 -1
- package/dist/utils/resolve-workspace.d.ts +0 -6
- package/dist/utils/resolve-workspace.d.ts.map +0 -1
- package/dist/utils/resolve-workspace.js +0 -32
- package/dist/utils/resolve-workspace.js.map +0 -1
- package/dist/utils/scanner.d.ts +0 -42
- package/dist/utils/scanner.d.ts.map +0 -1
- package/dist/utils/scanner.js +0 -156
- package/dist/utils/scanner.js.map +0 -1
- package/dist/utils/schema-diff.d.ts +0 -22
- package/dist/utils/schema-diff.d.ts.map +0 -1
- package/dist/utils/schema-diff.js +0 -107
- package/dist/utils/schema-diff.js.map +0 -1
- package/dist/utils/source-collector.d.ts +0 -19
- package/dist/utils/source-collector.d.ts.map +0 -1
- package/dist/utils/source-collector.js +0 -565
- package/dist/utils/source-collector.js.map +0 -1
- package/dist/utils/theme-builder.d.ts +0 -12
- package/dist/utils/theme-builder.d.ts.map +0 -1
- package/dist/utils/theme-builder.js +0 -117
- package/dist/utils/theme-builder.js.map +0 -1
- package/dist/utils/type-generator.d.ts +0 -9
- package/dist/utils/type-generator.d.ts.map +0 -1
- package/dist/utils/type-generator.js +0 -86
- package/dist/utils/type-generator.js.map +0 -1
- package/dist/utils/version.d.ts +0 -10
- package/dist/utils/version.d.ts.map +0 -1
- package/dist/utils/version.js +0 -30
- package/dist/utils/version.js.map +0 -1
- package/dist/utils/versions.d.ts.map +0 -1
- package/dist/utils/versions.js.map +0 -1
- package/src/dev-app/app/api/blocks/[name]/config/route.ts +0 -140
- package/src/dev-app/app/api/blocks/route.ts +0 -61
- package/src/dev-app/app/api/config/route.ts +0 -98
- package/src/dev-app/app/api/context/route.ts +0 -126
- package/src/dev-app/app/api/preview/[name]/route.ts +0 -137
- package/src/dev-app/app/api/workspaces/route.ts +0 -124
- package/src/dev-app/app/layout.tsx +0 -18
- package/src/dev-app/app/page.tsx +0 -1785
- package/test.d.ts +0 -1
- package/test.js +0 -2
package/dist/cli.js
CHANGED
|
@@ -1,340 +1,718 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
// src/utils/args.ts
|
|
5
|
+
function parseArgs(argv) {
|
|
6
|
+
const positionals = [];
|
|
7
|
+
const flags = {};
|
|
8
|
+
for (let i = 0; i < argv.length; i++) {
|
|
9
|
+
const arg = argv[i];
|
|
10
|
+
if (arg.startsWith("--")) {
|
|
11
|
+
const key = arg.slice(2);
|
|
12
|
+
const eq = key.indexOf("=");
|
|
13
|
+
if (eq !== -1) {
|
|
14
|
+
flags[key.slice(0, eq)] = key.slice(eq + 1);
|
|
15
|
+
} else {
|
|
16
|
+
const next = argv[i + 1];
|
|
17
|
+
if (next !== void 0 && !next.startsWith("-")) {
|
|
18
|
+
flags[key] = next;
|
|
19
|
+
i++;
|
|
20
|
+
} else {
|
|
21
|
+
flags[key] = true;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
} else if (arg.startsWith("-") && arg.length > 1) {
|
|
25
|
+
flags[arg.slice(1)] = true;
|
|
26
|
+
} else {
|
|
27
|
+
positionals.push(arg);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return { positionals, flags };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// src/utils/ui.ts
|
|
34
|
+
import { existsSync, readFileSync } from "fs";
|
|
22
35
|
import { dirname, join } from "path";
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
import { fileURLToPath } from "url";
|
|
37
|
+
import pc from "picocolors";
|
|
38
|
+
function getVersion() {
|
|
39
|
+
try {
|
|
40
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
41
|
+
for (let i = 0; i < 6; i++) {
|
|
42
|
+
const p = join(dir, "package.json");
|
|
43
|
+
if (existsSync(p)) {
|
|
44
|
+
const pkg = JSON.parse(readFileSync(p, "utf8"));
|
|
45
|
+
return pkg.version ?? "0.0.0";
|
|
46
|
+
}
|
|
47
|
+
dir = dirname(dir);
|
|
48
|
+
}
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
return "0.0.0";
|
|
52
|
+
}
|
|
53
|
+
var ui = {
|
|
54
|
+
info: (msg) => console.log(msg),
|
|
55
|
+
dim: (msg) => console.log(pc.dim(msg)),
|
|
56
|
+
success: (msg) => console.log(pc.green(msg)),
|
|
57
|
+
warn: (msg) => console.log(pc.yellow(msg)),
|
|
58
|
+
error: (msg) => console.error(pc.red(msg))
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// src/commands/init.ts
|
|
62
|
+
import { join as join7, resolve } from "path";
|
|
63
|
+
import { intro, log as log2, note, outro } from "@clack/prompts";
|
|
64
|
+
|
|
65
|
+
// src/utils/constants.ts
|
|
66
|
+
var CMSSY_DEPS = {
|
|
67
|
+
"@cmssy/next": "^0.5.6",
|
|
68
|
+
"@cmssy/react": "^0.5.6"
|
|
69
|
+
};
|
|
70
|
+
var DOCS_URL = "https://www.cmssy.com/docs";
|
|
71
|
+
var HEADLESS_SETTINGS_HINT = "cmssy dashboard -> Settings -> Headless";
|
|
72
|
+
|
|
73
|
+
// src/utils/overlay.ts
|
|
74
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
75
|
+
import { join as join3 } from "path";
|
|
76
|
+
|
|
77
|
+
// src/utils/files.ts
|
|
78
|
+
import { existsSync as existsSync2 } from "fs";
|
|
79
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
80
|
+
import { dirname as dirname2 } from "path";
|
|
81
|
+
function pathExists(p) {
|
|
82
|
+
return existsSync2(p);
|
|
83
|
+
}
|
|
84
|
+
async function ensureDir(dir) {
|
|
85
|
+
await mkdir(dir, { recursive: true });
|
|
86
|
+
}
|
|
87
|
+
async function writeFileSafe(dest, content, opts = {}) {
|
|
88
|
+
if (pathExists(dest)) {
|
|
89
|
+
const current = await readFile(dest, "utf8");
|
|
90
|
+
if (current === content) return "unchanged";
|
|
91
|
+
if (!opts.force) return "skipped";
|
|
92
|
+
}
|
|
93
|
+
await ensureDir(dirname2(dest));
|
|
94
|
+
await writeFile(dest, content, "utf8");
|
|
95
|
+
return "written";
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/utils/templates.ts
|
|
99
|
+
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync2, statSync } from "fs";
|
|
100
|
+
import { dirname as dirname3, join as join2, relative } from "path";
|
|
101
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
102
|
+
function templatesRoot() {
|
|
103
|
+
let dir = dirname3(fileURLToPath2(import.meta.url));
|
|
104
|
+
for (let i = 0; i < 6; i++) {
|
|
105
|
+
const candidate = join2(dir, "templates");
|
|
106
|
+
if (existsSync3(join2(candidate, "init"))) return candidate;
|
|
107
|
+
dir = dirname3(dir);
|
|
108
|
+
}
|
|
109
|
+
throw new Error("cmssy CLI templates not found");
|
|
110
|
+
}
|
|
111
|
+
function readTemplate(...segments) {
|
|
112
|
+
return readFileSync2(join2(templatesRoot(), ...segments), "utf8");
|
|
113
|
+
}
|
|
114
|
+
function renderTemplate(content, vars) {
|
|
115
|
+
return content.replace(
|
|
116
|
+
/\{\{(\w+)\}\}/g,
|
|
117
|
+
(match, key) => key in vars ? vars[key] : match
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
function collectFiles(subtree) {
|
|
121
|
+
const root = join2(templatesRoot(), subtree);
|
|
122
|
+
const out = [];
|
|
123
|
+
const walk = (dir) => {
|
|
124
|
+
for (const entry of readdirSync(dir)) {
|
|
125
|
+
const abs = join2(dir, entry);
|
|
126
|
+
if (statSync(abs).isDirectory()) {
|
|
127
|
+
walk(abs);
|
|
128
|
+
} else {
|
|
129
|
+
out.push({ rel: relative(root, abs).split("\\").join("/"), abs });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
walk(root);
|
|
134
|
+
return out;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// src/utils/overlay.ts
|
|
138
|
+
var ROOT_ONLY = /* @__PURE__ */ new Set([".env.example", "next.config.mjs"]);
|
|
139
|
+
var OTHER_NEXT_CONFIGS = [
|
|
140
|
+
"next.config.js",
|
|
141
|
+
"next.config.ts",
|
|
142
|
+
"next.config.cjs"
|
|
143
|
+
];
|
|
144
|
+
function destFor(rel) {
|
|
145
|
+
return rel === "env.example" ? ".env.example" : rel;
|
|
146
|
+
}
|
|
147
|
+
async function applyOverlay(targetDir, srcDir = false) {
|
|
148
|
+
const report = { written: [], skipped: [], unchanged: [] };
|
|
149
|
+
for (const file of collectFiles("init")) {
|
|
150
|
+
const dest = destFor(file.rel);
|
|
151
|
+
if (dest === "next.config.mjs" && OTHER_NEXT_CONFIGS.some((f) => existsSync4(join3(targetDir, f)))) {
|
|
152
|
+
report.skipped.push(dest);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const content = readFileSync3(file.abs, "utf8");
|
|
156
|
+
const full = srcDir && !ROOT_ONLY.has(dest) ? join3(targetDir, "src", dest) : join3(targetDir, dest);
|
|
157
|
+
const result = await writeFileSafe(full, content);
|
|
158
|
+
report[result].push(dest);
|
|
159
|
+
}
|
|
160
|
+
return report;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// src/utils/project.ts
|
|
164
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync4 } from "fs";
|
|
165
|
+
import { join as join4 } from "path";
|
|
166
|
+
var IGNORED_ENTRIES = /* @__PURE__ */ new Set([".git", ".DS_Store", "Thumbs.db"]);
|
|
167
|
+
function readPackageJson(cwd) {
|
|
168
|
+
const p = join4(cwd, "package.json");
|
|
169
|
+
if (!existsSync5(p)) return null;
|
|
170
|
+
try {
|
|
171
|
+
return JSON.parse(readFileSync4(p, "utf8"));
|
|
172
|
+
} catch {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function hasDep(pkg, name) {
|
|
177
|
+
if (!pkg) return false;
|
|
178
|
+
return Boolean(pkg.dependencies?.[name] ?? pkg.devDependencies?.[name]);
|
|
179
|
+
}
|
|
180
|
+
function detectProject(cwd) {
|
|
181
|
+
const pkg = readPackageJson(cwd);
|
|
182
|
+
const entries = existsSync5(cwd) ? readdirSync2(cwd).filter((e) => !IGNORED_ENTRIES.has(e)) : [];
|
|
183
|
+
const appDir = existsSync5(join4(cwd, "app")) ? join4(cwd, "app") : existsSync5(join4(cwd, "src", "app")) ? join4(cwd, "src", "app") : null;
|
|
184
|
+
const hasNext = hasDep(pkg, "next");
|
|
185
|
+
return {
|
|
186
|
+
cwd,
|
|
187
|
+
hasPackageJson: pkg !== null,
|
|
188
|
+
pkg,
|
|
189
|
+
isEmpty: entries.length === 0,
|
|
190
|
+
hasNext,
|
|
191
|
+
appDir,
|
|
192
|
+
isNextAppRouter: hasNext && appDir !== null
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// src/utils/pkg.ts
|
|
197
|
+
import { spawn } from "child_process";
|
|
198
|
+
import { existsSync as existsSync6 } from "fs";
|
|
199
|
+
import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
200
|
+
import { join as join5 } from "path";
|
|
201
|
+
function detectPackageManager(cwd) {
|
|
202
|
+
if (existsSync6(join5(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
203
|
+
if (existsSync6(join5(cwd, "yarn.lock"))) return "yarn";
|
|
204
|
+
if (existsSync6(join5(cwd, "bun.lockb")) || existsSync6(join5(cwd, "bun.lock"))) {
|
|
205
|
+
return "bun";
|
|
206
|
+
}
|
|
207
|
+
return "npm";
|
|
208
|
+
}
|
|
209
|
+
async function ensureDependencies(cwd, deps) {
|
|
210
|
+
const p = join5(cwd, "package.json");
|
|
211
|
+
const pkg = JSON.parse(await readFile2(p, "utf8"));
|
|
212
|
+
pkg.dependencies ??= {};
|
|
213
|
+
const added = [];
|
|
214
|
+
for (const [name, version] of Object.entries(deps)) {
|
|
215
|
+
if (!pkg.dependencies[name] && !pkg.devDependencies?.[name]) {
|
|
216
|
+
pkg.dependencies[name] = version;
|
|
217
|
+
added.push(name);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (added.length) {
|
|
221
|
+
await writeFile2(p, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
222
|
+
}
|
|
223
|
+
return added;
|
|
224
|
+
}
|
|
225
|
+
function run(command, args, cwd) {
|
|
226
|
+
return new Promise((resolve2, reject) => {
|
|
227
|
+
const child = spawn(command, args, { cwd, stdio: "inherit", shell: false });
|
|
228
|
+
child.on("error", reject);
|
|
229
|
+
child.on("close", (code) => {
|
|
230
|
+
if (code === 0) resolve2();
|
|
231
|
+
else
|
|
232
|
+
reject(new Error(`${command} ${args.join(" ")} exited with ${code}`));
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
function installArgs(pm) {
|
|
237
|
+
return pm === "yarn" ? [] : ["install"];
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// src/commands/link.ts
|
|
241
|
+
import { join as join6 } from "path";
|
|
242
|
+
import { cancel, isCancel, log, password, spinner, text } from "@clack/prompts";
|
|
243
|
+
|
|
244
|
+
// src/utils/delivery.ts
|
|
245
|
+
var DEFAULT_API_URL = "https://api.cmssy.io/graphql";
|
|
246
|
+
var SITE_CONFIG_QUERY = `query PublicSiteConfig($workspaceSlug: String!) {
|
|
247
|
+
publicSiteConfig(workspaceSlug: $workspaceSlug) {
|
|
248
|
+
workspaceId
|
|
249
|
+
siteName
|
|
250
|
+
defaultLanguage
|
|
251
|
+
enabledLanguages
|
|
252
|
+
}
|
|
253
|
+
}`;
|
|
254
|
+
function pickName(value, defaultLanguage) {
|
|
255
|
+
if (typeof value === "string") return value;
|
|
256
|
+
if (value && typeof value === "object") {
|
|
257
|
+
return value[defaultLanguage ?? "en"] ?? Object.values(value)[0] ?? null;
|
|
258
|
+
}
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
async function resolveWorkspace(slug, apiUrl = DEFAULT_API_URL) {
|
|
262
|
+
try {
|
|
263
|
+
const res = await fetch(apiUrl, {
|
|
264
|
+
method: "POST",
|
|
265
|
+
headers: { "content-type": "application/json" },
|
|
266
|
+
body: JSON.stringify({
|
|
267
|
+
query: SITE_CONFIG_QUERY,
|
|
268
|
+
variables: { workspaceSlug: slug }
|
|
269
|
+
})
|
|
270
|
+
});
|
|
271
|
+
if (!res.ok) {
|
|
272
|
+
return {
|
|
273
|
+
status: "error",
|
|
274
|
+
message: `delivery API returned ${res.status}`
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
const json = await res.json();
|
|
278
|
+
const config = json.data?.publicSiteConfig;
|
|
279
|
+
if (!config) return { status: "not-found" };
|
|
280
|
+
return {
|
|
281
|
+
status: "found",
|
|
282
|
+
siteName: pickName(config.siteName, config.defaultLanguage)
|
|
283
|
+
};
|
|
284
|
+
} catch (err) {
|
|
285
|
+
return {
|
|
286
|
+
status: "error",
|
|
287
|
+
message: err instanceof Error ? err.message : String(err)
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// src/utils/env.ts
|
|
293
|
+
import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
294
|
+
var KEY_LINE = /^([A-Z0-9_]+)=(.*)$/;
|
|
295
|
+
async function setEnvVars(filePath, vars, opts = {}) {
|
|
296
|
+
const existing = pathExists(filePath) ? await readFile3(filePath, "utf8") : "";
|
|
297
|
+
const lines = existing.length ? existing.replace(/\n$/, "").split("\n") : [];
|
|
298
|
+
const remaining = new Set(Object.keys(vars));
|
|
299
|
+
const next = lines.map((line) => {
|
|
300
|
+
const m = KEY_LINE.exec(line);
|
|
301
|
+
if (!m) return line;
|
|
302
|
+
const key = m[1];
|
|
303
|
+
if (!(key in vars)) return line;
|
|
304
|
+
remaining.delete(key);
|
|
305
|
+
const raw = m[2];
|
|
306
|
+
const comment = /\s+#.*$/.exec(raw);
|
|
307
|
+
const value = comment ? raw.slice(0, comment.index) : raw;
|
|
308
|
+
if (value.trim().length > 0 && !opts.overwrite) return line;
|
|
309
|
+
return `${key}=${vars[key]}${comment ? comment[0] : ""}`;
|
|
310
|
+
});
|
|
311
|
+
for (const key of remaining) {
|
|
312
|
+
next.push(`${key}=${vars[key]}`);
|
|
313
|
+
}
|
|
314
|
+
await writeFile3(filePath, next.join("\n") + "\n", "utf8");
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// src/commands/link.ts
|
|
318
|
+
function flagString(value) {
|
|
319
|
+
return typeof value === "string" ? value : void 0;
|
|
320
|
+
}
|
|
321
|
+
function bail() {
|
|
322
|
+
cancel("Cancelled.");
|
|
323
|
+
process.exit(0);
|
|
324
|
+
}
|
|
325
|
+
async function runLink(cwd, flags) {
|
|
326
|
+
let slug = flagString(flags.slug);
|
|
327
|
+
if (!slug) {
|
|
328
|
+
const answer = await text({
|
|
329
|
+
message: "Workspace slug",
|
|
330
|
+
placeholder: "my-workspace",
|
|
331
|
+
validate: (v) => v?.trim() ? void 0 : "Required"
|
|
332
|
+
});
|
|
333
|
+
if (isCancel(answer)) bail();
|
|
334
|
+
slug = answer.trim();
|
|
335
|
+
}
|
|
336
|
+
let secret = flagString(flags.secret);
|
|
337
|
+
if (!secret) {
|
|
338
|
+
const answer = await password({
|
|
339
|
+
message: `Draft secret (${HEADLESS_SETTINGS_HINT})`,
|
|
340
|
+
validate: (v) => v?.trim() ? void 0 : "Required"
|
|
341
|
+
});
|
|
342
|
+
if (isCancel(answer)) bail();
|
|
343
|
+
secret = answer.trim();
|
|
344
|
+
}
|
|
345
|
+
const apiUrl = flagString(flags["api-url"]);
|
|
346
|
+
const s = spinner();
|
|
347
|
+
s.start("Checking workspace");
|
|
348
|
+
const lookup = await resolveWorkspace(slug, apiUrl);
|
|
349
|
+
if (lookup.status === "found") {
|
|
350
|
+
s.stop(`Workspace found${lookup.siteName ? `: ${lookup.siteName}` : ""}`);
|
|
351
|
+
} else if (lookup.status === "not-found") {
|
|
352
|
+
s.stop(pc.yellow(`No published workspace "${slug}" yet - saving anyway`));
|
|
353
|
+
} else {
|
|
354
|
+
s.stop(pc.yellow(`Could not verify workspace (${lookup.message})`));
|
|
355
|
+
}
|
|
356
|
+
await setEnvVars(
|
|
357
|
+
join6(cwd, ".env"),
|
|
358
|
+
{ CMSSY_WORKSPACE_SLUG: slug, CMSSY_DRAFT_SECRET: secret },
|
|
359
|
+
{ overwrite: true }
|
|
360
|
+
);
|
|
361
|
+
await setEnvVars(
|
|
362
|
+
join6(cwd, ".env.example"),
|
|
363
|
+
{ CMSSY_WORKSPACE_SLUG: "", CMSSY_DRAFT_SECRET: "" },
|
|
364
|
+
{ overwrite: false }
|
|
365
|
+
);
|
|
366
|
+
log.success("Wrote .env");
|
|
367
|
+
}
|
|
368
|
+
async function linkCommand(args) {
|
|
369
|
+
const cwd = process.cwd();
|
|
370
|
+
const hasConfig = pathExists(join6(cwd, "cmssy.config.ts")) || pathExists(join6(cwd, "src", "cmssy.config.ts"));
|
|
371
|
+
if (!hasConfig) {
|
|
372
|
+
log.warn("No cmssy.config.ts here - run `cmssy init` first.");
|
|
373
|
+
}
|
|
374
|
+
const { intro: intro4, outro: outro4 } = await import("@clack/prompts");
|
|
375
|
+
intro4(pc.bold("cmssy link"));
|
|
376
|
+
await runLink(cwd, args.flags);
|
|
377
|
+
outro4("Linked.");
|
|
378
|
+
}
|
|
39
379
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
380
|
+
// src/commands/init.ts
|
|
381
|
+
var PMS = /* @__PURE__ */ new Set(["npm", "pnpm", "yarn", "bun"]);
|
|
382
|
+
function choosePm(flags, targetDir) {
|
|
383
|
+
const flag = typeof flags.pm === "string" ? flags.pm : void 0;
|
|
384
|
+
if (flag && PMS.has(flag)) return flag;
|
|
385
|
+
return detectPackageManager(targetDir);
|
|
386
|
+
}
|
|
387
|
+
async function initCommand(args) {
|
|
388
|
+
const cwd = process.cwd();
|
|
389
|
+
const dirArg = args.positionals[0];
|
|
390
|
+
const targetDir = dirArg ? resolve(cwd, dirArg) : cwd;
|
|
391
|
+
const { flags } = args;
|
|
392
|
+
intro(pc.bold("cmssy init"));
|
|
393
|
+
const info = detectProject(targetDir);
|
|
394
|
+
if (!info.isNextAppRouter) {
|
|
395
|
+
log2.error("No Next.js App Router project found here.");
|
|
396
|
+
ui.dim(
|
|
397
|
+
"cmssy init wires an existing Next.js App Router app. Create one first:"
|
|
398
|
+
);
|
|
399
|
+
ui.dim(" npx create-next-app@latest");
|
|
400
|
+
ui.dim("then run cmssy init inside it.");
|
|
401
|
+
process.exitCode = 1;
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
log2.info("Adding cmssy wiring to your Next.js app.");
|
|
405
|
+
const pm = choosePm(flags, targetDir);
|
|
406
|
+
const srcDir = info.appDir === join7(targetDir, "src", "app");
|
|
407
|
+
const report = await applyOverlay(targetDir, srcDir);
|
|
408
|
+
if (report.written.length) {
|
|
409
|
+
log2.success(`Added ${report.written.length} file(s)`);
|
|
410
|
+
}
|
|
411
|
+
if (report.skipped.length) {
|
|
412
|
+
log2.warn(`Skipped existing: ${report.skipped.join(", ")}`);
|
|
413
|
+
}
|
|
414
|
+
const added = await ensureDependencies(targetDir, CMSSY_DEPS);
|
|
415
|
+
if (added.length) log2.success(`Added deps: ${added.join(", ")}`);
|
|
416
|
+
if (!flags["no-link"]) {
|
|
417
|
+
await runLink(targetDir, flags);
|
|
418
|
+
}
|
|
419
|
+
const skipInstall = Boolean(flags["skip-install"]);
|
|
420
|
+
if (!skipInstall) {
|
|
421
|
+
log2.step(`Installing dependencies (${pm})`);
|
|
422
|
+
await run(pm, installArgs(pm), targetDir);
|
|
423
|
+
}
|
|
424
|
+
const steps = [
|
|
425
|
+
...dirArg ? [`cd ${dirArg}`] : [],
|
|
426
|
+
...skipInstall ? [`${pm} install`] : [],
|
|
427
|
+
`${pm === "npm" ? "npm run" : pm} dev`,
|
|
428
|
+
"Open the site in the cmssy editor to edit visually."
|
|
429
|
+
];
|
|
430
|
+
if (report.skipped.includes("next.config.mjs")) {
|
|
431
|
+
steps.push(
|
|
432
|
+
pc.yellow(
|
|
433
|
+
"Add images.remotePatterns for assets.cmssy.io to your next.config so cmssy media renders."
|
|
434
|
+
)
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
note(steps.join("\n"), "Next steps");
|
|
438
|
+
outro(`Done. Docs: ${DOCS_URL}`);
|
|
439
|
+
}
|
|
48
440
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
.command("init")
|
|
54
|
-
.description("Initialize a new Cmssy project or add Cmssy to existing one")
|
|
55
|
-
.argument("[name]", "Project name (creates new directory with create-next-app)")
|
|
56
|
-
.option("-y, --yes", "Skip prompts and use defaults")
|
|
57
|
-
.addHelpText("after", `
|
|
58
|
-
Examples:
|
|
59
|
-
$ cmssy init my-blocks New project (runs create-next-app)
|
|
60
|
-
$ cmssy init -y my-blocks New project with defaults (no prompts)
|
|
61
|
-
$ cmssy init Add Cmssy to existing Next.js project
|
|
62
|
-
`)
|
|
63
|
-
.action((name, options) => initCommand(name, options));
|
|
64
|
-
// cmssy create
|
|
65
|
-
const create = program
|
|
66
|
-
.command("create")
|
|
67
|
-
.description("Create a new block or template")
|
|
68
|
-
.addHelpText("after", `
|
|
69
|
-
Examples:
|
|
70
|
-
$ cmssy create block hero
|
|
71
|
-
$ cmssy create block pricing -c marketing -t "pricing,plans"
|
|
72
|
-
$ cmssy create template landing-page
|
|
73
|
-
`);
|
|
74
|
-
create
|
|
75
|
-
.command("block")
|
|
76
|
-
.description("Create a new block with scaffold files")
|
|
77
|
-
.argument("<name>", "Block name (kebab-case recommended)")
|
|
78
|
-
.option("-y, --yes", "Skip prompts and use defaults")
|
|
79
|
-
.option("-d, --description <description>", "Block description")
|
|
80
|
-
.option("-c, --category <category>", "Category: marketing, typography, media, layout, forms, navigation, other")
|
|
81
|
-
.option("-t, --tags <tags>", "Comma-separated tags")
|
|
82
|
-
.addHelpText("after", `
|
|
83
|
-
Creates:
|
|
84
|
-
blocks/<name>/
|
|
85
|
-
├── config.ts Type-safe configuration
|
|
86
|
-
├── package.json Name and version
|
|
87
|
-
├── preview.json Preview data for dev server
|
|
88
|
-
└── src/
|
|
89
|
-
├── index.tsx Entry point (mount/unmount)
|
|
90
|
-
├── Block.tsx React component
|
|
91
|
-
├── block.d.ts Auto-generated types
|
|
92
|
-
└── index.css Styles
|
|
93
|
-
`)
|
|
94
|
-
.action(createCommand.block);
|
|
95
|
-
create
|
|
96
|
-
.command("template")
|
|
97
|
-
.description("Create a new page template")
|
|
98
|
-
.argument("<name>", "Template name (kebab-case recommended)")
|
|
99
|
-
.option("-y, --yes", "Skip prompts and use defaults")
|
|
100
|
-
.option("-d, --description <description>", "Template description")
|
|
101
|
-
.action(createCommand.page);
|
|
102
|
-
// cmssy build
|
|
103
|
-
program
|
|
104
|
-
.command("build")
|
|
105
|
-
.description("Build blocks and templates for production")
|
|
106
|
-
.option("--block <names...>", "Build only specific blocks/templates")
|
|
107
|
-
.option("--framework <framework>", "Override framework from config")
|
|
108
|
-
.addHelpText("after", `
|
|
109
|
-
Examples:
|
|
110
|
-
$ cmssy build Build all blocks and templates
|
|
111
|
-
$ cmssy build --block hero pricing Build only hero and pricing blocks
|
|
441
|
+
// src/commands/add-block.ts
|
|
442
|
+
import { existsSync as existsSync7 } from "fs";
|
|
443
|
+
import { join as join8 } from "path";
|
|
444
|
+
import { cancel as cancel2, intro as intro2, isCancel as isCancel2, log as log3, outro as outro2, text as text2 } from "@clack/prompts";
|
|
112
445
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
446
|
+
// src/utils/names.ts
|
|
447
|
+
function words(input) {
|
|
448
|
+
return input.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[\s_-]+/).map((w) => w.replace(/[^a-zA-Z0-9]/g, "").toLowerCase()).filter(Boolean);
|
|
449
|
+
}
|
|
450
|
+
function blockNames(input) {
|
|
451
|
+
const parts = words(input);
|
|
452
|
+
if (parts.length === 0) {
|
|
453
|
+
throw new Error("Block name must contain at least one letter or digit.");
|
|
454
|
+
}
|
|
455
|
+
if (!/^[a-z]/.test(parts[0])) {
|
|
456
|
+
throw new Error(
|
|
457
|
+
"Block name must start with a letter (it becomes a TypeScript identifier)."
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
const type = parts.join("-");
|
|
461
|
+
const Pascal = parts.map((w) => w[0].toUpperCase() + w.slice(1)).join("");
|
|
462
|
+
const camel = Pascal[0].toLowerCase() + Pascal.slice(1);
|
|
463
|
+
const Label = parts.map((w) => w[0].toUpperCase() + w.slice(1)).join(" ");
|
|
464
|
+
return { type, camel, Pascal, Label };
|
|
465
|
+
}
|
|
129
466
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
467
|
+
// src/utils/registry.ts
|
|
468
|
+
import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
469
|
+
async function registerBlock(blocksFile, camel, type) {
|
|
470
|
+
let content = await readFile4(blocksFile, "utf8");
|
|
471
|
+
const token = `${camel}Block`;
|
|
472
|
+
const tokenRe = new RegExp(`\\b${token}\\b`);
|
|
473
|
+
const arrayRe = /export const blocks\s*=\s*\[([\s\S]*?)\]/;
|
|
474
|
+
const arrayMatch = arrayRe.exec(content);
|
|
475
|
+
if (!arrayMatch) {
|
|
476
|
+
throw new Error(
|
|
477
|
+
`Could not find \`export const blocks = [...]\` in ${blocksFile} - add ${token} manually.`
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
if (tokenRe.test(arrayMatch[1])) return false;
|
|
481
|
+
const lines = content.split("\n");
|
|
482
|
+
const alreadyImported = lines.some(
|
|
483
|
+
(line) => line.startsWith("import ") && tokenRe.test(line)
|
|
484
|
+
);
|
|
485
|
+
if (!alreadyImported) {
|
|
486
|
+
const importLine = `import { ${token} } from "@/blocks/${type}/block";`;
|
|
487
|
+
let lastImport = -1;
|
|
488
|
+
lines.forEach((line, i) => {
|
|
489
|
+
if (line.startsWith("import ")) lastImport = i;
|
|
490
|
+
});
|
|
491
|
+
lines.splice(lastImport + 1, 0, importLine);
|
|
492
|
+
content = lines.join("\n");
|
|
493
|
+
}
|
|
494
|
+
content = content.replace(arrayRe, (_match, inner) => {
|
|
495
|
+
const body = inner.replace(/\s+$/, "").replace(/,\s*$/, "");
|
|
496
|
+
const next = body.trim() ? `${body}, ${token}` : token;
|
|
497
|
+
return `export const blocks = [${next}]`;
|
|
498
|
+
});
|
|
499
|
+
await writeFile4(blocksFile, content, "utf8");
|
|
500
|
+
return true;
|
|
501
|
+
}
|
|
149
502
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
.
|
|
158
|
-
.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
503
|
+
// src/commands/add-block.ts
|
|
504
|
+
async function addBlockCommand(args) {
|
|
505
|
+
const cwd = process.cwd();
|
|
506
|
+
const base = existsSync7(join8(cwd, "src", "cmssy", "blocks.ts")) ? join8(cwd, "src") : cwd;
|
|
507
|
+
const blocksFile = join8(base, "cmssy", "blocks.ts");
|
|
508
|
+
intro2(pc.bold("cmssy add block"));
|
|
509
|
+
if (!existsSync7(blocksFile)) {
|
|
510
|
+
log3.error("No cmssy/blocks.ts found - run `cmssy init` first.");
|
|
511
|
+
process.exitCode = 1;
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
let input = args.positionals[0];
|
|
515
|
+
if (!input) {
|
|
516
|
+
const answer = await text2({
|
|
517
|
+
message: "Block name",
|
|
518
|
+
placeholder: "feature-grid",
|
|
519
|
+
validate: (v) => v?.trim() ? void 0 : "Required"
|
|
520
|
+
});
|
|
521
|
+
if (isCancel2(answer)) {
|
|
522
|
+
cancel2("Cancelled.");
|
|
523
|
+
process.exit(0);
|
|
524
|
+
}
|
|
525
|
+
input = answer.trim();
|
|
526
|
+
}
|
|
527
|
+
const names = blockNames(input);
|
|
528
|
+
const vars = {
|
|
529
|
+
type: names.type,
|
|
530
|
+
camel: names.camel,
|
|
531
|
+
Pascal: names.Pascal,
|
|
532
|
+
Label: names.Label
|
|
533
|
+
};
|
|
534
|
+
const dir = join8(base, "blocks", names.type);
|
|
535
|
+
const blockTs = renderTemplate(readTemplate("block", "block.ts.tpl"), vars);
|
|
536
|
+
const componentTsx = renderTemplate(
|
|
537
|
+
readTemplate("block", "Component.tsx.tpl"),
|
|
538
|
+
vars
|
|
539
|
+
);
|
|
540
|
+
const componentCss = renderTemplate(
|
|
541
|
+
readTemplate("block", "Component.module.css.tpl"),
|
|
542
|
+
vars
|
|
543
|
+
);
|
|
544
|
+
const results = [
|
|
545
|
+
await writeFileSafe(join8(dir, "block.ts"), blockTs),
|
|
546
|
+
await writeFileSafe(join8(dir, `${names.Pascal}.tsx`), componentTsx),
|
|
547
|
+
await writeFileSafe(join8(dir, `${names.Pascal}.module.css`), componentCss)
|
|
548
|
+
];
|
|
549
|
+
if (results.includes("skipped")) {
|
|
550
|
+
log3.warn(
|
|
551
|
+
`Block "${names.type}" already exists - left existing files alone.`
|
|
552
|
+
);
|
|
553
|
+
} else if (results.includes("written")) {
|
|
554
|
+
log3.success(
|
|
555
|
+
`Created blocks/${names.type}/ (block.ts + ${names.Pascal}.tsx + ${names.Pascal}.module.css)`
|
|
556
|
+
);
|
|
557
|
+
} else {
|
|
558
|
+
log3.info(`Block "${names.type}" already up to date.`);
|
|
559
|
+
}
|
|
560
|
+
const registered = await registerBlock(blocksFile, names.camel, names.type);
|
|
561
|
+
log3.info(
|
|
562
|
+
registered ? `Registered ${names.camel}Block in cmssy/blocks.ts` : `${names.camel}Block already registered`
|
|
563
|
+
);
|
|
564
|
+
outro2(
|
|
565
|
+
`Added "${names.type}". Edit its component, then use it in the editor.`
|
|
566
|
+
);
|
|
567
|
+
}
|
|
165
568
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
.
|
|
182
|
-
.
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
.
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
569
|
+
// src/commands/doctor.ts
|
|
570
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5 } from "fs";
|
|
571
|
+
import { join as join9 } from "path";
|
|
572
|
+
import { intro as intro3, outro as outro3 } from "@clack/prompts";
|
|
573
|
+
var MARK = {
|
|
574
|
+
pass: pc.green("\u2713"),
|
|
575
|
+
warn: pc.yellow("!"),
|
|
576
|
+
fail: pc.red("\u2717")
|
|
577
|
+
};
|
|
578
|
+
async function doctorCommand(_args) {
|
|
579
|
+
const cwd = process.cwd();
|
|
580
|
+
const base = existsSync8(join9(cwd, "src", "cmssy", "blocks.ts")) ? join9(cwd, "src") : cwd;
|
|
581
|
+
intro3(pc.bold("cmssy doctor"));
|
|
582
|
+
const checks = [];
|
|
583
|
+
const required = [
|
|
584
|
+
["cmssy.config.ts", join9(cwd, "cmssy.config.ts")],
|
|
585
|
+
["proxy.ts", join9(cwd, "proxy.ts")],
|
|
586
|
+
["app/[[...path]]/page.tsx", join9(base, "app", "[[...path]]", "page.tsx")],
|
|
587
|
+
["app/api/draft/route.ts", join9(base, "app", "api", "draft", "route.ts")],
|
|
588
|
+
["cmssy/blocks.ts", join9(base, "cmssy", "blocks.ts")],
|
|
589
|
+
["cmssy/editor.tsx", join9(base, "cmssy", "editor.tsx")]
|
|
590
|
+
];
|
|
591
|
+
for (const [label, p] of required) {
|
|
592
|
+
checks.push(
|
|
593
|
+
existsSync8(p) ? { label, status: "pass" } : { label, status: "fail", hint: "missing - run `cmssy init`" }
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
const pkg = readPackageJson(cwd);
|
|
597
|
+
const next = pkg?.dependencies?.["@cmssy/next"] ?? pkg?.devDependencies?.["@cmssy/next"];
|
|
598
|
+
const react = pkg?.dependencies?.["@cmssy/react"] ?? pkg?.devDependencies?.["@cmssy/react"];
|
|
599
|
+
if (!next || !react) {
|
|
600
|
+
checks.push({
|
|
601
|
+
label: "@cmssy/next + @cmssy/react installed",
|
|
602
|
+
status: "fail",
|
|
603
|
+
hint: "missing - add @cmssy/next and @cmssy/react"
|
|
604
|
+
});
|
|
605
|
+
} else if (next !== react) {
|
|
606
|
+
checks.push({
|
|
607
|
+
label: "@cmssy/* versions aligned",
|
|
608
|
+
status: "warn",
|
|
609
|
+
hint: `@cmssy/next ${next} vs @cmssy/react ${react}`
|
|
610
|
+
});
|
|
611
|
+
} else {
|
|
612
|
+
checks.push({ label: `@cmssy/* ${next}`, status: "pass" });
|
|
613
|
+
}
|
|
614
|
+
const envPath = join9(cwd, ".env");
|
|
615
|
+
const env = existsSync8(envPath) ? readFileSync5(envPath, "utf8") : "";
|
|
616
|
+
for (const key of ["CMSSY_WORKSPACE_SLUG", "CMSSY_DRAFT_SECRET"]) {
|
|
617
|
+
const set = new RegExp(`^${key}=.+$`, "m").test(env);
|
|
618
|
+
checks.push(
|
|
619
|
+
set ? { label: key, status: "pass" } : {
|
|
620
|
+
label: key,
|
|
621
|
+
status: "warn",
|
|
622
|
+
hint: "not set in .env - run `cmssy link`"
|
|
623
|
+
}
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
const blocksFile = join9(base, "cmssy", "blocks.ts");
|
|
627
|
+
if (existsSync8(blocksFile)) {
|
|
628
|
+
const content = readFileSync5(blocksFile, "utf8");
|
|
629
|
+
const refs = [...content.matchAll(/@\/blocks\/([^/]+)\/block/g)].map(
|
|
630
|
+
(m) => m[1]
|
|
631
|
+
);
|
|
632
|
+
const missing = refs.filter(
|
|
633
|
+
(name) => !existsSync8(join9(base, "blocks", name, "block.ts"))
|
|
634
|
+
);
|
|
635
|
+
checks.push(
|
|
636
|
+
missing.length === 0 ? { label: `block registry (${refs.length} block(s))`, status: "pass" } : {
|
|
637
|
+
label: "block registry imports resolve",
|
|
638
|
+
status: "fail",
|
|
639
|
+
hint: `missing block file(s): ${missing.join(", ")}`
|
|
640
|
+
}
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
for (const c of checks) {
|
|
644
|
+
console.log(
|
|
645
|
+
` ${MARK[c.status]} ${c.label}${c.hint ? pc.dim(` - ${c.hint}`) : ""}`
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
const failed = checks.filter((c) => c.status === "fail").length;
|
|
649
|
+
const warned = checks.filter((c) => c.status === "warn").length;
|
|
650
|
+
if (failed) {
|
|
651
|
+
process.exitCode = 1;
|
|
652
|
+
outro3(pc.red(`${failed} problem(s), ${warned} warning(s).`));
|
|
653
|
+
} else {
|
|
654
|
+
outro3(
|
|
655
|
+
warned ? pc.yellow(`OK with ${warned} warning(s).`) : pc.green("All good.")
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
206
659
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
.action((name, options) => publishBlockBuildtimeCommand(name, options));
|
|
211
|
-
// cmssy publish-template
|
|
212
|
-
program
|
|
213
|
-
.command("publish-template")
|
|
214
|
-
.description("Publish a single template (page tree + content) to a workspace.\n\n" +
|
|
215
|
-
" Templates are declarative - no sandbox build. Reads config.ts +\n" +
|
|
216
|
-
" pages.json and uploads via GraphQL. The backend revalidates the\n" +
|
|
217
|
-
" public-site cache so changes appear immediately.")
|
|
218
|
-
.argument("<name>", "Template directory name under templates/")
|
|
219
|
-
.option("-w, --workspace [id]", "Workspace id (defaults to .env)")
|
|
220
|
-
.option("--patch", "Bump patch version (1.0.0 -> 1.0.1) - default")
|
|
221
|
-
.option("--minor", "Bump minor version (1.0.0 -> 1.1.0)")
|
|
222
|
-
.option("--major", "Bump major version (1.0.0 -> 2.0.0)")
|
|
223
|
-
.option("--no-bump", "Publish without version bump")
|
|
224
|
-
.option("--dry-run", "Print plan without uploading")
|
|
225
|
-
.option("--overwrite-content", "Overwrite existing page content on republish (default: preserve)")
|
|
226
|
-
.addHelpText("after", `
|
|
227
|
-
Examples:
|
|
228
|
-
$ cmssy publish-template marketing-site Publish templates/marketing-site
|
|
229
|
-
$ cmssy publish-template marketing-site --dry-run Show the plan
|
|
230
|
-
$ cmssy publish-template blog -w 65f... --minor Override workspace + minor bump
|
|
660
|
+
// src/cli.ts
|
|
661
|
+
var HELP = `
|
|
662
|
+
${pc.bold("cmssy")} - wire a Next.js app to a headless cmssy workspace
|
|
231
663
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
`)
|
|
235
|
-
.action((name, options) => publishTemplateCommand(name, options));
|
|
236
|
-
const lib = program
|
|
237
|
-
.command("lib")
|
|
238
|
-
.description("Manage the workspace's npm dependency manifest");
|
|
239
|
-
lib
|
|
240
|
-
.command("install")
|
|
241
|
-
.description("Install npm package(s) locally and sync the manifest")
|
|
242
|
-
.argument("<packages...>", "npm package specs (e.g. lodash zod@^4)")
|
|
243
|
-
.option("-w, --workspace [id]", "Workspace id (defaults to .env)")
|
|
244
|
-
.addOption(new Option("--package-manager <pm>", "Force the package manager").choices([
|
|
245
|
-
"npm",
|
|
246
|
-
"pnpm",
|
|
247
|
-
"yarn",
|
|
248
|
-
"bun",
|
|
249
|
-
]))
|
|
250
|
-
.option("--skip-install", "Skip local install; only sync from package.json")
|
|
251
|
-
.option("--dry-run", "Don't push manifest, just print it")
|
|
252
|
-
.action((packages, options) => libInstallCommand(packages, options));
|
|
253
|
-
lib
|
|
254
|
-
.command("sync")
|
|
255
|
-
.description("Push current package.json deps to the workspace manifest")
|
|
256
|
-
.option("-w, --workspace [id]", "Workspace id (defaults to .env)")
|
|
257
|
-
.option("--dry-run", "Don't push manifest, just print it")
|
|
258
|
-
.action((options) => libSyncCommand(options));
|
|
259
|
-
// cmssy sync
|
|
260
|
-
program
|
|
261
|
-
.command("sync")
|
|
262
|
-
.description("Pull blocks from design library to local project")
|
|
263
|
-
.argument("[package]", "Package slug (e.g., @cmssy/blocks.hero)")
|
|
264
|
-
.option("--workspace <id>", "Sync from specific workspace")
|
|
265
|
-
.addHelpText("after", `
|
|
266
|
-
Examples:
|
|
267
|
-
$ cmssy sync @cmssy/blocks.hero Sync from design library
|
|
268
|
-
$ cmssy sync --workspace abc123 Sync all from workspace
|
|
269
|
-
`)
|
|
270
|
-
.action(syncCommand);
|
|
271
|
-
// cmssy migrate (hidden - legacy command, all projects already migrated)
|
|
272
|
-
program.addCommand(new Command("migrate")
|
|
273
|
-
.argument("[block-name]")
|
|
274
|
-
.description("Migrate legacy package.json config to config.ts")
|
|
275
|
-
.addHelpText("after", `
|
|
276
|
-
Examples:
|
|
277
|
-
$ cmssy migrate hero Migrate specific block
|
|
278
|
-
$ cmssy migrate Migrate all blocks/templates
|
|
664
|
+
${pc.bold("Usage")}
|
|
665
|
+
cmssy <command> [options]
|
|
279
666
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
program
|
|
286
|
-
.command("workspaces")
|
|
287
|
-
.description("List your workspaces and their IDs")
|
|
288
|
-
.addHelpText("after", `
|
|
289
|
-
Use workspace IDs with:
|
|
290
|
-
$ cmssy publish-block <name> --workspace <id>
|
|
291
|
-
$ cmssy publish-template <name> --workspace <id>
|
|
292
|
-
$ cmssy sync --workspace <id>
|
|
293
|
-
`)
|
|
294
|
-
.action(workspacesCommand);
|
|
295
|
-
// cmssy skills
|
|
296
|
-
const skills = program
|
|
297
|
-
.command("skills")
|
|
298
|
-
.description("Manage AI coding-assistant skills for cmssy");
|
|
299
|
-
skills
|
|
300
|
-
.command("list")
|
|
301
|
-
.description("List available skills")
|
|
302
|
-
.action(skillsListCommand);
|
|
303
|
-
skills
|
|
304
|
-
.command("install")
|
|
305
|
-
.description("Install a skill into your AI assistant's config directory")
|
|
306
|
-
.argument("[skill]", "Skill name: block, mcp-content (omit for interactive)")
|
|
307
|
-
.option("--target <editor>", "Editor target: claude (default)", "claude")
|
|
308
|
-
.option("--all", "Install every available skill")
|
|
309
|
-
.option("--local", "Install into ./.claude/skills (default: ~/.claude/skills)")
|
|
310
|
-
.option("--force", "Overwrite existing skill without prompting")
|
|
311
|
-
.option("-y, --yes", "Non-interactive mode (fail instead of prompting)")
|
|
312
|
-
.addHelpText("after", `
|
|
313
|
-
Examples:
|
|
314
|
-
$ cmssy skills list Show available skills
|
|
315
|
-
$ cmssy skills install Interactive prompt (pick a skill)
|
|
316
|
-
$ cmssy skills install block CLI + block dev workflow skill
|
|
317
|
-
$ cmssy skills install mcp-content Content editing via @cmssy/mcp-server
|
|
318
|
-
$ cmssy skills install --all Install every skill
|
|
319
|
-
$ cmssy skills install block --local Install into current project's .claude/
|
|
320
|
-
$ cmssy skills install block --force Overwrite existing skill
|
|
667
|
+
${pc.bold("Commands")}
|
|
668
|
+
init Add cmssy wiring to an existing Next.js App Router app
|
|
669
|
+
link Connect an initialized project to a workspace
|
|
670
|
+
add block <name> Scaffold a new block and register it
|
|
671
|
+
doctor Diagnose a cmssy project's setup
|
|
321
672
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
.
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
673
|
+
${pc.bold("Options")}
|
|
674
|
+
-h, --help Show this help
|
|
675
|
+
-v, --version Show version
|
|
676
|
+
`;
|
|
677
|
+
async function main() {
|
|
678
|
+
const { positionals, flags } = parseArgs(process.argv.slice(2));
|
|
679
|
+
const command = positionals[0];
|
|
680
|
+
if (flags.version || flags.v) {
|
|
681
|
+
console.log(getVersion());
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
if (!command || flags.help || flags.h) {
|
|
685
|
+
console.log(HELP);
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
const rest = { positionals: positionals.slice(1), flags };
|
|
689
|
+
switch (command) {
|
|
690
|
+
case "init":
|
|
691
|
+
await initCommand(rest);
|
|
692
|
+
break;
|
|
693
|
+
case "link":
|
|
694
|
+
await linkCommand(rest);
|
|
695
|
+
break;
|
|
696
|
+
case "add":
|
|
697
|
+
if (positionals[1] === "block") {
|
|
698
|
+
await addBlockCommand({ positionals: positionals.slice(2), flags });
|
|
699
|
+
} else {
|
|
700
|
+
ui.error(
|
|
701
|
+
`Unknown add target: ${pc.bold(positionals[1] ?? "")}. Try ${pc.bold("cmssy add block <name>")}.`
|
|
702
|
+
);
|
|
703
|
+
process.exitCode = 1;
|
|
704
|
+
}
|
|
705
|
+
break;
|
|
706
|
+
case "doctor":
|
|
707
|
+
await doctorCommand(rest);
|
|
708
|
+
break;
|
|
709
|
+
default:
|
|
710
|
+
ui.error(`Unknown command: ${pc.bold(command)}`);
|
|
711
|
+
console.log(HELP);
|
|
712
|
+
process.exitCode = 1;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
main().catch((err) => {
|
|
716
|
+
ui.error(err instanceof Error ? err.message : String(err));
|
|
717
|
+
process.exitCode = 1;
|
|
718
|
+
});
|