@canvasengine/compiler 2.0.0-beta.1 → 2.0.0-beta.11

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.js CHANGED
@@ -23,7 +23,6 @@ function canvasengine() {
23
23
  "Canvas",
24
24
  "Sprite",
25
25
  "Text",
26
- "Joystick",
27
26
  "Viewport",
28
27
  "Graphics",
29
28
  "Container",
@@ -31,7 +30,9 @@ function canvasengine() {
31
30
  "NineSliceSprite",
32
31
  "Rect",
33
32
  "Circle",
34
- "svg"
33
+ "TilingSprite",
34
+ "svg",
35
+ "Video"
35
36
  ];
36
37
  return {
37
38
  name: "vite-plugin-ce",
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../index.ts"],"sourcesContent":["import { createFilter } from \"vite\";\nimport { parse } from \"acorn\";\nimport fs from \"fs\";\nimport pkg from \"peggy\";\nimport path from \"path\";\nimport * as ts from \"typescript\";\nimport { fileURLToPath } from 'url';\n\nconst { generate } = pkg;\n\nconst DEV_SRC = \"../../src\"\n\nexport default function canvasengine() {\n const filter = createFilter(\"**/*.ce\");\n\n // Convert import.meta.url to a file path\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = path.dirname(__filename);\n\n const grammar = fs.readFileSync(\n path.join(__dirname, \"grammar.pegjs\").replace(\"dist/grammar.pegjs\", \"grammar.pegjs\"), \n \"utf8\");\n const parser = generate(grammar);\n const isDev = process.env.NODE_ENV === \"dev\";\n const FLAG_COMMENT = \"/*--[TPL]--*/\";\n\n const PRIMITIVE_COMPONENTS = [\n \"Canvas\",\n \"Sprite\",\n \"Text\",\n \"Joystick\",\n \"Viewport\",\n \"Graphics\",\n \"Container\",\n \"ImageMap\",\n \"NineSliceSprite\",\n \"Rect\",\n \"Circle\",\n \"svg\"\n ];\n\n return {\n name: \"vite-plugin-ce\",\n transform(code: string, id: string) {\n if (!filter(id)) return;\n\n // Extract the script content\n const scriptMatch = code.match(/<script>([\\s\\S]*?)<\\/script>/);\n let scriptContent = scriptMatch ? scriptMatch[1].trim() : \"\";\n \n // Transform SVG tags to Svg components\n let template = code.replace(/<script>[\\s\\S]*?<\\/script>/, \"\")\n .replace(/^\\s+|\\s+$/g, '');\n \n // Add SVG transformation\n template = template.replace(/<svg>([\\s\\S]*?)<\\/svg>/g, (match, content) => {\n return `<Svg content=\"${content.trim()}\" />`;\n });\n \n const parsedTemplate = parser.parse(template);\n\n // trick to avoid typescript remove imports in scriptContent\n scriptContent += FLAG_COMMENT + parsedTemplate\n\n let transpiledCode = ts.transpileModule(scriptContent, {\n compilerOptions: {\n module: ts.ModuleKind.Preserve,\n },\n }).outputText;\n\n // remove code after /*---*/\n transpiledCode = transpiledCode.split(FLAG_COMMENT)[0]\n\n // Use Acorn to parse the script content\n const parsed = parse(transpiledCode, {\n sourceType: \"module\",\n ecmaVersion: 2020,\n });\n\n // Extract imports\n const imports = parsed.body.filter(\n (node) => node.type === \"ImportDeclaration\"\n );\n\n // Extract non-import statements from scriptContent\n const nonImportCode = parsed.body\n .filter((node) => node.type !== \"ImportDeclaration\")\n .map((node) => transpiledCode.slice(node.start, node.end))\n .join(\"\\n\");\n\n let importsCode = imports\n .map((imp) => {\n let importCode = transpiledCode.slice(imp.start, imp.end);\n if (isDev && importCode.includes(\"from 'canvasengine'\")) {\n importCode = importCode.replace(\n \"from 'canvasengine'\",\n `from '${DEV_SRC}'`\n );\n }\n return importCode;\n })\n .join(\"\\n\");\n\n // Define an array for required imports\n const requiredImports = [\"h\", \"computed\", \"cond\", \"loop\"];\n\n // Check for missing imports\n const missingImports = requiredImports.filter(\n (importName) =>\n !imports.some(\n (imp) =>\n imp.specifiers &&\n imp.specifiers.some(\n (spec) =>\n spec.type === \"ImportSpecifier\" &&\n spec.imported && \n 'name' in spec.imported &&\n spec.imported.name === importName\n )\n )\n );\n\n // Add missing imports\n if (missingImports.length > 0) {\n const additionalImportCode = `import { ${missingImports.join(\n \", \"\n )} } from ${isDev ? `'${DEV_SRC}'` : \"'canvasengine'\"};`;\n importsCode = `${additionalImportCode}\\n${importsCode}`;\n }\n\n // Check for primitive components in parsedTemplate\n const primitiveImports = PRIMITIVE_COMPONENTS.filter((component) =>\n parsedTemplate.includes(`h(${component}`)\n );\n\n // Add missing imports for primitive components\n primitiveImports.forEach((component) => {\n const importStatement = `import { ${component} } from ${\n isDev ? `'${DEV_SRC}'` : \"'canvasengine'\"\n };`;\n if (!importsCode.includes(importStatement)) {\n importsCode = `${importStatement}\\n${importsCode}`;\n }\n });\n\n // Generate the output\n const output = String.raw`\n ${importsCode}\n import { useProps, useDefineProps } from ${isDev ? `'${DEV_SRC}'` : \"'canvasengine'\"}\n\n export default function component($$props) {\n const $props = useProps($$props)\n const defineProps = useDefineProps($$props)\n ${nonImportCode}\n let $this = ${parsedTemplate}\n return $this\n }\n `;\n\n return {\n code: output,\n map: null,\n };\n },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../index.ts"],"sourcesContent":["import { createFilter } from \"vite\";\nimport { parse } from \"acorn\";\nimport fs from \"fs\";\nimport pkg from \"peggy\";\nimport path from \"path\";\nimport * as ts from \"typescript\";\nimport { fileURLToPath } from 'url';\n\nconst { generate } = pkg;\n\nconst DEV_SRC = \"../../src\"\n\nexport default function canvasengine() {\n const filter = createFilter(\"**/*.ce\");\n\n // Convert import.meta.url to a file path\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = path.dirname(__filename);\n\n const grammar = fs.readFileSync(\n path.join(__dirname, \"grammar.pegjs\").replace(\"dist/grammar.pegjs\", \"grammar.pegjs\"), \n \"utf8\");\n const parser = generate(grammar);\n const isDev = process.env.NODE_ENV === \"dev\";\n const FLAG_COMMENT = \"/*--[TPL]--*/\";\n\n const PRIMITIVE_COMPONENTS = [\n \"Canvas\",\n \"Sprite\",\n \"Text\",\n \"Viewport\",\n \"Graphics\",\n \"Container\",\n \"ImageMap\",\n \"NineSliceSprite\",\n \"Rect\",\n \"Circle\",\n \"TilingSprite\",\n \"svg\",\n \"Video\"\n ];\n\n return {\n name: \"vite-plugin-ce\",\n transform(code: string, id: string) {\n if (!filter(id)) return;\n\n // Extract the script content\n const scriptMatch = code.match(/<script>([\\s\\S]*?)<\\/script>/);\n let scriptContent = scriptMatch ? scriptMatch[1].trim() : \"\";\n \n // Transform SVG tags to Svg components\n let template = code.replace(/<script>[\\s\\S]*?<\\/script>/, \"\")\n .replace(/^\\s+|\\s+$/g, '');\n \n // Add SVG transformation\n template = template.replace(/<svg>([\\s\\S]*?)<\\/svg>/g, (match, content) => {\n return `<Svg content=\"${content.trim()}\" />`;\n });\n \n const parsedTemplate = parser.parse(template);\n\n // trick to avoid typescript remove imports in scriptContent\n scriptContent += FLAG_COMMENT + parsedTemplate\n\n let transpiledCode = ts.transpileModule(scriptContent, {\n compilerOptions: {\n module: ts.ModuleKind.Preserve,\n },\n }).outputText;\n\n // remove code after /*---*/\n transpiledCode = transpiledCode.split(FLAG_COMMENT)[0]\n\n // Use Acorn to parse the script content\n const parsed = parse(transpiledCode, {\n sourceType: \"module\",\n ecmaVersion: 2020,\n });\n\n // Extract imports\n const imports = parsed.body.filter(\n (node) => node.type === \"ImportDeclaration\"\n );\n\n // Extract non-import statements from scriptContent\n const nonImportCode = parsed.body\n .filter((node) => node.type !== \"ImportDeclaration\")\n .map((node) => transpiledCode.slice(node.start, node.end))\n .join(\"\\n\");\n\n let importsCode = imports\n .map((imp) => {\n let importCode = transpiledCode.slice(imp.start, imp.end);\n if (isDev && importCode.includes(\"from 'canvasengine'\")) {\n importCode = importCode.replace(\n \"from 'canvasengine'\",\n `from '${DEV_SRC}'`\n );\n }\n return importCode;\n })\n .join(\"\\n\");\n\n // Define an array for required imports\n const requiredImports = [\"h\", \"computed\", \"cond\", \"loop\"];\n\n // Check for missing imports\n const missingImports = requiredImports.filter(\n (importName) =>\n !imports.some(\n (imp) =>\n imp.specifiers &&\n imp.specifiers.some(\n (spec) =>\n spec.type === \"ImportSpecifier\" &&\n spec.imported && \n 'name' in spec.imported &&\n spec.imported.name === importName\n )\n )\n );\n\n // Add missing imports\n if (missingImports.length > 0) {\n const additionalImportCode = `import { ${missingImports.join(\n \", \"\n )} } from ${isDev ? `'${DEV_SRC}'` : \"'canvasengine'\"};`;\n importsCode = `${additionalImportCode}\\n${importsCode}`;\n }\n\n // Check for primitive components in parsedTemplate\n const primitiveImports = PRIMITIVE_COMPONENTS.filter((component) =>\n parsedTemplate.includes(`h(${component}`)\n );\n\n // Add missing imports for primitive components\n primitiveImports.forEach((component) => {\n const importStatement = `import { ${component} } from ${\n isDev ? `'${DEV_SRC}'` : \"'canvasengine'\"\n };`;\n if (!importsCode.includes(importStatement)) {\n importsCode = `${importStatement}\\n${importsCode}`;\n }\n });\n\n // Generate the output\n const output = String.raw`\n ${importsCode}\n import { useProps, useDefineProps } from ${isDev ? `'${DEV_SRC}'` : \"'canvasengine'\"}\n\n export default function component($$props) {\n const $props = useProps($$props)\n const defineProps = useDefineProps($$props)\n ${nonImportCode}\n let $this = ${parsedTemplate}\n return $this\n }\n `;\n\n return {\n code: output,\n map: null,\n };\n },\n };\n}\n"],"mappings":"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","names":[]}
package/grammar.pegjs CHANGED
@@ -72,15 +72,23 @@ eventHandler
72
72
 
73
73
  dynamicAttribute
74
74
  = attributeName:attributeName _ "=" _ "{" _ attributeValue:attributeValue _ "}" {
75
- if (attributeValue.trim().match(/^[a-zA-Z_]\w*$/)) {
75
+ if (attributeValue.startsWith('h(') || attributeValue.includes('=>')) {
76
+ return `${attributeName}: ${attributeValue}`;
77
+ } else if (attributeValue.trim().match(/^[a-zA-Z_]\w*$/)) {
76
78
  return `${attributeName}: ${attributeValue}`;
77
79
  } else {
78
- return `${attributeName}: computed(() => ${attributeValue.replace(/@?[a-zA-Z_][a-zA-Z0-9_]*(?!:)/g, (match) => {
80
+ let foundSignal = false;
81
+ const computedValue = attributeValue.replace(/@?[a-zA-Z_][a-zA-Z0-9_]*(?!:)/g, (match) => {
79
82
  if (match.startsWith('@')) {
80
83
  return match.substring(1);
81
84
  }
85
+ foundSignal = true;
82
86
  return `${match}()`;
83
- })})`;
87
+ });
88
+ if (foundSignal) {
89
+ return `${attributeName}: computed(() => ${computedValue})`;
90
+ }
91
+ return `${attributeName}: ${computedValue}`;
84
92
  }
85
93
  }
86
94
  / attributeName:attributeName _ {
@@ -88,7 +96,9 @@ dynamicAttribute
88
96
  }
89
97
 
90
98
  attributeValue
91
- = $([^{}]* ("{" [^{}]* "}" [^{}]*)*) {
99
+ = element
100
+ / functionWithElement
101
+ / $([^{}]* ("{" [^{}]* "}" [^{}]*)*) {
92
102
  const t = text().trim()
93
103
  if (t.startsWith("{") && t.endsWith("}")) {
94
104
  return `(${t})`;
@@ -96,6 +106,25 @@ attributeValue
96
106
  return t
97
107
  }
98
108
 
109
+ functionWithElement
110
+ = "(" _ params:functionParams? _ ")" _ "=>" _ elem:element {
111
+ return `${params ? `(${params}) =>` : '() =>'} ${elem}`;
112
+ }
113
+
114
+ functionParams
115
+ = destructuredParams
116
+ / simpleParams
117
+
118
+ destructuredParams
119
+ = "{" _ param:identifier rest:(_ "," _ identifier)* _ "}" {
120
+ return `{${[param].concat(rest.map(r => r[3])).join(', ')}}`;
121
+ }
122
+
123
+ simpleParams
124
+ = param:identifier rest:(_ "," _ identifier)* {
125
+ return [param].concat(rest.map(r => r[3])).join(', ');
126
+ }
127
+
99
128
  staticAttribute
100
129
  = attributeName:attributeName _ "=" _ "\"" attributeValue:staticValue "\"" {
101
130
  return `${attributeName}: ${attributeValue}`;
@@ -109,7 +138,7 @@ eventAttribute
109
138
  staticValue
110
139
  = [^"]+ {
111
140
  var val = text();
112
- return isNaN(val) ? `'${val}'` : val;
141
+ return `'${val}'`
113
142
  }
114
143
 
115
144
  content
@@ -133,8 +162,13 @@ textElement
133
162
  }
134
163
 
135
164
  forLoop
136
- = _ "@for" _ "(" _ variableName:identifier _ "of" _ iterable:identifier _ ")" _ "{" _ content:content _ "}" _ {
137
- return `loop(${iterable}, (${variableName}) => ${content})`;
165
+ = _ "@for" _ "(" _ variableName:(tupleDestructuring / identifier) _ "of" _ iterable:identifier _ ")" _ "{" _ content:content _ "}" _ {
166
+ return `loop(${iterable}, ${variableName} => ${content})`;
167
+ }
168
+
169
+ tupleDestructuring
170
+ = "(" _ first:identifier _ "," _ second:identifier _ ")" {
171
+ return `(${first}, ${second})`;
138
172
  }
139
173
 
140
174
  ifCondition
@@ -158,7 +192,30 @@ iterable
158
192
  = [a-zA-Z_][a-zA-Z0-9_]* { return text(); }
159
193
 
160
194
  condition
161
- = $([^)]*) { return text().trim(); }
195
+ = functionCall
196
+ / $([^)]*) { return text().trim(); }
197
+
198
+ functionCall
199
+ = name:identifier "(" args:functionArgs? ")" {
200
+ return `${name}(${args || ''})`;
201
+ }
202
+
203
+ functionArgs
204
+ = arg:functionArg rest:("," _ functionArg)* {
205
+ return [arg].concat(rest.map(r => r[2])).join(', ');
206
+ }
207
+
208
+ functionArg
209
+ = _ value:(identifier / number / string) _ {
210
+ return value;
211
+ }
212
+
213
+ number
214
+ = [0-9]+ ("." [0-9]+)? { return text(); }
215
+
216
+ string
217
+ = '"' chars:[^"]* '"' { return text(); }
218
+ / "'" chars:[^']* "'" { return text(); }
162
219
 
163
220
  eventAction
164
221
  = [^"]* { return text(); }
package/index.ts CHANGED
@@ -28,7 +28,6 @@ export default function canvasengine() {
28
28
  "Canvas",
29
29
  "Sprite",
30
30
  "Text",
31
- "Joystick",
32
31
  "Viewport",
33
32
  "Graphics",
34
33
  "Container",
@@ -36,7 +35,9 @@ export default function canvasengine() {
36
35
  "NineSliceSprite",
37
36
  "Rect",
38
37
  "Circle",
39
- "svg"
38
+ "TilingSprite",
39
+ "svg",
40
+ "Video"
40
41
  ];
41
42
 
42
43
  return {
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@canvasengine/compiler",
3
- "version": "2.0.0-beta.1",
3
+ "version": "2.0.0-beta.11",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
8
8
  "build": "tsup",
9
- "watch": "tsup --watch"
9
+ "dev": "tsup --watch"
10
10
  },
11
11
  "type": "module",
12
12
  "publishConfig": {
@@ -18,10 +18,11 @@
18
18
  "dependencies": {
19
19
  "acorn": "^8.14.0",
20
20
  "peggy": "^4.2.0",
21
+ "typescript": "^5.7.2",
21
22
  "vite": "^6.0.3"
22
23
  },
23
24
  "devDependencies": {
24
- "tsup": "^8.3.5",
25
- "typescript": "^5.7.2"
25
+ "@types/node": "^22.10.2",
26
+ "tsup": "^8.3.5"
26
27
  }
27
28
  }
@@ -6,7 +6,7 @@ const { generate } = pkg;
6
6
  let parser: any;
7
7
 
8
8
  beforeAll(() => {
9
- const grammar = fs.readFileSync("compiler/grammar.pegjs", "utf8");
9
+ const grammar = fs.readFileSync("packages/compiler/grammar.pegjs", "utf8");
10
10
  parser = generate(grammar);
11
11
  });
12
12
 
@@ -57,10 +57,16 @@ describe("Compiler", () => {
57
57
  expect(output).toBe(`h(Canvas, { width: x })`);
58
58
  });
59
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
+
60
66
  test("should compile component with object attribute", () => {
61
67
  const input = `<Canvas width={ {x: 10, y: 20} } />`;
62
68
  const output = parser.parse(input);
63
- expect(output).toBe(`h(Canvas, { width: computed(() => ({x: 10, y: 20})) })`);
69
+ expect(output).toBe(`h(Canvas, { width: ({x: 10, y: 20}) })`);
64
70
  });
65
71
 
66
72
  test("should compile component with deep object attribute", () => {
@@ -78,7 +84,7 @@ describe("Compiler", () => {
78
84
  test("should compile component with deep object attribute but not all transform to signal", () => {
79
85
  const input = `<Canvas width={@deep.@value} />`;
80
86
  const output = parser.parse(input);
81
- expect(output).toBe(`h(Canvas, { width: computed(() => deep.value) })`);
87
+ expect(output).toBe(`h(Canvas, { width: deep.value })`);
82
88
  });
83
89
 
84
90
  test("should compile component with dynamic object attribute", () => {
@@ -90,7 +96,7 @@ describe("Compiler", () => {
90
96
  test("should compile component with array attribute", () => {
91
97
  const input = `<Canvas width={ [10, 20] } />`;
92
98
  const output = parser.parse(input);
93
- expect(output).toBe(`h(Canvas, { width: computed(() => [10, 20]) })`);
99
+ expect(output).toBe(`h(Canvas, { width: [10, 20] })`);
94
100
  });
95
101
 
96
102
  test("should compile component with dynamic array attribute", () => {
@@ -117,18 +123,18 @@ describe("Compiler", () => {
117
123
  expect(output).toBe(`h(Canvas, { width: computed(() => x() * 2 * y()) })`);
118
124
  });
119
125
 
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
126
  test("should compile component with static string attribute", () => {
127
127
  const input = `<Canvas width="val" />`;
128
128
  const output = parser.parse(input);
129
129
  expect(output).toBe(`h(Canvas, { width: 'val' })`);
130
130
  });
131
131
 
132
+ test("should compile component with static attribute (with number)", () => {
133
+ const input = `<Canvas width="10" />`;
134
+ const output = parser.parse(input);
135
+ expect(output).toBe(`h(Canvas, { width: '10' })`);
136
+ });
137
+
132
138
  test("should compile component with children", () => {
133
139
  const input = `
134
140
  <Canvas>
@@ -171,11 +177,52 @@ describe("Compiler", () => {
171
177
  expect(output).toBe(`h(Sprite, { click: () => console.log('click') })`);
172
178
  });
173
179
 
174
- // test("should compile component with component attribute", () => {
175
- // const input = `<Canvas child={<Sprite />} />`;
176
- // const output = parser.parse(input);
177
- // expect(output).toBe(`h(Canvas, { child: h(Sprite) })`);
178
- // });
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
+ test("should compile component with function returns component attribute", () => {
187
+ const input = `<Canvas child={() => <Sprite />} />`;
188
+ const output = parser.parse(input);
189
+ expect(output).toBe(`h(Canvas, { child: () => h(Sprite) })`);
190
+ });
191
+
192
+ test("should compile component with function (with params) returns component attribute", () => {
193
+ const input = `<Canvas child={(x, y) => <Sprite />} />`;
194
+ const output = parser.parse(input);
195
+ expect(output).toBe(`h(Canvas, { child: (x, y) => h(Sprite) })`);
196
+ });
197
+
198
+ test("should compile component with destructuring function (with params)", () => {
199
+ const input = `<Canvas child={({ x, y }) => <Sprite />} />`;
200
+ const output = parser.parse(input);
201
+ expect(output).toBe(`h(Canvas, { child: ({x, y}) => h(Sprite) })`);
202
+ });
203
+
204
+ test("should compile component with function returns component attribute and data", () => {
205
+ const input = `<Canvas child={() => <Text text="Hello" />} />`;
206
+ const output = parser.parse(input);
207
+ expect(output).toBe(`h(Canvas, { child: () => h(Text, { text: 'Hello' }) })`);
208
+ });
209
+
210
+ test("should compile component with function returns component attribute and child", () => {
211
+ const input = `<Canvas child={() => <Container>
212
+ <Text text="Hello" />
213
+ </Container>} />`;
214
+ const output = parser.parse(input);
215
+ expect(output).toBe(`h(Canvas, { child: () => h(Container, null, h(Text, { text: 'Hello' })) })`);
216
+ });
217
+
218
+ test("should compile component with function returns component attribute and children", () => {
219
+ const input = `<Canvas child={() => <Container>
220
+ <Text text="Hello 1" />
221
+ <Text text="Hello 2" />
222
+ </Container>} />`;
223
+ const output = parser.parse(input);
224
+ expect(output).toBe(`h(Canvas, { child: () => h(Container, null, [h(Text, { text: 'Hello 1' }), h(Text, { text: 'Hello 2' })]) })`);
225
+ });
179
226
  });
180
227
 
181
228
  describe("Loop", () => {
@@ -189,7 +236,7 @@ describe("Loop", () => {
189
236
  `;
190
237
  const output = parser.parse(input);
191
238
  expect(output.replace(/\s+/g, "")).toBe(
192
- `h(Canvas,null,loop(sprites,(sprite)=>h(Sprite)))`.replace(/\s+/g, "")
239
+ `h(Canvas,null,loop(sprites,sprite=>h(Sprite)))`.replace(/\s+/g, "")
193
240
  );
194
241
  });
195
242
 
@@ -201,7 +248,19 @@ describe("Loop", () => {
201
248
  `;
202
249
  const output = parser.parse(input);
203
250
  expect(output.replace(/\s+/g, "")).toBe(
204
- `loop(sprites,(sprite)=>h(Sprite))`.replace(/\s+/g, "")
251
+ `loop(sprites,sprite=>h(Sprite))`.replace(/\s+/g, "")
252
+ );
253
+ });
254
+
255
+ test("should compile loop with destructuring", () => {
256
+ const input = `
257
+ @for ((sprite, index) of sprites) {
258
+ <Sprite key={index} />
259
+ }
260
+ `;
261
+ const output = parser.parse(input);
262
+ expect(output.replace(/\s+/g, "")).toBe(
263
+ `loop(sprites,(sprite,index)=>h(Sprite, { key: index }))`.replace(/\s+/g, "")
205
264
  );
206
265
  });
207
266
 
@@ -215,7 +274,7 @@ describe("Loop", () => {
215
274
  `;
216
275
  const output = parser.parse(input);
217
276
  expect(output.replace(/\s+/g, "")).toBe(
218
- `loop(sprites,(sprite)=>loop(others,(other)=>h(Sprite)))`.replace(
277
+ `loop(sprites,sprite=>loop(others,other=>h(Sprite)))`.replace(
219
278
  /\s+/g,
220
279
  ""
221
280
  )
@@ -234,6 +293,16 @@ describe("Condition", () => {
234
293
  expect(output).toBe(`cond(sprite.visible, () => h(Sprite))`);
235
294
  });
236
295
 
296
+ test("should compile condition when function value", () => {
297
+ const input = `
298
+ @if (val()) {
299
+ <Sprite />
300
+ }
301
+ `;
302
+ const output = parser.parse(input);
303
+ expect(output).toBe(`cond(val(), () => h(Sprite))`);
304
+ });
305
+
237
306
  test("should compile condition for multiple sprites", () => {
238
307
  const input = `
239
308
  @if (sprite) {
@@ -308,7 +377,7 @@ describe("Condition in Loops", () => {
308
377
  `;
309
378
  const output = parser.parse(input);
310
379
  expect(output.replace(/\s+/g, "")).toBe(
311
- `h(Canvas,null,loop(sprites,(sprite)=>cond(sprite.visible,()=>h(Sprite))))`.replace(/\s+/g, "")
380
+ `h(Canvas,null,loop(sprites,sprite=>cond(sprite.visible,()=>h(Sprite))))`.replace(/\s+/g, "")
312
381
  );
313
382
  });
314
383
 
@@ -322,7 +391,7 @@ describe("Condition in Loops", () => {
322
391
  `;
323
392
  const output = parser.parse(input);
324
393
  expect(output.replace(/\s+/g, "")).toBe(
325
- `h(Canvas,null,loop(sprites,(sprite)=>h(Sprite)))`.replace(/\s+/g, "")
394
+ `h(Canvas,null,loop(sprites,sprite=>h(Sprite)))`.replace(/\s+/g, "")
326
395
  );
327
396
  });
328
397
 
@@ -339,7 +408,7 @@ describe("Condition in Loops", () => {
339
408
  `;
340
409
  const output = parser.parse(input);
341
410
  expect(output.replace(/\s+/g, "")).toBe(
342
- `h(Canvas,null,[loop(sprites,(sprite)=>h(Sprite)),loop(others,(other)=>h(Sprite))])`.replace(/\s+/g, "")
411
+ `h(Canvas,null,[loop(sprites,sprite=>h(Sprite)),loop(others,other=>h(Sprite))])`.replace(/\s+/g, "")
343
412
  );
344
413
  });
345
414