@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 +2 -0
- package/dist/index.js.map +1 -1
- package/grammar.pegjs +61 -7
- package/index.ts +2 -0
- package/package.json +1 -1
- package/tests/compiler.spec.ts +88 -0
package/dist/index.js
CHANGED
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:
|
|
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
|
-
=
|
|
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
package/package.json
CHANGED
package/tests/compiler.spec.ts
CHANGED
|
@@ -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) {
|