@zenithbuild/core 0.4.7 → 0.5.0-beta.2.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 (110) hide show
  1. package/CORE_CONTRACT.md +143 -0
  2. package/README.md +11 -31
  3. package/bin/zenith.js +68 -0
  4. package/package.json +41 -53
  5. package/src/config.js +134 -0
  6. package/src/core-template.js +30 -0
  7. package/src/errors.js +54 -0
  8. package/src/guards.js +61 -0
  9. package/src/hash.js +52 -0
  10. package/src/index.js +26 -0
  11. package/src/ir/index.js +1 -0
  12. package/src/order.js +69 -0
  13. package/src/path.js +131 -0
  14. package/src/schema.js +28 -0
  15. package/src/version.js +67 -0
  16. package/bin/zen-build.ts +0 -2
  17. package/bin/zen-dev.ts +0 -2
  18. package/bin/zen-preview.ts +0 -2
  19. package/bin/zenith.ts +0 -2
  20. package/cli/commands/add.ts +0 -37
  21. package/cli/commands/build.ts +0 -37
  22. package/cli/commands/create.ts +0 -702
  23. package/cli/commands/dev.ts +0 -335
  24. package/cli/commands/index.ts +0 -112
  25. package/cli/commands/preview.ts +0 -62
  26. package/cli/commands/remove.ts +0 -33
  27. package/cli/index.ts +0 -10
  28. package/cli/main.ts +0 -101
  29. package/cli/utils/branding.ts +0 -178
  30. package/cli/utils/content.ts +0 -112
  31. package/cli/utils/logger.ts +0 -46
  32. package/cli/utils/plugin-manager.ts +0 -114
  33. package/cli/utils/project.ts +0 -77
  34. package/compiler/README.md +0 -380
  35. package/compiler/build-analyzer.ts +0 -122
  36. package/compiler/css/index.ts +0 -317
  37. package/compiler/discovery/componentDiscovery.ts +0 -178
  38. package/compiler/discovery/layouts.ts +0 -70
  39. package/compiler/errors/compilerError.ts +0 -56
  40. package/compiler/finalize/finalizeOutput.ts +0 -192
  41. package/compiler/finalize/generateFinalBundle.ts +0 -82
  42. package/compiler/index.ts +0 -82
  43. package/compiler/ir/types.ts +0 -162
  44. package/compiler/output/types.ts +0 -34
  45. package/compiler/parse/detectMapExpressions.ts +0 -102
  46. package/compiler/parse/parseScript.ts +0 -46
  47. package/compiler/parse/parseTemplate.ts +0 -599
  48. package/compiler/parse/parseZenFile.ts +0 -66
  49. package/compiler/parse/scriptAnalysis.ts +0 -91
  50. package/compiler/parse/trackLoopContext.ts +0 -82
  51. package/compiler/runtime/dataExposure.ts +0 -317
  52. package/compiler/runtime/generateDOM.ts +0 -246
  53. package/compiler/runtime/generateHydrationBundle.ts +0 -407
  54. package/compiler/runtime/hydration.ts +0 -309
  55. package/compiler/runtime/navigation.ts +0 -432
  56. package/compiler/runtime/thinRuntime.ts +0 -160
  57. package/compiler/runtime/transformIR.ts +0 -363
  58. package/compiler/runtime/wrapExpression.ts +0 -95
  59. package/compiler/runtime/wrapExpressionWithLoop.ts +0 -83
  60. package/compiler/spa-build.ts +0 -917
  61. package/compiler/ssg-build.ts +0 -422
  62. package/compiler/test/validate-test.ts +0 -104
  63. package/compiler/transform/classifyExpression.ts +0 -444
  64. package/compiler/transform/componentResolver.ts +0 -312
  65. package/compiler/transform/componentScriptTransformer.ts +0 -147
  66. package/compiler/transform/expressionTransformer.ts +0 -385
  67. package/compiler/transform/fragmentLowering.ts +0 -634
  68. package/compiler/transform/generateBindings.ts +0 -47
  69. package/compiler/transform/generateHTML.ts +0 -28
  70. package/compiler/transform/layoutProcessor.ts +0 -132
  71. package/compiler/transform/slotResolver.ts +0 -292
  72. package/compiler/transform/transformNode.ts +0 -126
  73. package/compiler/transform/transformTemplate.ts +0 -38
  74. package/compiler/validate/invariants.ts +0 -292
  75. package/compiler/validate/validateExpressions.ts +0 -168
  76. package/core/config/index.ts +0 -16
  77. package/core/config/loader.ts +0 -69
  78. package/core/config/types.ts +0 -89
  79. package/core/index.ts +0 -135
  80. package/core/lifecycle/index.ts +0 -49
  81. package/core/lifecycle/zen-mount.ts +0 -182
  82. package/core/lifecycle/zen-unmount.ts +0 -88
  83. package/core/plugins/index.ts +0 -7
  84. package/core/plugins/registry.ts +0 -81
  85. package/core/reactivity/index.ts +0 -54
  86. package/core/reactivity/tracking.ts +0 -167
  87. package/core/reactivity/zen-batch.ts +0 -57
  88. package/core/reactivity/zen-effect.ts +0 -139
  89. package/core/reactivity/zen-memo.ts +0 -146
  90. package/core/reactivity/zen-ref.ts +0 -52
  91. package/core/reactivity/zen-signal.ts +0 -121
  92. package/core/reactivity/zen-state.ts +0 -180
  93. package/core/reactivity/zen-untrack.ts +0 -44
  94. package/dist/cli.js +0 -11659
  95. package/dist/zen-build.js +0 -15633
  96. package/dist/zen-dev.js +0 -15633
  97. package/dist/zen-preview.js +0 -15633
  98. package/dist/zenith.js +0 -15633
  99. package/router/index.ts +0 -76
  100. package/router/manifest.ts +0 -314
  101. package/router/navigation/ZenLink.zen +0 -231
  102. package/router/navigation/index.ts +0 -78
  103. package/router/navigation/zen-link.ts +0 -584
  104. package/router/runtime.ts +0 -458
  105. package/router/types.ts +0 -168
  106. package/runtime/build.ts +0 -17
  107. package/runtime/bundle-generator.ts +0 -943
  108. package/runtime/client-runtime.ts +0 -549
  109. package/runtime/serve.ts +0 -93
  110. package/tsconfig.json +0 -28
@@ -1,312 +0,0 @@
1
- /**
2
- * Component Resolution
3
- *
4
- * Resolves component nodes in IR by inlining component templates with slot substitution.
5
- * Uses compound component pattern for named slots (Card.Header, Card.Footer).
6
- */
7
-
8
- import type { TemplateNode, ComponentNode, ElementNode, ZenIR, LoopContext, ComponentScriptIR } from '../ir/types'
9
- import type { ComponentMetadata } from '../discovery/componentDiscovery'
10
- import { extractSlotsFromChildren, resolveSlots } from './slotResolver'
11
- import { throwOrphanCompoundError, throwUnresolvedComponentError } from '../validate/invariants'
12
-
13
- // Track which components have been used (for style and script collection)
14
- const usedComponents = new Set<string>()
15
-
16
- /**
17
- * Resolve all component nodes in a template IR
18
- *
19
- * Recursively replaces ComponentNode instances with their resolved templates
20
- * Also collects styles AND scripts from used components and adds them to the IR
21
- */
22
- export function resolveComponentsInIR(
23
- ir: ZenIR,
24
- components: Map<string, ComponentMetadata>
25
- ): ZenIR {
26
- // Clear used components tracking for this compilation
27
- usedComponents.clear()
28
-
29
- // Resolve components in template nodes
30
- const resolvedNodes = resolveComponentsInNodes(ir.template.nodes, components)
31
-
32
- // Collect styles from all used components
33
- const componentStyles = Array.from(usedComponents)
34
- .map(name => components.get(name))
35
- .filter((meta): meta is ComponentMetadata => meta !== undefined && meta.styles.length > 0)
36
- .flatMap(meta => meta.styles.map(raw => ({ raw })))
37
-
38
- // Collect scripts from all used components (for bundling)
39
- const componentScripts: ComponentScriptIR[] = Array.from(usedComponents)
40
- .map(name => components.get(name))
41
- .filter((meta): meta is ComponentMetadata => meta !== undefined && meta.script !== null)
42
- .map(meta => ({
43
- name: meta.name,
44
- script: meta.script!,
45
- props: meta.props,
46
- scriptAttributes: meta.scriptAttributes || {}
47
- }))
48
-
49
- return {
50
- ...ir,
51
- template: {
52
- ...ir.template,
53
- nodes: resolvedNodes
54
- },
55
- // Merge component styles with existing page styles
56
- styles: [...ir.styles, ...componentStyles],
57
- // Add component scripts for bundling
58
- componentScripts: [...(ir.componentScripts || []), ...componentScripts]
59
- }
60
- }
61
-
62
- /**
63
- * Resolve component nodes in a list of template nodes
64
- */
65
- function resolveComponentsInNodes(
66
- nodes: TemplateNode[],
67
- components: Map<string, ComponentMetadata>,
68
- depth: number = 0
69
- ): TemplateNode[] {
70
- const resolved: TemplateNode[] = []
71
-
72
- for (const node of nodes) {
73
- const resolvedNode = resolveComponentNode(node, components, depth)
74
-
75
- if (Array.isArray(resolvedNode)) {
76
- resolved.push(...resolvedNode)
77
- } else {
78
- resolved.push(resolvedNode)
79
- }
80
- }
81
-
82
- return resolved
83
- }
84
-
85
- /**
86
- * Resolve a single component node
87
- *
88
- * If the node is a component, look up its definition and inline it with slot resolution.
89
- * Otherwise, recursively process children.
90
- */
91
- function resolveComponentNode(
92
- node: TemplateNode,
93
- components: Map<string, ComponentMetadata>,
94
- depth: number = 0
95
- ): TemplateNode | TemplateNode[] {
96
- // Handle component nodes
97
- if (node.type === 'component') {
98
- return resolveComponent(node, components, depth)
99
- }
100
-
101
- // Handle element nodes - recursively resolve children
102
- if (node.type === 'element') {
103
- const resolvedChildren = resolveComponentsInNodes(node.children, components, depth + 1)
104
-
105
- return {
106
- ...node,
107
- children: resolvedChildren
108
- }
109
- }
110
-
111
- // Text and expression nodes pass through unchanged
112
- return node
113
- }
114
-
115
- /**
116
- * Get base component name from compound name
117
- *
118
- * "Card.Header" -> "Card"
119
- * "Button" -> "Button"
120
- */
121
- function getBaseComponentName(name: string): string {
122
- const dotIndex = name.indexOf('.')
123
- return dotIndex > 0 ? name.slice(0, dotIndex) : name
124
- }
125
-
126
- /**
127
- * Check if a component name is a compound slot marker
128
- *
129
- * "Card.Header" -> true (if Card exists)
130
- * "Card" -> false
131
- * "Button" -> false
132
- */
133
- function isCompoundSlotMarker(name: string, components: Map<string, ComponentMetadata>): boolean {
134
- const dotIndex = name.indexOf('.')
135
- if (dotIndex <= 0) return false
136
-
137
- const baseName = name.slice(0, dotIndex)
138
- return components.has(baseName)
139
- }
140
-
141
- /**
142
- * Resolve a component by inlining its template with slot substitution
143
- */
144
- function resolveComponent(
145
- componentNode: ComponentNode,
146
- components: Map<string, ComponentMetadata>,
147
- depth: number = 0
148
- ): TemplateNode | TemplateNode[] {
149
- const componentName = componentNode.name
150
-
151
- // Check if this is a compound slot marker (Card.Header, Card.Footer)
152
- // These are handled by the parent component, not resolved directly
153
- // INV007: Orphan compound slot markers are a compile-time error
154
- if (isCompoundSlotMarker(componentName, components)) {
155
- throwOrphanCompoundError(
156
- componentName,
157
- getBaseComponentName(componentName),
158
- 'component', // filePath not available here, will be caught by caller
159
- componentNode.location.line,
160
- componentNode.location.column
161
- )
162
- }
163
-
164
- // Look up component metadata
165
- const componentMeta = components.get(componentName)
166
-
167
- // INV003: Unresolved components are a compile-time error
168
- if (!componentMeta) {
169
- throwUnresolvedComponentError(
170
- componentName,
171
- 'component', // filePath not available here, will be caught by caller
172
- componentNode.location.line,
173
- componentNode.location.column
174
- )
175
- }
176
-
177
- // Track this component as used (for style collection)
178
- usedComponents.add(componentName)
179
-
180
- // Extract slots from component children FIRST (before resolving nested components)
181
- // This preserves compound component structure (Card.Header, Card.Footer)
182
- // IMPORTANT: Pass parent's loopContext to preserve reactive scope
183
- // Components are purely structural - they don't create new reactive boundaries
184
- const slots = extractSlotsFromChildren(
185
- componentName,
186
- componentNode.children,
187
- componentNode.loopContext // Preserve parent's reactive scope
188
- )
189
-
190
- // Now resolve nested components within the extracted slot content
191
- const resolvedSlots = {
192
- default: resolveComponentsInNodes(slots.default, components, depth + 1),
193
- named: new Map<string, TemplateNode[]>(),
194
- parentLoopContext: slots.parentLoopContext // Carry through the parent scope
195
- }
196
-
197
- for (const [slotName, slotContent] of slots.named) {
198
- resolvedSlots.named.set(slotName, resolveComponentsInNodes(slotContent, components, depth + 1))
199
- }
200
-
201
- // Deep clone the component template nodes to avoid mutation
202
- const templateNodes = JSON.parse(JSON.stringify(componentMeta.nodes)) as TemplateNode[]
203
-
204
- // Resolve slots in component template
205
- const resolvedTemplate = resolveSlots(templateNodes, resolvedSlots)
206
-
207
- // Forward attributes from component usage to the root element
208
- // Also adds data-zen-component marker for hydration-driven instantiation
209
- const forwardedTemplate = forwardAttributesToRoot(
210
- resolvedTemplate,
211
- componentNode.attributes,
212
- componentNode.loopContext,
213
- componentMeta.hasScript ? componentName : undefined // Only mark if component has script
214
- )
215
-
216
- // Recursively resolve any nested components in the resolved template
217
- const fullyResolved = resolveComponentsInNodes(forwardedTemplate, components, depth + 1)
218
-
219
- return fullyResolved
220
- }
221
-
222
- /**
223
- * Forward attributes from component usage to the template's root element
224
- *
225
- * When using <Button onclick="increment">Text</Button>,
226
- * the onclick should be applied to the <button> element in Button.zen template.
227
- *
228
- * Also adds data-zen-component marker if componentName is provided,
229
- * enabling hydration-driven instantiation.
230
- */
231
- function forwardAttributesToRoot(
232
- nodes: TemplateNode[],
233
- attributes: ComponentNode['attributes'],
234
- loopContext?: LoopContext,
235
- componentName?: string // If provided, adds hydration marker
236
- ): TemplateNode[] {
237
- // Find the first non-text element (the root element)
238
- const rootIndex = nodes.findIndex(n => n.type === 'element')
239
- if (rootIndex === -1) {
240
- return nodes
241
- }
242
-
243
- const root = nodes[rootIndex] as ElementNode
244
-
245
- // Start with existing attributes
246
- const mergedAttributes = [...root.attributes]
247
-
248
- // Add component hydration marker if this component has a script
249
- if (componentName) {
250
- mergedAttributes.push({
251
- name: 'data-zen-component',
252
- value: componentName,
253
- location: { line: 0, column: 0 }
254
- })
255
- }
256
-
257
- // Forward attributes from component usage
258
- for (const attr of attributes) {
259
- const existingIndex = mergedAttributes.findIndex(a => a.name === attr.name)
260
-
261
- // Attach parent's loopContext to forwarded attributes to preserve reactivity
262
- const forwardedAttr = {
263
- ...attr,
264
- loopContext: attr.loopContext || loopContext
265
- }
266
-
267
- if (existingIndex >= 0) {
268
- const existingAttr = mergedAttributes[existingIndex]!
269
- // Special handling for class: merge classes
270
- if (attr.name === 'class' && typeof attr.value === 'string' && typeof existingAttr.value === 'string') {
271
- mergedAttributes[existingIndex] = {
272
- ...existingAttr,
273
- value: `${existingAttr.value} ${attr.value}`
274
- }
275
- } else {
276
- // Override other attributes
277
- mergedAttributes[existingIndex] = forwardedAttr
278
- }
279
- } else {
280
- // Add new attribute
281
- mergedAttributes.push(forwardedAttr)
282
- }
283
- }
284
-
285
- // Return updated nodes with root element having merged attributes
286
- return [
287
- ...nodes.slice(0, rootIndex),
288
- {
289
- ...root,
290
- attributes: mergedAttributes,
291
- loopContext: root.loopContext || loopContext
292
- },
293
- ...nodes.slice(rootIndex + 1)
294
- ]
295
- }
296
-
297
- /**
298
- * Check if an IR contains any component nodes
299
- */
300
- export function hasComponents(nodes: TemplateNode[]): boolean {
301
- function checkNode(node: TemplateNode): boolean {
302
- if (node.type === 'component') {
303
- return true
304
- }
305
- if (node.type === 'element') {
306
- return node.children.some(checkNode)
307
- }
308
- return false
309
- }
310
-
311
- return nodes.some(checkNode)
312
- }
@@ -1,147 +0,0 @@
1
- /**
2
- * Component Script Transformer
3
- *
4
- * Transforms component scripts for instance-scoped execution.
5
- * Uses namespace binding pattern for cleaner output:
6
- * const { signal, effect, onMount, ... } = __inst;
7
- *
8
- * Then rewrites zen* prefixed calls to unprefixed:
9
- * zenSignal(v) → signal(v)
10
- * zenEffect(fn) → effect(fn)
11
- * zenOnMount(fn) → onMount(fn)
12
- */
13
-
14
- import type { ComponentScriptIR } from '../ir/types'
15
-
16
- /**
17
- * Namespace bindings - destructured from the instance
18
- * This is added at the top of every component script
19
- */
20
- const NAMESPACE_BINDINGS = `const {
21
- signal, state, memo, effect, ref,
22
- batch, untrack, onMount, onUnmount
23
- } = __inst;`
24
-
25
- /**
26
- * Mapping of zen* prefixed names to unprefixed names
27
- * These get rewritten to use the destructured namespace
28
- */
29
- const ZEN_PREFIX_MAPPINGS: Record<string, string> = {
30
- 'zenSignal': 'signal',
31
- 'zenState': 'state',
32
- 'zenMemo': 'memo',
33
- 'zenEffect': 'effect',
34
- 'zenRef': 'ref',
35
- 'zenBatch': 'batch',
36
- 'zenUntrack': 'untrack',
37
- 'zenOnMount': 'onMount',
38
- 'zenOnUnmount': 'onUnmount',
39
- }
40
-
41
- /**
42
- * Transform a component's script content for instance-scoped execution
43
- *
44
- * @param componentName - Name of the component
45
- * @param scriptContent - Raw script content from the component
46
- * @param props - Declared prop names
47
- * @returns Transformed script ready for bundling
48
- */
49
- export function transformComponentScript(
50
- componentName: string,
51
- scriptContent: string,
52
- props: string[]
53
- ): string {
54
- let transformed = scriptContent
55
-
56
- // Strip import statements for .zen files (resolved at compile time)
57
- transformed = transformed.replace(
58
- /import\s+\w+\s+from\s+['"][^'"]*\.zen['"];?\s*/g,
59
- ''
60
- )
61
-
62
- // Strip any other relative imports (components are inlined)
63
- transformed = transformed.replace(
64
- /import\s+{[^}]*}\s+from\s+['"][^'"]+['"];?\s*/g,
65
- ''
66
- )
67
-
68
- // Rewrite zen* prefixed calls to unprefixed (uses namespace bindings)
69
- for (const [zenName, unprefixedName] of Object.entries(ZEN_PREFIX_MAPPINGS)) {
70
- // Match the zen* name as a standalone call
71
- const regex = new RegExp(`(?<!\\w)${zenName}\\s*\\(`, 'g')
72
- transformed = transformed.replace(regex, `${unprefixedName}(`)
73
- }
74
-
75
- return transformed.trim()
76
- }
77
-
78
- /**
79
- * Generate a component factory function
80
- *
81
- * IMPORTANT: Factories are PASSIVE - they are registered but NOT invoked here.
82
- * Instantiation is driven by the hydrator when it discovers component markers.
83
- *
84
- * @param componentName - Name of the component
85
- * @param transformedScript - Script content after hook rewriting
86
- * @param propNames - Declared prop names for destructuring
87
- * @returns Component factory registration code (NO eager instantiation)
88
- */
89
- export function generateComponentFactory(
90
- componentName: string,
91
- transformedScript: string,
92
- propNames: string[]
93
- ): string {
94
- const propsDestructure = propNames.length > 0
95
- ? `const { ${propNames.join(', ')} } = props || {};`
96
- : ''
97
-
98
- // Register factory only - NO instantiation
99
- // Hydrator will call instantiate() when it finds data-zen-component markers
100
- return `
101
- // Component Factory: ${componentName}
102
- // Instantiation is driven by hydrator, not by bundle load
103
- __zenith.defineComponent('${componentName}', function(props, rootElement) {
104
- const __inst = __zenith.createInstance('${componentName}', rootElement);
105
-
106
- // Namespace bindings (instance-scoped primitives)
107
- ${NAMESPACE_BINDINGS}
108
-
109
- ${propsDestructure}
110
-
111
- // Component script (instance-scoped)
112
- ${transformedScript}
113
-
114
- // Execute mount lifecycle (rootElement is already in DOM)
115
- __inst.mount();
116
-
117
- return __inst;
118
- });
119
- `
120
- }
121
-
122
- /**
123
- * Transform all component scripts from collected ComponentScriptIR
124
- *
125
- * @param componentScripts - Array of component script IRs
126
- * @returns Combined JavaScript code for all component factories
127
- */
128
- export function transformAllComponentScripts(
129
- componentScripts: ComponentScriptIR[]
130
- ): string {
131
- if (!componentScripts || componentScripts.length === 0) {
132
- return ''
133
- }
134
-
135
- const factories = componentScripts
136
- .filter(comp => comp.script && comp.script.trim().length > 0)
137
- .map(comp => {
138
- const transformed = transformComponentScript(
139
- comp.name,
140
- comp.script,
141
- comp.props
142
- )
143
- return generateComponentFactory(comp.name, transformed, comp.props)
144
- })
145
-
146
- return factories.join('\n')
147
- }