@cmssy/cli 0.8.0 ā 0.10.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/config.d.ts +2 -2
- package/config.js +1 -1
- package/dist/cli.js +22 -8
- package/dist/cli.js.map +1 -1
- package/dist/commands/codegen.d.ts +7 -0
- package/dist/commands/codegen.d.ts.map +1 -0
- package/dist/commands/codegen.js +133 -0
- package/dist/commands/codegen.js.map +1 -0
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +11 -15
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +5 -2
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/init.d.ts +0 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +350 -367
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +119 -9
- package/dist/commands/publish.js.map +1 -1
- package/dist/utils/block-config.d.ts +65 -2
- package/dist/utils/block-config.d.ts.map +1 -1
- package/dist/utils/block-config.js +17 -5
- package/dist/utils/block-config.js.map +1 -1
- package/dist/utils/dev-generator/api-routes/blocks.d.ts +3 -0
- package/dist/utils/dev-generator/api-routes/blocks.d.ts.map +1 -0
- package/dist/utils/dev-generator/api-routes/blocks.js +194 -0
- package/dist/utils/dev-generator/api-routes/blocks.js.map +1 -0
- package/dist/utils/dev-generator/api-routes/config.d.ts +2 -0
- package/dist/utils/dev-generator/api-routes/config.d.ts.map +1 -0
- package/dist/utils/dev-generator/api-routes/config.js +107 -0
- package/dist/utils/dev-generator/api-routes/config.js.map +1 -0
- package/dist/utils/dev-generator/api-routes/preview.d.ts +2 -0
- package/dist/utils/dev-generator/api-routes/preview.d.ts.map +1 -0
- package/dist/utils/dev-generator/api-routes/preview.js +50 -0
- package/dist/utils/dev-generator/api-routes/preview.js.map +1 -0
- package/dist/utils/dev-generator/api-routes/workspaces.d.ts +2 -0
- package/dist/utils/dev-generator/api-routes/workspaces.d.ts.map +1 -0
- package/dist/utils/dev-generator/api-routes/workspaces.js +128 -0
- package/dist/utils/dev-generator/api-routes/workspaces.js.map +1 -0
- package/dist/utils/dev-generator/helpers.d.ts +23 -0
- package/dist/utils/dev-generator/helpers.d.ts.map +1 -0
- package/dist/utils/dev-generator/helpers.js +112 -0
- package/dist/utils/dev-generator/helpers.js.map +1 -0
- package/dist/utils/dev-generator/home-page.d.ts +2 -0
- package/dist/utils/dev-generator/home-page.d.ts.map +1 -0
- package/dist/utils/dev-generator/home-page.js +643 -0
- package/dist/utils/dev-generator/home-page.js.map +1 -0
- package/dist/utils/dev-generator/index.d.ts +12 -0
- package/dist/utils/dev-generator/index.d.ts.map +1 -0
- package/dist/utils/dev-generator/index.js +53 -0
- package/dist/utils/dev-generator/index.js.map +1 -0
- package/dist/utils/dev-generator/layout.d.ts +3 -0
- package/dist/utils/dev-generator/layout.d.ts.map +1 -0
- package/dist/utils/dev-generator/layout.js +59 -0
- package/dist/utils/dev-generator/layout.js.map +1 -0
- package/dist/utils/dev-generator/next-config.d.ts +3 -0
- package/dist/utils/dev-generator/next-config.d.ts.map +1 -0
- package/dist/utils/dev-generator/next-config.js +104 -0
- package/dist/utils/dev-generator/next-config.js.map +1 -0
- package/dist/utils/dev-generator/preview-pages.d.ts +3 -0
- package/dist/utils/dev-generator/preview-pages.d.ts.map +1 -0
- package/dist/utils/dev-generator/preview-pages.js +827 -0
- package/dist/utils/dev-generator/preview-pages.js.map +1 -0
- package/dist/utils/graphql.d.ts.map +1 -1
- package/dist/utils/graphql.js +2 -0
- package/dist/utils/graphql.js.map +1 -1
- package/package.json +2 -2
package/dist/commands/init.js
CHANGED
|
@@ -6,300 +6,23 @@ import ora from "ora";
|
|
|
6
6
|
import path from "path";
|
|
7
7
|
import { getFieldTypes } from "../utils/field-schema.js";
|
|
8
8
|
import { generateTypes } from "../utils/type-generator.js";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
answers = {
|
|
15
|
-
projectName: name || "my-blocks",
|
|
16
|
-
authorName: "",
|
|
17
|
-
authorEmail: "",
|
|
18
|
-
initGit: true,
|
|
19
|
-
};
|
|
20
|
-
console.log(chalk.gray(`Using defaults: ${answers.projectName}\n`));
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
answers = await inquirer.prompt([
|
|
24
|
-
{
|
|
25
|
-
type: "input",
|
|
26
|
-
name: "projectName",
|
|
27
|
-
message: "Project name:",
|
|
28
|
-
default: name || "my-blocks",
|
|
29
|
-
validate: (input) => {
|
|
30
|
-
if (/^[a-z0-9-_]+$/.test(input))
|
|
31
|
-
return true;
|
|
32
|
-
return "Project name must contain only lowercase letters, numbers, hyphens, and underscores";
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
type: "input",
|
|
37
|
-
name: "authorName",
|
|
38
|
-
message: "Author name:",
|
|
39
|
-
default: "",
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
type: "input",
|
|
43
|
-
name: "authorEmail",
|
|
44
|
-
message: "Author email:",
|
|
45
|
-
default: "",
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
type: "confirm",
|
|
49
|
-
name: "initGit",
|
|
50
|
-
message: "Initialize git repository?",
|
|
51
|
-
default: true,
|
|
52
|
-
},
|
|
53
|
-
]);
|
|
54
|
-
}
|
|
55
|
-
const projectPath = path.join(process.cwd(), answers.projectName);
|
|
56
|
-
if (fs.existsSync(projectPath)) {
|
|
57
|
-
console.error(chalk.red(`\nā Directory "${answers.projectName}" already exists\n`));
|
|
58
|
-
process.exit(1);
|
|
59
|
-
}
|
|
60
|
-
const spinner = ora("Creating project structure...").start();
|
|
61
|
-
try {
|
|
62
|
-
// Create project directory
|
|
63
|
-
fs.mkdirSync(projectPath, { recursive: true });
|
|
64
|
-
// Create directory structure
|
|
65
|
-
const dirs = ["blocks", "templates", "public", "styles", ".cmssy"];
|
|
66
|
-
dirs.forEach((dir) => {
|
|
67
|
-
fs.mkdirSync(path.join(projectPath, dir), { recursive: true });
|
|
68
|
-
});
|
|
69
|
-
// Create cmssy.config.js
|
|
70
|
-
const config = {
|
|
71
|
-
framework: "react",
|
|
72
|
-
projectName: answers.projectName,
|
|
73
|
-
author: {
|
|
74
|
-
name: answers.authorName,
|
|
75
|
-
email: answers.authorEmail,
|
|
76
|
-
},
|
|
77
|
-
build: {
|
|
78
|
-
outDir: "public",
|
|
79
|
-
minify: true,
|
|
80
|
-
sourcemap: true,
|
|
81
|
-
},
|
|
82
|
-
};
|
|
83
|
-
fs.writeFileSync(path.join(projectPath, "cmssy.config.js"), `export default ${JSON.stringify(config, null, 2)};\n`);
|
|
84
|
-
// Create package.json (versions match cmssy platform)
|
|
85
|
-
const packageJson = {
|
|
86
|
-
name: answers.projectName,
|
|
87
|
-
version: "1.0.0",
|
|
88
|
-
type: "module",
|
|
89
|
-
scripts: {
|
|
90
|
-
dev: "cmssy dev",
|
|
91
|
-
build: "cmssy build",
|
|
92
|
-
},
|
|
93
|
-
dependencies: {
|
|
94
|
-
react: "^19.2.0",
|
|
95
|
-
"react-dom": "^19.2.0",
|
|
96
|
-
next: "^16.0.10",
|
|
97
|
-
},
|
|
98
|
-
devDependencies: {
|
|
99
|
-
"@types/node": "^22.0.0",
|
|
100
|
-
"@types/react": "^19.0.0",
|
|
101
|
-
"@types/react-dom": "^19.0.0",
|
|
102
|
-
typescript: "^5.7.2",
|
|
103
|
-
tailwindcss: "^4.1.18",
|
|
104
|
-
"@tailwindcss/postcss": "^4.1.18",
|
|
105
|
-
},
|
|
106
|
-
};
|
|
107
|
-
fs.writeFileSync(path.join(projectPath, "package.json"), JSON.stringify(packageJson, null, 2) + "\n");
|
|
108
|
-
// Create tsconfig.json (jsx: "preserve" for Next.js compatibility)
|
|
109
|
-
const tsConfig = {
|
|
110
|
-
compilerOptions: {
|
|
111
|
-
target: "ES2020",
|
|
112
|
-
module: "ESNext",
|
|
113
|
-
lib: ["ES2020", "DOM", "DOM.Iterable"],
|
|
114
|
-
jsx: "preserve",
|
|
115
|
-
moduleResolution: "bundler",
|
|
116
|
-
allowImportingTsExtensions: true,
|
|
117
|
-
resolveJsonModule: true,
|
|
118
|
-
isolatedModules: true,
|
|
119
|
-
noEmit: true,
|
|
120
|
-
strict: true,
|
|
121
|
-
skipLibCheck: true,
|
|
122
|
-
esModuleInterop: true,
|
|
123
|
-
allowSyntheticDefaultImports: true,
|
|
124
|
-
forceConsistentCasingInFileNames: true,
|
|
125
|
-
plugins: [{ name: "next" }],
|
|
126
|
-
paths: {
|
|
127
|
-
"cmssy-cli/config": ["./node_modules/cmssy-cli/config"],
|
|
128
|
-
},
|
|
129
|
-
},
|
|
130
|
-
include: [
|
|
131
|
-
"blocks/**/*.ts",
|
|
132
|
-
"blocks/**/*.tsx",
|
|
133
|
-
"templates/**/*.ts",
|
|
134
|
-
"templates/**/*.tsx",
|
|
135
|
-
"components/**/*.ts",
|
|
136
|
-
"components/**/*.tsx",
|
|
137
|
-
"lib/**/*.ts",
|
|
138
|
-
],
|
|
139
|
-
exclude: ["node_modules", "dist", "public", ".cmssy"],
|
|
140
|
-
};
|
|
141
|
-
fs.writeFileSync(path.join(projectPath, "tsconfig.json"), JSON.stringify(tsConfig, null, 2) + "\n");
|
|
142
|
-
// Create .gitignore
|
|
143
|
-
const gitignore = `node_modules/
|
|
144
|
-
dist/
|
|
145
|
-
public/
|
|
146
|
-
.env
|
|
147
|
-
.DS_Store
|
|
148
|
-
*.log
|
|
149
|
-
.cmssy/
|
|
150
|
-
.next/
|
|
151
|
-
`;
|
|
152
|
-
fs.writeFileSync(path.join(projectPath, ".gitignore"), gitignore);
|
|
153
|
-
// Create next.config.mjs (enables Next.js dev mode in cmssy dev)
|
|
154
|
-
const nextConfig = `/** @type {import('next').NextConfig} */
|
|
155
|
-
const nextConfig = {
|
|
156
|
-
// Cmssy blocks are built with esbuild on publish, this config is only for dev
|
|
157
|
-
images: {
|
|
158
|
-
remotePatterns: [{ protocol: 'https', hostname: '**' }],
|
|
159
|
-
},
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
export default nextConfig;
|
|
163
|
-
`;
|
|
164
|
-
fs.writeFileSync(path.join(projectPath, "next.config.mjs"), nextConfig);
|
|
165
|
-
// Create postcss.config.js
|
|
166
|
-
const postcssConfig = `export default {
|
|
167
|
-
plugins: {
|
|
168
|
-
"@tailwindcss/postcss": {},
|
|
169
|
-
},
|
|
170
|
-
};
|
|
171
|
-
`;
|
|
172
|
-
fs.writeFileSync(path.join(projectPath, "postcss.config.js"), postcssConfig);
|
|
173
|
-
// Create styles/main.css
|
|
174
|
-
const mainCss = `@import "tailwindcss";
|
|
175
|
-
@source "../blocks";
|
|
176
|
-
@source "../templates";
|
|
177
|
-
@source "../components";
|
|
178
|
-
|
|
179
|
-
/* Set default border color (Tailwind v4 reset uses currentColor) */
|
|
180
|
-
@layer base {
|
|
181
|
-
*,
|
|
182
|
-
::after,
|
|
183
|
-
::before {
|
|
184
|
-
border-color: var(--border, currentColor);
|
|
185
|
-
}
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Detect existing Next.js project in cwd
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
function detectNextProject(cwd) {
|
|
13
|
+
return ["next.config.js", "next.config.mjs", "next.config.ts"].some((f) => fs.existsSync(path.join(cwd, f)));
|
|
186
14
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
} */
|
|
199
|
-
`;
|
|
200
|
-
fs.writeFileSync(path.join(projectPath, "styles", "main.css"), mainCss);
|
|
201
|
-
// Create .env.example
|
|
202
|
-
const envExample = `# Cmssy API Configuration
|
|
203
|
-
# Run 'cmssy configure' to set these values
|
|
204
|
-
|
|
205
|
-
# Cmssy GraphQL API URL
|
|
206
|
-
CMSSY_API_URL=https://api.cmssy.io/graphql
|
|
207
|
-
|
|
208
|
-
# Cmssy API Token (get from Dashboard ā API Tokens)
|
|
209
|
-
CMSSY_API_TOKEN=
|
|
210
|
-
|
|
211
|
-
# Workspace ID (for workspace publish)
|
|
212
|
-
CMSSY_WORKSPACE_ID=
|
|
213
|
-
`;
|
|
214
|
-
fs.writeFileSync(path.join(projectPath, ".env.example"), envExample);
|
|
215
|
-
// Create .vscode/settings.json (VS Code performance + DX)
|
|
216
|
-
fs.mkdirSync(path.join(projectPath, ".vscode"), { recursive: true });
|
|
217
|
-
const vscodeSettings = {
|
|
218
|
-
"files.watcherExclude": {
|
|
219
|
-
"**/public/**": true,
|
|
220
|
-
"**/.cmssy/**": true,
|
|
221
|
-
"**/node_modules/**": true,
|
|
222
|
-
"**/.git/objects/**": true,
|
|
223
|
-
"**/.git/subtree-cache/**": true,
|
|
224
|
-
"**/.next/**": true,
|
|
225
|
-
"**/dist/**": true,
|
|
226
|
-
},
|
|
227
|
-
"search.exclude": {
|
|
228
|
-
"**/public": true,
|
|
229
|
-
"**/.cmssy": true,
|
|
230
|
-
"**/node_modules": true,
|
|
231
|
-
"**/.next": true,
|
|
232
|
-
"**/dist": true,
|
|
233
|
-
"**/pnpm-lock.yaml": true,
|
|
234
|
-
},
|
|
235
|
-
"files.exclude": {
|
|
236
|
-
"**/.cmssy": true,
|
|
237
|
-
"**/public": true,
|
|
238
|
-
},
|
|
239
|
-
"eslint.enable": false,
|
|
240
|
-
"typescript.tsdk": "node_modules/typescript/lib",
|
|
241
|
-
"typescript.tsserver.maxTsServerMemory": 2048,
|
|
242
|
-
"typescript.tsserver.experimental.enableProjectDiagnostics": false,
|
|
243
|
-
"tailwindCSS.files.exclude": [
|
|
244
|
-
"**/node_modules/**",
|
|
245
|
-
"**/.cmssy/**",
|
|
246
|
-
"**/public/**",
|
|
247
|
-
"**/.next/**",
|
|
248
|
-
"**/dist/**",
|
|
249
|
-
],
|
|
250
|
-
};
|
|
251
|
-
fs.writeFileSync(path.join(projectPath, ".vscode", "settings.json"), JSON.stringify(vscodeSettings, null, 2) + "\n");
|
|
252
|
-
// Create .vscode/extensions.json (recommended extensions)
|
|
253
|
-
const vscodeExtensions = {
|
|
254
|
-
recommendations: [
|
|
255
|
-
"bradlc.vscode-tailwindcss",
|
|
256
|
-
"csstools.postcss",
|
|
257
|
-
],
|
|
258
|
-
unwantedRecommendations: [
|
|
259
|
-
"dbaeumer.vscode-eslint",
|
|
260
|
-
],
|
|
261
|
-
};
|
|
262
|
-
fs.writeFileSync(path.join(projectPath, ".vscode", "extensions.json"), JSON.stringify(vscodeExtensions, null, 2) + "\n");
|
|
263
|
-
// Create README.md
|
|
264
|
-
const readme = `# ${answers.projectName}
|
|
265
|
-
|
|
266
|
-
Cmssy project for building reusable UI blocks.
|
|
267
|
-
|
|
268
|
-
## Getting Started
|
|
269
|
-
|
|
270
|
-
\`\`\`bash
|
|
271
|
-
npm install
|
|
272
|
-
npm run dev
|
|
273
|
-
\`\`\`
|
|
274
|
-
|
|
275
|
-
## Commands
|
|
276
|
-
|
|
277
|
-
\`\`\`bash
|
|
278
|
-
cmssy dev # Start dev server
|
|
279
|
-
cmssy create block <name> # Create new block
|
|
280
|
-
cmssy build # Build for production
|
|
281
|
-
cmssy publish --workspace # Publish to workspace
|
|
282
|
-
\`\`\`
|
|
283
|
-
|
|
284
|
-
## Project Structure
|
|
285
|
-
|
|
286
|
-
\`\`\`
|
|
287
|
-
${answers.projectName}/
|
|
288
|
-
āāā blocks/ # Your UI blocks
|
|
289
|
-
āāā styles/
|
|
290
|
-
ā āāā main.css # Global Tailwind styles
|
|
291
|
-
āāā public/ # Build output
|
|
292
|
-
āāā cmssy.config.js
|
|
293
|
-
\`\`\`
|
|
294
|
-
`;
|
|
295
|
-
fs.writeFileSync(path.join(projectPath, "README.md"), readme);
|
|
296
|
-
spinner.succeed("Project structure created");
|
|
297
|
-
// Create example hero block
|
|
298
|
-
spinner.start("Creating example hero block...");
|
|
299
|
-
const heroBlockPath = path.join(projectPath, "blocks", "hero");
|
|
300
|
-
fs.mkdirSync(path.join(heroBlockPath, "src"), { recursive: true });
|
|
301
|
-
// Hero.tsx
|
|
302
|
-
const heroComponent = `import { BlockContent } from "./block";
|
|
15
|
+
function detectCmssyProject(cwd) {
|
|
16
|
+
return fs.existsSync(path.join(cwd, "cmssy.config.js"));
|
|
17
|
+
}
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Scaffold example hero block (shared between both modes)
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
async function scaffoldExampleBlock(projectPath, answers) {
|
|
22
|
+
const heroBlockPath = path.join(projectPath, "blocks", "hero");
|
|
23
|
+
fs.mkdirSync(path.join(heroBlockPath, "src"), { recursive: true });
|
|
24
|
+
// Hero.tsx
|
|
25
|
+
fs.writeFileSync(path.join(heroBlockPath, "src", "Hero.tsx"), `import { BlockContent } from "./block";
|
|
303
26
|
|
|
304
27
|
export default function Hero({ content }: { content: BlockContent }) {
|
|
305
28
|
const {
|
|
@@ -324,28 +47,22 @@ export default function Hero({ content }: { content: BlockContent }) {
|
|
|
324
47
|
</section>
|
|
325
48
|
);
|
|
326
49
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
const indexFile = `export { default } from "./Hero";
|
|
50
|
+
`);
|
|
51
|
+
// index.tsx
|
|
52
|
+
fs.writeFileSync(path.join(heroBlockPath, "src", "index.tsx"), `export { default } from "./Hero";
|
|
331
53
|
import "./index.css";
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
},
|
|
345
|
-
};
|
|
346
|
-
fs.writeFileSync(path.join(heroBlockPath, "package.json"), JSON.stringify(blockPackageJson, null, 2) + "\n");
|
|
347
|
-
// config.ts
|
|
348
|
-
const blockConfig = `import { defineBlock } from "cmssy-cli/config";
|
|
54
|
+
`);
|
|
55
|
+
// index.css
|
|
56
|
+
fs.writeFileSync(path.join(heroBlockPath, "src", "index.css"), `@import "../../../styles/main.css";\n`);
|
|
57
|
+
// package.json
|
|
58
|
+
fs.writeFileSync(path.join(heroBlockPath, "package.json"), JSON.stringify({
|
|
59
|
+
name: `@${answers.projectName}/blocks.hero`,
|
|
60
|
+
version: "1.0.0",
|
|
61
|
+
description: "Hero section block",
|
|
62
|
+
author: { name: answers.authorName, email: answers.authorEmail },
|
|
63
|
+
}, null, 2) + "\n");
|
|
64
|
+
// config.ts
|
|
65
|
+
fs.writeFileSync(path.join(heroBlockPath, "config.ts"), `import { defineBlock, field } from "@cmssy/cli/config";
|
|
349
66
|
|
|
350
67
|
export default defineBlock({
|
|
351
68
|
name: "Hero Section",
|
|
@@ -354,84 +71,350 @@ export default defineBlock({
|
|
|
354
71
|
tags: ["hero", "landing", "cta"],
|
|
355
72
|
|
|
356
73
|
schema: {
|
|
357
|
-
heading: {
|
|
74
|
+
heading: field({
|
|
358
75
|
type: "singleLine",
|
|
359
76
|
label: "Heading",
|
|
360
77
|
defaultValue: "Welcome to Cmssy",
|
|
361
|
-
},
|
|
362
|
-
subheading: {
|
|
78
|
+
}),
|
|
79
|
+
subheading: field({
|
|
363
80
|
type: "singleLine",
|
|
364
81
|
label: "Subheading",
|
|
365
82
|
defaultValue: "Build reusable UI blocks with React & Tailwind",
|
|
366
|
-
},
|
|
367
|
-
ctaText: {
|
|
83
|
+
}),
|
|
84
|
+
ctaText: field({
|
|
368
85
|
type: "singleLine",
|
|
369
86
|
label: "CTA Text",
|
|
370
87
|
defaultValue: "Get Started",
|
|
371
|
-
},
|
|
372
|
-
ctaUrl: {
|
|
88
|
+
}),
|
|
89
|
+
ctaUrl: field({
|
|
373
90
|
type: "link",
|
|
374
91
|
label: "CTA URL",
|
|
375
92
|
defaultValue: "#",
|
|
376
|
-
},
|
|
93
|
+
}),
|
|
377
94
|
},
|
|
378
|
-
|
|
379
|
-
pricing: { licenseType: "free" },
|
|
380
95
|
});
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
96
|
+
`);
|
|
97
|
+
// preview.json
|
|
98
|
+
fs.writeFileSync(path.join(heroBlockPath, "preview.json"), JSON.stringify({
|
|
99
|
+
heading: "Welcome to Cmssy",
|
|
100
|
+
subheading: "Build reusable UI blocks with React & Tailwind",
|
|
101
|
+
ctaText: "Get Started",
|
|
102
|
+
ctaUrl: "#",
|
|
103
|
+
}, null, 2) + "\n");
|
|
104
|
+
// Generate types
|
|
105
|
+
const heroSchema = {
|
|
106
|
+
heading: {
|
|
107
|
+
type: "singleLine",
|
|
108
|
+
label: "Heading",
|
|
109
|
+
defaultValue: "Welcome to Cmssy",
|
|
110
|
+
},
|
|
111
|
+
subheading: {
|
|
112
|
+
type: "singleLine",
|
|
113
|
+
label: "Subheading",
|
|
114
|
+
defaultValue: "Build reusable UI blocks",
|
|
115
|
+
},
|
|
116
|
+
ctaText: {
|
|
117
|
+
type: "singleLine",
|
|
118
|
+
label: "CTA Text",
|
|
119
|
+
defaultValue: "Get Started",
|
|
120
|
+
},
|
|
121
|
+
ctaUrl: { type: "link", label: "CTA URL", defaultValue: "#" },
|
|
122
|
+
};
|
|
123
|
+
const fieldTypes = await getFieldTypes();
|
|
124
|
+
await generateTypes({
|
|
125
|
+
blockPath: heroBlockPath,
|
|
126
|
+
schema: heroSchema,
|
|
127
|
+
fieldTypes,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
// Write cmssy.config.js
|
|
132
|
+
// ---------------------------------------------------------------------------
|
|
133
|
+
function writeCmssyConfig(projectPath, answers) {
|
|
134
|
+
const config = {
|
|
135
|
+
framework: "react",
|
|
136
|
+
projectName: answers.projectName,
|
|
137
|
+
author: { name: answers.authorName, email: answers.authorEmail },
|
|
138
|
+
build: { outDir: "public", minify: true, sourcemap: true },
|
|
139
|
+
};
|
|
140
|
+
fs.writeFileSync(path.join(projectPath, "cmssy.config.js"), `export default ${JSON.stringify(config, null, 2)};\n`);
|
|
141
|
+
}
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
// Write styles/main.css
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
function writeMainCss(projectPath) {
|
|
146
|
+
const stylesDir = path.join(projectPath, "styles");
|
|
147
|
+
fs.mkdirSync(stylesDir, { recursive: true });
|
|
148
|
+
fs.writeFileSync(path.join(stylesDir, "main.css"), `@import "tailwindcss";
|
|
149
|
+
@source "../blocks";
|
|
150
|
+
@source "../templates";
|
|
151
|
+
@source "../components";
|
|
152
|
+
|
|
153
|
+
/* Set default border color (Tailwind v4 reset uses currentColor) */
|
|
154
|
+
@layer base {
|
|
155
|
+
*,
|
|
156
|
+
::after,
|
|
157
|
+
::before {
|
|
158
|
+
border-color: var(--border, currentColor);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
`);
|
|
162
|
+
}
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
// Write .env.example
|
|
165
|
+
// ---------------------------------------------------------------------------
|
|
166
|
+
function writeEnvExample(projectPath) {
|
|
167
|
+
fs.writeFileSync(path.join(projectPath, ".env.example"), `# Cmssy API Configuration
|
|
168
|
+
# Run 'cmssy configure' to set these values
|
|
169
|
+
|
|
170
|
+
# Cmssy GraphQL API URL
|
|
171
|
+
CMSSY_API_URL=https://api.cmssy.io/graphql
|
|
172
|
+
|
|
173
|
+
# Cmssy API Token (get from Dashboard ā API Tokens)
|
|
174
|
+
CMSSY_API_TOKEN=
|
|
175
|
+
|
|
176
|
+
# Workspace ID (for workspace publish)
|
|
177
|
+
CMSSY_WORKSPACE_ID=
|
|
178
|
+
`);
|
|
179
|
+
}
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
// Merge cmssy scripts into existing package.json
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
function mergeCmssyScripts(projectPath) {
|
|
184
|
+
const pkgPath = path.join(projectPath, "package.json");
|
|
185
|
+
if (!fs.existsSync(pkgPath))
|
|
186
|
+
return;
|
|
187
|
+
const pkg = fs.readJsonSync(pkgPath);
|
|
188
|
+
pkg.scripts = pkg.scripts || {};
|
|
189
|
+
if (!pkg.scripts["cmssy:dev"]) {
|
|
190
|
+
pkg.scripts["cmssy:dev"] = "cmssy dev";
|
|
191
|
+
}
|
|
192
|
+
if (!pkg.scripts["cmssy:build"]) {
|
|
193
|
+
pkg.scripts["cmssy:build"] = "cmssy build";
|
|
194
|
+
}
|
|
195
|
+
if (!pkg.scripts["cmssy:publish"]) {
|
|
196
|
+
pkg.scripts["cmssy:publish"] = "cmssy publish --all";
|
|
197
|
+
}
|
|
198
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
199
|
+
}
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
// New project mode: create-next-app + cmssy layer
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
async function initNewProject(name, options) {
|
|
204
|
+
console.log(chalk.blue.bold("\nšØ Cmssy - Create New Project\n"));
|
|
205
|
+
let answers;
|
|
206
|
+
if (options.yes) {
|
|
207
|
+
answers = { projectName: name, authorName: "", authorEmail: "" };
|
|
208
|
+
console.log(chalk.gray(`Using defaults: ${name}\n`));
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
answers = await inquirer.prompt([
|
|
212
|
+
{
|
|
213
|
+
type: "input",
|
|
214
|
+
name: "projectName",
|
|
215
|
+
message: "Project name:",
|
|
216
|
+
default: name,
|
|
217
|
+
validate: (input) => {
|
|
218
|
+
if (/^[a-z0-9-_]+$/.test(input))
|
|
219
|
+
return true;
|
|
220
|
+
return "Must contain only lowercase letters, numbers, hyphens, underscores";
|
|
221
|
+
},
|
|
397
222
|
},
|
|
398
|
-
|
|
399
|
-
type: "
|
|
400
|
-
|
|
401
|
-
|
|
223
|
+
{
|
|
224
|
+
type: "input",
|
|
225
|
+
name: "authorName",
|
|
226
|
+
message: "Author name:",
|
|
227
|
+
default: "",
|
|
402
228
|
},
|
|
403
|
-
|
|
404
|
-
type: "
|
|
405
|
-
|
|
406
|
-
|
|
229
|
+
{
|
|
230
|
+
type: "input",
|
|
231
|
+
name: "authorEmail",
|
|
232
|
+
message: "Author email:",
|
|
233
|
+
default: "",
|
|
407
234
|
},
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
235
|
+
]);
|
|
236
|
+
name = answers.projectName;
|
|
237
|
+
}
|
|
238
|
+
const projectPath = path.join(process.cwd(), name);
|
|
239
|
+
if (fs.existsSync(projectPath)) {
|
|
240
|
+
console.error(chalk.red(`\nā Directory "${name}" already exists\n`));
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
// Step 1: Run create-next-app
|
|
244
|
+
const spinner = ora("Running create-next-app...").start();
|
|
245
|
+
try {
|
|
246
|
+
execSync(`npx create-next-app@latest ${name} --typescript --tailwind --app --src-dir --no-eslint --import-alias "@/*"`, { cwd: process.cwd(), stdio: "pipe" });
|
|
247
|
+
spinner.succeed("Next.js project created");
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
spinner.fail("Failed to run create-next-app");
|
|
251
|
+
console.error(chalk.red("\nMake sure npx is available and you have internet access.\n"));
|
|
252
|
+
process.exit(1);
|
|
253
|
+
}
|
|
254
|
+
// Step 2: Add cmssy layer
|
|
255
|
+
spinner.start("Adding Cmssy configuration...");
|
|
256
|
+
try {
|
|
257
|
+
// Create directories
|
|
258
|
+
fs.mkdirSync(path.join(projectPath, "blocks"), { recursive: true });
|
|
259
|
+
fs.mkdirSync(path.join(projectPath, "templates"), { recursive: true });
|
|
260
|
+
fs.mkdirSync(path.join(projectPath, ".cmssy"), { recursive: true });
|
|
261
|
+
writeCmssyConfig(projectPath, answers);
|
|
262
|
+
writeMainCss(projectPath);
|
|
263
|
+
writeEnvExample(projectPath);
|
|
264
|
+
mergeCmssyScripts(projectPath);
|
|
265
|
+
// Add .cmssy/ to gitignore
|
|
266
|
+
const gitignorePath = path.join(projectPath, ".gitignore");
|
|
267
|
+
if (fs.existsSync(gitignorePath)) {
|
|
268
|
+
const content = fs.readFileSync(gitignorePath, "utf-8");
|
|
269
|
+
if (!content.includes(".cmssy")) {
|
|
270
|
+
fs.appendFileSync(gitignorePath, "\n# Cmssy\n.cmssy/\npublic/\n");
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
spinner.succeed("Cmssy configuration added");
|
|
274
|
+
// Step 3: Install @cmssy/types
|
|
275
|
+
spinner.start("Installing @cmssy/types...");
|
|
276
|
+
execSync("npm install --save-dev @cmssy/types @cmssy/cli", {
|
|
277
|
+
cwd: projectPath,
|
|
278
|
+
stdio: "pipe",
|
|
415
279
|
});
|
|
280
|
+
spinner.succeed("Dependencies installed");
|
|
281
|
+
// Step 4: Create example block
|
|
282
|
+
spinner.start("Creating example hero block...");
|
|
283
|
+
await scaffoldExampleBlock(projectPath, answers);
|
|
416
284
|
spinner.succeed("Example hero block created");
|
|
417
|
-
// Initialize git
|
|
418
|
-
if (answers.initGit) {
|
|
419
|
-
spinner.start("Initializing git repository...");
|
|
420
|
-
process.chdir(projectPath);
|
|
421
|
-
execSync("git init", { stdio: "ignore" });
|
|
422
|
-
execSync("git branch -m main", { stdio: "ignore" });
|
|
423
|
-
spinner.succeed("Git repository initialized");
|
|
424
|
-
}
|
|
425
285
|
console.log(chalk.green.bold("\nā Project created successfully!\n"));
|
|
426
286
|
console.log(chalk.cyan("Next steps:\n"));
|
|
427
|
-
console.log(chalk.white(` cd ${
|
|
428
|
-
console.log(chalk.white(" npm
|
|
429
|
-
console.log(chalk.
|
|
287
|
+
console.log(chalk.white(` cd ${name}`));
|
|
288
|
+
console.log(chalk.white(" npm run cmssy:dev\n"));
|
|
289
|
+
console.log(chalk.gray("Commands:"));
|
|
290
|
+
console.log(chalk.gray(" cmssy create block <name> Add a new block"));
|
|
291
|
+
console.log(chalk.gray(" cmssy dev Start dev server"));
|
|
292
|
+
console.log(chalk.gray(" cmssy publish --all -w ID Publish to workspace\n"));
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
spinner.fail("Failed to set up Cmssy");
|
|
296
|
+
console.error(chalk.red("\nError:"), error);
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
// ---------------------------------------------------------------------------
|
|
301
|
+
// Existing project mode: add cmssy to current Next.js project
|
|
302
|
+
// ---------------------------------------------------------------------------
|
|
303
|
+
async function initExistingProject(options) {
|
|
304
|
+
const cwd = process.cwd();
|
|
305
|
+
console.log(chalk.blue.bold("\nšØ Cmssy - Initialize in Existing Project\n"));
|
|
306
|
+
console.log(chalk.gray(`Detected Next.js project in ${path.basename(cwd)}\n`));
|
|
307
|
+
if (detectCmssyProject(cwd)) {
|
|
308
|
+
console.error(chalk.red("ā This project already has cmssy.config.js. Nothing to do.\n"));
|
|
309
|
+
process.exit(1);
|
|
310
|
+
}
|
|
311
|
+
// Read project name from existing package.json
|
|
312
|
+
const pkgPath = path.join(cwd, "package.json");
|
|
313
|
+
const pkg = fs.existsSync(pkgPath) ? fs.readJsonSync(pkgPath) : {};
|
|
314
|
+
const defaultName = pkg.name || path.basename(cwd);
|
|
315
|
+
let answers;
|
|
316
|
+
if (options.yes) {
|
|
317
|
+
answers = { projectName: defaultName, authorName: "", authorEmail: "" };
|
|
318
|
+
console.log(chalk.gray(`Using defaults: ${defaultName}\n`));
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
answers = await inquirer.prompt([
|
|
322
|
+
{
|
|
323
|
+
type: "input",
|
|
324
|
+
name: "projectName",
|
|
325
|
+
message: "Cmssy project name:",
|
|
326
|
+
default: defaultName,
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
type: "input",
|
|
330
|
+
name: "authorName",
|
|
331
|
+
message: "Author name:",
|
|
332
|
+
default: pkg.author?.name || "",
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
type: "input",
|
|
336
|
+
name: "authorEmail",
|
|
337
|
+
message: "Author email:",
|
|
338
|
+
default: pkg.author?.email || "",
|
|
339
|
+
},
|
|
340
|
+
]);
|
|
341
|
+
}
|
|
342
|
+
const spinner = ora("Adding Cmssy configuration...").start();
|
|
343
|
+
try {
|
|
344
|
+
// Create directories
|
|
345
|
+
fs.mkdirSync(path.join(cwd, "blocks"), { recursive: true });
|
|
346
|
+
fs.mkdirSync(path.join(cwd, "templates"), { recursive: true });
|
|
347
|
+
fs.mkdirSync(path.join(cwd, ".cmssy"), { recursive: true });
|
|
348
|
+
writeCmssyConfig(cwd, answers);
|
|
349
|
+
writeMainCss(cwd);
|
|
350
|
+
writeEnvExample(cwd);
|
|
351
|
+
mergeCmssyScripts(cwd);
|
|
352
|
+
// Add .cmssy/ to gitignore
|
|
353
|
+
const gitignorePath = path.join(cwd, ".gitignore");
|
|
354
|
+
if (fs.existsSync(gitignorePath)) {
|
|
355
|
+
const content = fs.readFileSync(gitignorePath, "utf-8");
|
|
356
|
+
if (!content.includes(".cmssy")) {
|
|
357
|
+
fs.appendFileSync(gitignorePath, "\n# Cmssy\n.cmssy/\n");
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
spinner.succeed("Cmssy configuration added");
|
|
361
|
+
// Install dependencies
|
|
362
|
+
spinner.start("Installing @cmssy/types...");
|
|
363
|
+
const packageManager = detectPackageManager(cwd);
|
|
364
|
+
const installCmd = packageManager === "pnpm"
|
|
365
|
+
? "pnpm add -D @cmssy/types @cmssy/cli"
|
|
366
|
+
: packageManager === "yarn"
|
|
367
|
+
? "yarn add -D @cmssy/types @cmssy/cli"
|
|
368
|
+
: "npm install --save-dev @cmssy/types @cmssy/cli";
|
|
369
|
+
execSync(installCmd, { cwd, stdio: "pipe" });
|
|
370
|
+
spinner.succeed("Dependencies installed");
|
|
371
|
+
// Create example block
|
|
372
|
+
spinner.start("Creating example hero block...");
|
|
373
|
+
await scaffoldExampleBlock(cwd, answers);
|
|
374
|
+
spinner.succeed("Example hero block created");
|
|
375
|
+
console.log(chalk.green.bold("\nā Cmssy initialized successfully!\n"));
|
|
376
|
+
console.log(chalk.cyan("Next steps:\n"));
|
|
377
|
+
console.log(chalk.white(" npm run cmssy:dev\n"));
|
|
378
|
+
console.log(chalk.gray("New files:"));
|
|
379
|
+
console.log(chalk.gray(" cmssy.config.js Project configuration"));
|
|
380
|
+
console.log(chalk.gray(" blocks/hero/ Example block"));
|
|
381
|
+
console.log(chalk.gray(" styles/main.css Tailwind source paths"));
|
|
382
|
+
console.log(chalk.gray(" .env.example API credentials template\n"));
|
|
430
383
|
}
|
|
431
384
|
catch (error) {
|
|
432
|
-
spinner.fail("Failed to
|
|
385
|
+
spinner.fail("Failed to initialize Cmssy");
|
|
433
386
|
console.error(chalk.red("\nError:"), error);
|
|
434
387
|
process.exit(1);
|
|
435
388
|
}
|
|
436
389
|
}
|
|
390
|
+
// ---------------------------------------------------------------------------
|
|
391
|
+
// Detect package manager from lockfile
|
|
392
|
+
// ---------------------------------------------------------------------------
|
|
393
|
+
function detectPackageManager(cwd) {
|
|
394
|
+
if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml")))
|
|
395
|
+
return "pnpm";
|
|
396
|
+
if (fs.existsSync(path.join(cwd, "yarn.lock")))
|
|
397
|
+
return "yarn";
|
|
398
|
+
return "npm";
|
|
399
|
+
}
|
|
400
|
+
// ---------------------------------------------------------------------------
|
|
401
|
+
// Main entry point
|
|
402
|
+
// ---------------------------------------------------------------------------
|
|
403
|
+
export async function initCommand(name, options = {}) {
|
|
404
|
+
if (name) {
|
|
405
|
+
// Explicit name ā create new project
|
|
406
|
+
await initNewProject(name, options);
|
|
407
|
+
}
|
|
408
|
+
else if (detectNextProject(process.cwd())) {
|
|
409
|
+
// No name, but in Next.js project ā existing mode
|
|
410
|
+
await initExistingProject(options);
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
console.error(chalk.red("\nā No project name provided and no Next.js project detected.\n"));
|
|
414
|
+
console.log(chalk.cyan("Usage:\n"));
|
|
415
|
+
console.log(chalk.white(" cmssy init my-blocks Create a new project"));
|
|
416
|
+
console.log(chalk.white(" cmssy init Initialize in existing Next.js project\n"));
|
|
417
|
+
process.exit(1);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
437
420
|
//# sourceMappingURL=init.js.map
|