@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.
- package/LICENSE +21 -0
- package/README.md +30 -0
- package/dist/build-analyzer.d.ts +44 -0
- package/dist/build-analyzer.js +87 -0
- package/dist/bundler.d.ts +31 -0
- package/dist/bundler.js +86 -0
- package/dist/core/components/index.d.ts +9 -0
- package/dist/core/components/index.js +13 -0
- package/dist/core/config/index.d.ts +11 -0
- package/dist/core/config/index.js +10 -0
- package/dist/core/config/loader.d.ts +17 -0
- package/dist/core/config/loader.js +60 -0
- package/dist/core/config/types.d.ts +98 -0
- package/dist/core/config/types.js +32 -0
- package/dist/core/index.d.ts +7 -0
- package/dist/core/index.js +6 -0
- package/dist/core/lifecycle/index.d.ts +16 -0
- package/dist/core/lifecycle/index.js +19 -0
- package/dist/core/lifecycle/zen-mount.d.ts +66 -0
- package/dist/core/lifecycle/zen-mount.js +151 -0
- package/dist/core/lifecycle/zen-unmount.d.ts +54 -0
- package/dist/core/lifecycle/zen-unmount.js +76 -0
- package/dist/core/plugins/bridge.d.ts +116 -0
- package/dist/core/plugins/bridge.js +121 -0
- package/dist/core/plugins/index.d.ts +6 -0
- package/dist/core/plugins/index.js +6 -0
- package/dist/core/plugins/registry.d.ts +67 -0
- package/dist/core/plugins/registry.js +113 -0
- package/dist/core/reactivity/index.d.ts +30 -0
- package/dist/core/reactivity/index.js +33 -0
- package/dist/core/reactivity/tracking.d.ts +74 -0
- package/dist/core/reactivity/tracking.js +136 -0
- package/dist/core/reactivity/zen-batch.d.ts +45 -0
- package/dist/core/reactivity/zen-batch.js +54 -0
- package/dist/core/reactivity/zen-effect.d.ts +48 -0
- package/dist/core/reactivity/zen-effect.js +98 -0
- package/dist/core/reactivity/zen-memo.d.ts +43 -0
- package/dist/core/reactivity/zen-memo.js +100 -0
- package/dist/core/reactivity/zen-ref.d.ts +44 -0
- package/dist/core/reactivity/zen-ref.js +34 -0
- package/dist/core/reactivity/zen-signal.d.ts +48 -0
- package/dist/core/reactivity/zen-signal.js +84 -0
- package/dist/core/reactivity/zen-state.d.ts +35 -0
- package/dist/core/reactivity/zen-state.js +147 -0
- package/dist/core/reactivity/zen-untrack.d.ts +38 -0
- package/dist/core/reactivity/zen-untrack.js +41 -0
- package/dist/css/index.d.ts +73 -0
- package/dist/css/index.js +246 -0
- package/dist/discovery/componentDiscovery.d.ts +42 -0
- package/dist/discovery/componentDiscovery.js +56 -0
- package/dist/discovery/layouts.d.ts +13 -0
- package/dist/discovery/layouts.js +41 -0
- package/dist/errors/compilerError.d.ts +31 -0
- package/dist/errors/compilerError.js +51 -0
- package/dist/finalize/finalizeOutput.d.ts +32 -0
- package/dist/finalize/finalizeOutput.js +62 -0
- package/dist/finalize/generateFinalBundle.d.ts +24 -0
- package/dist/finalize/generateFinalBundle.js +68 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +51 -0
- package/dist/ir/types.d.ts +181 -0
- package/dist/ir/types.js +8 -0
- package/dist/output/types.d.ts +30 -0
- package/dist/output/types.js +6 -0
- package/dist/parse/detectMapExpressions.d.ts +45 -0
- package/dist/parse/detectMapExpressions.js +77 -0
- package/dist/parse/parseScript.d.ts +8 -0
- package/dist/parse/parseScript.js +36 -0
- package/dist/parse/parseTemplate.d.ts +11 -0
- package/dist/parse/parseTemplate.js +487 -0
- package/dist/parse/parseZenFile.d.ts +11 -0
- package/dist/parse/parseZenFile.js +50 -0
- package/dist/parse/scriptAnalysis.d.ts +25 -0
- package/dist/parse/scriptAnalysis.js +60 -0
- package/dist/parse/trackLoopContext.d.ts +20 -0
- package/dist/parse/trackLoopContext.js +62 -0
- package/dist/parseZenFile.d.ts +10 -0
- package/dist/parseZenFile.js +55 -0
- package/dist/runtime/analyzeAndEmit.d.ts +20 -0
- package/dist/runtime/analyzeAndEmit.js +70 -0
- package/dist/runtime/build.d.ts +6 -0
- package/dist/runtime/build.js +13 -0
- package/dist/runtime/bundle-generator.d.ts +27 -0
- package/dist/runtime/bundle-generator.js +1263 -0
- package/dist/runtime/client-runtime.d.ts +41 -0
- package/dist/runtime/client-runtime.js +397 -0
- package/dist/runtime/dataExposure.d.ts +52 -0
- package/dist/runtime/dataExposure.js +227 -0
- package/dist/runtime/generateDOM.d.ts +21 -0
- package/dist/runtime/generateDOM.js +194 -0
- package/dist/runtime/generateHydrationBundle.d.ts +15 -0
- package/dist/runtime/generateHydrationBundle.js +399 -0
- package/dist/runtime/hydration.d.ts +53 -0
- package/dist/runtime/hydration.js +271 -0
- package/dist/runtime/navigation.d.ts +58 -0
- package/dist/runtime/navigation.js +372 -0
- package/dist/runtime/serve.d.ts +13 -0
- package/dist/runtime/serve.js +76 -0
- package/dist/runtime/thinRuntime.d.ts +23 -0
- package/dist/runtime/thinRuntime.js +158 -0
- package/dist/runtime/transformIR.d.ts +19 -0
- package/dist/runtime/transformIR.js +285 -0
- package/dist/runtime/wrapExpression.d.ts +24 -0
- package/dist/runtime/wrapExpression.js +76 -0
- package/dist/runtime/wrapExpressionWithLoop.d.ts +17 -0
- package/dist/runtime/wrapExpressionWithLoop.js +75 -0
- package/dist/spa-build.d.ts +26 -0
- package/dist/spa-build.js +866 -0
- package/dist/ssg-build.d.ts +32 -0
- package/dist/ssg-build.js +408 -0
- package/dist/test/analyze-emit.test.d.ts +1 -0
- package/dist/test/analyze-emit.test.js +88 -0
- package/dist/test/bundler-contract.test.d.ts +1 -0
- package/dist/test/bundler-contract.test.js +137 -0
- package/dist/test/compiler-authority.test.d.ts +1 -0
- package/dist/test/compiler-authority.test.js +90 -0
- package/dist/test/component-instance-test.d.ts +1 -0
- package/dist/test/component-instance-test.js +115 -0
- package/dist/test/error-native-bridge.test.d.ts +1 -0
- package/dist/test/error-native-bridge.test.js +51 -0
- package/dist/test/error-serialization.test.d.ts +1 -0
- package/dist/test/error-serialization.test.js +38 -0
- package/dist/test/macro-inlining.test.d.ts +1 -0
- package/dist/test/macro-inlining.test.js +178 -0
- package/dist/test/validate-test.d.ts +6 -0
- package/dist/test/validate-test.js +95 -0
- package/dist/transform/classifyExpression.d.ts +46 -0
- package/dist/transform/classifyExpression.js +354 -0
- package/dist/transform/componentResolver.d.ts +15 -0
- package/dist/transform/componentResolver.js +30 -0
- package/dist/transform/expressionTransformer.d.ts +19 -0
- package/dist/transform/expressionTransformer.js +333 -0
- package/dist/transform/fragmentLowering.d.ts +25 -0
- package/dist/transform/fragmentLowering.js +468 -0
- package/dist/transform/layoutProcessor.d.ts +5 -0
- package/dist/transform/layoutProcessor.js +34 -0
- package/dist/transform/transformTemplate.d.ts +11 -0
- package/dist/transform/transformTemplate.js +33 -0
- package/dist/validate/invariants.d.ts +23 -0
- package/dist/validate/invariants.js +55 -0
- package/native/compiler-native/compiler-native.node +0 -0
- package/native/compiler-native/index.d.ts +113 -0
- package/native/compiler-native/index.js +19 -0
- package/native/compiler-native/package.json +19 -0
- package/package.json +49 -0
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|
package/dist/ir/types.js
ADDED
|
@@ -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,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,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;
|