@zenithbuild/core 0.4.7 → 0.5.0-beta.2.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 (110) hide show
  1. package/CORE_CONTRACT.md +143 -0
  2. package/README.md +11 -31
  3. package/bin/zenith.js +68 -0
  4. package/package.json +41 -53
  5. package/src/config.js +134 -0
  6. package/src/core-template.js +30 -0
  7. package/src/errors.js +54 -0
  8. package/src/guards.js +61 -0
  9. package/src/hash.js +52 -0
  10. package/src/index.js +26 -0
  11. package/src/ir/index.js +1 -0
  12. package/src/order.js +69 -0
  13. package/src/path.js +131 -0
  14. package/src/schema.js +28 -0
  15. package/src/version.js +67 -0
  16. package/bin/zen-build.ts +0 -2
  17. package/bin/zen-dev.ts +0 -2
  18. package/bin/zen-preview.ts +0 -2
  19. package/bin/zenith.ts +0 -2
  20. package/cli/commands/add.ts +0 -37
  21. package/cli/commands/build.ts +0 -37
  22. package/cli/commands/create.ts +0 -702
  23. package/cli/commands/dev.ts +0 -335
  24. package/cli/commands/index.ts +0 -112
  25. package/cli/commands/preview.ts +0 -62
  26. package/cli/commands/remove.ts +0 -33
  27. package/cli/index.ts +0 -10
  28. package/cli/main.ts +0 -101
  29. package/cli/utils/branding.ts +0 -178
  30. package/cli/utils/content.ts +0 -112
  31. package/cli/utils/logger.ts +0 -46
  32. package/cli/utils/plugin-manager.ts +0 -114
  33. package/cli/utils/project.ts +0 -77
  34. package/compiler/README.md +0 -380
  35. package/compiler/build-analyzer.ts +0 -122
  36. package/compiler/css/index.ts +0 -317
  37. package/compiler/discovery/componentDiscovery.ts +0 -178
  38. package/compiler/discovery/layouts.ts +0 -70
  39. package/compiler/errors/compilerError.ts +0 -56
  40. package/compiler/finalize/finalizeOutput.ts +0 -192
  41. package/compiler/finalize/generateFinalBundle.ts +0 -82
  42. package/compiler/index.ts +0 -82
  43. package/compiler/ir/types.ts +0 -162
  44. package/compiler/output/types.ts +0 -34
  45. package/compiler/parse/detectMapExpressions.ts +0 -102
  46. package/compiler/parse/parseScript.ts +0 -46
  47. package/compiler/parse/parseTemplate.ts +0 -599
  48. package/compiler/parse/parseZenFile.ts +0 -66
  49. package/compiler/parse/scriptAnalysis.ts +0 -91
  50. package/compiler/parse/trackLoopContext.ts +0 -82
  51. package/compiler/runtime/dataExposure.ts +0 -317
  52. package/compiler/runtime/generateDOM.ts +0 -246
  53. package/compiler/runtime/generateHydrationBundle.ts +0 -407
  54. package/compiler/runtime/hydration.ts +0 -309
  55. package/compiler/runtime/navigation.ts +0 -432
  56. package/compiler/runtime/thinRuntime.ts +0 -160
  57. package/compiler/runtime/transformIR.ts +0 -363
  58. package/compiler/runtime/wrapExpression.ts +0 -95
  59. package/compiler/runtime/wrapExpressionWithLoop.ts +0 -83
  60. package/compiler/spa-build.ts +0 -917
  61. package/compiler/ssg-build.ts +0 -422
  62. package/compiler/test/validate-test.ts +0 -104
  63. package/compiler/transform/classifyExpression.ts +0 -444
  64. package/compiler/transform/componentResolver.ts +0 -312
  65. package/compiler/transform/componentScriptTransformer.ts +0 -147
  66. package/compiler/transform/expressionTransformer.ts +0 -385
  67. package/compiler/transform/fragmentLowering.ts +0 -634
  68. package/compiler/transform/generateBindings.ts +0 -47
  69. package/compiler/transform/generateHTML.ts +0 -28
  70. package/compiler/transform/layoutProcessor.ts +0 -132
  71. package/compiler/transform/slotResolver.ts +0 -292
  72. package/compiler/transform/transformNode.ts +0 -126
  73. package/compiler/transform/transformTemplate.ts +0 -38
  74. package/compiler/validate/invariants.ts +0 -292
  75. package/compiler/validate/validateExpressions.ts +0 -168
  76. package/core/config/index.ts +0 -16
  77. package/core/config/loader.ts +0 -69
  78. package/core/config/types.ts +0 -89
  79. package/core/index.ts +0 -135
  80. package/core/lifecycle/index.ts +0 -49
  81. package/core/lifecycle/zen-mount.ts +0 -182
  82. package/core/lifecycle/zen-unmount.ts +0 -88
  83. package/core/plugins/index.ts +0 -7
  84. package/core/plugins/registry.ts +0 -81
  85. package/core/reactivity/index.ts +0 -54
  86. package/core/reactivity/tracking.ts +0 -167
  87. package/core/reactivity/zen-batch.ts +0 -57
  88. package/core/reactivity/zen-effect.ts +0 -139
  89. package/core/reactivity/zen-memo.ts +0 -146
  90. package/core/reactivity/zen-ref.ts +0 -52
  91. package/core/reactivity/zen-signal.ts +0 -121
  92. package/core/reactivity/zen-state.ts +0 -180
  93. package/core/reactivity/zen-untrack.ts +0 -44
  94. package/dist/cli.js +0 -11659
  95. package/dist/zen-build.js +0 -15633
  96. package/dist/zen-dev.js +0 -15633
  97. package/dist/zen-preview.js +0 -15633
  98. package/dist/zenith.js +0 -15633
  99. package/router/index.ts +0 -76
  100. package/router/manifest.ts +0 -314
  101. package/router/navigation/ZenLink.zen +0 -231
  102. package/router/navigation/index.ts +0 -78
  103. package/router/navigation/zen-link.ts +0 -584
  104. package/router/runtime.ts +0 -458
  105. package/router/types.ts +0 -168
  106. package/runtime/build.ts +0 -17
  107. package/runtime/bundle-generator.ts +0 -943
  108. package/runtime/client-runtime.ts +0 -549
  109. package/runtime/serve.ts +0 -93
  110. package/tsconfig.json +0 -28
@@ -1,549 +0,0 @@
1
- /**
2
- * Zenith Client Runtime
3
- *
4
- * Shared runtime module served as /runtime.js in dev mode.
5
- * Includes:
6
- * - Reactivity primitives (signal, state, effect, memo)
7
- * - Lifecycle hooks (zenOnMount, zenOnUnmount)
8
- * - Event wiring
9
- * - Hydration functions
10
- *
11
- * This is a standalone module that can be imported/served separately
12
- * from page-specific code.
13
- */
14
-
15
- // ============================================
16
- // Dependency Tracking System
17
- // ============================================
18
-
19
- let currentEffect: any = null;
20
- const effectStack: any[] = [];
21
- let batchDepth = 0;
22
- const pendingEffects = new Set<any>();
23
-
24
- function pushContext(effect: any) {
25
- effectStack.push(currentEffect);
26
- currentEffect = effect;
27
- }
28
-
29
- function popContext() {
30
- currentEffect = effectStack.pop() || null;
31
- }
32
-
33
- function trackDependency(subscribers: Set<any>) {
34
- if (currentEffect) {
35
- subscribers.add(currentEffect);
36
- currentEffect.dependencies.add(subscribers);
37
- }
38
- }
39
-
40
- function notifySubscribers(subscribers: Set<any>) {
41
- const effects = [...subscribers];
42
- for (const effect of effects) {
43
- if (batchDepth > 0) {
44
- pendingEffects.add(effect);
45
- } else {
46
- effect.run();
47
- }
48
- }
49
- }
50
-
51
- function cleanupEffect(effect: any) {
52
- for (const deps of effect.dependencies) {
53
- deps.delete(effect);
54
- }
55
- effect.dependencies.clear();
56
- }
57
-
58
- // ============================================
59
- // zenSignal - Atomic reactive value
60
- // ============================================
61
-
62
- export function zenSignal<T>(initialValue: T): (newValue?: T) => T {
63
- let value = initialValue;
64
- const subscribers = new Set<any>();
65
-
66
- function signal(newValue?: T): T {
67
- if (arguments.length === 0) {
68
- trackDependency(subscribers);
69
- return value;
70
- }
71
- if (newValue !== value) {
72
- value = newValue as T;
73
- notifySubscribers(subscribers);
74
- }
75
- return value;
76
- }
77
-
78
- return signal;
79
- }
80
-
81
- // ============================================
82
- // zenState - Deep reactive object with Proxy
83
- // ============================================
84
-
85
- export function zenState<T extends object>(initialObj: T): T {
86
- const subscribers = new Map<string, Set<any>>();
87
-
88
- function getSubscribers(path: string): Set<any> {
89
- if (!subscribers.has(path)) {
90
- subscribers.set(path, new Set());
91
- }
92
- return subscribers.get(path)!;
93
- }
94
-
95
- function createProxy(obj: any, parentPath: string = ''): any {
96
- if (obj === null || typeof obj !== 'object') {
97
- return obj;
98
- }
99
-
100
- return new Proxy(obj, {
101
- get(target, prop) {
102
- if (typeof prop === 'symbol') return target[prop];
103
-
104
- const path = parentPath ? `${parentPath}.${String(prop)}` : String(prop);
105
- trackDependency(getSubscribers(path));
106
-
107
- const value = target[prop];
108
- if (value !== null && typeof value === 'object') {
109
- return createProxy(value, path);
110
- }
111
- return value;
112
- },
113
-
114
- set(target, prop, newValue) {
115
- if (typeof prop === 'symbol') {
116
- target[prop] = newValue;
117
- return true;
118
- }
119
-
120
- const path = parentPath ? `${parentPath}.${String(prop)}` : String(prop);
121
- const oldValue = target[prop];
122
-
123
- if (oldValue !== newValue) {
124
- target[prop] = newValue;
125
-
126
- // Notify this path
127
- const subs = subscribers.get(path);
128
- if (subs) notifySubscribers(subs);
129
-
130
- // Notify parent paths
131
- const parts = path.split('.');
132
- for (let i = parts.length - 1; i >= 0; i--) {
133
- const parentPath = parts.slice(0, i).join('.');
134
- if (parentPath) {
135
- const parentSubs = subscribers.get(parentPath);
136
- if (parentSubs) notifySubscribers(parentSubs);
137
- }
138
- }
139
- }
140
- return true;
141
- }
142
- });
143
- }
144
-
145
- return createProxy(initialObj);
146
- }
147
-
148
- // ============================================
149
- // zenEffect - Reactive effect
150
- // ============================================
151
-
152
- export function zenEffect(fn: () => void | (() => void)): () => void {
153
- let cleanup: (() => void) | void;
154
-
155
- const effect = {
156
- dependencies: new Set<Set<any>>(),
157
- run() {
158
- cleanupEffect(effect);
159
- pushContext(effect);
160
- try {
161
- if (cleanup) cleanup();
162
- cleanup = fn();
163
- } finally {
164
- popContext();
165
- }
166
- }
167
- };
168
-
169
- effect.run();
170
-
171
- return () => {
172
- cleanupEffect(effect);
173
- if (cleanup) cleanup();
174
- };
175
- }
176
-
177
- // ============================================
178
- // zenMemo - Computed/derived value
179
- // ============================================
180
-
181
- export function zenMemo<T>(fn: () => T): () => T {
182
- let value: T;
183
- let dirty = true;
184
- const subscribers = new Set<any>();
185
-
186
- const effect = {
187
- dependencies: new Set<Set<any>>(),
188
- run() {
189
- cleanupEffect(effect);
190
- pushContext(effect);
191
- try {
192
- value = fn();
193
- dirty = false;
194
- notifySubscribers(subscribers);
195
- } finally {
196
- popContext();
197
- }
198
- }
199
- };
200
-
201
- return () => {
202
- trackDependency(subscribers);
203
- if (dirty) {
204
- effect.run();
205
- }
206
- return value;
207
- };
208
- }
209
-
210
- // ============================================
211
- // zenRef - Non-reactive mutable container
212
- // ============================================
213
-
214
- export function zenRef<T>(initialValue?: T): { current: T | null } {
215
- return { current: initialValue !== undefined ? initialValue : null };
216
- }
217
-
218
- // ============================================
219
- // zenBatch - Batch updates
220
- // ============================================
221
-
222
- export function zenBatch(fn: () => void): void {
223
- batchDepth++;
224
- try {
225
- fn();
226
- } finally {
227
- batchDepth--;
228
- if (batchDepth === 0) {
229
- const effects = [...pendingEffects];
230
- pendingEffects.clear();
231
- for (const effect of effects) {
232
- effect.run();
233
- }
234
- }
235
- }
236
- }
237
-
238
- // ============================================
239
- // zenUntrack - Read without tracking
240
- // ============================================
241
-
242
- export function zenUntrack<T>(fn: () => T): T {
243
- const prevEffect = currentEffect;
244
- currentEffect = null;
245
- try {
246
- return fn();
247
- } finally {
248
- currentEffect = prevEffect;
249
- }
250
- }
251
-
252
- // ============================================
253
- // Lifecycle Hooks
254
- // ============================================
255
-
256
- const mountCallbacks: Array<() => void | (() => void)> = [];
257
- const unmountCallbacks: Array<() => void> = [];
258
- let isMounted = false;
259
-
260
- export function zenOnMount(fn: () => void | (() => void)): void {
261
- if (isMounted) {
262
- const cleanup = fn();
263
- if (typeof cleanup === 'function') {
264
- unmountCallbacks.push(cleanup);
265
- }
266
- } else {
267
- mountCallbacks.push(fn);
268
- }
269
- }
270
-
271
- export function zenOnUnmount(fn: () => void): void {
272
- unmountCallbacks.push(fn);
273
- }
274
-
275
- export function triggerMount(): void {
276
- isMounted = true;
277
- for (const cb of mountCallbacks) {
278
- const cleanup = cb();
279
- if (typeof cleanup === 'function') {
280
- unmountCallbacks.push(cleanup);
281
- }
282
- }
283
- mountCallbacks.length = 0;
284
- }
285
-
286
- export function triggerUnmount(): void {
287
- isMounted = false;
288
- for (const cb of unmountCallbacks) {
289
- try { cb(); } catch (e) { console.error('[Zenith] Unmount error:', e); }
290
- }
291
- unmountCallbacks.length = 0;
292
- }
293
-
294
- // ============================================
295
- // Expression Registry
296
- // ============================================
297
-
298
- const expressionRegistry = new Map<string, (state: any) => any>();
299
-
300
- export function registerExpression(id: string, fn: (state: any) => any): void {
301
- expressionRegistry.set(id, fn);
302
- }
303
-
304
- export function getExpression(id: string): ((state: any) => any) | undefined {
305
- return expressionRegistry.get(id);
306
- }
307
-
308
- // ============================================
309
- // Hydration Functions
310
- // ============================================
311
-
312
- const bindings: Array<{ node: Element; type: string; expressionId: string; attributeName?: string }> = [];
313
-
314
- export function hydrate(state: any, container?: Element | Document): void {
315
- const root = container || document;
316
-
317
- // Clear existing bindings
318
- bindings.length = 0;
319
-
320
- // Find all text expression placeholders
321
- const textPlaceholders = root.querySelectorAll('[data-zen-text]');
322
- textPlaceholders.forEach((node) => {
323
- const expressionId = node.getAttribute('data-zen-text');
324
- if (!expressionId) return;
325
-
326
- bindings.push({ node: node as Element, type: 'text', expressionId });
327
- updateTextBinding(node as Element, expressionId, state);
328
- });
329
-
330
- // Find attribute bindings
331
- const attrSelectors = ['class', 'style', 'src', 'href', 'disabled', 'checked'];
332
- for (const attr of attrSelectors) {
333
- const attrPlaceholders = root.querySelectorAll(`[data-zen-attr-${attr}]`);
334
- attrPlaceholders.forEach((node) => {
335
- const expressionId = node.getAttribute(`data-zen-attr-${attr}`);
336
- if (!expressionId) return;
337
-
338
- bindings.push({ node: node as Element, type: 'attribute', expressionId, attributeName: attr });
339
- updateAttributeBinding(node as Element, attr, expressionId, state);
340
- });
341
- }
342
-
343
- // Bind event handlers
344
- bindEvents(root);
345
-
346
- // Trigger mount
347
- triggerMount();
348
- }
349
-
350
- function updateTextBinding(node: Element, expressionId: string, state: any): void {
351
- const expression = expressionRegistry.get(expressionId);
352
- if (!expression) {
353
- console.warn(`[Zenith] Expression ${expressionId} not found`);
354
- return;
355
- }
356
-
357
- try {
358
- const result = expression(state);
359
- if (result === null || result === undefined || result === false) {
360
- node.textContent = '';
361
- } else if (typeof result === 'string') {
362
- if (result.trim().startsWith('<') && result.trim().endsWith('>')) {
363
- node.innerHTML = result;
364
- } else {
365
- node.textContent = result;
366
- }
367
- } else if (result instanceof Node) {
368
- node.innerHTML = '';
369
- node.appendChild(result);
370
- } else if (Array.isArray(result)) {
371
- node.innerHTML = '';
372
- const fragment = document.createDocumentFragment();
373
- result.flat(Infinity).forEach(item => {
374
- if (item instanceof Node) fragment.appendChild(item);
375
- else if (item != null && item !== false) fragment.appendChild(document.createTextNode(String(item)));
376
- });
377
- node.appendChild(fragment);
378
- } else {
379
- node.textContent = String(result);
380
- }
381
- } catch (error) {
382
- console.error(`[Zenith] Error evaluating expression ${expressionId}:`, error);
383
- }
384
- }
385
-
386
- function updateAttributeBinding(element: Element, attrName: string, expressionId: string, state: any): void {
387
- const expression = expressionRegistry.get(expressionId);
388
- if (!expression) return;
389
-
390
- try {
391
- const result = expression(state);
392
-
393
- if (attrName === 'class' || attrName === 'className') {
394
- (element as HTMLElement).className = String(result ?? '');
395
- } else if (attrName === 'style' && typeof result === 'object') {
396
- const styleStr = Object.entries(result).map(([k, v]) => `${k}: ${v}`).join('; ');
397
- element.setAttribute('style', styleStr);
398
- } else if (['disabled', 'checked', 'readonly'].includes(attrName)) {
399
- if (result) {
400
- element.setAttribute(attrName, '');
401
- } else {
402
- element.removeAttribute(attrName);
403
- }
404
- } else {
405
- if (result === null || result === undefined || result === false) {
406
- element.removeAttribute(attrName);
407
- } else {
408
- element.setAttribute(attrName, String(result));
409
- }
410
- }
411
- } catch (error) {
412
- console.error(`[Zenith] Error updating attribute ${attrName}:`, error);
413
- }
414
- }
415
-
416
- export function update(state: any): void {
417
- for (const binding of bindings) {
418
- if (binding.type === 'text') {
419
- updateTextBinding(binding.node, binding.expressionId, state);
420
- } else if (binding.type === 'attribute' && binding.attributeName) {
421
- updateAttributeBinding(binding.node, binding.attributeName, binding.expressionId, state);
422
- }
423
- }
424
- }
425
-
426
- export function bindEvents(container: Element | Document): void {
427
- const eventTypes = ['click', 'change', 'input', 'submit', 'focus', 'blur', 'keyup', 'keydown'];
428
-
429
- for (const eventType of eventTypes) {
430
- const elements = container.querySelectorAll(`[data-zen-${eventType}]`);
431
-
432
- elements.forEach((element) => {
433
- const handlerName = element.getAttribute(`data-zen-${eventType}`);
434
- if (!handlerName) return;
435
-
436
- // Remove existing handler if any
437
- const handlerKey = `__zen_${eventType}_handler`;
438
- const existingHandler = (element as any)[handlerKey];
439
- if (existingHandler) {
440
- element.removeEventListener(eventType, existingHandler);
441
- }
442
-
443
- // Create new handler
444
- const handler = (event: Event) => {
445
- try {
446
- // Try window first, then expression registry
447
- let handlerFunc = (window as any)[handlerName];
448
- if (typeof handlerFunc !== 'function') {
449
- handlerFunc = (window as any).__ZENITH_EXPRESSIONS__?.get(handlerName);
450
- }
451
-
452
- if (typeof handlerFunc === 'function') {
453
- handlerFunc(event, element);
454
- } else {
455
- console.warn(`[Zenith] Event handler "${handlerName}" not found`);
456
- }
457
- } catch (error) {
458
- console.error(`[Zenith] Error executing handler "${handlerName}":`, error);
459
- }
460
- };
461
-
462
- (element as any)[handlerKey] = handler;
463
- element.addEventListener(eventType, handler);
464
- });
465
- }
466
- }
467
-
468
- export function cleanup(container?: Element | Document): void {
469
- const root = container || document;
470
- const eventTypes = ['click', 'change', 'input', 'submit', 'focus', 'blur', 'keyup', 'keydown'];
471
-
472
- for (const eventType of eventTypes) {
473
- const elements = root.querySelectorAll(`[data-zen-${eventType}]`);
474
- elements.forEach((element) => {
475
- const handlerKey = `__zen_${eventType}_handler`;
476
- const handler = (element as any)[handlerKey];
477
- if (handler) {
478
- element.removeEventListener(eventType, handler);
479
- delete (element as any)[handlerKey];
480
- }
481
- });
482
- }
483
-
484
- bindings.length = 0;
485
- triggerUnmount();
486
- }
487
-
488
- // ============================================
489
- // Browser Globals Setup
490
- // ============================================
491
-
492
- export function setupGlobals(): void {
493
- if (typeof window === 'undefined') return;
494
-
495
- const w = window as any;
496
-
497
- // Zenith namespace
498
- w.__zenith = {
499
- signal: zenSignal,
500
- state: zenState,
501
- effect: zenEffect,
502
- memo: zenMemo,
503
- ref: zenRef,
504
- batch: zenBatch,
505
- untrack: zenUntrack,
506
- onMount: zenOnMount,
507
- onUnmount: zenOnUnmount,
508
- triggerMount,
509
- triggerUnmount
510
- };
511
-
512
- // Expression registry
513
- w.__ZENITH_EXPRESSIONS__ = expressionRegistry;
514
-
515
- // Hydration functions
516
- w.__zenith_hydrate = hydrate;
517
- w.__zenith_update = update;
518
- w.__zenith_bindEvents = bindEvents;
519
- w.__zenith_cleanup = cleanup;
520
- w.zenithHydrate = hydrate;
521
- w.zenithUpdate = update;
522
- w.zenithBindEvents = bindEvents;
523
- w.zenithCleanup = cleanup;
524
-
525
- // Direct primitives
526
- w.zenSignal = zenSignal;
527
- w.zenState = zenState;
528
- w.zenEffect = zenEffect;
529
- w.zenMemo = zenMemo;
530
- w.zenRef = zenRef;
531
- w.zenBatch = zenBatch;
532
- w.zenUntrack = zenUntrack;
533
- w.zenOnMount = zenOnMount;
534
- w.zenOnUnmount = zenOnUnmount;
535
-
536
- // Short aliases
537
- w.signal = zenSignal;
538
- w.state = zenState;
539
- w.effect = zenEffect;
540
- w.memo = zenMemo;
541
- w.ref = zenRef;
542
- w.batch = zenBatch;
543
- w.untrack = zenUntrack;
544
- w.onMount = zenOnMount;
545
- w.onUnmount = zenOnUnmount;
546
- }
547
-
548
- // Auto-setup globals on import
549
- setupGlobals();
package/runtime/serve.ts DELETED
@@ -1,93 +0,0 @@
1
- /**
2
- * Zenith Development Server
3
- *
4
- * SPA-compatible server that:
5
- * - Serves static assets directly (js, css, ico, images)
6
- * - Serves index.html for all other routes (SPA fallback)
7
- *
8
- * This enables client-side routing to work on:
9
- * - Direct URL entry
10
- * - Hard refresh
11
- * - Back/forward navigation
12
- */
13
-
14
- import { serve } from "bun"
15
- import path from "path"
16
-
17
- const distDir = path.resolve(import.meta.dir, "..", "app", "dist")
18
-
19
- // File extensions that should be served as static assets
20
- const STATIC_EXTENSIONS = new Set([
21
- ".js",
22
- ".css",
23
- ".ico",
24
- ".png",
25
- ".jpg",
26
- ".jpeg",
27
- ".gif",
28
- ".svg",
29
- ".webp",
30
- ".woff",
31
- ".woff2",
32
- ".ttf",
33
- ".eot",
34
- ".json",
35
- ".map"
36
- ])
37
-
38
- serve({
39
- port: 3000,
40
-
41
- async fetch(req) {
42
- const url = new URL(req.url)
43
- const pathname = url.pathname
44
-
45
- // Get file extension
46
- const ext = path.extname(pathname).toLowerCase()
47
-
48
- // Check if this is a static asset request
49
- if (STATIC_EXTENSIONS.has(ext)) {
50
- const filePath = path.join(distDir, pathname)
51
- const file = Bun.file(filePath)
52
-
53
- // Check if file exists
54
- if (await file.exists()) {
55
- return new Response(file)
56
- }
57
-
58
- // Static file not found
59
- return new Response("Not found", { status: 404 })
60
- }
61
-
62
- // For all other routes, serve index.html (SPA fallback)
63
- const indexPath = path.join(distDir, "index.html")
64
- const indexFile = Bun.file(indexPath)
65
-
66
- if (await indexFile.exists()) {
67
- return new Response(indexFile, {
68
- headers: {
69
- "Content-Type": "text/html; charset=utf-8"
70
- }
71
- })
72
- }
73
-
74
- // No index.html found - likely need to run build first
75
- return new Response(
76
- `<html>
77
- <head><title>Zenith - Build Required</title></head>
78
- <body style="font-family: system-ui; padding: 2rem; text-align: center;">
79
- <h1>Build Required</h1>
80
- <p>Run <code>bun runtime/build.ts</code> first to compile the pages.</p>
81
- </body>
82
- </html>`,
83
- {
84
- status: 500,
85
- headers: { "Content-Type": "text/html; charset=utf-8" }
86
- }
87
- )
88
- }
89
- })
90
-
91
- console.log("🚀 Zenith dev server running at http://localhost:3000")
92
- console.log(" SPA mode: All routes serve index.html")
93
-
package/tsconfig.json DELETED
@@ -1,28 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- // Environment setup & latest features
4
- "lib": [
5
- "DOM",
6
- "ESNext"
7
- ],
8
- "target": "ESNext",
9
- "module": "ESNext",
10
- "moduleDetection": "force",
11
- "allowJs": true,
12
- // Bundler mode
13
- "moduleResolution": "bundler",
14
- "allowImportingTsExtensions": true,
15
- "verbatimModuleSyntax": true,
16
- "noEmit": true,
17
- // Best practices
18
- "strict": true,
19
- "skipLibCheck": true,
20
- "noFallthroughCasesInSwitch": true,
21
- "noUncheckedIndexedAccess": true,
22
- "noImplicitOverride": true,
23
- // Some stricter flags (disabled by default)
24
- "noUnusedLocals": false,
25
- "noUnusedParameters": false,
26
- "noPropertyAccessFromIndexSignature": false
27
- }
28
- }