@canvasengine/compiler 2.0.0-beta.47 → 2.0.0-beta.49
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/grammar2.pegjs +879 -0
- package/dist/index.js +13 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,879 @@
|
|
|
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
|
+
/*——— Custom error handler for syntax errors ———*/
|
|
10
|
+
function parseError(error) {
|
|
11
|
+
// error.expected : array of { type, description }
|
|
12
|
+
// error.found : string | null
|
|
13
|
+
// error.location : { start, end }
|
|
14
|
+
const { expected, found, location } = error;
|
|
15
|
+
|
|
16
|
+
// Group expected items by description to avoid duplicates
|
|
17
|
+
const uniqueExpected = [...new Set(expected.map(e => e.description))];
|
|
18
|
+
|
|
19
|
+
// Format the expected values in a more readable way
|
|
20
|
+
const expectedDesc = uniqueExpected
|
|
21
|
+
.map(desc => `'${desc}'`)
|
|
22
|
+
.join(' or ');
|
|
23
|
+
|
|
24
|
+
const foundDesc = found === null ? 'end of input' : `'${found}'`;
|
|
25
|
+
|
|
26
|
+
generateError(
|
|
27
|
+
`Syntax error: expected ${expectedDesc} but found ${foundDesc}`,
|
|
28
|
+
location
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// List of standard HTML DOM elements
|
|
33
|
+
const domElements = new Set([
|
|
34
|
+
'a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b', 'base', 'bdi', 'bdo', 'blockquote', 'body', 'br', 'button', 'caption', 'cite', 'code', 'col', 'colgroup', 'data', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'link', 'main', 'map', 'mark', 'menu', 'meta', 'meter', 'nav', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 'samp', 's', 'script', 'section', 'select', 'slot', 'small', 'source', 'span', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'u', 'ul', 'var', 'video', 'wbr'
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
// Framework components that should NOT be transformed to DOM elements
|
|
38
|
+
const frameworkComponents = new Set([
|
|
39
|
+
'Canvas', 'Container', 'Sprite', 'Text', 'DOMElement', 'Svg', 'Button'
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
const voidElements = new Set([
|
|
43
|
+
'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta',
|
|
44
|
+
'param', 'source', 'track', 'wbr'
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
const eventAttributes = new Set([
|
|
48
|
+
'click', 'tap', 'pointertap', 'pointerdown', 'pointerup', 'pointermove',
|
|
49
|
+
'pointerover', 'pointerout', 'pointerupoutside', 'mousedown', 'mouseup',
|
|
50
|
+
'mousemove', 'mouseover', 'mouseout', 'touchstart', 'touchend', 'touchmove',
|
|
51
|
+
'touchcancel', 'rightclick', 'keydown', 'keyup', 'keypress'
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
// DisplayObject special attributes that should not be in attrs
|
|
55
|
+
const displayObjectAttributes = new Set([
|
|
56
|
+
'x', 'y', 'scale', 'anchor', 'skew', 'tint', 'rotation', 'angle',
|
|
57
|
+
'zIndex', 'roundPixels', 'cursor', 'visible', 'alpha', 'pivot', 'filters', 'maskOf',
|
|
58
|
+
'blendMode', 'filterArea', 'minWidth', 'minHeight', 'maxWidth', 'maxHeight',
|
|
59
|
+
'aspectRatio', 'flexGrow', 'flexShrink', 'flexBasis', 'rowGap', 'columnGap',
|
|
60
|
+
'positionType', 'top', 'right', 'bottom', 'left', 'objectFit', 'objectPosition',
|
|
61
|
+
'transformOrigin', 'flexDirection', 'justifyContent', 'alignItems', 'alignContent',
|
|
62
|
+
'alignSelf', 'margin', 'padding', 'border', 'gap', 'blur', 'shadow'
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
function isDOMElement(tagName) {
|
|
66
|
+
// Don't transform framework components to DOM elements
|
|
67
|
+
if (frameworkComponents.has(tagName)) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
return domElements.has(tagName.toLowerCase());
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function isVoidElement(tagName) {
|
|
74
|
+
return voidElements.has(tagName.toLowerCase());
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function formatAttributes(attributes) {
|
|
78
|
+
if (attributes.length === 0) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Check if there's exactly one attribute and it's a spread attribute
|
|
83
|
+
if (attributes.length === 1 && attributes[0].startsWith('...')) {
|
|
84
|
+
// Return the identifier directly, removing the '...'
|
|
85
|
+
return attributes[0].substring(3);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Otherwise, format as an object literal
|
|
89
|
+
const formattedAttrs = attributes.map(attr => {
|
|
90
|
+
// If it's a spread attribute, keep it as is
|
|
91
|
+
if (attr.startsWith('...')) {
|
|
92
|
+
return attr;
|
|
93
|
+
}
|
|
94
|
+
// If it's a standalone attribute (doesn't contain ':'), format as shorthand property 'name'
|
|
95
|
+
if (!attr.includes(':')) {
|
|
96
|
+
return attr; // JS object literal shorthand
|
|
97
|
+
}
|
|
98
|
+
// Otherwise (key: value), keep it as is
|
|
99
|
+
return attr;
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
return `{ ${formattedAttrs.join(', ')} }`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function formatDOMElement(tagName, attributes) {
|
|
106
|
+
if (attributes.length === 0) {
|
|
107
|
+
return `h(DOMElement, { element: "${tagName}" })`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const { domAttrs, displayObjectAttrs } = splitAttributes(attributes);
|
|
111
|
+
|
|
112
|
+
// Build the result
|
|
113
|
+
const parts = [`element: "${tagName}"`];
|
|
114
|
+
|
|
115
|
+
if (domAttrs.length > 0) {
|
|
116
|
+
parts.push(`attrs: { ${domAttrs.join(', ')} }`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (displayObjectAttrs.length > 0) {
|
|
120
|
+
parts.push(...displayObjectAttrs);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return `h(DOMElement, { ${parts.join(', ')} })`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function splitAttributes(attributes) {
|
|
127
|
+
const domAttrs = [];
|
|
128
|
+
const displayObjectAttrs = [];
|
|
129
|
+
const classValues = [];
|
|
130
|
+
let classInsertIndex = null;
|
|
131
|
+
|
|
132
|
+
attributes.forEach(attr => {
|
|
133
|
+
// Handle spread attributes
|
|
134
|
+
if (attr.startsWith('...')) {
|
|
135
|
+
displayObjectAttrs.push(attr);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Extract attribute name and value (if present)
|
|
140
|
+
let attrName;
|
|
141
|
+
let attrValue;
|
|
142
|
+
if (attr.includes(':')) {
|
|
143
|
+
const colonIndex = attr.indexOf(':');
|
|
144
|
+
attrName = attr.slice(0, colonIndex).trim().replace(/['"]/g, '');
|
|
145
|
+
attrValue = attr.slice(colonIndex + 1).trim();
|
|
146
|
+
} else {
|
|
147
|
+
// Standalone attribute
|
|
148
|
+
attrName = attr.replace(/['"]/g, '');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Check if it's a DisplayObject attribute
|
|
152
|
+
if (displayObjectAttributes.has(attrName)) {
|
|
153
|
+
displayObjectAttrs.push(attr);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (attrName === 'class' && attrValue !== undefined) {
|
|
158
|
+
classValues.push(attrValue);
|
|
159
|
+
if (classInsertIndex === null) {
|
|
160
|
+
classInsertIndex = domAttrs.length;
|
|
161
|
+
}
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
domAttrs.push(attr);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
if (classValues.length > 0) {
|
|
169
|
+
const mergedClass = classValues.length === 1
|
|
170
|
+
? `class: ${classValues[0]}`
|
|
171
|
+
: `class: [${classValues.join(', ')}]`;
|
|
172
|
+
if (classInsertIndex === null) {
|
|
173
|
+
domAttrs.push(mergedClass);
|
|
174
|
+
} else {
|
|
175
|
+
domAttrs.splice(classInsertIndex, 0, mergedClass);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return { domAttrs, displayObjectAttrs };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function hasFunctionCall(value) {
|
|
183
|
+
return /[a-zA-Z_][a-zA-Z0-9_]*\s*\(/.test(value);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function hasIdentifier(value) {
|
|
187
|
+
return /[a-zA-Z_]/.test(value);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function isSimpleAccessor(value) {
|
|
191
|
+
return /^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*)*$/.test(value.trim());
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function formatObjectLiteralSpacing(value) {
|
|
195
|
+
const trimmed = value.trim();
|
|
196
|
+
if (!trimmed.startsWith('{') || !trimmed.endsWith('}')) {
|
|
197
|
+
return value;
|
|
198
|
+
}
|
|
199
|
+
const inner = trimmed.slice(1, -1).trim();
|
|
200
|
+
return `{ ${inner} }`;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function transformBareIdentifiersToSignals(value) {
|
|
204
|
+
return value.replace(/\b([a-zA-Z_][a-zA-Z0-9_]*)\b/g, (match, name, offset) => {
|
|
205
|
+
if (['true', 'false', 'null'].includes(name)) {
|
|
206
|
+
return match;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const beforeMatch = value.substring(0, offset);
|
|
210
|
+
const singleQuotesBefore = (beforeMatch.match(/'/g) || []).length;
|
|
211
|
+
const doubleQuotesBefore = (beforeMatch.match(/"/g) || []).length;
|
|
212
|
+
if (singleQuotesBefore % 2 === 1 || doubleQuotesBefore % 2 === 1) {
|
|
213
|
+
return match;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const charBefore = offset > 0 ? value[offset - 1] : '';
|
|
217
|
+
const charAfter = offset + match.length < value.length ? value[offset + match.length] : '';
|
|
218
|
+
|
|
219
|
+
if (charBefore === '.' || charAfter === '.') {
|
|
220
|
+
return match;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const afterSlice = value.slice(offset + match.length);
|
|
224
|
+
if (/^\s*\(/.test(afterSlice)) {
|
|
225
|
+
return match;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return `${name}()`;
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
start
|
|
234
|
+
= _ elements:(element)* _ {
|
|
235
|
+
if (elements.length === 1) {
|
|
236
|
+
return elements[0];
|
|
237
|
+
}
|
|
238
|
+
return `[${elements.join(',')}]`;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
element "component or control structure"
|
|
242
|
+
= forLoop
|
|
243
|
+
/ ifCondition
|
|
244
|
+
/ svgElement
|
|
245
|
+
/ domElementWithText
|
|
246
|
+
/ domElementWithMixedContent
|
|
247
|
+
/ selfClosingElement
|
|
248
|
+
/ voidElement
|
|
249
|
+
/ openCloseElement
|
|
250
|
+
/ openUnclosedTag
|
|
251
|
+
/ comment
|
|
252
|
+
|
|
253
|
+
selfClosingElement "self-closing component tag"
|
|
254
|
+
= _ "<" _ tagName:tagName _ attributes:attributes _ "/>" _ {
|
|
255
|
+
// Check if it's a DOM element
|
|
256
|
+
if (isDOMElement(tagName)) {
|
|
257
|
+
return formatDOMElement(tagName, attributes);
|
|
258
|
+
}
|
|
259
|
+
// Otherwise, treat as regular component
|
|
260
|
+
const attrsString = formatAttributes(attributes);
|
|
261
|
+
return attrsString ? `h(${tagName}, ${attrsString})` : `h(${tagName})`;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
voidElement "void DOM element tag"
|
|
265
|
+
= _ "<" _ tagName:tagName &{ return isVoidElement(tagName); } _ attributes:attributes _ ">" _ {
|
|
266
|
+
return formatDOMElement(tagName, attributes);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
domElementWithText "DOM element with text content"
|
|
270
|
+
= "<" _ tagName:tagName &{ return !isVoidElement(tagName); } _ attributes:attributes _ ">" _ text:simpleTextContent _ "</" _ closingTagName:tagName _ ">" _ {
|
|
271
|
+
if (tagName !== closingTagName) {
|
|
272
|
+
generateError(
|
|
273
|
+
`Mismatched tag: opened <${tagName}> but closed </${closingTagName}>`,
|
|
274
|
+
location()
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (isDOMElement(tagName)) {
|
|
279
|
+
if (attributes.length === 0) {
|
|
280
|
+
return `h(DOMElement, { element: "${tagName}", textContent: ${text} })`;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const { domAttrs, displayObjectAttrs } = splitAttributes(attributes);
|
|
284
|
+
|
|
285
|
+
// Build the result
|
|
286
|
+
const parts = [`element: "${tagName}"`];
|
|
287
|
+
|
|
288
|
+
if (domAttrs.length > 0) {
|
|
289
|
+
parts.push(`attrs: { ${domAttrs.join(', ')} }`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
parts.push(`textContent: ${text}`);
|
|
293
|
+
|
|
294
|
+
if (displayObjectAttrs.length > 0) {
|
|
295
|
+
parts.push(...displayObjectAttrs);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return `h(DOMElement, { ${parts.join(', ')} })`;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// If not a DOM element, fall back to regular parsing
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
domElementWithMixedContent "DOM element with mixed content"
|
|
306
|
+
= "<" _ tagName:tagName &{ return isDOMElement(tagName) && !isVoidElement(tagName); } _ attributes:attributes _ ">" _ children:domContent _ "</" _ closingTagName:tagName _ ">" _ {
|
|
307
|
+
if (tagName !== closingTagName) {
|
|
308
|
+
generateError(
|
|
309
|
+
`Mismatched tag: opened <${tagName}> but closed </${closingTagName}>`,
|
|
310
|
+
location()
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const childrenContent = children ? children : null;
|
|
315
|
+
|
|
316
|
+
if (attributes.length === 0) {
|
|
317
|
+
if (childrenContent) {
|
|
318
|
+
return `h(DOMElement, { element: "${tagName}" }, ${childrenContent})`;
|
|
319
|
+
} else {
|
|
320
|
+
return `h(DOMElement, { element: "${tagName}" })`;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const { domAttrs, displayObjectAttrs } = splitAttributes(attributes);
|
|
325
|
+
|
|
326
|
+
// Build the result
|
|
327
|
+
const parts = [`element: "${tagName}"`];
|
|
328
|
+
|
|
329
|
+
if (domAttrs.length > 0) {
|
|
330
|
+
parts.push(`attrs: { ${domAttrs.join(', ')} }`);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (displayObjectAttrs.length > 0) {
|
|
334
|
+
parts.push(...displayObjectAttrs);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (childrenContent) {
|
|
338
|
+
return `h(DOMElement, { ${parts.join(', ')} }, ${childrenContent})`;
|
|
339
|
+
} else {
|
|
340
|
+
return `h(DOMElement, { ${parts.join(', ')} })`;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
simpleTextContent "simple text content"
|
|
345
|
+
= parts:(simpleDynamicPart / simpleTextPart)+ {
|
|
346
|
+
const validParts = parts.filter(p => p !== null);
|
|
347
|
+
if (validParts.length === 0) return null;
|
|
348
|
+
if (validParts.length === 1) return validParts[0];
|
|
349
|
+
|
|
350
|
+
// Multiple parts - need to concatenate
|
|
351
|
+
const normalizedParts = validParts.map(part => {
|
|
352
|
+
if (typeof part === 'string' && part.startsWith('computed(() => ') && part.endsWith(')')) {
|
|
353
|
+
return part.slice('computed(() => '.length, -1);
|
|
354
|
+
}
|
|
355
|
+
return part;
|
|
356
|
+
});
|
|
357
|
+
const hasSignals = normalizedParts.some(part => part && part.includes && part.includes('()'));
|
|
358
|
+
if (hasSignals) {
|
|
359
|
+
return `computed(() => ${normalizedParts.join(' + ')})`;
|
|
360
|
+
}
|
|
361
|
+
return normalizedParts.join(' + ');
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
simpleTextPart "simple text part"
|
|
365
|
+
= !("@for" / "@if") text:$([^<{@]+) {
|
|
366
|
+
const trimmed = text.trim();
|
|
367
|
+
if (!trimmed) return null;
|
|
368
|
+
const escaped = text
|
|
369
|
+
.replace(/\\/g, '\\\\')
|
|
370
|
+
.replace(/'/g, "\\'")
|
|
371
|
+
.replace(/\r/g, '\\r')
|
|
372
|
+
.replace(/\n/g, '\\n')
|
|
373
|
+
.replace(/\t/g, '\\t');
|
|
374
|
+
return `'${escaped}'`;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
simpleDynamicPart "simple dynamic part"
|
|
378
|
+
= "{{" _ expr:attributeValue _ "}}" {
|
|
379
|
+
const trimmedExpr = expr.trim();
|
|
380
|
+
if (!trimmedExpr) {
|
|
381
|
+
return trimmedExpr;
|
|
382
|
+
}
|
|
383
|
+
if (hasFunctionCall(trimmedExpr)) {
|
|
384
|
+
return `computed(() => ${trimmedExpr})`;
|
|
385
|
+
}
|
|
386
|
+
return trimmedExpr;
|
|
387
|
+
}
|
|
388
|
+
/ "{" _ expr:attributeValue _ "}" {
|
|
389
|
+
const trimmedExpr = expr.trim();
|
|
390
|
+
if (!trimmedExpr) {
|
|
391
|
+
return trimmedExpr;
|
|
392
|
+
}
|
|
393
|
+
if (hasFunctionCall(trimmedExpr)) {
|
|
394
|
+
return `computed(() => ${trimmedExpr})`;
|
|
395
|
+
}
|
|
396
|
+
return trimmedExpr;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
openCloseElement "component with content"
|
|
400
|
+
= "<" _ tagName:tagName _ attributes:attributes _ ">" _ content:content _ "</" _ closingTagName:tagName _ ">" _ {
|
|
401
|
+
if (tagName !== closingTagName) {
|
|
402
|
+
generateError(
|
|
403
|
+
`Mismatched tag: opened <${tagName}> but closed </${closingTagName}>`,
|
|
404
|
+
location()
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Check if it's a DOM element
|
|
409
|
+
if (isDOMElement(tagName)) {
|
|
410
|
+
const children = content ? content : null;
|
|
411
|
+
|
|
412
|
+
if (attributes.length === 0) {
|
|
413
|
+
if (children) {
|
|
414
|
+
return `h(DOMElement, { element: "${tagName}" }, ${children})`;
|
|
415
|
+
} else {
|
|
416
|
+
return `h(DOMElement, { element: "${tagName}" })`;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const { domAttrs, displayObjectAttrs } = splitAttributes(attributes);
|
|
421
|
+
|
|
422
|
+
// Build the result
|
|
423
|
+
const parts = [`element: "${tagName}"`];
|
|
424
|
+
|
|
425
|
+
if (domAttrs.length > 0) {
|
|
426
|
+
parts.push(`attrs: { ${domAttrs.join(', ')} }`);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (displayObjectAttrs.length > 0) {
|
|
430
|
+
parts.push(...displayObjectAttrs);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if (children) {
|
|
434
|
+
return `h(DOMElement, { ${parts.join(', ')} }, ${children})`;
|
|
435
|
+
} else {
|
|
436
|
+
return `h(DOMElement, { ${parts.join(', ')} })`;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Otherwise, treat as regular component
|
|
441
|
+
const attrsString = formatAttributes(attributes);
|
|
442
|
+
const children = content ? content : null;
|
|
443
|
+
if (attrsString && children) {
|
|
444
|
+
return `h(${tagName}, ${attrsString}, ${children})`;
|
|
445
|
+
} else if (attrsString) {
|
|
446
|
+
return `h(${tagName}, ${attrsString})`;
|
|
447
|
+
} else if (children) {
|
|
448
|
+
return `h(${tagName}, null, ${children})`;
|
|
449
|
+
} else {
|
|
450
|
+
return `h(${tagName})`;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
attributes "component attributes"
|
|
455
|
+
= attrs:(attribute (_ attribute)*)? {
|
|
456
|
+
return attrs
|
|
457
|
+
? [attrs[0]].concat(attrs[1].map(a => a[1]))
|
|
458
|
+
: [];
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
attribute "attribute"
|
|
462
|
+
= staticAttribute
|
|
463
|
+
/ dynamicAttribute
|
|
464
|
+
/ eventHandler
|
|
465
|
+
/ spreadAttribute
|
|
466
|
+
/ unclosedQuote
|
|
467
|
+
/ unclosedBrace
|
|
468
|
+
|
|
469
|
+
spreadAttribute "spread attribute"
|
|
470
|
+
= "..." expr:(functionCallExpr / dotNotation) {
|
|
471
|
+
return "..." + expr;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
functionCallExpr "function call"
|
|
475
|
+
= name:dotNotation "(" args:functionArgs? ")" {
|
|
476
|
+
return `${name}(${args || ''})`;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
dotNotation "property access"
|
|
480
|
+
= first:identifier rest:("." identifier)* {
|
|
481
|
+
return text();
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
eventHandler "event handler"
|
|
485
|
+
= "@" eventName:identifier _ "=" _ "{" _ handlerName:attributeValue _ "}" {
|
|
486
|
+
const needsQuotes = /[^a-zA-Z0-9_$]/.test(eventName);
|
|
487
|
+
const formattedName = needsQuotes ? `'${eventName}'` : eventName;
|
|
488
|
+
return `${formattedName}: ${handlerName}`;
|
|
489
|
+
}
|
|
490
|
+
/ "@" eventName:attributeName _ {
|
|
491
|
+
const needsQuotes = /[^a-zA-Z0-9_$]/.test(eventName);
|
|
492
|
+
return needsQuotes ? `'${eventName}'` : eventName;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
dynamicAttribute "dynamic attribute"
|
|
496
|
+
= attributeName:attributeName _ "=" _ "{" _ attributeValue:attributeValue _ "}" {
|
|
497
|
+
// Check if attributeName needs to be quoted (contains dash or other invalid JS identifier chars)
|
|
498
|
+
const needsQuotes = /[^a-zA-Z0-9_$]/.test(attributeName);
|
|
499
|
+
const formattedName = needsQuotes ? `'${attributeName}'` : attributeName;
|
|
500
|
+
|
|
501
|
+
if (eventAttributes.has(attributeName)) {
|
|
502
|
+
return `${formattedName}: ${attributeValue}`;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// If it's a complex object with strings, preserve it as is
|
|
506
|
+
if (attributeValue.trim().startsWith('{') && attributeValue.trim().endsWith('}') &&
|
|
507
|
+
(attributeValue.includes('"') || attributeValue.includes("'"))) {
|
|
508
|
+
return `${formattedName}: ${attributeValue}`;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// If it's a template string, keep it as-is
|
|
512
|
+
if (attributeValue.trim().startsWith('`') && attributeValue.trim().endsWith('`')) {
|
|
513
|
+
return formattedName + ': ' + attributeValue;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Handle other types of values
|
|
517
|
+
if (attributeValue.startsWith('h(') || attributeValue.includes('=>')) {
|
|
518
|
+
return `${formattedName}: ${attributeValue}`;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
const trimmedValue = attributeValue.trim();
|
|
522
|
+
if (trimmedValue.match(/^[a-zA-Z_]\w*$/)) {
|
|
523
|
+
return `${formattedName}: ${attributeValue}`;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
if (/^\d+(\.\d+)?$/.test(trimmedValue) || ['true', 'false', 'null'].includes(trimmedValue)) {
|
|
527
|
+
return `${formattedName}: ${attributeValue}`;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
if (isSimpleAccessor(trimmedValue)) {
|
|
531
|
+
return `${formattedName}: ${attributeValue}`;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const isObjectLiteral = trimmedValue.startsWith('{') && trimmedValue.endsWith('}');
|
|
535
|
+
const isArrayLiteral = trimmedValue.startsWith('[') && trimmedValue.endsWith(']');
|
|
536
|
+
if (isObjectLiteral) {
|
|
537
|
+
const formattedObject = formatObjectLiteralSpacing(attributeValue);
|
|
538
|
+
if (hasFunctionCall(trimmedValue)) {
|
|
539
|
+
return `${formattedName}: computed(() => (${formattedObject}))`;
|
|
540
|
+
}
|
|
541
|
+
return `${formattedName}: ${formattedObject}`;
|
|
542
|
+
}
|
|
543
|
+
if (isArrayLiteral) {
|
|
544
|
+
if (hasFunctionCall(trimmedValue)) {
|
|
545
|
+
return `${formattedName}: computed(() => ${attributeValue})`;
|
|
546
|
+
}
|
|
547
|
+
return `${formattedName}: ${attributeValue}`;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
if (hasFunctionCall(trimmedValue)) {
|
|
551
|
+
return `${formattedName}: computed(() => ${attributeValue})`;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
if (!hasIdentifier(trimmedValue)) {
|
|
555
|
+
return `${formattedName}: ${attributeValue}`;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
const computedValue = transformBareIdentifiersToSignals(attributeValue);
|
|
559
|
+
return `${formattedName}: computed(() => ${computedValue})`;
|
|
560
|
+
}
|
|
561
|
+
/ attributeName:attributeName _ {
|
|
562
|
+
const needsQuotes = /[^a-zA-Z0-9_$]/.test(attributeName);
|
|
563
|
+
return needsQuotes ? `'${attributeName}'` : attributeName;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
attributeValue "attribute value"
|
|
567
|
+
= element
|
|
568
|
+
/ functionWithElement
|
|
569
|
+
/ objectLiteral
|
|
570
|
+
/ $([^{}]* ("{" [^{}]* "}" [^{}]*)*) {
|
|
571
|
+
return text().trim()
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
objectLiteral "object literal"
|
|
575
|
+
= "{" _ objContent:objectContent _ "}" {
|
|
576
|
+
return `{ ${objContent} }`;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
objectContent
|
|
580
|
+
= prop:objectProperty rest:(_ "," _ objectProperty)* {
|
|
581
|
+
return [prop].concat(rest.map(r => r[3])).join(', ');
|
|
582
|
+
}
|
|
583
|
+
/ "" { return ""; }
|
|
584
|
+
|
|
585
|
+
objectProperty
|
|
586
|
+
= key:identifier _ ":" _ value:propertyValue {
|
|
587
|
+
return `${key}: ${value}`;
|
|
588
|
+
}
|
|
589
|
+
/ key:identifier {
|
|
590
|
+
return key;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
propertyValue
|
|
594
|
+
= nestedObject
|
|
595
|
+
/ element
|
|
596
|
+
/ functionWithElement
|
|
597
|
+
/ stringLiteral
|
|
598
|
+
/ number
|
|
599
|
+
/ identifier
|
|
600
|
+
|
|
601
|
+
nestedObject
|
|
602
|
+
= "{" _ objContent:objectContent _ "}" {
|
|
603
|
+
return `{ ${objContent} }`;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
stringLiteral
|
|
607
|
+
= '"' chars:[^"]* '"' { return text(); }
|
|
608
|
+
/ "'" chars:[^']* "'" { return text(); }
|
|
609
|
+
|
|
610
|
+
functionWithElement "function expression"
|
|
611
|
+
= "(" _ params:functionParams? _ ")" _ "=>" _ elem:element {
|
|
612
|
+
return `${params ? `(${params}) =>` : '() =>'} ${elem}`;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
functionParams
|
|
616
|
+
= destructuredParams
|
|
617
|
+
/ simpleParams
|
|
618
|
+
|
|
619
|
+
destructuredParams
|
|
620
|
+
= "{" _ param:identifier rest:(_ "," _ identifier)* _ "}" {
|
|
621
|
+
return `{${[param].concat(rest.map(r => r[3])).join(', ')}}`;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
simpleParams
|
|
625
|
+
= param:identifier rest:(_ "," _ identifier)* {
|
|
626
|
+
return [param].concat(rest.map(r => r[3])).join(', ');
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
staticAttribute "static attribute"
|
|
630
|
+
= attributeName:attributeName _ "=" _ "\"" attributeValue:staticValue "\"" {
|
|
631
|
+
const needsQuotes = /[^a-zA-Z0-9_$]/.test(attributeName);
|
|
632
|
+
const formattedName = needsQuotes ? `'${attributeName}'` : attributeName;
|
|
633
|
+
return `${formattedName}: ${attributeValue}`;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
eventAttribute
|
|
637
|
+
= "(" _ eventName:eventName _ ")" _ "=" _ "\"" eventAction:eventAction "\"" {
|
|
638
|
+
return `${eventName}: () => { ${eventAction} }`;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
staticValue
|
|
642
|
+
= [^"]+ {
|
|
643
|
+
var val = text();
|
|
644
|
+
return `'${val}'`
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
content "component content"
|
|
648
|
+
= elements:(element)* {
|
|
649
|
+
const filteredElements = elements.filter(el => el !== null);
|
|
650
|
+
if (filteredElements.length === 0) return null;
|
|
651
|
+
if (filteredElements.length === 1) return filteredElements[0];
|
|
652
|
+
return `[${filteredElements.join(', ')}]`;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
domContent "DOM content"
|
|
656
|
+
= elements:(domContentPart)* {
|
|
657
|
+
const filteredElements = elements.filter(el => el !== null);
|
|
658
|
+
if (filteredElements.length === 0) return null;
|
|
659
|
+
if (filteredElements.length === 1) return filteredElements[0];
|
|
660
|
+
return `[${filteredElements.join(', ')}]`;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
domContentPart
|
|
664
|
+
= element
|
|
665
|
+
/ simpleTextContent
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
|
|
669
|
+
textNode
|
|
670
|
+
= text:$([^<]+) {
|
|
671
|
+
const trimmed = text.trim();
|
|
672
|
+
return trimmed ? `'${trimmed}'` : null;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
textElement
|
|
676
|
+
= text:[^<>]+ {
|
|
677
|
+
const trimmed = text.join('').trim();
|
|
678
|
+
return trimmed ? JSON.stringify(trimmed) : null;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
forLoop "for loop"
|
|
682
|
+
= _ "@for" _ "(" _ variableName:(tupleDestructuring / identifier) _ "of" _ iterable:iterable _ ")" _ "{" _ content:content _ "}" _ {
|
|
683
|
+
return `loop(${iterable}, ${variableName} => ${content})`;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
tupleDestructuring "destructuring pattern"
|
|
687
|
+
= "(" _ first:identifier _ "," _ second:identifier _ ")" {
|
|
688
|
+
return `(${first}, ${second})`;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
ifCondition "if condition"
|
|
692
|
+
= _ "@if" _ "(" _ condition:condition _ ")" _ "{" _ content:content _ "}" _ elseIfs:elseIfClause* elseClause:elseClause? _ {
|
|
693
|
+
let result = `cond(${condition}, () => ${content}`;
|
|
694
|
+
|
|
695
|
+
// Add else if clauses
|
|
696
|
+
elseIfs.forEach(elseIf => {
|
|
697
|
+
result += `, [${elseIf.condition}, () => ${elseIf.content}]`;
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
// Add else clause if present
|
|
701
|
+
if (elseClause) {
|
|
702
|
+
result += `, () => ${elseClause}`;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
result += ')';
|
|
706
|
+
return result;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
elseIfClause "else if clause"
|
|
710
|
+
= _ "@else" _ "if" _ "(" _ condition:condition _ ")" _ "{" _ content:content _ "}" _ {
|
|
711
|
+
return { condition, content };
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
elseClause "else clause"
|
|
715
|
+
= _ "@else" _ "{" _ content:content _ "}" _ {
|
|
716
|
+
return content;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
tagName "tag name"
|
|
720
|
+
= tagExpression
|
|
721
|
+
|
|
722
|
+
tagExpression "tag expression"
|
|
723
|
+
= first:tagPart rest:("." tagPart)* {
|
|
724
|
+
return text();
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
tagPart "tag part"
|
|
728
|
+
= name:[a-zA-Z][a-zA-Z0-9]* args:("(" functionArgs? ")")? {
|
|
729
|
+
return text();
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
attributeName "attribute name"
|
|
733
|
+
= [a-zA-Z][a-zA-Z0-9-]* { return text(); }
|
|
734
|
+
|
|
735
|
+
eventName
|
|
736
|
+
= [a-zA-Z][a-zA-Z0-9-]* { return text(); }
|
|
737
|
+
|
|
738
|
+
variableName
|
|
739
|
+
= [a-zA-Z_][a-zA-Z0-9_]* { return text(); }
|
|
740
|
+
|
|
741
|
+
iterable "iterable expression"
|
|
742
|
+
= id:identifier "(" _ args:functionArgs? _ ")" { // Direct function call
|
|
743
|
+
return `${id}(${args || ''})`;
|
|
744
|
+
}
|
|
745
|
+
/ first:identifier "." rest:dotFunctionChain { // Dot notation possibly with function call
|
|
746
|
+
return `${first}.${rest}`;
|
|
747
|
+
}
|
|
748
|
+
/ id:identifier { return id; }
|
|
749
|
+
|
|
750
|
+
dotFunctionChain
|
|
751
|
+
= segment:identifier "(" _ args:functionArgs? _ ")" rest:("." dotFunctionChain)? {
|
|
752
|
+
const restStr = rest ? `.${rest[1]}` : '';
|
|
753
|
+
return `${segment}(${args || ''})${restStr}`;
|
|
754
|
+
}
|
|
755
|
+
/ segment:identifier rest:("." dotFunctionChain)? {
|
|
756
|
+
const restStr = rest ? `.${rest[1]}` : '';
|
|
757
|
+
return `${segment}${restStr}`;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
condition "condition expression"
|
|
761
|
+
= text:$(conditionChunk*) {
|
|
762
|
+
const originalText = text.trim();
|
|
763
|
+
if (!originalText) {
|
|
764
|
+
return originalText;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
const hasOperator = /[!<>=&|]/.test(originalText);
|
|
768
|
+
if (hasOperator) {
|
|
769
|
+
return `computed(() => ${originalText})`;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
return originalText;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
conditionChunk
|
|
776
|
+
= "(" conditionChunk* ")"
|
|
777
|
+
/ [^()]
|
|
778
|
+
|
|
779
|
+
functionCall "function call"
|
|
780
|
+
= name:identifier "(" args:functionArgs? ")" {
|
|
781
|
+
return `${name}(${args || ''})`;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
functionCallWithArgs "function call with complex args"
|
|
785
|
+
= name:identifier "(" args:complexFunctionArgs? ")" {
|
|
786
|
+
return `${name}(${args || ''})`;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
functionArgs
|
|
790
|
+
= arg:functionArg rest:("," _ functionArg)* {
|
|
791
|
+
return [arg].concat(rest.map(r => r[2])).join(', ');
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
complexFunctionArgs
|
|
795
|
+
= arg:complexFunctionArg rest:("," _ complexFunctionArg)* {
|
|
796
|
+
return [arg].concat(rest.map(r => r[2])).join(', ');
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
functionArg
|
|
800
|
+
= _ value:(identifier / number / string) _ {
|
|
801
|
+
return value;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
complexFunctionArg "complex function argument"
|
|
805
|
+
= _ value:complexArgExpression _ {
|
|
806
|
+
return value.trim();
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
complexArgExpression "complex argument expression"
|
|
810
|
+
= $([^,)]* ("(" [^)]* ")" [^,)]*)*) {
|
|
811
|
+
return text().trim();
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
number
|
|
815
|
+
= [0-9]+ ("." [0-9]+)? { return text(); }
|
|
816
|
+
|
|
817
|
+
string
|
|
818
|
+
= '"' chars:[^"]* '"' { return text(); }
|
|
819
|
+
/ "'" chars:[^']* "'" { return text(); }
|
|
820
|
+
|
|
821
|
+
eventAction
|
|
822
|
+
= [^"]* { return text(); }
|
|
823
|
+
|
|
824
|
+
_ 'whitespace'
|
|
825
|
+
= [ \t\n\r]*
|
|
826
|
+
|
|
827
|
+
identifier
|
|
828
|
+
= [a-zA-Z_][a-zA-Z0-9_]* { return text(); }
|
|
829
|
+
|
|
830
|
+
comment
|
|
831
|
+
= singleComment+ {
|
|
832
|
+
return null
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
singleComment
|
|
836
|
+
= "<!--" _ content:((!("-->") .)* "-->") _ {
|
|
837
|
+
return null;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// Add a special error detection rule for unclosed tags
|
|
841
|
+
openUnclosedTag "unclosed tag"
|
|
842
|
+
= "<" _ tagName:tagName &{ return !isVoidElement(tagName); } _ attributes:attributes _ ">" _ content:content _ !("</" _ closingTagName:tagName _ ">") {
|
|
843
|
+
generateError(
|
|
844
|
+
`Unclosed tag: <${tagName}> is missing its closing tag`,
|
|
845
|
+
location()
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
// Add error detection for unclosed quotes in static attributes
|
|
850
|
+
unclosedQuote "unclosed string"
|
|
851
|
+
= attributeName:attributeName _ "=" _ "\"" [^"]* !("\"") {
|
|
852
|
+
generateError(
|
|
853
|
+
`Missing closing quote in attribute '${attributeName}'`,
|
|
854
|
+
location()
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// Add error detection for unclosed braces in dynamic attributes
|
|
859
|
+
unclosedBrace "unclosed brace"
|
|
860
|
+
= attributeName:attributeName _ "=" _ "{" !("}" / _ "}") [^{}]* {
|
|
861
|
+
generateError(
|
|
862
|
+
`Missing closing brace in dynamic attribute '${attributeName}'`,
|
|
863
|
+
location()
|
|
864
|
+
);
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
svgElement "SVG element"
|
|
868
|
+
= "<svg" attrs:([^>]*) ">" content:svgInnerContent "</svg>" _ {
|
|
869
|
+
const attributes = attrs.join('').trim();
|
|
870
|
+
// Clean up the content by removing extra whitespace and newlines
|
|
871
|
+
const cleanContent = content.replace(/\s+/g, ' ').trim();
|
|
872
|
+
const rawContent = `<svg${attributes ? ' ' + attributes : ''}>${cleanContent}</svg>`;
|
|
873
|
+
return `h(Svg, { content: \`${rawContent}\` })`;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
svgInnerContent "SVG inner content"
|
|
877
|
+
= content:$((!("</svg>") .)*) {
|
|
878
|
+
return content;
|
|
879
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -119,15 +119,18 @@ function shaderLoader() {
|
|
|
119
119
|
}
|
|
120
120
|
function canvasengine() {
|
|
121
121
|
const filter = createFilter("**/*.ce");
|
|
122
|
+
const useLegacyGrammar = process.env.CANVASENGINE_COMPILER_V1 === "1";
|
|
122
123
|
const __filename = fileURLToPath(import.meta.url);
|
|
123
124
|
const __dirname = path.dirname(__filename);
|
|
125
|
+
const grammarFile = useLegacyGrammar ? "grammar.pegjs" : "grammar2.pegjs";
|
|
124
126
|
const grammar = fs.readFileSync(
|
|
125
|
-
path.join(__dirname,
|
|
127
|
+
path.join(__dirname, grammarFile),
|
|
126
128
|
"utf8"
|
|
127
129
|
);
|
|
128
130
|
const parser = generate(grammar);
|
|
129
131
|
const isDev = process.env.NODE_ENV === "dev";
|
|
130
132
|
const FLAG_COMMENT = "/*--[TPL]--*/";
|
|
133
|
+
let warnedAboutGrammar = false;
|
|
131
134
|
const PRIMITIVE_COMPONENTS = [
|
|
132
135
|
"Canvas",
|
|
133
136
|
"Sprite",
|
|
@@ -157,6 +160,15 @@ function canvasengine() {
|
|
|
157
160
|
name: "vite-plugin-ce",
|
|
158
161
|
transform(code, id) {
|
|
159
162
|
if (!filter(id)) return null;
|
|
163
|
+
if (!warnedAboutGrammar) {
|
|
164
|
+
warnedAboutGrammar = true;
|
|
165
|
+
const legacyNote = "Set CANVASENGINE_COMPILER_V1=1 to compile with the legacy grammar (v1).";
|
|
166
|
+
if (useLegacyGrammar) {
|
|
167
|
+
console.warn(`[canvasengine] Using legacy grammar v1. ${legacyNote}`);
|
|
168
|
+
} else {
|
|
169
|
+
console.warn(`[canvasengine] Breaking change: compiler grammar v2 is now the default. ${legacyNote}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
160
172
|
const scriptMatch = code.match(/<script>([\s\S]*?)<\/script>/);
|
|
161
173
|
let scriptContent = scriptMatch ? scriptMatch[1].trim() : "";
|
|
162
174
|
const styleTagMatch = code.match(/<style([^>]*)>([\s\S]*?)<\/style>/);
|
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\n/**\n * Generates a short hash (8 characters, letters only) from a string\n * \n * @param {string} str - The string to hash\n * @returns {string} - An 8-character hash containing only lowercase letters (a-z)\n */\nfunction generateHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash; // Convert to 32-bit integer\n }\n // Convert to positive number and map to letters only (a-z)\n // Use modulo to map to 26 letters, then convert to character\n const positiveHash = Math.abs(hash);\n let result = '';\n for (let i = 0; i < 8; i++) {\n const letterIndex = (positiveHash + i * 31) % 26; // 31 is a prime to spread values\n result += String.fromCharCode(97 + letterIndex); // 97 is 'a'\n }\n return result;\n}\n\n/**\n * Formats a syntax error message with visual pointer to the error location\n * \n * @param {string} template - The template content that failed to parse\n * @param {object} error - The error object with location information\n * @returns {string} - Formatted error message with a visual pointer\n * \n * @example\n * ```\n * const errorMessage = showErrorMessage(\"<Canvas>test(d)</Canvas>\", syntaxError);\n * // Returns a formatted error message with an arrow pointing to 'd'\n * ```\n */\nfunction showErrorMessage(template: string, error: any): string {\n if (!error.location) {\n return `Syntax error: ${error.message}`;\n }\n\n const lines = template.split('\\n');\n const { line, column } = error.location.start;\n const errorLine = lines[line - 1] || '';\n \n // Create a visual pointer with an arrow\n const pointer = ' '.repeat(column - 1) + '^';\n \n return `Syntax error at line ${line}, column ${column}: ${error.message}\\n\\n` +\n `${errorLine}\\n${pointer}\\n`;\n}\n\n/**\n * Scopes CSS selectors by prefixing them with a class selector\n * \n * This function prefixes all CSS rule selectors (not @rules) with a class\n * selector to scope the styles to a specific component instance.\n * \n * @param {string} css - The CSS content to scope\n * @param {string} scopeClass - The unique scope class to use (without the dot)\n * @returns {string} - The scoped CSS content\n * \n * @example\n * ```\n * const scoped = scopeCSS('.my-class { color: red; }', 'ce-scope-abc123');\n * // Returns: '.ce-scope-abc123 .my-class { color: red; }'\n * ```\n */\nfunction scopeCSS(css: string, scopeClass: string): string {\n const scopeSelector = `.${scopeClass}`;\n \n // Process CSS by finding rule blocks while skipping @rules\n let result = '';\n let i = 0;\n let depth = 0;\n let inRule = false;\n let selectorBuffer = '';\n \n while (i < css.length) {\n const char = css[i];\n \n if (char === '@' && !inRule && selectorBuffer === '') {\n // Found @rule - copy it as-is until matching closing brace\n const atRuleStart = i;\n i++; // Skip '@'\n \n // Find the opening brace\n while (i < css.length && css[i] !== '{') {\n i++;\n }\n \n if (i < css.length) {\n // Found opening brace, now find matching closing brace\n depth = 1;\n i++; // Skip '{'\n \n while (i < css.length && depth > 0) {\n if (css[i] === '{') depth++;\n else if (css[i] === '}') depth--;\n i++;\n }\n \n // Copy entire @rule as-is\n result += css.substring(atRuleStart, i);\n }\n continue;\n }\n \n if (char === '{' && !inRule) {\n // Start of a rule block - scope the selector we just collected\n const selectorText = selectorBuffer.trim();\n \n if (selectorText) {\n // Split selectors by comma and scope each one\n const scopedSelectors = selectorText\n .split(',')\n .map(sel => {\n const trimmed = sel.trim();\n return trimmed ? `${scopeSelector} ${trimmed}` : trimmed;\n })\n .join(', ');\n \n result += scopedSelectors;\n }\n result += ' {';\n inRule = true;\n depth = 1;\n selectorBuffer = '';\n } else if (char === '{' && inRule) {\n // Nested brace\n result += char;\n depth++;\n } else if (char === '}' && inRule) {\n result += char;\n depth--;\n if (depth === 0) {\n inRule = false;\n }\n } else if (!inRule) {\n // Collecting selector\n selectorBuffer += char;\n } else {\n // Inside rule block\n result += char;\n if (char === '{') depth++;\n }\n \n i++;\n }\n \n // Add any remaining selector (shouldn't happen in valid CSS, but handle it)\n if (selectorBuffer.trim()) {\n const scopedSelectors = selectorBuffer.trim()\n .split(',')\n .map(sel => {\n const trimmed = sel.trim();\n return trimmed ? `${scopeSelector} ${trimmed}` : trimmed;\n })\n .join(', ');\n result += scopedSelectors;\n }\n \n return result;\n}\n\n/**\n * Vite plugin to load shader files (.frag, .vert, .wgsl) as text strings\n * \n * This plugin allows importing shader files directly as string literals in your code.\n * It supports fragment shaders (.frag), vertex shaders (.vert), and WebGPU shaders (.wgsl).\n * The content is loaded as a raw string and can be used directly with graphics APIs.\n * \n * @returns {object} - Vite plugin configuration object\n * \n * @example\n * ```typescript\n * // In your vite.config.ts\n * import { shaderLoader } from './path/to/compiler'\n * \n * export default defineConfig({\n * plugins: [shaderLoader()]\n * })\n * \n * // In your code\n * import fragmentShader from './shader.frag'\n * import vertexShader from './shader.vert'\n * import computeShader from './shader.wgsl'\n * \n * console.log(fragmentShader) // Raw shader code as string\n * ```\n */\nexport function shaderLoader() {\n const filter = createFilter(/\\.(frag|vert|wgsl)$/);\n\n return {\n name: \"vite-plugin-shader-loader\",\n transform(code: string, id: string) {\n if (!filter(id)) return;\n\n // Escape the shader code to be safely embedded in a JavaScript string\n const escapedCode = code\n .replace(/\\\\/g, '\\\\\\\\') // Escape backslashes\n .replace(/`/g, '\\\\`') // Escape backticks\n .replace(/\\$/g, '\\\\$'); // Escape dollar signs\n\n // Return the shader content as a default export string\n return {\n code: `export default \\`${escapedCode}\\`;`,\n map: null,\n };\n },\n };\n}\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\"),\n \"utf8\"\n );\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 \"Navigation\",\n \"ImageMap\",\n \"NineSliceSprite\",\n \"Rect\",\n \"Circle\",\n \"Ellipse\",\n \"Triangle\",\n \"TilingSprite\",\n \"svg\",\n \"Video\",\n \"Mesh\",\n \"Svg\",\n \"DOMContainer\",\n \"DOMElement\",\n \"DOMSprite\",\n \"Button\",\n \"Joystick\"\n ];\n\n return {\n name: \"vite-plugin-ce\",\n transform(code: string, id: string) {\n if (!filter(id)) return null;\n\n // Extract the script content\n const scriptMatch = code.match(/<script>([\\s\\S]*?)<\\/script>/);\n let scriptContent = scriptMatch ? scriptMatch[1].trim() : \"\";\n \n // Extract the style tag with attributes and content\n const styleTagMatch = code.match(/<style([^>]*)>([\\s\\S]*?)<\\/style>/);\n let styleContent = \"\";\n let isScoped = false;\n \n if (styleTagMatch) {\n const styleAttributes = styleTagMatch[1].trim();\n styleContent = styleTagMatch[2].trim();\n \n // Check if scoped attribute is present\n isScoped = /scoped(?:\\s|>|$)/.test(styleAttributes);\n }\n \n // Remove script and style tags from template before parsing\n let template = code\n .replace(/<script>[\\s\\S]*?<\\/script>/, \"\")\n .replace(/<style[^>]*>[\\s\\S]*?<\\/style>/, \"\")\n .replace(/^\\s+|\\s+$/g, '');\n\n let parsedTemplate;\n try {\n parsedTemplate = parser.parse(template);\n } catch (error) {\n const errorMsg = showErrorMessage(template, error);\n throw new Error(`Error parsing template in file ${id}:\\n${errorMsg}`);\n }\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 // Process CSS: scope it if scoped attribute is present\n let processedStyleContent = styleContent;\n let scopeClass = '';\n \n if (isScoped && styleContent) {\n // Generate short hash (8 characters) based on file path\n const fileHash = generateHash(id);\n scopeClass = fileHash;\n processedStyleContent = scopeCSS(styleContent, scopeClass);\n \n // Add _scopeClass prop to all DOMContainer in the template\n // Pattern: h(DOMContainer, { ... }) or h(DOMContainer) or h(DOMContainer, null, ...)\n parsedTemplate = parsedTemplate.replace(\n /h\\(DOMContainer\\s*,\\s*(\\{([^}]*)\\}|null)\\s*(,\\s*[^)]*)?\\)/g,\n (match, propsPart, propsContent, childrenPart) => {\n if (propsPart === 'null') {\n // h(DOMContainer, null, ...) -> h(DOMContainer, { _scopeClass: '...' }, ...)\n return `h(DOMContainer, { _scopeClass: '${scopeClass}' }${childrenPart || ''})`;\n } else {\n // h(DOMContainer, { ... }, ...) -> h(DOMContainer, { _scopeClass: '...', ... }, ...)\n // Need to insert _scopeClass at the beginning of the props object\n return `h(DOMContainer, { _scopeClass: '${scopeClass}', ${propsContent || ''} }${childrenPart || ''})`;\n }\n }\n );\n \n // Also handle h(DOMContainer) without props\n parsedTemplate = parsedTemplate.replace(\n /h\\(DOMContainer\\s*\\)(?!\\s*\\()/g,\n `h(DOMContainer, { _scopeClass: '${scopeClass}' })`\n );\n }\n \n // Escape style content for safe embedding in JavaScript string (using single quotes)\n // We need to escape: backslashes, single quotes, and line breaks\n const escapedStyleContent = processedStyleContent\n .replace(/\\\\/g, '\\\\\\\\') // Escape backslashes first\n .replace(/'/g, \"\\\\'\") // Escape single quotes\n .replace(/\\n/g, '\\\\n') // Escape newlines\n .replace(/\\r/g, '\\\\r'); // Escape carriage returns\n\n // Generate unique ID for style element based on file path\n const styleId = `ce-style-${id.replace(/[^a-zA-Z0-9]/g, '-')}`;\n\n // Generate CSS injection code if style content exists\n // Use single quotes to avoid escaping issues with backticks\n const styleInjectionCode = styleContent ? \n '// Inject CSS styles into the document head\\n' +\n `if (typeof document !== 'undefined' && !document.getElementById('${styleId}')) {\\n` +\n ' const styleElement = document.createElement(\\'style\\');\\n' +\n ` styleElement.id = '${styleId}';\\n` +\n ` styleElement.textContent = '${escapedStyleContent}';\\n` +\n ' document.head.appendChild(styleElement);\\n' +\n '}\\n'\n : '';\n \n\n // Generate the output\n const output = String.raw`\n ${importsCode}\n import { useProps, useDefineProps } from ${isDev ? `'${DEV_SRC}'` : \"'canvasengine'\"}\n ${styleInjectionCode}\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\n/**\n * Generates a short hash (8 characters, letters only) from a string\n * \n * @param {string} str - The string to hash\n * @returns {string} - An 8-character hash containing only lowercase letters (a-z)\n */\nfunction generateHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash; // Convert to 32-bit integer\n }\n // Convert to positive number and map to letters only (a-z)\n // Use modulo to map to 26 letters, then convert to character\n const positiveHash = Math.abs(hash);\n let result = '';\n for (let i = 0; i < 8; i++) {\n const letterIndex = (positiveHash + i * 31) % 26; // 31 is a prime to spread values\n result += String.fromCharCode(97 + letterIndex); // 97 is 'a'\n }\n return result;\n}\n\n/**\n * Formats a syntax error message with visual pointer to the error location\n * \n * @param {string} template - The template content that failed to parse\n * @param {object} error - The error object with location information\n * @returns {string} - Formatted error message with a visual pointer\n * \n * @example\n * ```\n * const errorMessage = showErrorMessage(\"<Canvas>test(d)</Canvas>\", syntaxError);\n * // Returns a formatted error message with an arrow pointing to 'd'\n * ```\n */\nfunction showErrorMessage(template: string, error: any): string {\n if (!error.location) {\n return `Syntax error: ${error.message}`;\n }\n\n const lines = template.split('\\n');\n const { line, column } = error.location.start;\n const errorLine = lines[line - 1] || '';\n \n // Create a visual pointer with an arrow\n const pointer = ' '.repeat(column - 1) + '^';\n \n return `Syntax error at line ${line}, column ${column}: ${error.message}\\n\\n` +\n `${errorLine}\\n${pointer}\\n`;\n}\n\n/**\n * Scopes CSS selectors by prefixing them with a class selector\n * \n * This function prefixes all CSS rule selectors (not @rules) with a class\n * selector to scope the styles to a specific component instance.\n * \n * @param {string} css - The CSS content to scope\n * @param {string} scopeClass - The unique scope class to use (without the dot)\n * @returns {string} - The scoped CSS content\n * \n * @example\n * ```\n * const scoped = scopeCSS('.my-class { color: red; }', 'ce-scope-abc123');\n * // Returns: '.ce-scope-abc123 .my-class { color: red; }'\n * ```\n */\nfunction scopeCSS(css: string, scopeClass: string): string {\n const scopeSelector = `.${scopeClass}`;\n \n // Process CSS by finding rule blocks while skipping @rules\n let result = '';\n let i = 0;\n let depth = 0;\n let inRule = false;\n let selectorBuffer = '';\n \n while (i < css.length) {\n const char = css[i];\n \n if (char === '@' && !inRule && selectorBuffer === '') {\n // Found @rule - copy it as-is until matching closing brace\n const atRuleStart = i;\n i++; // Skip '@'\n \n // Find the opening brace\n while (i < css.length && css[i] !== '{') {\n i++;\n }\n \n if (i < css.length) {\n // Found opening brace, now find matching closing brace\n depth = 1;\n i++; // Skip '{'\n \n while (i < css.length && depth > 0) {\n if (css[i] === '{') depth++;\n else if (css[i] === '}') depth--;\n i++;\n }\n \n // Copy entire @rule as-is\n result += css.substring(atRuleStart, i);\n }\n continue;\n }\n \n if (char === '{' && !inRule) {\n // Start of a rule block - scope the selector we just collected\n const selectorText = selectorBuffer.trim();\n \n if (selectorText) {\n // Split selectors by comma and scope each one\n const scopedSelectors = selectorText\n .split(',')\n .map(sel => {\n const trimmed = sel.trim();\n return trimmed ? `${scopeSelector} ${trimmed}` : trimmed;\n })\n .join(', ');\n \n result += scopedSelectors;\n }\n result += ' {';\n inRule = true;\n depth = 1;\n selectorBuffer = '';\n } else if (char === '{' && inRule) {\n // Nested brace\n result += char;\n depth++;\n } else if (char === '}' && inRule) {\n result += char;\n depth--;\n if (depth === 0) {\n inRule = false;\n }\n } else if (!inRule) {\n // Collecting selector\n selectorBuffer += char;\n } else {\n // Inside rule block\n result += char;\n if (char === '{') depth++;\n }\n \n i++;\n }\n \n // Add any remaining selector (shouldn't happen in valid CSS, but handle it)\n if (selectorBuffer.trim()) {\n const scopedSelectors = selectorBuffer.trim()\n .split(',')\n .map(sel => {\n const trimmed = sel.trim();\n return trimmed ? `${scopeSelector} ${trimmed}` : trimmed;\n })\n .join(', ');\n result += scopedSelectors;\n }\n \n return result;\n}\n\n/**\n * Vite plugin to load shader files (.frag, .vert, .wgsl) as text strings\n * \n * This plugin allows importing shader files directly as string literals in your code.\n * It supports fragment shaders (.frag), vertex shaders (.vert), and WebGPU shaders (.wgsl).\n * The content is loaded as a raw string and can be used directly with graphics APIs.\n * \n * @returns {object} - Vite plugin configuration object\n * \n * @example\n * ```typescript\n * // In your vite.config.ts\n * import { shaderLoader } from './path/to/compiler'\n * \n * export default defineConfig({\n * plugins: [shaderLoader()]\n * })\n * \n * // In your code\n * import fragmentShader from './shader.frag'\n * import vertexShader from './shader.vert'\n * import computeShader from './shader.wgsl'\n * \n * console.log(fragmentShader) // Raw shader code as string\n * ```\n */\nexport function shaderLoader() {\n const filter = createFilter(/\\.(frag|vert|wgsl)$/);\n\n return {\n name: \"vite-plugin-shader-loader\",\n transform(code: string, id: string) {\n if (!filter(id)) return;\n\n // Escape the shader code to be safely embedded in a JavaScript string\n const escapedCode = code\n .replace(/\\\\/g, '\\\\\\\\') // Escape backslashes\n .replace(/`/g, '\\\\`') // Escape backticks\n .replace(/\\$/g, '\\\\$'); // Escape dollar signs\n\n // Return the shader content as a default export string\n return {\n code: `export default \\`${escapedCode}\\`;`,\n map: null,\n };\n },\n };\n}\n\nexport default function canvasengine() {\n const filter = createFilter(\"**/*.ce\");\n const useLegacyGrammar = process.env.CANVASENGINE_COMPILER_V1 === \"1\";\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 grammarFile = useLegacyGrammar ? \"grammar.pegjs\" : \"grammar2.pegjs\";\n const grammar = fs.readFileSync(\n path.join(__dirname, grammarFile),\n \"utf8\"\n );\n const parser = generate(grammar);\n const isDev = process.env.NODE_ENV === \"dev\";\n const FLAG_COMMENT = \"/*--[TPL]--*/\";\n let warnedAboutGrammar = false;\n\n const PRIMITIVE_COMPONENTS = [\n \"Canvas\",\n \"Sprite\",\n \"Text\",\n \"Viewport\",\n \"Graphics\",\n \"Container\",\n \"Navigation\",\n \"ImageMap\",\n \"NineSliceSprite\",\n \"Rect\",\n \"Circle\",\n \"Ellipse\",\n \"Triangle\",\n \"TilingSprite\",\n \"svg\",\n \"Video\",\n \"Mesh\",\n \"Svg\",\n \"DOMContainer\",\n \"DOMElement\",\n \"DOMSprite\",\n \"Button\",\n \"Joystick\"\n ];\n\n return {\n name: \"vite-plugin-ce\",\n transform(code: string, id: string) {\n if (!filter(id)) return null;\n if (!warnedAboutGrammar) {\n warnedAboutGrammar = true;\n const legacyNote = \"Set CANVASENGINE_COMPILER_V1=1 to compile with the legacy grammar (v1).\";\n if (useLegacyGrammar) {\n console.warn(`[canvasengine] Using legacy grammar v1. ${legacyNote}`);\n } else {\n console.warn(`[canvasengine] Breaking change: compiler grammar v2 is now the default. ${legacyNote}`);\n }\n }\n\n // Extract the script content\n const scriptMatch = code.match(/<script>([\\s\\S]*?)<\\/script>/);\n let scriptContent = scriptMatch ? scriptMatch[1].trim() : \"\";\n \n // Extract the style tag with attributes and content\n const styleTagMatch = code.match(/<style([^>]*)>([\\s\\S]*?)<\\/style>/);\n let styleContent = \"\";\n let isScoped = false;\n \n if (styleTagMatch) {\n const styleAttributes = styleTagMatch[1].trim();\n styleContent = styleTagMatch[2].trim();\n \n // Check if scoped attribute is present\n isScoped = /scoped(?:\\s|>|$)/.test(styleAttributes);\n }\n \n // Remove script and style tags from template before parsing\n let template = code\n .replace(/<script>[\\s\\S]*?<\\/script>/, \"\")\n .replace(/<style[^>]*>[\\s\\S]*?<\\/style>/, \"\")\n .replace(/^\\s+|\\s+$/g, '');\n\n let parsedTemplate;\n try {\n parsedTemplate = parser.parse(template);\n } catch (error) {\n const errorMsg = showErrorMessage(template, error);\n throw new Error(`Error parsing template in file ${id}:\\n${errorMsg}`);\n }\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 // Process CSS: scope it if scoped attribute is present\n let processedStyleContent = styleContent;\n let scopeClass = '';\n \n if (isScoped && styleContent) {\n // Generate short hash (8 characters) based on file path\n const fileHash = generateHash(id);\n scopeClass = fileHash;\n processedStyleContent = scopeCSS(styleContent, scopeClass);\n \n // Add _scopeClass prop to all DOMContainer in the template\n // Pattern: h(DOMContainer, { ... }) or h(DOMContainer) or h(DOMContainer, null, ...)\n parsedTemplate = parsedTemplate.replace(\n /h\\(DOMContainer\\s*,\\s*(\\{([^}]*)\\}|null)\\s*(,\\s*[^)]*)?\\)/g,\n (match, propsPart, propsContent, childrenPart) => {\n if (propsPart === 'null') {\n // h(DOMContainer, null, ...) -> h(DOMContainer, { _scopeClass: '...' }, ...)\n return `h(DOMContainer, { _scopeClass: '${scopeClass}' }${childrenPart || ''})`;\n } else {\n // h(DOMContainer, { ... }, ...) -> h(DOMContainer, { _scopeClass: '...', ... }, ...)\n // Need to insert _scopeClass at the beginning of the props object\n return `h(DOMContainer, { _scopeClass: '${scopeClass}', ${propsContent || ''} }${childrenPart || ''})`;\n }\n }\n );\n \n // Also handle h(DOMContainer) without props\n parsedTemplate = parsedTemplate.replace(\n /h\\(DOMContainer\\s*\\)(?!\\s*\\()/g,\n `h(DOMContainer, { _scopeClass: '${scopeClass}' })`\n );\n }\n \n // Escape style content for safe embedding in JavaScript string (using single quotes)\n // We need to escape: backslashes, single quotes, and line breaks\n const escapedStyleContent = processedStyleContent\n .replace(/\\\\/g, '\\\\\\\\') // Escape backslashes first\n .replace(/'/g, \"\\\\'\") // Escape single quotes\n .replace(/\\n/g, '\\\\n') // Escape newlines\n .replace(/\\r/g, '\\\\r'); // Escape carriage returns\n\n // Generate unique ID for style element based on file path\n const styleId = `ce-style-${id.replace(/[^a-zA-Z0-9]/g, '-')}`;\n\n // Generate CSS injection code if style content exists\n // Use single quotes to avoid escaping issues with backticks\n const styleInjectionCode = styleContent ? \n '// Inject CSS styles into the document head\\n' +\n `if (typeof document !== 'undefined' && !document.getElementById('${styleId}')) {\\n` +\n ' const styleElement = document.createElement(\\'style\\');\\n' +\n ` styleElement.id = '${styleId}';\\n` +\n ` styleElement.textContent = '${escapedStyleContent}';\\n` +\n ' document.head.appendChild(styleElement);\\n' +\n '}\\n'\n : '';\n \n\n // Generate the output\n const output = String.raw`\n ${importsCode}\n import { useProps, useDefineProps } from ${isDev ? `'${DEV_SRC}'` : \"'canvasengine'\"}\n ${styleInjectionCode}\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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canvasengine/compiler",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.49",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"dist"
|
|
9
9
|
],
|
|
10
10
|
"scripts": {
|
|
11
|
-
"build": "tsup && cp
|
|
11
|
+
"build": "tsup && cp grammar2.pegjs ../../docs/public/grammar.pegjs",
|
|
12
12
|
"dev": "tsup --watch"
|
|
13
13
|
},
|
|
14
14
|
"type": "module",
|