vaderjs-native 1.0.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 (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.MD +99 -0
  3. package/app-template/.idea/.name +1 -0
  4. package/app-template/.idea/AndroidProjectSystem.xml +6 -0
  5. package/app-template/.idea/codeStyles/Project.xml +123 -0
  6. package/app-template/.idea/codeStyles/codeStyleConfig.xml +5 -0
  7. package/app-template/.idea/compiler.xml +6 -0
  8. package/app-template/.idea/deploymentTargetSelector.xml +10 -0
  9. package/app-template/.idea/gradle.xml +19 -0
  10. package/app-template/.idea/inspectionProfiles/Project_Default.xml +61 -0
  11. package/app-template/.idea/migrations.xml +10 -0
  12. package/app-template/.idea/misc.xml +9 -0
  13. package/app-template/.idea/runConfigurations.xml +17 -0
  14. package/app-template/app/build.gradle.kts +54 -0
  15. package/app-template/app/proguard-rules.pro +21 -0
  16. package/app-template/app/src/main/AndroidManifest.xml +31 -0
  17. package/app-template/app/src/main/java/com/example/myapplication/MainActivity.kt +74 -0
  18. package/app-template/app/src/main/java/com/example/myapplication/ui/theme/Color.kt +11 -0
  19. package/app-template/app/src/main/java/com/example/myapplication/ui/theme/Theme.kt +34 -0
  20. package/app-template/app/src/main/java/com/example/myapplication/ui/theme/Type.kt +36 -0
  21. package/app-template/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
  22. package/app-template/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
  23. package/app-template/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
  24. package/app-template/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
  25. package/app-template/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
  26. package/app-template/app/src/main/res/values/strings.xml +3 -0
  27. package/app-template/app/src/main/res/values/themes.xml +4 -0
  28. package/app-template/build.gradle.kts +5 -0
  29. package/app-template/gradle/libs.versions.toml +30 -0
  30. package/app-template/gradle/wrapper/gradle-wrapper.jar +0 -0
  31. package/app-template/gradle/wrapper/gradle-wrapper.properties +9 -0
  32. package/app-template/gradle.properties +23 -0
  33. package/app-template/gradlew +251 -0
  34. package/app-template/gradlew.bat +94 -0
  35. package/app-template/settings.gradle.kts +23 -0
  36. package/cli.ts +232 -0
  37. package/config/index.ts +14 -0
  38. package/index.ts +1083 -0
  39. package/jsconfig.json +7 -0
  40. package/logo.png +0 -0
  41. package/main.js +643 -0
  42. package/package.json +20 -0
  43. package/plugins/index.ts +63 -0
  44. package/plugins/tailwind.ts +53 -0
package/jsconfig.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "compilerOptions": {
3
+ "jsx": "react",
4
+ "jsxFactory": "Vader.createElement",
5
+ "jsxFragmentFactory": "Fragment"
6
+ }
7
+ }
package/logo.png ADDED
Binary file
package/main.js ADDED
@@ -0,0 +1,643 @@
1
+ #!/usr/bin/env bun
2
+
3
+
4
+ import { build, serve } from "bun";
5
+ import fs from "fs/promises";
6
+ import fsSync from "fs";
7
+ import path from "path";
8
+ import { init } from "./cli";
9
+
10
+ // --- UTILITIES for a Sleek CLI ---
11
+
12
+
13
+
14
+ async function copyCompiledJavascriptToJava(isDev = false) {
15
+ if (isDev) return;
16
+
17
+ const javaAssetsDir = path.join(
18
+ PROJECT_ROOT,
19
+ "node_modules",
20
+ "vaderjs-native",
21
+ "app-template",
22
+ "app",
23
+ "src",
24
+ "main",
25
+ "assets",
26
+ "myapp"
27
+ );
28
+
29
+ // Remove old files/folder if it exists
30
+ if (fsSync.existsSync(javaAssetsDir)) {
31
+ await fs.rm(javaAssetsDir, { recursive: true, force: true });
32
+ }
33
+
34
+ // Recreate the directory and copy fresh files
35
+ await fs.mkdir(javaAssetsDir, { recursive: true });
36
+ await fs.cp(DIST_DIR, javaAssetsDir, { recursive: true });
37
+
38
+ logger.success(`Copied compiled Javascript to Java assets at ${javaAssetsDir}`);
39
+ }
40
+
41
+ import { spawnSync } from "child_process";
42
+ async function buildAPK() {
43
+ const ROOT = path.join(
44
+ PROJECT_ROOT,
45
+ "node_modules",
46
+ "vaderjs-native",
47
+ "app-template"
48
+ );
49
+
50
+ const BUILD_DIR = path.join(ROOT, "app", "build");
51
+
52
+ // Remove old build folder safely
53
+ if (fsSync.existsSync(BUILD_DIR)) {
54
+ try {
55
+ fsSync.rmSync(BUILD_DIR, { recursive: true, force: true });
56
+ console.log("Old build folder removed.");
57
+ } catch (err) {
58
+ console.warn("Failed to remove old build folder, continuing...", err);
59
+ }
60
+ }
61
+
62
+ const APK_DEST_DIR = path.join(PROJECT_ROOT, "build");
63
+ if (!fsSync.existsSync(APK_DEST_DIR)) {
64
+ fsSync.mkdirSync(APK_DEST_DIR, { recursive: true });
65
+ }
66
+
67
+ const gradleCmd = process.platform === "win32" ? "gradlew.bat" : "./gradlew";
68
+
69
+ console.log("Building APK...");
70
+
71
+ const result = spawnSync(gradleCmd, ["assembleDebug"], {
72
+ cwd: ROOT,
73
+ stdio: "inherit",
74
+ shell: true,
75
+ });
76
+
77
+ if (result.error) {
78
+ console.error("Error running Gradle:", result.error);
79
+ }
80
+
81
+ // Kill any lingering Java processes from this build (Windows)
82
+ if (process.platform === "win32") {
83
+ try {
84
+ execSync('taskkill /F /IM java.exe /T', { stdio: "ignore" });
85
+ console.log("Cleaned up lingering Java processes.");
86
+ } catch (err) {
87
+ console.warn("No lingering Java processes to clean.", err.message);
88
+ }
89
+ } else {
90
+ // macOS/Linux
91
+ try {
92
+ execSync("pkill -f java", { stdio: "ignore" });
93
+ console.log("Cleaned up lingering Java processes.");
94
+ } catch (err) {
95
+ console.warn("No lingering Java processes to clean.", err.message);
96
+ }
97
+ }
98
+
99
+ // APK paths
100
+ const APK_SRC = path.join(
101
+ ROOT,
102
+ "app",
103
+ "build",
104
+ "outputs",
105
+ "apk",
106
+ "debug",
107
+ "app-debug.apk"
108
+ );
109
+ const APK_DEST = path.join(APK_DEST_DIR, "myapp-debug.apk");
110
+
111
+ if (fsSync.existsSync(APK_SRC)) {
112
+ fsSync.copyFileSync(APK_SRC, APK_DEST);
113
+ console.log(`APK built successfully at ${APK_DEST}`);
114
+ } else {
115
+ console.error("APK build failed: APK file not found.");
116
+ }
117
+ }
118
+
119
+ function safeWatch(dir, cb) {
120
+ try {
121
+ const watcher = fsSync.watch(dir, { recursive: true }, cb);
122
+ watcher.on("error", (err) => logger.warn(`Watcher error on ${dir}:`, err));
123
+ return watcher;
124
+ } catch (err) {
125
+ logger.warn(`Failed to watch ${dir}:`, err);
126
+ }
127
+ }
128
+
129
+
130
+ const colors = {
131
+ reset: "\x1b[0m",
132
+ red: "\x1b[31m",
133
+ green: "\x1b[32m",
134
+ yellow: "\x1b[33m",
135
+ blue: "\x1b[34m",
136
+ magenta: "\x1b[35m",
137
+ cyan: "\x1b[36m",
138
+ gray: "\x1b[90m",
139
+ bold: "\x1b[1m",
140
+ };
141
+
142
+ const logger = {
143
+ _log: (level, color, symbol, ...args) => {
144
+ const timestamp = new Date().toISOString();
145
+ console.log(
146
+ `${colors.gray}[${timestamp}]${colors.reset} ${color}${symbol}${colors.reset} ${colors.bold}${level}:${colors.reset}`,
147
+ ...args
148
+ );
149
+ },
150
+ info: (...args) => logger._log("INFO", colors.cyan, "ℹ", ...args),
151
+ success: (...args) => logger._log("SUCCESS", colors.green, "✅", ...args),
152
+ warn: (...args) => logger._log("WARN", colors.yellow, "⚠️", ...args),
153
+ error: (...args) => logger._log("ERROR", colors.red, "❌", ...args),
154
+ step: (...args) => {
155
+ const separator = colors.magenta + "═".repeat(50) + colors.reset;
156
+ console.log(`\n${separator}`);
157
+ console.log(`${colors.magenta}🚀 STEP:${colors.reset} ${colors.bold}`, ...args);
158
+ console.log(separator);
159
+ },
160
+ debug: (...args) => logger._log("DEBUG", colors.blue, "🐛", ...args),
161
+ plugin: (...args) => logger._log("PLUGIN", colors.magenta, "🧩", ...args),
162
+ table: (title, data) => {
163
+ console.log(`${colors.cyan}${title}${colors.reset}`);
164
+ console.table(data);
165
+ },
166
+ };
167
+
168
+
169
+ async function timedStep(name, fn) {
170
+ logger.step(`${name}...`);
171
+ const start = performance.now();
172
+ try {
173
+ await fn();
174
+ const duration = (performance.now() - start).toFixed(2);
175
+ logger.success(`Finished '${name}' in ${duration}ms`);
176
+ } catch (e) {
177
+ logger.error(`Error during '${name}':`, e);
178
+ if (!isDev) process.exit(1);
179
+ }
180
+ }
181
+
182
+ // --- CONSTANTS ---
183
+
184
+ const PROJECT_ROOT = process.cwd();
185
+ const APP_DIR = path.join(PROJECT_ROOT, "app");
186
+ const PUBLIC_DIR = path.join(PROJECT_ROOT, "public");
187
+ const DIST_DIR = path.join(PROJECT_ROOT, "dist");
188
+ const SRC_DIR = path.join(PROJECT_ROOT, "src");
189
+ const VADER_SRC_PATH = path.join(PROJECT_ROOT, "node_modules", "vaderjs-native", "index.ts");
190
+ const TEMP_SRC_DIR = path.join(PROJECT_ROOT, ".vader_temp_src");
191
+
192
+
193
+ // --- CONFIG & PLUGIN SYSTEM ---
194
+
195
+ let config = {};
196
+ let htmlInjections = [];
197
+
198
+ const vaderAPI = {
199
+ runCommand: async (cmd) => {
200
+ if (typeof cmd === "string") cmd = cmd.split(" ");
201
+ const p = Bun.spawn(cmd);
202
+ await p.exited;
203
+ },
204
+ injectHTML: (content) => htmlInjections.push(content),
205
+ log: (msg) => logger.info(`[Plugin] ${msg}`),
206
+ getProjectRoot: () => PROJECT_ROOT,
207
+ getDistDir: () => DIST_DIR,
208
+ getPublicDir: () => PUBLIC_DIR,
209
+ };
210
+
211
+ async function loadConfig() {
212
+ try {
213
+ const configModule = await import(path.join(PROJECT_ROOT, "vaderjs.config.js"));
214
+ return configModule.default || configModule;
215
+ } catch {
216
+ console.log(path.join(PROJECT_ROOT, "vaderjs.config.js"))
217
+ logger.warn("No 'vader.config.js' found, using defaults.");
218
+ return {};
219
+ }
220
+ }
221
+
222
+ export function defineConfig(config) {
223
+ return config;
224
+ }
225
+
226
+ async function runPluginHook(hookName) {
227
+ if (!config.plugins) return;
228
+ for (const plugin of config.plugins) {
229
+ if (typeof plugin[hookName] === "function") {
230
+ try {
231
+ await plugin[hookName](vaderAPI);
232
+ } catch (e) {
233
+ logger.error(`Plugin hook error (${hookName} in ${plugin.name || 'anonymous'}):`, e);
234
+ }
235
+ }
236
+ }
237
+ }
238
+
239
+
240
+
241
+ // --- BUILD LOGIC ---
242
+
243
+ /**
244
+ * Step 1: Transpile and bundle the core vaderjs library.
245
+ */
246
+ async function buildVaderCore() {
247
+ if (!fsSync.existsSync(VADER_SRC_PATH)) {
248
+ logger.error("VaderJS source not found:", VADER_SRC_PATH);
249
+ throw new Error("Missing vaderjs dependency.");
250
+ }
251
+
252
+ await build({
253
+ entrypoints: [VADER_SRC_PATH],
254
+ outdir: path.join(DIST_DIR, "src", "vader"),
255
+ target: "browser",
256
+ minify: false,
257
+ sourcemap: "external",
258
+ jsxFactory: "e",
259
+ jsxFragment: "Fragment",
260
+ jsxImportSource: "vaderjs",
261
+ });
262
+ }
263
+
264
+ /**
265
+ * Step 2: Patches source code to remove server-side hook imports.
266
+ */
267
+ function patchHooksUsage(code) {
268
+ return code.replace(/import\s+{[^}]*use(State|Effect|Memo|Navigation)[^}]*}\s+from\s+['"]vaderjs['"];?\n?/g, "");
269
+ }
270
+ function publicAssetPlugin() {
271
+ return {
272
+ name: "public-asset-replacer",
273
+ setup(build) {
274
+ build.onLoad({ filter: /\.(js|ts|jsx|tsx|html)$/ }, async (args) => {
275
+ let code = await fs.readFile(args.path, "utf8");
276
+
277
+ code = code.replace(/\{\{public:(.+?)\}\}/g, (_, relPath) => {
278
+ const absPath = path.join(PUBLIC_DIR, relPath.trim());
279
+ if (fsSync.existsSync(absPath)) {
280
+ return "/" + relPath.trim().replace(/\\/g, "/");
281
+ }
282
+ logger.warn(`Public asset not found: ${relPath}`);
283
+ return relPath;
284
+ });
285
+
286
+ return {
287
+ contents: code,
288
+ loader: args.path.endsWith(".html")
289
+ ? "text"
290
+ : args.path.endsWith(".tsx")
291
+ ? "tsx"
292
+ : args.path.endsWith(".jsx")
293
+ ? "jsx"
294
+ : args.path.endsWith(".ts")
295
+ ? "ts"
296
+ : "js",
297
+ };
298
+ });
299
+ },
300
+ };
301
+ }
302
+
303
+ /**
304
+ * Step 3: Pre-processes all files in `/src` into a temporary directory.
305
+ */
306
+
307
+ async function preprocessSources(srcDir, tempDir) {
308
+ await fs.mkdir(tempDir, { recursive: true });
309
+ for (const entry of await fs.readdir(srcDir, { withFileTypes: true })) {
310
+ const srcPath = path.join(srcDir, entry.name);
311
+ const destPath = path.join(tempDir, entry.name);
312
+
313
+ if (entry.isDirectory()) {
314
+ await preprocessSources(srcPath, destPath);
315
+ } else if (/\.(tsx|jsx|ts|js)$/.test(entry.name)) {
316
+ let content = await fs.readFile(srcPath, "utf8");
317
+ content = patchHooksUsage(content);
318
+ await fs.writeFile(destPath, content);
319
+ } else {
320
+ await fs.copyFile(srcPath, destPath);
321
+ }
322
+ }
323
+ }
324
+
325
+ /**
326
+ * Step 4: Build the application's source code from the preprocessed temp directory.
327
+ */
328
+ async function buildSrc() {
329
+ if (!fsSync.existsSync(SRC_DIR)) return;
330
+
331
+ if (fsSync.existsSync(TEMP_SRC_DIR)) {
332
+ await fs.rm(TEMP_SRC_DIR, { recursive: true, force: true });
333
+ }
334
+ await preprocessSources(SRC_DIR, TEMP_SRC_DIR);
335
+
336
+ const entrypoints = fsSync.readdirSync(TEMP_SRC_DIR, { recursive: true })
337
+ .map(file => path.join(TEMP_SRC_DIR, file))
338
+ .filter(file => /\.(ts|tsx|js|jsx)$/.test(file));
339
+
340
+ if (entrypoints.length === 0) {
341
+ logger.info("No source files found in /src to build.");
342
+ return;
343
+ }
344
+
345
+ await build({
346
+ entrypoints,
347
+ outdir: path.join(DIST_DIR, "src"),
348
+ root: TEMP_SRC_DIR,
349
+ naming: { entry: "[dir]/[name].js" },
350
+ jsxFactory: "e",
351
+ jsxFragment: "Fragment",
352
+ jsxImportSource: "vaderjs-native",
353
+ target: "browser",
354
+ minify: false,
355
+ plugins: [
356
+ publicAssetPlugin(),
357
+ ],
358
+ external: ["vaderjs-native"],
359
+ });
360
+ }
361
+
362
+ /**
363
+ * Step 5: Copy all assets from the `/public` directory to `/dist`.
364
+ */
365
+ async function copyPublicAssets() {
366
+ if (!fsSync.existsSync(PUBLIC_DIR)) return;
367
+ // Copy contents of public into dist, not the public folder itself
368
+ for (const item of await fs.readdir(PUBLIC_DIR)) {
369
+ await fs.cp(path.join(PUBLIC_DIR, item), path.join(DIST_DIR, item), { recursive: true });
370
+ }
371
+ }
372
+
373
+ async function buildAppEntrypoints(isDev = false) {
374
+ if (!fsSync.existsSync(APP_DIR)) {
375
+ logger.warn("No '/app' directory found, skipping app entrypoint build.");
376
+ return;
377
+ }
378
+
379
+ if (!fsSync.existsSync(DIST_DIR)) {
380
+ await fs.mkdir(DIST_DIR, { recursive: true });
381
+ }
382
+
383
+ const devClientScript = isDev
384
+ ? `<script>
385
+ new WebSocket("ws://" + location.host + "/__hmr").onmessage = (msg) => {
386
+ if (msg.data === "reload") location.reload();
387
+ };
388
+ </script>`
389
+ : "";
390
+
391
+ const entries = fsSync.readdirSync(APP_DIR, { recursive: true })
392
+ .filter(file => /index\.(jsx|tsx)$/.test(file))
393
+ .map(file => ({
394
+ name: path.dirname(file) === '.' ? 'index' : path.dirname(file).replace(/\\/g, '/'),
395
+ path: path.join(APP_DIR, file)
396
+ }));
397
+
398
+ // Helper to resolve any asset path from /public
399
+ function resolvePublicPath(p) {
400
+ const assetPath = p.replace(/^(\.\/|\/)/, ""); // strip leading ./ or /
401
+ const absPath = path.join(PUBLIC_DIR, assetPath);
402
+ if (fsSync.existsSync(absPath)) {
403
+ return "/" + assetPath.replace(/\\/g, "/");
404
+ }
405
+ return p; // leave unchanged if not in public
406
+ }
407
+
408
+ for (const { name, path: entryPath } of entries) {
409
+ const outDir = path.join(DIST_DIR, name === 'index' ? '' : name);
410
+ const outJsPath = path.join(outDir, 'index.js');
411
+ await fs.mkdir(outDir, { recursive: true });
412
+
413
+ // --- CSS HANDLING ---
414
+ const cssLinks = [];
415
+ let content = await fs.readFile(entryPath, "utf8");
416
+ const cssImports = [...content.matchAll(/import\s+['"](.*\.css)['"]/g)];
417
+ for (const match of cssImports) {
418
+ const cssImportPath = match[1];
419
+ const sourceCssPath = path.resolve(path.dirname(entryPath), cssImportPath);
420
+ if (fsSync.existsSync(sourceCssPath)) {
421
+ const relativeCssPath = path.relative(APP_DIR, sourceCssPath);
422
+ const destCssPath = path.join(DIST_DIR, relativeCssPath);
423
+ await fs.copyFile(sourceCssPath, destCssPath);
424
+ const htmlRelativePath = path.relative(outDir, destCssPath).replace(/\\/g, '/');
425
+ cssLinks.push(`<link rel="stylesheet" href="${htmlRelativePath}">`);
426
+ } else {
427
+ logger.warn(`CSS file not found: ${sourceCssPath}`);
428
+ }
429
+ }
430
+
431
+ // --- HTML GENERATION ---
432
+ let htmlContent = `<!DOCTYPE html>
433
+ <html lang="en">
434
+ <head>
435
+ <meta charset="UTF-8" />
436
+ <title>VaderJS App - ${name}</title>
437
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
438
+ ${cssLinks.join("\n ")}
439
+ ${htmlInjections.join("\n ")}
440
+ </head>
441
+ <body>
442
+ <div id="app"></div>
443
+ <script src="./index.js"></script>
444
+ ${devClientScript}
445
+ </body>
446
+ </html>`;
447
+
448
+ // --- FIX ASSET PATHS IN HTML ---
449
+ htmlContent = htmlContent.replace(
450
+ /(["'(])([^"'()]+?\.(png|jpe?g|gif|svg|webp|ico))(["')])/gi,
451
+ (match, p1, assetPath, ext, p4) => p1 + resolvePublicPath(assetPath) + p4
452
+ );
453
+
454
+ await fs.writeFile(path.join(outDir, "index.html"), htmlContent);
455
+
456
+ // --- JS BUILD ---
457
+ await build({
458
+ entrypoints: [entryPath],
459
+ outdir: outDir,
460
+ target: "browser",
461
+ minify: false,
462
+ sourcemap: "external",
463
+ jsxFactory: "Vader.createElement",
464
+ jsxFragment: "Fragment",
465
+ plugins: [
466
+ publicAssetPlugin(),
467
+ ],
468
+ });
469
+
470
+ // --- FIX IMPORT PATHS IN JS ---
471
+ let jsContent = await fs.readFile(outJsPath, "utf8");
472
+ const vaderSource = await fs.readFile(VADER_SRC_PATH, "utf8");
473
+ // Vader import fix
474
+
475
+ // Asset path fix for JS
476
+ jsContent = jsContent.replace(
477
+ /import\s+\*\s+as\s+Vader\s+from\s+['"]vaderjs['"];?/,
478
+ vaderSource
479
+ );
480
+
481
+ await fs.writeFile(outJsPath, jsContent);
482
+ }
483
+ }
484
+
485
+
486
+
487
+
488
+ async function buildAll(isDev = false) {
489
+ logger.info(`Starting VaderJS ${isDev ? 'development' : 'production'} build...`);
490
+ const totalTime = performance.now();
491
+
492
+ htmlInjections = [];
493
+
494
+ // Ensure dist directory exists before cleaning
495
+ if (fsSync.existsSync(DIST_DIR)) {
496
+ await fs.rm(DIST_DIR, { recursive: true, force: true });
497
+ }
498
+
499
+ // Create the dist directory if it doesn't exist
500
+ await fs.mkdir(DIST_DIR, { recursive: true });
501
+
502
+ await runPluginHook("onBuildStart");
503
+
504
+ // Build the components in steps and handle errors properly
505
+ await timedStep("Building VaderJS Core", buildVaderCore);
506
+ await timedStep("Building App Source (/src)", buildSrc);
507
+ await timedStep("Copying Public Assets", copyPublicAssets);
508
+ await timedStep("Building App Entrypoints (/app)", () => buildAppEntrypoints(isDev));
509
+ await timedStep("Copy Compiled Javascript to Java", () => copyCompiledJavascriptToJava(isDev));
510
+ await timedStep("Building APK", buildAPK);
511
+
512
+ await runPluginHook("onBuildFinish");
513
+
514
+ // Calculate the total duration and log it
515
+ const duration = (performance.now() - totalTime).toFixed(2);
516
+ logger.success(`Total build finished in ${duration}ms. Output is in /dist.`);
517
+ }
518
+
519
+ async function runDevServer() {
520
+ await buildAll(true);
521
+
522
+ const clients = new Set();
523
+ const port = config.port || 3000;
524
+
525
+ logger.info(`Starting dev server at http://localhost:${port}`);
526
+
527
+ serve({
528
+ port,
529
+ fetch(req, server) {
530
+ const url = new URL(req.url);
531
+ if (url.pathname === "/__hmr" && server.upgrade(req)) {
532
+ return;
533
+ }
534
+ let filePath = path.join(DIST_DIR, url.pathname);
535
+ if (!path.extname(filePath)) {
536
+ filePath = path.join(filePath, "index.html");
537
+ }
538
+ const file = Bun.file(filePath);
539
+ return file.exists().then(exists =>
540
+ exists ? new Response(file) : new Response("Not Found", { status: 404 })
541
+ );
542
+ },
543
+ websocket: {
544
+ open: (ws) => clients.add(ws),
545
+ close: (ws) => clients.delete(ws),
546
+ },
547
+ });
548
+
549
+ const debouncedBuild = debounce(async () => {
550
+ try {
551
+ await buildAll(true);
552
+ for (const client of clients) {
553
+ client.send("reload");
554
+ }
555
+ } catch (e) {
556
+ logger.error("Rebuild failed:", e);
557
+ }
558
+ }, 200);
559
+
560
+ const watchDirs = [APP_DIR, SRC_DIR, PUBLIC_DIR].filter(fsSync.existsSync);
561
+ for (const dir of watchDirs) {
562
+ safeWatch(dir, debouncedBuild);
563
+ }
564
+ }
565
+
566
+ async function runProdServer() {
567
+ const port = config.port || 3000;
568
+ logger.info(`Serving production build from /dist on http://localhost:${port}`);
569
+ serve({
570
+ port,
571
+ fetch(req) {
572
+ const url = new URL(req.url);
573
+ let filePath = path.join(DIST_DIR, url.pathname);
574
+ if (!path.extname(filePath)) {
575
+ filePath = path.join(filePath, "index.html");
576
+ }
577
+ const file = Bun.file(filePath);
578
+ return file.exists().then(exists =>
579
+ exists ? new Response(file) : new Response("Not Found", { status: 404 })
580
+ );
581
+ },
582
+ });
583
+ }
584
+
585
+ function debounce(fn, delay) {
586
+ let timeoutId;
587
+ return (...args) => {
588
+ clearTimeout(timeoutId);
589
+ timeoutId = setTimeout(() => fn(...args), delay);
590
+ };
591
+ }
592
+
593
+ // --- SCRIPT ENTRYPOINT ---
594
+
595
+ async function main() {
596
+ const banner = `${colors.magenta}
597
+ __ __ ____ ____ _______ __
598
+ | | / |/ __ \ / __ \ / ____/ |/ /
599
+ | | / / / / // /_/ // /___ | /
600
+ | | / / /_/ / \____// /___ / |
601
+ |____/____/_____/ /_____/ |_| |_|
602
+ ${colors.reset}`;
603
+
604
+ console.log(banner);
605
+
606
+
607
+ config = await loadConfig();
608
+ config.port = config.port || 3000;
609
+
610
+ const command = process.argv[2];
611
+
612
+ if (command === "dev") {
613
+ globalThis.isDev = true
614
+ await runDevServer();
615
+ } else if (command === "build") {
616
+ await buildAll(false);
617
+ } else if (command === "serve") {
618
+ await buildAll(false);
619
+ await runProdServer();
620
+ }
621
+ else if (command === "init") {
622
+ init().catch((e) => {
623
+ console.error("Initialization failed:", e);
624
+ process.exit(1);
625
+ });
626
+
627
+ } else {
628
+ logger.error(`Unknown command: '${command}'.`);
629
+ logger.info("Available commands: 'dev', 'build', 'serve', 'init'");
630
+ process.exit(1);
631
+ }
632
+ }
633
+
634
+ main().catch(err => {
635
+ logger.error("An unexpected error occurred:", err);
636
+ process.exit(1);
637
+ });
638
+ process.on("unhandledRejection", (err) => {
639
+ logger.error("Unhandled Promise rejection:", err);
640
+ });
641
+ process.on("uncaughtException", (err) => {
642
+ logger.error("Uncaught Exception:", err);
643
+ });
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "vaderjs-native",
3
+ "version": "1.0.0",
4
+ "description": "Build Android Applications using Vaderjs framework.",
5
+ "bin": {
6
+ "vaderjs": "./main.js"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/Postr-Inc/Vaderjs-Native"
11
+ },
12
+ "license": "MIT",
13
+ "dependencies": {
14
+ "@tailwindcss/postcss": "^4.1.18",
15
+ "ansi-colors": "latest",
16
+ "autoprefixer": "^10.4.23",
17
+ "postcss-cli": "^11.0.1",
18
+ "tailwindcss": "4"
19
+ }
20
+ }