@revealui/cli 0.0.1-pre.1 → 0.2.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 (68) hide show
  1. package/LICENSE +22 -202
  2. package/README.md +86 -0
  3. package/bin/create-revealui.js +6 -0
  4. package/dist/cli.d.ts +14 -0
  5. package/dist/cli.js +1075 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/index.d.ts +11 -2
  8. package/dist/index.js +1071 -123
  9. package/dist/index.js.map +1 -0
  10. package/package.json +42 -44
  11. package/templates/minimal/.env.example +36 -0
  12. package/templates/minimal/_gitignore +26 -0
  13. package/templates/minimal/next.config.mjs +10 -0
  14. package/templates/minimal/package.json +34 -0
  15. package/templates/minimal/revealui.config.ts +18 -0
  16. package/templates/minimal/src/app/globals.css +15 -0
  17. package/templates/minimal/src/app/layout.tsx +15 -0
  18. package/templates/minimal/src/app/page.tsx +20 -0
  19. package/templates/minimal/tsconfig.json +11 -0
  20. package/dist/commands/add.d.ts +0 -28
  21. package/dist/commands/add.d.ts.map +0 -1
  22. package/dist/commands/add.js +0 -115
  23. package/dist/commands/check.d.ts +0 -7
  24. package/dist/commands/check.d.ts.map +0 -1
  25. package/dist/commands/check.js +0 -34
  26. package/dist/commands/doctor/checks/build.d.ts +0 -10
  27. package/dist/commands/doctor/checks/build.d.ts.map +0 -1
  28. package/dist/commands/doctor/checks/build.js +0 -74
  29. package/dist/commands/doctor/checks/config.d.ts +0 -14
  30. package/dist/commands/doctor/checks/config.d.ts.map +0 -1
  31. package/dist/commands/doctor/checks/config.js +0 -116
  32. package/dist/commands/doctor/checks/dependencies.d.ts +0 -14
  33. package/dist/commands/doctor/checks/dependencies.d.ts.map +0 -1
  34. package/dist/commands/doctor/checks/dependencies.js +0 -126
  35. package/dist/commands/doctor/checks/practices.d.ts +0 -14
  36. package/dist/commands/doctor/checks/practices.d.ts.map +0 -1
  37. package/dist/commands/doctor/checks/practices.js +0 -142
  38. package/dist/commands/doctor/checks/structure.d.ts +0 -14
  39. package/dist/commands/doctor/checks/structure.d.ts.map +0 -1
  40. package/dist/commands/doctor/checks/structure.js +0 -107
  41. package/dist/commands/doctor/fixes/index.d.ts +0 -26
  42. package/dist/commands/doctor/fixes/index.d.ts.map +0 -1
  43. package/dist/commands/doctor/fixes/index.js +0 -108
  44. package/dist/commands/doctor/index.d.ts +0 -11
  45. package/dist/commands/doctor/index.d.ts.map +0 -1
  46. package/dist/commands/doctor/index.js +0 -37
  47. package/dist/commands/doctor/print.d.ts +0 -6
  48. package/dist/commands/doctor/print.d.ts.map +0 -1
  49. package/dist/commands/doctor/print.js +0 -31
  50. package/dist/commands/doctor/types.d.ts +0 -16
  51. package/dist/commands/doctor/types.d.ts.map +0 -1
  52. package/dist/commands/doctor/types.js +0 -1
  53. package/dist/commands/fix.d.ts +0 -5
  54. package/dist/commands/fix.d.ts.map +0 -1
  55. package/dist/commands/fix.js +0 -129
  56. package/dist/commands/init.d.ts +0 -35
  57. package/dist/commands/init.d.ts.map +0 -1
  58. package/dist/commands/init.js +0 -104
  59. package/dist/commands/upgrade.d.ts +0 -9
  60. package/dist/commands/upgrade.d.ts.map +0 -1
  61. package/dist/commands/upgrade.js +0 -85
  62. package/dist/index.d.ts.map +0 -1
  63. package/dist/onLoad.d.ts +0 -3
  64. package/dist/onLoad.d.ts.map +0 -1
  65. package/dist/onLoad.js +0 -5
  66. package/dist/utils.d.ts +0 -3
  67. package/dist/utils.d.ts.map +0 -1
  68. package/dist/utils.js +0 -6
package/dist/cli.js ADDED
@@ -0,0 +1,1075 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
+ };
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+
11
+ // ../../node_modules/.pnpm/tsup@8.5.1_@microsoft+api-extractor@7.56.2_@types+node@25.3.0__@swc+core@1.15.11_@swc+h_406b258ea3c313c6308725a34065a5eb/node_modules/tsup/assets/esm_shims.js
12
+ import path from "path";
13
+ import { fileURLToPath } from "url";
14
+ var init_esm_shims = __esm({
15
+ "../../node_modules/.pnpm/tsup@8.5.1_@microsoft+api-extractor@7.56.2_@types+node@25.3.0__@swc+core@1.15.11_@swc+h_406b258ea3c313c6308725a34065a5eb/node_modules/tsup/assets/esm_shims.js"() {
16
+ "use strict";
17
+ }
18
+ });
19
+
20
+ // src/validators/node-version.ts
21
+ import { createLogger } from "@revealui/setup/utils";
22
+ function validateNodeVersion() {
23
+ const currentVersion = process.version.slice(1);
24
+ const [currentMajor, currentMinor] = currentVersion.split(".").map(Number);
25
+ const [requiredMajor, requiredMinor] = REQUIRED_NODE_VERSION.split(".").map(Number);
26
+ if (currentMajor < requiredMajor || currentMajor === requiredMajor && currentMinor < requiredMinor) {
27
+ logger.error(
28
+ `Node.js ${REQUIRED_NODE_VERSION} or higher is required. You have ${currentVersion}.`
29
+ );
30
+ logger.info("Please upgrade Node.js: https://nodejs.org/");
31
+ return false;
32
+ }
33
+ return true;
34
+ }
35
+ var logger, REQUIRED_NODE_VERSION;
36
+ var init_node_version = __esm({
37
+ "src/validators/node-version.ts"() {
38
+ "use strict";
39
+ init_esm_shims();
40
+ logger = createLogger({ prefix: "Setup" });
41
+ REQUIRED_NODE_VERSION = "24.12.0";
42
+ }
43
+ });
44
+
45
+ // src/generators/devbox.ts
46
+ import fs from "fs/promises";
47
+ import path2 from "path";
48
+ async function generateDevbox(projectPath) {
49
+ const devboxConfig = {
50
+ packages: ["nodejs@24.12.0", "pnpm@10.28.2", "postgresql@16", "stripe-cli@latest"],
51
+ shell: {
52
+ init_hook: [
53
+ "corepack enable",
54
+ 'echo "\u{1F680} RevealUI Devbox shell ready!"',
55
+ 'echo "Run: pnpm dev to start development"'
56
+ ],
57
+ scripts: {
58
+ dev: "pnpm dev",
59
+ setup: "pnpm install && pnpm db:init",
60
+ test: "pnpm test"
61
+ }
62
+ },
63
+ env: {
64
+ NODE_ENV: "development"
65
+ }
66
+ };
67
+ await fs.writeFile(
68
+ path2.join(projectPath, "devbox.json"),
69
+ JSON.stringify(devboxConfig, null, 2),
70
+ "utf-8"
71
+ );
72
+ }
73
+ var init_devbox = __esm({
74
+ "src/generators/devbox.ts"() {
75
+ "use strict";
76
+ init_esm_shims();
77
+ }
78
+ });
79
+
80
+ // src/generators/devcontainer.ts
81
+ import fs2 from "fs/promises";
82
+ import path3 from "path";
83
+ async function generateDevContainer(projectPath) {
84
+ const devcontainerDir = path3.join(projectPath, ".devcontainer");
85
+ await fs2.mkdir(devcontainerDir, { recursive: true });
86
+ const devcontainerConfig = {
87
+ name: "RevealUI Development",
88
+ image: "mcr.microsoft.com/devcontainers/typescript-node:24",
89
+ features: {
90
+ "ghcr.io/devcontainers/features/common-utils:2": {
91
+ installZsh: true,
92
+ installOhMyZsh: true
93
+ }
94
+ },
95
+ forwardPorts: [3e3, 4e3, 5432],
96
+ portsAttributes: {
97
+ "3000": {
98
+ label: "Web App",
99
+ onAutoForward: "notify"
100
+ },
101
+ "4000": {
102
+ label: "CMS",
103
+ onAutoForward: "notify"
104
+ },
105
+ "5432": {
106
+ label: "PostgreSQL",
107
+ onAutoForward: "silent"
108
+ }
109
+ },
110
+ postCreateCommand: "corepack enable && pnpm install",
111
+ customizations: {
112
+ vscode: {
113
+ extensions: [
114
+ "dbaeumer.vscode-eslint",
115
+ "biomejs.biome",
116
+ "bradlc.vscode-tailwindcss",
117
+ "Prisma.prisma",
118
+ "ms-azuretools.vscode-docker",
119
+ "streetsidesoftware.code-spell-checker"
120
+ ],
121
+ settings: {
122
+ "editor.defaultFormatter": "biomejs.biome",
123
+ "editor.formatOnSave": true,
124
+ "editor.codeActionsOnSave": {
125
+ "quickfix.biome": "explicit",
126
+ "source.organizeImports.biome": "explicit"
127
+ }
128
+ }
129
+ }
130
+ },
131
+ remoteUser: "node"
132
+ };
133
+ await fs2.writeFile(
134
+ path3.join(devcontainerDir, "devcontainer.json"),
135
+ JSON.stringify(devcontainerConfig, null, 2),
136
+ "utf-8"
137
+ );
138
+ const dockerCompose = `version: '3.8'
139
+
140
+ services:
141
+ app:
142
+ build:
143
+ context: ..
144
+ dockerfile: .devcontainer/Dockerfile
145
+ volumes:
146
+ - ..:/workspace:cached
147
+ command: sleep infinity
148
+ network_mode: service:db
149
+
150
+ db:
151
+ image: pgvector/pgvector:pg16
152
+ restart: unless-stopped
153
+ environment:
154
+ POSTGRES_USER: postgres
155
+ POSTGRES_PASSWORD: postgres
156
+ POSTGRES_DB: revealui
157
+ volumes:
158
+ - postgres-data:/var/lib/postgresql/data
159
+
160
+ volumes:
161
+ postgres-data:
162
+ `;
163
+ await fs2.writeFile(path3.join(devcontainerDir, "docker-compose.yml"), dockerCompose, "utf-8");
164
+ const dockerfile = `FROM mcr.microsoft.com/devcontainers/typescript-node:24
165
+
166
+ # Install additional tools
167
+ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \\
168
+ && apt-get -y install --no-install-recommends postgresql-client
169
+
170
+ # Enable pnpm
171
+ RUN corepack enable
172
+ `;
173
+ await fs2.writeFile(path3.join(devcontainerDir, "Dockerfile"), dockerfile, "utf-8");
174
+ const readme = `# Dev Container Setup
175
+
176
+ This directory contains the Dev Container configuration for RevealUI.
177
+
178
+ ## Usage
179
+
180
+ ### VS Code
181
+
182
+ 1. Install the "Dev Containers" extension
183
+ 2. Open this folder in VS Code
184
+ 3. Press F1 and select "Dev Containers: Reopen in Container"
185
+
186
+ ### GitHub Codespaces
187
+
188
+ 1. Click the green "Code" button on GitHub
189
+ 2. Select "Codespaces" tab
190
+ 3. Click "Create codespace on main"
191
+
192
+ ## What's Included
193
+
194
+ - Node.js 24.12.0
195
+ - pnpm package manager
196
+ - PostgreSQL 16 with pgvector
197
+ - VS Code extensions:
198
+ - ESLint
199
+ - Biome
200
+ - Tailwind CSS
201
+ - Prisma
202
+ - Docker
203
+ - Code Spell Checker
204
+
205
+ ## Environment Variables
206
+
207
+ Environment variables are loaded from \`.env.development.local\`.
208
+ For GitHub Codespaces, set secrets in your repository settings.
209
+
210
+ ## Ports
211
+
212
+ - 3000: Web application
213
+ - 4000: CMS
214
+ - 5432: PostgreSQL database
215
+ `;
216
+ await fs2.writeFile(path3.join(devcontainerDir, "README.md"), readme, "utf-8");
217
+ }
218
+ var init_devcontainer = __esm({
219
+ "src/generators/devcontainer.ts"() {
220
+ "use strict";
221
+ init_esm_shims();
222
+ }
223
+ });
224
+
225
+ // src/generators/readme.ts
226
+ import fs3 from "fs/promises";
227
+ import path4 from "path";
228
+ async function generateReadme(projectPath, projectConfig) {
229
+ const readme = `# ${projectConfig.projectName}
230
+
231
+ A RevealUI project created with @revealui/cli.
232
+
233
+ ## Getting Started
234
+
235
+ First, install dependencies:
236
+
237
+ \`\`\`bash
238
+ pnpm install
239
+ \`\`\`
240
+
241
+ Then, initialize the database:
242
+
243
+ \`\`\`bash
244
+ pnpm db:init
245
+ pnpm db:migrate
246
+ \`\`\`
247
+
248
+ Run the development server:
249
+
250
+ \`\`\`bash
251
+ pnpm dev
252
+ \`\`\`
253
+
254
+ Open [http://localhost:4000](http://localhost:4000) with your browser to access the CMS.
255
+
256
+ The web application runs on [http://localhost:3000](http://localhost:3000).
257
+
258
+ ## Development Environments
259
+
260
+ ### Standard Setup
261
+
262
+ Requirements:
263
+ - Node.js 24.12.0 or higher
264
+ - pnpm 10.28.2 or higher
265
+ - PostgreSQL 16
266
+
267
+ ### Dev Containers
268
+
269
+ Open in VS Code and select "Reopen in Container", or use GitHub Codespaces.
270
+
271
+ ### Devbox
272
+
273
+ Install Devbox:
274
+
275
+ \`\`\`bash
276
+ curl -fsSL https://get.jetpack.io/devbox | bash
277
+ \`\`\`
278
+
279
+ Then start the Devbox shell:
280
+
281
+ \`\`\`bash
282
+ devbox shell
283
+ pnpm dev
284
+ \`\`\`
285
+
286
+ ## Project Structure
287
+
288
+ \`\`\`
289
+ ${projectConfig.projectName}/
290
+ \u251C\u2500\u2500 apps/
291
+ \u2502 \u251C\u2500\u2500 cms/ # CMS application
292
+ \u2502 \u2514\u2500\u2500 web/ # Frontend application
293
+ \u251C\u2500\u2500 packages/
294
+ \u2502 \u251C\u2500\u2500 auth/ # Authentication
295
+ \u2502 \u251C\u2500\u2500 db/ # Database
296
+ \u2502 \u2514\u2500\u2500 ... # Other shared packages
297
+ \u251C\u2500\u2500 .devcontainer/ # Dev Container configuration
298
+ \u251C\u2500\u2500 devbox.json # Devbox configuration
299
+ \u2514\u2500\u2500 .env.development.local # Environment variables
300
+ \`\`\`
301
+
302
+ ## Available Scripts
303
+
304
+ - \`pnpm dev\` - Start development servers
305
+ - \`pnpm build\` - Build for production
306
+ - \`pnpm test\` - Run tests
307
+ - \`pnpm lint\` - Run linters
308
+ - \`pnpm typecheck\` - Type check
309
+ - \`pnpm db:init\` - Initialize database
310
+ - \`pnpm db:migrate\` - Run migrations
311
+ - \`pnpm db:seed\` - Seed database
312
+
313
+ ## Learn More
314
+
315
+ - [RevealUI Documentation](https://github.com/your-org/RevealUI)
316
+ - [Next.js Documentation](https://nextjs.org/docs)
317
+ - [Hono Documentation](https://hono.dev)
318
+
319
+ ## Template
320
+
321
+ This project was created using the **${projectConfig.template}** template.
322
+
323
+ ## License
324
+
325
+ MIT
326
+ `;
327
+ await fs3.writeFile(path4.join(projectPath, "README.md"), readme, "utf-8");
328
+ }
329
+ var init_readme = __esm({
330
+ "src/generators/readme.ts"() {
331
+ "use strict";
332
+ init_esm_shims();
333
+ }
334
+ });
335
+
336
+ // src/installers/dependencies.ts
337
+ import { createLogger as createLogger2 } from "@revealui/setup/utils";
338
+ import { execa } from "execa";
339
+ import ora from "ora";
340
+ async function installDependencies(projectPath) {
341
+ const spinner = ora("Installing dependencies with pnpm...").start();
342
+ try {
343
+ await execa("pnpm", ["install"], {
344
+ cwd: projectPath,
345
+ stdio: "pipe"
346
+ });
347
+ spinner.succeed("Dependencies installed successfully");
348
+ } catch (error) {
349
+ spinner.fail("Failed to install dependencies");
350
+ logger2.error('Please run "pnpm install" manually');
351
+ throw error;
352
+ }
353
+ }
354
+ async function isPnpmInstalled() {
355
+ try {
356
+ await execa("pnpm", ["--version"]);
357
+ return true;
358
+ } catch {
359
+ return false;
360
+ }
361
+ }
362
+ var logger2;
363
+ var init_dependencies = __esm({
364
+ "src/installers/dependencies.ts"() {
365
+ "use strict";
366
+ init_esm_shims();
367
+ logger2 = createLogger2({ prefix: "Install" });
368
+ }
369
+ });
370
+
371
+ // src/utils/git.ts
372
+ import { createLogger as createLogger3 } from "@revealui/setup/utils";
373
+ import { execa as execa2 } from "execa";
374
+ async function initializeGitRepo(projectPath) {
375
+ try {
376
+ await execa2("git", ["init"], { cwd: projectPath });
377
+ logger3.success("Initialized git repository");
378
+ } catch (error) {
379
+ logger3.warn("Failed to initialize git repository");
380
+ throw error;
381
+ }
382
+ }
383
+ async function createInitialCommit(projectPath) {
384
+ try {
385
+ await execa2("git", ["add", "."], { cwd: projectPath });
386
+ await execa2("git", ["commit", "-m", "Initial commit from @revealui/cli"], { cwd: projectPath });
387
+ logger3.success("Created initial commit");
388
+ } catch (error) {
389
+ logger3.warn("Failed to create initial commit");
390
+ throw error;
391
+ }
392
+ }
393
+ async function isGitInstalled() {
394
+ try {
395
+ await execa2("git", ["--version"]);
396
+ return true;
397
+ } catch {
398
+ return false;
399
+ }
400
+ }
401
+ var logger3;
402
+ var init_git = __esm({
403
+ "src/utils/git.ts"() {
404
+ "use strict";
405
+ init_esm_shims();
406
+ logger3 = createLogger3({ prefix: "Git" });
407
+ }
408
+ });
409
+
410
+ // src/commands/create.ts
411
+ import crypto from "crypto";
412
+ import fs4 from "fs/promises";
413
+ import path5 from "path";
414
+ import { fileURLToPath as fileURLToPath2 } from "url";
415
+ import { createLogger as createLogger4 } from "@revealui/setup/utils";
416
+ import ora2 from "ora";
417
+ function buildEnvLocal(cfg) {
418
+ const lines = [
419
+ "# Generated by @revealui/cli \u2014 fill in the remaining placeholders before running `pnpm dev`",
420
+ "",
421
+ "# Core",
422
+ `REVEALUI_SECRET=${generateSecret()}`,
423
+ `REVEALUI_PUBLIC_SERVER_URL=http://localhost:4000`,
424
+ `NEXT_PUBLIC_SERVER_URL=http://localhost:4000`,
425
+ "",
426
+ "# Database"
427
+ ];
428
+ if (cfg.database.postgresUrl) {
429
+ lines.push(`POSTGRES_URL=${cfg.database.postgresUrl}`);
430
+ } else {
431
+ lines.push("POSTGRES_URL=postgresql://postgres:postgres@localhost:5432/revealui");
432
+ }
433
+ lines.push("", "# Storage (Vercel Blob)");
434
+ if (cfg.storage.provider === "vercel-blob" && cfg.storage.blobToken) {
435
+ lines.push(`BLOB_READ_WRITE_TOKEN=${cfg.storage.blobToken}`);
436
+ } else {
437
+ lines.push("BLOB_READ_WRITE_TOKEN=vercel_blob_rw_placeholder");
438
+ }
439
+ lines.push("", "# Stripe");
440
+ if (cfg.payment.enabled && cfg.payment.stripeSecretKey) {
441
+ lines.push(`STRIPE_SECRET_KEY=${cfg.payment.stripeSecretKey}`);
442
+ lines.push(`STRIPE_WEBHOOK_SECRET=${cfg.payment.stripeWebhookSecret || "whsec_placeholder"}`);
443
+ lines.push(
444
+ `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=${cfg.payment.stripePublishableKey || "pk_test_placeholder"}`
445
+ );
446
+ } else {
447
+ lines.push("STRIPE_SECRET_KEY=sk_test_placeholder");
448
+ lines.push("STRIPE_WEBHOOK_SECRET=whsec_placeholder");
449
+ lines.push("NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_placeholder");
450
+ }
451
+ lines.push("", "# Admin bootstrap (used on first run only)");
452
+ lines.push("REVEALUI_ADMIN_EMAIL=admin@example.com");
453
+ lines.push("REVEALUI_ADMIN_PASSWORD=changeme-min-12-chars");
454
+ return `${lines.join("\n")}
455
+ `;
456
+ }
457
+ function generateSecret() {
458
+ return crypto.randomBytes(24).toString("hex");
459
+ }
460
+ async function copyTemplate(templateName, targetPath) {
461
+ const templatePath = path5.join(TEMPLATES_DIR, templateName);
462
+ try {
463
+ await fs4.access(templatePath);
464
+ } catch {
465
+ throw new Error(
466
+ `Template "${templateName}" not found at ${templatePath}. Available templates: minimal, basic-blog, e-commerce, portfolio`
467
+ );
468
+ }
469
+ await copyDir(templatePath, targetPath);
470
+ }
471
+ async function copyDir(src, dest) {
472
+ await fs4.mkdir(dest, { recursive: true });
473
+ const entries = await fs4.readdir(src, { withFileTypes: true });
474
+ for (const entry of entries) {
475
+ const srcPath = path5.join(src, entry.name);
476
+ const destName = entry.name === "_gitignore" ? ".gitignore" : entry.name;
477
+ const destPath = path5.join(dest, destName);
478
+ if (entry.isDirectory()) {
479
+ await copyDir(srcPath, destPath);
480
+ } else {
481
+ await fs4.copyFile(srcPath, destPath);
482
+ }
483
+ }
484
+ }
485
+ function resolveTemplateName(template) {
486
+ const map = {
487
+ "basic-blog": "minimal",
488
+ "e-commerce": "minimal",
489
+ portfolio: "minimal"
490
+ };
491
+ return map[template] ?? "minimal";
492
+ }
493
+ async function createProject(cfg) {
494
+ const { project, skipGit = false, skipInstall = false } = cfg;
495
+ const { projectPath, projectName, template } = project;
496
+ const spinner = ora2(`Copying template "${template}"...`).start();
497
+ try {
498
+ await copyTemplate(resolveTemplateName(template), projectPath);
499
+ spinner.succeed("Template files copied");
500
+ } catch (err) {
501
+ spinner.fail("Failed to copy template files");
502
+ throw err;
503
+ }
504
+ const pkgJsonPath = path5.join(projectPath, "package.json");
505
+ try {
506
+ const raw = await fs4.readFile(pkgJsonPath, "utf-8");
507
+ await fs4.writeFile(pkgJsonPath, raw.replaceAll("{{PROJECT_NAME}}", projectName), "utf-8");
508
+ } catch {
509
+ }
510
+ const envSpinner = ora2("Writing .env.local...").start();
511
+ try {
512
+ await fs4.writeFile(path5.join(projectPath, ".env.local"), buildEnvLocal(cfg), "utf-8");
513
+ envSpinner.succeed(".env.local written");
514
+ } catch (err) {
515
+ envSpinner.fail("Failed to write .env.local");
516
+ throw err;
517
+ }
518
+ await generateReadme(projectPath, project);
519
+ logger4.success("README.md generated");
520
+ if (cfg.devenv.createDevContainer) {
521
+ await generateDevContainer(projectPath);
522
+ logger4.success(".devcontainer/ generated");
523
+ }
524
+ if (cfg.devenv.createDevbox) {
525
+ await generateDevbox(projectPath);
526
+ logger4.success("devbox.json generated");
527
+ }
528
+ if (!skipInstall) {
529
+ const pnpmOk = await isPnpmInstalled();
530
+ if (!pnpmOk) {
531
+ logger4.warn("pnpm not found \u2014 skipping dependency installation. Run `pnpm install` manually.");
532
+ } else {
533
+ await installDependencies(projectPath);
534
+ }
535
+ } else {
536
+ logger4.info("Skipping dependency installation (--skip-install)");
537
+ }
538
+ if (!skipGit) {
539
+ const gitOk = await isGitInstalled();
540
+ if (!gitOk) {
541
+ logger4.warn("git not found \u2014 skipping repository initialisation.");
542
+ } else {
543
+ await initializeGitRepo(projectPath);
544
+ await createInitialCommit(projectPath);
545
+ }
546
+ } else {
547
+ logger4.info("Skipping git initialisation (--skip-git)");
548
+ }
549
+ }
550
+ var logger4, __filename2, __dirname2, TEMPLATES_DIR;
551
+ var init_create = __esm({
552
+ "src/commands/create.ts"() {
553
+ "use strict";
554
+ init_esm_shims();
555
+ init_devbox();
556
+ init_devcontainer();
557
+ init_readme();
558
+ init_dependencies();
559
+ init_git();
560
+ logger4 = createLogger4({ prefix: "Create" });
561
+ __filename2 = fileURLToPath2(import.meta.url);
562
+ __dirname2 = path5.dirname(__filename2);
563
+ TEMPLATES_DIR = path5.resolve(__dirname2, "../../templates");
564
+ }
565
+ });
566
+
567
+ // src/validators/credentials.ts
568
+ import { createLogger as createLogger5 } from "@revealui/setup/utils";
569
+ async function validateStripeKey(key) {
570
+ if (!(key.startsWith("sk_test_") || key.startsWith("sk_live_"))) {
571
+ return {
572
+ valid: false,
573
+ message: "Stripe key must start with sk_test_ or sk_live_"
574
+ };
575
+ }
576
+ return { valid: true };
577
+ }
578
+ async function validateNeonUrl(url) {
579
+ try {
580
+ const parsed = new URL(url);
581
+ if (!parsed.protocol.startsWith("postgres")) {
582
+ return {
583
+ valid: false,
584
+ message: "Database URL must use postgres:// or postgresql:// protocol"
585
+ };
586
+ }
587
+ return { valid: true };
588
+ } catch {
589
+ return {
590
+ valid: false,
591
+ message: "Invalid database URL format"
592
+ };
593
+ }
594
+ }
595
+ async function validateVercelToken(token) {
596
+ if (!token || token.length < 20) {
597
+ return {
598
+ valid: false,
599
+ message: "Vercel token appears invalid (too short)"
600
+ };
601
+ }
602
+ return { valid: true };
603
+ }
604
+ async function validateSupabaseUrl(url) {
605
+ try {
606
+ const parsed = new URL(url);
607
+ if (!parsed.hostname.includes("supabase")) {
608
+ logger5.warn("URL does not appear to be a Supabase URL");
609
+ }
610
+ return { valid: true };
611
+ } catch {
612
+ return {
613
+ valid: false,
614
+ message: "Invalid Supabase URL format"
615
+ };
616
+ }
617
+ }
618
+ var logger5;
619
+ var init_credentials = __esm({
620
+ "src/validators/credentials.ts"() {
621
+ "use strict";
622
+ init_esm_shims();
623
+ logger5 = createLogger5({ prefix: "Validator" });
624
+ }
625
+ });
626
+
627
+ // src/prompts/database.ts
628
+ import inquirer from "inquirer";
629
+ async function promptDatabaseConfig() {
630
+ const { provider } = await inquirer.prompt([
631
+ {
632
+ type: "list",
633
+ name: "provider",
634
+ message: "Which database provider would you like to use?",
635
+ choices: [
636
+ {
637
+ name: "NeonDB - Serverless PostgreSQL (recommended)",
638
+ value: "neon"
639
+ },
640
+ {
641
+ name: "Supabase - PostgreSQL with built-in features",
642
+ value: "supabase"
643
+ },
644
+ {
645
+ name: "Local PostgreSQL - Use existing local database",
646
+ value: "local"
647
+ },
648
+ {
649
+ name: "Skip - Configure later",
650
+ value: "skip"
651
+ }
652
+ ],
653
+ default: "neon"
654
+ }
655
+ ]);
656
+ if (provider === "skip") {
657
+ return { provider: "skip" };
658
+ }
659
+ if (provider === "local") {
660
+ const { postgresUrl: postgresUrl2 } = await inquirer.prompt([
661
+ {
662
+ type: "input",
663
+ name: "postgresUrl",
664
+ message: "Enter your PostgreSQL connection string:",
665
+ default: "postgresql://postgres:postgres@localhost:5432/revealui",
666
+ validate: async (input) => {
667
+ const result = await validateNeonUrl(input);
668
+ return result.valid ? true : result.message || "Invalid database URL";
669
+ }
670
+ }
671
+ ]);
672
+ return { provider: "local", postgresUrl: postgresUrl2 };
673
+ }
674
+ const { postgresUrl } = await inquirer.prompt([
675
+ {
676
+ type: "input",
677
+ name: "postgresUrl",
678
+ message: `Enter your ${provider === "neon" ? "Neon" : "Supabase"} database connection string:`,
679
+ validate: async (input) => {
680
+ if (!input || input.trim() === "") {
681
+ return "Database URL is required";
682
+ }
683
+ const result = await validateNeonUrl(input);
684
+ return result.valid ? true : result.message || "Invalid database URL";
685
+ }
686
+ }
687
+ ]);
688
+ return { provider, postgresUrl };
689
+ }
690
+ var init_database = __esm({
691
+ "src/prompts/database.ts"() {
692
+ "use strict";
693
+ init_esm_shims();
694
+ init_credentials();
695
+ }
696
+ });
697
+
698
+ // src/prompts/devenv.ts
699
+ import inquirer2 from "inquirer";
700
+ async function promptDevEnvConfig() {
701
+ const answers = await inquirer2.prompt([
702
+ {
703
+ type: "confirm",
704
+ name: "createDevContainer",
705
+ message: "Create Dev Container configuration for VS Code / GitHub Codespaces?",
706
+ default: true
707
+ },
708
+ {
709
+ type: "confirm",
710
+ name: "createDevbox",
711
+ message: "Create Devbox configuration for Nix-powered development?",
712
+ default: true
713
+ }
714
+ ]);
715
+ return answers;
716
+ }
717
+ var init_devenv = __esm({
718
+ "src/prompts/devenv.ts"() {
719
+ "use strict";
720
+ init_esm_shims();
721
+ }
722
+ });
723
+
724
+ // src/prompts/payments.ts
725
+ import inquirer3 from "inquirer";
726
+ async function promptPaymentConfig() {
727
+ const { enabled } = await inquirer3.prompt([
728
+ {
729
+ type: "confirm",
730
+ name: "enabled",
731
+ message: "Do you want to configure Stripe payments?",
732
+ default: true
733
+ }
734
+ ]);
735
+ if (!enabled) {
736
+ return { enabled: false };
737
+ }
738
+ const answers = await inquirer3.prompt([
739
+ {
740
+ type: "input",
741
+ name: "stripeSecretKey",
742
+ message: "Enter your Stripe secret key (sk_test_... or sk_live_...):",
743
+ validate: async (input) => {
744
+ if (!input || input.trim() === "") {
745
+ return "Stripe secret key is required";
746
+ }
747
+ const result = await validateStripeKey(input);
748
+ return result.valid ? true : result.message || "Invalid Stripe key";
749
+ }
750
+ },
751
+ {
752
+ type: "input",
753
+ name: "stripePublishableKey",
754
+ message: "Enter your Stripe publishable key (pk_test_... or pk_live_...):",
755
+ validate: (input) => {
756
+ if (!input || input.trim() === "") {
757
+ return "Stripe publishable key is required";
758
+ }
759
+ if (!(input.startsWith("pk_test_") || input.startsWith("pk_live_"))) {
760
+ return "Stripe publishable key must start with pk_test_ or pk_live_";
761
+ }
762
+ return true;
763
+ }
764
+ },
765
+ {
766
+ type: "input",
767
+ name: "stripeWebhookSecret",
768
+ message: "Enter your Stripe webhook secret (whsec_..., optional - press Enter to skip):",
769
+ default: ""
770
+ }
771
+ ]);
772
+ return {
773
+ enabled: true,
774
+ stripeSecretKey: answers.stripeSecretKey,
775
+ stripePublishableKey: answers.stripePublishableKey,
776
+ stripeWebhookSecret: answers.stripeWebhookSecret || void 0
777
+ };
778
+ }
779
+ var init_payments = __esm({
780
+ "src/prompts/payments.ts"() {
781
+ "use strict";
782
+ init_esm_shims();
783
+ init_credentials();
784
+ }
785
+ });
786
+
787
+ // src/prompts/project.ts
788
+ import fs5 from "fs";
789
+ import path6 from "path";
790
+ import inquirer4 from "inquirer";
791
+ async function promptProjectConfig(defaultName) {
792
+ const answers = await inquirer4.prompt([
793
+ {
794
+ type: "input",
795
+ name: "projectName",
796
+ message: "What is your project name?",
797
+ default: defaultName || "my-revealui-project",
798
+ validate: (input) => {
799
+ if (!/^[a-z0-9-]+$/.test(input)) {
800
+ return "Project name must contain only lowercase letters, numbers, and hyphens";
801
+ }
802
+ const projectPath = path6.resolve(process.cwd(), input);
803
+ if (fs5.existsSync(projectPath)) {
804
+ return `Directory "${input}" already exists`;
805
+ }
806
+ return true;
807
+ }
808
+ },
809
+ {
810
+ type: "list",
811
+ name: "template",
812
+ message: "Which template would you like to use?",
813
+ choices: [
814
+ {
815
+ name: "Basic Blog - A simple blog with posts and pages",
816
+ value: "basic-blog"
817
+ },
818
+ {
819
+ name: "E-commerce - Product catalog with checkout",
820
+ value: "e-commerce"
821
+ },
822
+ {
823
+ name: "Portfolio - Personal portfolio site",
824
+ value: "portfolio"
825
+ }
826
+ ],
827
+ default: "basic-blog"
828
+ }
829
+ ]);
830
+ return {
831
+ projectName: answers.projectName,
832
+ projectPath: path6.resolve(process.cwd(), answers.projectName),
833
+ template: answers.template
834
+ };
835
+ }
836
+ var init_project = __esm({
837
+ "src/prompts/project.ts"() {
838
+ "use strict";
839
+ init_esm_shims();
840
+ }
841
+ });
842
+
843
+ // src/prompts/storage.ts
844
+ import inquirer5 from "inquirer";
845
+ async function promptStorageConfig() {
846
+ const { provider } = await inquirer5.prompt([
847
+ {
848
+ type: "list",
849
+ name: "provider",
850
+ message: "Which storage provider would you like to use?",
851
+ choices: [
852
+ {
853
+ name: "Vercel Blob - Simple object storage (recommended)",
854
+ value: "vercel-blob"
855
+ },
856
+ {
857
+ name: "Supabase Storage - Integrated with Supabase",
858
+ value: "supabase"
859
+ },
860
+ {
861
+ name: "Skip - Configure later",
862
+ value: "skip"
863
+ }
864
+ ],
865
+ default: "vercel-blob"
866
+ }
867
+ ]);
868
+ if (provider === "skip") {
869
+ return { provider: "skip" };
870
+ }
871
+ if (provider === "vercel-blob") {
872
+ const { blobToken } = await inquirer5.prompt([
873
+ {
874
+ type: "input",
875
+ name: "blobToken",
876
+ message: "Enter your Vercel Blob read/write token:",
877
+ validate: async (input) => {
878
+ if (!input || input.trim() === "") {
879
+ return "Blob token is required";
880
+ }
881
+ const result = await validateVercelToken(input);
882
+ return result.valid ? true : result.message || "Invalid token";
883
+ }
884
+ }
885
+ ]);
886
+ return { provider: "vercel-blob", blobToken };
887
+ }
888
+ const answers = await inquirer5.prompt([
889
+ {
890
+ type: "input",
891
+ name: "supabaseUrl",
892
+ message: "Enter your Supabase project URL:",
893
+ validate: async (input) => {
894
+ if (!input || input.trim() === "") {
895
+ return "Supabase URL is required";
896
+ }
897
+ const result = await validateSupabaseUrl(input);
898
+ return result.valid ? true : result.message || "Invalid URL";
899
+ }
900
+ },
901
+ {
902
+ type: "input",
903
+ name: "supabaseAnonKey",
904
+ message: "Enter your Supabase anonymous key:",
905
+ validate: (input) => {
906
+ if (!input || input.trim() === "") {
907
+ return "Supabase anonymous key is required";
908
+ }
909
+ return true;
910
+ }
911
+ }
912
+ ]);
913
+ return {
914
+ provider: "supabase",
915
+ supabaseUrl: answers.supabaseUrl,
916
+ supabaseAnonKey: answers.supabaseAnonKey
917
+ };
918
+ }
919
+ var init_storage = __esm({
920
+ "src/prompts/storage.ts"() {
921
+ "use strict";
922
+ init_esm_shims();
923
+ init_credentials();
924
+ }
925
+ });
926
+
927
+ // src/index.ts
928
+ var index_exports = {};
929
+ __export(index_exports, {
930
+ run: () => run
931
+ });
932
+ import { readFileSync } from "fs";
933
+ import { homedir } from "os";
934
+ import { join } from "path";
935
+ import { createLogger as createLogger6 } from "@revealui/setup/utils";
936
+ function checkProLicense() {
937
+ let key = process.env.REVEALUI_LICENSE_KEY;
938
+ if (!key) {
939
+ try {
940
+ const licenseFile = join(homedir(), ".revealui", "license.json");
941
+ const parsed = JSON.parse(readFileSync(licenseFile, "utf8"));
942
+ key = parsed.key;
943
+ } catch {
944
+ }
945
+ }
946
+ if (!key) return false;
947
+ try {
948
+ const parts = key.split(".");
949
+ if (parts.length < 2) return false;
950
+ const payload = JSON.parse(Buffer.from(parts[1] ?? "", "base64").toString("utf8"));
951
+ const tier = payload.tier ?? "free";
952
+ return tier === "pro" || tier === "enterprise";
953
+ } catch {
954
+ return false;
955
+ }
956
+ }
957
+ async function run(projectName, _options) {
958
+ try {
959
+ logger6.info("[1/8] Validating Node.js version...");
960
+ if (!validateNodeVersion()) {
961
+ process.exit(1);
962
+ }
963
+ logger6.success(`Node.js version: ${process.version}`);
964
+ logger6.info("[2/8] Configure your project");
965
+ const projectConfig = await promptProjectConfig(projectName);
966
+ logger6.success(`Project: ${projectConfig.projectName}`);
967
+ logger6.success(`Template: ${projectConfig.template}`);
968
+ if (PRO_TEMPLATES.has(projectConfig.template)) {
969
+ if (!checkProLicense()) {
970
+ logger6.error(`The "${projectConfig.template}" template requires a RevealUI Pro license.`);
971
+ logger6.info("Get Pro at https://revealui.com/pricing");
972
+ logger6.info("Set your license key: export REVEALUI_LICENSE_KEY=<your-key>");
973
+ process.exit(2);
974
+ }
975
+ logger6.success("Pro license verified");
976
+ }
977
+ logger6.info("[3/8] Configure database");
978
+ const databaseConfig = await promptDatabaseConfig();
979
+ if (databaseConfig.provider !== "skip") {
980
+ logger6.success(`Database: ${databaseConfig.provider}`);
981
+ } else {
982
+ logger6.info("Database configuration skipped");
983
+ }
984
+ logger6.info("[4/8] Configure storage");
985
+ const storageConfig = await promptStorageConfig();
986
+ if (storageConfig.provider !== "skip") {
987
+ logger6.success(`Storage: ${storageConfig.provider}`);
988
+ } else {
989
+ logger6.info("Storage configuration skipped");
990
+ }
991
+ logger6.info("[5/8] Configure payments");
992
+ const paymentConfig = await promptPaymentConfig();
993
+ if (paymentConfig.enabled) {
994
+ logger6.success("Stripe configured");
995
+ } else {
996
+ logger6.info("Payments disabled");
997
+ }
998
+ logger6.info("[6/8] Configure development environment");
999
+ const devEnvConfig = await promptDevEnvConfig();
1000
+ logger6.success(
1001
+ `Dev Container: ${devEnvConfig.createDevContainer ? "Yes" : "No"}, Devbox: ${devEnvConfig.createDevbox ? "Yes" : "No"}`
1002
+ );
1003
+ logger6.info("[7/8] Creating project...");
1004
+ await createProject({
1005
+ project: projectConfig,
1006
+ database: databaseConfig,
1007
+ storage: storageConfig,
1008
+ payment: paymentConfig,
1009
+ devenv: devEnvConfig,
1010
+ skipGit: _options.skipGit,
1011
+ skipInstall: _options.skipInstall
1012
+ });
1013
+ logger6.success("Project created successfully");
1014
+ logger6.info("[8/8] Next steps");
1015
+ logger6.divider();
1016
+ logger6.info(`cd ${projectConfig.projectName}`);
1017
+ logger6.info("pnpm install");
1018
+ logger6.info("pnpm dev");
1019
+ logger6.divider();
1020
+ logger6.success(`\u{1F389} Project ${projectConfig.projectName} created successfully!`);
1021
+ } catch (error) {
1022
+ if (error instanceof Error) {
1023
+ logger6.error(`Failed to create project: ${error.message}`);
1024
+ } else {
1025
+ logger6.error("An unexpected error occurred");
1026
+ }
1027
+ process.exit(1);
1028
+ }
1029
+ }
1030
+ var logger6, PRO_TEMPLATES;
1031
+ var init_index = __esm({
1032
+ "src/index.ts"() {
1033
+ "use strict";
1034
+ init_esm_shims();
1035
+ init_cli();
1036
+ init_node_version();
1037
+ init_create();
1038
+ init_database();
1039
+ init_devenv();
1040
+ init_payments();
1041
+ init_project();
1042
+ init_storage();
1043
+ logger6 = createLogger6({ prefix: "@revealui/cli" });
1044
+ PRO_TEMPLATES = /* @__PURE__ */ new Set(["e-commerce", "portfolio"]);
1045
+ if (import.meta.url === `file://${process.argv[1]}`) {
1046
+ const cli = createCli();
1047
+ cli.parse(process.argv);
1048
+ }
1049
+ }
1050
+ });
1051
+
1052
+ // src/cli.ts
1053
+ import { createLogger as createLogger7 } from "@revealui/setup/utils";
1054
+ import { Command } from "commander";
1055
+ function createCli() {
1056
+ const program = new Command();
1057
+ program.name("create-revealui").description("Create a new RevealUI project").version("0.1.0").argument("[project-name]", "Name of the project").option("-t, --template <name>", "Template to use (basic-blog, e-commerce, portfolio)").option("--skip-git", "Skip git initialization", false).option("--skip-install", "Skip dependency installation", false).action(async (projectName, options) => {
1058
+ logger7.header("\u{1F680} Create RevealUI Project");
1059
+ const { run: run2 } = await Promise.resolve().then(() => (init_index(), index_exports));
1060
+ await run2(projectName, options);
1061
+ });
1062
+ return program;
1063
+ }
1064
+ var logger7;
1065
+ var init_cli = __esm({
1066
+ "src/cli.ts"() {
1067
+ init_esm_shims();
1068
+ logger7 = createLogger7({ prefix: "CLI" });
1069
+ }
1070
+ });
1071
+ init_cli();
1072
+ export {
1073
+ createCli
1074
+ };
1075
+ //# sourceMappingURL=cli.js.map