@sigil-dev/compiler 0.7.5 → 0.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -1
- package/src/babel/handlers/dom/derived.ts +31 -1
- package/src/babel/handlers/dom/globals.ts +102 -0
- package/src/babel/handlers/ssr/state.ts +10 -10
- package/src/babel/index.ts +430 -269
- package/src/babel/jsx/children.ts +124 -124
- package/src/babel/jsx/element.ts +803 -582
- package/src/babel/jsx/ssr.ts +319 -310
- package/src/babel/jsx/utils.ts +95 -89
- package/src/babel/util/bind.ts +126 -135
- package/src/babel/util/css.ts +151 -151
- package/src/babel/util/helpers.ts +16 -2
- package/src/babel/util/magic.ts +5 -0
- package/src/bun-plugin.ts +86 -72
- package/src/vite/index.ts +7 -2
- package/test/jsx.test.ts +195 -195
- package/test/reactivity.test.ts +147 -1
package/src/babel/jsx/ssr.ts
CHANGED
|
@@ -1,310 +1,319 @@
|
|
|
1
|
-
import { types as t } from "@babel/core";
|
|
2
|
-
import { getTagName } from "../util/helpers";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
current
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* @param
|
|
58
|
-
* @
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
t.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
isSSRSafe(expr.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
t.
|
|
94
|
-
t.
|
|
95
|
-
expr.callee.property
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if (t.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
stmt
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
parts.push(
|
|
168
|
-
return buildTemplate(parts);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
parts.push(
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
[
|
|
275
|
-
);
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
}
|
|
1
|
+
import { types as t } from "@babel/core";
|
|
2
|
+
import { getTagName } from "../util/helpers";
|
|
3
|
+
import { isGlobalComponent } from "../handlers/dom/globals";
|
|
4
|
+
|
|
5
|
+
const VOID_ELEMENTS = new Set([
|
|
6
|
+
"area",
|
|
7
|
+
"base",
|
|
8
|
+
"br",
|
|
9
|
+
"col",
|
|
10
|
+
"embed",
|
|
11
|
+
"hr",
|
|
12
|
+
"img",
|
|
13
|
+
"input",
|
|
14
|
+
"link",
|
|
15
|
+
"meta",
|
|
16
|
+
"param",
|
|
17
|
+
"source",
|
|
18
|
+
"track",
|
|
19
|
+
"wbr",
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
const ATTR_MAP_SSR: Record<string, string> = {
|
|
23
|
+
className: "class",
|
|
24
|
+
htmlFor: "for",
|
|
25
|
+
};
|
|
26
|
+
//biome-ignore lint/suspicious/noShadowRestrictedNames: shut up
|
|
27
|
+
function escape(expr: t.Expression): t.CallExpression {
|
|
28
|
+
return t.callExpression(t.identifier("__e"), [expr]);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// builds a template literal from alternating string parts and expressions
|
|
32
|
+
function buildTemplate(parts: Array<string | t.Expression>): t.TemplateLiteral {
|
|
33
|
+
const quasis: t.TemplateElement[] = [];
|
|
34
|
+
const exprs: t.Expression[] = [];
|
|
35
|
+
let current = "";
|
|
36
|
+
|
|
37
|
+
for (const part of parts) {
|
|
38
|
+
if (typeof part === "string") {
|
|
39
|
+
current += part;
|
|
40
|
+
} else {
|
|
41
|
+
quasis.push(t.templateElement({ raw: current, cooked: current }));
|
|
42
|
+
current = "";
|
|
43
|
+
exprs.push(part);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
quasis.push(t.templateElement({ raw: current, cooked: current }, true));
|
|
47
|
+
return t.templateLiteral(quasis, exprs);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* <Component /> is always JSX so its always safe to render as is
|
|
52
|
+
* but the compiler can also register its own safe variables, like:
|
|
53
|
+
* ```ts
|
|
54
|
+
* const rendered = <Component foo="bar" />
|
|
55
|
+
* return <div>{rendered}</div>
|
|
56
|
+
* ```
|
|
57
|
+
* @param expr
|
|
58
|
+
* @param safeIdents
|
|
59
|
+
* @returns
|
|
60
|
+
*/
|
|
61
|
+
/*
|
|
62
|
+
function isSSRSafe(expr: t.Expression, safeIdents: Set<string>): boolean {
|
|
63
|
+
// direct JSX - compiled to HTML string by this very plugin
|
|
64
|
+
if (t.isJSXElement(expr) || t.isJSXFragment(expr)) return true;
|
|
65
|
+
|
|
66
|
+
// component call - returns HTML string by convention
|
|
67
|
+
if (
|
|
68
|
+
t.isCallExpression(expr) &&
|
|
69
|
+
t.isIdentifier(expr.callee) &&
|
|
70
|
+
/^[A-Z]/.test(expr.callee.name)
|
|
71
|
+
) return true;
|
|
72
|
+
|
|
73
|
+
// registered by compiler (e.g. children)
|
|
74
|
+
if (t.isIdentifier(expr)) {
|
|
75
|
+
// if sigil as a compiler doesnt have an insane CVE
|
|
76
|
+
// this will always be true
|
|
77
|
+
if (expr.name === "children") return true;
|
|
78
|
+
return safeIdents.has(expr.name);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// condition && <JSX> — right side produces HTML
|
|
82
|
+
if (t.isLogicalExpression(expr))
|
|
83
|
+
return isSSRSafe(expr.right as t.Expression, safeIdents);
|
|
84
|
+
|
|
85
|
+
// condition ? <JSX> : <JSX> — both branches must be checked
|
|
86
|
+
if (t.isConditionalExpression(expr)) {
|
|
87
|
+
return (
|
|
88
|
+
isSSRSafe(expr.consequent as t.Expression, safeIdents) ||
|
|
89
|
+
isSSRSafe(expr.alternate as t.Expression, safeIdents)
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
if (
|
|
93
|
+
t.isCallExpression(expr) &&
|
|
94
|
+
t.isMemberExpression(expr.callee) &&
|
|
95
|
+
t.isIdentifier(expr.callee.property) &&
|
|
96
|
+
expr.callee.property.name === "map"
|
|
97
|
+
) {
|
|
98
|
+
const cb = expr.arguments[0];
|
|
99
|
+
if (t.isArrowFunctionExpression(cb) || t.isFunctionExpression(cb)) {
|
|
100
|
+
const body = cb.body;
|
|
101
|
+
if (t.isJSXElement(body) || t.isJSXFragment(body)) return true;
|
|
102
|
+
if (t.isBlockStatement(body)) {
|
|
103
|
+
for (const stmt of body.body) {
|
|
104
|
+
if (
|
|
105
|
+
t.isReturnStatement(stmt) &&
|
|
106
|
+
stmt.argument &&
|
|
107
|
+
(t.isJSXElement(stmt.argument) || t.isJSXFragment(stmt.argument))
|
|
108
|
+
) return true;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
*/
|
|
116
|
+
export function processElementSSR(
|
|
117
|
+
node: t.JSXElement,
|
|
118
|
+
signals: Set<string>,
|
|
119
|
+
): t.TemplateLiteral {
|
|
120
|
+
const tagName = getTagName(node.openingElement.name);
|
|
121
|
+
const isComponent = /^[A-Z]/.test(tagName);
|
|
122
|
+
|
|
123
|
+
if (isComponent) {
|
|
124
|
+
// Global components (Window/Document/Body) produce no SSR output
|
|
125
|
+
if (isGlobalComponent(tagName)) {
|
|
126
|
+
return t.templateLiteral(
|
|
127
|
+
[t.templateElement({ raw: "", cooked: "" }, true)],
|
|
128
|
+
[],
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
return processComponentSSR(node, tagName, signals);
|
|
132
|
+
}
|
|
133
|
+
let dangerousHTML: t.Expression | null = null;
|
|
134
|
+
const parts: Array<string | t.Expression> = [];
|
|
135
|
+
parts.push(`<${tagName}`);
|
|
136
|
+
|
|
137
|
+
// attributes
|
|
138
|
+
for (const attr of node.openingElement.attributes) {
|
|
139
|
+
if (!t.isJSXAttribute(attr)) continue;
|
|
140
|
+
const attrName = (attr.name as t.JSXIdentifier).name;
|
|
141
|
+
|
|
142
|
+
if (attrName === "innerHTML") {
|
|
143
|
+
if (t.isJSXExpressionContainer(attr.value)) {
|
|
144
|
+
dangerousHTML = attr.value.expression as t.Expression;
|
|
145
|
+
}
|
|
146
|
+
continue; // don't output as attribute
|
|
147
|
+
}
|
|
148
|
+
// drop event handlers
|
|
149
|
+
if (attrName.startsWith("on")) continue;
|
|
150
|
+
// drop client-only props
|
|
151
|
+
if (attrName === "use") continue;
|
|
152
|
+
if (attrName.startsWith("bind")) continue;
|
|
153
|
+
|
|
154
|
+
const domAttr = ATTR_MAP_SSR[attrName] ?? attrName;
|
|
155
|
+
|
|
156
|
+
if (t.isStringLiteral(attr.value)) {
|
|
157
|
+
parts.push(` ${domAttr}="${attr.value.value}"`);
|
|
158
|
+
} else if (t.isJSXExpressionContainer(attr.value)) {
|
|
159
|
+
const expr = attr.value.expression as t.Expression;
|
|
160
|
+
parts.push(` ${domAttr}="`);
|
|
161
|
+
parts.push(escape(expr));
|
|
162
|
+
parts.push(`"`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (VOID_ELEMENTS.has(tagName)) {
|
|
167
|
+
parts.push(">");
|
|
168
|
+
return buildTemplate(parts);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
parts.push(">");
|
|
172
|
+
|
|
173
|
+
if (dangerousHTML) {
|
|
174
|
+
parts.push(dangerousHTML);
|
|
175
|
+
parts.push(`</${tagName}>`);
|
|
176
|
+
return buildTemplate(parts);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// children
|
|
180
|
+
for (const child of node.children) {
|
|
181
|
+
if (t.isJSXText(child)) {
|
|
182
|
+
const text = child.value;
|
|
183
|
+
if (!text.trim()) continue;
|
|
184
|
+
const normalized = text.replace(/\s*\n\s*/g, " ");
|
|
185
|
+
parts.push(normalized);
|
|
186
|
+
} else if (t.isJSXElement(child)) {
|
|
187
|
+
parts.push(processElementSSR(child, signals));
|
|
188
|
+
} else if (t.isJSXFragment(child)) {
|
|
189
|
+
parts.push(processFragmentSSR(child, signals));
|
|
190
|
+
} else if (t.isJSXExpressionContainer(child)) {
|
|
191
|
+
if (t.isJSXEmptyExpression(child.expression)) continue;
|
|
192
|
+
const expr = child.expression as t.Expression;
|
|
193
|
+
if (t.isJSXElement(expr)) {
|
|
194
|
+
parts.push(processElementSSR(expr, signals));
|
|
195
|
+
} else if (t.isJSXFragment(expr)) {
|
|
196
|
+
parts.push(processFragmentSSR(expr, signals));
|
|
197
|
+
} else {
|
|
198
|
+
// Delimit dynamic text segments so SSR produces separate text
|
|
199
|
+
// nodes that the hydrate compiler can claim individually.
|
|
200
|
+
parts.push("<!--g-->");
|
|
201
|
+
parts.push(escape(expr));
|
|
202
|
+
parts.push("<!--/g-->");
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
parts.push(`</${tagName}>`);
|
|
208
|
+
return buildTemplate(parts);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function processComponentSSR(
|
|
212
|
+
node: t.JSXElement,
|
|
213
|
+
tagName: string,
|
|
214
|
+
signals: Set<string>,
|
|
215
|
+
): t.TemplateLiteral {
|
|
216
|
+
const props = t.objectExpression([]);
|
|
217
|
+
|
|
218
|
+
for (const attr of node.openingElement.attributes) {
|
|
219
|
+
if (!t.isJSXAttribute(attr)) continue;
|
|
220
|
+
const attrName = (attr.name as t.JSXIdentifier).name;
|
|
221
|
+
// data-key is a framework-internal attribute for HTML elements only; skip for components
|
|
222
|
+
if (attrName === "data-key" || attrName === "key") continue;
|
|
223
|
+
// use StringLiteral key for hyphenated names, Identifier otherwise
|
|
224
|
+
const propKey = /[^a-zA-Z_$0-9]/.test(attrName)
|
|
225
|
+
? t.stringLiteral(attrName)
|
|
226
|
+
: t.identifier(attrName);
|
|
227
|
+
if (t.isStringLiteral(attr.value)) {
|
|
228
|
+
props.properties.push(t.objectProperty(propKey, attr.value));
|
|
229
|
+
} else if (t.isJSXExpressionContainer(attr.value)) {
|
|
230
|
+
props.properties.push(
|
|
231
|
+
t.objectProperty(propKey, attr.value.expression as t.Expression),
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// children as string
|
|
237
|
+
const childParts: Array<string | t.Expression> = [];
|
|
238
|
+
for (const child of node.children) {
|
|
239
|
+
if (t.isJSXText(child)) {
|
|
240
|
+
const text = child.value;
|
|
241
|
+
if (!text.trim()) continue;
|
|
242
|
+
const normalized = text.replace(/\s*\n\s*/g, " ");
|
|
243
|
+
childParts.push(normalized);
|
|
244
|
+
} else if (t.isJSXElement(child)) {
|
|
245
|
+
childParts.push(processElementSSR(child, signals));
|
|
246
|
+
} else if (t.isJSXFragment(child)) {
|
|
247
|
+
childParts.push(processFragmentSSR(child, signals));
|
|
248
|
+
} else if (t.isJSXExpressionContainer(child)) {
|
|
249
|
+
if (t.isJSXEmptyExpression(child.expression)) continue;
|
|
250
|
+
const expr = child.expression as t.Expression;
|
|
251
|
+
if (t.isJSXElement(expr)) {
|
|
252
|
+
childParts.push(processElementSSR(expr, signals));
|
|
253
|
+
} else if (t.isJSXFragment(expr)) {
|
|
254
|
+
childParts.push(processFragmentSSR(expr, signals));
|
|
255
|
+
} else {
|
|
256
|
+
childParts.push("<!--g-->");
|
|
257
|
+
childParts.push(escape(expr));
|
|
258
|
+
childParts.push("<!--/g-->");
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (childParts.length > 0) {
|
|
264
|
+
props.properties.push(
|
|
265
|
+
t.objectProperty(
|
|
266
|
+
t.identifier("children"),
|
|
267
|
+
t.callExpression(t.identifier("__h"), [buildTemplate(childParts)]),
|
|
268
|
+
),
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Component({ ...props }) - result is already a string
|
|
273
|
+
const call = t.callExpression(t.identifier("__h"), [
|
|
274
|
+
t.callExpression(t.identifier(tagName), [props]),
|
|
275
|
+
]);
|
|
276
|
+
|
|
277
|
+
// wrap in template so return type is consistent
|
|
278
|
+
return t.templateLiteral(
|
|
279
|
+
[
|
|
280
|
+
t.templateElement({ raw: "", cooked: "" }),
|
|
281
|
+
t.templateElement({ raw: "", cooked: "" }, true),
|
|
282
|
+
],
|
|
283
|
+
[call],
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export function processFragmentSSR(
|
|
288
|
+
node: t.JSXFragment,
|
|
289
|
+
signals: Set<string>,
|
|
290
|
+
): t.TemplateLiteral {
|
|
291
|
+
const parts: Array<string | t.Expression> = [];
|
|
292
|
+
|
|
293
|
+
for (const child of node.children) {
|
|
294
|
+
if (t.isJSXText(child)) {
|
|
295
|
+
const text = child.value;
|
|
296
|
+
if (!text.trim()) continue;
|
|
297
|
+
const normalized = text.replace(/\s*\n\s*/g, " ");
|
|
298
|
+
parts.push(normalized);
|
|
299
|
+
} else if (t.isJSXElement(child)) {
|
|
300
|
+
parts.push(processElementSSR(child, signals));
|
|
301
|
+
} else if (t.isJSXFragment(child)) {
|
|
302
|
+
parts.push(processFragmentSSR(child, signals));
|
|
303
|
+
} else if (t.isJSXExpressionContainer(child)) {
|
|
304
|
+
if (t.isJSXEmptyExpression(child.expression)) continue;
|
|
305
|
+
const expr = child.expression as t.Expression;
|
|
306
|
+
if (t.isJSXElement(expr)) {
|
|
307
|
+
parts.push(processElementSSR(expr, signals));
|
|
308
|
+
} else if (t.isJSXFragment(expr)) {
|
|
309
|
+
parts.push(processFragmentSSR(expr, signals));
|
|
310
|
+
} else {
|
|
311
|
+
parts.push("<!--g-->");
|
|
312
|
+
parts.push(escape(expr));
|
|
313
|
+
parts.push("<!--/g-->");
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return buildTemplate(parts);
|
|
319
|
+
}
|