@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.
- package/README.md +768 -0
- package/dist/compiler/attribute-processor.d.ts +128 -0
- package/dist/compiler/attribute-processor.d.ts.map +1 -0
- package/dist/compiler/attribute-processor.js +380 -0
- package/dist/compiler/attribute-processor.js.map +1 -0
- package/dist/compiler/babel-preset.d.ts +6 -0
- package/dist/compiler/babel-preset.d.ts.map +1 -0
- package/dist/compiler/babel-preset.js +27 -0
- package/dist/compiler/babel-preset.js.map +1 -0
- package/dist/compiler/builder.d.ts +22 -0
- package/dist/compiler/builder.d.ts.map +1 -0
- package/dist/compiler/builder.js +62 -0
- package/dist/compiler/builder.js.map +1 -0
- package/dist/compiler/compiler-utils.d.ts +81 -0
- package/dist/compiler/compiler-utils.d.ts.map +1 -0
- package/dist/compiler/compiler-utils.js +410 -0
- package/dist/compiler/compiler-utils.js.map +1 -0
- package/dist/compiler/config.d.ts +77 -0
- package/dist/compiler/config.d.ts.map +1 -0
- package/dist/compiler/config.js +78 -0
- package/dist/compiler/config.js.map +1 -0
- package/dist/compiler/postprocess.d.ts +5 -0
- package/dist/compiler/postprocess.d.ts.map +1 -0
- package/dist/compiler/postprocess.js +3 -0
- package/dist/compiler/postprocess.js.map +1 -0
- package/dist/compiler/preprocess.d.ts +5 -0
- package/dist/compiler/preprocess.d.ts.map +1 -0
- package/dist/compiler/preprocess.js +28 -0
- package/dist/compiler/preprocess.js.map +1 -0
- package/dist/compiler/transform-jsx.d.ts +5 -0
- package/dist/compiler/transform-jsx.d.ts.map +1 -0
- package/dist/compiler/transform-jsx.js +25 -0
- package/dist/compiler/transform-jsx.js.map +1 -0
- package/dist/compiler/transpiler.d.ts +48 -0
- package/dist/compiler/transpiler.d.ts.map +1 -0
- package/dist/compiler/transpiler.js +463 -0
- package/dist/compiler/transpiler.js.map +1 -0
- package/dist/compiler/vite-plugin.d.ts +38 -0
- package/dist/compiler/vite-plugin.d.ts.map +1 -0
- package/dist/compiler/vite-plugin.js +96 -0
- package/dist/compiler/vite-plugin.js.map +1 -0
- package/dist/runtime/choose-component.d.ts +39 -0
- package/dist/runtime/choose-component.d.ts.map +1 -0
- package/dist/runtime/choose-component.js +40 -0
- package/dist/runtime/choose-component.js.map +1 -0
- package/dist/runtime/compiler-ctors.d.ts +21 -0
- package/dist/runtime/compiler-ctors.d.ts.map +1 -0
- package/dist/runtime/compiler-ctors.js +21 -0
- package/dist/runtime/compiler-ctors.js.map +1 -0
- package/dist/runtime/for-component.d.ts +25 -0
- package/dist/runtime/for-component.d.ts.map +1 -0
- package/dist/runtime/for-component.js +35 -0
- package/dist/runtime/for-component.js.map +1 -0
- package/dist/runtime/literal-map.d.ts +22 -0
- package/dist/runtime/literal-map.d.ts.map +1 -0
- package/dist/runtime/literal-map.js +29 -0
- package/dist/runtime/literal-map.js.map +1 -0
- package/dist/runtime/rest-directive.d.ts +28 -0
- package/dist/runtime/rest-directive.d.ts.map +1 -0
- package/dist/runtime/rest-directive.js +49 -0
- package/dist/runtime/rest-directive.js.map +1 -0
- package/dist/runtime/show-component.d.ts +33 -0
- package/dist/runtime/show-component.d.ts.map +1 -0
- package/dist/runtime/show-component.js +30 -0
- package/dist/runtime/show-component.js.map +1 -0
- package/dist/runtime/tagged-template.d.ts +12 -0
- package/dist/runtime/tagged-template.d.ts.map +1 -0
- package/dist/runtime/tagged-template.js +12 -0
- package/dist/runtime/tagged-template.js.map +1 -0
- package/dist/runtime/type-helpers.d.ts +80 -0
- package/dist/runtime/type-helpers.d.ts.map +1 -0
- package/dist/runtime/type-helpers.js +85 -0
- package/dist/runtime/type-helpers.js.map +1 -0
- package/dist/shared/jsx-types.d.ts +2139 -0
- package/dist/shared/jsx-types.d.ts.map +1 -0
- package/dist/shared/jsx-types.js +2 -0
- package/dist/shared/jsx-types.js.map +1 -0
- package/dist/shared/jsx-utils.d.ts +30 -0
- package/dist/shared/jsx-utils.d.ts.map +1 -0
- package/dist/shared/jsx-utils.js +58 -0
- package/dist/shared/jsx-utils.js.map +1 -0
- package/dist/shared/mathml-tags.d.ts +12 -0
- package/dist/shared/mathml-tags.d.ts.map +1 -0
- package/dist/shared/mathml-tags.js +215 -0
- package/dist/shared/mathml-tags.js.map +1 -0
- package/dist/shared/svg-tags.d.ts +13 -0
- package/dist/shared/svg-tags.d.ts.map +1 -0
- package/dist/shared/svg-tags.js +95 -0
- package/dist/shared/svg-tags.js.map +1 -0
- package/dist/utils.d.ts +30 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +30 -0
- package/dist/utils.js.map +1 -0
- package/package.json +52 -0
- package/src/compiler/attribute-processor.ts +579 -0
- package/src/compiler/babel-preset.ts +34 -0
- package/src/compiler/builder.ts +86 -0
- package/src/compiler/compiler-utils.ts +789 -0
- package/src/compiler/config.ts +77 -0
- package/src/compiler/postprocess.ts +7 -0
- package/src/compiler/preprocess.ts +40 -0
- package/src/compiler/transform-jsx.ts +36 -0
- package/src/compiler/transpiler.ts +644 -0
- package/src/compiler/vite-plugin.ts +114 -0
- package/src/external.d.ts +9 -0
- package/src/runtime/choose-component.ts +53 -0
- package/src/runtime/compiler-ctors.ts +28 -0
- package/src/runtime/for-component.ts +54 -0
- package/src/runtime/literal-map.ts +37 -0
- package/src/runtime/rest-directive.ts +66 -0
- package/src/runtime/show-component.ts +48 -0
- package/src/runtime/tagged-template.ts +11 -0
- package/src/runtime/type-helpers.ts +91 -0
- package/src/shared/jsx-types.ts +2556 -0
- package/src/shared/jsx-utils.ts +85 -0
- package/src/shared/mathml-tags.ts +235 -0
- package/src/shared/svg-tags.ts +103 -0
- package/src/tsconfig.json +4 -0
- 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
|
+
}
|