@zenithbuild/core 0.1.0

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 (101) hide show
  1. package/.eslintignore +15 -0
  2. package/.gitattributes +2 -0
  3. package/.github/ISSUE_TEMPLATE/compiler-errors-for-invalid-state-declarations.md +25 -0
  4. package/.github/ISSUE_TEMPLATE/new_ticket.yaml +34 -0
  5. package/.github/pull_request_template.md +15 -0
  6. package/.github/workflows/discord-changelog.yml +141 -0
  7. package/.github/workflows/discord-notify.yml +242 -0
  8. package/.github/workflows/discord-version.yml +195 -0
  9. package/.prettierignore +13 -0
  10. package/.prettierrc +21 -0
  11. package/.zen.d.ts +15 -0
  12. package/LICENSE +21 -0
  13. package/README.md +55 -0
  14. package/app/components/Button.zen +46 -0
  15. package/app/components/Link.zen +11 -0
  16. package/app/favicon.ico +0 -0
  17. package/app/layouts/Main.zen +59 -0
  18. package/app/pages/about.zen +23 -0
  19. package/app/pages/blog/[id].zen +53 -0
  20. package/app/pages/blog/index.zen +32 -0
  21. package/app/pages/dynamic-dx.zen +712 -0
  22. package/app/pages/dynamic-primitives.zen +453 -0
  23. package/app/pages/index.zen +154 -0
  24. package/app/pages/navigation-demo.zen +229 -0
  25. package/app/pages/posts/[...slug].zen +61 -0
  26. package/app/pages/primitives-demo.zen +273 -0
  27. package/assets/logos/0E3B5DDD-605C-4839-BB2E-DFCA8ADC9604.PNG +0 -0
  28. package/assets/logos/760971E5-79A1-44F9-90B9-925DF30F4278.PNG +0 -0
  29. package/assets/logos/8A06ED80-9ED2-4689-BCBD-13B2E95EE8E4.JPG +0 -0
  30. package/assets/logos/C691FF58-ED13-4E8D-B6A3-02E835849340.PNG +0 -0
  31. package/assets/logos/C691FF58-ED13-4E8D-B6A3-02E835849340.svg +601 -0
  32. package/assets/logos/README.md +54 -0
  33. package/assets/logos/zen.icns +0 -0
  34. package/bun.lock +39 -0
  35. package/compiler/README.md +380 -0
  36. package/compiler/errors/compilerError.ts +24 -0
  37. package/compiler/finalize/finalizeOutput.ts +163 -0
  38. package/compiler/finalize/generateFinalBundle.ts +82 -0
  39. package/compiler/index.ts +44 -0
  40. package/compiler/ir/types.ts +83 -0
  41. package/compiler/legacy/binding.ts +254 -0
  42. package/compiler/legacy/bindings.ts +338 -0
  43. package/compiler/legacy/component-process.ts +1208 -0
  44. package/compiler/legacy/component.ts +301 -0
  45. package/compiler/legacy/event.ts +50 -0
  46. package/compiler/legacy/expression.ts +1149 -0
  47. package/compiler/legacy/mutation.ts +280 -0
  48. package/compiler/legacy/parse.ts +299 -0
  49. package/compiler/legacy/split.ts +608 -0
  50. package/compiler/legacy/types.ts +32 -0
  51. package/compiler/output/types.ts +34 -0
  52. package/compiler/parse/detectMapExpressions.ts +102 -0
  53. package/compiler/parse/parseScript.ts +22 -0
  54. package/compiler/parse/parseTemplate.ts +425 -0
  55. package/compiler/parse/parseZenFile.ts +66 -0
  56. package/compiler/parse/trackLoopContext.ts +82 -0
  57. package/compiler/runtime/dataExposure.ts +291 -0
  58. package/compiler/runtime/generateDOM.ts +144 -0
  59. package/compiler/runtime/generateHydrationBundle.ts +383 -0
  60. package/compiler/runtime/hydration.ts +309 -0
  61. package/compiler/runtime/navigation.ts +432 -0
  62. package/compiler/runtime/thinRuntime.ts +160 -0
  63. package/compiler/runtime/transformIR.ts +256 -0
  64. package/compiler/runtime/wrapExpression.ts +84 -0
  65. package/compiler/runtime/wrapExpressionWithLoop.ts +77 -0
  66. package/compiler/spa-build.ts +1000 -0
  67. package/compiler/test/validate-test.ts +104 -0
  68. package/compiler/transform/generateBindings.ts +47 -0
  69. package/compiler/transform/generateHTML.ts +28 -0
  70. package/compiler/transform/transformNode.ts +126 -0
  71. package/compiler/transform/transformTemplate.ts +38 -0
  72. package/compiler/validate/validateExpressions.ts +168 -0
  73. package/core/index.ts +135 -0
  74. package/core/lifecycle/index.ts +49 -0
  75. package/core/lifecycle/zen-mount.ts +182 -0
  76. package/core/lifecycle/zen-unmount.ts +88 -0
  77. package/core/reactivity/index.ts +54 -0
  78. package/core/reactivity/tracking.ts +167 -0
  79. package/core/reactivity/zen-batch.ts +57 -0
  80. package/core/reactivity/zen-effect.ts +139 -0
  81. package/core/reactivity/zen-memo.ts +146 -0
  82. package/core/reactivity/zen-ref.ts +52 -0
  83. package/core/reactivity/zen-signal.ts +121 -0
  84. package/core/reactivity/zen-state.ts +180 -0
  85. package/core/reactivity/zen-untrack.ts +44 -0
  86. package/docs/COMMENTS.md +111 -0
  87. package/docs/COMMITS.md +36 -0
  88. package/docs/CONTRIBUTING.md +116 -0
  89. package/docs/STYLEGUIDE.md +62 -0
  90. package/package.json +44 -0
  91. package/router/index.ts +76 -0
  92. package/router/manifest.ts +314 -0
  93. package/router/navigation/ZenLink.zen +231 -0
  94. package/router/navigation/index.ts +78 -0
  95. package/router/navigation/zen-link.ts +584 -0
  96. package/router/runtime.ts +458 -0
  97. package/router/types.ts +168 -0
  98. package/runtime/build.ts +17 -0
  99. package/runtime/serve.ts +93 -0
  100. package/scripts/webhook-proxy.ts +213 -0
  101. package/tsconfig.json +28 -0
@@ -0,0 +1,54 @@
1
+ # Zenith Logos
2
+
3
+ This directory contains the Zenith framework logo assets.
4
+
5
+ ## Logo Files
6
+
7
+ Based on the logo designs, you should place the following files here:
8
+
9
+ 1. **zenith-logo-icon.png** (or .svg) - The file icon version with the folded corner and ".ZENITH" button
10
+ 2. **zenith-logo-z.png** (or .svg) - The stylized "Z" logo with gradient and light trails
11
+ 3. **zenith-logo-full.png** (or .svg) - The full logo with "ZENITH" text below
12
+ 4. **zenith-logo-soon.png** (or .svg) - The logo variant with "Soon" text
13
+
14
+ ## File Icon for .zen Files
15
+
16
+ The SVG logo has been converted to a macOS `.icns` file to use as the file icon for `.zen` files.
17
+
18
+ ### Setting the Icon
19
+
20
+ **Option 1: Use the automated script (recommended)**
21
+ ```bash
22
+ cd assets/logos
23
+ ./set-zen-icon.sh path/to/your/file.zen
24
+ ```
25
+
26
+ **Option 2: Set icon for all .zen files in a directory**
27
+ ```bash
28
+ cd assets/logos
29
+ find ../app -name "*.zen" -exec ./set-zen-icon.sh {} \;
30
+ ```
31
+
32
+ **Option 3: Manual method**
33
+ 1. Open Finder and navigate to a `.zen` file
34
+ 2. Right-click the file and select "Get Info"
35
+ 3. Drag `zen.icns` onto the small icon in the top-left of the Get Info window
36
+ 4. Close the Get Info window
37
+
38
+ ### Recreating the .icns File
39
+
40
+ If you update the SVG and need to regenerate the `.icns` file:
41
+ ```bash
42
+ cd assets/logos
43
+ ./create-zen-icon.sh
44
+ ```
45
+
46
+ ## Usage
47
+
48
+ These logos can be used in:
49
+ - Documentation (`docs/`)
50
+ - README files
51
+ - Website/marketing materials
52
+ - Framework branding
53
+ - File type icons (`.zen` files)
54
+
Binary file
package/bun.lock ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "lockfileVersion": 1,
3
+ "configVersion": 1,
4
+ "workspaces": {
5
+ "": {
6
+ "name": "zenith",
7
+ "dependencies": {
8
+ "@types/parse5": "^7.0.0",
9
+ "parse5": "^8.0.0",
10
+ },
11
+ "devDependencies": {
12
+ "@types/bun": "latest",
13
+ "prettier": "^3.7.4",
14
+ },
15
+ "peerDependencies": {
16
+ "typescript": "^5",
17
+ },
18
+ },
19
+ },
20
+ "packages": {
21
+ "@types/bun": ["@types/bun@1.3.5", "", { "dependencies": { "bun-types": "1.3.5" } }, "sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w=="],
22
+
23
+ "@types/node": ["@types/node@25.0.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA=="],
24
+
25
+ "@types/parse5": ["@types/parse5@7.0.0", "", { "dependencies": { "parse5": "*" } }, "sha512-f2SeAxumolBmhuR62vNGTsSAvdz/Oj0k682xNrcKJ4dmRnTPODB74j6CPoNPzBPTHsu7Y7W7u93Mgp8Ovo8vWw=="],
26
+
27
+ "bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="],
28
+
29
+ "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
30
+
31
+ "parse5": ["parse5@8.0.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA=="],
32
+
33
+ "prettier": ["prettier@3.7.4", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA=="],
34
+
35
+ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
36
+
37
+ "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
38
+ }
39
+ }
@@ -0,0 +1,380 @@
1
+ # Zenith Compiler - Phase 1: Parse & Extract
2
+
3
+ ## Overview
4
+
5
+ This is Phase 1 of the Zenith compiler implementation. This phase focuses on **parsing and extracting** structure from `.zen` files without any runtime execution or transformation.
6
+
7
+ ## Directory Structure
8
+
9
+ ```
10
+ /compiler
11
+ /parse
12
+ parseZenFile.ts # Main file parser
13
+ parseTemplate.ts # Template/HTML parser
14
+ parseScript.ts # Script block extractor
15
+ /ir
16
+ types.ts # Intermediate Representation types
17
+ /errors
18
+ compilerError.ts # Compiler error handling
19
+ index.ts # Public API entry point
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ```typescript
25
+ import { compileZen } from './compiler/index'
26
+
27
+ const ir = compileZen('app/pages/example.zen')
28
+ // Returns ZenIR structure with parsed template, script, styles, and extracted expressions
29
+ ```
30
+
31
+ ## What This Phase Does
32
+
33
+ ✅ Parses `.zen` files into structured IR
34
+ ✅ Extracts `<script>` and `<style>` blocks
35
+ ✅ Parses HTML template structure
36
+ ✅ Extracts `{expression}` patterns from text and attributes
37
+ ✅ Records source locations for all nodes and expressions
38
+ ✅ Throws compiler errors with location information
39
+
40
+ ## What This Phase Does NOT Do
41
+
42
+ ❌ Execute expressions
43
+ ❌ Transform code
44
+ ❌ Generate runtime code
45
+ ❌ Handle reactivity
46
+ ❌ Render to DOM
47
+ ❌ Perform any runtime operations
48
+
49
+ ## IR Structure
50
+
51
+ The compiler produces a `ZenIR` object containing:
52
+
53
+ - `filePath`: Path to the source file
54
+ - `template`: TemplateIR with nodes and extracted expressions
55
+ - `script`: ScriptIR with raw script content (or null)
56
+ - `styles`: Array of StyleIR with raw style content
57
+
58
+ ## Expression Extraction
59
+
60
+ Expressions are extracted from:
61
+ - Text content: `Hello {name}!`
62
+ - Attribute values: `class={isActive ? "on" : "off"}`
63
+
64
+ Each expression gets:
65
+ - Unique ID (e.g., `expr_0`, `expr_1`)
66
+ - Source code (the expression content)
67
+ - Source location (line + column)
68
+
69
+ ## Phase 2: Transform IR → Static HTML + Runtime Bindings
70
+
71
+ Phase 2 transforms the IR into static HTML with explicit bindings:
72
+
73
+ - ✅ All `{}` expressions removed from HTML
74
+ - ✅ Expressions replaced with data attributes (`data-zen-text`, `data-zen-attr-*`)
75
+ - ✅ Bindings array generated with expression source code
76
+ - ✅ Scripts and styles pass through unchanged
77
+ - ✅ No runtime execution - pure transformation
78
+
79
+ ### Output Structure
80
+
81
+ ```typescript
82
+ {
83
+ html: string, // Static HTML with no {}
84
+ bindings: Binding[], // Array of expression bindings
85
+ scripts: string, // Raw script content
86
+ styles: string[] // Raw style content
87
+ }
88
+ ```
89
+
90
+ ### Binding Format
91
+
92
+ ```typescript
93
+ {
94
+ id: "exp_1",
95
+ type: "text" | "attribute",
96
+ target: "data-zen-text" | "class" | "style" | etc,
97
+ expression: "user.name" // Original expression code
98
+ }
99
+ ```
100
+
101
+ ## Phase 4: Runtime DOM & Reactivity
102
+
103
+ Phase 4 transforms the IR into fully functional runtime JavaScript code:
104
+
105
+ - ✅ Expression wrapping with state access
106
+ - ✅ DOM creation code generation
107
+ - ✅ Event handler binding
108
+ - ✅ State initialization
109
+ - ✅ Hydrate function for SPA hydration
110
+ - ✅ Style injection
111
+
112
+ ### Runtime Code Structure
113
+
114
+ ```typescript
115
+ {
116
+ expressions: string, // Wrapped expression functions
117
+ render: string, // renderDynamicPage(state) function
118
+ hydrate: string, // hydrate(root, state) function
119
+ styles: string, // Style injection code
120
+ script: string, // Transformed script code
121
+ stateInit: string // State initialization code
122
+ }
123
+ ```
124
+
125
+ ### Usage
126
+
127
+ ```typescript
128
+ import { compileZen } from './compiler/index'
129
+ import { transformIR } from './compiler/runtime/transformIR'
130
+
131
+ const { ir } = compileZen('app/pages/example.zen')
132
+ const runtime = transformIR(ir)
133
+
134
+ // Runtime code is now ready to execute
135
+ // - runtime.expressions: Expression wrapper functions
136
+ // - runtime.render: DOM creation function
137
+ // - runtime.hydrate: Hydration function
138
+ ```
139
+
140
+ ## Phase 5: Runtime Hydration
141
+
142
+ Phase 5 provides the browser-side runtime that hydrates static HTML with dynamic expressions:
143
+
144
+ - ✅ Expression evaluation via registry (`window.__ZENITH_EXPRESSIONS__`)
145
+ - ✅ Text binding updates (`data-zen-text` attributes)
146
+ - ✅ Attribute binding updates (`data-zen-attr-*` attributes)
147
+ - ✅ Event handler binding (`data-zen-{eventType}` attributes)
148
+ - ✅ Reactive state updates (`update(state)` function)
149
+ - ✅ Cleanup and memory management
150
+ - ✅ Error handling with expression IDs
151
+
152
+ ### Runtime Functions
153
+
154
+ ```typescript
155
+ // Hydrate static HTML with dynamic expressions
156
+ window.zenithHydrate(state, container?)
157
+
158
+ // Update all bindings when state changes
159
+ window.zenithUpdate(state)
160
+
161
+ // Bind event handlers
162
+ window.zenithBindEvents(container?)
163
+
164
+ // Cleanup bindings and event listeners
165
+ window.zenithCleanup(container?)
166
+ ```
167
+
168
+ ### Runtime Bundle
169
+
170
+ The `transformIR` function now generates a complete runtime bundle:
171
+
172
+ ```typescript
173
+ const runtime = transformIR(ir)
174
+ // runtime.bundle contains the complete JavaScript code ready for browser execution
175
+ ```
176
+
177
+ The bundle includes:
178
+ - Expression wrapper functions
179
+ - Expression registry initialization
180
+ - Hydration runtime code
181
+ - State initialization
182
+ - Style injection
183
+ - User script code
184
+
185
+ ### Usage Example
186
+
187
+ ```typescript
188
+ // In browser
189
+ const state = { user: { name: 'Alice' }, count: 5 }
190
+ window.zenithHydrate(state, document.body)
191
+
192
+ // On state change
193
+ state.count = 10
194
+ window.zenithUpdate(state)
195
+ ```
196
+
197
+ ## Phase 6: Explicit Data Exposure
198
+
199
+ Phase 6 ensures all data references are explicit rather than relying on implicit globals:
200
+
201
+ - ✅ Expression dependency analysis (loaderData, props, stores, state)
202
+ - ✅ Explicit function signatures with data arguments
203
+ - ✅ Updated hydration runtime to pass explicit data
204
+ - ✅ Compile-time validation of data references
205
+ - ✅ Backwards compatibility with legacy state-only expressions
206
+
207
+ ### Data Sources
208
+
209
+ Expressions can reference data from:
210
+ 1. **Loader Data** - Route-level `loader()` function data (`loaderData.user.name`)
211
+ 2. **Props** - Component/page props (`props.title`)
212
+ 3. **Stores** - Global stores (`stores.cart.items`)
213
+ 4. **State** - Reactive state (`state.count`)
214
+
215
+ ### Expression Wrapper Signatures
216
+
217
+ Phase 6 expressions now accept explicit arguments:
218
+
219
+ ```typescript
220
+ // Before (Phase 5)
221
+ const expr_0 = (state) => { with (state) { return user.name } }
222
+
223
+ // After (Phase 6)
224
+ const expr_0 = (state, loaderData, props, stores) => {
225
+ const __ctx = Object.assign({}, loaderData, props, stores, state);
226
+ with (__ctx) { return user.name }
227
+ }
228
+ ```
229
+
230
+ ### Runtime Hydration
231
+
232
+ ```typescript
233
+ // Phase 6 signature
234
+ window.zenithHydrate(state, loaderData, props, stores, container?)
235
+ window.zenithUpdate(state, loaderData, props, stores)
236
+ ```
237
+
238
+ The runtime maintains backwards compatibility - if called with only `state`, it uses legacy behavior.
239
+
240
+ ### Data Dependency Analysis
241
+
242
+ The compiler analyzes each expression to detect:
243
+ - `loaderData.property` → loader data dependency
244
+ - `props.name` → props dependency
245
+ - `stores.name` → stores dependency
246
+ - Simple identifiers → state dependency
247
+
248
+ All dependencies are validated at compile time with clear error messages.
249
+
250
+ ## Phase 7: Navigation, Prefetch & Bun Accelerator
251
+
252
+ Phase 7 implements safe SPA navigation with prefetching and explicit data exposure:
253
+
254
+ - ✅ Prefetch compiled output (HTML + JS) for routes
255
+ - ✅ Route caching system
256
+ - ✅ Safe SPA navigation with explicit data (loaderData, props, stores)
257
+ - ✅ Browser history handling (back/forward)
258
+ - ✅ ZenLink component with prefetch support
259
+ - ✅ Navigation runtime integration
260
+ - ✅ No raw .zen files in browser
261
+
262
+ ### Navigation API
263
+
264
+ ```typescript
265
+ // Prefetch a route
266
+ window.__zenith_prefetch('/dashboard')
267
+
268
+ // Navigate with explicit data
269
+ window.navigate('/dashboard', {
270
+ loaderData: { user: { name: 'Alice' } },
271
+ props: { title: 'Dashboard' },
272
+ stores: { cart: { items: 3 } },
273
+ replace: false // Use pushState instead of replaceState
274
+ })
275
+ ```
276
+
277
+ ### ZenLink Component
278
+
279
+ ```html
280
+ <!-- With prefetch (on hover) -->
281
+ <ZenLink href="/about" prefetch={true}>About</ZenLink>
282
+
283
+ <!-- Without prefetch -->
284
+ <ZenLink href="/blog">Blog</ZenLink>
285
+ ```
286
+
287
+ ### Key Principles
288
+
289
+ 1. **Compiler owns all expressions** - Runtime never parses .zen files
290
+ 2. **Prefetch compiled output** - HTML + JS bundles, not source
291
+ 3. **Explicit data exposure** - All data passed explicitly (no implicit globals)
292
+ 4. **Bun as accelerator** - Used for bundling/transpilation, not template parsing
293
+ 5. **Safe navigation** - No stacked mutations, proper cleanup, history handling
294
+
295
+ ### Route Cache
296
+
297
+ Prefetched routes are cached with:
298
+ - Compiled HTML
299
+ - Compiled JS runtime
300
+ - Styles
301
+ - Route metadata
302
+
303
+ ### Browser History
304
+
305
+ The navigation system:
306
+ - Handles `popstate` events for back/forward
307
+ - Updates DOM safely without re-parsing
308
+ - Maintains route state correctly
309
+ - Avoids stacked history mutations
310
+
311
+ ## Phase 8/9/10: Finalization & Build Guarantees
312
+
313
+ Phase 8/9/10 ensures deterministic compilation and compile-time validation:
314
+
315
+ - ✅ Compile-time expression validation
316
+ - ✅ Build fails on invalid expressions with line/column info
317
+ - ✅ No raw `{expression}` in HTML output
318
+ - ✅ Thin declarative runtime (no eval, no template parsing)
319
+ - ✅ Bun integration for bundling/transpilation (not template parsing)
320
+ - ✅ Final HTML + JS output ready for browser
321
+
322
+ ### Validation
323
+
324
+ All expressions are validated at compile time:
325
+ - Syntax validation
326
+ - Brace/parentheses/bracket matching
327
+ - Unsafe code detection (eval, Function, with)
328
+ - Build fails immediately on errors
329
+
330
+ ### Output Guarantees
331
+
332
+ - **HTML**: Contains only hydration markers (`data-zen-text`, `data-zen-attr-*`)
333
+ - **JS**: Pre-compiled expression functions, no template parsing
334
+ - **Runtime**: Thin, declarative - only DOM updates and event binding
335
+ - **No eval**: Runtime never uses `eval`, `new Function`, or `with(window)`
336
+
337
+ ### Bun Integration
338
+
339
+ Bun is used as an accelerator for:
340
+ - ✅ JS/TS transpilation
341
+ - ✅ Asset bundling
342
+ - ✅ SSR runtime support
343
+
344
+ Bun is **NOT** used for:
345
+ - ❌ Template parsing
346
+ - ❌ Expression analysis
347
+ - ❌ AST transformations
348
+
349
+ ### Build Guarantees
350
+
351
+ - Build success = UI guaranteed to work
352
+ - All failures are compile-time (never runtime)
353
+ - Error messages include file, line, column
354
+ - Invalid expressions fail the build immediately
355
+
356
+ ## Next Phases
357
+
358
+ Future enhancements:
359
+ - Enhanced conditional/ternary handling in IR
360
+ - Map iteration support in IR
361
+ - Advanced reactivity with dependency tracking
362
+ - SSR/SSG/ISR output generation
363
+ - Type checking for loader/props/stores data
364
+ - Route cache generation in build system
365
+
366
+ ## Testing
367
+
368
+ The compiler can be tested by importing and calling `compileZen()`:
369
+
370
+ ```typescript
371
+ import { compileZen } from './compiler/index'
372
+
373
+ try {
374
+ const ir = compileZen('app/pages/example.zen')
375
+ console.log(JSON.stringify(ir, null, 2))
376
+ } catch (error) {
377
+ console.error('Compiler error:', error)
378
+ }
379
+ ```
380
+
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Compiler Error Handling
3
+ *
4
+ * Compiler errors with source location information
5
+ */
6
+
7
+ export class CompilerError extends Error {
8
+ file: string
9
+ line: number
10
+ column: number
11
+
12
+ constructor(message: string, file: string, line: number, column: number) {
13
+ super(`${file}:${line}:${column} ${message}`)
14
+ this.name = 'CompilerError'
15
+ this.file = file
16
+ this.line = line
17
+ this.column = column
18
+ }
19
+
20
+ override toString(): string {
21
+ return `${this.file}:${this.line}:${this.column} ${this.message}`
22
+ }
23
+ }
24
+
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Finalize Output
3
+ *
4
+ * Phase 8/9/10: Generate final compiled HTML + JS output with hydration markers
5
+ *
6
+ * Ensures:
7
+ * - All expressions are replaced with hydration markers
8
+ * - HTML contains no raw {expression} syntax
9
+ * - JS runtime is ready for browser execution
10
+ * - Hydration markers are correctly placed
11
+ */
12
+
13
+ import type { CompiledTemplate } from '../output/types'
14
+ import type { ZenIR } from '../ir/types'
15
+ import { transformIR, type RuntimeCode } from '../runtime/transformIR'
16
+ import { validateExpressionsOrThrow } from '../validate/validateExpressions'
17
+
18
+ /**
19
+ * Finalized output ready for browser
20
+ */
21
+ export interface FinalizedOutput {
22
+ html: string
23
+ js: string
24
+ styles: string[]
25
+ hasErrors: boolean
26
+ errors: string[]
27
+ }
28
+
29
+ /**
30
+ * Finalize compiler output
31
+ *
32
+ * This is the final step that ensures:
33
+ * 1. All expressions are validated
34
+ * 2. HTML contains no raw expressions
35
+ * 3. JS runtime is generated
36
+ * 4. Output is ready for browser
37
+ *
38
+ * @param ir - Intermediate representation
39
+ * @param compiled - Compiled template from Phase 2
40
+ * @returns Finalized output
41
+ */
42
+ export function finalizeOutput(
43
+ ir: ZenIR,
44
+ compiled: CompiledTemplate
45
+ ): FinalizedOutput {
46
+ const errors: string[] = []
47
+
48
+ // 1. Validate all expressions (Phase 8/9/10 requirement)
49
+ try {
50
+ validateExpressionsOrThrow(ir.template.expressions, ir.filePath)
51
+ } catch (error: any) {
52
+ if (error instanceof Error) {
53
+ errors.push(error.message)
54
+ return {
55
+ html: '',
56
+ js: '',
57
+ styles: [],
58
+ hasErrors: true,
59
+ errors
60
+ }
61
+ }
62
+ }
63
+
64
+ // 2. Verify HTML contains no raw expressions
65
+ const htmlErrors = verifyNoRawExpressions(compiled.html, ir.filePath)
66
+ if (htmlErrors.length > 0) {
67
+ errors.push(...htmlErrors)
68
+ return {
69
+ html: '',
70
+ js: '',
71
+ styles: [],
72
+ hasErrors: true,
73
+ errors
74
+ }
75
+ }
76
+
77
+ // 3. Generate runtime code
78
+ let runtimeCode: RuntimeCode
79
+ try {
80
+ runtimeCode = transformIR(ir)
81
+ } catch (error: any) {
82
+ errors.push(`Runtime generation failed: ${error.message}`)
83
+ return {
84
+ html: '',
85
+ js: '',
86
+ styles: [],
87
+ hasErrors: true,
88
+ errors
89
+ }
90
+ }
91
+
92
+ // 4. Combine HTML and JS
93
+ const finalHTML = compiled.html
94
+ const finalJS = runtimeCode.bundle
95
+
96
+ return {
97
+ html: finalHTML,
98
+ js: finalJS,
99
+ styles: compiled.styles,
100
+ hasErrors: false,
101
+ errors: []
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Verify HTML contains no raw {expression} syntax
107
+ *
108
+ * This is a critical check - browser must never see raw expressions
109
+ */
110
+ function verifyNoRawExpressions(html: string, filePath: string): string[] {
111
+ const errors: string[] = []
112
+
113
+ // Check for raw {expression} patterns (not data-zen-* attributes)
114
+ // Allow data-zen-text, data-zen-attr-* but not raw { }
115
+ const rawExpressionPattern = /\{[^}]*\}/g
116
+ const matches = html.match(rawExpressionPattern)
117
+
118
+ if (matches && matches.length > 0) {
119
+ // Filter out false positives (comments, data attributes, etc.)
120
+ const actualExpressions = matches.filter(match => {
121
+ // Exclude if it's in a comment
122
+ if (html.includes(`<!--${match}`) || html.includes(`${match}-->`)) {
123
+ return false
124
+ }
125
+ // Exclude if it's in a data attribute value (already processed)
126
+ if (match.includes('data-zen-')) {
127
+ return false
128
+ }
129
+ // This looks like a raw expression
130
+ return true
131
+ })
132
+
133
+ if (actualExpressions.length > 0) {
134
+ errors.push(
135
+ `HTML contains raw expressions that were not compiled: ${actualExpressions.join(', ')}\n` +
136
+ `File: ${filePath}\n` +
137
+ `All expressions must be replaced with hydration markers (data-zen-text, data-zen-attr-*)`
138
+ )
139
+ }
140
+ }
141
+
142
+ return errors
143
+ }
144
+
145
+ /**
146
+ * Generate final output with error handling
147
+ *
148
+ * Throws if validation fails (build must fail on errors)
149
+ */
150
+ export function finalizeOutputOrThrow(
151
+ ir: ZenIR,
152
+ compiled: CompiledTemplate
153
+ ): FinalizedOutput {
154
+ const output = finalizeOutput(ir, compiled)
155
+
156
+ if (output.hasErrors) {
157
+ const errorMessage = output.errors.join('\n\n')
158
+ throw new Error(`Compilation failed:\n\n${errorMessage}`)
159
+ }
160
+
161
+ return output
162
+ }
163
+