@zenithbuild/core 0.6.2 → 1.0.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.
- package/README.md +20 -19
- package/cli/commands/add.ts +2 -2
- package/cli/commands/build.ts +2 -3
- package/cli/commands/dev.ts +182 -103
- package/cli/commands/index.ts +1 -1
- package/cli/commands/preview.ts +1 -1
- package/cli/commands/remove.ts +2 -2
- package/cli/index.ts +1 -1
- package/cli/main.ts +1 -1
- package/cli/utils/logger.ts +1 -1
- package/cli/utils/plugin-manager.ts +1 -1
- package/cli/utils/project.ts +4 -4
- package/core/components/ErrorPage.zen +218 -0
- package/core/components/index.ts +15 -0
- package/core/config.ts +1 -0
- package/core/index.ts +29 -0
- package/dist/compiler-native-frej59m4.node +0 -0
- package/dist/core/compiler-native-frej59m4.node +0 -0
- package/dist/core/index.js +6293 -0
- package/dist/runtime/lifecycle/index.js +1 -0
- package/dist/runtime/reactivity/index.js +1 -0
- package/dist/zen-build.js +7465 -19128
- package/dist/zen-dev.js +7465 -19128
- package/dist/zen-preview.js +7465 -19128
- package/dist/zenith.js +7465 -19128
- package/package.json +21 -22
- package/cli/utils/content.ts +0 -112
- package/compiler/README.md +0 -380
- package/compiler/build-analyzer.ts +0 -122
- package/compiler/css/index.ts +0 -317
- package/compiler/discovery/componentDiscovery.ts +0 -178
- package/compiler/discovery/layouts.ts +0 -70
- package/compiler/errors/compilerError.ts +0 -56
- package/compiler/finalize/finalizeOutput.ts +0 -192
- package/compiler/finalize/generateFinalBundle.ts +0 -82
- package/compiler/index.ts +0 -83
- package/compiler/ir/types.ts +0 -174
- package/compiler/output/types.ts +0 -34
- package/compiler/parse/detectMapExpressions.ts +0 -102
- package/compiler/parse/importTypes.ts +0 -78
- package/compiler/parse/parseImports.ts +0 -309
- package/compiler/parse/parseScript.ts +0 -46
- package/compiler/parse/parseTemplate.ts +0 -599
- package/compiler/parse/parseZenFile.ts +0 -66
- package/compiler/parse/scriptAnalysis.ts +0 -91
- package/compiler/parse/trackLoopContext.ts +0 -82
- package/compiler/runtime/dataExposure.ts +0 -317
- package/compiler/runtime/generateDOM.ts +0 -246
- package/compiler/runtime/generateHydrationBundle.ts +0 -407
- package/compiler/runtime/hydration.ts +0 -309
- package/compiler/runtime/navigation.ts +0 -432
- package/compiler/runtime/thinRuntime.ts +0 -160
- package/compiler/runtime/transformIR.ts +0 -370
- package/compiler/runtime/wrapExpression.ts +0 -95
- package/compiler/runtime/wrapExpressionWithLoop.ts +0 -83
- package/compiler/spa-build.ts +0 -917
- package/compiler/ssg-build.ts +0 -422
- package/compiler/test/validate-test.ts +0 -104
- package/compiler/transform/classifyExpression.ts +0 -444
- package/compiler/transform/componentResolver.ts +0 -312
- package/compiler/transform/componentScriptTransformer.ts +0 -303
- package/compiler/transform/expressionTransformer.ts +0 -385
- package/compiler/transform/fragmentLowering.ts +0 -634
- package/compiler/transform/generateBindings.ts +0 -47
- package/compiler/transform/generateHTML.ts +0 -28
- package/compiler/transform/layoutProcessor.ts +0 -132
- package/compiler/transform/slotResolver.ts +0 -292
- package/compiler/transform/transformNode.ts +0 -126
- package/compiler/transform/transformTemplate.ts +0 -38
- package/compiler/validate/invariants.ts +0 -292
- package/compiler/validate/validateExpressions.ts +0 -168
- package/core/config/index.ts +0 -16
- package/core/config/loader.ts +0 -69
- package/core/config/types.ts +0 -89
- package/core/plugins/index.ts +0 -7
- package/core/plugins/registry.ts +0 -81
- package/dist/cli.js +0 -11665
- package/router/manifest.ts +0 -314
- package/router/navigation/ZenLink.zen +0 -231
- package/router/navigation/index.ts +0 -78
- package/router/navigation/zen-link.ts +0 -584
- package/router/runtime.ts +0 -458
- package/router/types.ts +0 -168
- package/runtime/build.ts +0 -17
- package/runtime/bundle-generator.ts +0 -1247
- package/runtime/client-runtime.ts +0 -549
- package/runtime/serve.ts +0 -93
|
@@ -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
|
-
|