@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,256 @@
1
+ /**
2
+ * Transform IR to Runtime Code
3
+ *
4
+ * Phase 4: Transform ZenIR into runtime-ready JavaScript code with full reactivity
5
+ */
6
+
7
+ import type { ZenIR } from '../ir/types'
8
+ import { generateExpressionWrappers } from './wrapExpression'
9
+ import { generateDOMFunction } from './generateDOM'
10
+ import { generateHydrationRuntime, generateExpressionRegistry } from './generateHydrationBundle'
11
+ import { analyzeAllExpressions, type ExpressionDataDependencies } from './dataExposure'
12
+ import { generateNavigationRuntime } from './navigation'
13
+ import { extractStateDeclarations } from '../legacy/parse'
14
+
15
+ export interface RuntimeCode {
16
+ expressions: string // Expression wrapper functions
17
+ render: string // renderDynamicPage function (legacy, for reference)
18
+ hydration: string // Phase 5 hydration runtime code
19
+ styles: string // Style injection code
20
+ script: string // Transformed script code
21
+ stateInit: string // State initialization code
22
+ bundle: string // Complete runtime bundle (expressions + hydration + helpers)
23
+ }
24
+
25
+ /**
26
+ * Transform ZenIR into runtime JavaScript code
27
+ */
28
+ export function transformIR(ir: ZenIR): RuntimeCode {
29
+ // Phase 6: Analyze expression dependencies for explicit data exposure
30
+ const expressionDependencies = analyzeAllExpressions(
31
+ ir.template.expressions,
32
+ ir.filePath,
33
+ [], // declaredLoaderProps - can be enhanced with loader analysis
34
+ [], // declaredProps - can be enhanced with component prop analysis
35
+ [] // declaredStores - can be enhanced with store import analysis
36
+ )
37
+
38
+ // Generate expression wrappers with dependencies
39
+ const expressions = generateExpressionWrappers(ir.template.expressions, expressionDependencies)
40
+
41
+ // Generate DOM creation code
42
+ const renderFunction = generateDOMFunction(
43
+ ir.template.nodes,
44
+ ir.template.expressions,
45
+ 'renderDynamicPage'
46
+ )
47
+
48
+ // Generate hydrate function (legacy, for reference)
49
+ const hydrateFunction = generateHydrateFunction()
50
+
51
+ // Generate Phase 5 hydration runtime
52
+ const hydrationRuntime = generateHydrationRuntime()
53
+
54
+ // Generate Phase 7 navigation runtime
55
+ const navigationRuntime = generateNavigationRuntime()
56
+
57
+ // Generate expression registry initialization
58
+ const expressionRegistry = generateExpressionRegistry(ir.template.expressions)
59
+
60
+ // Generate style injection code
61
+ const stylesCode = generateStyleInjection(ir.styles)
62
+
63
+ // Extract state declarations and generate initialization
64
+ const scriptContent = ir.script?.raw || ''
65
+ const stateDeclarations = extractStateDeclarations(scriptContent)
66
+ const stateInitCode = generateStateInitialization(stateDeclarations)
67
+
68
+ // Transform script (remove state declarations, they're handled by runtime)
69
+ const scriptCode = transformScript(scriptContent, stateDeclarations)
70
+
71
+ // Generate complete runtime bundle
72
+ const bundle = generateRuntimeBundle({
73
+ expressions,
74
+ expressionRegistry,
75
+ hydrationRuntime,
76
+ navigationRuntime,
77
+ stylesCode,
78
+ scriptCode,
79
+ stateInitCode
80
+ })
81
+
82
+ return {
83
+ expressions,
84
+ render: renderFunction,
85
+ hydration: hydrationRuntime,
86
+ styles: stylesCode,
87
+ script: scriptCode,
88
+ stateInit: stateInitCode,
89
+ bundle
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Generate complete runtime bundle
95
+ */
96
+ function generateRuntimeBundle(parts: {
97
+ expressions: string
98
+ expressionRegistry: string
99
+ hydrationRuntime: string
100
+ navigationRuntime: string
101
+ stylesCode: string
102
+ scriptCode: string
103
+ stateInitCode: string
104
+ }): string {
105
+ return `// Zenith Runtime Bundle (Phase 5)
106
+ // Generated at compile time - no .zen parsing in browser
107
+
108
+ ${parts.expressions}
109
+
110
+ ${parts.expressionRegistry}
111
+
112
+ ${parts.hydrationRuntime}
113
+
114
+ ${parts.navigationRuntime}
115
+
116
+ ${parts.stateInitCode ? `// State initialization
117
+ ${parts.stateInitCode}` : ''}
118
+
119
+ ${parts.stylesCode ? `// Style injection
120
+ ${parts.stylesCode}` : ''}
121
+
122
+ ${parts.scriptCode ? `// User script code
123
+ ${parts.scriptCode}` : ''}
124
+
125
+ // Export hydration functions
126
+ if (typeof window !== 'undefined') {
127
+ window.zenithHydrate = window.__zenith_hydrate || function(state, container) {
128
+ console.warn('[Zenith] Hydration runtime not loaded');
129
+ };
130
+ window.zenithUpdate = window.__zenith_update || function(state) {
131
+ console.warn('[Zenith] Update runtime not loaded');
132
+ };
133
+ window.zenithBindEvents = window.__zenith_bindEvents || function(container) {
134
+ console.warn('[Zenith] Event binding runtime not loaded');
135
+ };
136
+ window.zenithCleanup = window.__zenith_cleanup || function(container) {
137
+ console.warn('[Zenith] Cleanup runtime not loaded');
138
+ };
139
+ }
140
+ `
141
+ }
142
+
143
+ /**
144
+ * Generate hydrate function that mounts the DOM with reactivity
145
+ */
146
+ function generateHydrateFunction(): string {
147
+ return `function hydrate(root, state) {
148
+ if (!root) {
149
+ // SSR fallback - return initial HTML string
150
+ console.warn('[Zenith] hydrate called without root element - SSR mode');
151
+ return '';
152
+ }
153
+
154
+ // Clear root
155
+ root.innerHTML = '';
156
+
157
+ // Render template
158
+ const dom = renderDynamicPage(state);
159
+
160
+ // Append to root
161
+ if (dom instanceof DocumentFragment) {
162
+ root.appendChild(dom);
163
+ } else if (dom instanceof Node) {
164
+ root.appendChild(dom);
165
+ }
166
+
167
+ // Bind event handlers
168
+ bindEventHandlers(root, state);
169
+
170
+ // Set up reactive updates (if state is reactive)
171
+ setupReactiveUpdates(root, state);
172
+
173
+ return root;
174
+ }
175
+
176
+ function bindEventHandlers(root, state) {
177
+ // Find all elements with data-zen-* event attributes
178
+ const eventTypes = ['click', 'change', 'input', 'submit', 'focus', 'blur'];
179
+
180
+ for (const eventType of eventTypes) {
181
+ const elements = root.querySelectorAll(\`[data-zen-\${eventType}]\`);
182
+ for (const el of elements) {
183
+ const handlerName = el.getAttribute(\`data-zen-\${eventType}\`);
184
+ if (handlerName && typeof window[handlerName] === 'function') {
185
+ el.addEventListener(eventType, (e) => {
186
+ window[handlerName](e, el);
187
+ });
188
+ }
189
+ }
190
+ }
191
+ }
192
+
193
+ function setupReactiveUpdates(root, state) {
194
+ // For now, reactive updates are handled by the existing binding system
195
+ // This is a placeholder for future reactive DOM updates
196
+ // The existing runtime handles reactivity via state property setters
197
+ }`
198
+ }
199
+
200
+ /**
201
+ * Generate style injection code
202
+ */
203
+ function generateStyleInjection(styles: Array<{ raw: string }>): string {
204
+ if (styles.length === 0) {
205
+ return ''
206
+ }
207
+
208
+ const styleBlocks = styles.map((style, index) => {
209
+ const escapedStyle = style.raw.replace(/`/g, '\\`').replace(/\$/g, '\\$')
210
+ return `
211
+ const style${index} = document.createElement('style');
212
+ style${index}.textContent = \`${escapedStyle}\`;
213
+ document.head.appendChild(style${index});`
214
+ }).join('')
215
+
216
+ return `function injectStyles() {${styleBlocks}
217
+ }`
218
+ }
219
+
220
+ /**
221
+ * Generate state initialization code
222
+ */
223
+ function generateStateInitialization(stateDeclarations: Map<string, string>): string {
224
+ if (stateDeclarations.size === 0) {
225
+ return ''
226
+ }
227
+
228
+ const initCode = Array.from(stateDeclarations.entries()).map(([name, value]) => {
229
+ return `
230
+ // Initialize state: ${name}
231
+ if (!state.${name}) {
232
+ state.${name} = ${value};
233
+ }`
234
+ }).join('')
235
+
236
+ return `function initializeState(state) {${initCode}
237
+ }`
238
+ }
239
+
240
+ /**
241
+ * Transform script content
242
+ * Removes state declarations (they're handled by state initialization)
243
+ */
244
+ function transformScript(scriptContent: string, stateDeclarations: Map<string, string>): string {
245
+ // Remove state declarations - they're handled by initializeState
246
+ let transformed = scriptContent
247
+
248
+ for (const [name] of stateDeclarations.entries()) {
249
+ // Remove "state name = value" declarations
250
+ const stateRegex = new RegExp(`state\\s+${name}\\s*=[^;]*;?`, 'g')
251
+ transformed = transformed.replace(stateRegex, '')
252
+ }
253
+
254
+ return transformed.trim()
255
+ }
256
+
@@ -0,0 +1,84 @@
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
+
10
+ import type { ExpressionIR, LoopContext } from '../ir/types'
11
+ import type { ExpressionDataDependencies } from './dataExposure'
12
+ import { generateExplicitExpressionWrapper } from './dataExposure'
13
+ import { wrapExpressionWithLoopContext } from './wrapExpressionWithLoop'
14
+
15
+ /**
16
+ * Wrap an expression into a runtime function with explicit data arguments
17
+ *
18
+ * Phase 6: Supports explicit loaderData, props, stores arguments
19
+ * Phase 7: Supports loop context for expressions inside map iterations
20
+ */
21
+ export function wrapExpression(
22
+ expr: ExpressionIR,
23
+ dependencies?: ExpressionDataDependencies,
24
+ loopContext?: LoopContext // Phase 7: Loop context for map expressions
25
+ ): string {
26
+ // Phase 7: If loop context is provided, use loop-aware wrapper
27
+ if (loopContext && loopContext.variables.length > 0) {
28
+ return wrapExpressionWithLoopContext(expr, loopContext, dependencies)
29
+ }
30
+
31
+ // If dependencies are provided, use explicit wrapper (Phase 6)
32
+ if (dependencies) {
33
+ return generateExplicitExpressionWrapper(expr, dependencies)
34
+ }
35
+
36
+ // Fallback to legacy wrapper (backwards compatibility)
37
+ const { id, code } = expr
38
+ const escapedCode = code.replace(/`/g, '\\`').replace(/\$/g, '\\$')
39
+
40
+ return `
41
+ // Expression: ${escapedCode}
42
+ const ${id} = (state) => {
43
+ try {
44
+ with (state) {
45
+ return ${code};
46
+ }
47
+ } catch (e) {
48
+ console.warn('[Zenith] Expression evaluation error:', ${JSON.stringify(code)}, e);
49
+ return undefined;
50
+ }
51
+ };`
52
+ }
53
+
54
+ /**
55
+ * Generate all expression wrappers for a set of expressions
56
+ *
57
+ * Phase 6: Accepts dependencies array for explicit data exposure
58
+ * Phase 7: Accepts loop contexts for expressions inside map iterations
59
+ */
60
+ export function generateExpressionWrappers(
61
+ expressions: ExpressionIR[],
62
+ dependencies?: ExpressionDataDependencies[],
63
+ loopContexts?: (LoopContext | undefined)[] // Phase 7: Loop contexts for each expression
64
+ ): string {
65
+ if (expressions.length === 0) {
66
+ return ''
67
+ }
68
+
69
+ if (dependencies && dependencies.length === expressions.length) {
70
+ // Use explicit wrappers with dependencies and optional loop contexts
71
+ return expressions
72
+ .map((expr, index) => {
73
+ const loopCtx = loopContexts && loopContexts[index] !== undefined
74
+ ? loopContexts[index]
75
+ : undefined
76
+ return wrapExpression(expr, dependencies[index], loopCtx)
77
+ })
78
+ .join('\n')
79
+ }
80
+
81
+ // Fallback to legacy wrappers (no dependencies, no loop contexts)
82
+ return expressions.map(expr => wrapExpression(expr)).join('\n')
83
+ }
84
+
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Expression Wrapper with Loop Context Support
3
+ *
4
+ * Phase 7: Wraps expressions that reference loop variables from map iterations
5
+ *
6
+ * Generates runtime functions that accept (state, loaderData, props, stores, loopContext)
7
+ * and evaluate expressions with both global state and loop-scoped variables available
8
+ */
9
+
10
+ import type { ExpressionIR, LoopContext } from '../ir/types'
11
+ import type { ExpressionDataDependencies } from './dataExposure'
12
+
13
+ /**
14
+ * Generate an expression wrapper that accepts loop context
15
+ *
16
+ * Phase 7: Expressions inside map loops need access to loop variables (e.g., todo, index)
17
+ * in addition to global state (state, loaderData, props, stores)
18
+ */
19
+ export function wrapExpressionWithLoopContext(
20
+ expr: ExpressionIR,
21
+ loopContext?: LoopContext,
22
+ dependencies?: ExpressionDataDependencies
23
+ ): string {
24
+ const { id, code } = expr
25
+ const escapedCode = code.replace(/`/g, '\\`').replace(/\$/g, '\\$')
26
+
27
+ if (!loopContext || loopContext.variables.length === 0) {
28
+ // No loop context - use standard wrapper (will be handled by wrapExpression)
29
+ return ''
30
+ }
31
+
32
+ // Determine arguments based on dependencies
33
+ const args: string[] = []
34
+ if (dependencies?.usesState || dependencies?.stateProperties.length > 0) args.push('state')
35
+ if (dependencies?.usesLoaderData) args.push('loaderData')
36
+ if (dependencies?.usesProps) args.push('props')
37
+ if (dependencies?.usesStores) args.push('stores')
38
+
39
+ // Phase 7: Always add loopContext as the last argument
40
+ args.push('loopContext')
41
+
42
+ const argsStr = args.join(', ')
43
+
44
+ // Generate function that merges state and loop context
45
+ // Loop context variables take precedence over state properties with the same name
46
+ const loopVarsDecl = loopContext.variables.map(v => ` const ${v} = loopContext?.${v};`).join('\n')
47
+ const loopVarsObject = `{ ${loopContext.variables.join(', ')} }`
48
+
49
+ // Create merged context for expression evaluation
50
+ // Order: loopContext > stores > props > loaderData > state
51
+ const contextMerge: string[] = []
52
+ if (dependencies?.usesState || dependencies?.stateProperties.length > 0) contextMerge.push('state')
53
+ if (dependencies?.usesStores) contextMerge.push('stores')
54
+ if (dependencies?.usesProps) contextMerge.push('props')
55
+ if (dependencies?.usesLoaderData) contextMerge.push('loaderData')
56
+ if (loopContext) contextMerge.push('loopContext')
57
+
58
+ const contextObject = contextMerge.length > 0
59
+ ? `const __ctx = Object.assign({}, ${contextMerge.join(', ')});`
60
+ : `const __ctx = loopContext || {};`
61
+
62
+ return `
63
+ // Expression with loop context: ${escapedCode}
64
+ // Loop variables: ${loopContext.variables.join(', ')}
65
+ const ${id} = (${argsStr}) => {
66
+ try {
67
+ ${contextObject}
68
+ with (__ctx) {
69
+ return ${code};
70
+ }
71
+ } catch (e) {
72
+ console.warn('[Zenith] Expression evaluation error for "${escapedCode}":', e);
73
+ return undefined;
74
+ }
75
+ };`
76
+ }
77
+