@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.
Files changed (69) hide show
  1. package/config.d.ts +2 -2
  2. package/config.js +1 -1
  3. package/dist/cli.js +22 -8
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/codegen.d.ts +7 -0
  6. package/dist/commands/codegen.d.ts.map +1 -0
  7. package/dist/commands/codegen.js +133 -0
  8. package/dist/commands/codegen.js.map +1 -0
  9. package/dist/commands/create.d.ts.map +1 -1
  10. package/dist/commands/create.js +11 -15
  11. package/dist/commands/create.js.map +1 -1
  12. package/dist/commands/dev.d.ts.map +1 -1
  13. package/dist/commands/dev.js +5 -2
  14. package/dist/commands/dev.js.map +1 -1
  15. package/dist/commands/init.d.ts +0 -1
  16. package/dist/commands/init.d.ts.map +1 -1
  17. package/dist/commands/init.js +350 -367
  18. package/dist/commands/init.js.map +1 -1
  19. package/dist/commands/publish.d.ts.map +1 -1
  20. package/dist/commands/publish.js +119 -9
  21. package/dist/commands/publish.js.map +1 -1
  22. package/dist/utils/block-config.d.ts +65 -2
  23. package/dist/utils/block-config.d.ts.map +1 -1
  24. package/dist/utils/block-config.js +17 -5
  25. package/dist/utils/block-config.js.map +1 -1
  26. package/dist/utils/dev-generator/api-routes/blocks.d.ts +3 -0
  27. package/dist/utils/dev-generator/api-routes/blocks.d.ts.map +1 -0
  28. package/dist/utils/dev-generator/api-routes/blocks.js +194 -0
  29. package/dist/utils/dev-generator/api-routes/blocks.js.map +1 -0
  30. package/dist/utils/dev-generator/api-routes/config.d.ts +2 -0
  31. package/dist/utils/dev-generator/api-routes/config.d.ts.map +1 -0
  32. package/dist/utils/dev-generator/api-routes/config.js +107 -0
  33. package/dist/utils/dev-generator/api-routes/config.js.map +1 -0
  34. package/dist/utils/dev-generator/api-routes/preview.d.ts +2 -0
  35. package/dist/utils/dev-generator/api-routes/preview.d.ts.map +1 -0
  36. package/dist/utils/dev-generator/api-routes/preview.js +50 -0
  37. package/dist/utils/dev-generator/api-routes/preview.js.map +1 -0
  38. package/dist/utils/dev-generator/api-routes/workspaces.d.ts +2 -0
  39. package/dist/utils/dev-generator/api-routes/workspaces.d.ts.map +1 -0
  40. package/dist/utils/dev-generator/api-routes/workspaces.js +128 -0
  41. package/dist/utils/dev-generator/api-routes/workspaces.js.map +1 -0
  42. package/dist/utils/dev-generator/helpers.d.ts +23 -0
  43. package/dist/utils/dev-generator/helpers.d.ts.map +1 -0
  44. package/dist/utils/dev-generator/helpers.js +112 -0
  45. package/dist/utils/dev-generator/helpers.js.map +1 -0
  46. package/dist/utils/dev-generator/home-page.d.ts +2 -0
  47. package/dist/utils/dev-generator/home-page.d.ts.map +1 -0
  48. package/dist/utils/dev-generator/home-page.js +643 -0
  49. package/dist/utils/dev-generator/home-page.js.map +1 -0
  50. package/dist/utils/dev-generator/index.d.ts +12 -0
  51. package/dist/utils/dev-generator/index.d.ts.map +1 -0
  52. package/dist/utils/dev-generator/index.js +53 -0
  53. package/dist/utils/dev-generator/index.js.map +1 -0
  54. package/dist/utils/dev-generator/layout.d.ts +3 -0
  55. package/dist/utils/dev-generator/layout.d.ts.map +1 -0
  56. package/dist/utils/dev-generator/layout.js +59 -0
  57. package/dist/utils/dev-generator/layout.js.map +1 -0
  58. package/dist/utils/dev-generator/next-config.d.ts +3 -0
  59. package/dist/utils/dev-generator/next-config.d.ts.map +1 -0
  60. package/dist/utils/dev-generator/next-config.js +104 -0
  61. package/dist/utils/dev-generator/next-config.js.map +1 -0
  62. package/dist/utils/dev-generator/preview-pages.d.ts +3 -0
  63. package/dist/utils/dev-generator/preview-pages.d.ts.map +1 -0
  64. package/dist/utils/dev-generator/preview-pages.js +827 -0
  65. package/dist/utils/dev-generator/preview-pages.js.map +1 -0
  66. package/dist/utils/graphql.d.ts.map +1 -1
  67. package/dist/utils/graphql.js +2 -0
  68. package/dist/utils/graphql.js.map +1 -1
  69. package/package.json +2 -2
@@ -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
- 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: [
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
- fs.writeFileSync(path.join(heroBlockPath, "src", "Hero.tsx"), heroComponent);
329
- // index.tsx - simplified format (standard React export)
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
- fs.writeFileSync(path.join(heroBlockPath, "src", "index.tsx"), indexFile);
334
- // index.css
335
- fs.writeFileSync(path.join(heroBlockPath, "src", "index.css"), `@import "../../../styles/main.css";\n`);
336
- // package.json
337
- const blockPackageJson = {
338
- name: `@${answers.projectName}/blocks.hero`,
339
- version: "1.0.0",
340
- description: "Hero section block",
341
- author: {
342
- name: answers.authorName,
343
- email: answers.authorEmail,
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
- fs.writeFileSync(path.join(heroBlockPath, "config.ts"), blockConfig);
383
- // preview.json
384
- const previewData = {
385
- heading: "Welcome to Cmssy",
386
- subheading: "Build reusable UI blocks with React & Tailwind",
387
- ctaText: "Get Started",
388
- ctaUrl: "#",
389
- };
390
- fs.writeFileSync(path.join(heroBlockPath, "preview.json"), JSON.stringify(previewData, null, 2) + "\n");
391
- // Generate types
392
- const heroSchema = {
393
- heading: {
394
- type: "singleLine",
395
- label: "Heading",
396
- 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
+ },
397
222
  },
398
- subheading: {
399
- type: "singleLine",
400
- label: "Subheading",
401
- defaultValue: "Build reusable UI blocks",
223
+ {
224
+ type: "input",
225
+ name: "authorName",
226
+ message: "Author name:",
227
+ default: "",
402
228
  },
403
- ctaText: {
404
- type: "singleLine",
405
- label: "CTA Text",
406
- defaultValue: "Get Started",
229
+ {
230
+ type: "input",
231
+ name: "authorEmail",
232
+ message: "Author email:",
233
+ default: "",
407
234
  },
408
- ctaUrl: { type: "link", label: "CTA URL", defaultValue: "#" },
409
- };
410
- const fieldTypes = await getFieldTypes();
411
- await generateTypes({
412
- blockPath: heroBlockPath,
413
- schema: heroSchema,
414
- 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",
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 ${answers.projectName}`));
428
- console.log(chalk.white(" npm install"));
429
- 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"));
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 create project");
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