@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,468 @@
1
+ /**
2
+ * Fragment Lowering
3
+ *
4
+ * Transforms JSX-returning expressions into structural fragment nodes.
5
+ *
6
+ * This phase runs AFTER parsing, BEFORE component resolution.
7
+ * Transforms ExpressionNode → ConditionalFragmentNode | OptionalFragmentNode | LoopFragmentNode
8
+ *
9
+ * IMPORTANT: JSX in Zenith is compile-time sugar only.
10
+ * The compiler enumerates all possible DOM shapes and lowers them at compile time.
11
+ * Runtime never creates DOM — it only toggles visibility and binds values.
12
+ */
13
+ import { classifyExpression } from './classifyExpression';
14
+ import { InvariantError } from '../errors/compilerError';
15
+ import { INVARIANT } from '../validate/invariants';
16
+ /**
17
+ * Lower JSX-returning expressions into structural fragments
18
+ *
19
+ * Walks the node tree and transforms ExpressionNode instances
20
+ * that return JSX into the appropriate fragment node types.
21
+ *
22
+ * @param nodes - Template nodes to process
23
+ * @param filePath - Source file path for error reporting
24
+ * @param expressions - Expression registry (mutated to add new expressions)
25
+ * @returns Lowered nodes with fragment bindings
26
+ */
27
+ export function lowerFragments(nodes, filePath, expressions) {
28
+ return nodes.map(node => lowerNode(node, filePath, expressions));
29
+ }
30
+ /**
31
+ * Lower a single node
32
+ */
33
+ function lowerNode(node, filePath, expressions) {
34
+ switch (node.type) {
35
+ case 'expression':
36
+ return lowerExpressionNode(node, filePath, expressions);
37
+ case 'element':
38
+ return {
39
+ ...node,
40
+ children: lowerFragments(node.children, filePath, expressions)
41
+ };
42
+ case 'component':
43
+ return {
44
+ ...node,
45
+ children: lowerFragments(node.children, filePath, expressions)
46
+ };
47
+ case 'conditional-fragment':
48
+ return {
49
+ ...node,
50
+ consequent: lowerFragments(node.consequent, filePath, expressions),
51
+ alternate: lowerFragments(node.alternate, filePath, expressions)
52
+ };
53
+ case 'optional-fragment':
54
+ return {
55
+ ...node,
56
+ fragment: lowerFragments(node.fragment, filePath, expressions)
57
+ };
58
+ case 'loop-fragment':
59
+ return {
60
+ ...node,
61
+ body: lowerFragments(node.body, filePath, expressions)
62
+ };
63
+ case 'text':
64
+ default:
65
+ return node;
66
+ }
67
+ }
68
+ /**
69
+ * Lower an expression node to a fragment if it returns JSX
70
+ */
71
+ function lowerExpressionNode(node, filePath, expressions) {
72
+ const classification = classifyExpression(node.expression);
73
+ // Primitive expressions pass through unchanged
74
+ if (classification.type === 'primitive') {
75
+ return node;
76
+ }
77
+ // Unknown expressions with JSX are compile errors
78
+ if (classification.type === 'unknown') {
79
+ throw new InvariantError(INVARIANT.NON_ENUMERABLE_JSX, `JSX expression output cannot be statically determined: ${node.expression.slice(0, 50)}...`, 'JSX expressions must have statically enumerable output. The compiler must know all possible DOM shapes at compile time.', filePath, node.location.line, node.location.column);
80
+ }
81
+ // Lower based on classification type
82
+ switch (classification.type) {
83
+ case 'conditional':
84
+ return lowerConditionalExpression(node, classification.condition, classification.consequent, classification.alternate, filePath, expressions);
85
+ case 'optional':
86
+ return lowerOptionalExpression(node, classification.optionalCondition, classification.optionalFragment, filePath, expressions);
87
+ case 'loop':
88
+ return lowerLoopExpression(node, classification.loopSource, classification.loopItemVar, classification.loopIndexVar, classification.loopBody, filePath, expressions);
89
+ case 'fragment':
90
+ return lowerInlineFragment(node, classification.fragmentCode, filePath, expressions);
91
+ default:
92
+ // Should not reach here
93
+ return node;
94
+ }
95
+ }
96
+ /**
97
+ * Lower conditional expression: condition ? <A /> : <B />
98
+ *
99
+ * Both branches are parsed and compiled at compile time.
100
+ */
101
+ function lowerConditionalExpression(node, condition, consequentCode, alternateCode, filePath, expressions) {
102
+ // Parse both branches as JSX fragments
103
+ const consequent = parseJSXToNodes(consequentCode, node.location, filePath, expressions, node.loopContext);
104
+ const alternate = parseJSXToNodes(alternateCode, node.location, filePath, expressions, node.loopContext);
105
+ return {
106
+ type: 'conditional-fragment',
107
+ condition,
108
+ consequent,
109
+ alternate,
110
+ location: node.location,
111
+ loopContext: node.loopContext
112
+ };
113
+ }
114
+ /**
115
+ * Lower optional expression: condition && <A />
116
+ *
117
+ * Fragment is parsed and compiled at compile time.
118
+ */
119
+ function lowerOptionalExpression(node, condition, fragmentCode, filePath, expressions) {
120
+ const fragment = parseJSXToNodes(fragmentCode, node.location, filePath, expressions, node.loopContext);
121
+ return {
122
+ type: 'optional-fragment',
123
+ condition,
124
+ fragment,
125
+ location: node.location,
126
+ loopContext: node.loopContext
127
+ };
128
+ }
129
+ /**
130
+ * Lower loop expression: items.map(item => <li>...</li>)
131
+ *
132
+ * Body is parsed and compiled once, instantiated per item at runtime.
133
+ */
134
+ function lowerLoopExpression(node, source, itemVar, indexVar, bodyCode, filePath, expressions) {
135
+ // Create loop context for the body
136
+ const loopVariables = [itemVar];
137
+ if (indexVar) {
138
+ loopVariables.push(indexVar);
139
+ }
140
+ const bodyLoopContext = {
141
+ variables: node.loopContext
142
+ ? [...node.loopContext.variables, ...loopVariables]
143
+ : loopVariables,
144
+ mapSource: source
145
+ };
146
+ // Parse body with loop context
147
+ const body = parseJSXToNodes(bodyCode, node.location, filePath, expressions, bodyLoopContext);
148
+ return {
149
+ type: 'loop-fragment',
150
+ source,
151
+ itemVar,
152
+ indexVar,
153
+ body,
154
+ location: node.location,
155
+ loopContext: bodyLoopContext
156
+ };
157
+ }
158
+ /**
159
+ * Lower inline fragment: <A /> or <><A /><B /></>
160
+ *
161
+ * JSX is parsed and inlined directly into the node tree.
162
+ * Returns the original expression node since inline JSX
163
+ * is already handled by the expression transformer.
164
+ */
165
+ function lowerInlineFragment(node, fragmentCode, filePath, expressions) {
166
+ // For now, inline fragments are handled by the existing expression transformer
167
+ // which converts JSX to __zenith.h() calls
168
+ // In a future iteration, we could parse them to static nodes here
169
+ return node;
170
+ }
171
+ /**
172
+ * Parse JSX code string into TemplateNode[]
173
+ *
174
+ * This is a simplified parser for JSX fragments within expressions.
175
+ * It handles basic JSX structure for lowering purposes.
176
+ */
177
+ function parseJSXToNodes(code, baseLocation, filePath, expressions, loopContext) {
178
+ const trimmed = code.trim();
179
+ // Handle fragment syntax <>...</>
180
+ if (trimmed.startsWith('<>')) {
181
+ const content = extractFragmentContent(trimmed);
182
+ return parseJSXChildren(content, baseLocation, filePath, expressions, loopContext);
183
+ }
184
+ // Handle single element
185
+ if (trimmed.startsWith('<')) {
186
+ const element = parseJSXElement(trimmed, baseLocation, filePath, expressions, loopContext);
187
+ return element ? [element] : [];
188
+ }
189
+ // Handle parenthesized expression
190
+ if (trimmed.startsWith('(')) {
191
+ const inner = trimmed.slice(1, -1).trim();
192
+ return parseJSXToNodes(inner, baseLocation, filePath, expressions, loopContext);
193
+ }
194
+ // Not JSX - return as expression node
195
+ return [{
196
+ type: 'expression',
197
+ expression: trimmed,
198
+ location: baseLocation,
199
+ loopContext
200
+ }];
201
+ }
202
+ /**
203
+ * Extract content from fragment syntax <>content</>
204
+ */
205
+ function extractFragmentContent(code) {
206
+ // Remove <> prefix and </> suffix
207
+ const withoutOpen = code.slice(2);
208
+ const closeIndex = withoutOpen.lastIndexOf('</>');
209
+ if (closeIndex === -1) {
210
+ return withoutOpen;
211
+ }
212
+ return withoutOpen.slice(0, closeIndex);
213
+ }
214
+ /**
215
+ * Parse JSX children content
216
+ */
217
+ function parseJSXChildren(content, baseLocation, filePath, expressions, loopContext) {
218
+ const nodes = [];
219
+ let i = 0;
220
+ let currentText = '';
221
+ while (i < content.length) {
222
+ const char = content[i];
223
+ // Check for JSX element
224
+ if (char === '<' && /[a-zA-Z]/.test(content[i + 1] || '')) {
225
+ // Save accumulated text
226
+ if (currentText.trim()) {
227
+ nodes.push({
228
+ type: 'text',
229
+ value: currentText.trim(),
230
+ location: baseLocation
231
+ });
232
+ currentText = '';
233
+ }
234
+ // Parse element
235
+ const result = parseJSXElementWithEnd(content, i, baseLocation, filePath, expressions, loopContext);
236
+ if (result) {
237
+ nodes.push(result.node);
238
+ i = result.endIndex;
239
+ continue;
240
+ }
241
+ }
242
+ // Check for expression {expr}
243
+ if (char === '{') {
244
+ const endBrace = findBalancedBraceEnd(content, i);
245
+ if (endBrace !== -1) {
246
+ // Save accumulated text
247
+ if (currentText.trim()) {
248
+ nodes.push({
249
+ type: 'text',
250
+ value: currentText.trim(),
251
+ location: baseLocation
252
+ });
253
+ currentText = '';
254
+ }
255
+ const exprCode = content.slice(i + 1, endBrace - 1).trim();
256
+ if (exprCode) {
257
+ nodes.push({
258
+ type: 'expression',
259
+ expression: exprCode,
260
+ location: baseLocation,
261
+ loopContext
262
+ });
263
+ }
264
+ i = endBrace;
265
+ continue;
266
+ }
267
+ }
268
+ currentText += char;
269
+ i++;
270
+ }
271
+ // Add remaining text
272
+ if (currentText.trim()) {
273
+ nodes.push({
274
+ type: 'text',
275
+ value: currentText.trim(),
276
+ location: baseLocation
277
+ });
278
+ }
279
+ return nodes;
280
+ }
281
+ /**
282
+ * Parse a single JSX element
283
+ */
284
+ function parseJSXElement(code, baseLocation, filePath, expressions, loopContext) {
285
+ const result = parseJSXElementWithEnd(code, 0, baseLocation, filePath, expressions, loopContext);
286
+ return result ? result.node : null;
287
+ }
288
+ /**
289
+ * Parse JSX element and return end index
290
+ */
291
+ function parseJSXElementWithEnd(code, startIndex, baseLocation, filePath, expressions, loopContext) {
292
+ // Extract tag name
293
+ const tagMatch = code.slice(startIndex).match(/^<([a-zA-Z][a-zA-Z0-9.]*)/);
294
+ if (!tagMatch)
295
+ return null;
296
+ const tagName = tagMatch[1];
297
+ let i = startIndex + tagMatch[0].length;
298
+ // Parse attributes (simplified)
299
+ const attributes = [];
300
+ // Skip whitespace and parse attributes until > or />
301
+ while (i < code.length) {
302
+ // Skip whitespace
303
+ while (i < code.length && /\s/.test(code[i]))
304
+ i++;
305
+ // Check for end of opening tag
306
+ if (code[i] === '>') {
307
+ i++;
308
+ break;
309
+ }
310
+ if (code[i] === '/' && code[i + 1] === '>') {
311
+ // Self-closing tag
312
+ const isComponent = tagName[0] === tagName[0].toUpperCase();
313
+ const node = isComponent ? {
314
+ type: 'component',
315
+ name: tagName,
316
+ attributes: attributes.map(a => ({ ...a, value: a.value })),
317
+ children: [],
318
+ location: baseLocation,
319
+ loopContext
320
+ } : {
321
+ type: 'element',
322
+ tag: tagName.toLowerCase(),
323
+ attributes: attributes.map(a => ({ ...a, value: a.value })),
324
+ children: [],
325
+ location: baseLocation,
326
+ loopContext
327
+ };
328
+ return { node, endIndex: i + 2 };
329
+ }
330
+ // Parse attribute name
331
+ const attrMatch = code.slice(i).match(/^([a-zA-Z_][a-zA-Z0-9_-]*)/);
332
+ if (!attrMatch) {
333
+ i++;
334
+ continue;
335
+ }
336
+ const attrName = attrMatch[1];
337
+ i += attrName.length;
338
+ // Skip whitespace
339
+ while (i < code.length && /\s/.test(code[i]))
340
+ i++;
341
+ // Check for value
342
+ if (code[i] !== '=') {
343
+ attributes.push({ name: attrName, value: 'true', location: baseLocation });
344
+ continue;
345
+ }
346
+ i++; // Skip =
347
+ // Skip whitespace
348
+ while (i < code.length && /\s/.test(code[i]))
349
+ i++;
350
+ // Parse value
351
+ if (code[i] === '"' || code[i] === "'") {
352
+ const quote = code[i];
353
+ let endQuote = i + 1;
354
+ while (endQuote < code.length && code[endQuote] !== quote) {
355
+ if (code[endQuote] === '\\')
356
+ endQuote++;
357
+ endQuote++;
358
+ }
359
+ attributes.push({ name: attrName, value: code.slice(i + 1, endQuote), location: baseLocation });
360
+ i = endQuote + 1;
361
+ }
362
+ else if (code[i] === '{') {
363
+ const endBrace = findBalancedBraceEnd(code, i);
364
+ if (endBrace !== -1) {
365
+ attributes.push({ name: attrName, value: code.slice(i, endBrace), location: baseLocation });
366
+ i = endBrace;
367
+ }
368
+ }
369
+ }
370
+ // Parse children until closing tag
371
+ const closeTag = `</${tagName}>`;
372
+ const closeIndex = findClosingTag(code, i, tagName);
373
+ let children = [];
374
+ if (closeIndex !== -1 && closeIndex > i) {
375
+ const childContent = code.slice(i, closeIndex);
376
+ children = parseJSXChildren(childContent, baseLocation, filePath, expressions, loopContext);
377
+ i = closeIndex + closeTag.length;
378
+ }
379
+ const isComponent = tagName[0] === tagName[0].toUpperCase();
380
+ const node = isComponent ? {
381
+ type: 'component',
382
+ name: tagName,
383
+ attributes: attributes.map(a => ({ ...a, value: a.value })),
384
+ children,
385
+ location: baseLocation,
386
+ loopContext
387
+ } : {
388
+ type: 'element',
389
+ tag: tagName.toLowerCase(),
390
+ attributes: attributes.map(a => ({ ...a, value: a.value })),
391
+ children,
392
+ location: baseLocation,
393
+ loopContext
394
+ };
395
+ return { node, endIndex: i };
396
+ }
397
+ /**
398
+ * Find closing tag for an element
399
+ */
400
+ function findClosingTag(code, startIndex, tagName) {
401
+ const closeTag = `</${tagName}>`;
402
+ let depth = 1;
403
+ let i = startIndex;
404
+ while (i < code.length && depth > 0) {
405
+ // Check for closing tag
406
+ if (code.slice(i, i + closeTag.length) === closeTag) {
407
+ depth--;
408
+ if (depth === 0)
409
+ return i;
410
+ i += closeTag.length;
411
+ continue;
412
+ }
413
+ // Check for opening tag (same name, nested)
414
+ const openPattern = new RegExp(`^<${tagName}(?:\\s|>|/>)`);
415
+ const match = code.slice(i).match(openPattern);
416
+ if (match) {
417
+ // Check if self-closing
418
+ const selfClosing = code.slice(i).match(new RegExp(`^<${tagName}[^>]*/>`));
419
+ if (!selfClosing) {
420
+ depth++;
421
+ }
422
+ i += match[0].length;
423
+ continue;
424
+ }
425
+ i++;
426
+ }
427
+ return -1;
428
+ }
429
+ /**
430
+ * Find balanced brace end
431
+ */
432
+ function findBalancedBraceEnd(code, startIndex) {
433
+ if (code[startIndex] !== '{')
434
+ return -1;
435
+ let depth = 1;
436
+ let i = startIndex + 1;
437
+ let inString = false;
438
+ let stringChar = '';
439
+ while (i < code.length && depth > 0) {
440
+ const char = code[i];
441
+ const prevChar = code[i - 1];
442
+ // Handle escape
443
+ if (prevChar === '\\') {
444
+ i++;
445
+ continue;
446
+ }
447
+ // Handle strings
448
+ if (!inString && (char === '"' || char === "'")) {
449
+ inString = true;
450
+ stringChar = char;
451
+ i++;
452
+ continue;
453
+ }
454
+ if (inString && char === stringChar) {
455
+ inString = false;
456
+ i++;
457
+ continue;
458
+ }
459
+ if (!inString) {
460
+ if (char === '{')
461
+ depth++;
462
+ else if (char === '}')
463
+ depth--;
464
+ }
465
+ i++;
466
+ }
467
+ return depth === 0 ? i : -1;
468
+ }
@@ -0,0 +1,5 @@
1
+ import type { LayoutMetadata } from '../discovery/layouts';
2
+ /**
3
+ * Process a page by inlining a layout
4
+ */
5
+ export declare function processLayout(source: string, layout: LayoutMetadata, props?: Record<string, any>): string;
@@ -0,0 +1,34 @@
1
+ let native;
2
+ try {
3
+ try {
4
+ native = require('../../native/compiler-native');
5
+ }
6
+ catch {
7
+ native = require('../../native/compiler-native/index.js');
8
+ }
9
+ }
10
+ catch (e) {
11
+ // Bridge load handled elsewhere
12
+ }
13
+ /**
14
+ * Process a page by inlining a layout
15
+ */
16
+ export function processLayout(source, layout, props = {}) {
17
+ if (native && native.process_layout_native) {
18
+ try {
19
+ // Convert Map to record for native serialization
20
+ const layoutForNative = {
21
+ name: layout.name,
22
+ html: layout.html,
23
+ scripts: layout.scripts,
24
+ styles: layout.styles
25
+ };
26
+ return native.process_layout_native(source, JSON.stringify(layoutForNative), JSON.stringify(props));
27
+ }
28
+ catch (error) {
29
+ console.warn(`[Zenith Native] Layout processing failed: ${error.message}`);
30
+ }
31
+ }
32
+ // Fallback: This should ideally not be reached if native is available
33
+ return source;
34
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Transform Template IR to Compiled Template
3
+ *
4
+ * Phase 2: Transform IR → Static HTML + Runtime Bindings
5
+ */
6
+ import type { ZenIR } from '../ir/types';
7
+ import type { CompiledTemplate } from '../output/types';
8
+ /**
9
+ * Transform a ZenIR into CompiledTemplate
10
+ */
11
+ export declare function transformTemplate(ir: ZenIR): CompiledTemplate;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Transform Template IR to Compiled Template
3
+ *
4
+ * Phase 2: Transform IR → Static HTML + Runtime Bindings
5
+ */
6
+ /**
7
+ * Transform a ZenIR into CompiledTemplate
8
+ */
9
+ export function transformTemplate(ir) {
10
+ let native;
11
+ try {
12
+ try {
13
+ native = require('../../native/compiler-native');
14
+ }
15
+ catch {
16
+ native = require('../../native/compiler-native/index.js');
17
+ }
18
+ }
19
+ catch (e) {
20
+ // Bridge load handled elsewhere
21
+ }
22
+ if (native && native.transformTemplateNative) {
23
+ const { html, bindings } = native.transformTemplateNative(JSON.stringify(ir.template.nodes), JSON.stringify(ir.template.expressions));
24
+ return {
25
+ html,
26
+ bindings,
27
+ scripts: ir.script ? ir.script.raw : null,
28
+ styles: ir.styles.map(s => s.raw)
29
+ };
30
+ }
31
+ // Fallback to legacy if bridge unavailable (though we aim for full native)
32
+ throw new Error('[Zenith Native] Transformation bridge unavailable');
33
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Invariant Validation
3
+ *
4
+ * NATIVE BRIDGE: All validation logic exists in Rust (`native/compiler-native/src/validate.rs`).
5
+ * The NAPI function `validateIr(irJson)` is the sole semantic authority.
6
+ */
7
+ import type { ZenIR } from '../ir/types';
8
+ /**
9
+ * Native bridge for IR validation
10
+ */
11
+ export declare function validateIr(ir: ZenIR): void;
12
+ export declare const INVARIANT: {
13
+ readonly LOOP_CONTEXT_LOST: "INV001";
14
+ readonly ATTRIBUTE_NOT_FORWARDED: "INV002";
15
+ readonly UNRESOLVED_COMPONENT: "INV003";
16
+ readonly REACTIVE_BOUNDARY: "INV004";
17
+ readonly TEMPLATE_TAG: "INV005";
18
+ readonly SLOT_ATTRIBUTE: "INV006";
19
+ readonly ORPHAN_COMPOUND: "INV007";
20
+ readonly NON_ENUMERABLE_JSX: "INV008";
21
+ readonly UNREGISTERED_EXPRESSION: "INV009";
22
+ readonly COMPONENT_PRECOMPILED: "INV010";
23
+ };
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Invariant Validation
3
+ *
4
+ * NATIVE BRIDGE: All validation logic exists in Rust (`native/compiler-native/src/validate.rs`).
5
+ * The NAPI function `validateIr(irJson)` is the sole semantic authority.
6
+ */
7
+ import { InvariantError } from '../errors/compilerError';
8
+ let native;
9
+ try {
10
+ try {
11
+ native = require('../../native/compiler-native');
12
+ }
13
+ catch {
14
+ native = require('../../native/compiler-native/index.js');
15
+ }
16
+ }
17
+ catch (e) {
18
+ // Bridge load handled elsewhere
19
+ }
20
+ /**
21
+ * Native bridge for IR validation
22
+ */
23
+ export function validateIr(ir) {
24
+ if (native && native.validateIr) {
25
+ const validationIR = {
26
+ filePath: ir.filePath,
27
+ template: {
28
+ raw: ir.template.raw,
29
+ nodes: ir.template.nodes,
30
+ expressions: ir.template.expressions,
31
+ },
32
+ styles: ir.styles,
33
+ script: ir.script,
34
+ };
35
+ const error = native.validateIr(JSON.stringify(validationIR));
36
+ if (error) {
37
+ throw new InvariantError(error.code, error.message, error.guarantee, error.file, error.line, error.column, error.context, error.hints);
38
+ }
39
+ }
40
+ else {
41
+ throw new Error('[Zenith Native] Validation bridge unavailable');
42
+ }
43
+ }
44
+ export const INVARIANT = {
45
+ LOOP_CONTEXT_LOST: 'INV001',
46
+ ATTRIBUTE_NOT_FORWARDED: 'INV002',
47
+ UNRESOLVED_COMPONENT: 'INV003',
48
+ REACTIVE_BOUNDARY: 'INV004',
49
+ TEMPLATE_TAG: 'INV005',
50
+ SLOT_ATTRIBUTE: 'INV006',
51
+ ORPHAN_COMPOUND: 'INV007',
52
+ NON_ENUMERABLE_JSX: 'INV008',
53
+ UNREGISTERED_EXPRESSION: 'INV009',
54
+ COMPONENT_PRECOMPILED: 'INV010',
55
+ };