vaderjs 2.3.11 → 2.3.12
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/index.ts +342 -152
- package/main.js +305 -152
- package/package.json +1 -1
package/main.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
|
|
3
2
|
|
|
4
3
|
import { build, serve } from "bun";
|
|
5
4
|
import fs from "fs/promises";
|
|
6
5
|
import fsSync from "fs";
|
|
7
6
|
import path from "path";
|
|
8
|
-
import { initProject, addPlugin, listPlugins, removePlugin} from "./cli";
|
|
7
|
+
import { initProject, addPlugin, listPlugins, removePlugin } from "./cli";
|
|
9
8
|
|
|
10
9
|
// --- UTILITIES for a Sleek CLI ---
|
|
11
10
|
|
|
@@ -19,17 +18,6 @@ const colors = {
|
|
|
19
18
|
cyan: "\x1b[36m",
|
|
20
19
|
};
|
|
21
20
|
|
|
22
|
-
function safeWatch(dir, cb) {
|
|
23
|
-
try {
|
|
24
|
-
const watcher = fsSync.watch(dir, { recursive: true }, cb);
|
|
25
|
-
watcher.on("error", (err) => logger.warn(`Watcher error on ${dir}:`, err));
|
|
26
|
-
return watcher;
|
|
27
|
-
} catch (err) {
|
|
28
|
-
logger.warn(`Failed to watch ${dir}:`, err);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
21
|
const logger = {
|
|
34
22
|
_log: (color, ...args) => console.log(color, ...args, colors.reset),
|
|
35
23
|
info: (...args) => logger._log(colors.cyan, "ℹ", ...args),
|
|
@@ -62,6 +50,141 @@ const SRC_DIR = path.join(PROJECT_ROOT, "src");
|
|
|
62
50
|
const VADER_SRC_PATH = path.join(PROJECT_ROOT, "node_modules", "vaderjs", "index.ts");
|
|
63
51
|
const TEMP_SRC_DIR = path.join(PROJECT_ROOT, ".vader_temp_src");
|
|
64
52
|
|
|
53
|
+
// --- SIMPLIFIED WATCHER ---
|
|
54
|
+
|
|
55
|
+
class FileWatcher {
|
|
56
|
+
constructor() {
|
|
57
|
+
this.watchers = new Map();
|
|
58
|
+
this.onChangeCallbacks = [];
|
|
59
|
+
this.isRebuilding = false;
|
|
60
|
+
this.lastRebuildTime = 0;
|
|
61
|
+
this.REBUILD_COOLDOWN = 1000; // 1 second cooldown between rebuilds
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
shouldIgnorePath(filePath) {
|
|
65
|
+
const normalized = path.normalize(filePath);
|
|
66
|
+
// Ignore dist folder and its contents
|
|
67
|
+
if (normalized.includes(path.normalize(DIST_DIR))) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
// Ignore node_modules
|
|
71
|
+
if (normalized.includes(path.normalize('node_modules'))) {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
// Ignore .git folder
|
|
75
|
+
if (normalized.includes(path.normalize('.git'))) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
// Ignore the temporary source directory
|
|
79
|
+
if (normalized.includes(path.normalize(TEMP_SRC_DIR))) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async watchDirectory(dirPath, recursive = true) {
|
|
86
|
+
// Skip if directory should be ignored
|
|
87
|
+
if (this.shouldIgnorePath(dirPath) || !fsSync.existsSync(dirPath)) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
// Close existing watcher if any
|
|
93
|
+
if (this.watchers.has(dirPath)) {
|
|
94
|
+
try {
|
|
95
|
+
this.watchers.get(dirPath).close();
|
|
96
|
+
} catch (err) {
|
|
97
|
+
// Ignore close errors
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Create new watcher
|
|
102
|
+
const watcher = fsSync.watch(dirPath, { recursive }, (eventType, filename) => {
|
|
103
|
+
if (!filename) return;
|
|
104
|
+
|
|
105
|
+
const changedFile = path.join(dirPath, filename);
|
|
106
|
+
const normalizedChanged = path.normalize(changedFile);
|
|
107
|
+
|
|
108
|
+
// Skip if file should be ignored
|
|
109
|
+
if (this.shouldIgnorePath(normalizedChanged)) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Check if this is a file we care about
|
|
114
|
+
if (this.shouldTriggerRebuild(normalizedChanged)) {
|
|
115
|
+
logger.info(`File changed: ${path.relative(PROJECT_ROOT, normalizedChanged)}`);
|
|
116
|
+
|
|
117
|
+
// Only trigger if not already rebuilding and cooldown has passed
|
|
118
|
+
const now = Date.now();
|
|
119
|
+
if (!this.isRebuilding && (now - this.lastRebuildTime) > this.REBUILD_COOLDOWN) {
|
|
120
|
+
this.triggerChange(normalizedChanged);
|
|
121
|
+
} else if (this.isRebuilding) {
|
|
122
|
+
logger.info(`Skipping rebuild - already rebuilding`);
|
|
123
|
+
} else {
|
|
124
|
+
logger.info(`Skipping rebuild - cooldown period`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
watcher.on('error', (err) => {
|
|
130
|
+
logger.warn(`Watcher error on ${dirPath}:`, err.message);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
this.watchers.set(dirPath, watcher);
|
|
134
|
+
|
|
135
|
+
logger.info(`Watching directory: ${path.relative(PROJECT_ROOT, dirPath)}`);
|
|
136
|
+
} catch (err) {
|
|
137
|
+
logger.warn(`Could not watch directory ${dirPath}:`, err.message);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
shouldTriggerRebuild(filePath) {
|
|
142
|
+
// Only trigger rebuild for specific file types
|
|
143
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
144
|
+
const triggerExtensions = ['.js', '.jsx', '.ts', '.tsx', '.css', '.html', '.json', '.config.js', '.config.ts'];
|
|
145
|
+
return triggerExtensions.includes(ext) || ext === '';
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
triggerChange(filePath) {
|
|
149
|
+
for (const callback of this.onChangeCallbacks) {
|
|
150
|
+
try {
|
|
151
|
+
callback(filePath);
|
|
152
|
+
} catch (err) {
|
|
153
|
+
logger.error("Change callback error:", err);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
onChange(callback) {
|
|
159
|
+
this.onChangeCallbacks.push(callback);
|
|
160
|
+
return () => {
|
|
161
|
+
const index = this.onChangeCallbacks.indexOf(callback);
|
|
162
|
+
if (index > -1) this.onChangeCallbacks.splice(index, 1);
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
setRebuilding(state) {
|
|
167
|
+
this.isRebuilding = state;
|
|
168
|
+
if (state) {
|
|
169
|
+
this.lastRebuildTime = Date.now();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
clear() {
|
|
174
|
+
for (const [dir, watcher] of this.watchers) {
|
|
175
|
+
try {
|
|
176
|
+
watcher.close();
|
|
177
|
+
} catch (err) {
|
|
178
|
+
// Ignore close errors
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
this.watchers.clear();
|
|
182
|
+
this.onChangeCallbacks = [];
|
|
183
|
+
this.isRebuilding = false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const watcher = new FileWatcher();
|
|
65
188
|
|
|
66
189
|
// --- CONFIG & PLUGIN SYSTEM ---
|
|
67
190
|
|
|
@@ -77,8 +200,7 @@ const vaderAPI = {
|
|
|
77
200
|
injectHTML: (content) => htmlInjections.push(content),
|
|
78
201
|
log: {
|
|
79
202
|
warn: (msg) => logger.warn(msg),
|
|
80
|
-
info: (msg) => logger.info(msg),
|
|
81
|
-
error: (msg) => logger.error(msg),
|
|
203
|
+
info: (msg) => logger.info(msg),
|
|
82
204
|
success: (msg) => logger.success(msg),
|
|
83
205
|
step: (msg) => logger.step(msg)
|
|
84
206
|
},
|
|
@@ -89,11 +211,10 @@ const vaderAPI = {
|
|
|
89
211
|
|
|
90
212
|
async function loadConfig() {
|
|
91
213
|
try {
|
|
92
|
-
const configModule = await import(path.join(PROJECT_ROOT, "vaderjs.config.js"));
|
|
214
|
+
const configModule = await import(path.join(PROJECT_ROOT, "vaderjs.config.js"));
|
|
93
215
|
return configModule.default || configModule;
|
|
94
216
|
} catch {
|
|
95
|
-
|
|
96
|
-
logger.warn("No 'vader.config.js' found, using defaults.");
|
|
217
|
+
logger.warn("No 'vaderjs.config.js' found, using defaults.");
|
|
97
218
|
return {};
|
|
98
219
|
}
|
|
99
220
|
}
|
|
@@ -102,7 +223,7 @@ export function defineConfig(config) {
|
|
|
102
223
|
return config;
|
|
103
224
|
}
|
|
104
225
|
|
|
105
|
-
async function runPluginHook(hookName) {
|
|
226
|
+
async function runPluginHook(hookName) {
|
|
106
227
|
if (!config.plugins) return;
|
|
107
228
|
for (const plugin of config.plugins) {
|
|
108
229
|
if (typeof plugin[hookName] === "function") {
|
|
@@ -115,8 +236,6 @@ async function runPluginHook(hookName) {
|
|
|
115
236
|
}
|
|
116
237
|
}
|
|
117
238
|
|
|
118
|
-
|
|
119
|
-
|
|
120
239
|
// --- BUILD LOGIC ---
|
|
121
240
|
|
|
122
241
|
/**
|
|
@@ -146,6 +265,7 @@ async function buildVaderCore() {
|
|
|
146
265
|
function patchHooksUsage(code) {
|
|
147
266
|
return code.replace(/import\s+{[^}]*use(State|Effect|Memo|Navigation)[^}]*}\s+from\s+['"]vaderjs['"];?\n?/g, "");
|
|
148
267
|
}
|
|
268
|
+
|
|
149
269
|
function publicAssetPlugin() {
|
|
150
270
|
return {
|
|
151
271
|
name: "public-asset-replacer",
|
|
@@ -182,7 +302,6 @@ function publicAssetPlugin() {
|
|
|
182
302
|
/**
|
|
183
303
|
* Step 3: Pre-processes all files in `/src` into a temporary directory.
|
|
184
304
|
*/
|
|
185
|
-
|
|
186
305
|
async function preprocessSources(srcDir, tempDir) {
|
|
187
306
|
await fs.mkdir(tempDir, { recursive: true });
|
|
188
307
|
for (const entry of await fs.readdir(srcDir, { withFileTypes: true })) {
|
|
@@ -193,7 +312,7 @@ async function preprocessSources(srcDir, tempDir) {
|
|
|
193
312
|
await preprocessSources(srcPath, destPath);
|
|
194
313
|
} else if (/\.(tsx|jsx|ts|js)$/.test(entry.name)) {
|
|
195
314
|
let content = await fs.readFile(srcPath, "utf8");
|
|
196
|
-
content = patchHooksUsage(content);
|
|
315
|
+
content = patchHooksUsage(content);
|
|
197
316
|
await fs.writeFile(destPath, content);
|
|
198
317
|
} else {
|
|
199
318
|
await fs.copyFile(srcPath, destPath);
|
|
@@ -249,14 +368,28 @@ async function copyPublicAssets() {
|
|
|
249
368
|
}
|
|
250
369
|
}
|
|
251
370
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
371
|
+
async function buildAppEntrypoint(entryPath, name, isDev = false) {
|
|
372
|
+
const outDir = path.join(DIST_DIR, name === 'index' ? '' : name);
|
|
373
|
+
const outJsPath = path.join(outDir, 'index.js');
|
|
374
|
+
await fs.mkdir(outDir, { recursive: true });
|
|
375
|
+
|
|
376
|
+
// --- CSS HANDLING ---
|
|
377
|
+
const cssLinks = [];
|
|
378
|
+
let content = await fs.readFile(entryPath, "utf8");
|
|
379
|
+
const cssImports = [...content.matchAll(/import\s+['"](.*\.css)['"]/g)];
|
|
380
|
+
for (const match of cssImports) {
|
|
381
|
+
const cssImportPath = match[1];
|
|
382
|
+
const sourceCssPath = path.resolve(path.dirname(entryPath), cssImportPath);
|
|
383
|
+
if (fsSync.existsSync(sourceCssPath)) {
|
|
384
|
+
const relativeCssPath = path.relative(APP_DIR, sourceCssPath);
|
|
385
|
+
const destCssPath = path.join(DIST_DIR, relativeCssPath);
|
|
386
|
+
await fs.mkdir(path.dirname(destCssPath), { recursive: true });
|
|
387
|
+
await fs.copyFile(sourceCssPath, destCssPath);
|
|
388
|
+
const htmlRelativePath = path.relative(outDir, destCssPath).replace(/\\/g, '/');
|
|
389
|
+
cssLinks.push(`<link rel="stylesheet" href="${htmlRelativePath}">`);
|
|
390
|
+
} else {
|
|
391
|
+
logger.warn(`CSS file not found: ${sourceCssPath}`);
|
|
392
|
+
}
|
|
260
393
|
}
|
|
261
394
|
|
|
262
395
|
const devClientScript = isDev
|
|
@@ -267,49 +400,8 @@ async function copyPublicAssets() {
|
|
|
267
400
|
</script>`
|
|
268
401
|
: "";
|
|
269
402
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
.map(file => ({
|
|
273
|
-
name: path.dirname(file) === '.' ? 'index' : path.dirname(file).replace(/\\/g, '/'),
|
|
274
|
-
path: path.join(APP_DIR, file)
|
|
275
|
-
}));
|
|
276
|
-
|
|
277
|
-
// Helper to resolve any asset path from /public
|
|
278
|
-
function resolvePublicPath(p) {
|
|
279
|
-
const assetPath = p.replace(/^(\.\/|\/)/, ""); // strip leading ./ or /
|
|
280
|
-
const absPath = path.join(PUBLIC_DIR, assetPath);
|
|
281
|
-
if (fsSync.existsSync(absPath)) {
|
|
282
|
-
return "/" + assetPath.replace(/\\/g, "/");
|
|
283
|
-
}
|
|
284
|
-
return p; // leave unchanged if not in public
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
for (const { name, path: entryPath } of entries) {
|
|
288
|
-
const outDir = path.join(DIST_DIR, name === 'index' ? '' : name);
|
|
289
|
-
const outJsPath = path.join(outDir, 'index.js');
|
|
290
|
-
await fs.mkdir(outDir, { recursive: true });
|
|
291
|
-
|
|
292
|
-
// --- CSS HANDLING ---
|
|
293
|
-
const cssLinks = [];
|
|
294
|
-
let content = await fs.readFile(entryPath, "utf8");
|
|
295
|
-
const cssImports = [...content.matchAll(/import\s+['"](.*\.css)['"]/g)];
|
|
296
|
-
for (const match of cssImports) {
|
|
297
|
-
const cssImportPath = match[1];
|
|
298
|
-
const sourceCssPath = path.resolve(path.dirname(entryPath), cssImportPath);
|
|
299
|
-
if (fsSync.existsSync(sourceCssPath)) {
|
|
300
|
-
const relativeCssPath = path.relative(APP_DIR, sourceCssPath);
|
|
301
|
-
const destCssPath = path.join(DIST_DIR, relativeCssPath);
|
|
302
|
-
await fs.mkdir(path.dirname(destCssPath), { recursive: true });
|
|
303
|
-
await fs.copyFile(sourceCssPath, destCssPath);
|
|
304
|
-
const htmlRelativePath = path.relative(outDir, destCssPath).replace(/\\/g, '/');
|
|
305
|
-
cssLinks.push(`<link rel="stylesheet" href="${htmlRelativePath}">`);
|
|
306
|
-
} else {
|
|
307
|
-
logger.warn(`CSS file not found: ${sourceCssPath}`);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// --- HTML GENERATION ---
|
|
312
|
-
let htmlContent = `<!DOCTYPE html>
|
|
403
|
+
// --- HTML GENERATION ---
|
|
404
|
+
let htmlContent = `<!DOCTYPE html>
|
|
313
405
|
<html lang="en">
|
|
314
406
|
<head>
|
|
315
407
|
<meta charset="UTF-8" />
|
|
@@ -330,50 +422,79 @@ async function copyPublicAssets() {
|
|
|
330
422
|
</body>
|
|
331
423
|
</html>`;
|
|
332
424
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
)
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
425
|
+
// Helper to resolve any asset path from /public
|
|
426
|
+
function resolvePublicPath(p) {
|
|
427
|
+
const assetPath = p.replace(/^(\.\/|\/)/, ""); // strip leading ./ or /
|
|
428
|
+
const absPath = path.join(PUBLIC_DIR, assetPath);
|
|
429
|
+
if (fsSync.existsSync(absPath)) {
|
|
430
|
+
return "/" + assetPath.replace(/\\/g, "/");
|
|
431
|
+
}
|
|
432
|
+
return p; // leave unchanged if not in public
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// --- FIX ASSET PATHS IN HTML ---
|
|
436
|
+
htmlContent = htmlContent.replace(
|
|
437
|
+
/(["'(])([^"'()]+?\.(png|jpe?g|gif|svg|webp|ico))(["')])/gi,
|
|
438
|
+
(match, p1, assetPath, ext, p4) => p1 + resolvePublicPath(assetPath) + p4
|
|
439
|
+
);
|
|
440
|
+
|
|
441
|
+
await fs.writeFile(path.join(outDir, "index.html"), htmlContent);
|
|
442
|
+
|
|
443
|
+
// --- JS BUILD ---
|
|
444
|
+
await build({
|
|
445
|
+
entrypoints: [entryPath],
|
|
446
|
+
outdir: outDir,
|
|
447
|
+
target: "browser",
|
|
448
|
+
minify: false,
|
|
449
|
+
sourcemap: "external",
|
|
450
|
+
external: ["vaderjs"],
|
|
451
|
+
jsxFactory: "e",
|
|
452
|
+
jsxFragment: "Fragment",
|
|
453
|
+
plugins: [
|
|
352
454
|
publicAssetPlugin(),
|
|
353
455
|
],
|
|
354
|
-
|
|
355
|
-
|
|
456
|
+
jsxImportSource: "vaderjs",
|
|
457
|
+
});
|
|
356
458
|
|
|
357
|
-
|
|
358
|
-
|
|
459
|
+
// --- FIX IMPORT PATHS IN JS ---
|
|
460
|
+
let jsContent = await fs.readFile(outJsPath, "utf8");
|
|
359
461
|
|
|
360
|
-
|
|
361
|
-
|
|
462
|
+
// Vader import fix
|
|
463
|
+
jsContent = jsContent.replace(/from\s+['"]vaderjs['"]/g, `from '/src/vader/index.js'`);
|
|
362
464
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
465
|
+
// Asset path fix for JS
|
|
466
|
+
jsContent = jsContent.replace(
|
|
467
|
+
/(["'(])([^"'()]+?\.(png|jpe?g|gif|svg|webp|ico))(["')])/gi,
|
|
468
|
+
(match, p1, assetPath, ext, p4) => p1 + resolvePublicPath(assetPath) + p4
|
|
469
|
+
);
|
|
368
470
|
|
|
369
|
-
|
|
370
|
-
}
|
|
471
|
+
await fs.writeFile(outJsPath, jsContent);
|
|
371
472
|
}
|
|
372
473
|
|
|
474
|
+
async function buildAppEntrypoints(isDev = false) {
|
|
475
|
+
if (!fsSync.existsSync(APP_DIR)) {
|
|
476
|
+
logger.warn("No '/app' directory found, skipping app entrypoint build.");
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (!fsSync.existsSync(DIST_DIR)) {
|
|
481
|
+
await fs.mkdir(DIST_DIR, { recursive: true });
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Find all index.jsx/tsx files in app directory
|
|
485
|
+
const entries = fsSync.readdirSync(APP_DIR, { recursive: true })
|
|
486
|
+
.filter(file => /index\.(jsx|tsx)$/.test(file))
|
|
487
|
+
.map(file => ({
|
|
488
|
+
name: path.dirname(file) === '.' ? 'index' : path.dirname(file).replace(/\\/g, '/'),
|
|
489
|
+
path: path.join(APP_DIR, file)
|
|
490
|
+
}));
|
|
373
491
|
|
|
374
|
-
|
|
492
|
+
for (const { name, path: entryPath } of entries) {
|
|
493
|
+
await buildAppEntrypoint(entryPath, name, isDev);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
375
496
|
|
|
376
|
-
|
|
497
|
+
async function buildAll(isDev = false) {
|
|
377
498
|
logger.info(`Starting VaderJS ${isDev ? 'development' : 'production'} build...`);
|
|
378
499
|
const totalTime = performance.now();
|
|
379
500
|
|
|
@@ -411,7 +532,35 @@ async function runDevServer() {
|
|
|
411
532
|
|
|
412
533
|
logger.info(`Starting dev server at http://localhost:${port}`);
|
|
413
534
|
|
|
414
|
-
|
|
535
|
+
// Set up watchers for all important directories
|
|
536
|
+
logger.info("Setting up file watchers...");
|
|
537
|
+
|
|
538
|
+
// Clear any existing watchers
|
|
539
|
+
watcher.clear();
|
|
540
|
+
|
|
541
|
+
// Watch app directory (excluding dist)
|
|
542
|
+
if (fsSync.existsSync(APP_DIR)) {
|
|
543
|
+
await watcher.watchDirectory(APP_DIR, true);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Watch src directory (excluding dist)
|
|
547
|
+
if (fsSync.existsSync(SRC_DIR)) {
|
|
548
|
+
await watcher.watchDirectory(SRC_DIR, true);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// Watch public directory (excluding dist)
|
|
552
|
+
if (fsSync.existsSync(PUBLIC_DIR)) {
|
|
553
|
+
await watcher.watchDirectory(PUBLIC_DIR, true);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// Watch config files by watching their directory
|
|
557
|
+
const configPath = path.join(PROJECT_ROOT, "vaderjs.config.js");
|
|
558
|
+
const configPathTs = path.join(PROJECT_ROOT, "vaderjs.config.ts");
|
|
559
|
+
|
|
560
|
+
// Watch the project root for config files
|
|
561
|
+
await watcher.watchDirectory(PROJECT_ROOT, false);
|
|
562
|
+
|
|
563
|
+
const server = serve({
|
|
415
564
|
port,
|
|
416
565
|
fetch(req, server) {
|
|
417
566
|
const url = new URL(req.url);
|
|
@@ -433,40 +582,51 @@ async function runDevServer() {
|
|
|
433
582
|
},
|
|
434
583
|
});
|
|
435
584
|
|
|
436
|
-
const
|
|
437
|
-
logger.info(`File change detected: ${filename || 'unknown'}`);
|
|
585
|
+
const rebuild = async (changedFile) => {
|
|
438
586
|
try {
|
|
439
|
-
|
|
440
|
-
|
|
587
|
+
watcher.setRebuilding(true);
|
|
588
|
+
|
|
589
|
+
logger.info(`Rebuilding due to: ${path.relative(PROJECT_ROOT, changedFile)}`);
|
|
590
|
+
|
|
591
|
+
// Reload config if config file changed
|
|
592
|
+
if (changedFile.includes('vaderjs.config.')) {
|
|
593
|
+
config = await loadConfig();
|
|
594
|
+
}
|
|
441
595
|
|
|
596
|
+
// Always rebuild when something changes in development
|
|
442
597
|
await buildAll(true);
|
|
443
598
|
|
|
599
|
+
// Send reload signal to all clients
|
|
444
600
|
for (const client of clients) {
|
|
445
601
|
client.send("reload");
|
|
446
602
|
}
|
|
603
|
+
|
|
604
|
+
logger.success("Rebuild complete");
|
|
447
605
|
} catch (e) {
|
|
448
606
|
logger.error("Rebuild failed:", e);
|
|
607
|
+
} finally {
|
|
608
|
+
watcher.setRebuilding(false);
|
|
449
609
|
}
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
// --- IMPROVED WATCHER ---
|
|
453
|
-
const configPath = path.join(PROJECT_ROOT, "vaderjs.config.js");
|
|
454
|
-
const configPathTs = path.join(PROJECT_ROOT, "vaderjs.config.ts");
|
|
610
|
+
};
|
|
455
611
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
configPath,
|
|
461
|
-
configPathTs
|
|
462
|
-
].filter(p => fsSync.existsSync(p));
|
|
612
|
+
// Set up file change listener
|
|
613
|
+
const removeListener = watcher.onChange((changedFile) => {
|
|
614
|
+
rebuild(changedFile);
|
|
615
|
+
});
|
|
463
616
|
|
|
464
|
-
|
|
617
|
+
// Cleanup on exit
|
|
618
|
+
const cleanup = () => {
|
|
619
|
+
removeListener();
|
|
620
|
+
watcher.clear();
|
|
621
|
+
server.stop();
|
|
622
|
+
process.exit(0);
|
|
623
|
+
};
|
|
465
624
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
625
|
+
process.on('SIGINT', cleanup);
|
|
626
|
+
process.on('SIGTERM', cleanup);
|
|
627
|
+
process.on('exit', cleanup);
|
|
469
628
|
}
|
|
629
|
+
|
|
470
630
|
async function runProdServer() {
|
|
471
631
|
const port = config.port || 3000;
|
|
472
632
|
logger.info(`Serving production build from /dist on http://localhost:${port}`);
|
|
@@ -486,14 +646,6 @@ async function runProdServer() {
|
|
|
486
646
|
});
|
|
487
647
|
}
|
|
488
648
|
|
|
489
|
-
function debounce(fn, delay) {
|
|
490
|
-
let timeoutId;
|
|
491
|
-
return (...args) => {
|
|
492
|
-
clearTimeout(timeoutId);
|
|
493
|
-
timeoutId = setTimeout(() => fn(...args), delay);
|
|
494
|
-
};
|
|
495
|
-
}
|
|
496
|
-
|
|
497
649
|
// --- SCRIPT ENTRYPOINT ---
|
|
498
650
|
|
|
499
651
|
async function main() {
|
|
@@ -522,7 +674,7 @@ async function main() {
|
|
|
522
674
|
logger.error("Please specify a plugin to add.");
|
|
523
675
|
process.exit(1);
|
|
524
676
|
}
|
|
525
|
-
await addPlugin(arg)
|
|
677
|
+
await addPlugin(arg);
|
|
526
678
|
return;
|
|
527
679
|
}
|
|
528
680
|
|
|
@@ -533,21 +685,21 @@ async function main() {
|
|
|
533
685
|
switch (command) {
|
|
534
686
|
case "add":
|
|
535
687
|
if (!arg) {
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
688
|
+
logger.error("Please specify a plugin to add.");
|
|
689
|
+
process.exit(1);
|
|
690
|
+
}
|
|
691
|
+
await addPlugin(arg);
|
|
692
|
+
return;
|
|
541
693
|
case "list_plugins":
|
|
542
|
-
await listPlugins()
|
|
543
|
-
return
|
|
544
|
-
case
|
|
694
|
+
await listPlugins();
|
|
695
|
+
return;
|
|
696
|
+
case "remove":
|
|
545
697
|
if (!arg) {
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
await removePlugin(arg)
|
|
550
|
-
|
|
698
|
+
logger.error("Please specify a plugin to remove.");
|
|
699
|
+
process.exit(1);
|
|
700
|
+
}
|
|
701
|
+
await removePlugin(arg);
|
|
702
|
+
return;
|
|
551
703
|
|
|
552
704
|
case "dev":
|
|
553
705
|
globalThis.isDev = true;
|
|
@@ -584,14 +736,15 @@ Available commands:
|
|
|
584
736
|
}
|
|
585
737
|
}
|
|
586
738
|
|
|
587
|
-
|
|
588
739
|
main().catch(err => {
|
|
589
740
|
logger.error("An unexpected error occurred:", err);
|
|
590
741
|
process.exit(1);
|
|
591
742
|
});
|
|
743
|
+
|
|
592
744
|
process.on("unhandledRejection", (err) => {
|
|
593
745
|
logger.error("Unhandled Promise rejection:", err);
|
|
594
746
|
});
|
|
747
|
+
|
|
595
748
|
process.on("uncaughtException", (err) => {
|
|
596
749
|
logger.error("Uncaught Exception:", err);
|
|
597
750
|
});
|