@zenithbuild/compiler 1.3.2 → 1.3.9
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/dist/build-analyzer.d.ts +44 -0
- package/dist/build-analyzer.js +87 -0
- package/dist/bundler.d.ts +31 -0
- package/dist/bundler.js +92 -0
- package/dist/core/config/index.d.ts +11 -0
- package/dist/core/config/index.js +10 -0
- package/dist/core/config/loader.d.ts +17 -0
- package/dist/core/config/loader.js +60 -0
- package/dist/core/config/types.d.ts +98 -0
- package/dist/core/config/types.js +32 -0
- package/dist/core/index.d.ts +7 -0
- package/dist/core/index.js +6 -0
- package/dist/core/plugins/bridge.d.ts +116 -0
- package/dist/core/plugins/bridge.js +121 -0
- package/dist/core/plugins/index.d.ts +6 -0
- package/dist/core/plugins/index.js +6 -0
- package/dist/core/plugins/registry.d.ts +67 -0
- package/dist/core/plugins/registry.js +112 -0
- package/dist/css/index.d.ts +73 -0
- package/dist/css/index.js +246 -0
- package/dist/discovery/componentDiscovery.d.ts +41 -0
- package/dist/discovery/componentDiscovery.js +66 -0
- package/dist/discovery/layouts.d.ts +14 -0
- package/dist/discovery/layouts.js +36 -0
- package/dist/errors/compilerError.d.ts +31 -0
- package/dist/errors/compilerError.js +51 -0
- package/dist/finalize/generateFinalBundle.d.ts +24 -0
- package/dist/finalize/generateFinalBundle.js +68 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +44 -0
- package/dist/ir/types.d.ts +206 -0
- package/dist/ir/types.js +8 -0
- package/dist/output/types.d.ts +39 -0
- package/dist/output/types.js +6 -0
- package/dist/parseZenFile.d.ts +17 -0
- package/dist/parseZenFile.js +52 -0
- package/dist/runtime/build.d.ts +6 -0
- package/dist/runtime/build.js +13 -0
- package/dist/runtime/bundle-generator.d.ts +27 -0
- package/dist/runtime/bundle-generator.js +1438 -0
- package/dist/runtime/client-runtime.d.ts +41 -0
- package/dist/runtime/client-runtime.js +397 -0
- package/dist/runtime/hydration.d.ts +53 -0
- package/dist/runtime/hydration.js +271 -0
- package/dist/runtime/navigation.d.ts +58 -0
- package/dist/runtime/navigation.js +372 -0
- package/dist/runtime/serve.d.ts +13 -0
- package/dist/runtime/serve.js +76 -0
- package/dist/runtime/thinRuntime.d.ts +23 -0
- package/dist/runtime/thinRuntime.js +158 -0
- package/dist/spa-build.d.ts +26 -0
- package/dist/spa-build.js +849 -0
- package/dist/ssg-build.d.ts +31 -0
- package/dist/ssg-build.js +429 -0
- package/dist/test/bundler-contract.test.d.ts +1 -0
- package/dist/test/bundler-contract.test.js +137 -0
- package/dist/test/compiler-entry.test.d.ts +1 -0
- package/dist/test/compiler-entry.test.js +28 -0
- package/dist/test/error-native-bridge.test.d.ts +1 -0
- package/dist/test/error-native-bridge.test.js +31 -0
- package/dist/test/error-serialization.test.d.ts +1 -0
- package/dist/test/error-serialization.test.js +38 -0
- package/dist/test/phase5-boundary.test.d.ts +1 -0
- package/dist/test/phase5-boundary.test.js +51 -0
- package/dist/transform/layoutProcessor.d.ts +26 -0
- package/dist/transform/layoutProcessor.js +34 -0
- package/dist/validate/invariants.d.ts +23 -0
- package/dist/validate/invariants.js +55 -0
- package/native/compiler-native/compiler-native.node +0 -0
- package/native/compiler-native/index.d.ts +2 -46
- package/native/compiler-native/index.js +1 -16
- package/native/compiler-native/package.json +1 -1
- package/package.json +15 -5
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zenith Client Runtime - Final Canonical Implementation
|
|
3
|
+
* Satisfies strict reactivity requirements:
|
|
4
|
+
* - Signals are functions (get/set)
|
|
5
|
+
* - State is a Proxy
|
|
6
|
+
* - Effects support cleanup/debounce
|
|
7
|
+
* - Memos are computed signals
|
|
8
|
+
* - Direct DOM updates via fine-grained reactivity
|
|
9
|
+
*/
|
|
10
|
+
export declare function zenSignal<T>(initialValue: T): {
|
|
11
|
+
(newValue?: T): T;
|
|
12
|
+
_isSignal: boolean;
|
|
13
|
+
toString(): string;
|
|
14
|
+
valueOf(): T;
|
|
15
|
+
};
|
|
16
|
+
export declare function zenState<T extends object>(initialObj: T): T;
|
|
17
|
+
export declare function zenEffect(fn: () => void | (() => void), options?: {
|
|
18
|
+
debounce?: number;
|
|
19
|
+
defer?: boolean;
|
|
20
|
+
}): () => void;
|
|
21
|
+
export declare function zenMemo<T>(fn: () => T): {
|
|
22
|
+
(): T;
|
|
23
|
+
_isSignal: boolean;
|
|
24
|
+
};
|
|
25
|
+
export declare function zenRef<T>(initial?: T): {
|
|
26
|
+
current: NonNullable<T> | null;
|
|
27
|
+
};
|
|
28
|
+
export declare function zenBatch(fn: () => void): void;
|
|
29
|
+
export declare function zenUntrack<T>(fn: () => T): T;
|
|
30
|
+
export interface ComponentInstance {
|
|
31
|
+
mountHooks: Array<() => void | (() => void)>;
|
|
32
|
+
}
|
|
33
|
+
export declare function setActiveInstance(inst: ComponentInstance | null): void;
|
|
34
|
+
export declare function zenOnMount(cb: any): void;
|
|
35
|
+
export declare function zenOnUnmount(cb: any): void;
|
|
36
|
+
export declare function triggerMount(inst: ComponentInstance): void;
|
|
37
|
+
export declare function h(tag: string, props: any, ...children: any[]): Element;
|
|
38
|
+
export declare function fragment(children: any): DocumentFragment;
|
|
39
|
+
export declare function zenLoop(sourceFn: () => any[], itemFn: (item: any, idx: number) => Node): DocumentFragment;
|
|
40
|
+
export declare function zenithHydrate(state: any, container?: Element | Document): void;
|
|
41
|
+
export declare function setup(): void;
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zenith Client Runtime - Final Canonical Implementation
|
|
3
|
+
* Satisfies strict reactivity requirements:
|
|
4
|
+
* - Signals are functions (get/set)
|
|
5
|
+
* - State is a Proxy
|
|
6
|
+
* - Effects support cleanup/debounce
|
|
7
|
+
* - Memos are computed signals
|
|
8
|
+
* - Direct DOM updates via fine-grained reactivity
|
|
9
|
+
*/
|
|
10
|
+
let currentEffect = null;
|
|
11
|
+
const contextStack = [];
|
|
12
|
+
let batchDepth = 0;
|
|
13
|
+
const pendingEffects = new Set();
|
|
14
|
+
function pushContext(effect) {
|
|
15
|
+
contextStack.push(currentEffect);
|
|
16
|
+
currentEffect = effect;
|
|
17
|
+
}
|
|
18
|
+
function popContext() {
|
|
19
|
+
currentEffect = contextStack.pop();
|
|
20
|
+
}
|
|
21
|
+
function trackDependency(subscribers) {
|
|
22
|
+
if (currentEffect) {
|
|
23
|
+
subscribers.add(currentEffect);
|
|
24
|
+
currentEffect.dependencies.add(subscribers);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function notifySubscribers(subscribers) {
|
|
28
|
+
const effects = Array.from(subscribers);
|
|
29
|
+
for (const effect of effects) {
|
|
30
|
+
if (batchDepth > 0) {
|
|
31
|
+
pendingEffects.add(effect);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
effect.run();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function cleanupEffect(effect) {
|
|
39
|
+
for (const dependency of effect.dependencies) {
|
|
40
|
+
dependency.delete(effect);
|
|
41
|
+
}
|
|
42
|
+
effect.dependencies.clear();
|
|
43
|
+
}
|
|
44
|
+
// === zenSignal ===
|
|
45
|
+
export function zenSignal(initialValue) {
|
|
46
|
+
let value = initialValue;
|
|
47
|
+
const subscribers = new Set();
|
|
48
|
+
function signal(newValue) {
|
|
49
|
+
if (arguments.length === 0) {
|
|
50
|
+
trackDependency(subscribers);
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
if (newValue !== value) {
|
|
54
|
+
value = newValue;
|
|
55
|
+
notifySubscribers(subscribers);
|
|
56
|
+
}
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
signal._isSignal = true;
|
|
60
|
+
signal.toString = () => String(value);
|
|
61
|
+
signal.valueOf = () => value;
|
|
62
|
+
// .value getter for convenience/compat
|
|
63
|
+
Object.defineProperty(signal, 'value', {
|
|
64
|
+
get: () => signal(),
|
|
65
|
+
set: (v) => signal(v)
|
|
66
|
+
});
|
|
67
|
+
return signal;
|
|
68
|
+
}
|
|
69
|
+
// === zenState ===
|
|
70
|
+
export function zenState(initialObj) {
|
|
71
|
+
const subscribers = new Map();
|
|
72
|
+
function getSubscribers(path) {
|
|
73
|
+
if (!subscribers.has(path)) {
|
|
74
|
+
subscribers.set(path, new Set());
|
|
75
|
+
}
|
|
76
|
+
return subscribers.get(path);
|
|
77
|
+
}
|
|
78
|
+
function createProxy(obj, parentPath = '') {
|
|
79
|
+
if (obj === null || typeof obj !== 'object' || obj._isSignal)
|
|
80
|
+
return obj;
|
|
81
|
+
return new Proxy(obj, {
|
|
82
|
+
get(target, prop) {
|
|
83
|
+
if (typeof prop === 'symbol')
|
|
84
|
+
return target[prop];
|
|
85
|
+
const path = parentPath ? `${parentPath}.${String(prop)}` : String(prop);
|
|
86
|
+
trackDependency(getSubscribers(path));
|
|
87
|
+
const value = target[prop];
|
|
88
|
+
if (value !== null && typeof value === 'object' && !value._isSignal) {
|
|
89
|
+
return createProxy(value, path);
|
|
90
|
+
}
|
|
91
|
+
return value;
|
|
92
|
+
},
|
|
93
|
+
set(target, prop, newValue) {
|
|
94
|
+
if (typeof prop === 'symbol') {
|
|
95
|
+
target[prop] = newValue;
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
const path = parentPath ? `${parentPath}.${String(prop)}` : String(prop);
|
|
99
|
+
const oldValue = target[prop];
|
|
100
|
+
if (oldValue && typeof oldValue === 'function' && oldValue._isSignal) {
|
|
101
|
+
oldValue(newValue);
|
|
102
|
+
}
|
|
103
|
+
else if (oldValue !== newValue) {
|
|
104
|
+
target[prop] = newValue;
|
|
105
|
+
notifySubscribers(getSubscribers(path));
|
|
106
|
+
// Bubble up
|
|
107
|
+
const parts = path.split('.');
|
|
108
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
109
|
+
const pp = parts.slice(0, i).join('.');
|
|
110
|
+
if (pp)
|
|
111
|
+
notifySubscribers(getSubscribers(pp));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return createProxy(initialObj);
|
|
119
|
+
}
|
|
120
|
+
// === zenEffect ===
|
|
121
|
+
export function zenEffect(fn, options = {}) {
|
|
122
|
+
let cleanup;
|
|
123
|
+
let timeout;
|
|
124
|
+
const effect = {
|
|
125
|
+
dependencies: new Set(),
|
|
126
|
+
run: () => {
|
|
127
|
+
if (options.debounce) {
|
|
128
|
+
if (timeout)
|
|
129
|
+
clearTimeout(timeout);
|
|
130
|
+
timeout = setTimeout(execute, options.debounce);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
execute();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
function execute() {
|
|
138
|
+
cleanupEffect(effect);
|
|
139
|
+
pushContext(effect);
|
|
140
|
+
try {
|
|
141
|
+
if (cleanup)
|
|
142
|
+
cleanup();
|
|
143
|
+
cleanup = fn();
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
popContext();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (!options.defer)
|
|
150
|
+
execute();
|
|
151
|
+
return () => {
|
|
152
|
+
cleanupEffect(effect);
|
|
153
|
+
if (cleanup)
|
|
154
|
+
cleanup();
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
// === zenMemo ===
|
|
158
|
+
export function zenMemo(fn) {
|
|
159
|
+
const signal = zenSignal(undefined);
|
|
160
|
+
zenEffect(() => { signal(fn()); });
|
|
161
|
+
const memo = () => signal();
|
|
162
|
+
memo._isSignal = true;
|
|
163
|
+
return memo;
|
|
164
|
+
}
|
|
165
|
+
// === zenRef ===
|
|
166
|
+
export function zenRef(initial) {
|
|
167
|
+
return { current: initial || null };
|
|
168
|
+
}
|
|
169
|
+
// === zenBatch ===
|
|
170
|
+
export function zenBatch(fn) {
|
|
171
|
+
batchDepth++;
|
|
172
|
+
try {
|
|
173
|
+
fn();
|
|
174
|
+
}
|
|
175
|
+
finally {
|
|
176
|
+
batchDepth--;
|
|
177
|
+
if (batchDepth === 0) {
|
|
178
|
+
const effects = Array.from(pendingEffects);
|
|
179
|
+
pendingEffects.clear();
|
|
180
|
+
effects.forEach(e => e.run());
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
export function zenUntrack(fn) {
|
|
185
|
+
pushContext(null);
|
|
186
|
+
try {
|
|
187
|
+
return fn();
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
popContext();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
let activeInstance = null;
|
|
194
|
+
export function setActiveInstance(inst) { activeInstance = inst; }
|
|
195
|
+
export function zenOnMount(cb) {
|
|
196
|
+
if (activeInstance)
|
|
197
|
+
activeInstance.mountHooks.push(cb);
|
|
198
|
+
// Fallback: if no active instance (e.g. top level), run immediately?
|
|
199
|
+
// Better to strict scope it, but for safety in simple usages:
|
|
200
|
+
else if (typeof window !== 'undefined' && document.readyState === 'complete') {
|
|
201
|
+
// cb(); // Uncomment if lax behavior desired
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
export function zenOnUnmount(cb) {
|
|
205
|
+
// Placeholder: requires unmount hooks in instance
|
|
206
|
+
}
|
|
207
|
+
export function triggerMount(inst) {
|
|
208
|
+
if (inst && inst.mountHooks) {
|
|
209
|
+
inst.mountHooks.forEach(cb => {
|
|
210
|
+
const cleanup = cb();
|
|
211
|
+
// TODO: Store cleanup for unmount
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// === Rendering & Hydration ===
|
|
216
|
+
const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
|
|
217
|
+
const SVG_TAGS = new Set([
|
|
218
|
+
'svg', 'path', 'circle', 'ellipse', 'line', 'polyline', 'polygon', 'rect',
|
|
219
|
+
'g', 'defs', 'use', 'symbol', 'clipPath', 'mask', 'pattern', 'marker',
|
|
220
|
+
'linearGradient', 'radialGradient', 'stop', 'filter', 'feGaussianBlur',
|
|
221
|
+
'feOffset', 'feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite',
|
|
222
|
+
'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feFlood',
|
|
223
|
+
'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feImage', 'feMerge', 'feMergeNode',
|
|
224
|
+
'feMorphology', 'fePointLight', 'feSpotLight', 'feSpecularLighting', 'feTile',
|
|
225
|
+
'feTurbulence', 'foreignObject', 'image', 'switch', 'text', 'tspan', 'textPath',
|
|
226
|
+
'title', 'desc', 'metadata', 'a', 'view', 'animate', 'animateMotion',
|
|
227
|
+
'animateTransform', 'set', 'mpath'
|
|
228
|
+
]);
|
|
229
|
+
// Track current namespace context for nested elements
|
|
230
|
+
let currentNamespace = null;
|
|
231
|
+
export function h(tag, props, ...children) {
|
|
232
|
+
// Determine if this element should be in SVG namespace
|
|
233
|
+
const isSvgTag = SVG_TAGS.has(tag) || SVG_TAGS.has(tag.toLowerCase());
|
|
234
|
+
const useSvgNamespace = isSvgTag || currentNamespace === SVG_NAMESPACE;
|
|
235
|
+
// Create element with correct namespace
|
|
236
|
+
const el = useSvgNamespace
|
|
237
|
+
? document.createElementNS(SVG_NAMESPACE, tag)
|
|
238
|
+
: document.createElement(tag);
|
|
239
|
+
// Track namespace context for children
|
|
240
|
+
const previousNamespace = currentNamespace;
|
|
241
|
+
if (tag === 'svg' || tag === 'SVG') {
|
|
242
|
+
currentNamespace = SVG_NAMESPACE;
|
|
243
|
+
}
|
|
244
|
+
if (props) {
|
|
245
|
+
for (const [key, val] of Object.entries(props)) {
|
|
246
|
+
if (key === 'ref') {
|
|
247
|
+
if (val && typeof val === 'object' && 'current' in val)
|
|
248
|
+
val.current = el;
|
|
249
|
+
else if (typeof val === 'string') {
|
|
250
|
+
const state = window.__ZENITH_STATE__;
|
|
251
|
+
if (state && state[val] && typeof state[val] === 'object' && 'current' in state[val])
|
|
252
|
+
state[val].current = el;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
else if (key.startsWith('on') && typeof val === 'function') {
|
|
256
|
+
el.addEventListener(key.slice(2).toLowerCase(), (e) => {
|
|
257
|
+
// Check if val returns a function (expression wrapper) or is the handler
|
|
258
|
+
const res = val(e, el);
|
|
259
|
+
if (typeof res === 'function')
|
|
260
|
+
res(e, el);
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
else if (typeof val === 'function') {
|
|
264
|
+
zenEffect(() => { updateAttr(el, key, val()); });
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
updateAttr(el, key, val);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
const spread = (items) => items.reduce((acc, v) => acc.concat(Array.isArray(v) ? spread(v) : v), []);
|
|
272
|
+
spread(children).forEach(child => {
|
|
273
|
+
if (typeof child === 'function') {
|
|
274
|
+
// For reactive expressions, we need a placeholder that can be updated
|
|
275
|
+
// The placeholder is a comment node that marks where content should go
|
|
276
|
+
const placeholder = document.createComment('expr');
|
|
277
|
+
el.appendChild(placeholder);
|
|
278
|
+
let currentNodes = [];
|
|
279
|
+
zenEffect(() => {
|
|
280
|
+
const result = child();
|
|
281
|
+
// Remove old nodes
|
|
282
|
+
currentNodes.forEach(n => { if (n.parentNode)
|
|
283
|
+
n.parentNode.removeChild(n); });
|
|
284
|
+
currentNodes = [];
|
|
285
|
+
if (result == null) {
|
|
286
|
+
// null/undefined - render nothing
|
|
287
|
+
}
|
|
288
|
+
else if (result instanceof Node) {
|
|
289
|
+
// DOM node - insert it
|
|
290
|
+
placeholder.before(result);
|
|
291
|
+
currentNodes = [result];
|
|
292
|
+
}
|
|
293
|
+
else if (Array.isArray(result)) {
|
|
294
|
+
// Array of nodes/strings
|
|
295
|
+
result.forEach(item => {
|
|
296
|
+
const node = item instanceof Node ? item : document.createTextNode(String(item));
|
|
297
|
+
placeholder.before(node);
|
|
298
|
+
currentNodes.push(node);
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
// Primitive (string, number) - create text node
|
|
303
|
+
const textNode = document.createTextNode(String(result));
|
|
304
|
+
placeholder.before(textNode);
|
|
305
|
+
currentNodes = [textNode];
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
el.appendChild(child instanceof Node ? child : document.createTextNode(String(child)));
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
// Restore previous namespace context
|
|
314
|
+
currentNamespace = previousNamespace;
|
|
315
|
+
return el;
|
|
316
|
+
}
|
|
317
|
+
function updateAttr(el, key, val) {
|
|
318
|
+
if (key === 'class' || key === 'className') {
|
|
319
|
+
// SVG uses className.baseVal, HTML uses className directly
|
|
320
|
+
if ('className' in el && typeof el.className === 'object') {
|
|
321
|
+
el.className.baseVal = String(val || '');
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
el.className = String(val || '');
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
else if (key === 'style' && typeof val === 'object')
|
|
328
|
+
Object.assign(el.style, val);
|
|
329
|
+
else if (val == null || val === false)
|
|
330
|
+
el.removeAttribute(key);
|
|
331
|
+
else
|
|
332
|
+
el.setAttribute(key, String(val));
|
|
333
|
+
}
|
|
334
|
+
export function fragment(children) {
|
|
335
|
+
const frag = document.createDocumentFragment();
|
|
336
|
+
const items = typeof children === 'function' ? children() : children;
|
|
337
|
+
const spread = (its) => its.reduce((acc, v) => acc.concat(Array.isArray(v) ? spread(v) : v), []);
|
|
338
|
+
spread(Array.isArray(items) ? items : [items]).forEach(item => {
|
|
339
|
+
frag.appendChild(item instanceof Node ? item : document.createTextNode(String(item)));
|
|
340
|
+
});
|
|
341
|
+
return frag;
|
|
342
|
+
}
|
|
343
|
+
export function zenLoop(sourceFn, itemFn) {
|
|
344
|
+
const start = document.createComment('loop:start');
|
|
345
|
+
const end = document.createComment('loop:end');
|
|
346
|
+
const frag = document.createDocumentFragment();
|
|
347
|
+
frag.append(start, end);
|
|
348
|
+
zenEffect(() => {
|
|
349
|
+
const items = sourceFn() || [];
|
|
350
|
+
let curr = start.nextSibling;
|
|
351
|
+
while (curr && curr !== end) {
|
|
352
|
+
const next = curr.nextSibling;
|
|
353
|
+
curr.remove();
|
|
354
|
+
curr = next;
|
|
355
|
+
}
|
|
356
|
+
items.forEach((item, i) => end.before(itemFn(item, i)));
|
|
357
|
+
});
|
|
358
|
+
return frag;
|
|
359
|
+
}
|
|
360
|
+
export function zenithHydrate(state, container = document) {
|
|
361
|
+
const ir = window.canonicalIR;
|
|
362
|
+
if (!ir)
|
|
363
|
+
return;
|
|
364
|
+
window.__ZENITH_STATE__ = state;
|
|
365
|
+
const nodes = ir(state);
|
|
366
|
+
const mount = container === document ? document.body : container;
|
|
367
|
+
mount.innerHTML = '';
|
|
368
|
+
const arr = Array.isArray(nodes) ? nodes : [nodes];
|
|
369
|
+
arr.forEach(n => {
|
|
370
|
+
if (n == null || n === false)
|
|
371
|
+
return;
|
|
372
|
+
// If it's a full <html> node, we might need to be more careful,
|
|
373
|
+
// but for now let's just avoid stringifying nulls.
|
|
374
|
+
mount.appendChild(n instanceof Node ? n : document.createTextNode(String(n)));
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
// === Global Setup ===
|
|
378
|
+
export function setup() {
|
|
379
|
+
if (typeof window === 'undefined')
|
|
380
|
+
return;
|
|
381
|
+
const w = window;
|
|
382
|
+
w.__zenith = { h, fragment, loop: zenLoop, state: zenState, signal: zenSignal, effect: zenEffect, memo: zenMemo, ref: zenRef, batch: zenBatch, onMount: zenOnMount, setActiveInstance, triggerMount, spread_children: (a) => a };
|
|
383
|
+
w.zenithHydrate = zenithHydrate;
|
|
384
|
+
// Expose globals for imported usage
|
|
385
|
+
w.zenSignal = zenSignal;
|
|
386
|
+
w.zenState = zenState;
|
|
387
|
+
w.zenEffect = zenEffect;
|
|
388
|
+
w.zenMemo = zenMemo;
|
|
389
|
+
w.zenRef = zenRef;
|
|
390
|
+
w.zenBatch = zenBatch;
|
|
391
|
+
w.zenUntrack = zenUntrack;
|
|
392
|
+
w.zenOnMount = zenOnMount;
|
|
393
|
+
// [ZENITH-NATIVE] zenOrder: Scheduling primitive
|
|
394
|
+
w.zenOrder = (fn) => { if (typeof fn === 'function')
|
|
395
|
+
fn(); };
|
|
396
|
+
}
|
|
397
|
+
setup();
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime Hydration Layer
|
|
3
|
+
*
|
|
4
|
+
* Phase 5: Browser-side runtime that hydrates static HTML with dynamic expressions
|
|
5
|
+
*
|
|
6
|
+
* This runtime:
|
|
7
|
+
* - Locates DOM placeholders (data-zen-text, data-zen-attr-*)
|
|
8
|
+
* - Evaluates precompiled expressions against state
|
|
9
|
+
* - Updates DOM textContent, attributes, and properties
|
|
10
|
+
* - Binds event handlers
|
|
11
|
+
* - Handles reactive state updates
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Expression registry - maps expression IDs to their evaluation functions
|
|
15
|
+
* Populated at runtime with compiled expressions
|
|
16
|
+
*/
|
|
17
|
+
declare global {
|
|
18
|
+
interface Window {
|
|
19
|
+
__ZENITH_EXPRESSIONS__?: Map<string, (state: any) => any>;
|
|
20
|
+
__ZENITH_STATE__?: any;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Hydrate static HTML with dynamic expressions
|
|
25
|
+
*
|
|
26
|
+
* @param state - The state object to evaluate expressions against
|
|
27
|
+
* @param container - The container element to hydrate (defaults to document)
|
|
28
|
+
*/
|
|
29
|
+
export declare function hydrate(state: any, container?: Document | Element): void;
|
|
30
|
+
/**
|
|
31
|
+
* Bind event handlers to DOM elements
|
|
32
|
+
*
|
|
33
|
+
* @param container - The container element to bind events in (defaults to document)
|
|
34
|
+
*/
|
|
35
|
+
export declare function bindEvents(container?: Document | Element): void;
|
|
36
|
+
/**
|
|
37
|
+
* Update all bindings when state changes
|
|
38
|
+
*
|
|
39
|
+
* @param state - The new state object
|
|
40
|
+
*/
|
|
41
|
+
export declare function update(state: any): void;
|
|
42
|
+
/**
|
|
43
|
+
* Initialize the expression registry
|
|
44
|
+
* Called once when the runtime loads
|
|
45
|
+
*
|
|
46
|
+
* @param expressions - Map of expression IDs to evaluation functions
|
|
47
|
+
*/
|
|
48
|
+
export declare function initExpressions(expressions: Map<string, (state: any) => any>): void;
|
|
49
|
+
/**
|
|
50
|
+
* Clear all bindings and event listeners
|
|
51
|
+
* Useful for cleanup when navigating away
|
|
52
|
+
*/
|
|
53
|
+
export declare function cleanup(container?: Document | Element): void;
|