@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,36 @@
1
+ import type { ZenIR } from './ir/types';
2
+ import type { CompiledTemplate } from './output/types';
3
+ import type { FinalizedOutput } from './finalize/finalizeOutput';
4
+ import { parseZenFile } from './parseZenFile';
5
+ /**
6
+ * Compile a .zen file into IR and CompiledTemplate
7
+ */
8
+ export declare function compileZen(filePath: string): Promise<{
9
+ ir: ZenIR;
10
+ compiled: CompiledTemplate;
11
+ finalized?: FinalizedOutput;
12
+ }>;
13
+ /**
14
+ * Compile Zen source string into IR and CompiledTemplate
15
+ */
16
+ export declare function compileZenSource(source: string, filePath: string, options?: {
17
+ componentsDir?: string;
18
+ }): Promise<{
19
+ ir: ZenIR;
20
+ compiled: CompiledTemplate;
21
+ finalized?: FinalizedOutput;
22
+ }>;
23
+ export { parseZenFile };
24
+ export { discoverLayouts } from './discovery/layouts';
25
+ export { processLayout } from './transform/layoutProcessor';
26
+ export { bundlePageScript } from './bundler';
27
+ export { buildSSG } from './ssg-build';
28
+ export { buildSPA } from './spa-build';
29
+ export { generateBundleJS } from './runtime/bundle-generator';
30
+ export { generateRouteDefinition } from '@zenithbuild/router/manifest';
31
+ export { compileCss, compileCssAsync, resolveGlobalsCss } from './css';
32
+ export { loadZenithConfig } from './core/config/loader';
33
+ export { PluginRegistry, createPluginContext, getPluginDataByNamespace } from './core/plugins/registry';
34
+ export { createBridgeAPI, runPluginHooks, collectHookReturns, buildRuntimeEnvelope, clearHooks } from './core/plugins/bridge';
35
+ export type { HookContext } from './core/plugins/bridge';
36
+ export type { BundlePlan, ExpressionIR, ZenIR, TemplateNode } from './ir/types';
package/dist/index.js ADDED
@@ -0,0 +1,51 @@
1
+ import { readFileSync } from 'fs';
2
+ import { transformTemplate } from './transform/transformTemplate';
3
+ import { finalizeOutputOrThrow } from './finalize/finalizeOutput';
4
+ import { validateIr } from './validate/invariants';
5
+ import { parseZenFile } from './parseZenFile';
6
+ /**
7
+ * Compile a .zen file into IR and CompiledTemplate
8
+ */
9
+ export async function compileZen(filePath) {
10
+ const source = readFileSync(filePath, 'utf-8');
11
+ return compileZenSource(source, filePath);
12
+ }
13
+ /**
14
+ * Compile Zen source string into IR and CompiledTemplate
15
+ */
16
+ export async function compileZenSource(source, filePath, options) {
17
+ // Parse with native bridge
18
+ let ir = parseZenFile(filePath, source);
19
+ // Resolve components if components directory is provided
20
+ if (options?.componentsDir) {
21
+ const { discoverComponents } = require('./discovery/componentDiscovery');
22
+ const { resolveComponentsInIR } = require('./transform/componentResolver');
23
+ // Component resolution may throw InvariantError — let it propagate
24
+ const components = discoverComponents(options.componentsDir);
25
+ ir = resolveComponentsInIR(ir, components);
26
+ }
27
+ // Validate all compiler invariants after resolution
28
+ // Throws InvariantError if any invariant is violated
29
+ validateIr(ir);
30
+ const compiled = transformTemplate(ir);
31
+ try {
32
+ const finalized = await finalizeOutputOrThrow(ir, compiled);
33
+ return { ir, compiled, finalized };
34
+ }
35
+ catch (error) {
36
+ throw new Error(`Failed to finalize output for ${filePath}:\n${error.message}`);
37
+ }
38
+ }
39
+ export { parseZenFile };
40
+ // Feature exports
41
+ export { discoverLayouts } from './discovery/layouts';
42
+ export { processLayout } from './transform/layoutProcessor';
43
+ export { bundlePageScript } from './bundler';
44
+ export { buildSSG } from './ssg-build';
45
+ export { buildSPA } from './spa-build';
46
+ export { generateBundleJS } from './runtime/bundle-generator';
47
+ export { generateRouteDefinition } from '@zenithbuild/router/manifest';
48
+ export { compileCss, compileCssAsync, resolveGlobalsCss } from './css';
49
+ export { loadZenithConfig } from './core/config/loader';
50
+ export { PluginRegistry, createPluginContext, getPluginDataByNamespace } from './core/plugins/registry';
51
+ export { createBridgeAPI, runPluginHooks, collectHookReturns, buildRuntimeEnvelope, clearHooks } from './core/plugins/bridge';
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Zenith Intermediate Representation (IR)
3
+ *
4
+ * Phase 1: Parse & Extract
5
+ * This IR represents the parsed structure of a .zen file
6
+ * without any runtime execution or transformation.
7
+ */
8
+ /**
9
+ * Structured ES module import metadata
10
+ * Parsed from component scripts, used for deterministic bundling
11
+ */
12
+ export interface ScriptImport {
13
+ source: string;
14
+ specifiers: string;
15
+ typeOnly: boolean;
16
+ sideEffect: boolean;
17
+ }
18
+ /**
19
+ * Component Script IR - represents a component's script block
20
+ * Used for collecting and bundling component scripts
21
+ */
22
+ export type ComponentScriptIR = {
23
+ name: string;
24
+ script: string;
25
+ props: string[];
26
+ scriptAttributes: Record<string, string>;
27
+ imports: ScriptImport[];
28
+ instanceId?: string;
29
+ propValues?: Record<string, any>;
30
+ expressions?: ExpressionIR[];
31
+ };
32
+ export type ZenIR = {
33
+ filePath: string;
34
+ template: TemplateIR;
35
+ script: ScriptIR | null;
36
+ styles: StyleIR[];
37
+ componentScripts?: ComponentScriptIR[];
38
+ pageBindings?: string[];
39
+ };
40
+ export type TemplateIR = {
41
+ raw: string;
42
+ nodes: TemplateNode[];
43
+ expressions: ExpressionIR[];
44
+ };
45
+ export type TemplateNode = ElementNode | TextNode | ExpressionNode | ComponentNode | ConditionalFragmentNode | OptionalFragmentNode | LoopFragmentNode | DoctypeNode;
46
+ export type DoctypeNode = {
47
+ type: 'doctype';
48
+ name: string;
49
+ publicId: string;
50
+ systemId: string;
51
+ location: SourceLocation;
52
+ };
53
+ export type ElementNode = {
54
+ type: 'element';
55
+ tag: string;
56
+ attributes: AttributeIR[];
57
+ children: TemplateNode[];
58
+ location: SourceLocation;
59
+ loopContext?: LoopContext;
60
+ };
61
+ export type ComponentNode = {
62
+ type: 'component';
63
+ name: string;
64
+ attributes: AttributeIR[];
65
+ children: TemplateNode[];
66
+ location: SourceLocation;
67
+ loopContext?: LoopContext;
68
+ };
69
+ export type TextNode = {
70
+ type: 'text';
71
+ value: string;
72
+ location: SourceLocation;
73
+ };
74
+ export type ExpressionNode = {
75
+ type: 'expression';
76
+ expression: string;
77
+ location: SourceLocation;
78
+ loopContext?: LoopContext;
79
+ };
80
+ /**
81
+ * Conditional Fragment Node
82
+ *
83
+ * Represents ternary expressions with JSX branches: {cond ? <A /> : <B />}
84
+ *
85
+ * BOTH branches are compiled at compile time.
86
+ * Runtime toggles visibility — never creates DOM.
87
+ */
88
+ export type ConditionalFragmentNode = {
89
+ type: 'conditional-fragment';
90
+ condition: string;
91
+ consequent: TemplateNode[];
92
+ alternate: TemplateNode[];
93
+ location: SourceLocation;
94
+ loopContext?: LoopContext;
95
+ };
96
+ /**
97
+ * Optional Fragment Node
98
+ *
99
+ * Represents logical AND expressions with JSX: {cond && <A />}
100
+ *
101
+ * Fragment is compiled at compile time.
102
+ * Runtime toggles mount/unmount based on condition.
103
+ */
104
+ export type OptionalFragmentNode = {
105
+ type: 'optional-fragment';
106
+ condition: string;
107
+ fragment: TemplateNode[];
108
+ location: SourceLocation;
109
+ loopContext?: LoopContext;
110
+ };
111
+ /**
112
+ * Loop Fragment Node
113
+ *
114
+ * Represents .map() expressions with JSX body: {items.map(i => <li>...</li>)}
115
+ *
116
+ * Desugars to @for loop semantics at compile time.
117
+ * Body is compiled once, instantiated per item at runtime.
118
+ * Node identity is compiler-owned via stable keys.
119
+ */
120
+ export type LoopFragmentNode = {
121
+ type: 'loop-fragment';
122
+ source: string;
123
+ itemVar: string;
124
+ indexVar?: string;
125
+ body: TemplateNode[];
126
+ location: SourceLocation;
127
+ loopContext: LoopContext;
128
+ };
129
+ export type AttributeIR = {
130
+ name: string;
131
+ value: string | ExpressionIR;
132
+ location: SourceLocation;
133
+ loopContext?: LoopContext;
134
+ };
135
+ /**
136
+ * Loop context for expressions inside map iterations
137
+ * Phase 7: Tracks loop variables (e.g., todo, index) for expressions inside .map() calls
138
+ */
139
+ export type LoopContext = {
140
+ variables: string[];
141
+ mapSource?: string;
142
+ };
143
+ export type ExpressionIR = {
144
+ id: string;
145
+ code: string;
146
+ location: SourceLocation;
147
+ loopContext?: LoopContext;
148
+ };
149
+ export type ScriptIR = {
150
+ raw: string;
151
+ attributes: Record<string, string>;
152
+ };
153
+ export type StyleIR = {
154
+ raw: string;
155
+ };
156
+ export type SourceLocation = {
157
+ line: number;
158
+ column: number;
159
+ };
160
+ /**
161
+ * BundlePlan - Compiler-emitted contract for bundler execution
162
+ *
163
+ * If a plan exists, bundling MUST occur.
164
+ * If no plan exists, bundling MUST NOT occur.
165
+ * The bundler performs ZERO inference—it executes exactly what the compiler specifies.
166
+ */
167
+ export interface BundlePlan {
168
+ /** Entry point code (not a file path) */
169
+ entry: string;
170
+ /** Target platform */
171
+ platform: 'browser' | 'node';
172
+ /** Output format */
173
+ format: 'esm' | 'cjs';
174
+ /** Directories to resolve modules from */
175
+ resolveRoots: string[];
176
+ /** Virtual modules provided by the compiler */
177
+ virtualModules: Array<{
178
+ id: string;
179
+ code: string;
180
+ }>;
181
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Zenith Intermediate Representation (IR)
3
+ *
4
+ * Phase 1: Parse & Extract
5
+ * This IR represents the parsed structure of a .zen file
6
+ * without any runtime execution or transformation.
7
+ */
8
+ export {};
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Compiled Template Output Types
3
+ *
4
+ * Phase 2: Transform IR → Static HTML + Runtime Bindings
5
+ */
6
+ export type CompiledTemplate = {
7
+ html: string;
8
+ bindings: Binding[];
9
+ scripts: string | null;
10
+ styles: string[];
11
+ };
12
+ export type Binding = {
13
+ id: string;
14
+ type: 'text' | 'attribute' | 'conditional' | 'optional' | 'loop';
15
+ target: string;
16
+ expression: string;
17
+ location?: {
18
+ line: number;
19
+ column: number;
20
+ };
21
+ loopContext?: LoopContext;
22
+ };
23
+ /**
24
+ * Loop context for expressions inside map iterations
25
+ * Phase 7: Tracks loop variables for runtime setter generation
26
+ */
27
+ export type LoopContext = {
28
+ variables: string[];
29
+ mapSource?: string;
30
+ };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Compiled Template Output Types
3
+ *
4
+ * Phase 2: Transform IR → Static HTML + Runtime Bindings
5
+ */
6
+ export {};
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Map Expression Detection
3
+ *
4
+ * Phase 7: Detects .map() expressions and extracts loop context information
5
+ *
6
+ * This module analyzes expression code to detect map expressions like:
7
+ * - todoItems.map(todo => ...)
8
+ * - notifications.map((n, index) => ...)
9
+ *
10
+ * It extracts:
11
+ * - The array source (todoItems, notifications)
12
+ * - Loop variable names (todo, n, index)
13
+ * - The map body/template
14
+ */
15
+ import type { ExpressionIR } from '../ir/types';
16
+ /**
17
+ * Detected map expression information
18
+ */
19
+ export interface MapExpressionInfo {
20
+ isMap: boolean;
21
+ arraySource?: string;
22
+ itemVariable?: string;
23
+ indexVariable?: string;
24
+ mapBody?: string;
25
+ fullExpression?: string;
26
+ }
27
+ /**
28
+ * Detect if an expression is a map expression and extract loop context
29
+ *
30
+ * Patterns detected:
31
+ * - arraySource.map(item => body)
32
+ * - arraySource.map((item, index) => body)
33
+ * - arraySource.map(item => (body))
34
+ */
35
+ export declare function detectMapExpression(expr: ExpressionIR): MapExpressionInfo;
36
+ /**
37
+ * Extract loop variables from a map expression
38
+ * Returns array of variable names in order: [itemVariable, indexVariable?]
39
+ */
40
+ export declare function extractLoopVariables(mapInfo: MapExpressionInfo): string[];
41
+ /**
42
+ * Check if an expression references a loop variable
43
+ * Used to determine if an expression needs loop context
44
+ */
45
+ export declare function referencesLoopVariable(exprCode: string, loopVars: string[]): boolean;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Map Expression Detection
3
+ *
4
+ * Phase 7: Detects .map() expressions and extracts loop context information
5
+ *
6
+ * This module analyzes expression code to detect map expressions like:
7
+ * - todoItems.map(todo => ...)
8
+ * - notifications.map((n, index) => ...)
9
+ *
10
+ * It extracts:
11
+ * - The array source (todoItems, notifications)
12
+ * - Loop variable names (todo, n, index)
13
+ * - The map body/template
14
+ */
15
+ /**
16
+ * Detect if an expression is a map expression and extract loop context
17
+ *
18
+ * Patterns detected:
19
+ * - arraySource.map(item => body)
20
+ * - arraySource.map((item, index) => body)
21
+ * - arraySource.map(item => (body))
22
+ */
23
+ export function detectMapExpression(expr) {
24
+ const { code } = expr;
25
+ // Pattern: arraySource.map(item => body)
26
+ // Pattern: arraySource.map((item, index) => body)
27
+ // Pattern: arraySource.map(item => (body))
28
+ const mapPattern = /^(.+?)\.\s*map\s*\(\s*\(?([^)=,\s]+)(?:\s*,\s*([^)=,\s]+))?\s*\)?\s*=>\s*(.+?)\)?$/s;
29
+ const match = code.match(mapPattern);
30
+ if (!match) {
31
+ return { isMap: false };
32
+ }
33
+ const arraySource = match[1]?.trim();
34
+ const itemVariable = match[2]?.trim();
35
+ const indexVariable = match[3]?.trim();
36
+ const mapBody = match[4]?.trim();
37
+ if (!arraySource || !itemVariable || !mapBody) {
38
+ return { isMap: false };
39
+ }
40
+ return {
41
+ isMap: true,
42
+ arraySource,
43
+ itemVariable,
44
+ indexVariable,
45
+ mapBody,
46
+ fullExpression: code
47
+ };
48
+ }
49
+ /**
50
+ * Extract loop variables from a map expression
51
+ * Returns array of variable names in order: [itemVariable, indexVariable?]
52
+ */
53
+ export function extractLoopVariables(mapInfo) {
54
+ if (!mapInfo.isMap || !mapInfo.itemVariable) {
55
+ return [];
56
+ }
57
+ const vars = [mapInfo.itemVariable];
58
+ if (mapInfo.indexVariable) {
59
+ vars.push(mapInfo.indexVariable);
60
+ }
61
+ return vars;
62
+ }
63
+ /**
64
+ * Check if an expression references a loop variable
65
+ * Used to determine if an expression needs loop context
66
+ */
67
+ export function referencesLoopVariable(exprCode, loopVars) {
68
+ for (const loopVar of loopVars) {
69
+ // Match variable references: loopVar.property, loopVar, etc.
70
+ // Use word boundaries to avoid partial matches
71
+ const pattern = new RegExp(`\\b${loopVar}\\b`);
72
+ if (pattern.test(exprCode)) {
73
+ return true;
74
+ }
75
+ }
76
+ return false;
77
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Script Parser
3
+ *
4
+ * Extracts <script> blocks from .zen files
5
+ * Phase 1: Only extracts raw content, no evaluation
6
+ */
7
+ import type { ScriptIR } from '../ir/types';
8
+ export declare function parseScript(html: string): ScriptIR | null;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Script Parser
3
+ *
4
+ * Extracts <script> blocks from .zen files
5
+ * Phase 1: Only extracts raw content, no evaluation
6
+ */
7
+ export function parseScript(html) {
8
+ const scripts = [];
9
+ const attributes = {};
10
+ const scriptRegex = /<script\b([^>]*)>([\s\S]*?)<\/script>/gi;
11
+ let match;
12
+ while ((match = scriptRegex.exec(html)) !== null) {
13
+ const attrString = match[1] || '';
14
+ const content = match[2] || '';
15
+ // Parse attributes
16
+ const attrRegex = /([a-z0-9-]+)(?:=(?:"([^"]*)"|'([^']*)'|([^>\s]+)))?/gi;
17
+ let attrMatch;
18
+ while ((attrMatch = attrRegex.exec(attrString)) !== null) {
19
+ const name = attrMatch[1];
20
+ if (name) {
21
+ const value = attrMatch[2] || attrMatch[3] || attrMatch[4] || 'true';
22
+ attributes[name] = value;
23
+ }
24
+ }
25
+ if (content) {
26
+ scripts.push(content.trim());
27
+ }
28
+ }
29
+ if (scripts.length === 0) {
30
+ return null;
31
+ }
32
+ return {
33
+ raw: scripts.join('\n\n'),
34
+ attributes
35
+ };
36
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Template Parser
3
+ *
4
+ * Parses HTML template and extracts expressions
5
+ * Phase 1: Only extracts, does not execute
6
+ */
7
+ import type { TemplateIR } from '../ir/types';
8
+ /**
9
+ * Parse template from HTML string
10
+ */
11
+ export declare function parseTemplate(html: string, filePath: string): TemplateIR;