elit 3.0.0 → 3.0.2

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 (87) hide show
  1. package/dist/build.d.ts +4 -12
  2. package/dist/build.d.ts.map +1 -0
  3. package/dist/chokidar.d.ts +7 -9
  4. package/dist/chokidar.d.ts.map +1 -0
  5. package/dist/cli.d.ts +6 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +17 -4
  8. package/dist/config.d.ts +29 -0
  9. package/dist/config.d.ts.map +1 -0
  10. package/dist/dom.d.ts +7 -14
  11. package/dist/dom.d.ts.map +1 -0
  12. package/dist/el.d.ts +19 -191
  13. package/dist/el.d.ts.map +1 -0
  14. package/dist/fs.d.ts +35 -35
  15. package/dist/fs.d.ts.map +1 -0
  16. package/dist/hmr.d.ts +3 -3
  17. package/dist/hmr.d.ts.map +1 -0
  18. package/dist/http.d.ts +20 -22
  19. package/dist/http.d.ts.map +1 -0
  20. package/dist/https.d.ts +12 -15
  21. package/dist/https.d.ts.map +1 -0
  22. package/dist/index.d.ts +10 -629
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/mime-types.d.ts +9 -9
  25. package/dist/mime-types.d.ts.map +1 -0
  26. package/dist/path.d.ts +22 -19
  27. package/dist/path.d.ts.map +1 -0
  28. package/dist/router.d.ts +10 -17
  29. package/dist/router.d.ts.map +1 -0
  30. package/dist/runtime.d.ts +5 -6
  31. package/dist/runtime.d.ts.map +1 -0
  32. package/dist/server.d.ts +105 -7
  33. package/dist/server.d.ts.map +1 -0
  34. package/dist/server.js +14 -2
  35. package/dist/server.mjs +14 -2
  36. package/dist/state.d.ts +21 -27
  37. package/dist/state.d.ts.map +1 -0
  38. package/dist/style.d.ts +14 -55
  39. package/dist/style.d.ts.map +1 -0
  40. package/dist/types.d.ts +26 -240
  41. package/dist/types.d.ts.map +1 -0
  42. package/dist/ws.d.ts +14 -17
  43. package/dist/ws.d.ts.map +1 -0
  44. package/dist/wss.d.ts +16 -16
  45. package/dist/wss.d.ts.map +1 -0
  46. package/package.json +3 -2
  47. package/src/build.ts +337 -0
  48. package/src/chokidar.ts +401 -0
  49. package/src/cli.ts +638 -0
  50. package/src/config.ts +205 -0
  51. package/src/dom.ts +817 -0
  52. package/src/el.ts +164 -0
  53. package/src/fs.ts +727 -0
  54. package/src/hmr.ts +137 -0
  55. package/src/http.ts +775 -0
  56. package/src/https.ts +411 -0
  57. package/src/index.ts +14 -0
  58. package/src/mime-types.ts +222 -0
  59. package/src/path.ts +493 -0
  60. package/src/router.ts +237 -0
  61. package/src/runtime.ts +97 -0
  62. package/src/server.ts +1290 -0
  63. package/src/state.ts +468 -0
  64. package/src/style.ts +524 -0
  65. package/{dist/types-Du6kfwTm.d.ts → src/types.ts} +58 -141
  66. package/src/ws.ts +506 -0
  67. package/src/wss.ts +241 -0
  68. package/dist/build.d.mts +0 -20
  69. package/dist/chokidar.d.mts +0 -134
  70. package/dist/dom.d.mts +0 -87
  71. package/dist/el.d.mts +0 -207
  72. package/dist/fs.d.mts +0 -255
  73. package/dist/hmr.d.mts +0 -38
  74. package/dist/http.d.mts +0 -163
  75. package/dist/https.d.mts +0 -108
  76. package/dist/index.d.mts +0 -629
  77. package/dist/mime-types.d.mts +0 -48
  78. package/dist/path.d.mts +0 -163
  79. package/dist/router.d.mts +0 -47
  80. package/dist/runtime.d.mts +0 -97
  81. package/dist/server.d.mts +0 -7
  82. package/dist/state.d.mts +0 -111
  83. package/dist/style.d.mts +0 -159
  84. package/dist/types-C0nGi6MX.d.mts +0 -346
  85. package/dist/types.d.mts +0 -452
  86. package/dist/ws.d.mts +0 -195
  87. package/dist/wss.d.mts +0 -108
package/src/cli.ts ADDED
@@ -0,0 +1,638 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Main CLI for Elit
4
+ */
5
+
6
+ import { loadConfig, mergeConfig, loadEnv } from './config';
7
+ import { createDevServer } from './server';
8
+ import { build } from './build';
9
+ import type { DevServerOptions, BuildOptions, PreviewOptions } from './types';
10
+
11
+ const COMMANDS = ['dev', 'build', 'preview', 'help', 'version'] as const;
12
+ type Command = typeof COMMANDS[number];
13
+
14
+ /**
15
+ * Helper: Setup graceful shutdown handlers (eliminates duplication in runDev and runPreview)
16
+ */
17
+ function setupShutdownHandlers(closeFunc: () => Promise<void>): void {
18
+ const shutdown = async () => {
19
+ console.log('\n[Server] Shutting down...');
20
+ await closeFunc();
21
+ process.exit(0);
22
+ };
23
+
24
+ process.on('SIGINT', shutdown);
25
+ process.on('SIGTERM', shutdown);
26
+ }
27
+
28
+ /**
29
+ * Helper: Execute build with error handling (eliminates duplication in runBuild)
30
+ */
31
+ async function executeBuild(options: BuildOptions): Promise<void> {
32
+ try {
33
+ await build(options);
34
+ } catch (error) {
35
+ process.exit(1);
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Helper: Validate entry file (eliminates duplication in runBuild)
41
+ */
42
+ function validateEntry(entry: string | undefined, buildIndex?: number): void {
43
+ if (!entry) {
44
+ if (buildIndex !== undefined) {
45
+ console.error(`Error: Entry file is required for build #${buildIndex + 1}`);
46
+ } else {
47
+ console.error('Error: Entry file is required');
48
+ console.error('Specify in config file or use --entry <file>');
49
+ }
50
+ process.exit(1);
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Helper: Ensure env is set (eliminates duplication in runBuild)
56
+ */
57
+ function ensureEnv(options: BuildOptions, env: Record<string, string>): void {
58
+ if (!options.env) {
59
+ options.env = env;
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Helper: Generic argument parser (eliminates duplication in parseDevArgs, parseBuildArgs, parsePreviewArgs)
65
+ */
66
+ type ArgHandler<T> = (options: T, value: string, index: { current: number }) => void;
67
+
68
+ function parseArgs<T>(args: string[], handlers: Record<string, ArgHandler<T>>, options: T): T {
69
+ for (let i = 0; i < args.length; i++) {
70
+ const arg = args[i];
71
+ const handler = handlers[arg];
72
+ if (handler) {
73
+ const index = { current: i };
74
+ handler(options, args[i + 1], index);
75
+ i = index.current;
76
+ }
77
+ }
78
+ return options;
79
+ }
80
+
81
+ async function main() {
82
+ const args = process.argv.slice(2);
83
+ const command = (args[0] as Command) || 'help';
84
+
85
+ if (!COMMANDS.includes(command)) {
86
+ console.error(`Unknown command: ${command}`);
87
+ printHelp();
88
+ process.exit(1);
89
+ }
90
+
91
+ switch (command) {
92
+ case 'dev':
93
+ await runDev(args.slice(1));
94
+ break;
95
+ case 'build':
96
+ await runBuild(args.slice(1));
97
+ break;
98
+ case 'preview':
99
+ await runPreview(args.slice(1));
100
+ break;
101
+ case 'version':
102
+ printVersion();
103
+ break;
104
+ case 'help':
105
+ default:
106
+ printHelp();
107
+ break;
108
+ }
109
+ }
110
+
111
+ async function runDev(args: string[]) {
112
+ const cliOptions = parseDevArgs(args);
113
+ const config = await loadConfig();
114
+
115
+ const options = config?.dev
116
+ ? mergeConfig(config.dev, cliOptions)
117
+ : cliOptions as DevServerOptions;
118
+
119
+ // Ensure we have at least root or clients
120
+ if (!options.root && (!options.clients || options.clients.length === 0)) {
121
+ options.root = process.cwd();
122
+ }
123
+
124
+ // Set mode to 'dev' for dev command
125
+ options.mode = 'dev';
126
+
127
+ const devServer = createDevServer(options);
128
+
129
+ // Handle graceful shutdown
130
+ setupShutdownHandlers(() => devServer.close());
131
+ }
132
+
133
+ async function runBuild(args: string[]) {
134
+ const cliOptions = parseBuildArgs(args);
135
+ const config = await loadConfig();
136
+
137
+ // Load environment variables
138
+ const mode = process.env.MODE || 'production';
139
+ const env = loadEnv(mode);
140
+
141
+ // Check if config has build array or single build
142
+ if (config?.build) {
143
+ const builds = Array.isArray(config.build) ? config.build : [config.build];
144
+
145
+ // If CLI options provided, merge with first build or use as standalone
146
+ if (Object.keys(cliOptions).length > 0) {
147
+ const options = mergeConfig(builds[0] || {}, cliOptions) as BuildOptions;
148
+
149
+ ensureEnv(options, env);
150
+ validateEntry(options.entry);
151
+
152
+ await executeBuild(options);
153
+ } else {
154
+ // Run all builds from config
155
+ console.log(`Building ${builds.length} ${builds.length === 1 ? 'entry' : 'entries'}...\n`);
156
+
157
+ for (let i = 0; i < builds.length; i++) {
158
+ const buildConfig = builds[i];
159
+
160
+ ensureEnv(buildConfig, env);
161
+ validateEntry(buildConfig.entry, i);
162
+
163
+ console.log(`[${i + 1}/${builds.length}] Building ${buildConfig.entry}...`);
164
+
165
+ try {
166
+ await build(buildConfig);
167
+ } catch (error) {
168
+ console.error(`Build #${i + 1} failed`);
169
+ process.exit(1);
170
+ }
171
+
172
+ if (i < builds.length - 1) {
173
+ console.log(''); // Empty line between builds
174
+ }
175
+ }
176
+
177
+ console.log(`\n✓ All ${builds.length} builds completed successfully`);
178
+ }
179
+ } else {
180
+ // No config, use CLI options only
181
+ const options = cliOptions as BuildOptions;
182
+
183
+ ensureEnv(options, env);
184
+ validateEntry(options.entry);
185
+
186
+ await executeBuild(options);
187
+ }
188
+ }
189
+
190
+ async function runPreview(args: string[]) {
191
+ const cliOptions = parsePreviewArgs(args);
192
+ const config = await loadConfig();
193
+
194
+ const previewConfig = config?.preview || {};
195
+ const mergedOptions: PreviewOptions = {
196
+ ...previewConfig,
197
+ ...Object.fromEntries(
198
+ Object.entries(cliOptions).filter(([_, v]) => v !== undefined)
199
+ )
200
+ };
201
+
202
+ // Build DevServerOptions from PreviewOptions
203
+ const options: DevServerOptions = {
204
+ port: mergedOptions.port || 4173,
205
+ host: mergedOptions.host || 'localhost',
206
+ open: mergedOptions.open ?? true,
207
+ logging: mergedOptions.logging ?? true
208
+ };
209
+
210
+ // Support both single root and clients array
211
+ if (mergedOptions.clients && mergedOptions.clients.length > 0) {
212
+ options.clients = mergedOptions.clients;
213
+ console.log('Starting preview server with multiple clients...');
214
+ console.log(` Clients: ${mergedOptions.clients.length}`);
215
+ mergedOptions.clients.forEach((client, i) => {
216
+ console.log(` ${i + 1}. ${client.basePath} -> ${client.root}`);
217
+ });
218
+ } else {
219
+ // Get outDir from build config (use first build if array)
220
+ const buildConfig = config?.build;
221
+ const defaultOutDir = Array.isArray(buildConfig) ? buildConfig[0]?.outDir : buildConfig?.outDir;
222
+
223
+ options.root = mergedOptions.root || defaultOutDir || 'dist';
224
+ options.basePath = mergedOptions.basePath;
225
+ options.index = mergedOptions.index;
226
+ console.log('Starting preview server...');
227
+ console.log(` Root: ${options.root}`);
228
+ }
229
+
230
+ // Add global proxy if configured
231
+ if (mergedOptions.proxy && mergedOptions.proxy.length > 0) {
232
+ options.proxy = mergedOptions.proxy;
233
+ }
234
+
235
+ // Add global worker if configured
236
+ if (mergedOptions.worker && mergedOptions.worker.length > 0) {
237
+ options.worker = mergedOptions.worker;
238
+ }
239
+
240
+ // Add API router if configured
241
+ if (mergedOptions.api) {
242
+ options.api = mergedOptions.api;
243
+ }
244
+
245
+ // Add HTTPS if configured
246
+ if (mergedOptions.https) {
247
+ options.https = mergedOptions.https;
248
+ }
249
+
250
+ // Add middleware if configured
251
+ if (mergedOptions.middleware) {
252
+ options.middleware = mergedOptions.middleware;
253
+ }
254
+
255
+ // Add SSR if configured
256
+ if (mergedOptions.ssr) {
257
+ options.ssr = mergedOptions.ssr;
258
+ }
259
+
260
+ // Set mode to 'preview' for preview command
261
+ options.mode = 'preview';
262
+
263
+ const devServer = createDevServer(options);
264
+
265
+ setupShutdownHandlers(() => devServer.close());
266
+ }
267
+
268
+ function parseDevArgs(args: string[]): Partial<DevServerOptions> {
269
+ const options: Partial<DevServerOptions> = {};
270
+
271
+ const handlers: Record<string, ArgHandler<Partial<DevServerOptions>>> = {
272
+ '-p': (opts, value, index) => { opts.port = parseInt(value, 10); index.current++; },
273
+ '--port': (opts, value, index) => { opts.port = parseInt(value, 10); index.current++; },
274
+ '-h': (opts, value, index) => { opts.host = value; index.current++; },
275
+ '--host': (opts, value, index) => { opts.host = value; index.current++; },
276
+ '-r': (opts, value, index) => { opts.root = value; index.current++; },
277
+ '--root': (opts, value, index) => { opts.root = value; index.current++; },
278
+ '--no-open': (opts) => { opts.open = false; },
279
+ '--silent': (opts) => { opts.logging = false; },
280
+ };
281
+
282
+ return parseArgs(args, handlers, options);
283
+ }
284
+
285
+ function parseBuildArgs(args: string[]): Partial<BuildOptions> {
286
+ const options: Partial<BuildOptions> = {};
287
+
288
+ const handlers: Record<string, ArgHandler<Partial<BuildOptions>>> = {
289
+ '-e': (opts, value, index) => { opts.entry = value; index.current++; },
290
+ '--entry': (opts, value, index) => { opts.entry = value; index.current++; },
291
+ '-o': (opts, value, index) => { opts.outDir = value; index.current++; },
292
+ '--out-dir': (opts, value, index) => { opts.outDir = value; index.current++; },
293
+ '--no-minify': (opts) => { opts.minify = false; },
294
+ '--sourcemap': (opts) => { opts.sourcemap = true; },
295
+ '-f': (opts, value, index) => { opts.format = value as BuildOptions['format']; index.current++; },
296
+ '--format': (opts, value, index) => { opts.format = value as BuildOptions['format']; index.current++; },
297
+ '--silent': (opts) => { opts.logging = false; },
298
+ };
299
+
300
+ return parseArgs(args, handlers, options);
301
+ }
302
+
303
+ function parsePreviewArgs(args: string[]): Partial<{ port: number; host: string; root: string; basePath: string; open: boolean; logging: boolean }> {
304
+ const options: Partial<{ port: number; host: string; root: string; basePath: string; open: boolean; logging: boolean }> = {};
305
+
306
+ type PreviewOptions = { port: number; host: string; root: string; basePath: string; open: boolean; logging: boolean };
307
+
308
+ const handlers: Record<string, ArgHandler<Partial<PreviewOptions>>> = {
309
+ '-p': (opts, value, index) => { opts.port = parseInt(value, 10); index.current++; },
310
+ '--port': (opts, value, index) => { opts.port = parseInt(value, 10); index.current++; },
311
+ '-h': (opts, value, index) => { opts.host = value; index.current++; },
312
+ '--host': (opts, value, index) => { opts.host = value; index.current++; },
313
+ '-r': (opts, value, index) => { opts.root = value; index.current++; },
314
+ '--root': (opts, value, index) => { opts.root = value; index.current++; },
315
+ '-b': (opts, value, index) => { opts.basePath = value; index.current++; },
316
+ '--base-path': (opts, value, index) => { opts.basePath = value; index.current++; },
317
+ '--no-open': (opts) => { opts.open = false; },
318
+ '--silent': (opts) => { opts.logging = false; },
319
+ };
320
+
321
+ return parseArgs(args, handlers, options);
322
+ }
323
+
324
+ function printHelp() {
325
+ console.log(`
326
+ Elit - Modern Web Development Toolkit
327
+
328
+ Usage:
329
+ elit <command> [options]
330
+
331
+ Commands:
332
+ dev Start development server
333
+ build Build for production
334
+ preview Preview production build
335
+ version Show version number
336
+ help Show this help message
337
+
338
+ Dev Options:
339
+ -p, --port <number> Port to run server on (default: 3000)
340
+ -h, --host <string> Host to bind to (default: localhost)
341
+ -r, --root <path> Root directory to serve
342
+ --no-open Don't open browser automatically
343
+ --silent Disable logging
344
+
345
+ Build Options:
346
+ -e, --entry <file> Entry file to build (required)
347
+ -o, --out-dir <dir> Output directory (default: dist)
348
+ -f, --format <format> Output format: esm, cjs, iife (default: esm)
349
+ --no-minify Disable minification
350
+ --sourcemap Generate sourcemap
351
+ --silent Disable logging
352
+
353
+ Note: Build configuration supports both single and multiple builds:
354
+ - Single build: build: { entry: 'src/app.ts', outDir: 'dist' }
355
+ - Multiple builds: build: [{ entry: 'src/app1.ts' }, { entry: 'src/app2.ts' }]
356
+ When using array, all builds run sequentially.
357
+
358
+ Preview Options:
359
+ -p, --port <number> Port to run server on (default: 4173)
360
+ -h, --host <string> Host to bind to (default: localhost)
361
+ -r, --root <dir> Root directory to serve (default: dist or build.outDir)
362
+ -b, --base-path <path> Base path for the application
363
+ --no-open Don't open browser automatically
364
+ --silent Disable logging
365
+
366
+ Note: Preview mode has full feature parity with dev mode:
367
+ - Single root and multi-client configurations (use clients[] in config)
368
+ - REST API endpoints (use api option in config)
369
+ - Proxy forwarding and Web Workers
370
+ - HTTPS support, custom middleware, and SSR
371
+
372
+ Config File:
373
+ Create elit.config.ts, elit.config.js, or elit.config.json in project root
374
+
375
+ Proxy Configuration:
376
+ Configure proxy in the config file to forward requests to backend servers.
377
+ Supports both global proxy (applies to all clients) and client-specific proxy.
378
+
379
+ Options:
380
+ - context: Path prefix to match (required, e.g., '/api', '/graphql')
381
+ - target: Backend server URL (required, e.g., 'http://localhost:8080')
382
+ - changeOrigin: Change the origin header to match target (default: false)
383
+ - pathRewrite: Rewrite request paths (e.g., { '^/api': '/v1/api' })
384
+ - headers: Add custom headers to proxied requests
385
+ - ws: Enable WebSocket proxying (default: false)
386
+
387
+ Proxy Priority:
388
+ 1. Client-specific proxy (defined in clients[].proxy)
389
+ 2. Global proxy (defined in dev.proxy)
390
+ The first matching proxy configuration will be used.
391
+
392
+ Worker Configuration:
393
+ Configure Web Workers in the config file for background processing.
394
+ Supports both global workers (applies to all clients) and client-specific workers.
395
+
396
+ Options:
397
+ - path: Worker script path relative to root directory (required)
398
+ - name: Worker name/identifier (optional, defaults to filename)
399
+ - type: Worker type - 'module' (ESM) or 'classic' (default: 'module')
400
+
401
+ Worker Priority:
402
+ 1. Client-specific workers (defined in clients[].worker)
403
+ 2. Global workers (defined in dev.worker or preview.worker)
404
+ Both global and client-specific workers will be loaded.
405
+
406
+ API and Middleware Configuration:
407
+ Configure REST API endpoints and custom middleware per client or globally.
408
+ Supports both global configuration and client-specific configuration.
409
+
410
+ Client-specific API and Middleware:
411
+ - Each client can have its own API router (clients[].api)
412
+ - Each client can have its own middleware chain (clients[].middleware)
413
+ - Client-specific configuration is isolated to that client's routes
414
+ - API paths are automatically prefixed with the client's basePath
415
+ Example: If basePath is '/app1' and route is '/api/health',
416
+ the full path will be '/app1/api/health'
417
+
418
+ Priority:
419
+ 1. Client-specific middleware runs first (defined in clients[].middleware)
420
+ 2. Global middleware runs second (defined in dev.middleware or preview.middleware)
421
+ 3. Client-specific API routes are matched (defined in clients[].api)
422
+ 4. Global API routes are matched (defined in dev.api or preview.api)
423
+
424
+ Examples:
425
+ elit dev
426
+ elit dev --port 8080
427
+ elit build --entry src/app.ts
428
+ elit preview
429
+ elit preview --port 5000
430
+
431
+ Config file example (elit.config.ts):
432
+ export default {
433
+ dev: {
434
+ port: 3000,
435
+ clients: [
436
+ {
437
+ root: './app1',
438
+ basePath: '/app1',
439
+ proxy: [
440
+ {
441
+ context: '/api',
442
+ target: 'http://localhost:8080',
443
+ changeOrigin: true
444
+ }
445
+ ],
446
+ worker: [
447
+ {
448
+ path: 'workers/data-processor.js',
449
+ name: 'dataProcessor',
450
+ type: 'module'
451
+ }
452
+ ],
453
+ // API routes are prefixed with basePath
454
+ // This route becomes: /app1/api/health
455
+ api: router()
456
+ .get('/api/health', (req, res) => {
457
+ res.json({ status: 'ok', app: 'app1' });
458
+ }),
459
+ middleware: [
460
+ (req, res, next) => {
461
+ console.log('App1 middleware:', req.url);
462
+ next();
463
+ }
464
+ ]
465
+ },
466
+ {
467
+ root: './app2',
468
+ basePath: '/app2',
469
+ proxy: [
470
+ {
471
+ context: '/graphql',
472
+ target: 'http://localhost:4000',
473
+ changeOrigin: true
474
+ }
475
+ ],
476
+ worker: [
477
+ {
478
+ path: 'workers/image-worker.js',
479
+ type: 'module'
480
+ }
481
+ ],
482
+ // API routes are prefixed with basePath
483
+ // This route becomes: /app2/api/status
484
+ api: router()
485
+ .get('/api/status', (req, res) => {
486
+ res.json({ status: 'running', app: 'app2' });
487
+ }),
488
+ middleware: [
489
+ (req, res, next) => {
490
+ console.log('App2 middleware:', req.url);
491
+ next();
492
+ }
493
+ ]
494
+ }
495
+ ],
496
+ // Global proxy (applies to all clients)
497
+ proxy: [
498
+ {
499
+ context: '/shared-api',
500
+ target: 'http://localhost:9000',
501
+ changeOrigin: true
502
+ }
503
+ ],
504
+ // Global workers (applies to all clients)
505
+ worker: [
506
+ {
507
+ path: 'workers/shared-worker.js',
508
+ name: 'sharedWorker',
509
+ type: 'module'
510
+ }
511
+ ]
512
+ },
513
+ // Single build configuration
514
+ build: {
515
+ entry: 'src/app.ts',
516
+ outDir: 'dist',
517
+ format: 'esm'
518
+ },
519
+ // Alternative: Multiple builds
520
+ // build: [
521
+ // {
522
+ // entry: 'src/app1.ts',
523
+ // outDir: 'dist/app1',
524
+ // outFile: 'app1.js',
525
+ // format: 'esm',
526
+ // minify: true
527
+ // },
528
+ // {
529
+ // entry: 'src/app2.ts',
530
+ // outDir: 'dist/app2',
531
+ // outFile: 'app2.js',
532
+ // format: 'esm',
533
+ // minify: true
534
+ // },
535
+ // {
536
+ // entry: 'src/worker.ts',
537
+ // outDir: 'dist/workers',
538
+ // outFile: 'worker.js',
539
+ // format: 'esm',
540
+ // platform: 'browser'
541
+ // }
542
+ // ],
543
+ preview: {
544
+ port: 4173,
545
+ // Single client preview
546
+ root: 'dist',
547
+ basePath: '/app',
548
+ https: false,
549
+ // API router (import from elit/server)
550
+ api: router()
551
+ .get('/api/data', (req, res) => {
552
+ res.json({ message: 'Hello from preview API' });
553
+ }),
554
+ // Custom middleware
555
+ middleware: [
556
+ (req, res, next) => {
557
+ console.log('Preview request:', req.url);
558
+ next();
559
+ }
560
+ ],
561
+ // SSR render function
562
+ ssr: () => '<h1>Server-rendered content</h1>',
563
+ proxy: [
564
+ {
565
+ context: '/api',
566
+ target: 'http://localhost:8080'
567
+ }
568
+ ],
569
+ worker: [
570
+ {
571
+ path: 'workers/cache-worker.js',
572
+ type: 'module'
573
+ }
574
+ ]
575
+ // Multi-client preview (alternative)
576
+ // clients: [
577
+ // {
578
+ // root: './dist/app1',
579
+ // basePath: '/app1',
580
+ // proxy: [
581
+ // {
582
+ // context: '/api',
583
+ // target: 'http://localhost:8080'
584
+ // }
585
+ // ],
586
+ // worker: [
587
+ // {
588
+ // path: 'workers/app1-worker.js',
589
+ // type: 'module'
590
+ // }
591
+ // ],
592
+ // api: router()
593
+ // .get('/api/health', (req, res) => {
594
+ // res.json({ status: 'ok', app: 'app1' });
595
+ // }),
596
+ // middleware: [
597
+ // (req, res, next) => {
598
+ // console.log('App1 request:', req.url);
599
+ // next();
600
+ // }
601
+ // ]
602
+ // },
603
+ // {
604
+ // root: './dist/app2',
605
+ // basePath: '/app2',
606
+ // worker: [
607
+ // {
608
+ // path: 'workers/app2-worker.js',
609
+ // type: 'module'
610
+ // }
611
+ // ],
612
+ // api: router()
613
+ // .get('/api/status', (req, res) => {
614
+ // res.json({ status: 'running', app: 'app2' });
615
+ // }),
616
+ // middleware: [
617
+ // (req, res, next) => {
618
+ // console.log('App2 request:', req.url);
619
+ // next();
620
+ // }
621
+ // ]
622
+ // }
623
+ // ]
624
+ }
625
+ }
626
+ `);
627
+ }
628
+
629
+ function printVersion() {
630
+ const pkg = require('../package.json');
631
+ console.log(`elit v${pkg.version}`);
632
+ }
633
+
634
+ // Run CLI
635
+ main().catch((error) => {
636
+ console.error('Fatal error:', error);
637
+ process.exit(1);
638
+ });