@teamvelix/cli 5.1.5 → 5.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,31 +1,12 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ VERSION,
4
+ log,
5
+ showBanner
6
+ } from "./chunk-NP7HMX6A.js";
2
7
 
3
8
  // index.ts
4
- import fs from "fs";
5
- import path from "path";
6
- import { fileURLToPath } from "url";
7
9
  import pc from "picocolors";
8
- import prompts from "prompts";
9
-
10
- // version.ts
11
- var VERSION = "5.1.5";
12
-
13
- // index.ts
14
- var __filename = fileURLToPath(import.meta.url);
15
- var __dirname = path.dirname(__filename);
16
- var log = {
17
- info: (msg) => console.log(` ${pc.cyan("\u2139")} ${msg}`),
18
- success: (msg) => console.log(` ${pc.green("\u2714")} ${msg}`),
19
- warn: (msg) => console.log(` ${pc.yellow("\u26A0")} ${pc.yellow(msg)}`),
20
- error: (msg) => console.log(` ${pc.red("\u2716")} ${pc.red(msg)}`),
21
- blank: () => console.log("")
22
- };
23
- function showBanner() {
24
- console.log("");
25
- console.log(` ${pc.cyan("\u25B2")} ${pc.bold("Velix")} ${pc.dim(`v${VERSION}`)}`);
26
- console.log(` ${pc.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}`);
27
- console.log("");
28
- }
29
10
  function showHelp() {
30
11
  showBanner();
31
12
  console.log(` ${pc.bold("Usage:")} velix <command> [options]`);
@@ -63,779 +44,58 @@ async function main() {
63
44
  return;
64
45
  }
65
46
  switch (command) {
66
- case "create":
67
- await createProject(args[1]);
47
+ case "create": {
48
+ const { createCommand } = await import("./create-OVXR7Q47.js");
49
+ await createCommand(args[1]);
68
50
  break;
69
- case "dev":
70
- await startDev();
51
+ }
52
+ case "dev": {
53
+ const { devCommand } = await import("./dev-H7P7YCEP.js");
54
+ await devCommand();
71
55
  break;
72
- case "build":
73
- await buildProject();
56
+ }
57
+ case "build": {
58
+ const { buildCommand } = await import("./build-ODNAFOEX.js");
59
+ await buildCommand();
74
60
  break;
75
- case "start":
76
- await startProd();
61
+ }
62
+ case "start": {
63
+ const { startCommand } = await import("./build-ODNAFOEX.js");
64
+ await startCommand();
77
65
  break;
66
+ }
78
67
  case "g":
79
- case "generate":
80
- await generate(args[1], args[2]);
68
+ case "generate": {
69
+ const { generateCommand } = await import("./generate-5KBJNW3M.js");
70
+ await generateCommand(args[1], args[2]);
81
71
  break;
82
- case "doctor":
83
- await doctor();
72
+ }
73
+ case "doctor": {
74
+ const { doctorCommand } = await import("./doctor-YYTH474N.js");
75
+ await doctorCommand();
84
76
  break;
85
- case "info":
86
- await info();
77
+ }
78
+ case "info": {
79
+ const { infoCommand } = await import("./doctor-YYTH474N.js");
80
+ await infoCommand();
87
81
  break;
82
+ }
88
83
  case "analyze":
89
84
  log.info("Bundle analysis coming soon...");
90
85
  break;
91
- case "ui":
86
+ case "ui": {
87
+ const { handleUiCommand } = await import("./ui-IROXYLW2.js");
92
88
  await handleUiCommand(args.slice(1));
93
89
  break;
90
+ }
94
91
  default:
95
92
  log.error(`Unknown command: ${command}`);
96
93
  showHelp();
97
94
  process.exit(1);
98
95
  }
99
96
  }
100
- async function createProject(name) {
101
- showBanner();
102
- if (!name) {
103
- const response = await prompts({
104
- type: "text",
105
- name: "name",
106
- message: "Project name:",
107
- initial: "my-velix-app"
108
- });
109
- name = response.name;
110
- if (!name) {
111
- log.error("Project name is required");
112
- process.exit(1);
113
- }
114
- }
115
- const projectDir = path.resolve(process.cwd(), name);
116
- if (fs.existsSync(projectDir)) {
117
- log.error(`Directory ${name} already exists`);
118
- process.exit(1);
119
- }
120
- const flags = process.argv.slice(3);
121
- const templateFlag = flags.find((a) => a.startsWith("--template="))?.split("=")[1];
122
- const tailwindFlag = flags.includes("--tailwind");
123
- const noTailwindFlag = flags.includes("--no-tailwind");
124
- let template = templateFlag;
125
- let useTailwind = tailwindFlag ? true : noTailwindFlag ? false : void 0;
126
- if (!template) {
127
- const response = await prompts({
128
- type: "select",
129
- name: "template",
130
- message: "Select a template:",
131
- choices: [
132
- { title: "\u2728 Default - Full Velix app with examples", value: "default" },
133
- { title: "\u26A1 Minimal", value: "minimal" }
134
- ]
135
- });
136
- template = response.template;
137
- }
138
- if (!template) {
139
- log.error("No template selected");
140
- process.exit(1);
141
- }
142
- if (useTailwind === void 0) {
143
- const twResponse = await prompts({
144
- type: "confirm",
145
- name: "useTailwind",
146
- message: "Use Tailwind CSS?",
147
- initial: true
148
- });
149
- useTailwind = twResponse.useTailwind;
150
- }
151
- let useShadcn = flags.includes("--shadcn") ? true : flags.includes("--no-shadcn") ? false : void 0;
152
- if (useTailwind && useShadcn === void 0) {
153
- const shResponse = await prompts({
154
- type: "confirm",
155
- name: "useShadcn",
156
- message: "Use Shadcn UI components?",
157
- initial: true
158
- });
159
- useShadcn = shResponse.useShadcn;
160
- }
161
- const { default: ora } = await import("ora");
162
- const spinner = ora("Creating project...").start();
163
- try {
164
- fs.mkdirSync(projectDir, { recursive: true });
165
- generateProjectFiles(projectDir, name, template, useTailwind, useShadcn);
166
- spinner.succeed(`Project ${pc.bold(name)} created!`);
167
- log.blank();
168
- console.log(` ${pc.bold("Next steps:")}`);
169
- console.log(` ${pc.dim("$")} cd ${name}`);
170
- console.log(` ${pc.dim("$")} npm install`);
171
- console.log(` ${pc.dim("$")} npm run dev`);
172
- log.blank();
173
- } catch (err) {
174
- spinner.fail("Failed to create project");
175
- log.error(err.message);
176
- process.exit(1);
177
- }
178
- }
179
- function generateProjectFiles(dir, name, template, useTailwind = true, useShadcn = false) {
180
- const pkg = {
181
- name,
182
- version: "0.1.0",
183
- private: true,
184
- type: "module",
185
- scripts: {
186
- dev: "velix dev",
187
- build: "velix build",
188
- start: "velix start"
189
- },
190
- dependencies: {
191
- "@teamvelix/velix": `^${VERSION}`,
192
- react: "^19.0.0",
193
- "react-dom": "^19.0.0"
194
- },
195
- devDependencies: {
196
- "@teamvelix/cli": `^${VERSION}`,
197
- typescript: "^5.7.0",
198
- "@types/react": "^19.0.0",
199
- "@types/react-dom": "^19.0.0"
200
- }
201
- };
202
- if (useTailwind) {
203
- pkg.devDependencies = {
204
- ...pkg.devDependencies,
205
- "tailwindcss": "^4.0.0",
206
- "@tailwindcss/cli": "^4.0.0"
207
- };
208
- }
209
- if (useShadcn) {
210
- pkg.dependencies = {
211
- ...pkg.dependencies,
212
- "clsx": "^2.1.0",
213
- "tailwind-merge": "^2.2.1",
214
- "lucide-react": "^0.359.0"
215
- };
216
- }
217
- writeFile(path.join(dir, "package.json"), JSON.stringify(pkg, null, 2));
218
- writeFile(path.join(dir, "velix.config.ts"), `import { defineConfig${useTailwind ? ", tailwindPlugin" : ""} } from "@teamvelix/velix";
219
-
220
- export default defineConfig({
221
- app: {
222
- name: "${name}",
223
- },
224
- server: {
225
- port: 3000,
226
- host: "localhost",
227
- },
228
- seo: {
229
- sitemap: true,
230
- robots: true,
231
- openGraph: true,
232
- },
233
- favicon: "/favicon.webp",
234
- ${useTailwind ? `plugins: [
235
- tailwindPlugin()
236
- ],` : "plugins: [],"}
237
- });
238
- `);
239
- writeFile(path.join(dir, "tsconfig.json"), JSON.stringify({
240
- compilerOptions: {
241
- target: "ES2022",
242
- module: "ESNext",
243
- moduleResolution: "bundler",
244
- jsx: "react-jsx",
245
- strict: true,
246
- esModuleInterop: true,
247
- skipLibCheck: true,
248
- forceConsistentCasingInFileNames: true
249
- },
250
- include: ["app/**/*.ts", "app/**/*.tsx", "server/**/*.ts"],
251
- exclude: ["node_modules", ".velix"]
252
- }, null, 2));
253
- if (useTailwind) {
254
- writeFile(path.join(dir, "tailwind.config.ts"), `import type { Config } from "tailwindcss";
255
-
256
- export default {
257
- content: [
258
- "./index.html",
259
- "./app/**/*.{js,ts,jsx,tsx}",
260
- "./components/**/*.{js,ts,jsx,tsx}",
261
- "./lib/**/*.{js,ts,jsx,tsx}",
262
- "./src/**/*.{js,ts,jsx,tsx}",
263
- ],
264
- } satisfies Config;
265
- `);
266
- }
267
- fs.mkdirSync(path.join(dir, "app"), { recursive: true });
268
- writeFile(path.join(dir, "app", "globals.css"), useTailwind ? `@import "tailwindcss";
269
-
270
- @theme {
271
- --color-velix-deep: #0B1120;
272
- --color-velix-dark: #0F172A;
273
- --color-velix-accent: #2563EB;
274
- --color-velix-cyan: #22D3EE;
275
- --color-velix-glow: #38BDF8;
276
- }
277
- ` : `body { margin: 0; font-family: sans-serif; }
278
- `);
279
- writeFile(path.join(dir, "app", "layout.tsx"), `import "./globals.css";
280
-
281
- export const metadata = {
282
- title: "${name}",
283
- description: "Built with Velix v5",
284
- };
285
-
286
- export default function RootLayout({ children }: { children: React.ReactNode }) {
287
- return (
288
- <html lang="en">
289
- <body className="${useTailwind ? "bg-velix-deep text-slate-100" : "bg-slate-900 text-white"} min-h-screen font-sans antialiased">{children}</body>
290
- </html>
291
- );
292
- }
293
- `);
294
- if (template === "minimal") {
295
- writeFile(path.join(dir, "app", "page.tsx"), `export const metadata = {
296
- title: "${name}",
297
- };
298
-
299
- export default function MinimalPage() {
300
- return (
301
- <main className="min-h-screen flex flex-col items-center justify-center bg-[#0F172A] text-slate-100 font-sans">
302
- <h1 className="text-4xl font-bold tracking-tight text-white mb-2">Velix</h1>
303
- <p className="text-slate-400">Minimal starter.</p>
304
- </main>
305
- );
306
- }
307
- `);
308
- } else {
309
- fs.mkdirSync(path.join(dir, "components", "ui"), { recursive: true });
310
- let buttonCode = `import React from 'react';
311
-
312
- export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
313
- variant?: 'primary' | 'secondary';
314
- }
315
-
316
- export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
317
- ({ className = '', variant = 'primary', ...props }, ref) => {
318
- const base = "inline-flex flex-row gap-2 items-center justify-center font-medium transition-all duration-300 h-12 rounded-xl px-8 focus:outline-none focus:ring-2 focus:ring-velix-cyan/50";
319
- const variants = {
320
- primary: "bg-gradient-to-r from-velix-accent to-velix-cyan text-white shadow-[0_0_20px_rgba(34,211,238,0.25)] hover:shadow-[0_0_30px_rgba(34,211,238,0.45)]",
321
- secondary: "bg-white/5 text-slate-200 border border-white/10 hover:bg-white/10 hover:border-velix-cyan/30"
322
- };
323
- return <button ref={ref} className={\`\${base} \${variants[variant]} \${className}\`} {...props} />;
324
- }
325
- );
326
- Button.displayName = "Button";
327
- `;
328
- if (useShadcn) {
329
- fs.mkdirSync(path.join(dir, "lib"), { recursive: true });
330
- writeFile(path.join(dir, "lib", "utils.ts"), `import { clsx, type ClassValue } from "clsx";
331
- import { twMerge } from "tailwind-merge";
332
-
333
- export function cn(...inputs: ClassValue[]) {
334
- return twMerge(clsx(inputs));
335
- }
336
- `);
337
- buttonCode = `import * as React from "react";
338
- import { cn } from "../../lib/utils";
339
-
340
- export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
341
- variant?: 'primary' | 'secondary';
342
- }
343
-
344
- export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
345
- ({ className, variant = 'primary', ...props }, ref) => {
346
- const classes = cn(
347
- "inline-flex flex-row gap-2 items-center justify-center font-medium transition-all duration-300 h-12 rounded-xl px-8 focus:outline-none focus:ring-2 focus:ring-velix-cyan/50",
348
- variant === 'primary' ? "bg-gradient-to-r from-velix-accent to-velix-cyan text-white shadow-[0_0_20px_rgba(34,211,238,0.25)] hover:shadow-[0_0_30px_rgba(34,211,238,0.45)]" : "bg-white/5 text-slate-200 border border-white/10 hover:bg-white/10 hover:border-velix-cyan/30",
349
- className
350
- );
351
- return <button className={classes} ref={ref} {...props} />;
352
- }
353
- );
354
- Button.displayName = "Button";
355
- `;
356
- }
357
- writeFile(path.join(dir, "components", "ui", "button.tsx"), buttonCode);
358
- const cardClasses = "group relative p-8 bg-velix-dark/60 border border-white/5 rounded-2xl hover:border-velix-cyan/20 transition-colors duration-300 overflow-hidden";
359
- const cardGradient = "absolute inset-0 bg-gradient-to-br from-velix-accent/0 to-velix-cyan/0 group-hover:from-velix-accent/5 group-hover:to-velix-cyan/5 transition-all duration-500";
360
- let cardCode;
361
- if (useShadcn) {
362
- cardCode = [
363
- 'import React from "react";',
364
- 'import { cn } from "../../lib/utils";',
365
- "",
366
- "export function Card({ title, description, className = '' }: { title: string; description: string; className?: string }) {",
367
- " return (",
368
- ' <div className={cn("' + cardClasses + '", className)}>',
369
- ' <div className="' + cardGradient + '"></div>',
370
- ' <h3 className="text-xl font-semibold text-slate-100 mb-3 relative z-10">{title}</h3>',
371
- ' <p className="text-sm text-slate-400 leading-relaxed relative z-10">{description}</p>',
372
- " </div>",
373
- " );",
374
- "}",
375
- ""
376
- ].join("\n");
377
- } else {
378
- cardCode = [
379
- 'import React from "react";',
380
- "",
381
- "export function Card({ title, description, className = '' }: { title: string; description: string; className?: string }) {",
382
- " return (",
383
- ' <div className={"' + cardClasses + ' " + className}>',
384
- ' <div className="' + cardGradient + '"></div>',
385
- ' <h3 className="text-xl font-semibold text-slate-100 mb-3 relative z-10">{title}</h3>',
386
- ' <p className="text-sm text-slate-400 leading-relaxed relative z-10">{description}</p>',
387
- " </div>",
388
- " );",
389
- "}",
390
- ""
391
- ].join("\n");
392
- }
393
- writeFile(path.join(dir, "components", "ui", "card.tsx"), cardCode);
394
- writeFile(path.join(dir, "app", "page.tsx"), `import { Button } from "../components/ui/button";
395
- import { Card } from "../components/ui/card";
396
-
397
- export const metadata = {
398
- title: "Welcome to Velix",
399
- description: "Build fast. Ship faster.",
400
- };
401
-
402
- export default function HomePage() {
403
- return (
404
- <main className="min-h-screen flex flex-col items-center justify-center p-8 bg-gradient-to-b from-[#0B1628] via-velix-dark to-velix-deep text-slate-100 font-sans relative overflow-hidden">
405
- {/* Background glow effects */}
406
- <div className="absolute top-1/4 left-1/3 w-[500px] h-[500px] bg-velix-accent/15 rounded-full blur-[140px] pointer-events-none"></div>
407
- <div className="absolute bottom-1/4 right-1/4 w-[400px] h-[400px] bg-velix-cyan/10 rounded-full blur-[120px] pointer-events-none"></div>
408
-
409
- <div className="z-10 flex flex-col items-center max-w-5xl w-full text-center mt-12 mb-auto">
410
- <div className="mb-10 w-24 h-24 bg-gradient-to-br from-velix-accent to-velix-cyan rounded-2xl shadow-[0_0_50px_rgba(34,211,238,0.3)] flex items-center justify-center relative group">
411
- <div className="absolute inset-0 bg-velix-cyan/20 rounded-2xl blur-xl group-hover:blur-2xl transition-all duration-500"></div>
412
- <span className="text-5xl font-black text-white relative z-10 tracking-tighter">V</span>
413
- </div>
414
-
415
- <h1 className="text-5xl md:text-7xl font-extrabold mb-6 tracking-tight">
416
- <span className="bg-clip-text text-transparent bg-gradient-to-r from-white to-slate-400">Welcome to</span>{" "}
417
- <span className="bg-clip-text text-transparent bg-gradient-to-r from-velix-cyan via-velix-glow to-velix-accent">Velix</span>
418
- </h1>
419
-
420
- <p className="text-xl md:text-2xl text-slate-400 mb-12 tracking-wide font-light">
421
- Build fast. Ship faster.
422
- </p>
423
-
424
- <div className="flex flex-col sm:flex-row gap-5 mb-24 w-full sm:w-auto">
425
- <a href="https://github.com/Velixteam/velix" target="_blank" rel="noreferrer" className="w-full sm:w-auto">
426
- <Button variant="primary" className="w-full">
427
- Get Started
428
- </Button>
429
- </a>
430
- <a href="https://teamvelix.vercel.app" target="_blank" rel="noreferrer" className="w-full sm:w-auto">
431
- <Button variant="secondary" className="w-full">
432
- Documentation
433
- </Button>
434
- </a>
435
- </div>
436
-
437
- <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 w-full text-left">
438
- <Card title="Routing" description="File-system based routing that feels instantly familiar and snappy." />
439
- <Card title="Actions" description="Type-safe server actions mapped seamlessly directly to your client." />
440
- <Card title="Plugins" description="Extend the framework capabilities with a simple yet powerful API." />
441
- <Card title="Deployment" description="Deploy to any cloud provider or serverless edge with zero config." />
442
- </div>
443
- </div>
444
-
445
- <div className="mt-16 pb-8 text-slate-600 text-sm tracking-widest uppercase font-mono">
446
- Velix &copy; ${(/* @__PURE__ */ new Date()).getFullYear()}
447
- </div>
448
- </main>
449
- );
450
- }
451
- `);
452
- }
453
- if (template !== "minimal") {
454
- fs.mkdirSync(path.join(dir, "server", "api"), { recursive: true });
455
- writeFile(path.join(dir, "server", "api", "hello.ts"), `export function GET() {
456
- return Response.json({ message: "Hello from Velix API!" });
457
- }
458
-
459
- export function POST(request: any) {
460
- return Response.json({ received: true });
461
- }
462
- `);
463
- fs.mkdirSync(path.join(dir, "public"), { recursive: true });
464
- }
465
- const logoSrc = path.join(__dirname, "..", "assets", "logo.webp");
466
- if (fs.existsSync(logoSrc)) {
467
- fs.mkdirSync(path.join(dir, "public"), { recursive: true });
468
- fs.copyFileSync(logoSrc, path.join(dir, "public", "favicon.webp"));
469
- }
470
- }
471
- async function startDev() {
472
- showBanner();
473
- log.info("Starting development server...");
474
- const { spawn } = await import("child_process");
475
- const cwd = process.cwd();
476
- const candidates = [
477
- path.join(cwd, "node_modules", "@teamvelix", "velix", "dist", "runtime", "start-dev.js"),
478
- path.join(cwd, "packages", "velix", "dist", "runtime", "start-dev.js"),
479
- path.join(cwd, "packages", "velix", "runtime", "start-dev.ts")
480
- ];
481
- const devScript = candidates.find((c) => fs.existsSync(c));
482
- if (!devScript) {
483
- log.error("Could not find Velix runtime. Run `npm install` first.");
484
- process.exit(1);
485
- }
486
- const child = spawn(`npx tsx --no-cache "${devScript}"`, {
487
- stdio: "inherit",
488
- cwd,
489
- shell: true
490
- });
491
- child.on("error", (err) => {
492
- log.error(`Failed to start dev server: ${err.message}`);
493
- process.exit(1);
494
- });
495
- }
496
- async function buildProject() {
497
- showBanner();
498
- log.info("Building for production...");
499
- const { spawn } = await import("child_process");
500
- const cwd = process.cwd();
501
- const candidates = [
502
- path.join(cwd, "node_modules", "@teamvelix", "velix", "dist", "runtime", "start-build.js"),
503
- path.join(cwd, "packages", "velix", "dist", "runtime", "start-build.js"),
504
- path.join(cwd, "packages", "velix", "runtime", "start-build.ts")
505
- ];
506
- const buildScript = candidates.find((c) => fs.existsSync(c));
507
- if (!buildScript) {
508
- log.error("Could not find Velix runtime. Run `npm install` first.");
509
- process.exit(1);
510
- }
511
- const child = spawn(`npx tsx "${buildScript}"`, {
512
- stdio: "inherit",
513
- cwd,
514
- shell: true
515
- });
516
- child.on("error", (err) => {
517
- log.error(`Failed to build: ${err.message}`);
518
- process.exit(1);
519
- });
520
- }
521
- async function startProd() {
522
- showBanner();
523
- log.info("Starting production server...");
524
- const { spawn } = await import("child_process");
525
- const cwd = process.cwd();
526
- const candidates = [
527
- path.join(cwd, "node_modules", "@teamvelix", "velix", "dist", "runtime", "start-prod.js"),
528
- path.join(cwd, "packages", "velix", "dist", "runtime", "start-prod.js"),
529
- path.join(cwd, "packages", "velix", "runtime", "start-prod.ts")
530
- ];
531
- const prodScript = candidates.find((c) => fs.existsSync(c));
532
- if (!prodScript) {
533
- log.error("Could not find Velix runtime. Run `npm install` first.");
534
- process.exit(1);
535
- }
536
- const child = spawn(`npx tsx "${prodScript}"`, {
537
- stdio: "inherit",
538
- cwd,
539
- shell: true
540
- });
541
- child.on("error", (err) => {
542
- log.error(`Failed to start production server: ${err.message}`);
543
- process.exit(1);
544
- });
545
- }
546
- async function doctor() {
547
- showBanner();
548
- console.log(` ${pc.bold("Velix Doctor")}`);
549
- log.blank();
550
- const checks = [
551
- { name: "Node.js version", check: () => {
552
- const v = parseInt(process.version.slice(1));
553
- return v >= 18 ? "\u2714" : "\u2716";
554
- }, info: process.version },
555
- { name: "velix.config.ts", check: () => fs.existsSync("velix.config.ts") || fs.existsSync("velix.config.js") ? "\u2714" : "\u2716", info: "" },
556
- { name: "app/ directory", check: () => fs.existsSync("app") ? "\u2714" : "\u2716", info: "" },
557
- { name: "package.json", check: () => fs.existsSync("package.json") ? "\u2714" : "\u2716", info: "" },
558
- { name: "tsconfig.json", check: () => fs.existsSync("tsconfig.json") ? "\u2714" : "\u2716", info: "" },
559
- { name: "node_modules", check: () => fs.existsSync("node_modules") ? "\u2714" : "\u26A0 Run npm install", info: "" }
560
- ];
561
- for (const { name, check, info: info2 } of checks) {
562
- const result = check();
563
- const icon = result === "\u2714" ? pc.green("\u2714") : result.startsWith("\u2716") ? pc.red("\u2716") : pc.yellow("\u26A0");
564
- const infoStr = info2 ? ` ${pc.dim(info2)}` : result.length > 1 ? ` ${pc.yellow(result.slice(2))}` : "";
565
- console.log(` ${icon} ${name}${infoStr}`);
566
- }
567
- log.blank();
568
- }
569
- async function info() {
570
- showBanner();
571
- console.log(` ${pc.bold("Environment:")}`);
572
- console.log(` Velix: ${pc.cyan(`v${VERSION}`)}`);
573
- console.log(` Node: ${pc.dim(process.version)}`);
574
- console.log(` Platform: ${pc.dim(process.platform)}`);
575
- console.log(` Arch: ${pc.dim(process.arch)}`);
576
- console.log(` CWD: ${pc.dim(process.cwd())}`);
577
- log.blank();
578
- }
579
- async function generate(type, name) {
580
- const validTypes = ["page", "layout", "component", "hook", "api", "action", "middleware", "context", "loading", "error", "not-found"];
581
- if (!type) {
582
- const { type: selectedType } = await prompts({
583
- type: "select",
584
- name: "type",
585
- message: "What do you want to generate?",
586
- choices: validTypes.map((t) => ({ title: t, value: t }))
587
- });
588
- type = selectedType;
589
- if (!type) process.exit(0);
590
- }
591
- if (!validTypes.includes(type)) {
592
- log.error(`Invalid type: ${type}. Valid: ${validTypes.join(", ")}`);
593
- process.exit(1);
594
- }
595
- if (!name && !["loading", "error", "not-found"].includes(type)) {
596
- const { name: inputName } = await prompts({
597
- type: "text",
598
- name: "name",
599
- message: `${type} name:`
600
- });
601
- name = inputName;
602
- if (!name) process.exit(0);
603
- }
604
- const templates = {
605
- page: (n) => ({
606
- path: `app/${n}/page.tsx`,
607
- content: `export const metadata = {
608
- title: "${capitalize(n)}",
609
- };
610
-
611
- export default function ${pascalCase(n)}Page() {
612
- return (
613
- <main>
614
- <h1>${capitalize(n)}</h1>
615
- </main>
616
- );
617
- }
618
- `
619
- }),
620
- layout: (n) => ({
621
- path: `app/${n}/layout.tsx`,
622
- content: `export default function ${pascalCase(n)}Layout({ children }: { children: React.ReactNode }) {
623
- return <div>{children}</div>;
624
- }
625
- `
626
- }),
627
- component: (n) => ({
628
- path: `components/${pascalCase(n)}.tsx`,
629
- content: `interface ${pascalCase(n)}Props {
630
- // props
631
- }
632
-
633
- export default function ${pascalCase(n)}({}: ${pascalCase(n)}Props) {
634
- return <div>${pascalCase(n)}</div>;
635
- }
636
- `
637
- }),
638
- hook: (n) => ({
639
- path: `hooks/use${pascalCase(n)}.ts`,
640
- content: `import { useState } from 'react';
641
-
642
- export function use${pascalCase(n)}() {
643
- const [state, setState] = useState(null);
644
- return { state, setState };
645
- }
646
- `
647
- }),
648
- api: (n) => ({
649
- path: `server/api/${n}.ts`,
650
- content: `export function GET(request: any) {
651
- return Response.json({ message: "Hello from ${n}" });
652
- }
653
-
654
- export function POST(request: any) {
655
- return Response.json({ received: true });
656
- }
657
- `
658
- }),
659
- action: (n) => ({
660
- path: `server/actions/${n}.ts`,
661
- content: `'use server';
662
-
663
- export async function ${camelCase(n)}Action(prevState: any, formData: FormData) {
664
- // Server action logic
665
- return { success: true };
666
- }
667
- `
668
- }),
669
- middleware: (n) => ({
670
- path: `middleware/${n}.ts`,
671
- content: `export default async function ${camelCase(n)}Middleware(req: any, res: any, next: () => Promise<void>) {
672
- // Middleware logic
673
- await next();
674
- }
675
- `
676
- }),
677
- context: (n) => ({
678
- path: `contexts/${pascalCase(n)}Context.tsx`,
679
- content: `'use client';
680
- import { createContext, useContext, useState, type ReactNode } from 'react';
681
-
682
- interface ${pascalCase(n)}ContextType {
683
- // context values
684
- }
685
-
686
- const ${pascalCase(n)}Context = createContext<${pascalCase(n)}ContextType | null>(null);
687
-
688
- export function ${pascalCase(n)}Provider({ children }: { children: ReactNode }) {
689
- return <${pascalCase(n)}Context.Provider value={{}}>{children}</${pascalCase(n)}Context.Provider>;
690
- }
691
-
692
- export function use${pascalCase(n)}() {
693
- const ctx = useContext(${pascalCase(n)}Context);
694
- if (!ctx) throw new Error('use${pascalCase(n)} must be used within ${pascalCase(n)}Provider');
695
- return ctx;
696
- }
697
- `
698
- }),
699
- loading: () => ({
700
- path: `app/loading.tsx`,
701
- content: `export default function Loading() {
702
- return <div>Loading...</div>;
703
- }
704
- `
705
- }),
706
- error: () => ({
707
- path: `app/error.tsx`,
708
- content: `'use client';
709
-
710
- export default function Error({ error, reset }: { error: Error; reset: () => void }) {
711
- return (
712
- <div>
713
- <h2>Something went wrong!</h2>
714
- <p>{error.message}</p>
715
- <button onClick={reset}>Try again</button>
716
- </div>
717
- );
718
- }
719
- `
720
- }),
721
- "not-found": () => ({
722
- path: `app/not-found.tsx`,
723
- content: `export default function NotFound() {
724
- return (
725
- <div>
726
- <h1>404 - Not Found</h1>
727
- <p>The page you're looking for doesn't exist.</p>
728
- </div>
729
- );
730
- }
731
- `
732
- })
733
- };
734
- const generator = templates[type];
735
- if (!generator) {
736
- log.error(`No template for type: ${type}`);
737
- process.exit(1);
738
- }
739
- const { path: filePath, content } = generator(name || "");
740
- const fullPath = path.resolve(process.cwd(), filePath);
741
- if (fs.existsSync(fullPath)) {
742
- log.warn(`File already exists: ${filePath}`);
743
- const { overwrite } = await prompts({
744
- type: "confirm",
745
- name: "overwrite",
746
- message: "Overwrite?",
747
- initial: false
748
- });
749
- if (!overwrite) process.exit(0);
750
- }
751
- fs.mkdirSync(path.dirname(fullPath), { recursive: true });
752
- fs.writeFileSync(fullPath, content);
753
- log.success(`Created ${pc.cyan(filePath)}`);
754
- }
755
- function writeFile(filePath, content) {
756
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
757
- fs.writeFileSync(filePath, content);
758
- }
759
- function capitalize(str) {
760
- return str.charAt(0).toUpperCase() + str.slice(1);
761
- }
762
- function pascalCase(str) {
763
- return str.split(/[-_\/]/).map((s) => capitalize(s)).join("");
764
- }
765
- function camelCase(str) {
766
- const pascal = pascalCase(str);
767
- return pascal.charAt(0).toLowerCase() + pascal.slice(1);
768
- }
769
- async function handleUiCommand(args) {
770
- const [subCommand, componentName] = args;
771
- if (subCommand !== "add" || !componentName) {
772
- log.error("Usage: velix ui add <component>");
773
- return;
774
- }
775
- const cwd = process.cwd();
776
- const uiDir = path.join(cwd, "components", "ui");
777
- const utilsDir = path.join(cwd, "lib");
778
- if (componentName === "button") {
779
- if (!fs.existsSync(utilsDir)) fs.mkdirSync(utilsDir, { recursive: true });
780
- const utilsPath = path.join(utilsDir, "utils.ts");
781
- if (!fs.existsSync(utilsPath)) {
782
- writeFile(utilsPath, `import { clsx, type ClassValue } from "clsx";
783
- import { twMerge } from "tailwind-merge";
784
-
785
- export function cn(...inputs: ClassValue[]) {
786
- return twMerge(clsx(inputs));
787
- }
788
- `);
789
- log.info("Created lib/utils.ts (please run in project: npm i clsx tailwind-merge)");
790
- }
791
- if (!fs.existsSync(uiDir)) fs.mkdirSync(uiDir, { recursive: true });
792
- const buttonPath = path.join(uiDir, "button.tsx");
793
- if (fs.existsSync(buttonPath)) {
794
- log.warn("Button component already exists.");
795
- return;
796
- }
797
- writeFile(buttonPath, `import * as React from "react";
798
- import { cn } from "../../lib/utils";
799
-
800
- export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
801
- variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link';
802
- size?: 'default' | 'sm' | 'lg' | 'icon';
803
- }
804
-
805
- export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
806
- ({ className, variant = 'default', size = 'default', ...props }, ref) => {
807
- const variants: Record<string, string> = {
808
- default: 'bg-slate-900 text-slate-50 hover:bg-slate-900/90',
809
- destructive: 'bg-red-500 text-slate-50 hover:bg-red-500/90',
810
- outline: 'border border-slate-200 bg-white hover:bg-slate-100 hover:text-slate-900',
811
- secondary: 'bg-slate-100 text-slate-900 hover:bg-slate-100/80',
812
- ghost: 'hover:bg-slate-100 hover:text-slate-900',
813
- link: 'text-slate-900 underline-offset-4 hover:underline',
814
- };
815
- const sizes: Record<string, string> = {
816
- default: 'h-10 px-4 py-2',
817
- sm: 'h-9 rounded-md px-3',
818
- lg: 'h-11 rounded-md px-8',
819
- icon: 'h-10 w-10',
820
- };
821
- const classes = cn(
822
- 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
823
- variants[variant],
824
- sizes[size],
825
- className
826
- );
827
- return <button className={classes} ref={ref} {...props} />;
828
- }
829
- );
830
- Button.displayName = "Button";
831
- `);
832
- log.success("Installed component: Button (components/ui/button.tsx)");
833
- } else {
834
- log.error(`Component "${componentName}" is not available yet in the mock registry.`);
835
- }
836
- }
837
97
  main().catch((err) => {
838
- log.error(err.message);
98
+ log.error(err instanceof Error ? err.message : String(err));
839
99
  process.exit(1);
840
100
  });
841
101
  //# sourceMappingURL=index.js.map