@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.
- package/CORE_CONTRACT.md +143 -0
- package/README.md +11 -31
- package/bin/zenith.js +68 -0
- package/package.json +41 -53
- package/src/config.js +134 -0
- package/src/core-template.js +30 -0
- package/src/errors.js +54 -0
- package/src/guards.js +61 -0
- package/src/hash.js +52 -0
- package/src/index.js +26 -0
- package/src/ir/index.js +1 -0
- package/src/order.js +69 -0
- package/src/path.js +131 -0
- package/src/schema.js +28 -0
- package/src/version.js +67 -0
- package/bin/zen-build.ts +0 -2
- package/bin/zen-dev.ts +0 -2
- package/bin/zen-preview.ts +0 -2
- package/bin/zenith.ts +0 -2
- package/cli/commands/add.ts +0 -37
- package/cli/commands/build.ts +0 -37
- package/cli/commands/create.ts +0 -702
- package/cli/commands/dev.ts +0 -335
- package/cli/commands/index.ts +0 -112
- package/cli/commands/preview.ts +0 -62
- package/cli/commands/remove.ts +0 -33
- package/cli/index.ts +0 -10
- package/cli/main.ts +0 -101
- package/cli/utils/branding.ts +0 -178
- package/cli/utils/content.ts +0 -112
- package/cli/utils/logger.ts +0 -46
- package/cli/utils/plugin-manager.ts +0 -114
- package/cli/utils/project.ts +0 -77
- package/compiler/README.md +0 -380
- package/compiler/build-analyzer.ts +0 -122
- package/compiler/css/index.ts +0 -317
- package/compiler/discovery/componentDiscovery.ts +0 -178
- package/compiler/discovery/layouts.ts +0 -70
- package/compiler/errors/compilerError.ts +0 -56
- package/compiler/finalize/finalizeOutput.ts +0 -192
- package/compiler/finalize/generateFinalBundle.ts +0 -82
- package/compiler/index.ts +0 -82
- package/compiler/ir/types.ts +0 -162
- package/compiler/output/types.ts +0 -34
- package/compiler/parse/detectMapExpressions.ts +0 -102
- package/compiler/parse/parseScript.ts +0 -46
- package/compiler/parse/parseTemplate.ts +0 -599
- package/compiler/parse/parseZenFile.ts +0 -66
- package/compiler/parse/scriptAnalysis.ts +0 -91
- package/compiler/parse/trackLoopContext.ts +0 -82
- package/compiler/runtime/dataExposure.ts +0 -317
- package/compiler/runtime/generateDOM.ts +0 -246
- package/compiler/runtime/generateHydrationBundle.ts +0 -407
- package/compiler/runtime/hydration.ts +0 -309
- package/compiler/runtime/navigation.ts +0 -432
- package/compiler/runtime/thinRuntime.ts +0 -160
- package/compiler/runtime/transformIR.ts +0 -363
- package/compiler/runtime/wrapExpression.ts +0 -95
- package/compiler/runtime/wrapExpressionWithLoop.ts +0 -83
- package/compiler/spa-build.ts +0 -917
- package/compiler/ssg-build.ts +0 -422
- package/compiler/test/validate-test.ts +0 -104
- package/compiler/transform/classifyExpression.ts +0 -444
- package/compiler/transform/componentResolver.ts +0 -312
- package/compiler/transform/componentScriptTransformer.ts +0 -147
- package/compiler/transform/expressionTransformer.ts +0 -385
- package/compiler/transform/fragmentLowering.ts +0 -634
- package/compiler/transform/generateBindings.ts +0 -47
- package/compiler/transform/generateHTML.ts +0 -28
- package/compiler/transform/layoutProcessor.ts +0 -132
- package/compiler/transform/slotResolver.ts +0 -292
- package/compiler/transform/transformNode.ts +0 -126
- package/compiler/transform/transformTemplate.ts +0 -38
- package/compiler/validate/invariants.ts +0 -292
- package/compiler/validate/validateExpressions.ts +0 -168
- package/core/config/index.ts +0 -16
- package/core/config/loader.ts +0 -69
- package/core/config/types.ts +0 -89
- package/core/index.ts +0 -135
- package/core/lifecycle/index.ts +0 -49
- package/core/lifecycle/zen-mount.ts +0 -182
- package/core/lifecycle/zen-unmount.ts +0 -88
- package/core/plugins/index.ts +0 -7
- package/core/plugins/registry.ts +0 -81
- package/core/reactivity/index.ts +0 -54
- package/core/reactivity/tracking.ts +0 -167
- package/core/reactivity/zen-batch.ts +0 -57
- package/core/reactivity/zen-effect.ts +0 -139
- package/core/reactivity/zen-memo.ts +0 -146
- package/core/reactivity/zen-ref.ts +0 -52
- package/core/reactivity/zen-signal.ts +0 -121
- package/core/reactivity/zen-state.ts +0 -180
- package/core/reactivity/zen-untrack.ts +0 -44
- package/dist/cli.js +0 -11659
- package/dist/zen-build.js +0 -15633
- package/dist/zen-dev.js +0 -15633
- package/dist/zen-preview.js +0 -15633
- package/dist/zenith.js +0 -15633
- package/router/index.ts +0 -76
- package/router/manifest.ts +0 -314
- package/router/navigation/ZenLink.zen +0 -231
- package/router/navigation/index.ts +0 -78
- package/router/navigation/zen-link.ts +0 -584
- package/router/runtime.ts +0 -458
- package/router/types.ts +0 -168
- package/runtime/build.ts +0 -17
- package/runtime/bundle-generator.ts +0 -943
- package/runtime/client-runtime.ts +0 -549
- package/runtime/serve.ts +0 -93
- 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
|
-
}
|