vaderjs 2.3.14 → 2.3.16

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/main.ts ADDED
@@ -0,0 +1,744 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { build, serve } from "bun";
4
+ import fs from "fs/promises";
5
+ import fsSync from "fs";
6
+ import path from "path";
7
+ import { initProject } from "vaderjs/cli";
8
+
9
+ const colors = {
10
+ reset: "\x1b[0m",
11
+ red: "\x1b[31m",
12
+ green: "\x1b[32m",
13
+ yellow: "\x1b[33m",
14
+ blue: "\x1b[34m",
15
+ magenta: "\x1b[35m",
16
+ cyan: "\x1b[36m",
17
+ };
18
+
19
+ globalThis.isDev = process.argv[2] === "dev";
20
+
21
+ const logger = {
22
+ _log: (color, ...args) => console.log(color, ...args, colors.reset),
23
+ info: (...args) => logger._log(colors.cyan, "ℹ", ...args),
24
+ success: (...args) => logger._log(colors.green, "✅", ...args),
25
+ warn: (...args) => logger._log(colors.yellow, "⚠️", ...args),
26
+ error: (...args) => logger._log(colors.red, "❌", ...args),
27
+ step: (...args) => logger._log(colors.magenta, "\n🚀", ...args),
28
+ };
29
+
30
+ async function timedStep(name, fn) {
31
+ logger.step(`${name}...`);
32
+ const start = performance.now();
33
+ try {
34
+ await fn();
35
+ const duration = (performance.now() - start).toFixed(2);
36
+ logger.success(`Finished '${name}' in ${duration}ms`);
37
+ } catch (e) {
38
+ logger.error(`Error during '${name}':`, e);
39
+ if (!isDev) process.exit(1);
40
+ }
41
+ }
42
+
43
+ // --- CONSTANTS ---
44
+
45
+ const PROJECT_ROOT = process.cwd();
46
+ const PUBLIC_DIR = path.join(PROJECT_ROOT, "public");
47
+ const DIST_DIR = path.join(PROJECT_ROOT, "dist");
48
+ const SRC_DIR = path.join(PROJECT_ROOT, "src");
49
+ const APP_DIR = path.join(PROJECT_ROOT, "app");
50
+
51
+ const VADER_SRC_PATH = path.join(
52
+ PROJECT_ROOT,
53
+ "node_modules",
54
+ "vaderjs",
55
+ "index.ts"
56
+ );
57
+
58
+ const TEMP_SRC_DIR = path.join(PROJECT_ROOT, ".vader_temp_src");
59
+
60
+ let config: any = {};
61
+ let htmlInjections: string[] = [];
62
+
63
+ // --- Plugin Support ---
64
+
65
+ interface Plugin {
66
+ name: string;
67
+ version: string;
68
+ description?: string;
69
+ onBuildStart?: (api: PluginAPI) => Promise<void> | void;
70
+ onBuildFinish?: (api: PluginAPI) => Promise<void> | void;
71
+ onFileChange?: (file: string, api: PluginAPI) => Promise<void> | void;
72
+ }
73
+
74
+ interface PluginAPI {
75
+ injectHTML(html: string): void;
76
+ addWatchPath(path: string): void;
77
+ config: any;
78
+ isDev: boolean;
79
+ distDir: string;
80
+ srcDir: string;
81
+ publicDir: string;
82
+ projectRoot: string;
83
+ }
84
+
85
+ let plugins: Plugin[] = [];
86
+
87
+ // Create plugin API helper
88
+ function createPluginAPI(): PluginAPI {
89
+ return {
90
+ injectHTML: (html: string) => {
91
+ htmlInjections.push(html);
92
+ },
93
+ addWatchPath: (watchPath: string) => {
94
+ if (fsSync.existsSync(watchPath)) {
95
+ watcher.watch(watchPath);
96
+ }
97
+ },
98
+ config: config,
99
+ isDev: globalThis.isDev,
100
+ distDir: DIST_DIR,
101
+ srcDir: SRC_DIR,
102
+ publicDir: PUBLIC_DIR,
103
+ projectRoot: PROJECT_ROOT
104
+ };
105
+ }
106
+
107
+ // Run plugin hooks
108
+ async function runPluginHook(hookName: 'onBuildStart' | 'onBuildFinish', api: PluginAPI) {
109
+ for (const plugin of plugins) {
110
+ if (plugin[hookName]) {
111
+ try {
112
+ logger.info(`Running plugin hook: ${plugin.name} - ${hookName}`);
113
+ await plugin[hookName]!(api);
114
+ } catch (e) {
115
+ logger.error(`Error in plugin "${plugin.name}" during ${hookName}:`, e);
116
+ if (!globalThis.isDev) process.exit(1);
117
+ }
118
+ }
119
+ }
120
+ }
121
+
122
+ // Load plugins from config
123
+ async function loadPluginsFromConfig() {
124
+ console.log(config)
125
+ if (!config.plugins || !Array.isArray(config.plugins)) {
126
+ logger.info("No plugins defined in config");
127
+ return;
128
+ }
129
+
130
+ const loadedPlugins: Plugin[] = [];
131
+
132
+ for (const pluginConfig of config.plugins) {
133
+ try {
134
+ let plugin;
135
+
136
+ // If plugin is a string, import it
137
+ if (typeof pluginConfig === 'string') {
138
+ const pluginPath = path.isAbsolute(pluginConfig)
139
+ ? pluginConfig
140
+ : path.join(PROJECT_ROOT, pluginConfig);
141
+
142
+ // Check if file exists
143
+ if (fsSync.existsSync(pluginPath)) {
144
+ const pluginModule = await import(pluginPath);
145
+ plugin = pluginModule.default || pluginModule;
146
+ } else {
147
+ // Try as npm package
148
+ plugin = await import(pluginConfig);
149
+ }
150
+ }
151
+ // If plugin is an object with resolve property
152
+ else if (typeof pluginConfig === 'object' && pluginConfig.resolve) {
153
+ const pluginModule = await import(pluginConfig.resolve);
154
+ plugin = pluginModule.default || pluginModule;
155
+
156
+ // Pass options to plugin if it's a factory function
157
+ if (typeof plugin === 'function' && pluginConfig.options) {
158
+ plugin = await plugin(pluginConfig.options);
159
+ }
160
+ }
161
+ // If plugin is already a plugin object
162
+ else if (typeof pluginConfig === 'object' && pluginConfig.name) {
163
+ plugin = pluginConfig;
164
+ }
165
+
166
+ if (plugin && typeof plugin === 'object' && plugin.name) {
167
+ loadedPlugins.push(plugin);
168
+ logger.success(`Loaded plugin: ${plugin.name} v${plugin.version || 'unknown'}`);
169
+ } else {
170
+ logger.warn(`Invalid plugin: missing name property`);
171
+ }
172
+ } catch (e) {
173
+ logger.error(`Failed to load plugin:`, e);
174
+ }
175
+ }
176
+
177
+ plugins = loadedPlugins;
178
+ }
179
+
180
+ // Also load from plugins directory (backward compatibility)
181
+ async function loadPluginsFromDirectory() {
182
+ const pluginsDir = path.join(PROJECT_ROOT, "plugins");
183
+
184
+ if (!fsSync.existsSync(pluginsDir)) {
185
+ return;
186
+ }
187
+
188
+ const pluginFiles = await fs.readdir(pluginsDir);
189
+ const loadedPlugins: Plugin[] = [];
190
+
191
+ for (const file of pluginFiles) {
192
+ if (file.endsWith('.js') || file.endsWith('.ts')) {
193
+ try {
194
+ const pluginPath = path.join(pluginsDir, file);
195
+ const pluginModule = await import(pluginPath);
196
+ const plugin = pluginModule.default || pluginModule;
197
+
198
+ if (plugin && typeof plugin === 'object' && plugin.name) {
199
+ // Check if plugin already loaded from config
200
+ if (!plugins.some(p => p.name === plugin.name)) {
201
+ loadedPlugins.push(plugin);
202
+ logger.info(`Loaded plugin from directory: ${plugin.name} v${plugin.version}`);
203
+ }
204
+ } else {
205
+ logger.warn(`Invalid plugin in ${file}: missing name property`);
206
+ }
207
+ } catch (e) {
208
+ logger.error(`Failed to load plugin ${file}:`, e);
209
+ }
210
+ }
211
+ }
212
+
213
+ plugins = [...plugins, ...loadedPlugins];
214
+ }
215
+
216
+ // --- JSConfig Setup ---
217
+
218
+ async function ensureJSConfig() {
219
+ const jsconfigPath = path.join(PROJECT_ROOT, "jsconfig.json");
220
+
221
+ // Check if jsconfig.json already exists
222
+ let existingConfig = {};
223
+ if (fsSync.existsSync(jsconfigPath)) {
224
+ try {
225
+ const content = await fs.readFile(jsconfigPath, "utf8");
226
+ existingConfig = JSON.parse(content);
227
+ } catch (e) {
228
+ logger.warn("Existing jsconfig.json is invalid, will overwrite");
229
+ }
230
+ }
231
+
232
+ // Define the required VaderJS configuration
233
+ const vaderConfig = {
234
+ compilerOptions: {
235
+ jsx: "react",
236
+ jsxFactory: "Vader.createElement",
237
+ jsxFragmentFactory: "Fragment"
238
+ }
239
+ };
240
+
241
+ // Merge with existing config (preserve other settings)
242
+ const mergedConfig = {
243
+ ...existingConfig,
244
+ compilerOptions: {
245
+ ...(existingConfig.compilerOptions || {}),
246
+ ...vaderConfig.compilerOptions
247
+ }
248
+ };
249
+
250
+ // Write the config
251
+ await fs.writeFile(jsconfigPath, JSON.stringify(mergedConfig, null, 2));
252
+ logger.success(`jsconfig.json created/updated at ${jsconfigPath}`);
253
+ }
254
+
255
+ // --- FILE WATCHER ---
256
+
257
+ class FileWatcher {
258
+ watchers = new Map<string, any>();
259
+ callbacks: ((file: string) => void)[] = [];
260
+
261
+ watch(dir: string) {
262
+ if (!fsSync.existsSync(dir)) return;
263
+
264
+ const watcher = fsSync.watch(dir, { recursive: true }, (_, filename) => {
265
+ if (!filename) return;
266
+
267
+ const file = path.join(dir, filename);
268
+
269
+ if (
270
+ file.includes("node_modules") ||
271
+ file.includes("dist") ||
272
+ file.includes(".git")
273
+ )
274
+ return;
275
+
276
+ this.callbacks.forEach((cb) => cb(file));
277
+ });
278
+
279
+ this.watchers.set(dir, watcher);
280
+ }
281
+
282
+ onChange(cb: (file: string) => void) {
283
+ this.callbacks.push(cb);
284
+ }
285
+
286
+ clear() {
287
+ this.watchers.forEach((w) => w.close());
288
+ this.watchers.clear();
289
+ }
290
+ }
291
+
292
+ const watcher = new FileWatcher();
293
+
294
+ // --- CONFIG ---
295
+
296
+ export async function loadConfig() {
297
+ try {
298
+ const configPath = path.join(PROJECT_ROOT, "vaderjs.config.ts");
299
+ if (fsSync.existsSync(configPath)) {
300
+ const mod = await import(configPath);
301
+ return mod.default || mod;
302
+ }
303
+ return {};
304
+ } catch (error) {
305
+ logger.warn("Failed to load config, using defaults:", error);
306
+ return {};
307
+ }
308
+ }
309
+
310
+ export function defineConfig(cfg: any) {
311
+ return cfg;
312
+ }
313
+
314
+ // --- BUILD HELPERS ---
315
+
316
+ function patchHooksUsage(code: string) {
317
+ return code.replace(
318
+ /import\s+{[^}]*use(State|Effect|Memo)[^}]*}\s+from\s+['"]vaderjs['"];?\n?/g,
319
+ ""
320
+ );
321
+ }
322
+
323
+ async function preprocessSources(src: string, dest: string) {
324
+ await fs.mkdir(dest, { recursive: true });
325
+
326
+ const entries = await fs.readdir(src, { withFileTypes: true });
327
+
328
+ for (const entry of entries) {
329
+ const srcPath = path.join(src, entry.name);
330
+ const destPath = path.join(dest, entry.name);
331
+
332
+ if (entry.isDirectory()) {
333
+ await preprocessSources(srcPath, destPath);
334
+ } else if (/\.(ts|tsx|js|jsx)$/.test(entry.name)) {
335
+ let content = await fs.readFile(srcPath, "utf8");
336
+ content = patchHooksUsage(content);
337
+ await fs.writeFile(destPath, content);
338
+ } else {
339
+ await fs.copyFile(srcPath, destPath);
340
+ }
341
+ }
342
+ }
343
+
344
+ async function buildVaderCore() {
345
+ if (!fsSync.existsSync(VADER_SRC_PATH)) {
346
+ throw new Error("Missing vaderjs dependency");
347
+ }
348
+
349
+ await build({
350
+ entrypoints: [VADER_SRC_PATH],
351
+ outdir: path.join(DIST_DIR, "src", "vader"),
352
+ target: "browser",
353
+ jsxFactory: "e",
354
+ jsxFragment: "Fragment",
355
+ jsxImportSource: "vaderjs",
356
+ sourcemap: "external",
357
+ });
358
+ }
359
+
360
+ async function buildSrc() {
361
+ if (!fsSync.existsSync(SRC_DIR)) return;
362
+
363
+ if (fsSync.existsSync(TEMP_SRC_DIR)) {
364
+ await fs.rm(TEMP_SRC_DIR, { recursive: true, force: true });
365
+ }
366
+
367
+ await preprocessSources(SRC_DIR, TEMP_SRC_DIR);
368
+
369
+ const entrypoints: string[] = [];
370
+
371
+ function collect(dir: string) {
372
+ const files = fsSync.readdirSync(dir, { withFileTypes: true });
373
+
374
+ for (const f of files) {
375
+ const full = path.join(dir, f.name);
376
+
377
+ if (f.isDirectory()) collect(full);
378
+ else if (/\.(ts|tsx|js|jsx)$/.test(f.name)) entrypoints.push(full);
379
+ }
380
+ }
381
+
382
+ collect(TEMP_SRC_DIR);
383
+
384
+ if (!entrypoints.length) return;
385
+
386
+ await build({
387
+ entrypoints,
388
+ outdir: path.join(DIST_DIR, "src"),
389
+ root: TEMP_SRC_DIR,
390
+ naming: { entry: "[dir]/[name].js" },
391
+ jsxFactory: "e",
392
+ jsxFragment: "Fragment",
393
+ jsxImportSource: "vaderjs",
394
+ target: "browser",
395
+ external: ["vaderjs"],
396
+ });
397
+ }
398
+
399
+ async function copyPublicAssets() {
400
+ if (!fsSync.existsSync(PUBLIC_DIR)) return;
401
+
402
+ const items = await fs.readdir(PUBLIC_DIR);
403
+
404
+ for (const item of items) {
405
+ await fs.cp(
406
+ path.join(PUBLIC_DIR, item),
407
+ path.join(DIST_DIR, item),
408
+ { recursive: true }
409
+ );
410
+ }
411
+ }
412
+
413
+ // Helper function to find App file
414
+ function findAppFile(): string | null {
415
+ const possiblePaths = [
416
+ path.join(PROJECT_ROOT, "App.tsx"),
417
+ path.join(PROJECT_ROOT, "App.jsx"),
418
+ path.join(PROJECT_ROOT, "App.ts"),
419
+ path.join(PROJECT_ROOT, "App.js"),
420
+ path.join(APP_DIR, "index.tsx"),
421
+ path.join(APP_DIR, "index.jsx"),
422
+ ];
423
+
424
+ for (const appPath of possiblePaths) {
425
+ if (fsSync.existsSync(appPath)) {
426
+ return appPath;
427
+ }
428
+ }
429
+
430
+ return null;
431
+ }
432
+
433
+ async function buildAppEntrypoints() {
434
+ // First check for root App file (VaderJS standard)
435
+ const appFile = findAppFile();
436
+
437
+ // Build HTML with injections from plugins
438
+ const htmlInjectionsString = htmlInjections.join('\n ');
439
+
440
+ if (appFile) {
441
+ logger.info(`Building App from: ${appFile}`);
442
+
443
+ await build({
444
+ entrypoints: [appFile],
445
+ outdir: DIST_DIR,
446
+ target: "browser",
447
+ jsxFactory: "e",
448
+ jsxFragment: "Fragment",
449
+ jsxImportSource: "vaderjs",
450
+ naming: "index.js",
451
+ external: [], // Bundle everything for website
452
+ });
453
+
454
+ const html = `<!DOCTYPE html>
455
+ <html>
456
+ <head>
457
+ <meta charset="UTF-8"/>
458
+ <meta name="viewport" content="width=device-width,initial-scale=1"/>
459
+ <title>${config.title || 'Vader App'}</title>
460
+ ${htmlInjectionsString}
461
+ </head>
462
+ <body>
463
+ <div id="app"></div>
464
+ <script type="module" src="/index.js"></script>
465
+ </body>
466
+ </html>`;
467
+
468
+ await fs.writeFile(path.join(DIST_DIR, "index.html"), html);
469
+ return;
470
+ }
471
+
472
+ // Fallback to app directory structure
473
+ if (!fsSync.existsSync(APP_DIR)) {
474
+ logger.warn("No App.tsx or app directory found");
475
+ return;
476
+ }
477
+
478
+ const entries = fsSync
479
+ .readdirSync(APP_DIR, { recursive: true })
480
+ .filter((f) => /index\.(tsx|jsx)$/.test(f as string))
481
+ .map((f) => ({
482
+ name:
483
+ path.dirname(f as string) === "."
484
+ ? "index"
485
+ : path.dirname(f as string),
486
+ path: path.join(APP_DIR, f as string),
487
+ }));
488
+
489
+ for (const entry of entries) {
490
+ var outDir = path.join(DIST_DIR, entry.name === "index" ? "" : entry.name);
491
+
492
+ await fs.mkdir(outDir, { recursive: true });
493
+ console.log("Building entrypoint:", entry.path);
494
+ await build({
495
+ entrypoints: [entry.path],
496
+ outdir: outDir,
497
+ target: "browser",
498
+ jsxFactory: "e",
499
+ jsxFragment: "Fragment",
500
+ jsxImportSource: "vaderjs",
501
+ external: [], // Bundle everything for website
502
+ });
503
+
504
+ const html = `<!DOCTYPE html>
505
+ <html>
506
+ <head>
507
+ <meta charset="UTF-8"/>
508
+ <meta name="viewport" content="width=device-width,initial-scale=1"/>
509
+ <title>${config.title || 'Vader App'}</title>
510
+ ${htmlInjectionsString}
511
+ </head>
512
+ <body>
513
+ <div id="app"></div>
514
+ <script type="module" src="/index.js"></script>
515
+ </body>
516
+ </html>`;
517
+
518
+ await fs.writeFile(path.join(outDir, "index.html"), html);
519
+ }
520
+ }
521
+
522
+ async function buildAll(dev = false) {
523
+ const start = performance.now();
524
+
525
+ // Reset HTML injections before build
526
+ htmlInjections = [];
527
+
528
+ // Load plugins from config first
529
+ await loadPluginsFromConfig();
530
+
531
+ // Also load from plugins directory (backward compatibility)
532
+ await loadPluginsFromDirectory();
533
+
534
+ // Create plugin API
535
+ const pluginAPI = createPluginAPI();
536
+
537
+ // Run onBuildStart hooks
538
+ await runPluginHook('onBuildStart', pluginAPI);
539
+
540
+ // Ensure jsconfig.json exists before building
541
+ await ensureJSConfig();
542
+
543
+ if (fsSync.existsSync(DIST_DIR)) {
544
+ await fs.rm(DIST_DIR, { recursive: true, force: true });
545
+ }
546
+
547
+ await fs.mkdir(DIST_DIR, { recursive: true });
548
+
549
+ await timedStep("Build Vader Core", buildVaderCore);
550
+ await timedStep("Build Src", buildSrc);
551
+ await timedStep("Copy Public", copyPublicAssets);
552
+ await timedStep("Build App", buildAppEntrypoints);
553
+
554
+ // Run onBuildFinish hooks
555
+ await runPluginHook('onBuildFinish', pluginAPI);
556
+
557
+ logger.success(
558
+ `Build finished in ${(performance.now() - start).toFixed(1)}ms`
559
+ );
560
+ }
561
+
562
+ // --- DEV SERVER ---
563
+
564
+ async function runDevServer() {
565
+ await buildAll(true);
566
+
567
+ const clients = new Set<any>();
568
+
569
+ const port = config.port || 3000;
570
+
571
+ const server = serve({
572
+ port,
573
+
574
+ fetch(req, server) {
575
+ const url = new URL(req.url);
576
+
577
+ if (url.pathname === "/__hmr" && server.upgrade(req)) return;
578
+
579
+ let filePath = path.join(DIST_DIR, url.pathname);
580
+
581
+ if (!path.extname(filePath)) {
582
+ filePath = path.join(filePath, "index.html");
583
+ }
584
+
585
+ // Ensure we're serving from dist directory
586
+ if (url.pathname === "/" || url.pathname === "") {
587
+ filePath = path.join(DIST_DIR, "index.html");
588
+ }
589
+
590
+ console.log("Serving:", filePath);
591
+
592
+ const file = Bun.file(filePath);
593
+
594
+ return file.exists().then((exists) =>
595
+ exists
596
+ ? new Response(file)
597
+ : new Response("Not Found", { status: 404 })
598
+ );
599
+ },
600
+
601
+ websocket: {
602
+ open(ws) {
603
+ clients.add(ws);
604
+ },
605
+ close(ws) {
606
+ clients.delete(ws);
607
+ },
608
+ },
609
+ });
610
+
611
+ watcher.watch(APP_DIR);
612
+ watcher.watch(SRC_DIR);
613
+ watcher.watch(PUBLIC_DIR);
614
+
615
+ // Watch config file
616
+ watcher.watch(path.join(PROJECT_ROOT, "vaderjs.config.js"));
617
+
618
+ // Watch plugins directory
619
+ const pluginsDir = path.join(PROJECT_ROOT, "plugins");
620
+ if (fsSync.existsSync(pluginsDir)) {
621
+ watcher.watch(pluginsDir);
622
+ }
623
+
624
+ // Also watch for root App file
625
+ const rootAppDir = path.dirname(findAppFile() || "");
626
+ if (rootAppDir && rootAppDir !== PROJECT_ROOT) {
627
+ watcher.watch(rootAppDir);
628
+ }
629
+
630
+ watcher.onChange(async (file) => {
631
+ logger.info("Changes detected, rebuilding...");
632
+
633
+ // If config or plugin file changed, reload config and plugins
634
+ if (file.includes('vaderjs.config.js') || file.includes('plugins')) {
635
+ logger.info("Config or plugin changed, reloading...");
636
+ config = await loadConfig();
637
+ htmlInjections = [];
638
+ plugins = [];
639
+ await loadPluginsFromConfig();
640
+ await loadPluginsFromDirectory();
641
+ }
642
+
643
+ // Run onFileChange hooks for plugins
644
+ const pluginAPI = createPluginAPI();
645
+ for (const plugin of plugins) {
646
+ if (plugin.onFileChange) {
647
+ await plugin.onFileChange(file, pluginAPI);
648
+ }
649
+ }
650
+
651
+ await buildAll(true);
652
+
653
+ for (const c of clients) c.send("reload");
654
+ });
655
+
656
+ logger.success(`Dev server running http://localhost:${port}`);
657
+ }
658
+
659
+ // --- PROD SERVER ---
660
+
661
+ async function runProdServer() {
662
+ const port = config.port || 3000;
663
+
664
+ const server = serve({
665
+ port,
666
+ fetch(req) {
667
+ const url = new URL(req.url);
668
+ let filePath = path.join(DIST_DIR, url.pathname);
669
+
670
+ if (!path.extname(filePath)) {
671
+ filePath = path.join(filePath, "index.html");
672
+ }
673
+
674
+ if (url.pathname === "/" || url.pathname === "") {
675
+ filePath = path.join(DIST_DIR, "index.html");
676
+ }
677
+
678
+ const file = Bun.file(filePath);
679
+
680
+ return file.exists().then((exists) =>
681
+ exists
682
+ ? new Response(file)
683
+ : new Response("Not Found", { status: 404 })
684
+ );
685
+ },
686
+ });
687
+
688
+ logger.success(`Serving production http://localhost:${port}`);
689
+ }
690
+
691
+ // --- CLI ENTRY ---
692
+
693
+ async function main() {
694
+ console.log(`${colors.magenta}
695
+ __ __ ____ ____ _______ __
696
+ | | / |/ __ \\ / __ \\ / ____/ |/ /
697
+ | | / / / / // /_/ // /___ | /
698
+ | | / / /_/ / \\____// /___ / |
699
+ |____/____/_____/ /_____/ |_| |_|
700
+ ${colors.reset}`);
701
+
702
+ const cmd = process.argv[2];
703
+
704
+ if (cmd === "init") {
705
+ await initProject(process.argv[3]);
706
+ // Also create jsconfig.json when initializing a new project
707
+ await ensureJSConfig();
708
+ return;
709
+ }
710
+
711
+ config = await loadConfig();
712
+ config.port ||= 3000;
713
+
714
+ switch (cmd) {
715
+ case "dev":
716
+ await runDevServer();
717
+ break;
718
+
719
+ case "build":
720
+ await buildAll(false);
721
+ break;
722
+
723
+ case "serve":
724
+ await buildAll(false);
725
+ await runProdServer();
726
+ break;
727
+
728
+ default:
729
+ logger.info(`
730
+ Commands:
731
+ dev Start development server with hot reload
732
+ build Build for production
733
+ serve Build and serve production build
734
+ init Initialize a new VaderJS project
735
+
736
+ Make sure you have:
737
+ - App.tsx or App.jsx in your project root
738
+ - OR an app/ directory with index.tsx/jsx files
739
+ - vaderjs installed as a dependency
740
+ `);
741
+ }
742
+ }
743
+
744
+ main();