vaderjs 2.3.18 → 2.3.19

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 (2) hide show
  1. package/main.ts +137 -99
  2. package/package.json +1 -1
package/main.ts CHANGED
@@ -5,7 +5,6 @@ import fs from "fs/promises";
5
5
  import fsSync from "fs";
6
6
  import path from "path";
7
7
  import { initProject } from "vaderjs/cli";
8
- import { rimraf } from "rimraf";
9
8
 
10
9
  const colors = {
11
10
  reset: "\x1b[0m",
@@ -37,7 +36,7 @@ async function timedStep(name, fn) {
37
36
  logger.success(`Finished '${name}' in ${duration}ms`);
38
37
  } catch (e) {
39
38
  logger.error(`Error during '${name}':`, e);
40
- if (!isDev) process.exit(1);
39
+ if (!globalThis.isDev) process.exit(1);
41
40
  }
42
41
  }
43
42
 
@@ -89,10 +88,8 @@ class HTMLInjectionManager {
89
88
  private injections: Map<string, string> = new Map();
90
89
 
91
90
  add(html: string, id?: string): void {
92
- // Generate an ID if not provided (based on content hash)
93
91
  const injectionId = id || this.generateId(html);
94
92
 
95
- // Only add if not already present
96
93
  if (!this.injections.has(injectionId)) {
97
94
  this.injections.set(injectionId, html);
98
95
  logger.info(`Added HTML injection: ${injectionId}`);
@@ -102,26 +99,21 @@ class HTMLInjectionManager {
102
99
  }
103
100
 
104
101
  private generateId(html: string): string {
105
- // Simple ID generation based on content
106
- // For CSS links, extract the href
107
102
  const hrefMatch = html.match(/href=["']([^"']+)["']/);
108
103
  if (hrefMatch) {
109
104
  return `link:${hrefMatch[1]}`;
110
105
  }
111
106
 
112
- // For script tags, extract the src
113
107
  const srcMatch = html.match(/src=["']([^"']+)["']/);
114
108
  if (srcMatch) {
115
109
  return `script:${srcMatch[1]}`;
116
110
  }
117
111
 
118
- // For meta tags, extract name or property
119
112
  const nameMatch = html.match(/(?:name|property)=["']([^"']+)["']/);
120
113
  if (nameMatch) {
121
114
  return `meta:${nameMatch[1]}`;
122
115
  }
123
116
 
124
- // Fallback to hash of content
125
117
  let hash = 0;
126
118
  for (let i = 0; i < html.length; i++) {
127
119
  hash = ((hash << 5) - hash) + html.charCodeAt(i);
@@ -137,10 +129,6 @@ class HTMLInjectionManager {
137
129
  clear(): void {
138
130
  this.injections.clear();
139
131
  }
140
-
141
- has(id: string): boolean {
142
- return this.injections.has(id);
143
- }
144
132
  }
145
133
 
146
134
  const htmlInjectionManager = new HTMLInjectionManager();
@@ -182,7 +170,6 @@ async function runPluginHook(hookName: 'onBuildStart' | 'onBuildFinish', api: Pl
182
170
 
183
171
  // Load plugins from config
184
172
  async function loadPluginsFromConfig() {
185
- console.log(config)
186
173
  if (!config.plugins || !Array.isArray(config.plugins)) {
187
174
  logger.info("No plugins defined in config");
188
175
  return;
@@ -194,32 +181,26 @@ async function loadPluginsFromConfig() {
194
181
  try {
195
182
  let plugin;
196
183
 
197
- // If plugin is a string, import it
198
184
  if (typeof pluginConfig === 'string') {
199
185
  const pluginPath = path.isAbsolute(pluginConfig)
200
186
  ? pluginConfig
201
187
  : path.join(PROJECT_ROOT, pluginConfig);
202
188
 
203
- // Check if file exists
204
189
  if (fsSync.existsSync(pluginPath)) {
205
190
  const pluginModule = await import(pluginPath);
206
191
  plugin = pluginModule.default || pluginModule;
207
192
  } else {
208
- // Try as npm package
209
193
  plugin = await import(pluginConfig);
210
194
  }
211
195
  }
212
- // If plugin is an object with resolve property
213
196
  else if (typeof pluginConfig === 'object' && pluginConfig.resolve) {
214
197
  const pluginModule = await import(pluginConfig.resolve);
215
198
  plugin = pluginModule.default || pluginModule;
216
199
 
217
- // Pass options to plugin if it's a factory function
218
200
  if (typeof plugin === 'function' && pluginConfig.options) {
219
201
  plugin = await plugin(pluginConfig.options);
220
202
  }
221
203
  }
222
- // If plugin is already a plugin object
223
204
  else if (typeof pluginConfig === 'object' && pluginConfig.name) {
224
205
  plugin = pluginConfig;
225
206
  }
@@ -238,7 +219,7 @@ async function loadPluginsFromConfig() {
238
219
  plugins = loadedPlugins;
239
220
  }
240
221
 
241
- // Also load from plugins directory (backward compatibility)
222
+ // Also load from plugins directory
242
223
  async function loadPluginsFromDirectory() {
243
224
  const pluginsDir = path.join(PROJECT_ROOT, "plugins");
244
225
 
@@ -257,7 +238,6 @@ async function loadPluginsFromDirectory() {
257
238
  const plugin = pluginModule.default || pluginModule;
258
239
 
259
240
  if (plugin && typeof plugin === 'object' && plugin.name) {
260
- // Check if plugin already loaded from config
261
241
  if (!plugins.some(p => p.name === plugin.name)) {
262
242
  loadedPlugins.push(plugin);
263
243
  logger.info(`Loaded plugin from directory: ${plugin.name} v${plugin.version}`);
@@ -279,7 +259,6 @@ async function loadPluginsFromDirectory() {
279
259
  async function ensureJSConfig() {
280
260
  const jsconfigPath = path.join(PROJECT_ROOT, "jsconfig.json");
281
261
 
282
- // Check if jsconfig.json already exists
283
262
  let existingConfig = {};
284
263
  if (fsSync.existsSync(jsconfigPath)) {
285
264
  try {
@@ -290,7 +269,6 @@ async function ensureJSConfig() {
290
269
  }
291
270
  }
292
271
 
293
- // Define the required VaderJS configuration
294
272
  const vaderConfig = {
295
273
  compilerOptions: {
296
274
  jsx: "react",
@@ -299,7 +277,6 @@ async function ensureJSConfig() {
299
277
  }
300
278
  };
301
279
 
302
- // Merge with existing config (preserve other settings)
303
280
  const mergedConfig = {
304
281
  ...existingConfig,
305
282
  compilerOptions: {
@@ -308,7 +285,6 @@ async function ensureJSConfig() {
308
285
  }
309
286
  };
310
287
 
311
- // Write the config
312
288
  await fs.writeFile(jsconfigPath, JSON.stringify(mergedConfig, null, 2));
313
289
  logger.success(`jsconfig.json created/updated at ${jsconfigPath}`);
314
290
  }
@@ -339,7 +315,6 @@ class FileWatcher {
339
315
  )
340
316
  return;
341
317
 
342
- // Debounce file changes
343
318
  this.pendingFiles.add(file);
344
319
 
345
320
  if (this.debounceTimer) {
@@ -350,13 +325,12 @@ class FileWatcher {
350
325
  const files = Array.from(this.pendingFiles);
351
326
  this.pendingFiles.clear();
352
327
 
353
- // Only trigger if not already rebuilding
354
328
  if (!this.rebuildInProgress) {
355
329
  this.callbacks.forEach((cb) => {
356
330
  files.forEach(file => cb(file));
357
331
  });
358
332
  }
359
- }, 100); // Wait 100ms for multiple file changes
333
+ }, 100);
360
334
  });
361
335
 
362
336
  this.watchers.set(dir, watcher);
@@ -503,7 +477,6 @@ async function copyPublicAssets() {
503
477
  }
504
478
  }
505
479
 
506
- // Helper function to find App file
507
480
  function findAppFile(): string | null {
508
481
  const possiblePaths = [
509
482
  path.join(PROJECT_ROOT, "App.tsx"),
@@ -523,28 +496,23 @@ function findAppFile(): string | null {
523
496
  return null;
524
497
  }
525
498
 
526
- // Helper to deduplicate HTML injections
527
499
  function getUniqueInjections(injections: string[]): string[] {
528
500
  const seen = new Set<string>();
529
501
  const unique: string[] = [];
530
502
 
531
503
  for (const injection of injections) {
532
- // Create a normalized key for comparison
533
504
  let key = injection;
534
505
 
535
- // For link tags, normalize by href
536
506
  const hrefMatch = injection.match(/href=["']([^"']+)["']/);
537
507
  if (hrefMatch) {
538
508
  key = `link:${hrefMatch[1]}`;
539
509
  }
540
510
 
541
- // For script tags, normalize by src
542
511
  const srcMatch = injection.match(/src=["']([^"']+)["']/);
543
512
  if (srcMatch) {
544
513
  key = `script:${srcMatch[1]}`;
545
514
  }
546
515
 
547
- // For meta tags, normalize by name/property
548
516
  const nameMatch = injection.match(/(?:name|property)=["']([^"']+)["']/);
549
517
  if (nameMatch) {
550
518
  key = `meta:${nameMatch[1]}`;
@@ -559,9 +527,107 @@ function getUniqueInjections(injections: string[]): string[] {
559
527
  return unique;
560
528
  }
561
529
 
562
- async function buildAppEntrypoints() {
530
+ // --- VERCEL CONFIG GENERATION ---
531
+
532
+ async function generateVercelConfig(routes: { route: string; htmlPath: string }[]) {
533
+ if (config.host_provider !== "vercel") {
534
+ logger.info("Hosting provider is not 'vercel', skipping vercel.json generation");
535
+ return;
536
+ }
537
+
538
+ logger.info("🔧 Generating Vercel configuration for deployment...");
539
+
540
+ // Build the vercel.json configuration
541
+ const vercelConfig: any = {
542
+ version: 2,
543
+ buildCommand: "bun run build",
544
+ outputDirectory: "dist",
545
+ installCommand: "bun install",
546
+ framework: null,
547
+ rewrites: [],
548
+ };
549
+
550
+ // Add specific rewrites for each generated HTML route
551
+ for (const route of routes) {
552
+ if (route.route === "index") {
553
+ // Root route
554
+ vercelConfig.rewrites.push({
555
+ source: "/",
556
+ destination: "/dist/index.html",
557
+ });
558
+ } else {
559
+ // Nested route - exact match
560
+ vercelConfig.rewrites.push({
561
+ source: `/${route.route}`,
562
+ destination: `/dist/${route.route}/index.html`,
563
+ });
564
+ // Also handle subpaths (for client-side routing)
565
+ vercelConfig.rewrites.push({
566
+ source: `/${route.route}/:path*`,
567
+ destination: `/dist/${route.route}/index.html`,
568
+ });
569
+ }
570
+ }
571
+
572
+ // Fallback for static assets (CSS, JS, images)
573
+ vercelConfig.rewrites.push({
574
+ source: "/:path*",
575
+ destination: "/dist/:path*",
576
+ });
577
+
578
+ // Final fallback for any unmatched routes (SPA behavior)
579
+ vercelConfig.rewrites.push({
580
+ source: "/(.*)",
581
+ destination: "/dist/index.html",
582
+ });
583
+
584
+ // Remove duplicate rewrites (keep first occurrence for each source pattern)
585
+ const uniqueRewrites = [];
586
+ const seenSources = new Set();
587
+ for (const rewrite of vercelConfig.rewrites) {
588
+ if (!seenSources.has(rewrite.source)) {
589
+ seenSources.add(rewrite.source);
590
+ uniqueRewrites.push(rewrite);
591
+ }
592
+ }
593
+ vercelConfig.rewrites = uniqueRewrites;
594
+
595
+ // Write vercel.json to PROJECT ROOT (not inside dist)
596
+ const vercelPath = path.join(PROJECT_ROOT, "vercel.json");
597
+ await fs.writeFile(vercelPath, JSON.stringify(vercelConfig, null, 2));
598
+
599
+ logger.success(`✅ vercel.json generated at ${vercelPath}`);
600
+ logger.info(` Routes configured to serve from ./dist directory`);
601
+ logger.info(` ${routes.length} route(s) configured`);
602
+ }
603
+
604
+ async function generateVercelProjectConfig() {
605
+ if (config.hosting !== "vercel") return;
606
+
607
+ const vercelDir = path.join(PROJECT_ROOT, ".vercel");
608
+ await fs.mkdir(vercelDir, { recursive: true });
609
+
610
+ const projectConfig = {
611
+ projectId: config.vercelProjectId || "",
612
+ orgId: config.vercelOrgId || "",
613
+ settings: {
614
+ framework: null,
615
+ devCommand: "bun run dev",
616
+ installCommand: "bun install",
617
+ buildCommand: "bun run build",
618
+ outputDirectory: "dist",
619
+ },
620
+ };
621
+
622
+ const vercelProjectPath = path.join(vercelDir, "project.json");
623
+ await fs.writeFile(vercelProjectPath, JSON.stringify(projectConfig, null, 2));
624
+ logger.info(`📁 Generated .vercel/project.json`);
625
+ }
626
+
627
+ // --- BUILD APP ENTRYPOINTS ---
628
+
629
+ async function buildAppEntrypoints() {
563
630
 
564
- // Get unique HTML injections from plugins
565
631
  const allInjections = htmlInjectionManager.getAll();
566
632
  const uniqueInjections = getUniqueInjections(allInjections);
567
633
  const htmlInjectionsString = uniqueInjections.join('\n ');
@@ -570,34 +636,31 @@ function getUniqueInjections(injections: string[]): string[] {
570
636
  logger.info(`Injecting ${uniqueInjections.length} unique HTML items into page`);
571
637
  }
572
638
 
573
-
574
- // Handle app directory structure with nested routes
575
639
  if (!fsSync.existsSync(APP_DIR)) {
576
- logger.warn("No App.tsx or app directory found");
640
+ logger.warn("No app directory found");
577
641
  return;
578
642
  }
579
643
 
580
- // Find all index.{tsx,jsx} files in app directory (including nested)
581
644
  const entrypoints: { route: string; path: string }[] = [];
582
- console.log(`Searching for entrypoints in ${APP_DIR}...`);
645
+ const generatedRoutes: { route: string; htmlPath: string }[] = [];
646
+
583
647
  function findIndexFiles(dir: string, baseRoute: string = "") {
584
648
  const entries = fsSync.readdirSync(dir, { withFileTypes: true });
585
- console.log(entries)
586
649
 
587
650
  for (const entry of entries) {
588
651
  const fullPath = path.join(dir, entry.name);
589
652
  const routePath = path.join(baseRoute, entry.name);
590
653
 
591
654
  if (entry.isDirectory()) {
592
- // Recursively search subdirectories
593
655
  findIndexFiles(fullPath, routePath);
594
656
  } else if (entry.name.match(/^index\.(tsx|jsx)$/)) {
595
- // Found an index file
596
657
  let route = baseRoute;
597
658
 
598
- // Special handling for root index file
599
659
  if (route === "") {
600
660
  route = "index";
661
+ } else {
662
+ // Convert Windows backslashes to forward slashes for URL
663
+ route = route.replace(/\\/g, '/');
601
664
  }
602
665
 
603
666
  entrypoints.push({
@@ -617,19 +680,13 @@ function getUniqueInjections(injections: string[]): string[] {
617
680
  return;
618
681
  }
619
682
 
620
- // Build each entrypoint
621
683
  for (const entry of entrypoints) {
622
- // Determine output directory based on route
623
684
  let outDir: string;
624
685
  let outputName = "index.js";
625
686
 
626
- console.log(entry)
627
-
628
687
  if (entry.route === "index") {
629
- // Root route goes to dist root
630
688
  outDir = DIST_DIR;
631
689
  } else {
632
- // Nested route goes to corresponding subdirectory
633
690
  outDir = path.join(DIST_DIR, entry.route);
634
691
  }
635
692
 
@@ -645,16 +702,25 @@ function getUniqueInjections(injections: string[]): string[] {
645
702
  jsxFragment: "Fragment",
646
703
  jsxImportSource: "vaderjs",
647
704
  naming: outputName,
648
- external: [], // Bundle everything for website
705
+ external: [],
706
+ });
707
+
708
+ const htmlPath = path.join(outDir, "index.html");
709
+ generatedRoutes.push({
710
+ route: entry.route,
711
+ htmlPath: htmlPath
649
712
  });
650
713
 
651
- // Generate HTML file for this route
714
+ const pageTitle = entry.route === "index"
715
+ ? (config.title || 'Vader App')
716
+ : `${config.title || 'Vader App'} - ${entry.route.charAt(0).toUpperCase() + entry.route.slice(1)}`;
717
+
652
718
  const html = `<!DOCTYPE html>
653
719
  <html>
654
720
  <head>
655
721
  <meta charset="UTF-8"/>
656
722
  <meta name="viewport" content="width=device-width,initial-scale=1"/>
657
- <title>${config.title || 'Vader App'} - ${entry.route === 'index' ? 'Home' : entry.route}</title>
723
+ <title>${pageTitle}</title>
658
724
  ${htmlInjectionsString}
659
725
  </head>
660
726
  <body>
@@ -663,25 +729,25 @@ ${htmlInjectionsString}
663
729
  </body>
664
730
  </html>`;
665
731
 
666
- const htmlPath = path.join(outDir, "index.html");
667
732
  await fs.writeFile(htmlPath, html);
668
-
669
733
  logger.success(`Generated ${htmlPath}`);
670
734
  }
671
735
 
672
- // Log summary
673
736
  logger.success(`Built ${entrypoints.length} routes: ${entrypoints.map(e => e.route).join(', ')}`);
737
+
738
+ // Generate Vercel config if hosting is set to vercel
739
+ if (config.host_provider === "vercel") {
740
+ await generateVercelConfig(generatedRoutes);
741
+ }
674
742
  }
675
743
 
676
744
  // Windows-compatible directory removal
677
745
  async function removeDirectory(dir: string) {
678
746
  try {
679
747
  if (fsSync.existsSync(dir)) {
680
- // Use fs.rm with recursive option for Windows compatibility
681
748
  await fs.rm(dir, { recursive: true, force: true });
682
749
  }
683
750
  } catch (error) {
684
- // Fallback: manual deletion for stubborn directories
685
751
  if (fsSync.existsSync(dir)) {
686
752
  const files = await fs.readdir(dir);
687
753
  for (const file of files) {
@@ -698,38 +764,33 @@ async function removeDirectory(dir: string) {
698
764
  }
699
765
  }
700
766
 
767
+ // --- MAIN BUILD ---
768
+
701
769
  async function buildAll(dev = false) {
702
770
  const start = performance.now();
703
771
 
704
- // Clear HTML injections before build
705
772
  htmlInjectionManager.clear();
706
773
 
707
- // Load plugins from config first
708
774
  await loadPluginsFromConfig();
709
-
710
- // Also load from plugins directory (backward compatibility)
711
775
  await loadPluginsFromDirectory();
712
776
 
713
- // Create plugin API
714
777
  const pluginAPI = createPluginAPI();
715
-
716
- // Run onBuildStart hooks
717
778
  await runPluginHook('onBuildStart', pluginAPI);
718
779
 
719
- // Ensure jsconfig.json exists before building
720
780
  await ensureJSConfig();
721
-
722
- // Remove dist directory with Windows compatibility
723
781
  await removeDirectory(DIST_DIR);
724
-
725
782
  await fs.mkdir(DIST_DIR, { recursive: true });
726
783
 
727
784
  await timedStep("Build Vader Core", buildVaderCore);
728
785
  await timedStep("Build Src", buildSrc);
729
786
  await timedStep("Copy Public", copyPublicAssets);
730
787
  await timedStep("Build App", buildAppEntrypoints);
788
+
789
+ // Generate Vercel project config if needed (optional)
790
+ if (config.hosting === "vercel") {
791
+ await generateVercelProjectConfig();
792
+ }
731
793
 
732
- // Run onBuildFinish hooks
733
794
  await runPluginHook('onBuildFinish', pluginAPI);
734
795
 
735
796
  logger.success(
@@ -739,11 +800,10 @@ async function buildAll(dev = false) {
739
800
 
740
801
  // --- DEV SERVER ---
741
802
 
742
- async function runDevServer() {
803
+ async function runDevServer() {
743
804
  let buildPromise: Promise<void> | null = null;
744
805
  let reloadTimeout: NodeJS.Timeout | null = null;
745
806
 
746
- // Function to trigger reload with debounce
747
807
  const triggerReload = (clients: Set<any>) => {
748
808
  if (reloadTimeout) {
749
809
  clearTimeout(reloadTimeout);
@@ -754,9 +814,7 @@ async function buildAll(dev = false) {
754
814
  for (const c of clients) {
755
815
  try {
756
816
  c.send("reload");
757
- } catch (e) {
758
- // Client might be disconnected
759
- }
817
+ } catch (e) {}
760
818
  }
761
819
  reloadTimeout = null;
762
820
  }, 50);
@@ -773,27 +831,21 @@ async function buildAll(dev = false) {
773
831
  fetch(req, server) {
774
832
  const url = new URL(req.url);
775
833
 
776
- // Handle HMR upgrade
777
834
  if (url.pathname === "/__hmr" && server.upgrade(req)) return;
778
835
 
779
- // Remove leading slash and handle path
780
836
  let requestPath = url.pathname;
781
837
 
782
- // If path ends with slash, serve index.html from that directory
783
838
  if (requestPath.endsWith('/')) {
784
839
  requestPath = path.join(requestPath, 'index.html');
785
840
  }
786
841
 
787
- // Build the file path
788
842
  let filePath = path.join(DIST_DIR, requestPath);
789
843
 
790
- // If no extension, try as directory with index.html
791
844
  if (!path.extname(filePath)) {
792
845
  const indexPath = path.join(filePath, 'index.html');
793
846
  if (fsSync.existsSync(indexPath)) {
794
847
  filePath = indexPath;
795
848
  } else {
796
- // Try as .html file
797
849
  const htmlPath = filePath + '.html';
798
850
  if (fsSync.existsSync(htmlPath)) {
799
851
  filePath = htmlPath;
@@ -801,15 +853,12 @@ async function buildAll(dev = false) {
801
853
  }
802
854
  }
803
855
 
804
- // Special handling for root
805
856
  if (url.pathname === "/" || url.pathname === "") {
806
857
  filePath = path.join(DIST_DIR, "index.html");
807
858
  }
808
859
 
809
- // Check if file exists and serve it
810
860
  if (fsSync.existsSync(filePath)) {
811
861
  const file = Bun.file(filePath);
812
- // Set correct content type
813
862
  const ext = path.extname(filePath);
814
863
  const contentType = {
815
864
  '.html': 'text/html',
@@ -831,8 +880,7 @@ async function buildAll(dev = false) {
831
880
  });
832
881
  }
833
882
 
834
- // 404 - Not found
835
- logger.warn(`404 Not Found: ${requestPath} (resolved to ${filePath})`);
883
+ logger.warn(`404 Not Found: ${requestPath}`);
836
884
  return new Response("Not Found", { status: 404 });
837
885
  },
838
886
 
@@ -848,31 +896,26 @@ async function buildAll(dev = false) {
848
896
  },
849
897
  });
850
898
 
851
- // Watch all relevant directories
852
899
  watcher.watch(APP_DIR);
853
900
  watcher.watch(SRC_DIR);
854
901
  watcher.watch(PUBLIC_DIR);
855
902
 
856
- // Watch config file
857
903
  const configPath = path.join(PROJECT_ROOT, "vaderjs.config.ts");
858
904
  if (fsSync.existsSync(configPath)) {
859
905
  watcher.watch(configPath);
860
906
  }
861
907
 
862
- // Watch plugins directory
863
908
  const pluginsDir = path.join(PROJECT_ROOT, "plugins");
864
909
  if (fsSync.existsSync(pluginsDir)) {
865
910
  watcher.watch(pluginsDir);
866
911
  }
867
912
 
868
- // Also watch for root App file
869
913
  const rootAppFile = findAppFile();
870
914
  if (rootAppFile) {
871
915
  watcher.watch(path.dirname(rootAppFile));
872
916
  }
873
917
 
874
918
  watcher.onChange(async (file) => {
875
- // Don't trigger rebuild if one is already in progress
876
919
  if (buildPromise) {
877
920
  logger.info("Build already in progress, skipping...");
878
921
  return;
@@ -882,7 +925,6 @@ async function buildAll(dev = false) {
882
925
  watcher.setRebuildStatus(true);
883
926
 
884
927
  try {
885
- // If config or plugin file changed, reload config and plugins
886
928
  if (file.includes('vaderjs.config.ts') || file.includes('plugins')) {
887
929
  logger.info("Config or plugin changed, reloading...");
888
930
  config = await loadConfig();
@@ -891,7 +933,6 @@ async function buildAll(dev = false) {
891
933
  await loadPluginsFromDirectory();
892
934
  }
893
935
 
894
- // Run onFileChange hooks for plugins
895
936
  const pluginAPI = createPluginAPI();
896
937
  for (const plugin of plugins) {
897
938
  if (plugin.onFileChange) {
@@ -899,12 +940,10 @@ async function buildAll(dev = false) {
899
940
  }
900
941
  }
901
942
 
902
- // Rebuild
903
943
  buildPromise = buildAll(true);
904
944
  await buildPromise;
905
945
  buildPromise = null;
906
946
 
907
- // Trigger reload for all connected clients
908
947
  triggerReload(clients);
909
948
 
910
949
  } catch (error) {
@@ -918,7 +957,6 @@ async function buildAll(dev = false) {
918
957
  logger.success(`Dev server running http://localhost:${port}`);
919
958
  logger.info("Waiting for changes... (Press Ctrl+C to stop)");
920
959
 
921
- // Log available routes
922
960
  const distFiles = fsSync.readdirSync(DIST_DIR, { recursive: true });
923
961
  const htmlFiles = distFiles.filter(f => f.toString().endsWith('index.html'));
924
962
  if (htmlFiles.length > 0) {
@@ -978,7 +1016,6 @@ ${colors.reset}`);
978
1016
 
979
1017
  if (cmd === "init") {
980
1018
  await initProject(process.argv[3]);
981
- // Also create jsconfig.json when initializing a new project
982
1019
  await ensureJSConfig();
983
1020
  return;
984
1021
  }
@@ -1012,6 +1049,7 @@ Make sure you have:
1012
1049
  - App.tsx or App.jsx in your project root
1013
1050
  - OR an app/ directory with index.tsx/jsx files
1014
1051
  - vaderjs installed as a dependency
1052
+ - For Vercel deployment: set hosting: "vercel" in vaderjs.config.ts
1015
1053
  `);
1016
1054
  }
1017
1055
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vaderjs",
3
- "version": "2.3.18",
3
+ "version": "2.3.19",
4
4
  "description": "A simple and powerful JavaScript library for building modern web applications.",
5
5
  "bin": {
6
6
  "vaderjs": "./main.ts"