vaderjs 2.3.15 → 2.3.17

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 (3) hide show
  1. package/config/index.ts +2 -2
  2. package/main.ts +215 -6
  3. package/package.json +1 -1
package/config/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export type Config = {
2
- name: string,
3
- version: string,
2
+ name?: string,
3
+ version?: string,
4
4
  description?: string,
5
5
  port: number,
6
6
  host?: string,
package/main.ts CHANGED
@@ -60,6 +60,159 @@ const TEMP_SRC_DIR = path.join(PROJECT_ROOT, ".vader_temp_src");
60
60
  let config: any = {};
61
61
  let htmlInjections: string[] = [];
62
62
 
63
+ // --- Plugin Support ---
64
+
65
+ interface Plugin {
66
+ name: string;
67
+ version: string;
68
+ description?: string;
69
+ onBuildStart?: (api: PluginAPI) => Promise<void> | void;
70
+ onBuildFinish?: (api: PluginAPI) => Promise<void> | void;
71
+ onFileChange?: (file: string, api: PluginAPI) => Promise<void> | void;
72
+ }
73
+
74
+ interface PluginAPI {
75
+ injectHTML(html: string): void;
76
+ addWatchPath(path: string): void;
77
+ config: any;
78
+ isDev: boolean;
79
+ distDir: string;
80
+ srcDir: string;
81
+ publicDir: string;
82
+ projectRoot: string;
83
+ }
84
+
85
+ let plugins: Plugin[] = [];
86
+
87
+ // Create plugin API helper
88
+ function createPluginAPI(): PluginAPI {
89
+ return {
90
+ injectHTML: (html: string) => {
91
+ htmlInjections.push(html);
92
+ },
93
+ addWatchPath: (watchPath: string) => {
94
+ if (fsSync.existsSync(watchPath)) {
95
+ watcher.watch(watchPath);
96
+ }
97
+ },
98
+ config: config,
99
+ isDev: globalThis.isDev,
100
+ distDir: DIST_DIR,
101
+ srcDir: SRC_DIR,
102
+ publicDir: PUBLIC_DIR,
103
+ projectRoot: PROJECT_ROOT
104
+ };
105
+ }
106
+
107
+ // Run plugin hooks
108
+ async function runPluginHook(hookName: 'onBuildStart' | 'onBuildFinish', api: PluginAPI) {
109
+ for (const plugin of plugins) {
110
+ if (plugin[hookName]) {
111
+ try {
112
+ logger.info(`Running plugin hook: ${plugin.name} - ${hookName}`);
113
+ await plugin[hookName]!(api);
114
+ } catch (e) {
115
+ logger.error(`Error in plugin "${plugin.name}" during ${hookName}:`, e);
116
+ if (!globalThis.isDev) process.exit(1);
117
+ }
118
+ }
119
+ }
120
+ }
121
+
122
+ // Load plugins from config
123
+ async function loadPluginsFromConfig() {
124
+ console.log(config)
125
+ if (!config.plugins || !Array.isArray(config.plugins)) {
126
+ logger.info("No plugins defined in config");
127
+ return;
128
+ }
129
+
130
+ const loadedPlugins: Plugin[] = [];
131
+
132
+ for (const pluginConfig of config.plugins) {
133
+ try {
134
+ let plugin;
135
+
136
+ // If plugin is a string, import it
137
+ if (typeof pluginConfig === 'string') {
138
+ const pluginPath = path.isAbsolute(pluginConfig)
139
+ ? pluginConfig
140
+ : path.join(PROJECT_ROOT, pluginConfig);
141
+
142
+ // Check if file exists
143
+ if (fsSync.existsSync(pluginPath)) {
144
+ const pluginModule = await import(pluginPath);
145
+ plugin = pluginModule.default || pluginModule;
146
+ } else {
147
+ // Try as npm package
148
+ plugin = await import(pluginConfig);
149
+ }
150
+ }
151
+ // If plugin is an object with resolve property
152
+ else if (typeof pluginConfig === 'object' && pluginConfig.resolve) {
153
+ const pluginModule = await import(pluginConfig.resolve);
154
+ plugin = pluginModule.default || pluginModule;
155
+
156
+ // Pass options to plugin if it's a factory function
157
+ if (typeof plugin === 'function' && pluginConfig.options) {
158
+ plugin = await plugin(pluginConfig.options);
159
+ }
160
+ }
161
+ // If plugin is already a plugin object
162
+ else if (typeof pluginConfig === 'object' && pluginConfig.name) {
163
+ plugin = pluginConfig;
164
+ }
165
+
166
+ if (plugin && typeof plugin === 'object' && plugin.name) {
167
+ loadedPlugins.push(plugin);
168
+ logger.success(`Loaded plugin: ${plugin.name} v${plugin.version || 'unknown'}`);
169
+ } else {
170
+ logger.warn(`Invalid plugin: missing name property`);
171
+ }
172
+ } catch (e) {
173
+ logger.error(`Failed to load plugin:`, e);
174
+ }
175
+ }
176
+
177
+ plugins = loadedPlugins;
178
+ }
179
+
180
+ // Also load from plugins directory (backward compatibility)
181
+ async function loadPluginsFromDirectory() {
182
+ const pluginsDir = path.join(PROJECT_ROOT, "plugins");
183
+
184
+ if (!fsSync.existsSync(pluginsDir)) {
185
+ return;
186
+ }
187
+
188
+ const pluginFiles = await fs.readdir(pluginsDir);
189
+ const loadedPlugins: Plugin[] = [];
190
+
191
+ for (const file of pluginFiles) {
192
+ if (file.endsWith('.js') || file.endsWith('.ts')) {
193
+ try {
194
+ const pluginPath = path.join(pluginsDir, file);
195
+ const pluginModule = await import(pluginPath);
196
+ const plugin = pluginModule.default || pluginModule;
197
+
198
+ if (plugin && typeof plugin === 'object' && plugin.name) {
199
+ // Check if plugin already loaded from config
200
+ if (!plugins.some(p => p.name === plugin.name)) {
201
+ loadedPlugins.push(plugin);
202
+ logger.info(`Loaded plugin from directory: ${plugin.name} v${plugin.version}`);
203
+ }
204
+ } else {
205
+ logger.warn(`Invalid plugin in ${file}: missing name property`);
206
+ }
207
+ } catch (e) {
208
+ logger.error(`Failed to load plugin ${file}:`, e);
209
+ }
210
+ }
211
+ }
212
+
213
+ plugins = [...plugins, ...loadedPlugins];
214
+ }
215
+
63
216
  // --- JSConfig Setup ---
64
217
 
65
218
  async function ensureJSConfig() {
@@ -142,9 +295,14 @@ const watcher = new FileWatcher();
142
295
 
143
296
  export async function loadConfig() {
144
297
  try {
145
- const mod = await import(path.join(PROJECT_ROOT, "vaderjs.config.js"));
146
- return mod.default || mod;
147
- } catch {
298
+ const configPath = path.join(PROJECT_ROOT, "vaderjs.config.ts");
299
+ if (fsSync.existsSync(configPath)) {
300
+ const mod = await import(configPath);
301
+ return mod.default || mod;
302
+ }
303
+ return {};
304
+ } catch (error) {
305
+ logger.warn("Failed to load config, using defaults:", error);
148
306
  return {};
149
307
  }
150
308
  }
@@ -276,6 +434,9 @@ async function buildAppEntrypoints() {
276
434
  // First check for root App file (VaderJS standard)
277
435
  const appFile = findAppFile();
278
436
 
437
+ // Build HTML with injections from plugins
438
+ const htmlInjectionsString = htmlInjections.join('\n ');
439
+
279
440
  if (appFile) {
280
441
  logger.info(`Building App from: ${appFile}`);
281
442
 
@@ -295,7 +456,8 @@ async function buildAppEntrypoints() {
295
456
  <head>
296
457
  <meta charset="UTF-8"/>
297
458
  <meta name="viewport" content="width=device-width,initial-scale=1"/>
298
- <title>Vader App</title>
459
+ <title>${config.title || 'Vader App'}</title>
460
+ ${htmlInjectionsString}
299
461
  </head>
300
462
  <body>
301
463
  <div id="app"></div>
@@ -344,7 +506,8 @@ async function buildAppEntrypoints() {
344
506
  <head>
345
507
  <meta charset="UTF-8"/>
346
508
  <meta name="viewport" content="width=device-width,initial-scale=1"/>
347
- <title>Vader App</title>
509
+ <title>${config.title || 'Vader App'}</title>
510
+ ${htmlInjectionsString}
348
511
  </head>
349
512
  <body>
350
513
  <div id="app"></div>
@@ -359,6 +522,21 @@ async function buildAppEntrypoints() {
359
522
  async function buildAll(dev = false) {
360
523
  const start = performance.now();
361
524
 
525
+ // Reset HTML injections before build
526
+ htmlInjections = [];
527
+
528
+ // Load plugins from config first
529
+ await loadPluginsFromConfig();
530
+
531
+ // Also load from plugins directory (backward compatibility)
532
+ await loadPluginsFromDirectory();
533
+
534
+ // Create plugin API
535
+ const pluginAPI = createPluginAPI();
536
+
537
+ // Run onBuildStart hooks
538
+ await runPluginHook('onBuildStart', pluginAPI);
539
+
362
540
  // Ensure jsconfig.json exists before building
363
541
  await ensureJSConfig();
364
542
 
@@ -373,6 +551,9 @@ async function buildAll(dev = false) {
373
551
  await timedStep("Copy Public", copyPublicAssets);
374
552
  await timedStep("Build App", buildAppEntrypoints);
375
553
 
554
+ // Run onBuildFinish hooks
555
+ await runPluginHook('onBuildFinish', pluginAPI);
556
+
376
557
  logger.success(
377
558
  `Build finished in ${(performance.now() - start).toFixed(1)}ms`
378
559
  );
@@ -431,14 +612,42 @@ async function runDevServer() {
431
612
  watcher.watch(SRC_DIR);
432
613
  watcher.watch(PUBLIC_DIR);
433
614
 
615
+ // Watch config file
616
+ watcher.watch(path.join(PROJECT_ROOT, "vaderjs.config.js"));
617
+
618
+ // Watch plugins directory
619
+ const pluginsDir = path.join(PROJECT_ROOT, "plugins");
620
+ if (fsSync.existsSync(pluginsDir)) {
621
+ watcher.watch(pluginsDir);
622
+ }
623
+
434
624
  // Also watch for root App file
435
625
  const rootAppDir = path.dirname(findAppFile() || "");
436
626
  if (rootAppDir && rootAppDir !== PROJECT_ROOT) {
437
627
  watcher.watch(rootAppDir);
438
628
  }
439
629
 
440
- watcher.onChange(async () => {
630
+ watcher.onChange(async (file) => {
441
631
  logger.info("Changes detected, rebuilding...");
632
+
633
+ // If config or plugin file changed, reload config and plugins
634
+ if (file.includes('vaderjs.config.js') || file.includes('plugins')) {
635
+ logger.info("Config or plugin changed, reloading...");
636
+ config = await loadConfig();
637
+ htmlInjections = [];
638
+ plugins = [];
639
+ await loadPluginsFromConfig();
640
+ await loadPluginsFromDirectory();
641
+ }
642
+
643
+ // Run onFileChange hooks for plugins
644
+ const pluginAPI = createPluginAPI();
645
+ for (const plugin of plugins) {
646
+ if (plugin.onFileChange) {
647
+ await plugin.onFileChange(file, pluginAPI);
648
+ }
649
+ }
650
+
442
651
  await buildAll(true);
443
652
 
444
653
  for (const c of clients) c.send("reload");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vaderjs",
3
- "version": "2.3.15",
3
+ "version": "2.3.17",
4
4
  "description": "A simple and powerful JavaScript library for building modern web applications.",
5
5
  "bin": {
6
6
  "vaderjs": "./main.ts"