vaderjs 2.3.10 → 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 +304 -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
|
}
|
|
@@ -103,7 +224,6 @@ export function defineConfig(config) {
|
|
|
103
224
|
}
|
|
104
225
|
|
|
105
226
|
async function runPluginHook(hookName) {
|
|
106
|
-
console.log(config)
|
|
107
227
|
if (!config.plugins) return;
|
|
108
228
|
for (const plugin of config.plugins) {
|
|
109
229
|
if (typeof plugin[hookName] === "function") {
|
|
@@ -116,8 +236,6 @@ async function runPluginHook(hookName) {
|
|
|
116
236
|
}
|
|
117
237
|
}
|
|
118
238
|
|
|
119
|
-
|
|
120
|
-
|
|
121
239
|
// --- BUILD LOGIC ---
|
|
122
240
|
|
|
123
241
|
/**
|
|
@@ -147,6 +265,7 @@ async function buildVaderCore() {
|
|
|
147
265
|
function patchHooksUsage(code) {
|
|
148
266
|
return code.replace(/import\s+{[^}]*use(State|Effect|Memo|Navigation)[^}]*}\s+from\s+['"]vaderjs['"];?\n?/g, "");
|
|
149
267
|
}
|
|
268
|
+
|
|
150
269
|
function publicAssetPlugin() {
|
|
151
270
|
return {
|
|
152
271
|
name: "public-asset-replacer",
|
|
@@ -183,7 +302,6 @@ function publicAssetPlugin() {
|
|
|
183
302
|
/**
|
|
184
303
|
* Step 3: Pre-processes all files in `/src` into a temporary directory.
|
|
185
304
|
*/
|
|
186
|
-
|
|
187
305
|
async function preprocessSources(srcDir, tempDir) {
|
|
188
306
|
await fs.mkdir(tempDir, { recursive: true });
|
|
189
307
|
for (const entry of await fs.readdir(srcDir, { withFileTypes: true })) {
|
|
@@ -194,7 +312,7 @@ async function preprocessSources(srcDir, tempDir) {
|
|
|
194
312
|
await preprocessSources(srcPath, destPath);
|
|
195
313
|
} else if (/\.(tsx|jsx|ts|js)$/.test(entry.name)) {
|
|
196
314
|
let content = await fs.readFile(srcPath, "utf8");
|
|
197
|
-
content = patchHooksUsage(content);
|
|
315
|
+
content = patchHooksUsage(content);
|
|
198
316
|
await fs.writeFile(destPath, content);
|
|
199
317
|
} else {
|
|
200
318
|
await fs.copyFile(srcPath, destPath);
|
|
@@ -250,14 +368,28 @@ async function copyPublicAssets() {
|
|
|
250
368
|
}
|
|
251
369
|
}
|
|
252
370
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
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
|
+
}
|
|
261
393
|
}
|
|
262
394
|
|
|
263
395
|
const devClientScript = isDev
|
|
@@ -268,49 +400,8 @@ async function copyPublicAssets() {
|
|
|
268
400
|
</script>`
|
|
269
401
|
: "";
|
|
270
402
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
.map(file => ({
|
|
274
|
-
name: path.dirname(file) === '.' ? 'index' : path.dirname(file).replace(/\\/g, '/'),
|
|
275
|
-
path: path.join(APP_DIR, file)
|
|
276
|
-
}));
|
|
277
|
-
|
|
278
|
-
// Helper to resolve any asset path from /public
|
|
279
|
-
function resolvePublicPath(p) {
|
|
280
|
-
const assetPath = p.replace(/^(\.\/|\/)/, ""); // strip leading ./ or /
|
|
281
|
-
const absPath = path.join(PUBLIC_DIR, assetPath);
|
|
282
|
-
if (fsSync.existsSync(absPath)) {
|
|
283
|
-
return "/" + assetPath.replace(/\\/g, "/");
|
|
284
|
-
}
|
|
285
|
-
return p; // leave unchanged if not in public
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
for (const { name, path: entryPath } of entries) {
|
|
289
|
-
const outDir = path.join(DIST_DIR, name === 'index' ? '' : name);
|
|
290
|
-
const outJsPath = path.join(outDir, 'index.js');
|
|
291
|
-
await fs.mkdir(outDir, { recursive: true });
|
|
292
|
-
|
|
293
|
-
// --- CSS HANDLING ---
|
|
294
|
-
const cssLinks = [];
|
|
295
|
-
let content = await fs.readFile(entryPath, "utf8");
|
|
296
|
-
const cssImports = [...content.matchAll(/import\s+['"](.*\.css)['"]/g)];
|
|
297
|
-
for (const match of cssImports) {
|
|
298
|
-
const cssImportPath = match[1];
|
|
299
|
-
const sourceCssPath = path.resolve(path.dirname(entryPath), cssImportPath);
|
|
300
|
-
if (fsSync.existsSync(sourceCssPath)) {
|
|
301
|
-
const relativeCssPath = path.relative(APP_DIR, sourceCssPath);
|
|
302
|
-
const destCssPath = path.join(DIST_DIR, relativeCssPath);
|
|
303
|
-
await fs.mkdir(path.dirname(destCssPath), { recursive: true });
|
|
304
|
-
await fs.copyFile(sourceCssPath, destCssPath);
|
|
305
|
-
const htmlRelativePath = path.relative(outDir, destCssPath).replace(/\\/g, '/');
|
|
306
|
-
cssLinks.push(`<link rel="stylesheet" href="${htmlRelativePath}">`);
|
|
307
|
-
} else {
|
|
308
|
-
logger.warn(`CSS file not found: ${sourceCssPath}`);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// --- HTML GENERATION ---
|
|
313
|
-
let htmlContent = `<!DOCTYPE html>
|
|
403
|
+
// --- HTML GENERATION ---
|
|
404
|
+
let htmlContent = `<!DOCTYPE html>
|
|
314
405
|
<html lang="en">
|
|
315
406
|
<head>
|
|
316
407
|
<meta charset="UTF-8" />
|
|
@@ -331,50 +422,79 @@ async function copyPublicAssets() {
|
|
|
331
422
|
</body>
|
|
332
423
|
</html>`;
|
|
333
424
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
)
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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: [
|
|
353
454
|
publicAssetPlugin(),
|
|
354
455
|
],
|
|
355
|
-
|
|
356
|
-
|
|
456
|
+
jsxImportSource: "vaderjs",
|
|
457
|
+
});
|
|
357
458
|
|
|
358
|
-
|
|
359
|
-
|
|
459
|
+
// --- FIX IMPORT PATHS IN JS ---
|
|
460
|
+
let jsContent = await fs.readFile(outJsPath, "utf8");
|
|
360
461
|
|
|
361
|
-
|
|
362
|
-
|
|
462
|
+
// Vader import fix
|
|
463
|
+
jsContent = jsContent.replace(/from\s+['"]vaderjs['"]/g, `from '/src/vader/index.js'`);
|
|
363
464
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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
|
+
);
|
|
369
470
|
|
|
370
|
-
|
|
371
|
-
}
|
|
471
|
+
await fs.writeFile(outJsPath, jsContent);
|
|
372
472
|
}
|
|
373
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
|
+
}));
|
|
374
491
|
|
|
375
|
-
|
|
492
|
+
for (const { name, path: entryPath } of entries) {
|
|
493
|
+
await buildAppEntrypoint(entryPath, name, isDev);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
376
496
|
|
|
377
|
-
|
|
497
|
+
async function buildAll(isDev = false) {
|
|
378
498
|
logger.info(`Starting VaderJS ${isDev ? 'development' : 'production'} build...`);
|
|
379
499
|
const totalTime = performance.now();
|
|
380
500
|
|
|
@@ -412,7 +532,35 @@ async function runDevServer() {
|
|
|
412
532
|
|
|
413
533
|
logger.info(`Starting dev server at http://localhost:${port}`);
|
|
414
534
|
|
|
415
|
-
|
|
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({
|
|
416
564
|
port,
|
|
417
565
|
fetch(req, server) {
|
|
418
566
|
const url = new URL(req.url);
|
|
@@ -434,40 +582,51 @@ async function runDevServer() {
|
|
|
434
582
|
},
|
|
435
583
|
});
|
|
436
584
|
|
|
437
|
-
const
|
|
438
|
-
logger.info(`File change detected: ${filename || 'unknown'}`);
|
|
585
|
+
const rebuild = async (changedFile) => {
|
|
439
586
|
try {
|
|
440
|
-
|
|
441
|
-
|
|
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
|
+
}
|
|
442
595
|
|
|
596
|
+
// Always rebuild when something changes in development
|
|
443
597
|
await buildAll(true);
|
|
444
598
|
|
|
599
|
+
// Send reload signal to all clients
|
|
445
600
|
for (const client of clients) {
|
|
446
601
|
client.send("reload");
|
|
447
602
|
}
|
|
603
|
+
|
|
604
|
+
logger.success("Rebuild complete");
|
|
448
605
|
} catch (e) {
|
|
449
606
|
logger.error("Rebuild failed:", e);
|
|
607
|
+
} finally {
|
|
608
|
+
watcher.setRebuilding(false);
|
|
450
609
|
}
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
// --- IMPROVED WATCHER ---
|
|
454
|
-
const configPath = path.join(PROJECT_ROOT, "vaderjs.config.js");
|
|
455
|
-
const configPathTs = path.join(PROJECT_ROOT, "vaderjs.config.ts");
|
|
610
|
+
};
|
|
456
611
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
configPath,
|
|
462
|
-
configPathTs
|
|
463
|
-
].filter(p => fsSync.existsSync(p));
|
|
612
|
+
// Set up file change listener
|
|
613
|
+
const removeListener = watcher.onChange((changedFile) => {
|
|
614
|
+
rebuild(changedFile);
|
|
615
|
+
});
|
|
464
616
|
|
|
465
|
-
|
|
617
|
+
// Cleanup on exit
|
|
618
|
+
const cleanup = () => {
|
|
619
|
+
removeListener();
|
|
620
|
+
watcher.clear();
|
|
621
|
+
server.stop();
|
|
622
|
+
process.exit(0);
|
|
623
|
+
};
|
|
466
624
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
625
|
+
process.on('SIGINT', cleanup);
|
|
626
|
+
process.on('SIGTERM', cleanup);
|
|
627
|
+
process.on('exit', cleanup);
|
|
470
628
|
}
|
|
629
|
+
|
|
471
630
|
async function runProdServer() {
|
|
472
631
|
const port = config.port || 3000;
|
|
473
632
|
logger.info(`Serving production build from /dist on http://localhost:${port}`);
|
|
@@ -487,14 +646,6 @@ async function runProdServer() {
|
|
|
487
646
|
});
|
|
488
647
|
}
|
|
489
648
|
|
|
490
|
-
function debounce(fn, delay) {
|
|
491
|
-
let timeoutId;
|
|
492
|
-
return (...args) => {
|
|
493
|
-
clearTimeout(timeoutId);
|
|
494
|
-
timeoutId = setTimeout(() => fn(...args), delay);
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
|
|
498
649
|
// --- SCRIPT ENTRYPOINT ---
|
|
499
650
|
|
|
500
651
|
async function main() {
|
|
@@ -523,7 +674,7 @@ async function main() {
|
|
|
523
674
|
logger.error("Please specify a plugin to add.");
|
|
524
675
|
process.exit(1);
|
|
525
676
|
}
|
|
526
|
-
await addPlugin(arg)
|
|
677
|
+
await addPlugin(arg);
|
|
527
678
|
return;
|
|
528
679
|
}
|
|
529
680
|
|
|
@@ -534,21 +685,21 @@ async function main() {
|
|
|
534
685
|
switch (command) {
|
|
535
686
|
case "add":
|
|
536
687
|
if (!arg) {
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
688
|
+
logger.error("Please specify a plugin to add.");
|
|
689
|
+
process.exit(1);
|
|
690
|
+
}
|
|
691
|
+
await addPlugin(arg);
|
|
692
|
+
return;
|
|
542
693
|
case "list_plugins":
|
|
543
|
-
await listPlugins()
|
|
544
|
-
return
|
|
545
|
-
case
|
|
694
|
+
await listPlugins();
|
|
695
|
+
return;
|
|
696
|
+
case "remove":
|
|
546
697
|
if (!arg) {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
await removePlugin(arg)
|
|
551
|
-
|
|
698
|
+
logger.error("Please specify a plugin to remove.");
|
|
699
|
+
process.exit(1);
|
|
700
|
+
}
|
|
701
|
+
await removePlugin(arg);
|
|
702
|
+
return;
|
|
552
703
|
|
|
553
704
|
case "dev":
|
|
554
705
|
globalThis.isDev = true;
|
|
@@ -585,14 +736,15 @@ Available commands:
|
|
|
585
736
|
}
|
|
586
737
|
}
|
|
587
738
|
|
|
588
|
-
|
|
589
739
|
main().catch(err => {
|
|
590
740
|
logger.error("An unexpected error occurred:", err);
|
|
591
741
|
process.exit(1);
|
|
592
742
|
});
|
|
743
|
+
|
|
593
744
|
process.on("unhandledRejection", (err) => {
|
|
594
745
|
logger.error("Unhandled Promise rejection:", err);
|
|
595
746
|
});
|
|
747
|
+
|
|
596
748
|
process.on("uncaughtException", (err) => {
|
|
597
749
|
logger.error("Uncaught Exception:", err);
|
|
598
750
|
});
|