@canvasengine/compiler 2.0.0-beta.4 → 2.0.0-beta.6
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 -1
- package/dist/index.js.map +1 -1
- package/grammar.pegjs +57 -5
- package/index.ts +2 -1
- package/package.json +1 -1
- package/tests/compiler.spec.ts +65 -8
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 ];\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":";AAAA,SAAS,oBAAoB;AAC7B,SAAS,aAAa;AACtB,OAAO,QAAQ;AACf,OAAO,SAAS;AAChB,OAAO,UAAU;AACjB,YAAY,QAAQ;AACpB,SAAS,qBAAqB;AAE9B,IAAM,EAAE,SAAS,IAAI;AAErB,IAAM,UAAU;AAED,SAAR,eAAgC;AACrC,QAAM,SAAS,aAAa,SAAS;AAGrC,QAAM,aAAa,cAAc,YAAY,GAAG;AAChD,QAAM,YAAY,KAAK,QAAQ,UAAU;AAEzC,QAAM,UAAU,GAAG;AAAA,IACjB,KAAK,KAAK,WAAW,eAAe,EAAE,QAAQ,sBAAsB,eAAe;AAAA,IACrF;AAAA,EAAM;AACN,QAAM,SAAS,SAAS,OAAO;AAC/B,QAAM,QAAQ,QAAQ,IAAI,aAAa;AACvC,QAAM,eAAe;AAErB,QAAM,uBAAuB;AAAA,IAC3B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU,MAAc,IAAY;AAClC,UAAI,CAAC,OAAO,EAAE,EAAG;AAGjB,YAAM,cAAc,KAAK,MAAM,8BAA8B;AAC7D,UAAI,gBAAgB,cAAc,YAAY,CAAC,EAAE,KAAK,IAAI;AAG1D,UAAI,WAAW,KAAK,QAAQ,8BAA8B,EAAE,EACzD,QAAQ,cAAc,EAAE;AAG3B,iBAAW,SAAS,QAAQ,2BAA2B,CAAC,OAAO,YAAY;AACzE,eAAO,iBAAiB,QAAQ,KAAK,CAAC;AAAA,MACxC,CAAC;AAED,YAAM,iBAAiB,OAAO,MAAM,QAAQ;AAG5C,uBAAiB,eAAe;AAEhC,UAAI,iBAAoB,mBAAgB,eAAe;AAAA,QACrD,iBAAiB;AAAA,UACf,QAAW,cAAW;AAAA,QACxB;AAAA,MACF,CAAC,EAAE;AAGH,uBAAiB,eAAe,MAAM,YAAY,EAAE,CAAC;AAGrD,YAAM,SAAS,MAAM,gBAAgB;AAAA,QACnC,YAAY;AAAA,QACZ,aAAa;AAAA,MACf,CAAC;AAGD,YAAM,UAAU,OAAO,KAAK;AAAA,QAC1B,CAAC,SAAS,KAAK,SAAS;AAAA,MAC1B;AAGA,YAAM,gBAAgB,OAAO,KAC1B,OAAO,CAAC,SAAS,KAAK,SAAS,mBAAmB,EAClD,IAAI,CAAC,SAAS,eAAe,MAAM,KAAK,OAAO,KAAK,GAAG,CAAC,EACxD,KAAK,IAAI;AAEZ,UAAI,cAAc,QACf,IAAI,CAAC,QAAQ;AACZ,YAAI,aAAa,eAAe,MAAM,IAAI,OAAO,IAAI,GAAG;AACxD,YAAI,SAAS,WAAW,SAAS,qBAAqB,GAAG;AACvD,uBAAa,WAAW;AAAA,YACtB;AAAA,YACA,SAAS,OAAO;AAAA,UAClB;AAAA,QACF;AACA,eAAO;AAAA,MACT,CAAC,EACA,KAAK,IAAI;AAGZ,YAAM,kBAAkB,CAAC,KAAK,YAAY,QAAQ,MAAM;AAGxD,YAAM,iBAAiB,gBAAgB;AAAA,QACrC,CAAC,eACC,CAAC,QAAQ;AAAA,UACP,CAAC,QACC,IAAI,cACJ,IAAI,WAAW;AAAA,YACb,CAAC,SACC,KAAK,SAAS,qBACd,KAAK,YACL,UAAU,KAAK,YACf,KAAK,SAAS,SAAS;AAAA,UAC3B;AAAA,QACJ;AAAA,MACJ;AAGA,UAAI,eAAe,SAAS,GAAG;AAC7B,cAAM,uBAAuB,YAAY,eAAe;AAAA,UACtD;AAAA,QACF,CAAC,WAAW,QAAQ,IAAI,OAAO,MAAM,gBAAgB;AACrD,sBAAc,GAAG,oBAAoB;AAAA,EAAK,WAAW;AAAA,MACvD;AAGA,YAAM,mBAAmB,qBAAqB;AAAA,QAAO,CAAC,cACpD,eAAe,SAAS,KAAK,SAAS,EAAE;AAAA,MAC1C;AAGA,uBAAiB,QAAQ,CAAC,cAAc;AACtC,cAAM,kBAAkB,YAAY,SAAS,WAC3C,QAAQ,IAAI,OAAO,MAAM,gBAC3B;AACA,YAAI,CAAC,YAAY,SAAS,eAAe,GAAG;AAC1C,wBAAc,GAAG,eAAe;AAAA,EAAK,WAAW;AAAA,QAClD;AAAA,MACF,CAAC;AAGD,YAAM,SAAS,OAAO;AAAA,QACpB,WAAW;AAAA,iDAC8B,QAAQ,IAAI,OAAO,MAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA,UAKhF,aAAa;AAAA,sBACD,cAAc;AAAA;AAAA;AAAA;AAK9B,aAAO;AAAA,QACL,MAAM;AAAA,QACN,KAAK;AAAA,MACP;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
package/grammar.pegjs
CHANGED
|
@@ -72,15 +72,23 @@ eventHandler
|
|
|
72
72
|
|
|
73
73
|
dynamicAttribute
|
|
74
74
|
= attributeName:attributeName _ "=" _ "{" _ attributeValue:attributeValue _ "}" {
|
|
75
|
-
if (attributeValue.
|
|
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
|
-
|
|
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}`;
|
|
@@ -158,7 +187,30 @@ iterable
|
|
|
158
187
|
= [a-zA-Z_][a-zA-Z0-9_]* { return text(); }
|
|
159
188
|
|
|
160
189
|
condition
|
|
161
|
-
=
|
|
190
|
+
= functionCall
|
|
191
|
+
/ $([^)]*) { return text().trim(); }
|
|
192
|
+
|
|
193
|
+
functionCall
|
|
194
|
+
= name:identifier "(" args:functionArgs? ")" {
|
|
195
|
+
return `${name}(${args || ''})`;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
functionArgs
|
|
199
|
+
= arg:functionArg rest:("," _ functionArg)* {
|
|
200
|
+
return [arg].concat(rest.map(r => r[2])).join(', ');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
functionArg
|
|
204
|
+
= _ value:(identifier / number / string) _ {
|
|
205
|
+
return value;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
number
|
|
209
|
+
= [0-9]+ ("." [0-9]+)? { return text(); }
|
|
210
|
+
|
|
211
|
+
string
|
|
212
|
+
= '"' chars:[^"]* '"' { return text(); }
|
|
213
|
+
/ "'" chars:[^']* "'" { return text(); }
|
|
162
214
|
|
|
163
215
|
eventAction
|
|
164
216
|
= [^"]* { return text(); }
|
package/index.ts
CHANGED
package/package.json
CHANGED
package/tests/compiler.spec.ts
CHANGED
|
@@ -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:
|
|
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:
|
|
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:
|
|
99
|
+
expect(output).toBe(`h(Canvas, { width: [10, 20] })`);
|
|
94
100
|
});
|
|
95
101
|
|
|
96
102
|
test("should compile component with dynamic array attribute", () => {
|
|
@@ -171,11 +177,52 @@ describe("Compiler", () => {
|
|
|
171
177
|
expect(output).toBe(`h(Sprite, { click: () => console.log('click') })`);
|
|
172
178
|
});
|
|
173
179
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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", () => {
|
|
@@ -234,6 +281,16 @@ describe("Condition", () => {
|
|
|
234
281
|
expect(output).toBe(`cond(sprite.visible, () => h(Sprite))`);
|
|
235
282
|
});
|
|
236
283
|
|
|
284
|
+
test("should compile condition when function value", () => {
|
|
285
|
+
const input = `
|
|
286
|
+
@if (val()) {
|
|
287
|
+
<Sprite />
|
|
288
|
+
}
|
|
289
|
+
`;
|
|
290
|
+
const output = parser.parse(input);
|
|
291
|
+
expect(output).toBe(`cond(val(), () => h(Sprite))`);
|
|
292
|
+
});
|
|
293
|
+
|
|
237
294
|
test("should compile condition for multiple sprites", () => {
|
|
238
295
|
const input = `
|
|
239
296
|
@if (sprite) {
|