@zenithbuild/core 0.4.6 → 0.5.0-beta.2.1

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 (109) 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 +40 -52
  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 -174
  38. package/compiler/discovery/layouts.ts +0 -61
  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 -81
  43. package/compiler/ir/types.ts +0 -150
  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 -591
  48. package/compiler/parse/parseZenFile.ts +0 -66
  49. package/compiler/parse/scriptAnalysis.ts +0 -83
  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 -343
  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 -289
  65. package/compiler/transform/expressionTransformer.ts +0 -385
  66. package/compiler/transform/fragmentLowering.ts +0 -634
  67. package/compiler/transform/generateBindings.ts +0 -47
  68. package/compiler/transform/generateHTML.ts +0 -28
  69. package/compiler/transform/layoutProcessor.ts +0 -132
  70. package/compiler/transform/slotResolver.ts +0 -292
  71. package/compiler/transform/transformNode.ts +0 -126
  72. package/compiler/transform/transformTemplate.ts +0 -38
  73. package/compiler/validate/invariants.ts +0 -292
  74. package/compiler/validate/validateExpressions.ts +0 -168
  75. package/core/config/index.ts +0 -16
  76. package/core/config/loader.ts +0 -69
  77. package/core/config/types.ts +0 -89
  78. package/core/index.ts +0 -135
  79. package/core/lifecycle/index.ts +0 -49
  80. package/core/lifecycle/zen-mount.ts +0 -182
  81. package/core/lifecycle/zen-unmount.ts +0 -88
  82. package/core/plugins/index.ts +0 -7
  83. package/core/plugins/registry.ts +0 -81
  84. package/core/reactivity/index.ts +0 -54
  85. package/core/reactivity/tracking.ts +0 -167
  86. package/core/reactivity/zen-batch.ts +0 -57
  87. package/core/reactivity/zen-effect.ts +0 -139
  88. package/core/reactivity/zen-memo.ts +0 -146
  89. package/core/reactivity/zen-ref.ts +0 -52
  90. package/core/reactivity/zen-signal.ts +0 -121
  91. package/core/reactivity/zen-state.ts +0 -180
  92. package/core/reactivity/zen-untrack.ts +0 -44
  93. package/dist/cli.js +0 -11653
  94. package/dist/zen-build.js +0 -15388
  95. package/dist/zen-dev.js +0 -15388
  96. package/dist/zen-preview.js +0 -15388
  97. package/dist/zenith.js +0 -15388
  98. package/router/index.ts +0 -76
  99. package/router/manifest.ts +0 -314
  100. package/router/navigation/ZenLink.zen +0 -231
  101. package/router/navigation/index.ts +0 -78
  102. package/router/navigation/zen-link.ts +0 -584
  103. package/router/runtime.ts +0 -458
  104. package/router/types.ts +0 -168
  105. package/runtime/build.ts +0 -17
  106. package/runtime/bundle-generator.ts +0 -800
  107. package/runtime/client-runtime.ts +0 -549
  108. package/runtime/serve.ts +0 -93
  109. 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
- }