@zenithbuild/core 1.2.2 → 1.2.3
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/README.md +20 -19
- package/cli/commands/add.ts +2 -2
- package/cli/commands/build.ts +2 -3
- package/cli/commands/dev.ts +93 -73
- package/cli/commands/index.ts +1 -1
- package/cli/commands/preview.ts +1 -1
- package/cli/commands/remove.ts +2 -2
- package/cli/index.ts +1 -1
- package/cli/main.ts +1 -1
- package/cli/utils/logger.ts +1 -1
- package/cli/utils/plugin-manager.ts +1 -1
- package/cli/utils/project.ts +4 -4
- package/core/components/ErrorPage.zen +218 -0
- package/core/components/index.ts +15 -0
- package/core/config.ts +1 -0
- package/core/index.ts +29 -0
- package/dist/compiler-native-frej59m4.node +0 -0
- package/dist/core/compiler-native-frej59m4.node +0 -0
- package/dist/core/index.js +6293 -0
- package/dist/runtime/lifecycle/index.js +1 -0
- package/dist/runtime/reactivity/index.js +1 -0
- package/dist/zen-build.js +1 -20118
- package/dist/zen-dev.js +1 -20118
- package/dist/zen-preview.js +1 -20118
- package/dist/zenith.js +1 -20118
- package/package.json +11 -20
- 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 -242
- 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 -83
- package/compiler/ir/types.ts +0 -174
- package/compiler/output/types.ts +0 -48
- package/compiler/parse/detectMapExpressions.ts +0 -102
- package/compiler/parse/importTypes.ts +0 -78
- package/compiler/parse/parseImports.ts +0 -309
- package/compiler/parse/parseScript.ts +0 -46
- package/compiler/parse/parseTemplate.ts +0 -628
- 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 -332
- package/compiler/runtime/generateDOM.ts +0 -255
- 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 -406
- package/compiler/runtime/wrapExpression.ts +0 -114
- package/compiler/runtime/wrapExpressionWithLoop.ts +0 -97
- package/compiler/spa-build.ts +0 -917
- package/compiler/ssg-build.ts +0 -486
- package/compiler/test/component-stacking.test.ts +0 -365
- package/compiler/test/map-lowering.test.ts +0 -130
- package/compiler/test/validate-test.ts +0 -104
- package/compiler/transform/classifyExpression.ts +0 -444
- package/compiler/transform/componentResolver.ts +0 -350
- package/compiler/transform/componentScriptTransformer.ts +0 -303
- package/compiler/transform/expressionTransformer.ts +0 -385
- package/compiler/transform/fragmentLowering.ts +0 -819
- package/compiler/transform/generateBindings.ts +0 -68
- 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 -314
- 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 -18
- package/core/config/loader.ts +0 -69
- package/core/config/types.ts +0 -119
- package/core/plugins/bridge.ts +0 -193
- package/core/plugins/index.ts +0 -7
- package/core/plugins/registry.ts +0 -126
- package/dist/cli.js +0 -11675
- package/runtime/build.ts +0 -17
- package/runtime/bundle-generator.ts +0 -1266
- package/runtime/client-runtime.ts +0 -891
- package/runtime/serve.ts +0 -93
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Script Analysis Utilities
|
|
3
|
-
*
|
|
4
|
-
* Extracts state and prop declarations from <script> blocks
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
export interface StateInfo {
|
|
8
|
-
name: string
|
|
9
|
-
value: string
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Extract state declarations: state name = value
|
|
14
|
-
*/
|
|
15
|
-
export function extractStateDeclarations(script: string): Map<string, string> {
|
|
16
|
-
const states = new Map<string, string>()
|
|
17
|
-
const statePattern = /state\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*([^;]+?)(?:\s*;|\s*$)/gm
|
|
18
|
-
let match
|
|
19
|
-
|
|
20
|
-
while ((match = statePattern.exec(script)) !== null) {
|
|
21
|
-
if (match[1] && match[2]) {
|
|
22
|
-
states.set(match[1], match[2].trim())
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return states
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Extract prop declarations: export let props: Props;
|
|
31
|
-
*/
|
|
32
|
-
export function extractProps(script: string): string[] {
|
|
33
|
-
const props: string[] = []
|
|
34
|
-
const propPattern = /export\s+let\s+props(?:\s*:\s*([^;]+))?[ \t]*;?/g
|
|
35
|
-
let match
|
|
36
|
-
|
|
37
|
-
while ((match = propPattern.exec(script)) !== null) {
|
|
38
|
-
if (!props.includes('props')) {
|
|
39
|
-
props.push('props')
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return props
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Transform script by removing state and prop declarations
|
|
48
|
-
* Also strips .zen imports (resolved at compile time) and other compile-time-only imports
|
|
49
|
-
*/
|
|
50
|
-
export function transformStateDeclarations(script: string): string {
|
|
51
|
-
let transformed = script
|
|
52
|
-
|
|
53
|
-
// Remove state declarations (state count = 0)
|
|
54
|
-
transformed = transformed.replace(/state\s+([a-zA-Z_$][a-zA-Z0-9_$]*)[ \t]*=[ \t]*([^;]+?)(?:[ \t]*;|\s*$)/gm, '')
|
|
55
|
-
|
|
56
|
-
// Remove export let props (legacy)
|
|
57
|
-
transformed = transformed.replace(/export\s+let\s+props(?:\s*:\s*([^;]+))?\s*;?[ \t]*/g, '')
|
|
58
|
-
|
|
59
|
-
// Remove type/interface Props (carefully handling comments)
|
|
60
|
-
// We search for the start of the word 'type' or 'interface' and match until the closing brace
|
|
61
|
-
transformed = transformed.replace(/(?:type|interface)\s+Props\s*=?\s*\{[^}]*(?:\{[^}]*\}[^}]*)*\}[ \t]*;?/gs, '')
|
|
62
|
-
|
|
63
|
-
// Remove zenith/runtime imports
|
|
64
|
-
transformed = transformed.replace(/import\s+{[^}]+}\s+from\s+['"]zenith\/runtime['"]\s*;?[ \t]*/g, '')
|
|
65
|
-
|
|
66
|
-
// Remove .zen file imports (resolved at compile time)
|
|
67
|
-
// Matches: import Name from '.../file.zen'; or import Name from '.../file.zen'
|
|
68
|
-
transformed = transformed.replace(/import\s+\w+\s+from\s+['"][^'"]*\.zen['"];?\s*/g, '')
|
|
69
|
-
|
|
70
|
-
// Remove relative imports with destructuring (components are inlined)
|
|
71
|
-
transformed = transformed.replace(/import\s+{[^}]*}\s+from\s+['"][^'"]+\.zen['"];?\s*/g, '')
|
|
72
|
-
|
|
73
|
-
// Transform zenith:content imports to global lookups
|
|
74
|
-
transformed = transformed.replace(
|
|
75
|
-
/import\s*{\s*([^}]+)\s*}\s*from\s*['"]zenith:content['"]\s*;?/g,
|
|
76
|
-
(_, imports) => `const { ${imports.trim()} } = window.__zenith;`
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
return transformed.trim()
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Inject props into a setup script as top-level variables
|
|
84
|
-
*/
|
|
85
|
-
export function injectPropsIntoSetup(script: string, props: Record<string, any>): string {
|
|
86
|
-
const propDeclarations = Object.entries(props)
|
|
87
|
-
.map(([key, value]) => `const ${key} = ${typeof value === 'string' ? `'${value}'` : JSON.stringify(value)};`)
|
|
88
|
-
.join('\n')
|
|
89
|
-
|
|
90
|
-
return `${propDeclarations}\n\n${script}`
|
|
91
|
-
}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Loop Context Tracking
|
|
3
|
-
*
|
|
4
|
-
* Phase 7: Utilities for tracking and propagating loop context through the parse tree
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { LoopContext, ExpressionIR } from '../ir/types'
|
|
8
|
-
import { detectMapExpression, referencesLoopVariable } from './detectMapExpressions'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Check if an expression should have loop context attached
|
|
12
|
-
* Returns the loop context if the expression references loop variables
|
|
13
|
-
*/
|
|
14
|
-
export function shouldAttachLoopContext(
|
|
15
|
-
expr: ExpressionIR,
|
|
16
|
-
parentLoopContext?: LoopContext
|
|
17
|
-
): LoopContext | undefined {
|
|
18
|
-
if (!parentLoopContext) {
|
|
19
|
-
return undefined
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Check if this expression references any loop variables
|
|
23
|
-
if (referencesLoopVariable(expr.code, parentLoopContext.variables)) {
|
|
24
|
-
return parentLoopContext
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return undefined
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Merge loop contexts for nested loops
|
|
32
|
-
* Inner loops inherit outer loop variables
|
|
33
|
-
*/
|
|
34
|
-
export function mergeLoopContext(
|
|
35
|
-
outer?: LoopContext,
|
|
36
|
-
inner?: LoopContext
|
|
37
|
-
): LoopContext | undefined {
|
|
38
|
-
if (!inner) {
|
|
39
|
-
return outer
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (!outer) {
|
|
43
|
-
return inner
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Merge variables: outer variables come first, then inner
|
|
47
|
-
// This allows expressions to reference both outer and inner loop variables
|
|
48
|
-
return {
|
|
49
|
-
variables: [...outer.variables, ...inner.variables],
|
|
50
|
-
mapSource: inner.mapSource || outer.mapSource
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Detect if an expression is a map expression and extract its loop context
|
|
56
|
-
*/
|
|
57
|
-
export function extractLoopContextFromExpression(expr: ExpressionIR): LoopContext | undefined {
|
|
58
|
-
const mapInfo = detectMapExpression(expr)
|
|
59
|
-
|
|
60
|
-
if (!mapInfo.isMap) {
|
|
61
|
-
return undefined
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// extractLoopVariables expects a MapExpressionInfo, not a string
|
|
65
|
-
const variables: string[] = []
|
|
66
|
-
if (mapInfo.itemVariable) {
|
|
67
|
-
variables.push(mapInfo.itemVariable)
|
|
68
|
-
}
|
|
69
|
-
if (mapInfo.indexVariable) {
|
|
70
|
-
variables.push(mapInfo.indexVariable)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (variables.length === 0) {
|
|
74
|
-
return undefined
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
variables,
|
|
79
|
-
mapSource: mapInfo.arraySource
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
@@ -1,332 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Explicit Data Exposure Analysis
|
|
3
|
-
*
|
|
4
|
-
* Phase 6: Analyzes expressions to detect data dependencies and ensure
|
|
5
|
-
* all data references are explicit (loader, props, stores) rather than implicit globals
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { ExpressionIR } from '../ir/types'
|
|
9
|
-
import { CompilerError } from '../errors/compilerError'
|
|
10
|
-
import { transformExpressionJSX } from '../transform/expressionTransformer'
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Data dependency information for an expression
|
|
14
|
-
*/
|
|
15
|
-
export interface ExpressionDataDependencies {
|
|
16
|
-
expressionId: string
|
|
17
|
-
usesLoaderData: boolean
|
|
18
|
-
usesProps: boolean
|
|
19
|
-
usesStores: boolean
|
|
20
|
-
usesState: boolean
|
|
21
|
-
loaderProperties: string[] // e.g., ['user', 'user.name']
|
|
22
|
-
propNames: string[] // e.g., ['title', 'showWelcome']
|
|
23
|
-
storeNames: string[] // e.g., ['cart', 'notifications']
|
|
24
|
-
stateProperties: string[] // e.g., ['count', 'isLoading']
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Analyze an expression to detect its data dependencies
|
|
29
|
-
*
|
|
30
|
-
* This is a simple heuristic-based analyzer that looks for patterns like:
|
|
31
|
-
* - user.name, user.email → loader data
|
|
32
|
-
* - props.title, props.showWelcome → props
|
|
33
|
-
* - stores.cart, stores.notifications → stores
|
|
34
|
-
* - count, isLoading → state (top-level properties)
|
|
35
|
-
*/
|
|
36
|
-
export function analyzeExpressionDependencies(
|
|
37
|
-
expr: ExpressionIR,
|
|
38
|
-
declaredLoaderProps: string[] = [],
|
|
39
|
-
declaredProps: string[] = [],
|
|
40
|
-
declaredStores: string[] = []
|
|
41
|
-
): ExpressionDataDependencies {
|
|
42
|
-
const { id, code } = expr
|
|
43
|
-
|
|
44
|
-
const dependencies: ExpressionDataDependencies = {
|
|
45
|
-
expressionId: id,
|
|
46
|
-
usesLoaderData: false,
|
|
47
|
-
usesProps: false,
|
|
48
|
-
usesStores: false,
|
|
49
|
-
usesState: false,
|
|
50
|
-
loaderProperties: [],
|
|
51
|
-
propNames: [],
|
|
52
|
-
storeNames: [],
|
|
53
|
-
stateProperties: []
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Simple pattern matching (for Phase 6 - can be enhanced with proper AST parsing later)
|
|
57
|
-
|
|
58
|
-
// Check for loader data references (loaderData.property or direct property access)
|
|
59
|
-
// We assume properties not starting with props/stores/state are loader data
|
|
60
|
-
const loaderPattern = /\b(loaderData\.(\w+(?:\.\w+)*)|(?<!props\.|stores\.|state\.)(\w+)\.(\w+))/g
|
|
61
|
-
let match
|
|
62
|
-
|
|
63
|
-
// Check for explicit loaderData references
|
|
64
|
-
if (/loaderData\./.test(code)) {
|
|
65
|
-
dependencies.usesLoaderData = true
|
|
66
|
-
while ((match = loaderPattern.exec(code)) !== null) {
|
|
67
|
-
if (match[1]?.startsWith('loaderData.')) {
|
|
68
|
-
const propPath = match[1].replace('loaderData.', '')
|
|
69
|
-
if (!dependencies.loaderProperties.includes(propPath)) {
|
|
70
|
-
dependencies.loaderProperties.push(propPath)
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Check for props references
|
|
77
|
-
const propsPattern = /\bprops\.(\w+)(?:\.(\w+))*/g
|
|
78
|
-
if (/props\./.test(code)) {
|
|
79
|
-
dependencies.usesProps = true
|
|
80
|
-
while ((match = propsPattern.exec(code)) !== null) {
|
|
81
|
-
const propName = match[1]
|
|
82
|
-
if (propName && !dependencies.propNames.includes(propName)) {
|
|
83
|
-
dependencies.propNames.push(propName)
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Check for stores references
|
|
89
|
-
const storesPattern = /\bstores\.(\w+)(?:\.(\w+))*/g
|
|
90
|
-
if (/stores\./.test(code)) {
|
|
91
|
-
dependencies.usesStores = true
|
|
92
|
-
while ((match = storesPattern.exec(code)) !== null) {
|
|
93
|
-
const storeName = match[1]
|
|
94
|
-
if (storeName && !dependencies.storeNames.includes(storeName)) {
|
|
95
|
-
dependencies.storeNames.push(storeName)
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Check for state references (top-level properties)
|
|
101
|
-
// Simple identifiers that aren't part of props/stores/loaderData paths
|
|
102
|
-
const identifierPattern = /\b([a-zA-Z_$][a-zA-Z0-9_$]*)\b/g
|
|
103
|
-
const reserved = ['props', 'stores', 'loaderData', 'state', 'true', 'false', 'null', 'undefined', 'this', 'window']
|
|
104
|
-
|
|
105
|
-
const identifiers = new Set<string>()
|
|
106
|
-
while ((match = identifierPattern.exec(code)) !== null) {
|
|
107
|
-
const ident = match[1]
|
|
108
|
-
if (ident && !reserved.includes(ident) && !ident.includes('.')) {
|
|
109
|
-
identifiers.add(ident)
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// If we have identifiers, check if they are props or state
|
|
114
|
-
if (identifiers.size > 0) {
|
|
115
|
-
const propIdents: string[] = []
|
|
116
|
-
const stateIdents: string[] = []
|
|
117
|
-
|
|
118
|
-
for (const ident of identifiers) {
|
|
119
|
-
if (declaredProps.includes(ident)) {
|
|
120
|
-
propIdents.push(ident)
|
|
121
|
-
} else {
|
|
122
|
-
stateIdents.push(ident)
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (propIdents.length > 0) {
|
|
127
|
-
dependencies.usesProps = true
|
|
128
|
-
dependencies.propNames = [...new Set([...dependencies.propNames, ...propIdents])]
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (stateIdents.length > 0) {
|
|
132
|
-
dependencies.usesState = true
|
|
133
|
-
dependencies.stateProperties = Array.from(new Set([...dependencies.stateProperties, ...stateIdents]))
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return dependencies
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Validate that all referenced data exists
|
|
142
|
-
*/
|
|
143
|
-
export function validateDataDependencies(
|
|
144
|
-
dependencies: ExpressionDataDependencies,
|
|
145
|
-
filePath: string,
|
|
146
|
-
declaredLoaderProps: string[] = [],
|
|
147
|
-
declaredProps: string[] = [],
|
|
148
|
-
declaredStores: string[] = []
|
|
149
|
-
): void {
|
|
150
|
-
const errors: CompilerError[] = []
|
|
151
|
-
|
|
152
|
-
// Validate loader data properties
|
|
153
|
-
if (dependencies.usesLoaderData && dependencies.loaderProperties.length > 0) {
|
|
154
|
-
// For Phase 6, we'll allow any loader property (can be enhanced with type checking later)
|
|
155
|
-
// Just warn if property path is suspicious
|
|
156
|
-
for (const prop of dependencies.loaderProperties) {
|
|
157
|
-
if (!/^[a-zA-Z_$][a-zA-Z0-9_$.]*$/.test(prop)) {
|
|
158
|
-
errors.push(new CompilerError(
|
|
159
|
-
`Invalid loader data property reference: ${prop}`,
|
|
160
|
-
filePath,
|
|
161
|
-
0,
|
|
162
|
-
0
|
|
163
|
-
))
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Validate props
|
|
169
|
-
if (dependencies.usesProps && dependencies.propNames.length > 0) {
|
|
170
|
-
for (const propName of dependencies.propNames) {
|
|
171
|
-
if (declaredProps.length > 0 && !declaredProps.includes(propName)) {
|
|
172
|
-
// This is a warning, not an error - props might be passed at runtime
|
|
173
|
-
console.warn(`[Zenith] Prop "${propName}" referenced but not declared in component`)
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Validate stores
|
|
179
|
-
if (dependencies.usesStores && dependencies.storeNames.length > 0) {
|
|
180
|
-
for (const storeName of dependencies.storeNames) {
|
|
181
|
-
if (declaredStores.length > 0 && !declaredStores.includes(storeName)) {
|
|
182
|
-
errors.push(new CompilerError(
|
|
183
|
-
`Store "${storeName}" referenced but not imported or declared`,
|
|
184
|
-
filePath,
|
|
185
|
-
0,
|
|
186
|
-
0
|
|
187
|
-
))
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (errors.length > 0) {
|
|
193
|
-
throw errors[0] // Throw first error (can be enhanced to collect all)
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Transform expression code to use explicit data arguments
|
|
199
|
-
*
|
|
200
|
-
* Converts patterns like:
|
|
201
|
-
* - user.name → loaderData.user.name
|
|
202
|
-
* - title → props.title (if declared as prop)
|
|
203
|
-
* - cart.items → stores.cart.items
|
|
204
|
-
*/
|
|
205
|
-
export function transformExpressionCode(
|
|
206
|
-
code: string,
|
|
207
|
-
dependencies: ExpressionDataDependencies,
|
|
208
|
-
declaredProps: string[] = []
|
|
209
|
-
): string {
|
|
210
|
-
let transformed = code
|
|
211
|
-
|
|
212
|
-
// For Phase 6, we keep the code as-is but ensure expressions
|
|
213
|
-
// receive the right arguments. The actual transformation happens
|
|
214
|
-
// in the expression wrapper function signature.
|
|
215
|
-
|
|
216
|
-
// However, if the code references properties directly (without loaderData/props/stores prefix),
|
|
217
|
-
// we need to assume they're state properties (backwards compatibility)
|
|
218
|
-
|
|
219
|
-
return transformed
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Generate expression wrapper with explicit data arguments
|
|
224
|
-
*/
|
|
225
|
-
export function generateExplicitExpressionWrapper(
|
|
226
|
-
expr: ExpressionIR,
|
|
227
|
-
dependencies: ExpressionDataDependencies
|
|
228
|
-
): string {
|
|
229
|
-
const { id, code } = expr
|
|
230
|
-
|
|
231
|
-
// Build function signature based on dependencies
|
|
232
|
-
const params: string[] = ['state']
|
|
233
|
-
|
|
234
|
-
if (dependencies.usesLoaderData) {
|
|
235
|
-
params.push('loaderData')
|
|
236
|
-
}
|
|
237
|
-
if (dependencies.usesProps) {
|
|
238
|
-
params.push('props')
|
|
239
|
-
}
|
|
240
|
-
if (dependencies.usesStores) {
|
|
241
|
-
params.push('stores')
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const paramList = params.join(', ')
|
|
245
|
-
|
|
246
|
-
// Build evaluation context
|
|
247
|
-
const contextParts: string[] = []
|
|
248
|
-
|
|
249
|
-
if (dependencies.usesLoaderData) {
|
|
250
|
-
contextParts.push('loaderData')
|
|
251
|
-
}
|
|
252
|
-
if (dependencies.usesProps) {
|
|
253
|
-
contextParts.push('props')
|
|
254
|
-
}
|
|
255
|
-
if (dependencies.usesStores) {
|
|
256
|
-
contextParts.push('stores')
|
|
257
|
-
}
|
|
258
|
-
if (dependencies.usesState) {
|
|
259
|
-
contextParts.push('state')
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Create merged context code
|
|
263
|
-
const contextCode = contextParts.length > 0
|
|
264
|
-
? `var __ctx = Object.assign({}, ${contextParts.join(', ')});`
|
|
265
|
-
: 'var __ctx = state || {};'
|
|
266
|
-
|
|
267
|
-
// Escape the code for use in a single-line comment (replace newlines with spaces)
|
|
268
|
-
const commentCode = code.replace(/[\r\n]+/g, ' ').replace(/\s+/g, ' ').substring(0, 100)
|
|
269
|
-
|
|
270
|
-
// JSON.stringify the code for error messages (properly escapes quotes, newlines, etc.)
|
|
271
|
-
const jsonEscapedCode = JSON.stringify(code)
|
|
272
|
-
|
|
273
|
-
// Transform JSX
|
|
274
|
-
const transformedCode = transformExpressionJSX(code)
|
|
275
|
-
|
|
276
|
-
// Properly escape the transformed code for use inside a string
|
|
277
|
-
const escapedTransformedCode = transformedCode
|
|
278
|
-
.replace(/\\/g, '\\\\')
|
|
279
|
-
.replace(/'/g, "\\'")
|
|
280
|
-
.replace(/\n/g, '\\n')
|
|
281
|
-
.replace(/\r/g, '\\r')
|
|
282
|
-
|
|
283
|
-
return `
|
|
284
|
-
// Expression: ${commentCode}${code.length > 100 ? '...' : ''}
|
|
285
|
-
// Dependencies: ${JSON.stringify({
|
|
286
|
-
loaderData: dependencies.usesLoaderData,
|
|
287
|
-
props: dependencies.usesProps,
|
|
288
|
-
stores: dependencies.usesStores,
|
|
289
|
-
state: dependencies.usesState
|
|
290
|
-
})}
|
|
291
|
-
const ${id} = (function() {
|
|
292
|
-
// Create the evaluator function once (with 'with' support in sloppy mode)
|
|
293
|
-
var evalFn = new Function('__ctx',
|
|
294
|
-
'with (__ctx) { return (' + '${escapedTransformedCode}' + '); }'
|
|
295
|
-
);
|
|
296
|
-
|
|
297
|
-
return function(${paramList}) {
|
|
298
|
-
try {
|
|
299
|
-
// Merge window globals with context (for script-level variables)
|
|
300
|
-
var __baseCtx = Object.assign({}, window);
|
|
301
|
-
${contextCode.replace('var __ctx', 'var __ctx').replace('= Object.assign({},', '= Object.assign(__baseCtx,')}
|
|
302
|
-
return evalFn(__ctx);
|
|
303
|
-
} catch (e) {
|
|
304
|
-
console.warn('[Zenith] Expression evaluation error:', ${jsonEscapedCode}, e);
|
|
305
|
-
return undefined;
|
|
306
|
-
}
|
|
307
|
-
};
|
|
308
|
-
})();`
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* Analyze all expressions in a template
|
|
313
|
-
*/
|
|
314
|
-
export function analyzeAllExpressions(
|
|
315
|
-
expressions: ExpressionIR[],
|
|
316
|
-
filePath: string,
|
|
317
|
-
declaredLoaderProps: string[] = [],
|
|
318
|
-
declaredProps: string[] = [],
|
|
319
|
-
declaredStores: string[] = []
|
|
320
|
-
): ExpressionDataDependencies[] {
|
|
321
|
-
const dependencies = expressions.map(expr =>
|
|
322
|
-
analyzeExpressionDependencies(expr, declaredLoaderProps, declaredProps, declaredStores)
|
|
323
|
-
)
|
|
324
|
-
|
|
325
|
-
// Validate all dependencies
|
|
326
|
-
for (const dep of dependencies) {
|
|
327
|
-
validateDataDependencies(dep, filePath, declaredLoaderProps, declaredProps, declaredStores)
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
return dependencies
|
|
331
|
-
}
|
|
332
|
-
|