@zenithbuild/compiler 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +30 -0
  3. package/dist/build-analyzer.d.ts +44 -0
  4. package/dist/build-analyzer.js +87 -0
  5. package/dist/bundler.d.ts +31 -0
  6. package/dist/bundler.js +86 -0
  7. package/dist/core/components/index.d.ts +9 -0
  8. package/dist/core/components/index.js +13 -0
  9. package/dist/core/config/index.d.ts +11 -0
  10. package/dist/core/config/index.js +10 -0
  11. package/dist/core/config/loader.d.ts +17 -0
  12. package/dist/core/config/loader.js +60 -0
  13. package/dist/core/config/types.d.ts +98 -0
  14. package/dist/core/config/types.js +32 -0
  15. package/dist/core/index.d.ts +7 -0
  16. package/dist/core/index.js +6 -0
  17. package/dist/core/lifecycle/index.d.ts +16 -0
  18. package/dist/core/lifecycle/index.js +19 -0
  19. package/dist/core/lifecycle/zen-mount.d.ts +66 -0
  20. package/dist/core/lifecycle/zen-mount.js +151 -0
  21. package/dist/core/lifecycle/zen-unmount.d.ts +54 -0
  22. package/dist/core/lifecycle/zen-unmount.js +76 -0
  23. package/dist/core/plugins/bridge.d.ts +116 -0
  24. package/dist/core/plugins/bridge.js +121 -0
  25. package/dist/core/plugins/index.d.ts +6 -0
  26. package/dist/core/plugins/index.js +6 -0
  27. package/dist/core/plugins/registry.d.ts +67 -0
  28. package/dist/core/plugins/registry.js +113 -0
  29. package/dist/core/reactivity/index.d.ts +30 -0
  30. package/dist/core/reactivity/index.js +33 -0
  31. package/dist/core/reactivity/tracking.d.ts +74 -0
  32. package/dist/core/reactivity/tracking.js +136 -0
  33. package/dist/core/reactivity/zen-batch.d.ts +45 -0
  34. package/dist/core/reactivity/zen-batch.js +54 -0
  35. package/dist/core/reactivity/zen-effect.d.ts +48 -0
  36. package/dist/core/reactivity/zen-effect.js +98 -0
  37. package/dist/core/reactivity/zen-memo.d.ts +43 -0
  38. package/dist/core/reactivity/zen-memo.js +100 -0
  39. package/dist/core/reactivity/zen-ref.d.ts +44 -0
  40. package/dist/core/reactivity/zen-ref.js +34 -0
  41. package/dist/core/reactivity/zen-signal.d.ts +48 -0
  42. package/dist/core/reactivity/zen-signal.js +84 -0
  43. package/dist/core/reactivity/zen-state.d.ts +35 -0
  44. package/dist/core/reactivity/zen-state.js +147 -0
  45. package/dist/core/reactivity/zen-untrack.d.ts +38 -0
  46. package/dist/core/reactivity/zen-untrack.js +41 -0
  47. package/dist/css/index.d.ts +73 -0
  48. package/dist/css/index.js +246 -0
  49. package/dist/discovery/componentDiscovery.d.ts +42 -0
  50. package/dist/discovery/componentDiscovery.js +56 -0
  51. package/dist/discovery/layouts.d.ts +13 -0
  52. package/dist/discovery/layouts.js +41 -0
  53. package/dist/errors/compilerError.d.ts +31 -0
  54. package/dist/errors/compilerError.js +51 -0
  55. package/dist/finalize/finalizeOutput.d.ts +32 -0
  56. package/dist/finalize/finalizeOutput.js +62 -0
  57. package/dist/finalize/generateFinalBundle.d.ts +24 -0
  58. package/dist/finalize/generateFinalBundle.js +68 -0
  59. package/dist/index.d.ts +36 -0
  60. package/dist/index.js +51 -0
  61. package/dist/ir/types.d.ts +181 -0
  62. package/dist/ir/types.js +8 -0
  63. package/dist/output/types.d.ts +30 -0
  64. package/dist/output/types.js +6 -0
  65. package/dist/parse/detectMapExpressions.d.ts +45 -0
  66. package/dist/parse/detectMapExpressions.js +77 -0
  67. package/dist/parse/parseScript.d.ts +8 -0
  68. package/dist/parse/parseScript.js +36 -0
  69. package/dist/parse/parseTemplate.d.ts +11 -0
  70. package/dist/parse/parseTemplate.js +487 -0
  71. package/dist/parse/parseZenFile.d.ts +11 -0
  72. package/dist/parse/parseZenFile.js +50 -0
  73. package/dist/parse/scriptAnalysis.d.ts +25 -0
  74. package/dist/parse/scriptAnalysis.js +60 -0
  75. package/dist/parse/trackLoopContext.d.ts +20 -0
  76. package/dist/parse/trackLoopContext.js +62 -0
  77. package/dist/parseZenFile.d.ts +10 -0
  78. package/dist/parseZenFile.js +55 -0
  79. package/dist/runtime/analyzeAndEmit.d.ts +20 -0
  80. package/dist/runtime/analyzeAndEmit.js +70 -0
  81. package/dist/runtime/build.d.ts +6 -0
  82. package/dist/runtime/build.js +13 -0
  83. package/dist/runtime/bundle-generator.d.ts +27 -0
  84. package/dist/runtime/bundle-generator.js +1263 -0
  85. package/dist/runtime/client-runtime.d.ts +41 -0
  86. package/dist/runtime/client-runtime.js +397 -0
  87. package/dist/runtime/dataExposure.d.ts +52 -0
  88. package/dist/runtime/dataExposure.js +227 -0
  89. package/dist/runtime/generateDOM.d.ts +21 -0
  90. package/dist/runtime/generateDOM.js +194 -0
  91. package/dist/runtime/generateHydrationBundle.d.ts +15 -0
  92. package/dist/runtime/generateHydrationBundle.js +399 -0
  93. package/dist/runtime/hydration.d.ts +53 -0
  94. package/dist/runtime/hydration.js +271 -0
  95. package/dist/runtime/navigation.d.ts +58 -0
  96. package/dist/runtime/navigation.js +372 -0
  97. package/dist/runtime/serve.d.ts +13 -0
  98. package/dist/runtime/serve.js +76 -0
  99. package/dist/runtime/thinRuntime.d.ts +23 -0
  100. package/dist/runtime/thinRuntime.js +158 -0
  101. package/dist/runtime/transformIR.d.ts +19 -0
  102. package/dist/runtime/transformIR.js +285 -0
  103. package/dist/runtime/wrapExpression.d.ts +24 -0
  104. package/dist/runtime/wrapExpression.js +76 -0
  105. package/dist/runtime/wrapExpressionWithLoop.d.ts +17 -0
  106. package/dist/runtime/wrapExpressionWithLoop.js +75 -0
  107. package/dist/spa-build.d.ts +26 -0
  108. package/dist/spa-build.js +866 -0
  109. package/dist/ssg-build.d.ts +32 -0
  110. package/dist/ssg-build.js +408 -0
  111. package/dist/test/analyze-emit.test.d.ts +1 -0
  112. package/dist/test/analyze-emit.test.js +88 -0
  113. package/dist/test/bundler-contract.test.d.ts +1 -0
  114. package/dist/test/bundler-contract.test.js +137 -0
  115. package/dist/test/compiler-authority.test.d.ts +1 -0
  116. package/dist/test/compiler-authority.test.js +90 -0
  117. package/dist/test/component-instance-test.d.ts +1 -0
  118. package/dist/test/component-instance-test.js +115 -0
  119. package/dist/test/error-native-bridge.test.d.ts +1 -0
  120. package/dist/test/error-native-bridge.test.js +51 -0
  121. package/dist/test/error-serialization.test.d.ts +1 -0
  122. package/dist/test/error-serialization.test.js +38 -0
  123. package/dist/test/macro-inlining.test.d.ts +1 -0
  124. package/dist/test/macro-inlining.test.js +178 -0
  125. package/dist/test/validate-test.d.ts +6 -0
  126. package/dist/test/validate-test.js +95 -0
  127. package/dist/transform/classifyExpression.d.ts +46 -0
  128. package/dist/transform/classifyExpression.js +354 -0
  129. package/dist/transform/componentResolver.d.ts +15 -0
  130. package/dist/transform/componentResolver.js +30 -0
  131. package/dist/transform/expressionTransformer.d.ts +19 -0
  132. package/dist/transform/expressionTransformer.js +333 -0
  133. package/dist/transform/fragmentLowering.d.ts +25 -0
  134. package/dist/transform/fragmentLowering.js +468 -0
  135. package/dist/transform/layoutProcessor.d.ts +5 -0
  136. package/dist/transform/layoutProcessor.js +34 -0
  137. package/dist/transform/transformTemplate.d.ts +11 -0
  138. package/dist/transform/transformTemplate.js +33 -0
  139. package/dist/validate/invariants.d.ts +23 -0
  140. package/dist/validate/invariants.js +55 -0
  141. package/native/compiler-native/compiler-native.node +0 -0
  142. package/native/compiler-native/index.d.ts +113 -0
  143. package/native/compiler-native/index.js +19 -0
  144. package/native/compiler-native/package.json +19 -0
  145. package/package.json +49 -0
@@ -0,0 +1,13 @@
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
+ export {};
@@ -0,0 +1,76 @@
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
+ import { serve } from "bun";
14
+ import path from "path";
15
+ const distDir = path.resolve(import.meta.dir, "..", "app", "dist");
16
+ // File extensions that should be served as static assets
17
+ const STATIC_EXTENSIONS = new Set([
18
+ ".js",
19
+ ".css",
20
+ ".ico",
21
+ ".png",
22
+ ".jpg",
23
+ ".jpeg",
24
+ ".gif",
25
+ ".svg",
26
+ ".webp",
27
+ ".woff",
28
+ ".woff2",
29
+ ".ttf",
30
+ ".eot",
31
+ ".json",
32
+ ".map"
33
+ ]);
34
+ serve({
35
+ port: 3000,
36
+ async fetch(req) {
37
+ const url = new URL(req.url);
38
+ const pathname = url.pathname;
39
+ // Get file extension
40
+ const ext = path.extname(pathname).toLowerCase();
41
+ // Check if this is a static asset request
42
+ if (STATIC_EXTENSIONS.has(ext)) {
43
+ const filePath = path.join(distDir, pathname);
44
+ const file = Bun.file(filePath);
45
+ // Check if file exists
46
+ if (await file.exists()) {
47
+ return new Response(file);
48
+ }
49
+ // Static file not found
50
+ return new Response("Not found", { status: 404 });
51
+ }
52
+ // For all other routes, serve index.html (SPA fallback)
53
+ const indexPath = path.join(distDir, "index.html");
54
+ const indexFile = Bun.file(indexPath);
55
+ if (await indexFile.exists()) {
56
+ return new Response(indexFile, {
57
+ headers: {
58
+ "Content-Type": "text/html; charset=utf-8"
59
+ }
60
+ });
61
+ }
62
+ // No index.html found - likely need to run build first
63
+ return new Response(`<html>
64
+ <head><title>Zenith - Build Required</title></head>
65
+ <body style="font-family: system-ui; padding: 2rem; text-align: center;">
66
+ <h1>Build Required</h1>
67
+ <p>Run <code>bun runtime/build.ts</code> first to compile the pages.</p>
68
+ </body>
69
+ </html>`, {
70
+ status: 500,
71
+ headers: { "Content-Type": "text/html; charset=utf-8" }
72
+ });
73
+ }
74
+ });
75
+ console.log("🚀 Zenith dev server running at http://localhost:3000");
76
+ console.log(" SPA mode: All routes serve index.html");
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Thin Runtime
3
+ *
4
+ * Phase 8/9/10: Declarative runtime for DOM updates and event binding
5
+ *
6
+ * This runtime is purely declarative - it:
7
+ * - Updates DOM nodes by ID
8
+ * - Binds event handlers
9
+ * - Reacts to state changes
10
+ * - Does NOT parse templates or expressions
11
+ * - Does NOT use eval, new Function, or with(window)
12
+ */
13
+ /**
14
+ * Generate thin declarative runtime code
15
+ *
16
+ * This runtime is minimal and safe - it only:
17
+ * 1. Updates DOM nodes using pre-compiled expression functions
18
+ * 2. Binds event handlers by ID
19
+ * 3. Provides reactive state updates
20
+ *
21
+ * All expressions are pre-compiled at build time.
22
+ */
23
+ export declare function generateThinRuntime(): string;
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Thin Runtime
3
+ *
4
+ * Phase 8/9/10: Declarative runtime for DOM updates and event binding
5
+ *
6
+ * This runtime is purely declarative - it:
7
+ * - Updates DOM nodes by ID
8
+ * - Binds event handlers
9
+ * - Reacts to state changes
10
+ * - Does NOT parse templates or expressions
11
+ * - Does NOT use eval, new Function, or with(window)
12
+ */
13
+ /**
14
+ * Generate thin declarative runtime code
15
+ *
16
+ * This runtime is minimal and safe - it only:
17
+ * 1. Updates DOM nodes using pre-compiled expression functions
18
+ * 2. Binds event handlers by ID
19
+ * 3. Provides reactive state updates
20
+ *
21
+ * All expressions are pre-compiled at build time.
22
+ */
23
+ export function generateThinRuntime() {
24
+ return `
25
+ // Zenith Thin Runtime (Phase 8/9/10)
26
+ // Purely declarative - no template parsing, no eval, no with(window)
27
+
28
+ (function() {
29
+ 'use strict';
30
+
31
+ /**
32
+ * Update a single DOM node with expression result
33
+ * Node is identified by data-zen-text or data-zen-attr-* attribute
34
+ */
35
+ function updateNode(node, expressionId, state, loaderData, props, stores) {
36
+ const expression = window.__ZENITH_EXPRESSIONS__.get(expressionId);
37
+ if (!expression) {
38
+ console.warn('[Zenith] Expression not found:', expressionId);
39
+ return;
40
+ }
41
+
42
+ try {
43
+ const result = expression(state, loaderData, props, stores);
44
+
45
+ // Update node based on attribute type
46
+ if (node.hasAttribute('data-zen-text')) {
47
+ // Text node update
48
+ if (result === null || result === undefined || result === false) {
49
+ node.textContent = '';
50
+ } else {
51
+ node.textContent = String(result);
52
+ }
53
+ } else {
54
+ // Attribute update - determine attribute name from data-zen-attr-*
55
+ const attrMatch = Array.from(node.attributes)
56
+ .find(attr => attr.name.startsWith('data-zen-attr-'));
57
+
58
+ if (attrMatch) {
59
+ const attrName = attrMatch.name.replace('data-zen-attr-', '');
60
+
61
+ if (attrName === 'class' || attrName === 'className') {
62
+ node.className = String(result ?? '');
63
+ } else if (attrName === 'style') {
64
+ if (typeof result === 'string') {
65
+ node.setAttribute('style', result);
66
+ }
67
+ } else if (attrName === 'disabled' || attrName === 'checked') {
68
+ if (result) {
69
+ node.setAttribute(attrName, '');
70
+ } else {
71
+ node.removeAttribute(attrName);
72
+ }
73
+ } else {
74
+ if (result != null && result !== false) {
75
+ node.setAttribute(attrName, String(result));
76
+ } else {
77
+ node.removeAttribute(attrName);
78
+ }
79
+ }
80
+ }
81
+ }
82
+ } catch (error) {
83
+ console.error('[Zenith] Error updating node:', expressionId, error);
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Update all hydrated nodes
89
+ * Called when state changes
90
+ */
91
+ function updateAll(state, loaderData, props, stores) {
92
+ // Find all nodes with hydration markers
93
+ const textNodes = document.querySelectorAll('[data-zen-text]');
94
+ const attrNodes = document.querySelectorAll('[data-zen-attr-class], [data-zen-attr-style], [data-zen-attr-src], [data-zen-attr-href]');
95
+
96
+ textNodes.forEach(node => {
97
+ const expressionId = node.getAttribute('data-zen-text');
98
+ if (expressionId) {
99
+ updateNode(node, expressionId, state, loaderData, props, stores);
100
+ }
101
+ });
102
+
103
+ attrNodes.forEach(node => {
104
+ const attrMatch = Array.from(node.attributes)
105
+ .find(attr => attr.name.startsWith('data-zen-attr-'));
106
+ if (attrMatch) {
107
+ const expressionId = attrMatch.value;
108
+ if (expressionId) {
109
+ updateNode(node, expressionId, state, loaderData, props, stores);
110
+ }
111
+ }
112
+ });
113
+ }
114
+
115
+ /**
116
+ * Bind event handlers
117
+ * Handlers are pre-compiled and registered on window
118
+ */
119
+ function bindEvents(container) {
120
+ container = container || document;
121
+
122
+ const eventTypes = ['click', 'change', 'input', 'submit', 'focus', 'blur', 'keyup', 'keydown', 'mouseenter'];
123
+
124
+ eventTypes.forEach(eventType => {
125
+ const elements = container.querySelectorAll('[data-zen-' + eventType + ']');
126
+ elements.forEach(element => {
127
+ const handlerName = element.getAttribute('data-zen-' + eventType);
128
+ if (!handlerName) return;
129
+
130
+ // Remove existing handler
131
+ const handlerKey = '__zen_' + eventType + '_handler';
132
+ const existingHandler = element[handlerKey];
133
+ if (existingHandler) {
134
+ element.removeEventListener(eventType, existingHandler);
135
+ }
136
+
137
+ // Bind new handler (pre-compiled, registered on window)
138
+ const handler = function(event) {
139
+ const handlerFunc = window[handlerName];
140
+ if (typeof handlerFunc === 'function') {
141
+ handlerFunc(event, element);
142
+ }
143
+ };
144
+
145
+ element[handlerKey] = handler;
146
+ element.addEventListener(eventType, handler);
147
+ });
148
+ });
149
+ }
150
+
151
+ // Export to window
152
+ if (typeof window !== 'undefined') {
153
+ window.__zenith_updateAll = updateAll;
154
+ window.__zenith_bindEvents = bindEvents;
155
+ }
156
+ })();
157
+ `;
158
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Transform IR to Runtime Code
3
+ *
4
+ * Phase 4: Transform ZenIR into runtime-ready JavaScript code with full reactivity
5
+ */
6
+ import type { ZenIR } from '../ir/types';
7
+ export interface RuntimeCode {
8
+ expressions: string;
9
+ render: string;
10
+ hydration: string;
11
+ styles: string;
12
+ script: string;
13
+ stateInit: string;
14
+ bundle: string;
15
+ }
16
+ /**
17
+ * Transform ZenIR into runtime JavaScript code
18
+ */
19
+ export declare function transformIR(ir: ZenIR): RuntimeCode;
@@ -0,0 +1,285 @@
1
+ /**
2
+ * Transform IR to Runtime Code
3
+ *
4
+ * Phase 4: Transform ZenIR into runtime-ready JavaScript code with full reactivity
5
+ */
6
+ import { generateExpressionWrappers } from './wrapExpression';
7
+ import { generateDOMFunction } from './generateDOM';
8
+ import { generateHydrationRuntime, generateExpressionRegistry } from './generateHydrationBundle';
9
+ import { analyzeAllExpressions } from './dataExposure';
10
+ import { generateNavigationRuntime } from './navigation';
11
+ import { extractStateDeclarations, extractProps, transformStateDeclarations } from '../parse/scriptAnalysis';
12
+ /**
13
+ * Transform ZenIR into runtime JavaScript code
14
+ */
15
+ export function transformIR(ir) {
16
+ // Phase 6: Analyze expression dependencies for explicit data exposure
17
+ const expressionDependencies = analyzeAllExpressions(ir.template.expressions, ir.filePath, [], // declaredLoaderProps
18
+ ir.script?.attributes['props'] ? ir.script.attributes['props'].split(',') : [], // declaredProps
19
+ [] // declaredStores
20
+ );
21
+ // Generate expression wrappers with dependencies
22
+ const expressions = generateExpressionWrappers(ir.template.expressions, expressionDependencies);
23
+ // Generate DOM creation code
24
+ const renderFunction = generateDOMFunction(ir.template.nodes, ir.template.expressions, 'renderDynamicPage');
25
+ // Generate hydrate function (legacy, for reference)
26
+ const hydrateFunction = generateHydrateFunction();
27
+ // Generate Phase 5 hydration runtime
28
+ const hydrationRuntime = generateHydrationRuntime();
29
+ // Generate Phase 7 navigation runtime
30
+ const navigationRuntime = generateNavigationRuntime();
31
+ // Generate expression registry initialization
32
+ const expressionRegistry = generateExpressionRegistry(ir.template.expressions);
33
+ // Generate style injection code
34
+ const stylesCode = generateStyleInjection(ir.styles);
35
+ // Extract state and prop declarations
36
+ const scriptContent = ir.script?.raw || '';
37
+ const stateDeclarations = extractStateDeclarations(scriptContent);
38
+ const propKeys = Object.keys(ir.script?.attributes || {}).filter(k => k !== 'setup' && k !== 'lang');
39
+ const propDeclarations = extractProps(scriptContent);
40
+ const stateInitCode = generateStateInitialization(stateDeclarations, [...propDeclarations, ...propKeys]);
41
+ // Transform script (remove state and prop declarations, they're handled by runtime)
42
+ const scriptCode = transformStateDeclarations(scriptContent);
43
+ // Generate complete runtime bundle
44
+ const bundle = generateRuntimeBundle({
45
+ expressions,
46
+ expressionRegistry,
47
+ hydrationRuntime,
48
+ navigationRuntime,
49
+ stylesCode,
50
+ scriptCode,
51
+ stateInitCode
52
+ });
53
+ return {
54
+ expressions,
55
+ render: renderFunction,
56
+ hydration: hydrationRuntime,
57
+ styles: stylesCode,
58
+ script: scriptCode,
59
+ stateInit: stateInitCode,
60
+ bundle
61
+ };
62
+ }
63
+ /**
64
+ * Generate complete runtime bundle
65
+ */
66
+ function generateRuntimeBundle(parts) {
67
+ // Extract function declarations from script code to register on window
68
+ const functionRegistrations = extractFunctionRegistrations(parts.scriptCode);
69
+ return `// Zenith Runtime Bundle (Phase 5)
70
+ // Generated at compile time - no .zen parsing in browser
71
+
72
+ ${parts.expressions}
73
+
74
+ ${parts.expressionRegistry}
75
+
76
+ ${parts.hydrationRuntime}
77
+
78
+ ${parts.navigationRuntime}
79
+
80
+ ${parts.stylesCode ? `// Style injection
81
+ ${parts.stylesCode}` : ''}
82
+
83
+ // User script code - executed first to define variables needed by state initialization
84
+ ${parts.scriptCode ? parts.scriptCode : ''}
85
+
86
+ ${functionRegistrations}
87
+
88
+ ${parts.stateInitCode ? `// State initialization
89
+ ${parts.stateInitCode}` : ''}
90
+
91
+ // Export hydration functions
92
+ if (typeof window !== 'undefined') {
93
+ window.zenithHydrate = window.__zenith_hydrate || function(state, container) {
94
+ console.warn('[Zenith] Hydration runtime not loaded');
95
+ };
96
+ window.zenithUpdate = window.__zenith_update || function(state) {
97
+ console.warn('[Zenith] Update runtime not loaded');
98
+ };
99
+ window.zenithBindEvents = window.__zenith_bindEvents || function(container) {
100
+ console.warn('[Zenith] Event binding runtime not loaded');
101
+ };
102
+ window.zenithCleanup = window.__zenith_cleanup || function(container) {
103
+ console.warn('[Zenith] Cleanup runtime not loaded');
104
+ };
105
+ }
106
+
107
+ // Auto-hydrate on page mount
108
+ (function() {
109
+ 'use strict';
110
+
111
+ function autoHydrate() {
112
+ // Initialize state object
113
+ const state = window.__ZENITH_STATE__ || {};
114
+
115
+ // Run state initialization if defined
116
+ if (typeof initializeState === 'function') {
117
+ initializeState(state);
118
+ }
119
+
120
+ // Store state globally
121
+ window.__ZENITH_STATE__ = state;
122
+
123
+ // Expose state variables on window with reactive getters/setters
124
+ // This allows user functions (like increment) to access state variables directly
125
+ for (const key in state) {
126
+ if (state.hasOwnProperty(key) && !window.hasOwnProperty(key)) {
127
+ Object.defineProperty(window, key, {
128
+ get: function() { return window.__ZENITH_STATE__[key]; },
129
+ set: function(value) {
130
+ window.__ZENITH_STATE__[key] = value;
131
+ // Trigger reactive update
132
+ if (window.__zenith_update) {
133
+ window.__zenith_update(window.__ZENITH_STATE__);
134
+ }
135
+ },
136
+ configurable: true
137
+ });
138
+ }
139
+ }
140
+
141
+ // Inject styles if defined
142
+ if (typeof injectStyles === 'function') {
143
+ injectStyles();
144
+ }
145
+
146
+ // Get the router outlet or body
147
+ const container = document.querySelector('#app') || document.body;
148
+
149
+ // Hydrate with state
150
+ if (window.__zenith_hydrate) {
151
+ window.__zenith_hydrate(state, {}, {}, {}, container);
152
+ }
153
+ }
154
+
155
+ // Run on DOM ready
156
+ if (document.readyState === 'loading') {
157
+ document.addEventListener('DOMContentLoaded', autoHydrate);
158
+ } else {
159
+ // DOM already loaded, hydrate immediately
160
+ autoHydrate();
161
+ }
162
+ })();
163
+ `;
164
+ }
165
+ /**
166
+ * Extract function declarations and generate window registration code
167
+ */
168
+ function extractFunctionRegistrations(scriptCode) {
169
+ if (!scriptCode)
170
+ return '';
171
+ // Match function declarations: function name(...) { ... }
172
+ const functionPattern = /function\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\(/g;
173
+ const functionNames = [];
174
+ let match;
175
+ while ((match = functionPattern.exec(scriptCode)) !== null) {
176
+ if (match[1]) {
177
+ functionNames.push(match[1]);
178
+ }
179
+ }
180
+ if (functionNames.length === 0) {
181
+ return '';
182
+ }
183
+ // Generate window registration for each function
184
+ const registrations = functionNames.map(name => ` if (typeof ${name} === 'function') window.${name} = ${name};`).join('\n');
185
+ return `// Register functions on window for event handlers\n${registrations}`;
186
+ }
187
+ /**
188
+ * Generate hydrate function that mounts the DOM with reactivity
189
+ */
190
+ function generateHydrateFunction() {
191
+ return `function hydrate(root, state) {
192
+ if (!root) {
193
+ // SSR fallback - return initial HTML string
194
+ console.warn('[Zenith] hydrate called without root element - SSR mode');
195
+ return '';
196
+ }
197
+
198
+ // Clear root
199
+ root.innerHTML = '';
200
+
201
+ // Render template
202
+ const dom = renderDynamicPage(state);
203
+
204
+ // Append to root
205
+ if (dom instanceof DocumentFragment) {
206
+ root.appendChild(dom);
207
+ } else if (dom instanceof Node) {
208
+ root.appendChild(dom);
209
+ }
210
+
211
+ // Bind event handlers
212
+ bindEventHandlers(root, state);
213
+
214
+ // Set up reactive updates (if state is reactive)
215
+ setupReactiveUpdates(root, state);
216
+
217
+ return root;
218
+ }
219
+
220
+ function bindEventHandlers(root, state) {
221
+ // Find all elements with data-zen-* event attributes
222
+ const eventTypes = ['click', 'change', 'input', 'submit', 'focus', 'blur'];
223
+
224
+ for (const eventType of eventTypes) {
225
+ const elements = root.querySelectorAll(\`[data-zen-\${eventType}]\`);
226
+ for (const el of elements) {
227
+ const handlerName = el.getAttribute(\`data-zen-\${eventType}\`);
228
+ if (handlerName && typeof window[handlerName] === 'function') {
229
+ el.addEventListener(eventType, (e) => {
230
+ window[handlerName](e, el);
231
+ });
232
+ }
233
+ }
234
+ }
235
+ }
236
+
237
+ function setupReactiveUpdates(root, state) {
238
+ // For now, reactive updates are handled by the existing binding system
239
+ // This is a placeholder for future reactive DOM updates
240
+ // The existing runtime handles reactivity via state property setters
241
+ }`;
242
+ }
243
+ /**
244
+ * Generate style injection code
245
+ */
246
+ function generateStyleInjection(styles) {
247
+ if (styles.length === 0) {
248
+ return '';
249
+ }
250
+ const styleBlocks = styles.map((style, index) => {
251
+ const escapedStyle = style.raw.replace(/`/g, '\\`').replace(/\$/g, '\\$');
252
+ return `
253
+ const style${index} = document.createElement('style');
254
+ style${index}.textContent = \`${escapedStyle}\`;
255
+ document.head.appendChild(style${index});`;
256
+ }).join('');
257
+ return `function injectStyles() {${styleBlocks}
258
+ }`;
259
+ }
260
+ /**
261
+ * Generate state initialization code
262
+ * In Phase 9: Also handles props passing
263
+ */
264
+ function generateStateInitialization(stateDeclarations, propDeclarations) {
265
+ const stateInit = Array.from(stateDeclarations.entries()).map(([name, value]) => {
266
+ return `
267
+ // Initialize state: ${name}
268
+ if (typeof state.${name} === 'undefined') {
269
+ state.${name} = ${value};
270
+ }`;
271
+ }).join('');
272
+ const legacyPropInit = propDeclarations.includes('props') ? `
273
+ // Initialize props object (legacy)
274
+ if (typeof window.__ZEN_PROPS__ !== 'undefined') {
275
+ state.props = window.__ZEN_PROPS__;
276
+ }` : '';
277
+ const individualPropInit = propDeclarations.filter(p => p !== 'props').map(prop => `
278
+ // Initialize prop: ${prop}
279
+ if (typeof state.${prop} === 'undefined' && typeof window.__ZEN_PROPS__ !== 'undefined' && typeof window.__ZEN_PROPS__.${prop} !== 'undefined') {
280
+ state.${prop} = window.__ZEN_PROPS__.${prop};
281
+ }`).join('');
282
+ return `function initializeState(state) {${stateInit}${legacyPropInit}${individualPropInit}
283
+ }`;
284
+ }
285
+ // Note: transformScript is now handled by transformStateDeclarations in legacy/parse.ts
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Expression Wrapper
3
+ *
4
+ * Wraps extracted expressions into runtime functions with explicit data arguments
5
+ *
6
+ * Phase 6: Expressions now accept explicit loaderData, props, stores arguments
7
+ * instead of relying on implicit globals
8
+ */
9
+ import type { ExpressionIR, LoopContext } from '../ir/types';
10
+ import type { ExpressionDataDependencies } from './dataExposure';
11
+ /**
12
+ * Wrap an expression into a runtime function with explicit data arguments
13
+ *
14
+ * Phase 6: Supports explicit loaderData, props, stores arguments
15
+ * Phase 7: Supports loop context for expressions inside map iterations
16
+ */
17
+ export declare function wrapExpression(expr: ExpressionIR, dependencies?: ExpressionDataDependencies, loopContext?: LoopContext): string;
18
+ /**
19
+ * Generate all expression wrappers for a set of expressions
20
+ *
21
+ * Phase 6: Accepts dependencies array for explicit data exposure
22
+ * Phase 7: Accepts loop contexts for expressions inside map iterations
23
+ */
24
+ export declare function generateExpressionWrappers(expressions: ExpressionIR[], dependencies?: ExpressionDataDependencies[], loopContexts?: (LoopContext | undefined)[]): string;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Expression Wrapper
3
+ *
4
+ * Wraps extracted expressions into runtime functions with explicit data arguments
5
+ *
6
+ * Phase 6: Expressions now accept explicit loaderData, props, stores arguments
7
+ * instead of relying on implicit globals
8
+ */
9
+ import { generateExplicitExpressionWrapper } from './dataExposure';
10
+ import { wrapExpressionWithLoopContext } from './wrapExpressionWithLoop';
11
+ import { transformExpressionJSX } from '../transform/expressionTransformer';
12
+ /**
13
+ * Wrap an expression into a runtime function with explicit data arguments
14
+ *
15
+ * Phase 6: Supports explicit loaderData, props, stores arguments
16
+ * Phase 7: Supports loop context for expressions inside map iterations
17
+ */
18
+ export function wrapExpression(expr, dependencies, loopContext // Phase 7: Loop context for map expressions
19
+ ) {
20
+ const { id, code } = expr;
21
+ // Phase 7: If loop context is provided, use loop-aware wrapper
22
+ if (loopContext && loopContext.variables.length > 0) {
23
+ return wrapExpressionWithLoopContext(expr, loopContext, dependencies);
24
+ }
25
+ // If dependencies are provided, use explicit wrapper (Phase 6)
26
+ if (dependencies) {
27
+ return generateExplicitExpressionWrapper(expr, dependencies);
28
+ }
29
+ // Fallback to legacy wrapper (backwards compatibility)
30
+ // Transform JSX-like tags inside expression code
31
+ const transformedCode = transformExpressionJSX(code);
32
+ // Escape the code for use in a single-line comment (replace newlines with spaces)
33
+ const commentCode = code.replace(/[\r\n]+/g, ' ').replace(/\s+/g, ' ').substring(0, 100);
34
+ const jsonEscapedCode = JSON.stringify(code);
35
+ return `
36
+ // Expression: ${commentCode}${code.length > 100 ? '...' : ''}
37
+ const ${id} = (state) => {
38
+ try {
39
+ // Expose zenith helpers for JSX and content
40
+ const __zenith = window.__zenith || {};
41
+ const zenCollection = __zenith.zenCollection || ((name) => ({ get: () => [] }));
42
+
43
+ with (state) {
44
+ return ${transformedCode};
45
+ }
46
+ } catch (e) {
47
+ console.warn('[Zenith] Expression evaluation error:', ${jsonEscapedCode}, e);
48
+ return undefined;
49
+ }
50
+ };`;
51
+ }
52
+ /**
53
+ * Generate all expression wrappers for a set of expressions
54
+ *
55
+ * Phase 6: Accepts dependencies array for explicit data exposure
56
+ * Phase 7: Accepts loop contexts for expressions inside map iterations
57
+ */
58
+ export function generateExpressionWrappers(expressions, dependencies, loopContexts // Phase 7: Loop contexts for each expression
59
+ ) {
60
+ if (expressions.length === 0) {
61
+ return '';
62
+ }
63
+ if (dependencies && dependencies.length === expressions.length) {
64
+ // Use explicit wrappers with dependencies and optional loop contexts
65
+ return expressions
66
+ .map((expr, index) => {
67
+ const loopCtx = loopContexts && loopContexts[index] !== undefined
68
+ ? loopContexts[index]
69
+ : undefined;
70
+ return wrapExpression(expr, dependencies[index], loopCtx);
71
+ })
72
+ .join('\n');
73
+ }
74
+ // Fallback to legacy wrappers (no dependencies, no loop contexts)
75
+ return expressions.map(expr => wrapExpression(expr)).join('\n');
76
+ }