@canvasengine/compiler 2.0.0-beta.5 → 2.0.0-beta.50
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/grammar.pegjs +1119 -0
- package/dist/grammar2.pegjs +995 -0
- package/dist/index.d.ts +35 -2
- package/dist/index.js +335 -10
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
- package/grammar.pegjs +0 -186
- package/index.ts +0 -167
- package/tests/compiler.spec.ts +0 -352
- package/tsconfig.json +0 -29
- package/tsup.config.ts +0 -14
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
|
-
}
|
package/tests/compiler.spec.ts
DELETED
|
@@ -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
|
-
});
|