@canvasengine/compiler 2.0.0-beta.2 → 2.0.0-beta.20
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/dist/index.d.ts +42 -0
- package/dist/index.js +158 -0
- package/dist/index.js.map +1 -0
- package/grammar.pegjs +559 -45
- package/index.ts +94 -9
- package/package.json +1 -1
- package/tests/compiler.spec.ts +565 -20
package/tests/compiler.spec.ts
CHANGED
|
@@ -51,16 +51,106 @@ describe("Compiler", () => {
|
|
|
51
51
|
expect(output).toBe(`h(Canvas)`);
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
+
describe("Dot notation", () => {
|
|
55
|
+
test("should compile component with dot notation", () => {
|
|
56
|
+
const input = `<MyComp.test />`;
|
|
57
|
+
const output = parser.parse(input);
|
|
58
|
+
expect(output).toBe(`h(MyComp.test)`);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("object function call", () => {
|
|
62
|
+
const input = `<MyComp.test() />`;
|
|
63
|
+
const output = parser.parse(input);
|
|
64
|
+
expect(output).toBe(`h(MyComp.test())`);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("function call with return object", () => {
|
|
68
|
+
const input = `<MyComp().test />`;
|
|
69
|
+
const output = parser.parse(input);
|
|
70
|
+
expect(output).toBe(`h(MyComp().test)`);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("function call with return object and params", () => {
|
|
74
|
+
const input = `<MyComp().test(x, y) />`;
|
|
75
|
+
const output = parser.parse(input);
|
|
76
|
+
expect(output).toBe(`h(MyComp().test(x, y))`);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("function call and params with return object and", () => {
|
|
80
|
+
const input = `<MyComp(x, y).test />`;
|
|
81
|
+
const output = parser.parse(input);
|
|
82
|
+
expect(output).toBe(`h(MyComp(x, y).test)`);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("function call", () => {
|
|
86
|
+
const input = `<MyComp() />`;
|
|
87
|
+
const output = parser.parse(input);
|
|
88
|
+
expect(output).toBe(`h(MyComp())`);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("function call and params", () => {
|
|
92
|
+
const input = `<MyComp(x, y) />`;
|
|
93
|
+
const output = parser.parse(input);
|
|
94
|
+
expect(output).toBe(`h(MyComp(x, y))`);
|
|
95
|
+
});
|
|
96
|
+
})
|
|
97
|
+
|
|
54
98
|
test("should compile component with dynamic attribute", () => {
|
|
55
99
|
const input = `<Canvas width={x} />`;
|
|
56
100
|
const output = parser.parse(input);
|
|
57
101
|
expect(output).toBe(`h(Canvas, { width: x })`);
|
|
58
102
|
});
|
|
59
103
|
|
|
104
|
+
test("should compile component with spread operator", () => {
|
|
105
|
+
const input = `<Canvas ...obj />`;
|
|
106
|
+
const output = parser.parse(input);
|
|
107
|
+
expect(output).toBe(`h(Canvas, obj)`);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("should compile component with spread operator object", () => {
|
|
111
|
+
const input = `<Canvas ...obj.prop />`;
|
|
112
|
+
const output = parser.parse(input);
|
|
113
|
+
expect(output).toBe(`h(Canvas, obj.prop)`);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("should compile component with spread operator function", () => {
|
|
117
|
+
const input = `<Canvas ...fn() />`;
|
|
118
|
+
const output = parser.parse(input);
|
|
119
|
+
expect(output).toBe(`h(Canvas, fn())`);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("should compile component with spread operator function and params", () => {
|
|
123
|
+
const input = `<Canvas ...fn(x, y) />`;
|
|
124
|
+
const output = parser.parse(input);
|
|
125
|
+
expect(output).toBe(`h(Canvas, fn(x, y))`);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("should compile component with dynamic attribute but is not a signal", () => {
|
|
129
|
+
const input = `<Canvas width={20} />`;
|
|
130
|
+
const output = parser.parse(input);
|
|
131
|
+
expect(output).toBe(`h(Canvas, { width: 20 })`);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
|
|
60
135
|
test("should compile component with object attribute", () => {
|
|
61
136
|
const input = `<Canvas width={ {x: 10, y: 20} } />`;
|
|
62
137
|
const output = parser.parse(input);
|
|
63
|
-
expect(output).toBe(`h(Canvas, { width:
|
|
138
|
+
expect(output).toBe(`h(Canvas, { width: ({x: 10, y: 20}) })`);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("should compile component with complex object attribute", () => {
|
|
142
|
+
const input = `<Sprite
|
|
143
|
+
sheet={{
|
|
144
|
+
definition,
|
|
145
|
+
playing: "stand",
|
|
146
|
+
params: {
|
|
147
|
+
direction: "right"
|
|
148
|
+
},
|
|
149
|
+
onFinish
|
|
150
|
+
}}
|
|
151
|
+
/>`;
|
|
152
|
+
const output = parser.parse(input);
|
|
153
|
+
expect(output).toBe(`h(Sprite, { sheet: { definition, playing: "stand", params: { direction: "right" }, onFinish } })`);
|
|
64
154
|
});
|
|
65
155
|
|
|
66
156
|
test("should compile component with deep object attribute", () => {
|
|
@@ -78,7 +168,7 @@ describe("Compiler", () => {
|
|
|
78
168
|
test("should compile component with deep object attribute but not all transform to signal", () => {
|
|
79
169
|
const input = `<Canvas width={@deep.@value} />`;
|
|
80
170
|
const output = parser.parse(input);
|
|
81
|
-
expect(output).toBe(`h(Canvas, { width:
|
|
171
|
+
expect(output).toBe(`h(Canvas, { width: deep.value })`);
|
|
82
172
|
});
|
|
83
173
|
|
|
84
174
|
test("should compile component with dynamic object attribute", () => {
|
|
@@ -90,7 +180,7 @@ describe("Compiler", () => {
|
|
|
90
180
|
test("should compile component with array attribute", () => {
|
|
91
181
|
const input = `<Canvas width={ [10, 20] } />`;
|
|
92
182
|
const output = parser.parse(input);
|
|
93
|
-
expect(output).toBe(`h(Canvas, { width:
|
|
183
|
+
expect(output).toBe(`h(Canvas, { width: [10, 20] })`);
|
|
94
184
|
});
|
|
95
185
|
|
|
96
186
|
test("should compile component with dynamic array attribute", () => {
|
|
@@ -117,18 +207,24 @@ describe("Compiler", () => {
|
|
|
117
207
|
expect(output).toBe(`h(Canvas, { width: computed(() => x() * 2 * y()) })`);
|
|
118
208
|
});
|
|
119
209
|
|
|
120
|
-
test("should compile component with static numeric attribute", () => {
|
|
121
|
-
const input = `<Canvas width="10" />`;
|
|
122
|
-
const output = parser.parse(input);
|
|
123
|
-
expect(output).toBe(`h(Canvas, { width: 10 })`);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
210
|
test("should compile component with static string attribute", () => {
|
|
127
211
|
const input = `<Canvas width="val" />`;
|
|
128
212
|
const output = parser.parse(input);
|
|
129
213
|
expect(output).toBe(`h(Canvas, { width: 'val' })`);
|
|
130
214
|
});
|
|
131
215
|
|
|
216
|
+
test("should compile component with static string attribute with dash", () => {
|
|
217
|
+
const input = `<Canvas max-width="val" />`;
|
|
218
|
+
const output = parser.parse(input);
|
|
219
|
+
expect(output).toBe(`h(Canvas, { 'max-width': 'val' })`);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test("should compile component with static attribute (with number)", () => {
|
|
223
|
+
const input = `<Canvas width="10" />`;
|
|
224
|
+
const output = parser.parse(input);
|
|
225
|
+
expect(output).toBe(`h(Canvas, { width: '10' })`);
|
|
226
|
+
});
|
|
227
|
+
|
|
132
228
|
test("should compile component with children", () => {
|
|
133
229
|
const input = `
|
|
134
230
|
<Canvas>
|
|
@@ -142,6 +238,18 @@ describe("Compiler", () => {
|
|
|
142
238
|
);
|
|
143
239
|
});
|
|
144
240
|
|
|
241
|
+
test("should compile component with multiple children", () => {
|
|
242
|
+
const input = `<Container>
|
|
243
|
+
<Container></Container>
|
|
244
|
+
<Container></Container>
|
|
245
|
+
</Container>
|
|
246
|
+
`;
|
|
247
|
+
const output = parser.parse(input);
|
|
248
|
+
expect(output.replace(/\s+/g, "")).toBe(
|
|
249
|
+
`h(Container,null,[h(Container),h(Container)])`.replace(/\s+/g, "")
|
|
250
|
+
);
|
|
251
|
+
});
|
|
252
|
+
|
|
145
253
|
test("should compile component with multi children", () => {
|
|
146
254
|
const input = `
|
|
147
255
|
<Sprite />
|
|
@@ -171,11 +279,52 @@ describe("Compiler", () => {
|
|
|
171
279
|
expect(output).toBe(`h(Sprite, { click: () => console.log('click') })`);
|
|
172
280
|
});
|
|
173
281
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
282
|
+
test("should compile component with component attribute", () => {
|
|
283
|
+
const input = `<Canvas child={<Sprite />} />`;
|
|
284
|
+
const output = parser.parse(input);
|
|
285
|
+
expect(output).toBe(`h(Canvas, { child: h(Sprite) })`);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
test("should compile component with function returns component attribute", () => {
|
|
289
|
+
const input = `<Canvas child={() => <Sprite />} />`;
|
|
290
|
+
const output = parser.parse(input);
|
|
291
|
+
expect(output).toBe(`h(Canvas, { child: () => h(Sprite) })`);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test("should compile component with function (with params) returns component attribute", () => {
|
|
295
|
+
const input = `<Canvas child={(x, y) => <Sprite />} />`;
|
|
296
|
+
const output = parser.parse(input);
|
|
297
|
+
expect(output).toBe(`h(Canvas, { child: (x, y) => h(Sprite) })`);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
test("should compile component with destructuring function (with params)", () => {
|
|
301
|
+
const input = `<Canvas child={({ x, y }) => <Sprite />} />`;
|
|
302
|
+
const output = parser.parse(input);
|
|
303
|
+
expect(output).toBe(`h(Canvas, { child: ({x, y}) => h(Sprite) })`);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
test("should compile component with function returns component attribute and data", () => {
|
|
307
|
+
const input = `<Canvas child={() => <Text text="Hello" />} />`;
|
|
308
|
+
const output = parser.parse(input);
|
|
309
|
+
expect(output).toBe(`h(Canvas, { child: () => h(Text, { text: 'Hello' }) })`);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
test("should compile component with function returns component attribute and child", () => {
|
|
313
|
+
const input = `<Canvas child={() => <Container>
|
|
314
|
+
<Text text="Hello" />
|
|
315
|
+
</Container>} />`;
|
|
316
|
+
const output = parser.parse(input);
|
|
317
|
+
expect(output).toBe(`h(Canvas, { child: () => h(Container, null, h(Text, { text: 'Hello' })) })`);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
test("should compile component with function returns component attribute and children", () => {
|
|
321
|
+
const input = `<Canvas child={() => <Container>
|
|
322
|
+
<Text text="Hello 1" />
|
|
323
|
+
<Text text="Hello 2" />
|
|
324
|
+
</Container>} />`;
|
|
325
|
+
const output = parser.parse(input);
|
|
326
|
+
expect(output).toBe(`h(Canvas, { child: () => h(Container, null, [h(Text, { text: 'Hello 1' }), h(Text, { text: 'Hello 2' })]) })`);
|
|
327
|
+
});
|
|
179
328
|
});
|
|
180
329
|
|
|
181
330
|
describe("Loop", () => {
|
|
@@ -189,7 +338,7 @@ describe("Loop", () => {
|
|
|
189
338
|
`;
|
|
190
339
|
const output = parser.parse(input);
|
|
191
340
|
expect(output.replace(/\s+/g, "")).toBe(
|
|
192
|
-
`h(Canvas,null,loop(sprites,
|
|
341
|
+
`h(Canvas,null,loop(sprites,sprite=>h(Sprite)))`.replace(/\s+/g, "")
|
|
193
342
|
);
|
|
194
343
|
});
|
|
195
344
|
|
|
@@ -201,7 +350,79 @@ describe("Loop", () => {
|
|
|
201
350
|
`;
|
|
202
351
|
const output = parser.parse(input);
|
|
203
352
|
expect(output.replace(/\s+/g, "")).toBe(
|
|
204
|
-
`loop(sprites,
|
|
353
|
+
`loop(sprites,sprite=>h(Sprite))`.replace(/\s+/g, "")
|
|
354
|
+
);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
test("should compile loop with object", () => {
|
|
358
|
+
const input = `
|
|
359
|
+
@for (sprite of sprites.items) {
|
|
360
|
+
<Sprite />
|
|
361
|
+
}
|
|
362
|
+
`;
|
|
363
|
+
const output = parser.parse(input);
|
|
364
|
+
expect(output.replace(/\s+/g, "")).toBe(
|
|
365
|
+
`loop(sprites.items,sprite=>h(Sprite))`.replace(/\s+/g, "")
|
|
366
|
+
);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test("should compile loop with deep object", () => {
|
|
370
|
+
const input = `
|
|
371
|
+
@for (sprite of sprites.items.items) {
|
|
372
|
+
<Sprite />
|
|
373
|
+
}
|
|
374
|
+
`;
|
|
375
|
+
const output = parser.parse(input);
|
|
376
|
+
expect(output.replace(/\s+/g, "")).toBe(
|
|
377
|
+
`loop(sprites.items.items,sprite=>h(Sprite))`.replace(/\s+/g, "")
|
|
378
|
+
);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
test("should compile loop with function", () => {
|
|
382
|
+
const input = `
|
|
383
|
+
@for (sprite of sprites()) {
|
|
384
|
+
<Sprite />
|
|
385
|
+
}
|
|
386
|
+
`;
|
|
387
|
+
const output = parser.parse(input);
|
|
388
|
+
expect(output.replace(/\s+/g, "")).toBe(
|
|
389
|
+
`loop(sprites(),sprite=>h(Sprite))`.replace(/\s+/g, "")
|
|
390
|
+
);
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
test("should compile loop with function and params", () => {
|
|
394
|
+
const input = `
|
|
395
|
+
@for (sprite of sprites(x, y)) {
|
|
396
|
+
<Sprite />
|
|
397
|
+
}
|
|
398
|
+
`;
|
|
399
|
+
const output = parser.parse(input);
|
|
400
|
+
expect(output.replace(/\s+/g, "")).toBe(
|
|
401
|
+
`loop(sprites(x,y),sprite=>h(Sprite))`.replace(/\s+/g, "")
|
|
402
|
+
);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
test("should compile loop with object and function and params", () => {
|
|
406
|
+
const input = `
|
|
407
|
+
@for (sprite of sprites.items(x, y)) {
|
|
408
|
+
<Sprite />
|
|
409
|
+
}
|
|
410
|
+
`;
|
|
411
|
+
const output = parser.parse(input);
|
|
412
|
+
expect(output.replace(/\s+/g, "")).toBe(
|
|
413
|
+
`loop(sprites.items(x,y),sprite=>h(Sprite))`.replace(/\s+/g, "")
|
|
414
|
+
);
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
test("should compile loop with destructuring", () => {
|
|
418
|
+
const input = `
|
|
419
|
+
@for ((sprite, index) of sprites) {
|
|
420
|
+
<Sprite key={index} />
|
|
421
|
+
}
|
|
422
|
+
`;
|
|
423
|
+
const output = parser.parse(input);
|
|
424
|
+
expect(output.replace(/\s+/g, "")).toBe(
|
|
425
|
+
`loop(sprites,(sprite,index)=>h(Sprite, { key: index }))`.replace(/\s+/g, "")
|
|
205
426
|
);
|
|
206
427
|
});
|
|
207
428
|
|
|
@@ -215,7 +436,7 @@ describe("Loop", () => {
|
|
|
215
436
|
`;
|
|
216
437
|
const output = parser.parse(input);
|
|
217
438
|
expect(output.replace(/\s+/g, "")).toBe(
|
|
218
|
-
`loop(sprites,
|
|
439
|
+
`loop(sprites,sprite=>loop(others,other=>h(Sprite)))`.replace(
|
|
219
440
|
/\s+/g,
|
|
220
441
|
""
|
|
221
442
|
)
|
|
@@ -224,6 +445,46 @@ describe("Loop", () => {
|
|
|
224
445
|
});
|
|
225
446
|
|
|
226
447
|
describe("Condition", () => {
|
|
448
|
+
test("should compile condition", () => {
|
|
449
|
+
const input = `
|
|
450
|
+
@if (sprite) {
|
|
451
|
+
<Sprite />
|
|
452
|
+
}
|
|
453
|
+
`;
|
|
454
|
+
const output = parser.parse(input);
|
|
455
|
+
expect(output).toBe(`cond(sprite, () => h(Sprite))`);
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
test("should compile negative condition", () => {
|
|
459
|
+
const input = `
|
|
460
|
+
@if (!sprite) {
|
|
461
|
+
<Sprite />
|
|
462
|
+
}
|
|
463
|
+
`;
|
|
464
|
+
const output = parser.parse(input);
|
|
465
|
+
expect(output).toBe(`cond(computed(() => !sprite()), () => h(Sprite))`);
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
test("should compile negative condition with multiple condition", () => {
|
|
469
|
+
const input = `
|
|
470
|
+
@if (!sprite && other) {
|
|
471
|
+
<Sprite />
|
|
472
|
+
}
|
|
473
|
+
`;
|
|
474
|
+
const output = parser.parse(input);
|
|
475
|
+
expect(output).toBe(`cond(computed(() => !sprite() && other()), () => h(Sprite))`);
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
test("should compile negative condition with multiple condition (or)", () => {
|
|
479
|
+
const input = `
|
|
480
|
+
@if (!sprite || other) {
|
|
481
|
+
<Sprite />
|
|
482
|
+
}
|
|
483
|
+
`;
|
|
484
|
+
const output = parser.parse(input);
|
|
485
|
+
expect(output).toBe(`cond(computed(() => !sprite() || other()), () => h(Sprite))`);
|
|
486
|
+
});
|
|
487
|
+
|
|
227
488
|
test("should compile condition when sprite is visible", () => {
|
|
228
489
|
const input = `
|
|
229
490
|
@if (sprite.visible) {
|
|
@@ -234,6 +495,16 @@ describe("Condition", () => {
|
|
|
234
495
|
expect(output).toBe(`cond(sprite.visible, () => h(Sprite))`);
|
|
235
496
|
});
|
|
236
497
|
|
|
498
|
+
test("should compile condition when function value", () => {
|
|
499
|
+
const input = `
|
|
500
|
+
@if (val()) {
|
|
501
|
+
<Sprite />
|
|
502
|
+
}
|
|
503
|
+
`;
|
|
504
|
+
const output = parser.parse(input);
|
|
505
|
+
expect(output).toBe(`cond(val(), () => h(Sprite))`);
|
|
506
|
+
});
|
|
507
|
+
|
|
237
508
|
test("should compile condition for multiple sprites", () => {
|
|
238
509
|
const input = `
|
|
239
510
|
@if (sprite) {
|
|
@@ -308,7 +579,7 @@ describe("Condition in Loops", () => {
|
|
|
308
579
|
`;
|
|
309
580
|
const output = parser.parse(input);
|
|
310
581
|
expect(output.replace(/\s+/g, "")).toBe(
|
|
311
|
-
`h(Canvas,null,loop(sprites,
|
|
582
|
+
`h(Canvas,null,loop(sprites,sprite=>cond(sprite.visible,()=>h(Sprite))))`.replace(/\s+/g, "")
|
|
312
583
|
);
|
|
313
584
|
});
|
|
314
585
|
|
|
@@ -322,7 +593,7 @@ describe("Condition in Loops", () => {
|
|
|
322
593
|
`;
|
|
323
594
|
const output = parser.parse(input);
|
|
324
595
|
expect(output.replace(/\s+/g, "")).toBe(
|
|
325
|
-
`h(Canvas,null,loop(sprites,
|
|
596
|
+
`h(Canvas,null,loop(sprites,sprite=>h(Sprite)))`.replace(/\s+/g, "")
|
|
326
597
|
);
|
|
327
598
|
});
|
|
328
599
|
|
|
@@ -339,8 +610,282 @@ describe("Condition in Loops", () => {
|
|
|
339
610
|
`;
|
|
340
611
|
const output = parser.parse(input);
|
|
341
612
|
expect(output.replace(/\s+/g, "")).toBe(
|
|
342
|
-
`h(Canvas,null,[loop(sprites,
|
|
613
|
+
`h(Canvas,null,[loop(sprites,sprite=>h(Sprite)),loop(others,other=>h(Sprite))])`.replace(/\s+/g, "")
|
|
614
|
+
);
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
describe('Svg', () => {
|
|
620
|
+
test('should compile svg', () => {
|
|
621
|
+
const input = `<svg>
|
|
622
|
+
<path d="M 100 350 l 150 -300" stroke="red" stroke-width="4"/>
|
|
623
|
+
</svg>`;
|
|
624
|
+
const output = parser.parse(input);
|
|
625
|
+
expect(output).toBe('h(Svg, { content: `<svg><path d="M 100 350 l 150 -300" stroke="red" stroke-width="4"/></svg>` })');
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
test('should compile svg with canvas', () => {
|
|
629
|
+
const input = `<Canvas antialias={true}>
|
|
630
|
+
<svg height="400" width="450" xmlns="http://www.w3.org/2000/svg"></svg></Canvas>`;
|
|
631
|
+
const output = parser.parse(input);
|
|
632
|
+
expect(output).toBe('h(Canvas, { antialias: true }, h(Svg, { content: `<svg height="400" width="450" xmlns="http://www.w3.org/2000/svg"></svg>` }))');
|
|
633
|
+
});
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
describe('DOM', () => {
|
|
637
|
+
test('should compile input DOM', () => {
|
|
638
|
+
const input = `<input type="text" />`;
|
|
639
|
+
const output = parser.parse(input);
|
|
640
|
+
expect(output).toBe('h(DOMContainer, { element: "input", attrs: { type: \'text\' } })');
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
test('should compile div DOM', () => {
|
|
644
|
+
const input = `<div class="container" />`;
|
|
645
|
+
const output = parser.parse(input);
|
|
646
|
+
expect(output).toBe('h(DOMContainer, { element: "div", attrs: { class: \'container\' } })');
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
test('should compile button DOM', () => {
|
|
650
|
+
const input = `<button type="submit" />`;
|
|
651
|
+
const output = parser.parse(input);
|
|
652
|
+
expect(output).toBe('h(DOMContainer, { element: "button", attrs: { type: \'submit\' } })');
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
test('should compile button DOM', () => {
|
|
656
|
+
const input = `<button>Text</button>`;
|
|
657
|
+
const output = parser.parse(input);
|
|
658
|
+
expect(output).toBe('h(DOMContainer, { element: "button", textContent: \'Text\' })');
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
test('should compile textarea DOM with dynamic attributes', () => {
|
|
662
|
+
const input = `<textarea rows={rows} cols={cols} />`;
|
|
663
|
+
const output = parser.parse(input);
|
|
664
|
+
expect(output).toBe('h(DOMContainer, { element: "textarea", attrs: { rows: rows, cols: cols } })');
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
test('should not transform Canvas to DOM', () => {
|
|
668
|
+
const input = `<Canvas width={800} />`;
|
|
669
|
+
const output = parser.parse(input);
|
|
670
|
+
expect(output).toBe('h(Canvas, { width: 800 })');
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
test('should not transform Sprite to DOM', () => {
|
|
674
|
+
const input = `<Sprite image="test.png" />`;
|
|
675
|
+
const output = parser.parse(input);
|
|
676
|
+
expect(output).toBe('h(Sprite, { image: \'test.png\' })');
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
// Tests avec imbrications DOM
|
|
680
|
+
test('should compile nested DOM elements', () => {
|
|
681
|
+
const input = `<div class="container">
|
|
682
|
+
<p>Hello World</p>
|
|
683
|
+
</div>`;
|
|
684
|
+
const output = parser.parse(input);
|
|
685
|
+
expect(output).toBe('h(DOMContainer, { element: "div", attrs: { class: \'container\' } }, h(DOMContainer, { element: "p", textContent: \'Hello World\' }))');
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
test('should compile deeply nested DOM elements', () => {
|
|
689
|
+
const input = `<div class="wrapper">
|
|
690
|
+
<section>
|
|
691
|
+
<h1>Title</h1>
|
|
692
|
+
<p>Content</p>
|
|
693
|
+
</section>
|
|
694
|
+
</div>`;
|
|
695
|
+
const output = parser.parse(input);
|
|
696
|
+
expect(output.replace(/\s+/g, "")).toBe(
|
|
697
|
+
`h(DOMContainer, { element: "div", attrs: { class: 'wrapper' } }, h(DOMContainer, { element: "section" }, [h(DOMContainer, { element: "h1", textContent: 'Title' }), h(DOMContainer, { element: "p", textContent: 'Content' })]))`.replace(/\s+/g, "")
|
|
343
698
|
);
|
|
344
699
|
});
|
|
345
700
|
|
|
701
|
+
test('should compile DOM with multiple children', () => {
|
|
702
|
+
const input = `<ul>
|
|
703
|
+
<li>Item 1</li>
|
|
704
|
+
<li>Item 2</li>
|
|
705
|
+
<li>Item 3</li>
|
|
706
|
+
</ul>`;
|
|
707
|
+
const output = parser.parse(input);
|
|
708
|
+
expect(output.replace(/\s+/g, "")).toBe(
|
|
709
|
+
`h(DOMContainer, { element: "ul" }, [h(DOMContainer, { element: "li", textContent: 'Item 1' }), h(DOMContainer, { element: "li", textContent: 'Item 2' }), h(DOMContainer, { element: "li", textContent: 'Item 3' })])`.replace(/\s+/g, "")
|
|
710
|
+
);
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
test('should compile mixed DOM and framework components', () => {
|
|
714
|
+
const input = `<div class="game-container">
|
|
715
|
+
<Canvas width={800} height={600} />
|
|
716
|
+
<div class="ui">
|
|
717
|
+
<button>Start Game</button>
|
|
718
|
+
</div>
|
|
719
|
+
</div>`;
|
|
720
|
+
const output = parser.parse(input);
|
|
721
|
+
expect(output.replace(/\s+/g, "")).toBe(
|
|
722
|
+
`h(DOMContainer, { element: "div", attrs: { class: 'game-container' } }, [h(Canvas, { width: 800, height: 600 }), h(DOMContainer, { element: "div", attrs: { class: 'ui' } }, h(DOMContainer, { element: "button", textContent: 'Start Game' }))])`.replace(/\s+/g, "")
|
|
723
|
+
);
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
test('should compile DOM with attributes and text content', () => {
|
|
727
|
+
const input = `<button class="btn primary" type="submit">Submit Form</button>`;
|
|
728
|
+
const output = parser.parse(input);
|
|
729
|
+
expect(output).toBe('h(DOMContainer, { element: "button", attrs: { class: \'btn primary\', type: \'submit\' }, textContent: \'Submit Form\' })');
|
|
730
|
+
});
|
|
731
|
+
});
|
|
732
|
+
|
|
733
|
+
describe('DOM with special attributes', () => {
|
|
734
|
+
test('should compile DOM with special attributes', () => {
|
|
735
|
+
const input = `<input type="password" x={100} y={100} />`;
|
|
736
|
+
const output = parser.parse(input);
|
|
737
|
+
expect(output).toBe('h(DOMContainer, { element: "input", attrs: { type: \'password\' }, x: 100, y: 100 })');
|
|
738
|
+
});
|
|
346
739
|
});
|
|
740
|
+
|
|
741
|
+
describe('DOM with Control Structures', () => {
|
|
742
|
+
test('should compile @for loop with DOM elements', () => {
|
|
743
|
+
const input = `
|
|
744
|
+
@for (item of items) {
|
|
745
|
+
<li>{item.name}</li>
|
|
746
|
+
}
|
|
747
|
+
`;
|
|
748
|
+
const output = parser.parse(input);
|
|
749
|
+
expect(output.replace(/\s+/g, "")).toBe(
|
|
750
|
+
`loop(items, item => h(DOMContainer, { element: "li", textContent: computed(() => item().name()) }))`.replace(/\s+/g, "")
|
|
751
|
+
);
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
test('should compile @for loop with nested DOM structure', () => {
|
|
755
|
+
const input = `
|
|
756
|
+
<ul class="menu">
|
|
757
|
+
@for (item of menuItems) {
|
|
758
|
+
<li class="menu-item">
|
|
759
|
+
<a href={item.url}>{item.title}</a>
|
|
760
|
+
</li>
|
|
761
|
+
}
|
|
762
|
+
</ul>
|
|
763
|
+
`;
|
|
764
|
+
const output = parser.parse(input);
|
|
765
|
+
expect(output.replace(/\s+/g, "")).toBe(
|
|
766
|
+
`h(DOMContainer, { element: "ul", attrs: { class: 'menu' } }, loop(menuItems, item => h(DOMContainer, { element: "li", attrs: { class: 'menu-item' } }, h(DOMContainer, { element: "a", attrs: { href: computed(() => item().url()) }, textContent: computed(() => item().title()) }))))`.replace(/\s+/g, "")
|
|
767
|
+
);
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
test('should compile @if condition with DOM elements', () => {
|
|
771
|
+
const input = `
|
|
772
|
+
@if (showMessage) {
|
|
773
|
+
<div class="alert">
|
|
774
|
+
<p>Important message!</p>
|
|
775
|
+
</div>
|
|
776
|
+
}
|
|
777
|
+
`;
|
|
778
|
+
const output = parser.parse(input);
|
|
779
|
+
expect(output.replace(/\s+/g, "")).toBe(
|
|
780
|
+
`cond(showMessage, () => h(DOMContainer, { element: "div", attrs: { class: 'alert' } }, h(DOMContainer, { element: "p", textContent: 'Important message!' })))`.replace(/\s+/g, "")
|
|
781
|
+
);
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
test('should compile @if condition with simple DOM element', () => {
|
|
785
|
+
const input = `
|
|
786
|
+
@if (isVisible) {
|
|
787
|
+
<button>Click me</button>
|
|
788
|
+
}
|
|
789
|
+
`;
|
|
790
|
+
const output = parser.parse(input);
|
|
791
|
+
expect(output.replace(/\s+/g, "")).toBe(
|
|
792
|
+
`cond(isVisible, () => h(DOMContainer, { element: "button", textContent: 'Click me' }))`.replace(/\s+/g, "")
|
|
793
|
+
);
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
test('should compile nested @for and @if with DOM', () => {
|
|
797
|
+
const input = `
|
|
798
|
+
<div class="container">
|
|
799
|
+
@for (section of sections) {
|
|
800
|
+
@if (section.visible) {
|
|
801
|
+
<section class="content">
|
|
802
|
+
<h2>{section.title}</h2>
|
|
803
|
+
<div class="items">
|
|
804
|
+
@for (item of section.items) {
|
|
805
|
+
<div class="item">{item.name}</div>
|
|
806
|
+
}
|
|
807
|
+
</div>
|
|
808
|
+
</section>
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
</div>
|
|
812
|
+
`;
|
|
813
|
+
const output = parser.parse(input);
|
|
814
|
+
expect(output.replace(/\s+/g, "")).toBe(
|
|
815
|
+
`h(DOMContainer, { element: "div", attrs: { class: 'container' } }, loop(sections, section => cond(section.visible, () => h(DOMContainer, { element: "section", attrs: { class: 'content' } }, [h(DOMContainer, { element: "h2", textContent: computed(() => section().title()) }), h(DOMContainer, { element: "div", attrs: { class: 'items' } }, loop(section.items, item => h(DOMContainer, { element: "div", attrs: { class: 'item' }, textContent: computed(() => item().name()) })))]))))`.replace(/\s+/g, "")
|
|
816
|
+
);
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
test('should compile @for with DOM table structure', () => {
|
|
820
|
+
const input = `
|
|
821
|
+
<table>
|
|
822
|
+
<thead>
|
|
823
|
+
<tr>
|
|
824
|
+
<th>Name</th>
|
|
825
|
+
<th>Age</th>
|
|
826
|
+
</tr>
|
|
827
|
+
</thead>
|
|
828
|
+
<tbody>
|
|
829
|
+
@for (user of users) {
|
|
830
|
+
<tr>
|
|
831
|
+
<td>{user.name}</td>
|
|
832
|
+
<td>{user.age}</td>
|
|
833
|
+
</tr>
|
|
834
|
+
}
|
|
835
|
+
</tbody>
|
|
836
|
+
</table>
|
|
837
|
+
`;
|
|
838
|
+
const output = parser.parse(input);
|
|
839
|
+
expect(output.replace(/\s+/g, "")).toBe(
|
|
840
|
+
`h(DOMContainer, { element: "table" }, [h(DOMContainer, { element: "thead" }, h(DOMContainer, { element: "tr" }, [h(DOMContainer, { element: "th", textContent: 'Name' }), h(DOMContainer, { element: "th", textContent: 'Age' })])), h(DOMContainer, { element: "tbody" }, loop(users, user => h(DOMContainer, { element: "tr" }, [h(DOMContainer, { element: "td", textContent: computed(() => user().name()) }), h(DOMContainer, { element: "td", textContent: computed(() => user().age()) })])))])`.replace(/\s+/g, "")
|
|
841
|
+
);
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
test('should compile @if with multiple DOM conditions', () => {
|
|
845
|
+
const input = `
|
|
846
|
+
<div class="status">
|
|
847
|
+
@if (isLoading) {
|
|
848
|
+
<div class="spinner">Loading...</div>
|
|
849
|
+
}
|
|
850
|
+
@if (hasError) {
|
|
851
|
+
<div class="error">Error occurred!</div>
|
|
852
|
+
}
|
|
853
|
+
@if (isSuccess) {
|
|
854
|
+
<div class="success">Success!</div>
|
|
855
|
+
}
|
|
856
|
+
</div>
|
|
857
|
+
`;
|
|
858
|
+
const output = parser.parse(input);
|
|
859
|
+
expect(output.replace(/\s+/g, "")).toBe(
|
|
860
|
+
`h(DOMContainer, { element: "div", attrs: { class: 'status' } }, [cond(isLoading, () => h(DOMContainer, { element: "div", attrs: { class: 'spinner' }, textContent: 'Loading...' })), cond(hasError, () => h(DOMContainer, { element: "div", attrs: { class: 'error' }, textContent: 'Error occurred!' })), cond(isSuccess, () => h(DOMContainer, { element: "div", attrs: { class: 'success' }, textContent: 'Success!' }))])`.replace(/\s+/g, "")
|
|
861
|
+
);
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
test('should compile mixed Canvas and DOM with control structures', () => {
|
|
865
|
+
const input = `
|
|
866
|
+
<div class="game-wrapper">
|
|
867
|
+
<Canvas width={800} height={600}>
|
|
868
|
+
@for (sprite of sprites) {
|
|
869
|
+
<Sprite x={sprite.x} y={sprite.y} />
|
|
870
|
+
}
|
|
871
|
+
</Canvas>
|
|
872
|
+
<div class="ui-overlay">
|
|
873
|
+
@if (showScore) {
|
|
874
|
+
<div class="score">Score: {score}</div>
|
|
875
|
+
}
|
|
876
|
+
@if (showMenu) {
|
|
877
|
+
<div class="menu">
|
|
878
|
+
@for (option of menuOptions) {
|
|
879
|
+
<button class="menu-btn">{option.label}</button>
|
|
880
|
+
}
|
|
881
|
+
</div>
|
|
882
|
+
}
|
|
883
|
+
</div>
|
|
884
|
+
</div>
|
|
885
|
+
`;
|
|
886
|
+
const output = parser.parse(input);
|
|
887
|
+
expect(output.replace(/\s+/g, "")).toBe(
|
|
888
|
+
`h(DOMContainer, { element: "div", attrs: { class: 'game-wrapper' } }, [h(Canvas, { width: 800, height: 600 }, loop(sprites, sprite => h(Sprite, { x: computed(() => sprite().x()), y: computed(() => sprite().y()) }))), h(DOMContainer, { element: "div", attrs: { class: 'ui-overlay' } }, [cond(showScore, () => h(DOMContainer, { element: "div", attrs: { class: 'score' }, textContent: computed(() => 'Score: ' + computed(() => score())) })), cond(showMenu, () => h(DOMContainer, { element: "div", attrs: { class: 'menu' } }, loop(menuOptions, option => h(DOMContainer, { element: "button", attrs: { class: 'menu-btn' }, textContent: computed(() => option().label()) }))))])])`.replace(/\s+/g, "")
|
|
889
|
+
);
|
|
890
|
+
});
|
|
891
|
+
});
|