@arcmantle/lit-jsx 1.0.0

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.
Files changed (119) hide show
  1. package/README.md +768 -0
  2. package/dist/compiler/attribute-processor.d.ts +128 -0
  3. package/dist/compiler/attribute-processor.d.ts.map +1 -0
  4. package/dist/compiler/attribute-processor.js +380 -0
  5. package/dist/compiler/attribute-processor.js.map +1 -0
  6. package/dist/compiler/babel-preset.d.ts +6 -0
  7. package/dist/compiler/babel-preset.d.ts.map +1 -0
  8. package/dist/compiler/babel-preset.js +27 -0
  9. package/dist/compiler/babel-preset.js.map +1 -0
  10. package/dist/compiler/builder.d.ts +22 -0
  11. package/dist/compiler/builder.d.ts.map +1 -0
  12. package/dist/compiler/builder.js +62 -0
  13. package/dist/compiler/builder.js.map +1 -0
  14. package/dist/compiler/compiler-utils.d.ts +81 -0
  15. package/dist/compiler/compiler-utils.d.ts.map +1 -0
  16. package/dist/compiler/compiler-utils.js +410 -0
  17. package/dist/compiler/compiler-utils.js.map +1 -0
  18. package/dist/compiler/config.d.ts +77 -0
  19. package/dist/compiler/config.d.ts.map +1 -0
  20. package/dist/compiler/config.js +78 -0
  21. package/dist/compiler/config.js.map +1 -0
  22. package/dist/compiler/postprocess.d.ts +5 -0
  23. package/dist/compiler/postprocess.d.ts.map +1 -0
  24. package/dist/compiler/postprocess.js +3 -0
  25. package/dist/compiler/postprocess.js.map +1 -0
  26. package/dist/compiler/preprocess.d.ts +5 -0
  27. package/dist/compiler/preprocess.d.ts.map +1 -0
  28. package/dist/compiler/preprocess.js +28 -0
  29. package/dist/compiler/preprocess.js.map +1 -0
  30. package/dist/compiler/transform-jsx.d.ts +5 -0
  31. package/dist/compiler/transform-jsx.d.ts.map +1 -0
  32. package/dist/compiler/transform-jsx.js +25 -0
  33. package/dist/compiler/transform-jsx.js.map +1 -0
  34. package/dist/compiler/transpiler.d.ts +48 -0
  35. package/dist/compiler/transpiler.d.ts.map +1 -0
  36. package/dist/compiler/transpiler.js +463 -0
  37. package/dist/compiler/transpiler.js.map +1 -0
  38. package/dist/compiler/vite-plugin.d.ts +38 -0
  39. package/dist/compiler/vite-plugin.d.ts.map +1 -0
  40. package/dist/compiler/vite-plugin.js +96 -0
  41. package/dist/compiler/vite-plugin.js.map +1 -0
  42. package/dist/runtime/choose-component.d.ts +39 -0
  43. package/dist/runtime/choose-component.d.ts.map +1 -0
  44. package/dist/runtime/choose-component.js +40 -0
  45. package/dist/runtime/choose-component.js.map +1 -0
  46. package/dist/runtime/compiler-ctors.d.ts +21 -0
  47. package/dist/runtime/compiler-ctors.d.ts.map +1 -0
  48. package/dist/runtime/compiler-ctors.js +21 -0
  49. package/dist/runtime/compiler-ctors.js.map +1 -0
  50. package/dist/runtime/for-component.d.ts +25 -0
  51. package/dist/runtime/for-component.d.ts.map +1 -0
  52. package/dist/runtime/for-component.js +35 -0
  53. package/dist/runtime/for-component.js.map +1 -0
  54. package/dist/runtime/literal-map.d.ts +22 -0
  55. package/dist/runtime/literal-map.d.ts.map +1 -0
  56. package/dist/runtime/literal-map.js +29 -0
  57. package/dist/runtime/literal-map.js.map +1 -0
  58. package/dist/runtime/rest-directive.d.ts +28 -0
  59. package/dist/runtime/rest-directive.d.ts.map +1 -0
  60. package/dist/runtime/rest-directive.js +49 -0
  61. package/dist/runtime/rest-directive.js.map +1 -0
  62. package/dist/runtime/show-component.d.ts +33 -0
  63. package/dist/runtime/show-component.d.ts.map +1 -0
  64. package/dist/runtime/show-component.js +30 -0
  65. package/dist/runtime/show-component.js.map +1 -0
  66. package/dist/runtime/tagged-template.d.ts +12 -0
  67. package/dist/runtime/tagged-template.d.ts.map +1 -0
  68. package/dist/runtime/tagged-template.js +12 -0
  69. package/dist/runtime/tagged-template.js.map +1 -0
  70. package/dist/runtime/type-helpers.d.ts +80 -0
  71. package/dist/runtime/type-helpers.d.ts.map +1 -0
  72. package/dist/runtime/type-helpers.js +85 -0
  73. package/dist/runtime/type-helpers.js.map +1 -0
  74. package/dist/shared/jsx-types.d.ts +2139 -0
  75. package/dist/shared/jsx-types.d.ts.map +1 -0
  76. package/dist/shared/jsx-types.js +2 -0
  77. package/dist/shared/jsx-types.js.map +1 -0
  78. package/dist/shared/jsx-utils.d.ts +30 -0
  79. package/dist/shared/jsx-utils.d.ts.map +1 -0
  80. package/dist/shared/jsx-utils.js +58 -0
  81. package/dist/shared/jsx-utils.js.map +1 -0
  82. package/dist/shared/mathml-tags.d.ts +12 -0
  83. package/dist/shared/mathml-tags.d.ts.map +1 -0
  84. package/dist/shared/mathml-tags.js +215 -0
  85. package/dist/shared/mathml-tags.js.map +1 -0
  86. package/dist/shared/svg-tags.d.ts +13 -0
  87. package/dist/shared/svg-tags.d.ts.map +1 -0
  88. package/dist/shared/svg-tags.js +95 -0
  89. package/dist/shared/svg-tags.js.map +1 -0
  90. package/dist/utils.d.ts +30 -0
  91. package/dist/utils.d.ts.map +1 -0
  92. package/dist/utils.js +30 -0
  93. package/dist/utils.js.map +1 -0
  94. package/package.json +52 -0
  95. package/src/compiler/attribute-processor.ts +579 -0
  96. package/src/compiler/babel-preset.ts +34 -0
  97. package/src/compiler/builder.ts +86 -0
  98. package/src/compiler/compiler-utils.ts +789 -0
  99. package/src/compiler/config.ts +77 -0
  100. package/src/compiler/postprocess.ts +7 -0
  101. package/src/compiler/preprocess.ts +40 -0
  102. package/src/compiler/transform-jsx.ts +36 -0
  103. package/src/compiler/transpiler.ts +644 -0
  104. package/src/compiler/vite-plugin.ts +114 -0
  105. package/src/external.d.ts +9 -0
  106. package/src/runtime/choose-component.ts +53 -0
  107. package/src/runtime/compiler-ctors.ts +28 -0
  108. package/src/runtime/for-component.ts +54 -0
  109. package/src/runtime/literal-map.ts +37 -0
  110. package/src/runtime/rest-directive.ts +66 -0
  111. package/src/runtime/show-component.ts +48 -0
  112. package/src/runtime/tagged-template.ts +11 -0
  113. package/src/runtime/type-helpers.ts +91 -0
  114. package/src/shared/jsx-types.ts +2556 -0
  115. package/src/shared/jsx-utils.ts +85 -0
  116. package/src/shared/mathml-tags.ts +235 -0
  117. package/src/shared/svg-tags.ts +103 -0
  118. package/src/tsconfig.json +4 -0
  119. package/src/utils.ts +30 -0
@@ -0,0 +1,644 @@
1
+ import type { NodePath } from '@babel/traverse';
2
+ import * as t from '@babel/types';
3
+
4
+ import {
5
+ CompiledAttributeProcessor,
6
+ CreateCompiledPart,
7
+ type ProcessorContext,
8
+ TemplateAttributeProcessor,
9
+ } from './attribute-processor.js';
10
+ import { CompiledBuilder, TemplateBuilder } from './builder.js';
11
+ import {
12
+ ensureImports,
13
+ getProgramFromPath,
14
+ getTemplateType,
15
+ isJSXElementPath,
16
+ isJSXElementStatic,
17
+ isJSXFragmentPath,
18
+ isValidOpeningElement,
19
+ } from './compiler-utils.js';
20
+ import {
21
+ Ensure,
22
+ getJSXElementName,
23
+ isJSXCustomElementComponent,
24
+ isJSXFunctionElementComponent,
25
+ isValidJSXElement,
26
+ } from './compiler-utils.js';
27
+ import {
28
+ COMPONENT_LITERAL_PREFIX,
29
+ ERROR_MESSAGES,
30
+ VARIABLES,
31
+ WHITESPACE_TAGS,
32
+ } from './config.js';
33
+
34
+
35
+ abstract class JSXTranspiler<TContext extends ProcessorContext> {
36
+
37
+ protected abstract createContext(path: NodePath<t.JSXElement | t.JSXFragment>): TContext;
38
+
39
+ start(path: NodePath<t.JSXElement | t.JSXFragment>): t.Expression {
40
+ const context = this.createContext(path);
41
+
42
+ this.process(context);
43
+
44
+ const expression = this.createExpression(context);
45
+
46
+ ensureImports(context);
47
+
48
+ return expression;
49
+ }
50
+
51
+ abstract process(context: TContext): void;
52
+ abstract openingTag(context: TContext): void;
53
+ abstract attributes(context: TContext): void;
54
+ abstract children(context: TContext): void;
55
+ abstract closingTag(context: TContext): void;
56
+ abstract createExpression(context: TContext): t.Expression;
57
+ abstract createFunctionalComponent(path: NodePath<t.JSXElement | t.JSXFragment>): t.Expression;
58
+ abstract functionalComponent(context: TContext): void;
59
+
60
+ }
61
+
62
+
63
+ export interface TemplateContext extends ProcessorContext {
64
+ builder: TemplateBuilder;
65
+ literalName: string;
66
+ }
67
+
68
+ export class TemplateTranspiler extends JSXTranspiler<TemplateContext> {
69
+
70
+ protected override createContext(path: NodePath<t.JSXElement | t.JSXFragment>): TemplateContext {
71
+ const context: TemplateContext = {
72
+ program: getProgramFromPath(path),
73
+ path,
74
+ literalName: '',
75
+ tagName: '',
76
+ isInitialElement: true,
77
+ builder: new TemplateBuilder(),
78
+ importsUsed: new Set(),
79
+ };
80
+
81
+ return context;
82
+ }
83
+
84
+ override process(context: TemplateContext): void {
85
+ if (t.isJSXFragment(context.path.node)) {
86
+ context.builder.addText('');
87
+
88
+ this.children(context);
89
+
90
+ return;
91
+ }
92
+
93
+ context.tagName = getJSXElementName(context.path.node);
94
+
95
+ if (isJSXCustomElementComponent(context.tagName)) {
96
+ this.openingTag(context);
97
+ this.attributes(context);
98
+ this.children(context);
99
+ this.closingTag(context);
100
+
101
+ return;
102
+ }
103
+
104
+ if (isJSXFunctionElementComponent(context.tagName)) {
105
+ // Process attributes and children into a props object
106
+ if (!context.isInitialElement)
107
+ this.functionalComponent(context);
108
+
109
+ // If this is the initial element, this should not happen.
110
+ // and it should instead have been processed as a single expression.
111
+
112
+ return;
113
+ }
114
+
115
+ this.openingTag(context);
116
+ this.attributes(context);
117
+ this.children(context);
118
+ this.closingTag(context);
119
+ }
120
+
121
+ override openingTag(context: TemplateContext): void {
122
+ if (isJSXCustomElementComponent(context.tagName)) {
123
+ const literalIdentifier = Ensure.componentLiteral(
124
+ context.tagName,
125
+ COMPONENT_LITERAL_PREFIX + context.tagName,
126
+ context.path,
127
+ context.program,
128
+ );
129
+
130
+ context.literalName = literalIdentifier.name;
131
+
132
+ context.builder.addText('<');
133
+ context.builder.addExpression(literalIdentifier);
134
+ }
135
+ else {
136
+ // If the tag is not a component, we will treat it as a regular HTML element.
137
+ context.builder.addText('<' + context.tagName);
138
+ }
139
+ }
140
+
141
+ override attributes(context: TemplateContext): void {
142
+ if (!isValidJSXElement(context.path))
143
+ throw new Error(ERROR_MESSAGES.INVALID_OPENING_TAG);
144
+
145
+ const { attributes } = context.path.node.openingElement;
146
+
147
+ const processor = new TemplateAttributeProcessor();
148
+
149
+ for (const attr of attributes.values())
150
+ processor.processAttribute(attr, context);
151
+
152
+ // Close the opening tag
153
+ context.builder.addText('>');
154
+ }
155
+
156
+ override children(context: TemplateContext): void {
157
+ for (const [ index, child ] of context.path.node.children.entries()) {
158
+ if (t.isJSXText(child)) {
159
+ if (WHITESPACE_TAGS.includes(context.tagName))
160
+ context.builder.addText(child.value);
161
+ else
162
+ context.builder.addText(child.value.trim());
163
+ }
164
+ else if (t.isJSXExpressionContainer(child)) {
165
+ if (t.isJSXEmptyExpression(child.expression))
166
+ continue;
167
+
168
+ context.builder.addExpression(child.expression);
169
+ }
170
+ else if (t.isJSXElement(child)) {
171
+ const currentPath = context.path.get(`children.${ index }`);
172
+
173
+ // Recursively process child elements
174
+ if (isJSXElementPath(currentPath) || isJSXFragmentPath(currentPath)) {
175
+ this.process({
176
+ ...context,
177
+ path: currentPath,
178
+ isInitialElement: false,
179
+ });
180
+ }
181
+ }
182
+ }
183
+ }
184
+
185
+ override closingTag(context: TemplateContext): void {
186
+ // If it's a component tag, we need to close it with the static literal.
187
+ if (context.literalName) {
188
+ context.builder.addText('</');
189
+ context.builder.addExpression(t.identifier(context.literalName));
190
+ context.builder.addText('>');
191
+ }
192
+ else {
193
+ context.builder.addText('</' + context.tagName + '>');
194
+ }
195
+ }
196
+
197
+ override createExpression(context: TemplateContext): t.Expression {
198
+ const isStatic = isJSXElementStatic(context.path);
199
+ const templateType = getTemplateType(context.path);
200
+
201
+ let identifier: string = '';
202
+
203
+ if (isStatic) {
204
+ if (templateType === VARIABLES.HTML) {
205
+ identifier = VARIABLES.HTML_STATIC;
206
+ context.importsUsed.add('htmlStatic');
207
+ }
208
+ // This will not happen, as svg and mathml dynamic tags are not supported yet.
209
+ else if (templateType === VARIABLES.SVG) {
210
+ identifier = VARIABLES.SVG_STATIC;
211
+ context.importsUsed.add('svgStatic');
212
+ }
213
+ // This will not happen, as svg and mathml dynamic tags are not supported yet.
214
+ else if (templateType === VARIABLES.MATHML) {
215
+ identifier = VARIABLES.MATHML_STATIC;
216
+ context.importsUsed.add('mathmlStatic');
217
+ }
218
+ else {
219
+ throw new Error(ERROR_MESSAGES.UNKNOWN_TEMPLATE_TYPE(templateType));
220
+ }
221
+ }
222
+ else {
223
+ if (templateType === VARIABLES.HTML) {
224
+ identifier = VARIABLES.HTML;
225
+ context.importsUsed.add('html');
226
+ }
227
+ else if (templateType === VARIABLES.SVG) {
228
+ identifier = VARIABLES.SVG;
229
+ context.importsUsed.add('svg');
230
+ }
231
+ else if (templateType === VARIABLES.MATHML) {
232
+ identifier = VARIABLES.MATHML;
233
+ context.importsUsed.add('mathml');
234
+ }
235
+ else {
236
+ throw new Error(ERROR_MESSAGES.UNKNOWN_TEMPLATE_TYPE(templateType));
237
+ }
238
+ }
239
+
240
+ return context.builder.createTaggedTemplate(identifier);
241
+ }
242
+
243
+ override createFunctionalComponent(path: NodePath<t.JSXElement | t.JSXFragment>): t.Expression {
244
+ const context = this.createContext(path);
245
+
246
+ if (!isValidJSXElement(context.path))
247
+ throw new Error(ERROR_MESSAGES.INVALID_OPENING_TAG);
248
+
249
+ const properties: (t.ObjectProperty | t.SpreadElement)[] = [];
250
+ const attributes = context.path.node.openingElement.attributes;
251
+ const tagName = getJSXElementName(context.path.node);
252
+
253
+ for (const attr of attributes) {
254
+ // Handle spread attributes by spreading the object
255
+ if (t.isJSXSpreadAttribute(attr)) {
256
+ properties.push(t.spreadElement(attr.argument));
257
+
258
+ continue;
259
+ }
260
+
261
+ const name = attr.name.name.toString();
262
+ const camelCaseName = name
263
+ .replace(/-([a-zA-Z])/g, (_, letter) => letter.toUpperCase());
264
+
265
+ let value: t.Expression;
266
+
267
+ if (attr.value) {
268
+ if (t.isJSXExpressionContainer(attr.value)) {
269
+ // If the expression is empty, skip it
270
+ if (t.isJSXEmptyExpression(attr.value.expression))
271
+ continue;
272
+
273
+ value = attr.value.expression;
274
+ }
275
+ else if (t.isStringLiteral(attr.value)) {
276
+ value = attr.value;
277
+ }
278
+ // Other literal types
279
+ else {
280
+ value = attr.value as t.Expression;
281
+ }
282
+ }
283
+ else {
284
+ // Boolean attribute (no value means true)
285
+ value = t.booleanLiteral(true);
286
+ }
287
+
288
+ properties.push(t.objectProperty(t.identifier(camelCaseName), value));
289
+ }
290
+
291
+ // Process children
292
+ if (context.path.node.children.length > 0) {
293
+ const childrenArray: t.Expression[] = [];
294
+
295
+ const childrenPaths = context.path.get(`children`);
296
+ for (const childPath of childrenPaths) {
297
+ const child = childPath.node;
298
+
299
+ if (t.isJSXText(child)) {
300
+ const trimmedValue = child.value.trim();
301
+ if (trimmedValue)
302
+ childrenArray.push(t.stringLiteral(trimmedValue));
303
+ }
304
+ else if (t.isJSXExpressionContainer(child)) {
305
+ if (t.isJSXEmptyExpression(child.expression))
306
+ continue;
307
+
308
+ childrenArray.push(child.expression);
309
+ }
310
+ else if (t.isJSXElement(child)) {
311
+ if (!isValidOpeningElement(childPath))
312
+ throw new Error(ERROR_MESSAGES.INVALID_OPENING_TAG);
313
+
314
+ const isStatic = isJSXElementStatic(childPath);
315
+ const templateType = getTemplateType(childPath);
316
+
317
+ if (isStatic || templateType !== 'html') {
318
+ // Create a new builder for this child element
319
+ const childContext: TemplateContext = {
320
+ ...context,
321
+ path: childPath,
322
+ builder: new TemplateBuilder(),
323
+ isInitialElement: false,
324
+ };
325
+
326
+ this.process(childContext);
327
+
328
+ childrenArray.push(this.createExpression(childContext));
329
+ }
330
+ else {
331
+ // For compiled version, we need to process child elements differently
332
+ // Create a new compiled builder for this child element
333
+ const childContext: CompiledContext = {
334
+ ...context,
335
+ path: childPath,
336
+ builder: new CompiledBuilder(),
337
+ currentIndex: 0,
338
+ isInitialElement: false,
339
+ };
340
+
341
+ const transpiler = new CompiledTranspiler();
342
+ transpiler.process(childContext);
343
+
344
+ // Add the child expression to the array
345
+ childrenArray.push(transpiler.createExpression(childContext));
346
+ }
347
+ }
348
+ }
349
+
350
+ // Add children property if there are any children
351
+ if (childrenArray.length > 0) {
352
+ // If there's only one child, we can use it directly.
353
+ // If there are multiple children, we wrap them in an array.
354
+ // This is because JSX Component Functions expects
355
+ // either a single child or an array of children.
356
+ const childrenValue: t.Expression = childrenArray.length === 1
357
+ ? childrenArray[0]!
358
+ : t.arrayExpression(childrenArray);
359
+
360
+ properties.push(t.objectProperty(
361
+ t.identifier('children'),
362
+ childrenValue,
363
+ ));
364
+ }
365
+ }
366
+
367
+ const expression = t.callExpression(
368
+ t.identifier(tagName),
369
+ [ t.objectExpression(properties) ],
370
+ );
371
+
372
+ return expression;
373
+ }
374
+
375
+ override functionalComponent(context: TemplateContext): void {
376
+ const expression = this.createFunctionalComponent(context.path);
377
+
378
+ context.builder.addText('');
379
+ context.builder.addExpression(expression);
380
+ context.builder.addText('');
381
+ }
382
+
383
+ }
384
+
385
+
386
+ export interface CompiledContext extends ProcessorContext {
387
+ builder: CompiledBuilder;
388
+ currentIndex: number;
389
+ }
390
+
391
+ export class CompiledTranspiler extends JSXTranspiler<CompiledContext> {
392
+
393
+ protected override createContext(path: NodePath<t.JSXElement | t.JSXFragment>): CompiledContext {
394
+ const context: CompiledContext = {
395
+ program: getProgramFromPath(path),
396
+ path,
397
+ currentIndex: 0,
398
+ tagName: '',
399
+ isInitialElement: true,
400
+ builder: new CompiledBuilder(),
401
+ importsUsed: new Set([ 'taggedTemplateUtil' ]),
402
+ };
403
+
404
+ return context;
405
+ }
406
+
407
+ override process(context: CompiledContext): void {
408
+ if (t.isJSXFragment(context.path.node)) {
409
+ context.builder.addText('');
410
+
411
+ this.children(context);
412
+
413
+ return;
414
+ }
415
+
416
+ context.tagName = getJSXElementName(context.path.node);
417
+
418
+ if (isJSXFunctionElementComponent(context.tagName)) {
419
+ // Process attributes and children into a props object
420
+ if (!context.isInitialElement)
421
+ this.functionalComponent(context);
422
+
423
+ // If this is the initial element, this should not happen.
424
+ // and it should instead have been processed as a single expression.
425
+
426
+ return;
427
+ }
428
+
429
+ this.openingTag(context);
430
+ this.attributes(context);
431
+ this.children(context);
432
+ this.closingTag(context);
433
+ }
434
+
435
+ override openingTag(context: CompiledContext): void {
436
+ context.builder.addText('<' + context.tagName);
437
+ }
438
+
439
+ override attributes(context: CompiledContext): void {
440
+ if (!isValidJSXElement(context.path))
441
+ throw new Error(ERROR_MESSAGES.INVALID_OPENING_TAG);
442
+
443
+ const { attributes } = context.path.node.openingElement;
444
+ const processor = new CompiledAttributeProcessor();
445
+
446
+ // Process the attributes
447
+ for (const attr of attributes.values())
448
+ processor.processAttribute(attr, context);
449
+
450
+ // Close the opening tag
451
+ context.builder.addText('>');
452
+ }
453
+
454
+ override children(context: CompiledContext): void {
455
+ for (const childPath of context.path.get('children').values()) {
456
+ const child = childPath.node;
457
+
458
+ // Index is incremented to ensure correct part indices.
459
+ const partIndex = context.currentIndex + 1;
460
+
461
+ if (t.isJSXText(child)) {
462
+ if (WHITESPACE_TAGS.includes(context.tagName))
463
+ context.builder.addText(child.value);
464
+ else
465
+ context.builder.addText(child.value.trim());
466
+ }
467
+ else if (t.isJSXExpressionContainer(child)) {
468
+ if (t.isJSXEmptyExpression(child.expression))
469
+ throw new Error(ERROR_MESSAGES.EMPTY_EXPRESSION);
470
+
471
+ context.builder.addText('<?>');
472
+ context.builder.addValue(child.expression);
473
+ context.builder.addPart(CreateCompiledPart.child(partIndex));
474
+ }
475
+ else if (t.isJSXElement(child)) {
476
+ if (!isValidJSXElement(childPath))
477
+ throw new Error(ERROR_MESSAGES.INVALID_OPENING_TAG);
478
+
479
+ // Recursively process child elements
480
+ this.process({
481
+ ...context,
482
+ path: childPath,
483
+ currentIndex: partIndex,
484
+ isInitialElement: false,
485
+ });
486
+ }
487
+ }
488
+ }
489
+
490
+ override closingTag(context: CompiledContext): void {
491
+ context.builder.addText('</' + context.tagName + '>');
492
+ }
493
+
494
+ override createExpression(context: CompiledContext): t.Expression {
495
+ const variableName = context.path.scope.generateUid();
496
+ const compiledTemplate = context.builder.createCompiledTemplate();
497
+ const compiledExpression = context.builder.createExpression(variableName);
498
+
499
+ Ensure.hoistAsTopLevelVariable(context.path, variableName, compiledTemplate);
500
+
501
+ return compiledExpression;
502
+ }
503
+
504
+ override createFunctionalComponent(path: NodePath<t.JSXElement | t.JSXFragment>): t.Expression {
505
+ const context = this.createContext(path);
506
+
507
+ if (!isValidJSXElement(context.path))
508
+ throw new Error(ERROR_MESSAGES.INVALID_OPENING_TAG);
509
+
510
+ const properties: (t.ObjectProperty | t.SpreadElement)[] = [];
511
+ const attributes = context.path.node.openingElement.attributes;
512
+ const tagName = getJSXElementName(context.path.node);
513
+
514
+ for (const attr of attributes) {
515
+ // Handle spread attributes by spreading the object
516
+ if (t.isJSXSpreadAttribute(attr)) {
517
+ properties.push(t.spreadElement(attr.argument));
518
+
519
+ continue;
520
+ }
521
+
522
+ const name = attr.name.name.toString();
523
+ const camelCaseName = name
524
+ .replace(/-([a-zA-Z])/g, (_, letter) => letter.toUpperCase());
525
+
526
+ let value: t.Expression;
527
+
528
+ if (attr.value) {
529
+ if (t.isJSXExpressionContainer(attr.value)) {
530
+ // If the expression is empty, skip it
531
+ if (t.isJSXEmptyExpression(attr.value.expression))
532
+ continue;
533
+
534
+ value = attr.value.expression;
535
+ }
536
+ else if (t.isStringLiteral(attr.value)) {
537
+ value = attr.value;
538
+ }
539
+ // Other literal types
540
+ else {
541
+ value = attr.value as t.Expression;
542
+ }
543
+ }
544
+ else {
545
+ // Boolean attribute (no value means true)
546
+ value = t.booleanLiteral(true);
547
+ }
548
+
549
+ properties.push(t.objectProperty(t.identifier(camelCaseName), value));
550
+ }
551
+
552
+ // Process children
553
+ if (context.path.node.children.length > 0) {
554
+ const childrenArray: t.Expression[] = [];
555
+
556
+ for (const childPath of context.path.get('children')) {
557
+ const child = childPath.node;
558
+
559
+ if (t.isJSXText(child)) {
560
+ const trimmedValue = child.value.trim();
561
+ if (trimmedValue)
562
+ childrenArray.push(t.stringLiteral(trimmedValue));
563
+ }
564
+ else if (t.isJSXExpressionContainer(child)) {
565
+ if (t.isJSXEmptyExpression(child.expression))
566
+ continue;
567
+
568
+ childrenArray.push(child.expression);
569
+ }
570
+ else if (t.isJSXElement(child)) {
571
+ if (!isValidJSXElement(childPath))
572
+ throw new Error(ERROR_MESSAGES.INVALID_OPENING_TAG);
573
+
574
+ const isStatic = isJSXElementStatic(childPath);
575
+ const templateType = getTemplateType(childPath);
576
+
577
+ if (isStatic || templateType !== 'html') {
578
+ // Create a new builder for this child element
579
+ const childContext: TemplateContext = {
580
+ ...context,
581
+ literalName: '',
582
+ path: childPath,
583
+ builder: new TemplateBuilder(),
584
+ isInitialElement: false,
585
+ };
586
+
587
+ const transpiler = new TemplateTranspiler();
588
+ transpiler.process(childContext);
589
+
590
+ childrenArray.push(transpiler.createExpression(childContext));
591
+ }
592
+ else {
593
+ // For compiled version, we need to process child elements differently
594
+ // Create a new compiled builder for this child element
595
+ const childContext: CompiledContext = {
596
+ ...context,
597
+ path: childPath,
598
+ builder: new CompiledBuilder(),
599
+ currentIndex: context.currentIndex + 1,
600
+ isInitialElement: false,
601
+ };
602
+
603
+ this.process(childContext);
604
+
605
+ // Add the child expression to the array
606
+ childrenArray.push(this.createExpression(childContext));
607
+ }
608
+ }
609
+ }
610
+
611
+ // Add children property if there are any children
612
+ if (childrenArray.length > 0) {
613
+ // If there's only one child, we can use it directly.
614
+ // If there are multiple children, we wrap them in an array.
615
+ // This is because JSX Component Functions expects
616
+ // either a single child or an array of children.
617
+ const childrenValue: t.Expression = childrenArray.length === 1
618
+ ? childrenArray[0]!
619
+ : t.arrayExpression(childrenArray);
620
+
621
+ properties.push(t.objectProperty(
622
+ t.identifier('children'),
623
+ childrenValue,
624
+ ));
625
+ }
626
+ }
627
+
628
+ const expression = t.callExpression(
629
+ t.identifier(tagName),
630
+ [ t.objectExpression(properties) ],
631
+ );
632
+
633
+ return expression;
634
+ }
635
+
636
+ override functionalComponent(context: CompiledContext): void {
637
+ const expression = this.createFunctionalComponent(context.path);
638
+
639
+ context.builder.addText('<?>');
640
+ context.builder.addValue(expression);
641
+ context.builder.addPart(CreateCompiledPart.child(context.currentIndex + 1));
642
+ }
643
+
644
+ }