@canvasengine/compiler 2.0.0-beta.5 → 2.0.0-beta.51

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/grammar.pegjs DELETED
@@ -1,186 +0,0 @@
1
- {
2
- function generateError(message, location) {
3
- const { start, end } = location;
4
- const errorMessage = `${message}\n` +
5
- `at line ${start.line}, column ${start.column} to line ${end.line}, column ${end.column}`;
6
- throw new Error(errorMessage);
7
- }
8
- }
9
-
10
- start
11
- = _ elements:(element)* _ {
12
- if (elements.length === 1) {
13
- return elements[0];
14
- }
15
- return `[${elements.join(',')}]`;
16
- }
17
-
18
- element
19
- = forLoop
20
- / ifCondition
21
- / selfClosingElement
22
- / openCloseElement
23
- / comment
24
-
25
- selfClosingElement
26
- = _ "<" _ tagName:tagName _ attributes:attributes _ "/>" _ {
27
- const attrs = attributes.length > 0 ? `{ ${attributes.join(', ')} }` : null;
28
- return attrs ? `h(${tagName}, ${attrs})` : `h(${tagName})`;
29
- }
30
-
31
- openCloseElement
32
- = "<" _ tagName:tagName _ attributes:attributes _ ">" _ content:content _ "</" _ closingTagName:tagName _ ">" {
33
- if (tagName !== closingTagName) {
34
- error("Mismatched opening and closing tags");
35
- }
36
- const attrs = attributes.length > 0 ? `{ ${attributes.join(', ')} }` : null;
37
- const children = content ? content : null;
38
- if (attrs && children) {
39
- return `h(${tagName}, ${attrs}, ${children})`;
40
- } else if (attrs) {
41
- return `h(${tagName}, ${attrs})`;
42
- } else if (children) {
43
- return `h(${tagName}, null, ${children})`;
44
- } else {
45
- return `h(${tagName})`;
46
- }
47
- }
48
-
49
- / "<" _ tagName:tagName _ attributes:attributes _ {
50
- generateError("Syntaxe d'élément invalide", location());
51
- }
52
-
53
- attributes
54
- = attrs:(attribute (_ attribute)*)? {
55
- return attrs
56
- ? [attrs[0]].concat(attrs[1].map(a => a[1]))
57
- : [];
58
- }
59
-
60
- attribute
61
- = staticAttribute
62
- / dynamicAttribute
63
- / eventHandler
64
-
65
- eventHandler
66
- = "@" eventName:identifier _ "=" _ "{" _ handlerName:attributeValue _ "}" {
67
- return `${eventName}: ${handlerName}`;
68
- }
69
- / "@" eventName:attributeName _ {
70
- return eventName;
71
- }
72
-
73
- dynamicAttribute
74
- = attributeName:attributeName _ "=" _ "{" _ attributeValue:attributeValue _ "}" {
75
- if (attributeValue.trim().match(/^[a-zA-Z_]\w*$/)) {
76
- return `${attributeName}: ${attributeValue}`;
77
- } else {
78
- let foundSignal = false
79
- const computedValue = attributeValue.replace(/@?[a-zA-Z_][a-zA-Z0-9_]*(?!:)/g, (match) => {
80
- if (match.startsWith('@')) {
81
- return match.substring(1);
82
- }
83
- foundSignal = true
84
- return `${match}()`;
85
- });
86
- if (foundSignal) {
87
- return `${attributeName}: computed(() => ${computedValue})`;
88
- }
89
- return `${attributeName}: ${computedValue}`;
90
- }
91
- }
92
- / attributeName:attributeName _ {
93
- return attributeName;
94
- }
95
-
96
- attributeValue
97
- = $([^{}]* ("{" [^{}]* "}" [^{}]*)*) {
98
- const t = text().trim()
99
- if (t.startsWith("{") && t.endsWith("}")) {
100
- return `(${t})`;
101
- }
102
- return t
103
- }
104
-
105
- staticAttribute
106
- = attributeName:attributeName _ "=" _ "\"" attributeValue:staticValue "\"" {
107
- return `${attributeName}: ${attributeValue}`;
108
- }
109
-
110
- eventAttribute
111
- = "(" _ eventName:eventName _ ")" _ "=" _ "\"" eventAction:eventAction "\"" {
112
- return `${eventName}: () => { ${eventAction} }`;
113
- }
114
-
115
- staticValue
116
- = [^"]+ {
117
- var val = text();
118
- return isNaN(val) ? `'${val}'` : val;
119
- }
120
-
121
- content
122
- = elements:(element)* {
123
- const filteredElements = elements.filter(el => el !== null);
124
- if (filteredElements.length === 0) return null;
125
- if (filteredElements.length === 1) return filteredElements[0];
126
- return `[${filteredElements.join(', ')}]`;
127
- }
128
-
129
- textNode
130
- = text:$([^<]+) {
131
- const trimmed = text.trim();
132
- return trimmed ? `'${trimmed}'` : null;
133
- }
134
-
135
- textElement
136
- = text:[^<>]+ {
137
- const trimmed = text.join('').trim();
138
- return trimmed ? JSON.stringify(trimmed) : null;
139
- }
140
-
141
- forLoop
142
- = _ "@for" _ "(" _ variableName:identifier _ "of" _ iterable:identifier _ ")" _ "{" _ content:content _ "}" _ {
143
- return `loop(${iterable}, (${variableName}) => ${content})`;
144
- }
145
-
146
- ifCondition
147
- = _ "@if" _ "(" _ condition:condition _ ")" _ "{" _ content:content _ "}" _ {
148
- return `cond(${condition}, () => ${content})`;
149
- }
150
-
151
- tagName
152
- = [a-zA-Z][a-zA-Z0-9]* { return text(); }
153
-
154
- attributeName
155
- = [a-zA-Z][a-zA-Z0-9-]* { return text(); }
156
-
157
- eventName
158
- = [a-zA-Z][a-zA-Z0-9-]* { return text(); }
159
-
160
- variableName
161
- = [a-zA-Z_][a-zA-Z0-9_]* { return text(); }
162
-
163
- iterable
164
- = [a-zA-Z_][a-zA-Z0-9_]* { return text(); }
165
-
166
- condition
167
- = $([^)]*) { return text().trim(); }
168
-
169
- eventAction
170
- = [^"]* { return text(); }
171
-
172
- _ 'whitespace'
173
- = [ \t\n\r]*
174
-
175
- identifier
176
- = [a-zA-Z_][a-zA-Z0-9_]* { return text(); }
177
-
178
- comment
179
- = singleComment+ {
180
- return null
181
- }
182
-
183
- singleComment
184
- = "<!--" _ content:((!("-->") .)* "-->") _ {
185
- return null;
186
- }
package/index.ts DELETED
@@ -1,167 +0,0 @@
1
- import { createFilter } from "vite";
2
- import { parse } from "acorn";
3
- import fs from "fs";
4
- import pkg from "peggy";
5
- import path from "path";
6
- import * as ts from "typescript";
7
- import { fileURLToPath } from 'url';
8
-
9
- const { generate } = pkg;
10
-
11
- const DEV_SRC = "../../src"
12
-
13
- export default function canvasengine() {
14
- const filter = createFilter("**/*.ce");
15
-
16
- // Convert import.meta.url to a file path
17
- const __filename = fileURLToPath(import.meta.url);
18
- const __dirname = path.dirname(__filename);
19
-
20
- const grammar = fs.readFileSync(
21
- path.join(__dirname, "grammar.pegjs").replace("dist/grammar.pegjs", "grammar.pegjs"),
22
- "utf8");
23
- const parser = generate(grammar);
24
- const isDev = process.env.NODE_ENV === "dev";
25
- const FLAG_COMMENT = "/*--[TPL]--*/";
26
-
27
- const PRIMITIVE_COMPONENTS = [
28
- "Canvas",
29
- "Sprite",
30
- "Text",
31
- "Viewport",
32
- "Graphics",
33
- "Container",
34
- "ImageMap",
35
- "NineSliceSprite",
36
- "Rect",
37
- "Circle",
38
- "TilingSprite",
39
- "svg",
40
- "Video"
41
- ];
42
-
43
- return {
44
- name: "vite-plugin-ce",
45
- transform(code: string, id: string) {
46
- if (!filter(id)) return;
47
-
48
- // Extract the script content
49
- const scriptMatch = code.match(/<script>([\s\S]*?)<\/script>/);
50
- let scriptContent = scriptMatch ? scriptMatch[1].trim() : "";
51
-
52
- // Transform SVG tags to Svg components
53
- let template = code.replace(/<script>[\s\S]*?<\/script>/, "")
54
- .replace(/^\s+|\s+$/g, '');
55
-
56
- // Add SVG transformation
57
- template = template.replace(/<svg>([\s\S]*?)<\/svg>/g, (match, content) => {
58
- return `<Svg content="${content.trim()}" />`;
59
- });
60
-
61
- const parsedTemplate = parser.parse(template);
62
-
63
- // trick to avoid typescript remove imports in scriptContent
64
- scriptContent += FLAG_COMMENT + parsedTemplate
65
-
66
- let transpiledCode = ts.transpileModule(scriptContent, {
67
- compilerOptions: {
68
- module: ts.ModuleKind.Preserve,
69
- },
70
- }).outputText;
71
-
72
- // remove code after /*---*/
73
- transpiledCode = transpiledCode.split(FLAG_COMMENT)[0]
74
-
75
- // Use Acorn to parse the script content
76
- const parsed = parse(transpiledCode, {
77
- sourceType: "module",
78
- ecmaVersion: 2020,
79
- });
80
-
81
- // Extract imports
82
- const imports = parsed.body.filter(
83
- (node) => node.type === "ImportDeclaration"
84
- );
85
-
86
- // Extract non-import statements from scriptContent
87
- const nonImportCode = parsed.body
88
- .filter((node) => node.type !== "ImportDeclaration")
89
- .map((node) => transpiledCode.slice(node.start, node.end))
90
- .join("\n");
91
-
92
- let importsCode = imports
93
- .map((imp) => {
94
- let importCode = transpiledCode.slice(imp.start, imp.end);
95
- if (isDev && importCode.includes("from 'canvasengine'")) {
96
- importCode = importCode.replace(
97
- "from 'canvasengine'",
98
- `from '${DEV_SRC}'`
99
- );
100
- }
101
- return importCode;
102
- })
103
- .join("\n");
104
-
105
- // Define an array for required imports
106
- const requiredImports = ["h", "computed", "cond", "loop"];
107
-
108
- // Check for missing imports
109
- const missingImports = requiredImports.filter(
110
- (importName) =>
111
- !imports.some(
112
- (imp) =>
113
- imp.specifiers &&
114
- imp.specifiers.some(
115
- (spec) =>
116
- spec.type === "ImportSpecifier" &&
117
- spec.imported &&
118
- 'name' in spec.imported &&
119
- spec.imported.name === importName
120
- )
121
- )
122
- );
123
-
124
- // Add missing imports
125
- if (missingImports.length > 0) {
126
- const additionalImportCode = `import { ${missingImports.join(
127
- ", "
128
- )} } from ${isDev ? `'${DEV_SRC}'` : "'canvasengine'"};`;
129
- importsCode = `${additionalImportCode}\n${importsCode}`;
130
- }
131
-
132
- // Check for primitive components in parsedTemplate
133
- const primitiveImports = PRIMITIVE_COMPONENTS.filter((component) =>
134
- parsedTemplate.includes(`h(${component}`)
135
- );
136
-
137
- // Add missing imports for primitive components
138
- primitiveImports.forEach((component) => {
139
- const importStatement = `import { ${component} } from ${
140
- isDev ? `'${DEV_SRC}'` : "'canvasengine'"
141
- };`;
142
- if (!importsCode.includes(importStatement)) {
143
- importsCode = `${importStatement}\n${importsCode}`;
144
- }
145
- });
146
-
147
- // Generate the output
148
- const output = String.raw`
149
- ${importsCode}
150
- import { useProps, useDefineProps } from ${isDev ? `'${DEV_SRC}'` : "'canvasengine'"}
151
-
152
- export default function component($$props) {
153
- const $props = useProps($$props)
154
- const defineProps = useDefineProps($$props)
155
- ${nonImportCode}
156
- let $this = ${parsedTemplate}
157
- return $this
158
- }
159
- `;
160
-
161
- return {
162
- code: output,
163
- map: null,
164
- };
165
- },
166
- };
167
- }
@@ -1,352 +0,0 @@
1
- import pkg from "peggy";
2
- import fs from "fs";
3
- import { beforeAll, describe, test, expect } from "vitest";
4
-
5
- const { generate } = pkg;
6
- let parser: any;
7
-
8
- beforeAll(() => {
9
- const grammar = fs.readFileSync("packages/compiler/grammar.pegjs", "utf8");
10
- parser = generate(grammar);
11
- });
12
-
13
- describe("Compiler", () => {
14
- test("should compile comment", () => {
15
- const input = `
16
- <Canvas>
17
- <!-- Comment -->
18
- </Canvas>
19
- `;
20
- const output = parser.parse(input);
21
- expect(output).toBe(`h(Canvas)`);
22
- });
23
-
24
- test("should compile multiple comment", () => {
25
- const input = `
26
- <Canvas>
27
- <!-- Comment -->
28
- <!-- Comment -->
29
- </Canvas>
30
- `;
31
- const output = parser.parse(input);
32
- expect(output).toBe(`h(Canvas)`);
33
- });
34
-
35
- test("should compile multiple line comment", () => {
36
- const input = `
37
- <Canvas>
38
- <!--
39
- Comment
40
- Comment
41
- -->
42
- </Canvas>
43
- `;
44
- const output = parser.parse(input);
45
- expect(output).toBe(`h(Canvas)`);
46
- });
47
-
48
- test("should compile simple component", () => {
49
- const input = `<Canvas />`;
50
- const output = parser.parse(input);
51
- expect(output).toBe(`h(Canvas)`);
52
- });
53
-
54
- test("should compile component with dynamic attribute", () => {
55
- const input = `<Canvas width={x} />`;
56
- const output = parser.parse(input);
57
- expect(output).toBe(`h(Canvas, { width: x })`);
58
- });
59
-
60
- test("should compile component with dynamic attribute but is not a signal", () => {
61
- const input = `<Canvas width={20} />`;
62
- const output = parser.parse(input);
63
- expect(output).toBe(`h(Canvas, { width: 20 })`);
64
- });
65
-
66
- test("should compile component with object attribute", () => {
67
- const input = `<Canvas width={ {x: 10, y: 20} } />`;
68
- const output = parser.parse(input);
69
- expect(output).toBe(`h(Canvas, { width: ({x: 10, y: 20}) })`);
70
- });
71
-
72
- test("should compile component with deep object attribute", () => {
73
- const input = `<Canvas width={deep.value} />`;
74
- const output = parser.parse(input);
75
- expect(output).toBe(`h(Canvas, { width: computed(() => deep().value()) })`);
76
- });
77
-
78
- test("should compile component with deep object attribute but not transform to signal", () => {
79
- const input = `<Canvas width={@deep.value} />`;
80
- const output = parser.parse(input);
81
- expect(output).toBe(`h(Canvas, { width: computed(() => deep.value()) })`);
82
- });
83
-
84
- test("should compile component with deep object attribute but not all transform to signal", () => {
85
- const input = `<Canvas width={@deep.@value} />`;
86
- const output = parser.parse(input);
87
- expect(output).toBe(`h(Canvas, { width: deep.value })`);
88
- });
89
-
90
- test("should compile component with dynamic object attribute", () => {
91
- const input = `<Canvas width={ {x: x, y: 20} } />`;
92
- const output = parser.parse(input);
93
- expect(output).toBe(`h(Canvas, { width: computed(() => ({x: x(), y: 20})) })`);
94
- });
95
-
96
- test("should compile component with array attribute", () => {
97
- const input = `<Canvas width={ [10, 20] } />`;
98
- const output = parser.parse(input);
99
- expect(output).toBe(`h(Canvas, { width: [10, 20] })`);
100
- });
101
-
102
- test("should compile component with dynamic array attribute", () => {
103
- const input = `<Canvas width={ [x, 20] } />`;
104
- const output = parser.parse(input);
105
- expect(output).toBe(`h(Canvas, { width: computed(() => [x(), 20]) })`);
106
- });
107
-
108
- test("should compile component with standalone dynamic attribute", () => {
109
- const input = `<Canvas width />`;
110
- const output = parser.parse(input);
111
- expect(output).toBe(`h(Canvas, { width })`);
112
- });
113
-
114
- test("should compile component with computed dynamic attribute", () => {
115
- const input = `<Canvas width={x * 2} />`;
116
- const output = parser.parse(input);
117
- expect(output).toBe(`h(Canvas, { width: computed(() => x() * 2) })`);
118
- });
119
-
120
- test("should compile component with multiple computed dynamic attributes", () => {
121
- const input = `<Canvas width={x * 2 * y} />`;
122
- const output = parser.parse(input);
123
- expect(output).toBe(`h(Canvas, { width: computed(() => x() * 2 * y()) })`);
124
- });
125
-
126
- test("should compile component with static numeric attribute", () => {
127
- const input = `<Canvas width="10" />`;
128
- const output = parser.parse(input);
129
- expect(output).toBe(`h(Canvas, { width: 10 })`);
130
- });
131
-
132
- test("should compile component with static string attribute", () => {
133
- const input = `<Canvas width="val" />`;
134
- const output = parser.parse(input);
135
- expect(output).toBe(`h(Canvas, { width: 'val' })`);
136
- });
137
-
138
- test("should compile component with children", () => {
139
- const input = `
140
- <Canvas>
141
- <Sprite />
142
- <Text />
143
- </Canvas>
144
- `;
145
- const output = parser.parse(input);
146
- expect(output.replace(/\s+/g, "")).toBe(
147
- `h(Canvas,null,[h(Sprite),h(Text)])`.replace(/\s+/g, "")
148
- );
149
- });
150
-
151
- test("should compile component with multi children", () => {
152
- const input = `
153
- <Sprite />
154
- <Sprite />
155
- `;
156
- const output = parser.parse(input);
157
- expect(output.replace(/\s+/g, "")).toBe(
158
- `[h(Sprite),h(Sprite)]`.replace(/\s+/g, "")
159
- );
160
- });
161
-
162
- test("should compile component with event handler", () => {
163
- const input = `<Sprite @click={fn} />`;
164
- const output = parser.parse(input);
165
- expect(output).toBe(`h(Sprite, { click: fn })`);
166
- });
167
-
168
- test("should compile component with standalone event handler", () => {
169
- const input = `<Sprite @click />`;
170
- const output = parser.parse(input);
171
- expect(output).toBe(`h(Sprite, { click })`);
172
- });
173
-
174
- test('should compile component with inline event handler', () => {
175
- const input = `<Sprite @click={() => console.log('click')} />`;
176
- const output = parser.parse(input);
177
- expect(output).toBe(`h(Sprite, { click: () => console.log('click') })`);
178
- });
179
-
180
- // test("should compile component with component attribute", () => {
181
- // const input = `<Canvas child={<Sprite />} />`;
182
- // const output = parser.parse(input);
183
- // expect(output).toBe(`h(Canvas, { child: h(Sprite) })`);
184
- // });
185
- });
186
-
187
- describe("Loop", () => {
188
- test("loop in canvas", () => {
189
- const input = `
190
- <Canvas>
191
- @for (sprite of sprites) {
192
- <Sprite />
193
- }
194
- </Canvas>
195
- `;
196
- const output = parser.parse(input);
197
- expect(output.replace(/\s+/g, "")).toBe(
198
- `h(Canvas,null,loop(sprites,(sprite)=>h(Sprite)))`.replace(/\s+/g, "")
199
- );
200
- });
201
-
202
- test("should compile loop", () => {
203
- const input = `
204
- @for (sprite of sprites) {
205
- <Sprite />
206
- }
207
- `;
208
- const output = parser.parse(input);
209
- expect(output.replace(/\s+/g, "")).toBe(
210
- `loop(sprites,(sprite)=>h(Sprite))`.replace(/\s+/g, "")
211
- );
212
- });
213
-
214
- test("should compile nestedloop", () => {
215
- const input = `
216
- @for (sprite of sprites) {
217
- @for (other of others) {
218
- <Sprite />
219
- }
220
- }
221
- `;
222
- const output = parser.parse(input);
223
- expect(output.replace(/\s+/g, "")).toBe(
224
- `loop(sprites,(sprite)=>loop(others,(other)=>h(Sprite)))`.replace(
225
- /\s+/g,
226
- ""
227
- )
228
- );
229
- });
230
- });
231
-
232
- describe("Condition", () => {
233
- test("should compile condition when sprite is visible", () => {
234
- const input = `
235
- @if (sprite.visible) {
236
- <Sprite />
237
- }
238
- `;
239
- const output = parser.parse(input);
240
- expect(output).toBe(`cond(sprite.visible, () => h(Sprite))`);
241
- });
242
-
243
- test("should compile condition for multiple sprites", () => {
244
- const input = `
245
- @if (sprite) {
246
- <Sprite />
247
- }
248
- @if (other) {
249
- <Sprite />
250
- }
251
- `;
252
- const output = parser.parse(input);
253
- expect(output).toBe(
254
- `[cond(sprite, () => h(Sprite)),cond(other, () => h(Sprite))]`
255
- );
256
- });
257
-
258
- test("should compile nested condition when sprite is visible", () => {
259
- const input = `
260
- @if (sprite.visible) {
261
- @if (deep) {
262
- <Sprite />
263
- }
264
- }
265
- `;
266
- const output = parser.parse(input);
267
- expect(output).toBe(
268
- `cond(sprite.visible, () => cond(deep, () => h(Sprite)))`
269
- );
270
- });
271
-
272
- test("should compile condition with nested sprite when sprite is visible", () => {
273
- const input = `
274
- @if (sprite.visible) {
275
- <Sprite />
276
- @if (deep) {
277
- <Sprite />
278
- }
279
- }
280
- `;
281
- const output = parser.parse(input);
282
- expect(output).toBe(
283
- `cond(sprite.visible, () => [h(Sprite), cond(deep, () => h(Sprite))])`
284
- );
285
- });
286
-
287
- test("should compile condition with multiple sprites when sprite is visible", () => {
288
- const input = `
289
- @if (sprite.visible) {
290
- <Sprite />
291
- @if (deep) {
292
- <Sprite />
293
- }
294
- <Sprite />
295
- }
296
- `;
297
- const output = parser.parse(input);
298
- expect(output).toBe(
299
- `cond(sprite.visible, () => [h(Sprite), cond(deep, () => h(Sprite)), h(Sprite)])`
300
- );
301
- });
302
- });
303
-
304
- describe("Condition in Loops", () => {
305
- test("should compile condition within a loop", () => { // New test for condition in a loop
306
- const input = `
307
- <Canvas>
308
- @for (sprite of sprites) {
309
- @if (sprite.visible) {
310
- <Sprite />
311
- }
312
- }
313
- </Canvas>
314
- `;
315
- const output = parser.parse(input);
316
- expect(output.replace(/\s+/g, "")).toBe(
317
- `h(Canvas,null,loop(sprites,(sprite)=>cond(sprite.visible,()=>h(Sprite))))`.replace(/\s+/g, "")
318
- );
319
- });
320
-
321
- test("should compile elements within a loop", () => { // New test for elements in a loop
322
- const input = `
323
- <Canvas>
324
- @for (sprite of sprites) {
325
- <Sprite />
326
- }
327
- </Canvas>
328
- `;
329
- const output = parser.parse(input);
330
- expect(output.replace(/\s+/g, "")).toBe(
331
- `h(Canvas,null,loop(sprites,(sprite)=>h(Sprite)))`.replace(/\s+/g, "")
332
- );
333
- });
334
-
335
- test("should compile multiple loops at the same level", () => { // New test for multiple loops
336
- const input = `
337
- <Canvas>
338
- @for (sprite of sprites) {
339
- <Sprite />
340
- }
341
- @for (other of others) {
342
- <Sprite />
343
- }
344
- </Canvas>
345
- `;
346
- const output = parser.parse(input);
347
- expect(output.replace(/\s+/g, "")).toBe(
348
- `h(Canvas,null,[loop(sprites,(sprite)=>h(Sprite)),loop(others,(other)=>h(Sprite))])`.replace(/\s+/g, "")
349
- );
350
- });
351
-
352
- });