@zenithbuild/core 1.2.1 → 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 +12 -21
- 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/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 -692
- 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 -240
- 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,365 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Component Stacking Test
|
|
3
|
-
*
|
|
4
|
-
* Tests that multiple sibling components and multiple instances
|
|
5
|
-
* of the same component render correctly.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { compileZenSource } from '../index'
|
|
9
|
-
import { discoverComponents, type ComponentMetadata } from '../discovery/componentDiscovery'
|
|
10
|
-
import { resolveComponentsInIR } from '../transform/componentResolver'
|
|
11
|
-
import { parseTemplate } from '../parse/parseTemplate'
|
|
12
|
-
import * as fs from 'fs'
|
|
13
|
-
import * as path from 'path'
|
|
14
|
-
|
|
15
|
-
// Test 1: Multiple sibling components
|
|
16
|
-
async function testMultipleSiblingComponents() {
|
|
17
|
-
console.log('\n=== Test 1: Multiple Sibling Components ===\n')
|
|
18
|
-
|
|
19
|
-
// Create mock components
|
|
20
|
-
const mockComponents = new Map<string, ComponentMetadata>()
|
|
21
|
-
|
|
22
|
-
mockComponents.set('Header', {
|
|
23
|
-
name: 'Header',
|
|
24
|
-
path: '/mock/Header.zen',
|
|
25
|
-
template: '<header class="test-header">Header Content</header>',
|
|
26
|
-
nodes: [{
|
|
27
|
-
type: 'element',
|
|
28
|
-
tag: 'header',
|
|
29
|
-
attributes: [{ name: 'class', value: 'test-header', location: { line: 1, column: 1 } }],
|
|
30
|
-
children: [{ type: 'text', value: 'Header Content', location: { line: 1, column: 1 } }],
|
|
31
|
-
location: { line: 1, column: 1 }
|
|
32
|
-
}],
|
|
33
|
-
slots: [],
|
|
34
|
-
props: [],
|
|
35
|
-
styles: [],
|
|
36
|
-
script: null,
|
|
37
|
-
scriptAttributes: null,
|
|
38
|
-
hasScript: false,
|
|
39
|
-
hasStyles: false
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
mockComponents.set('Hero', {
|
|
43
|
-
name: 'Hero',
|
|
44
|
-
path: '/mock/Hero.zen',
|
|
45
|
-
template: '<section class="test-hero">Hero Content</section>',
|
|
46
|
-
nodes: [{
|
|
47
|
-
type: 'element',
|
|
48
|
-
tag: 'section',
|
|
49
|
-
attributes: [{ name: 'class', value: 'test-hero', location: { line: 1, column: 1 } }],
|
|
50
|
-
children: [{ type: 'text', value: 'Hero Content', location: { line: 1, column: 1 } }],
|
|
51
|
-
location: { line: 1, column: 1 }
|
|
52
|
-
}],
|
|
53
|
-
slots: [],
|
|
54
|
-
props: [],
|
|
55
|
-
styles: [],
|
|
56
|
-
script: null,
|
|
57
|
-
scriptAttributes: null,
|
|
58
|
-
hasScript: false,
|
|
59
|
-
hasStyles: false
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
mockComponents.set('Footer', {
|
|
63
|
-
name: 'Footer',
|
|
64
|
-
path: '/mock/Footer.zen',
|
|
65
|
-
template: '<footer class="test-footer">Footer Content</footer>',
|
|
66
|
-
nodes: [{
|
|
67
|
-
type: 'element',
|
|
68
|
-
tag: 'footer',
|
|
69
|
-
attributes: [{ name: 'class', value: 'test-footer', location: { line: 1, column: 1 } }],
|
|
70
|
-
children: [{ type: 'text', value: 'Footer Content', location: { line: 1, column: 1 } }],
|
|
71
|
-
location: { line: 1, column: 1 }
|
|
72
|
-
}],
|
|
73
|
-
slots: [],
|
|
74
|
-
props: [],
|
|
75
|
-
styles: [],
|
|
76
|
-
script: null,
|
|
77
|
-
scriptAttributes: null,
|
|
78
|
-
hasScript: false,
|
|
79
|
-
hasStyles: false
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
// Test source with multiple sibling components
|
|
83
|
-
const testSource = `
|
|
84
|
-
<script setup="ts">
|
|
85
|
-
</script>
|
|
86
|
-
<div class="page">
|
|
87
|
-
<Header />
|
|
88
|
-
<Hero />
|
|
89
|
-
<Footer />
|
|
90
|
-
</div>
|
|
91
|
-
`
|
|
92
|
-
|
|
93
|
-
// Parse the template
|
|
94
|
-
const template = parseTemplate(testSource, 'test.zen')
|
|
95
|
-
|
|
96
|
-
console.log('Parsed nodes:', JSON.stringify(template.nodes, null, 2))
|
|
97
|
-
console.log('\nComponent nodes found:')
|
|
98
|
-
|
|
99
|
-
// Find all component nodes
|
|
100
|
-
function findComponents(nodes: any[], depth = 0): void {
|
|
101
|
-
for (const node of nodes) {
|
|
102
|
-
if (node.type === 'component') {
|
|
103
|
-
console.log(`${' '.repeat(depth)}Component: ${node.name}`)
|
|
104
|
-
} else if (node.type === 'element') {
|
|
105
|
-
console.log(`${' '.repeat(depth)}Element: <${node.tag}>`)
|
|
106
|
-
if (node.children) {
|
|
107
|
-
findComponents(node.children, depth + 1)
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
findComponents(template.nodes)
|
|
114
|
-
|
|
115
|
-
// Create IR
|
|
116
|
-
const ir = {
|
|
117
|
-
filePath: 'test.zen',
|
|
118
|
-
template,
|
|
119
|
-
script: { raw: '', attributes: {} },
|
|
120
|
-
styles: []
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Resolve components
|
|
124
|
-
const resolvedIR = resolveComponentsInIR(ir, mockComponents)
|
|
125
|
-
|
|
126
|
-
console.log('\nResolved nodes:', JSON.stringify(resolvedIR.template.nodes, null, 2))
|
|
127
|
-
|
|
128
|
-
// Count how many test-* classes appear in the resolved HTML
|
|
129
|
-
const jsonStr = JSON.stringify(resolvedIR.template.nodes)
|
|
130
|
-
const countHeaders = (jsonStr.match(/"value":"test-header"/g) || []).length
|
|
131
|
-
const countHeros = (jsonStr.match(/"value":"test-hero"/g) || []).length
|
|
132
|
-
const countFooters = (jsonStr.match(/"value":"test-footer"/g) || []).length
|
|
133
|
-
|
|
134
|
-
console.log(`\nResults:`)
|
|
135
|
-
console.log(` Headers: ${countHeaders} (expected: 1)`)
|
|
136
|
-
console.log(` Heros: ${countHeros} (expected: 1)`)
|
|
137
|
-
console.log(` Footers: ${countFooters} (expected: 1)`)
|
|
138
|
-
|
|
139
|
-
const passed = countHeaders === 1 && countHeros === 1 && countFooters === 1
|
|
140
|
-
console.log(`\n${passed ? '✅ PASSED' : '❌ FAILED'}: Multiple sibling components`)
|
|
141
|
-
|
|
142
|
-
return passed
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Test 2: Multiple instances of the same component
|
|
146
|
-
async function testMultipleInstances() {
|
|
147
|
-
console.log('\n=== Test 2: Multiple Instances of Same Component ===\n')
|
|
148
|
-
|
|
149
|
-
const mockComponents = new Map<string, ComponentMetadata>()
|
|
150
|
-
|
|
151
|
-
mockComponents.set('Card', {
|
|
152
|
-
name: 'Card',
|
|
153
|
-
path: '/mock/Card.zen',
|
|
154
|
-
template: '<div class="card">Card Content</div>',
|
|
155
|
-
nodes: [{
|
|
156
|
-
type: 'element',
|
|
157
|
-
tag: 'div',
|
|
158
|
-
attributes: [{ name: 'class', value: 'card', location: { line: 1, column: 1 } }],
|
|
159
|
-
children: [{ type: 'text', value: 'Card Content', location: { line: 1, column: 1 } }],
|
|
160
|
-
location: { line: 1, column: 1 }
|
|
161
|
-
}],
|
|
162
|
-
slots: [],
|
|
163
|
-
props: [],
|
|
164
|
-
styles: [],
|
|
165
|
-
script: null,
|
|
166
|
-
scriptAttributes: null,
|
|
167
|
-
hasScript: false,
|
|
168
|
-
hasStyles: false
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
const testSource = `
|
|
172
|
-
<script setup="ts">
|
|
173
|
-
</script>
|
|
174
|
-
<div class="grid">
|
|
175
|
-
<Card />
|
|
176
|
-
<Card />
|
|
177
|
-
<Card />
|
|
178
|
-
</div>
|
|
179
|
-
`
|
|
180
|
-
|
|
181
|
-
const template = parseTemplate(testSource, 'test.zen')
|
|
182
|
-
|
|
183
|
-
console.log('Component nodes found:')
|
|
184
|
-
|
|
185
|
-
function countComponents(nodes: any[]): number {
|
|
186
|
-
let count = 0
|
|
187
|
-
for (const node of nodes) {
|
|
188
|
-
if (node.type === 'component') {
|
|
189
|
-
console.log(` Found component: ${node.name}`)
|
|
190
|
-
count++
|
|
191
|
-
} else if (node.type === 'element' && node.children) {
|
|
192
|
-
count += countComponents(node.children)
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
return count
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const componentCount = countComponents(template.nodes)
|
|
199
|
-
console.log(`Total component nodes before resolution: ${componentCount}`)
|
|
200
|
-
|
|
201
|
-
const ir = {
|
|
202
|
-
filePath: 'test.zen',
|
|
203
|
-
template,
|
|
204
|
-
script: { raw: '', attributes: {} },
|
|
205
|
-
styles: []
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const resolvedIR = resolveComponentsInIR(ir, mockComponents)
|
|
209
|
-
|
|
210
|
-
// Count card divs in resolved output - search for the attribute value
|
|
211
|
-
const jsonStr = JSON.stringify(resolvedIR.template.nodes)
|
|
212
|
-
const cardCount = (jsonStr.match(/"value":"card"/g) || []).length
|
|
213
|
-
|
|
214
|
-
console.log(`\nResolved JSON (first 500 chars): ${jsonStr.substring(0, 500)}...`)
|
|
215
|
-
console.log(`\nCard divs in resolved output: ${cardCount} (expected: 3)`)
|
|
216
|
-
|
|
217
|
-
const passed = cardCount === 3
|
|
218
|
-
console.log(`\n${passed ? '✅ PASSED' : '❌ FAILED'}: Multiple instances of same component`)
|
|
219
|
-
|
|
220
|
-
return passed
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Test 3: Nested components
|
|
224
|
-
async function testNestedComponents() {
|
|
225
|
-
console.log('\n=== Test 3: Nested Components ===\n')
|
|
226
|
-
|
|
227
|
-
const mockComponents = new Map<string, ComponentMetadata>()
|
|
228
|
-
|
|
229
|
-
mockComponents.set('Outer', {
|
|
230
|
-
name: 'Outer',
|
|
231
|
-
path: '/mock/Outer.zen',
|
|
232
|
-
template: '<div class="outer"><slot /></div>',
|
|
233
|
-
nodes: [{
|
|
234
|
-
type: 'element',
|
|
235
|
-
tag: 'div',
|
|
236
|
-
attributes: [{ name: 'class', value: 'outer', location: { line: 1, column: 1 } }],
|
|
237
|
-
children: [{
|
|
238
|
-
type: 'element',
|
|
239
|
-
tag: 'slot',
|
|
240
|
-
attributes: [],
|
|
241
|
-
children: [],
|
|
242
|
-
location: { line: 1, column: 1 }
|
|
243
|
-
}],
|
|
244
|
-
location: { line: 1, column: 1 }
|
|
245
|
-
}],
|
|
246
|
-
slots: [{ name: null, location: { line: 1, column: 1 } }],
|
|
247
|
-
props: [],
|
|
248
|
-
styles: [],
|
|
249
|
-
script: null,
|
|
250
|
-
scriptAttributes: null,
|
|
251
|
-
hasScript: false,
|
|
252
|
-
hasStyles: false
|
|
253
|
-
})
|
|
254
|
-
|
|
255
|
-
mockComponents.set('Inner', {
|
|
256
|
-
name: 'Inner',
|
|
257
|
-
path: '/mock/Inner.zen',
|
|
258
|
-
template: '<span class="inner">Inner Content</span>',
|
|
259
|
-
nodes: [{
|
|
260
|
-
type: 'element',
|
|
261
|
-
tag: 'span',
|
|
262
|
-
attributes: [{ name: 'class', value: 'inner', location: { line: 1, column: 1 } }],
|
|
263
|
-
children: [{ type: 'text', value: 'Inner Content', location: { line: 1, column: 1 } }],
|
|
264
|
-
location: { line: 1, column: 1 }
|
|
265
|
-
}],
|
|
266
|
-
slots: [],
|
|
267
|
-
props: [],
|
|
268
|
-
styles: [],
|
|
269
|
-
script: null,
|
|
270
|
-
scriptAttributes: null,
|
|
271
|
-
hasScript: false,
|
|
272
|
-
hasStyles: false
|
|
273
|
-
})
|
|
274
|
-
|
|
275
|
-
const testSource = `
|
|
276
|
-
<script setup="ts">
|
|
277
|
-
</script>
|
|
278
|
-
<Outer>
|
|
279
|
-
<Inner />
|
|
280
|
-
</Outer>
|
|
281
|
-
`
|
|
282
|
-
|
|
283
|
-
const template = parseTemplate(testSource, 'test.zen')
|
|
284
|
-
|
|
285
|
-
const ir = {
|
|
286
|
-
filePath: 'test.zen',
|
|
287
|
-
template,
|
|
288
|
-
script: { raw: '', attributes: {} },
|
|
289
|
-
styles: []
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const resolvedIR = resolveComponentsInIR(ir, mockComponents)
|
|
293
|
-
|
|
294
|
-
const jsonStr = JSON.stringify(resolvedIR.template.nodes)
|
|
295
|
-
const hasOuter = jsonStr.includes('"value":"outer"')
|
|
296
|
-
const hasInner = jsonStr.includes('"value":"inner"')
|
|
297
|
-
|
|
298
|
-
console.log(`Resolved JSON (first 500 chars): ${jsonStr.substring(0, 500)}...`)
|
|
299
|
-
console.log(`Outer div present: ${hasOuter}`)
|
|
300
|
-
console.log(`Inner span present: ${hasInner}`)
|
|
301
|
-
|
|
302
|
-
const passed = hasOuter && hasInner
|
|
303
|
-
console.log(`\n${passed ? '✅ PASSED' : '❌ FAILED'}: Nested components`)
|
|
304
|
-
|
|
305
|
-
return passed
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// Test 4: Auto-import naming (filename-based)
|
|
309
|
-
async function testAutoImportNaming() {
|
|
310
|
-
console.log('\n=== Test 4: Auto-Import Naming ===\n')
|
|
311
|
-
|
|
312
|
-
// Test the naming algorithm directly
|
|
313
|
-
// With the new convention, component name = filename (subdirectories are ignored)
|
|
314
|
-
const testCases = [
|
|
315
|
-
{ input: 'components/Header.zen', expected: 'Header' },
|
|
316
|
-
{ input: 'components/globals/Header.zen', expected: 'Header' }, // Filename only!
|
|
317
|
-
{ input: 'components/ui/buttons/Primary.zen', expected: 'Primary' },
|
|
318
|
-
{ input: 'components/sections/HeroSection.zen', expected: 'HeroSection' },
|
|
319
|
-
{ input: 'components/ui-kit/Button.zen', expected: 'Button' },
|
|
320
|
-
]
|
|
321
|
-
|
|
322
|
-
let passed = true
|
|
323
|
-
|
|
324
|
-
for (const tc of testCases) {
|
|
325
|
-
// Component name is just the filename without .zen extension
|
|
326
|
-
const result = path.basename(tc.input, '.zen')
|
|
327
|
-
|
|
328
|
-
const match = result === tc.expected
|
|
329
|
-
console.log(` ${match ? '✓' : '✗'} "${tc.input}" → "${result}" (expected: "${tc.expected}")`)
|
|
330
|
-
|
|
331
|
-
if (!match) passed = false
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
console.log(`\n${passed ? '✅ PASSED' : '❌ FAILED'}: Auto-import naming`)
|
|
335
|
-
|
|
336
|
-
return passed
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Run all tests
|
|
340
|
-
async function runTests() {
|
|
341
|
-
console.log('╔════════════════════════════════════════════╗')
|
|
342
|
-
console.log('║ Component Stacking Tests ║')
|
|
343
|
-
console.log('╚════════════════════════════════════════════╝')
|
|
344
|
-
|
|
345
|
-
const results = []
|
|
346
|
-
|
|
347
|
-
results.push(await testMultipleSiblingComponents())
|
|
348
|
-
results.push(await testMultipleInstances())
|
|
349
|
-
results.push(await testNestedComponents())
|
|
350
|
-
results.push(await testAutoImportNaming())
|
|
351
|
-
|
|
352
|
-
console.log('\n════════════════════════════════════════════')
|
|
353
|
-
console.log('Summary:')
|
|
354
|
-
console.log(` Total: ${results.length}`)
|
|
355
|
-
console.log(` Passed: ${results.filter(r => r).length}`)
|
|
356
|
-
console.log(` Failed: ${results.filter(r => !r).length}`)
|
|
357
|
-
console.log('════════════════════════════════════════════\n')
|
|
358
|
-
|
|
359
|
-
process.exit(results.every(r => r) ? 0 : 1)
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
runTests().catch(err => {
|
|
363
|
-
console.error('Test error:', err)
|
|
364
|
-
process.exit(1)
|
|
365
|
-
})
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test Cases for Expression Validation
|
|
3
|
-
*
|
|
4
|
-
* Phase 8/9/10: Tests that invalid expressions fail the build
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { validateExpressions, validateExpressionsOrThrow } from '../validate/validateExpressions'
|
|
8
|
-
import type { ExpressionIR } from '../ir/types'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Test valid expressions
|
|
12
|
-
*/
|
|
13
|
-
function testValidExpressions() {
|
|
14
|
-
const validExpressions: ExpressionIR[] = [
|
|
15
|
-
{
|
|
16
|
-
id: 'expr_0',
|
|
17
|
-
code: 'user.name',
|
|
18
|
-
location: { line: 10, column: 5 }
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
id: 'expr_1',
|
|
22
|
-
code: 'count + 1',
|
|
23
|
-
location: { line: 11, column: 8 }
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
id: 'expr_2',
|
|
27
|
-
code: 'isActive ? "on" : "off"',
|
|
28
|
-
location: { line: 12, column: 12 }
|
|
29
|
-
}
|
|
30
|
-
]
|
|
31
|
-
|
|
32
|
-
const result = validateExpressions(validExpressions, 'test.zen')
|
|
33
|
-
console.assert(result.valid === true, 'Valid expressions should pass validation')
|
|
34
|
-
console.assert(result.errors.length === 0, 'Valid expressions should have no errors')
|
|
35
|
-
console.log('✅ Valid expressions test passed')
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Test invalid expressions
|
|
40
|
-
*/
|
|
41
|
-
function testInvalidExpressions() {
|
|
42
|
-
const invalidExpressions: ExpressionIR[] = [
|
|
43
|
-
{
|
|
44
|
-
id: 'expr_0',
|
|
45
|
-
code: 'user.name}', // Mismatched brace
|
|
46
|
-
location: { line: 10, column: 5 }
|
|
47
|
-
}
|
|
48
|
-
]
|
|
49
|
-
|
|
50
|
-
const result = validateExpressions(invalidExpressions, 'test.zen')
|
|
51
|
-
console.assert(result.valid === false, 'Invalid expressions should fail validation')
|
|
52
|
-
console.assert(result.errors.length > 0, 'Invalid expressions should have errors')
|
|
53
|
-
console.log('✅ Invalid expressions test passed')
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Test unsafe code detection
|
|
58
|
-
*/
|
|
59
|
-
function testUnsafeCode() {
|
|
60
|
-
const unsafeExpressions: ExpressionIR[] = [
|
|
61
|
-
{
|
|
62
|
-
id: 'expr_0',
|
|
63
|
-
code: 'eval("alert(1)")',
|
|
64
|
-
location: { line: 10, column: 5 }
|
|
65
|
-
}
|
|
66
|
-
]
|
|
67
|
-
|
|
68
|
-
const result = validateExpressions(unsafeExpressions, 'test.zen')
|
|
69
|
-
console.assert(result.valid === false, 'Unsafe code should fail validation')
|
|
70
|
-
console.assert(result.errors.length > 0, 'Unsafe code should have errors')
|
|
71
|
-
console.log('✅ Unsafe code detection test passed')
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Test validateExpressionsOrThrow
|
|
76
|
-
*/
|
|
77
|
-
function testThrowOnInvalid() {
|
|
78
|
-
const invalidExpressions: ExpressionIR[] = [
|
|
79
|
-
{
|
|
80
|
-
id: 'expr_0',
|
|
81
|
-
code: 'user.name}', // Mismatched brace
|
|
82
|
-
location: { line: 10, column: 5 }
|
|
83
|
-
}
|
|
84
|
-
]
|
|
85
|
-
|
|
86
|
-
try {
|
|
87
|
-
validateExpressionsOrThrow(invalidExpressions, 'test.zen')
|
|
88
|
-
console.assert(false, 'Should have thrown on invalid expressions')
|
|
89
|
-
} catch (error) {
|
|
90
|
-
console.assert(error instanceof Error, 'Should throw Error')
|
|
91
|
-
console.log('✅ Throw on invalid expressions test passed')
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Run tests
|
|
96
|
-
if (require.main === module) {
|
|
97
|
-
console.log('Running validation tests...')
|
|
98
|
-
testValidExpressions()
|
|
99
|
-
testInvalidExpressions()
|
|
100
|
-
testUnsafeCode()
|
|
101
|
-
testThrowOnInvalid()
|
|
102
|
-
console.log('✅ All validation tests passed!')
|
|
103
|
-
}
|
|
104
|
-
|