@canvasengine/compiler 2.0.0-beta.14 → 2.0.0-beta.16

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
@@ -30,6 +30,8 @@ function canvasengine() {
30
30
  "NineSliceSprite",
31
31
  "Rect",
32
32
  "Circle",
33
+ "Ellipse",
34
+ "Triangle",
33
35
  "TilingSprite",
34
36
  "svg",
35
37
  "Video"
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 \"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":[]}
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 \"Ellipse\",\n \"Triangle\",\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
@@ -57,7 +57,7 @@ selfClosingElement
57
57
  }
58
58
 
59
59
  openCloseElement
60
- = "<" _ tagName:tagName _ attributes:attributes _ ">" _ content:content _ "</" _ closingTagName:tagName _ ">" {
60
+ = "<" _ tagName:tagName _ attributes:attributes _ ">" _ content:content _ "</" _ closingTagName:tagName _ ">" _ {
61
61
  if (tagName !== closingTagName) {
62
62
  error("Mismatched opening and closing tags");
63
63
  }
@@ -74,10 +74,6 @@ openCloseElement
74
74
  }
75
75
  }
76
76
 
77
- / "<" _ tagName:tagName _ attributes:attributes _ {
78
- generateError("Syntaxe d'élément invalide", location());
79
- }
80
-
81
77
  attributes
82
78
  = attrs:(attribute (_ attribute)*)? {
83
79
  return attrs
@@ -123,6 +119,12 @@ dynamicAttribute
123
119
  const needsQuotes = /[^a-zA-Z0-9_$]/.test(attributeName);
124
120
  const formattedName = needsQuotes ? `'${attributeName}'` : attributeName;
125
121
 
122
+ // If it's a complex object literal starting with curly braces, preserve it as is
123
+ if (attributeValue.trim().startsWith('{') && attributeValue.trim().endsWith('}')) {
124
+ return `${formattedName}: ${attributeValue}`;
125
+ }
126
+
127
+ // Handle other types of values
126
128
  if (attributeValue.startsWith('h(') || attributeValue.includes('=>')) {
127
129
  return `${formattedName}: ${attributeValue}`;
128
130
  } else if (attributeValue.trim().match(/^[a-zA-Z_]\w*$/)) {
@@ -150,6 +152,7 @@ dynamicAttribute
150
152
  attributeValue
151
153
  = element
152
154
  / functionWithElement
155
+ / objectLiteral
153
156
  / $([^{}]* ("{" [^{}]* "}" [^{}]*)*) {
154
157
  const t = text().trim()
155
158
  if (t.startsWith("{") && t.endsWith("}")) {
@@ -158,6 +161,41 @@ attributeValue
158
161
  return t
159
162
  }
160
163
 
164
+ objectLiteral
165
+ = "{" _ objContent:objectContent _ "}" {
166
+ return `{ ${objContent} }`;
167
+ }
168
+
169
+ objectContent
170
+ = prop:objectProperty rest:(_ "," _ objectProperty)* {
171
+ return [prop].concat(rest.map(r => r[3])).join(', ');
172
+ }
173
+ / "" { return ""; }
174
+
175
+ objectProperty
176
+ = key:identifier _ ":" _ value:propertyValue {
177
+ return `${key}: ${value}`;
178
+ }
179
+ / key:identifier {
180
+ return key;
181
+ }
182
+
183
+ propertyValue
184
+ = nestedObject
185
+ / element
186
+ / functionWithElement
187
+ / stringLiteral
188
+ / identifier
189
+
190
+ nestedObject
191
+ = "{" _ objContent:objectContent _ "}" {
192
+ return `{ ${objContent} }`;
193
+ }
194
+
195
+ stringLiteral
196
+ = '"' chars:[^"]* '"' { return text(); }
197
+ / "'" chars:[^']* "'" { return text(); }
198
+
161
199
  functionWithElement
162
200
  = "(" _ params:functionParams? _ ")" _ "=>" _ elem:element {
163
201
  return `${params ? `(${params}) =>` : '() =>'} ${elem}`;
@@ -216,7 +254,7 @@ textElement
216
254
  }
217
255
 
218
256
  forLoop
219
- = _ "@for" _ "(" _ variableName:(tupleDestructuring / identifier) _ "of" _ iterable:identifier _ ")" _ "{" _ content:content _ "}" _ {
257
+ = _ "@for" _ "(" _ variableName:(tupleDestructuring / identifier) _ "of" _ iterable:iterable _ ")" _ "{" _ content:content _ "}" _ {
220
258
  return `loop(${iterable}, ${variableName} => ${content})`;
221
259
  }
222
260
 
@@ -245,7 +283,23 @@ variableName
245
283
  = [a-zA-Z_][a-zA-Z0-9_]* { return text(); }
246
284
 
247
285
  iterable
248
- = [a-zA-Z_][a-zA-Z0-9_]* { return text(); }
286
+ = id:identifier "(" _ args:functionArgs? _ ")" { // Direct function call
287
+ return `${id}(${args || ''})`;
288
+ }
289
+ / first:identifier "." rest:dotFunctionChain { // Dot notation possibly with function call
290
+ return `${first}.${rest}`;
291
+ }
292
+ / id:identifier { return id; }
293
+
294
+ dotFunctionChain
295
+ = segment:identifier "(" _ args:functionArgs? _ ")" rest:("." dotFunctionChain)? {
296
+ const restStr = rest ? `.${rest[1]}` : '';
297
+ return `${segment}(${args || ''})${restStr}`;
298
+ }
299
+ / segment:identifier rest:("." dotFunctionChain)? {
300
+ const restStr = rest ? `.${rest[1]}` : '';
301
+ return `${segment}${restStr}`;
302
+ }
249
303
 
250
304
  condition
251
305
  = functionCall
package/index.ts CHANGED
@@ -35,6 +35,8 @@ export default function canvasengine() {
35
35
  "NineSliceSprite",
36
36
  "Rect",
37
37
  "Circle",
38
+ "Ellipse",
39
+ "Triangle",
38
40
  "TilingSprite",
39
41
  "svg",
40
42
  "Video"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canvasengine/compiler",
3
- "version": "2.0.0-beta.14",
3
+ "version": "2.0.0-beta.16",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -93,12 +93,28 @@ describe("Compiler", () => {
93
93
  expect(output).toBe(`h(Canvas, { width: 20 })`);
94
94
  });
95
95
 
96
+
96
97
  test("should compile component with object attribute", () => {
97
98
  const input = `<Canvas width={ {x: 10, y: 20} } />`;
98
99
  const output = parser.parse(input);
99
100
  expect(output).toBe(`h(Canvas, { width: ({x: 10, y: 20}) })`);
100
101
  });
101
102
 
103
+ test("should compile component with complex object attribute", () => {
104
+ const input = `<Sprite
105
+ sheet={{
106
+ definition,
107
+ playing: "stand",
108
+ params: {
109
+ direction: "right"
110
+ },
111
+ onFinish
112
+ }}
113
+ />`;
114
+ const output = parser.parse(input);
115
+ expect(output).toBe(`h(Sprite, { sheet: { definition, playing: "stand", params: { direction: "right" }, onFinish } })`);
116
+ });
117
+
102
118
  test("should compile component with deep object attribute", () => {
103
119
  const input = `<Canvas width={deep.value} />`;
104
120
  const output = parser.parse(input);
@@ -184,6 +200,18 @@ describe("Compiler", () => {
184
200
  );
185
201
  });
186
202
 
203
+ test("should compile component with multiple children", () => {
204
+ const input = `<Container>
205
+ <Container></Container>
206
+ <Container></Container>
207
+ </Container>
208
+ `;
209
+ const output = parser.parse(input);
210
+ expect(output.replace(/\s+/g, "")).toBe(
211
+ `h(Container,null,[h(Container),h(Container)])`.replace(/\s+/g, "")
212
+ );
213
+ });
214
+
187
215
  test("should compile component with multi children", () => {
188
216
  const input = `
189
217
  <Sprite />
@@ -288,6 +316,66 @@ describe("Loop", () => {
288
316
  );
289
317
  });
290
318
 
319
+ test("should compile loop with object", () => {
320
+ const input = `
321
+ @for (sprite of sprites.items) {
322
+ <Sprite />
323
+ }
324
+ `;
325
+ const output = parser.parse(input);
326
+ expect(output.replace(/\s+/g, "")).toBe(
327
+ `loop(sprites.items,sprite=>h(Sprite))`.replace(/\s+/g, "")
328
+ );
329
+ });
330
+
331
+ test("should compile loop with deep object", () => {
332
+ const input = `
333
+ @for (sprite of sprites.items.items) {
334
+ <Sprite />
335
+ }
336
+ `;
337
+ const output = parser.parse(input);
338
+ expect(output.replace(/\s+/g, "")).toBe(
339
+ `loop(sprites.items.items,sprite=>h(Sprite))`.replace(/\s+/g, "")
340
+ );
341
+ });
342
+
343
+ test("should compile loop with function", () => {
344
+ const input = `
345
+ @for (sprite of sprites()) {
346
+ <Sprite />
347
+ }
348
+ `;
349
+ const output = parser.parse(input);
350
+ expect(output.replace(/\s+/g, "")).toBe(
351
+ `loop(sprites(),sprite=>h(Sprite))`.replace(/\s+/g, "")
352
+ );
353
+ });
354
+
355
+ test("should compile loop with function and params", () => {
356
+ const input = `
357
+ @for (sprite of sprites(x, y)) {
358
+ <Sprite />
359
+ }
360
+ `;
361
+ const output = parser.parse(input);
362
+ expect(output.replace(/\s+/g, "")).toBe(
363
+ `loop(sprites(x,y),sprite=>h(Sprite))`.replace(/\s+/g, "")
364
+ );
365
+ });
366
+
367
+ test("should compile loop with object and function and params", () => {
368
+ const input = `
369
+ @for (sprite of sprites.items(x, y)) {
370
+ <Sprite />
371
+ }
372
+ `;
373
+ const output = parser.parse(input);
374
+ expect(output.replace(/\s+/g, "")).toBe(
375
+ `loop(sprites.items(x,y),sprite=>h(Sprite))`.replace(/\s+/g, "")
376
+ );
377
+ });
378
+
291
379
  test("should compile loop with destructuring", () => {
292
380
  const input = `
293
381
  @for ((sprite, index) of sprites) {