@cmssy/cli 0.9.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.
Files changed (60) hide show
  1. package/config.d.ts +1 -1
  2. package/dist/cli.js +22 -8
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/codegen.d.ts +7 -0
  5. package/dist/commands/codegen.d.ts.map +1 -0
  6. package/dist/commands/codegen.js +133 -0
  7. package/dist/commands/codegen.js.map +1 -0
  8. package/dist/commands/dev.d.ts.map +1 -1
  9. package/dist/commands/dev.js +5 -2
  10. package/dist/commands/dev.js.map +1 -1
  11. package/dist/commands/init.d.ts +0 -1
  12. package/dist/commands/init.d.ts.map +1 -1
  13. package/dist/commands/init.js +342 -352
  14. package/dist/commands/init.js.map +1 -1
  15. package/dist/commands/publish.js +109 -4
  16. package/dist/commands/publish.js.map +1 -1
  17. package/dist/utils/dev-generator/api-routes/blocks.d.ts +3 -0
  18. package/dist/utils/dev-generator/api-routes/blocks.d.ts.map +1 -0
  19. package/dist/utils/dev-generator/api-routes/blocks.js +194 -0
  20. package/dist/utils/dev-generator/api-routes/blocks.js.map +1 -0
  21. package/dist/utils/dev-generator/api-routes/config.d.ts +2 -0
  22. package/dist/utils/dev-generator/api-routes/config.d.ts.map +1 -0
  23. package/dist/utils/dev-generator/api-routes/config.js +107 -0
  24. package/dist/utils/dev-generator/api-routes/config.js.map +1 -0
  25. package/dist/utils/dev-generator/api-routes/preview.d.ts +2 -0
  26. package/dist/utils/dev-generator/api-routes/preview.d.ts.map +1 -0
  27. package/dist/utils/dev-generator/api-routes/preview.js +50 -0
  28. package/dist/utils/dev-generator/api-routes/preview.js.map +1 -0
  29. package/dist/utils/dev-generator/api-routes/workspaces.d.ts +2 -0
  30. package/dist/utils/dev-generator/api-routes/workspaces.d.ts.map +1 -0
  31. package/dist/utils/dev-generator/api-routes/workspaces.js +128 -0
  32. package/dist/utils/dev-generator/api-routes/workspaces.js.map +1 -0
  33. package/dist/utils/dev-generator/helpers.d.ts +23 -0
  34. package/dist/utils/dev-generator/helpers.d.ts.map +1 -0
  35. package/dist/utils/dev-generator/helpers.js +112 -0
  36. package/dist/utils/dev-generator/helpers.js.map +1 -0
  37. package/dist/utils/dev-generator/home-page.d.ts +2 -0
  38. package/dist/utils/dev-generator/home-page.d.ts.map +1 -0
  39. package/dist/utils/dev-generator/home-page.js +643 -0
  40. package/dist/utils/dev-generator/home-page.js.map +1 -0
  41. package/dist/utils/dev-generator/index.d.ts +12 -0
  42. package/dist/utils/dev-generator/index.d.ts.map +1 -0
  43. package/dist/utils/dev-generator/index.js +53 -0
  44. package/dist/utils/dev-generator/index.js.map +1 -0
  45. package/dist/utils/dev-generator/layout.d.ts +3 -0
  46. package/dist/utils/dev-generator/layout.d.ts.map +1 -0
  47. package/dist/utils/dev-generator/layout.js +59 -0
  48. package/dist/utils/dev-generator/layout.js.map +1 -0
  49. package/dist/utils/dev-generator/next-config.d.ts +3 -0
  50. package/dist/utils/dev-generator/next-config.d.ts.map +1 -0
  51. package/dist/utils/dev-generator/next-config.js +104 -0
  52. package/dist/utils/dev-generator/next-config.js.map +1 -0
  53. package/dist/utils/dev-generator/preview-pages.d.ts +3 -0
  54. package/dist/utils/dev-generator/preview-pages.d.ts.map +1 -0
  55. package/dist/utils/dev-generator/preview-pages.js +827 -0
  56. package/dist/utils/dev-generator/preview-pages.js.map +1 -0
  57. package/dist/utils/graphql.d.ts.map +1 -1
  58. package/dist/utils/graphql.js +2 -0
  59. package/dist/utils/graphql.js.map +1 -1
  60. package/package.json +2 -2
@@ -6,295 +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
- export async function initCommand(name, options = {}) {
10
- console.log(chalk.blue.bold("\nšŸ”Ø Cmssy - Initialize Project\n"));
11
- let answers;
12
- if (options.yes) {
13
- // Skip prompts, use defaults
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
- /* Custom theme - customize your design system here */
189
- /* @theme inline {
190
- --color-primary: var(--primary);
191
- --color-border: var(--border);
192
- } */
193
-
194
- /* CSS variables for theming */
195
- /* :root {
196
- --primary: oklch(0.6 0.25 292);
197
- --border: oklch(0.9 0 0);
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: ["bradlc.vscode-tailwindcss", "csstools.postcss"],
255
- unwantedRecommendations: ["dbaeumer.vscode-eslint"],
256
- };
257
- fs.writeFileSync(path.join(projectPath, ".vscode", "extensions.json"), JSON.stringify(vscodeExtensions, null, 2) + "\n");
258
- // Create README.md
259
- const readme = `# ${answers.projectName}
260
-
261
- Cmssy project for building reusable UI blocks.
262
-
263
- ## Getting Started
264
-
265
- \`\`\`bash
266
- npm install
267
- npm run dev
268
- \`\`\`
269
-
270
- ## Commands
271
-
272
- \`\`\`bash
273
- cmssy dev # Start dev server
274
- cmssy create block <name> # Create new block
275
- cmssy build # Build for production
276
- cmssy publish --workspace # Publish to workspace
277
- \`\`\`
278
-
279
- ## Project Structure
280
-
281
- \`\`\`
282
- ${answers.projectName}/
283
- ā”œā”€ā”€ blocks/ # Your UI blocks
284
- ā”œā”€ā”€ styles/
285
- │ └── main.css # Global Tailwind styles
286
- ā”œā”€ā”€ public/ # Build output
287
- └── cmssy.config.js
288
- \`\`\`
289
- `;
290
- fs.writeFileSync(path.join(projectPath, "README.md"), readme);
291
- spinner.succeed("Project structure created");
292
- // Create example hero block
293
- spinner.start("Creating example hero block...");
294
- const heroBlockPath = path.join(projectPath, "blocks", "hero");
295
- fs.mkdirSync(path.join(heroBlockPath, "src"), { recursive: true });
296
- // Hero.tsx
297
- 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";
298
26
 
299
27
  export default function Hero({ content }: { content: BlockContent }) {
300
28
  const {
@@ -319,28 +47,22 @@ export default function Hero({ content }: { content: BlockContent }) {
319
47
  </section>
320
48
  );
321
49
  }
322
- `;
323
- fs.writeFileSync(path.join(heroBlockPath, "src", "Hero.tsx"), heroComponent);
324
- // index.tsx - simplified format (standard React export)
325
- const indexFile = `export { default } from "./Hero";
50
+ `);
51
+ // index.tsx
52
+ fs.writeFileSync(path.join(heroBlockPath, "src", "index.tsx"), `export { default } from "./Hero";
326
53
  import "./index.css";
327
- `;
328
- fs.writeFileSync(path.join(heroBlockPath, "src", "index.tsx"), indexFile);
329
- // index.css
330
- fs.writeFileSync(path.join(heroBlockPath, "src", "index.css"), `@import "../../../styles/main.css";\n`);
331
- // package.json
332
- const blockPackageJson = {
333
- name: `@${answers.projectName}/blocks.hero`,
334
- version: "1.0.0",
335
- description: "Hero section block",
336
- author: {
337
- name: answers.authorName,
338
- email: answers.authorEmail,
339
- },
340
- };
341
- fs.writeFileSync(path.join(heroBlockPath, "package.json"), JSON.stringify(blockPackageJson, null, 2) + "\n");
342
- // config.ts
343
- const blockConfig = `import { defineBlock, field } 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";
344
66
 
345
67
  export default defineBlock({
346
68
  name: "Hero Section",
@@ -371,60 +93,328 @@ export default defineBlock({
371
93
  }),
372
94
  },
373
95
  });
374
- `;
375
- fs.writeFileSync(path.join(heroBlockPath, "config.ts"), blockConfig);
376
- // preview.json
377
- const previewData = {
378
- heading: "Welcome to Cmssy",
379
- subheading: "Build reusable UI blocks with React & Tailwind",
380
- ctaText: "Get Started",
381
- ctaUrl: "#",
382
- };
383
- fs.writeFileSync(path.join(heroBlockPath, "preview.json"), JSON.stringify(previewData, null, 2) + "\n");
384
- // Generate types
385
- const heroSchema = {
386
- heading: {
387
- type: "singleLine",
388
- label: "Heading",
389
- defaultValue: "Welcome to Cmssy",
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
+ },
390
222
  },
391
- subheading: {
392
- type: "singleLine",
393
- label: "Subheading",
394
- defaultValue: "Build reusable UI blocks",
223
+ {
224
+ type: "input",
225
+ name: "authorName",
226
+ message: "Author name:",
227
+ default: "",
395
228
  },
396
- ctaText: {
397
- type: "singleLine",
398
- label: "CTA Text",
399
- defaultValue: "Get Started",
229
+ {
230
+ type: "input",
231
+ name: "authorEmail",
232
+ message: "Author email:",
233
+ default: "",
400
234
  },
401
- ctaUrl: { type: "link", label: "CTA URL", defaultValue: "#" },
402
- };
403
- const fieldTypes = await getFieldTypes();
404
- await generateTypes({
405
- blockPath: heroBlockPath,
406
- schema: heroSchema,
407
- fieldTypes,
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",
408
279
  });
280
+ spinner.succeed("Dependencies installed");
281
+ // Step 4: Create example block
282
+ spinner.start("Creating example hero block...");
283
+ await scaffoldExampleBlock(projectPath, answers);
409
284
  spinner.succeed("Example hero block created");
410
- // Initialize git
411
- if (answers.initGit) {
412
- spinner.start("Initializing git repository...");
413
- process.chdir(projectPath);
414
- execSync("git init", { stdio: "ignore" });
415
- execSync("git branch -m main", { stdio: "ignore" });
416
- spinner.succeed("Git repository initialized");
417
- }
418
285
  console.log(chalk.green.bold("\nāœ“ Project created successfully!\n"));
419
286
  console.log(chalk.cyan("Next steps:\n"));
420
- console.log(chalk.white(` cd ${answers.projectName}`));
421
- console.log(chalk.white(" npm install"));
422
- console.log(chalk.white(" npm run dev\n"));
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"));
423
293
  }
424
294
  catch (error) {
425
- spinner.fail("Failed to create project");
295
+ spinner.fail("Failed to set up Cmssy");
426
296
  console.error(chalk.red("\nError:"), error);
427
297
  process.exit(1);
428
298
  }
429
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"));
383
+ }
384
+ catch (error) {
385
+ spinner.fail("Failed to initialize Cmssy");
386
+ console.error(chalk.red("\nError:"), error);
387
+ process.exit(1);
388
+ }
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
+ }
430
420
  //# sourceMappingURL=init.js.map