what-compiler 0.5.4 → 0.6.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 +1 -1
- package/dist/babel-plugin.js +1238 -0
- package/dist/babel-plugin.js.map +7 -0
- package/dist/babel-plugin.min.js +2 -0
- package/dist/babel-plugin.min.js.map +7 -0
- package/dist/file-router.js +195 -0
- package/dist/file-router.js.map +7 -0
- package/dist/file-router.min.js +3 -0
- package/dist/file-router.min.js.map +7 -0
- package/dist/index.js +1996 -0
- package/dist/index.js.map +7 -0
- package/dist/index.min.js +397 -0
- package/dist/index.min.js.map +7 -0
- package/dist/runtime.js +9 -0
- package/dist/runtime.js.map +7 -0
- package/dist/runtime.min.js +2 -0
- package/dist/runtime.min.js.map +7 -0
- package/dist/vite-plugin.js +1985 -0
- package/dist/vite-plugin.js.map +7 -0
- package/dist/vite-plugin.min.js +397 -0
- package/dist/vite-plugin.min.js.map +7 -0
- package/package.json +27 -11
- package/src/babel-plugin.js +818 -520
- package/src/error-overlay.js +190 -119
- package/src/file-router.js +2 -1
- package/src/vite-plugin.js +86 -3
|
@@ -0,0 +1,1238 @@
|
|
|
1
|
+
// packages/compiler/src/babel-plugin.js
|
|
2
|
+
var EVENT_MODIFIERS = /* @__PURE__ */ new Set(["preventDefault", "stopPropagation", "once", "capture", "passive", "self"]);
|
|
3
|
+
var EVENT_OPTION_MODIFIERS = /* @__PURE__ */ new Set(["once", "capture", "passive"]);
|
|
4
|
+
var VOID_HTML_ELEMENTS = /* @__PURE__ */ new Set([
|
|
5
|
+
"area",
|
|
6
|
+
"base",
|
|
7
|
+
"br",
|
|
8
|
+
"col",
|
|
9
|
+
"embed",
|
|
10
|
+
"hr",
|
|
11
|
+
"img",
|
|
12
|
+
"input",
|
|
13
|
+
"link",
|
|
14
|
+
"meta",
|
|
15
|
+
"param",
|
|
16
|
+
"source",
|
|
17
|
+
"track",
|
|
18
|
+
"wbr"
|
|
19
|
+
]);
|
|
20
|
+
var DELEGATED_EVENTS = /* @__PURE__ */ new Set([
|
|
21
|
+
"click",
|
|
22
|
+
"input",
|
|
23
|
+
"change",
|
|
24
|
+
"keydown",
|
|
25
|
+
"keyup",
|
|
26
|
+
"submit",
|
|
27
|
+
"focusin",
|
|
28
|
+
"focusout",
|
|
29
|
+
"mousedown",
|
|
30
|
+
"mouseup"
|
|
31
|
+
]);
|
|
32
|
+
var SAFE_GLOBAL_CALLS = /* @__PURE__ */ new Set([
|
|
33
|
+
"Math",
|
|
34
|
+
"Number",
|
|
35
|
+
"String",
|
|
36
|
+
"Boolean",
|
|
37
|
+
"parseInt",
|
|
38
|
+
"parseFloat",
|
|
39
|
+
"isNaN",
|
|
40
|
+
"isFinite",
|
|
41
|
+
"encodeURIComponent",
|
|
42
|
+
"decodeURIComponent",
|
|
43
|
+
"encodeURI",
|
|
44
|
+
"decodeURI",
|
|
45
|
+
"JSON",
|
|
46
|
+
"Date",
|
|
47
|
+
"Array",
|
|
48
|
+
"Object",
|
|
49
|
+
"console",
|
|
50
|
+
"RegExp"
|
|
51
|
+
]);
|
|
52
|
+
var SIGNAL_CREATORS = /* @__PURE__ */ new Set([
|
|
53
|
+
"useSignal",
|
|
54
|
+
"signal",
|
|
55
|
+
"computed",
|
|
56
|
+
"useComputed",
|
|
57
|
+
"useState",
|
|
58
|
+
"useReducer",
|
|
59
|
+
"createResource",
|
|
60
|
+
"useSWR",
|
|
61
|
+
"useQuery",
|
|
62
|
+
"useInfiniteQuery"
|
|
63
|
+
]);
|
|
64
|
+
function whatBabelPlugin({ types: t }) {
|
|
65
|
+
function parseEventModifiers(name) {
|
|
66
|
+
const parts = name.split("|");
|
|
67
|
+
const eventName = parts[0];
|
|
68
|
+
const modifiers = parts.slice(1).filter((m) => EVENT_MODIFIERS.has(m));
|
|
69
|
+
return { eventName, modifiers };
|
|
70
|
+
}
|
|
71
|
+
function isBindingAttribute(name) {
|
|
72
|
+
return name.startsWith("bind:");
|
|
73
|
+
}
|
|
74
|
+
function getBindingProperty(name) {
|
|
75
|
+
return name.slice(5);
|
|
76
|
+
}
|
|
77
|
+
function isComponent(name) {
|
|
78
|
+
return /^[A-Z]/.test(name);
|
|
79
|
+
}
|
|
80
|
+
function isVoidHtmlElement(name) {
|
|
81
|
+
return VOID_HTML_ELEMENTS.has(String(name).toLowerCase());
|
|
82
|
+
}
|
|
83
|
+
function getAttributeValue(value) {
|
|
84
|
+
if (!value) return t.booleanLiteral(true);
|
|
85
|
+
if (t.isJSXExpressionContainer(value)) return value.expression;
|
|
86
|
+
if (t.isStringLiteral(value)) return value;
|
|
87
|
+
return t.stringLiteral(value.value || "");
|
|
88
|
+
}
|
|
89
|
+
function normalizeAttrName(attrName) {
|
|
90
|
+
if (attrName === "className") return "class";
|
|
91
|
+
if (attrName === "htmlFor") return "for";
|
|
92
|
+
return attrName;
|
|
93
|
+
}
|
|
94
|
+
function getAttrName(attr) {
|
|
95
|
+
if (t.isJSXNamespacedName(attr.name)) {
|
|
96
|
+
return `${attr.name.namespace.name}:${attr.name.name.name}`;
|
|
97
|
+
}
|
|
98
|
+
return typeof attr.name.name === "string" ? attr.name.name : String(attr.name.name);
|
|
99
|
+
}
|
|
100
|
+
function createEventHandler(handler, modifiers) {
|
|
101
|
+
if (modifiers.length === 0) return handler;
|
|
102
|
+
let wrappedHandler = handler;
|
|
103
|
+
for (const mod of modifiers) {
|
|
104
|
+
switch (mod) {
|
|
105
|
+
case "preventDefault":
|
|
106
|
+
wrappedHandler = t.arrowFunctionExpression(
|
|
107
|
+
[t.identifier("e")],
|
|
108
|
+
t.blockStatement([
|
|
109
|
+
t.expressionStatement(
|
|
110
|
+
t.callExpression(
|
|
111
|
+
t.memberExpression(t.identifier("e"), t.identifier("preventDefault")),
|
|
112
|
+
[]
|
|
113
|
+
)
|
|
114
|
+
),
|
|
115
|
+
t.expressionStatement(
|
|
116
|
+
t.callExpression(wrappedHandler, [t.identifier("e")])
|
|
117
|
+
)
|
|
118
|
+
])
|
|
119
|
+
);
|
|
120
|
+
break;
|
|
121
|
+
case "stopPropagation":
|
|
122
|
+
wrappedHandler = t.arrowFunctionExpression(
|
|
123
|
+
[t.identifier("e")],
|
|
124
|
+
t.blockStatement([
|
|
125
|
+
t.expressionStatement(
|
|
126
|
+
t.callExpression(
|
|
127
|
+
t.memberExpression(t.identifier("e"), t.identifier("stopPropagation")),
|
|
128
|
+
[]
|
|
129
|
+
)
|
|
130
|
+
),
|
|
131
|
+
t.expressionStatement(
|
|
132
|
+
t.callExpression(wrappedHandler, [t.identifier("e")])
|
|
133
|
+
)
|
|
134
|
+
])
|
|
135
|
+
);
|
|
136
|
+
break;
|
|
137
|
+
case "self":
|
|
138
|
+
wrappedHandler = t.arrowFunctionExpression(
|
|
139
|
+
[t.identifier("e")],
|
|
140
|
+
t.blockStatement([
|
|
141
|
+
t.ifStatement(
|
|
142
|
+
t.binaryExpression(
|
|
143
|
+
"===",
|
|
144
|
+
t.memberExpression(t.identifier("e"), t.identifier("target")),
|
|
145
|
+
t.memberExpression(t.identifier("e"), t.identifier("currentTarget"))
|
|
146
|
+
),
|
|
147
|
+
t.expressionStatement(
|
|
148
|
+
t.callExpression(wrappedHandler, [t.identifier("e")])
|
|
149
|
+
)
|
|
150
|
+
)
|
|
151
|
+
])
|
|
152
|
+
);
|
|
153
|
+
break;
|
|
154
|
+
case "once":
|
|
155
|
+
case "capture":
|
|
156
|
+
case "passive":
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return wrappedHandler;
|
|
161
|
+
}
|
|
162
|
+
function isSignalIdentifier(name, signalNames) {
|
|
163
|
+
return signalNames.has(name);
|
|
164
|
+
}
|
|
165
|
+
function collectSignalNamesFromScope(path) {
|
|
166
|
+
const signalNames = /* @__PURE__ */ new Set();
|
|
167
|
+
function extractFromDeclarator(decl) {
|
|
168
|
+
const init = decl.init;
|
|
169
|
+
if (!init || !t.isCallExpression(init)) return;
|
|
170
|
+
const callee = init.callee;
|
|
171
|
+
let calleeName = "";
|
|
172
|
+
if (t.isIdentifier(callee)) {
|
|
173
|
+
calleeName = callee.name;
|
|
174
|
+
} else if (t.isMemberExpression(callee) && t.isIdentifier(callee.property)) {
|
|
175
|
+
calleeName = callee.property.name;
|
|
176
|
+
}
|
|
177
|
+
if (SIGNAL_CREATORS.has(calleeName)) {
|
|
178
|
+
const id = decl.id;
|
|
179
|
+
if (t.isIdentifier(id)) {
|
|
180
|
+
signalNames.add(id.name);
|
|
181
|
+
} else if (t.isArrayPattern(id)) {
|
|
182
|
+
for (const el of id.elements) {
|
|
183
|
+
if (t.isIdentifier(el)) signalNames.add(el.name);
|
|
184
|
+
}
|
|
185
|
+
} else if (t.isObjectPattern(id)) {
|
|
186
|
+
for (const prop of id.properties) {
|
|
187
|
+
if (t.isObjectProperty(prop) && t.isIdentifier(prop.value)) {
|
|
188
|
+
signalNames.add(prop.value.name);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
let scope = path.scope;
|
|
195
|
+
while (scope) {
|
|
196
|
+
for (const [name, binding] of Object.entries(scope.bindings)) {
|
|
197
|
+
if (binding.path.isVariableDeclarator()) {
|
|
198
|
+
extractFromDeclarator(binding.path.node);
|
|
199
|
+
}
|
|
200
|
+
if (binding.path.isIdentifier() || binding.kind === "param") {
|
|
201
|
+
const fnPath = binding.scope.path;
|
|
202
|
+
if (fnPath && fnPath.node && fnPath.node.params) {
|
|
203
|
+
for (const param of fnPath.node.params) {
|
|
204
|
+
if (t.isObjectPattern(param)) {
|
|
205
|
+
for (const prop of param.properties) {
|
|
206
|
+
if (t.isObjectProperty(prop) && t.isIdentifier(prop.value)) {
|
|
207
|
+
signalNames.add(prop.value.name);
|
|
208
|
+
} else if (t.isRestElement(prop) && t.isIdentifier(prop.argument)) {
|
|
209
|
+
signalNames.add(prop.argument.name);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
scope = scope.parent;
|
|
218
|
+
}
|
|
219
|
+
return signalNames;
|
|
220
|
+
}
|
|
221
|
+
function collectSignalNames(path) {
|
|
222
|
+
return collectSignalNamesFromScope(path);
|
|
223
|
+
}
|
|
224
|
+
function isSafeGlobalCall(expr) {
|
|
225
|
+
if (!t.isCallExpression(expr)) return false;
|
|
226
|
+
const callee = expr.callee;
|
|
227
|
+
if (t.isMemberExpression(callee) && t.isIdentifier(callee.object)) {
|
|
228
|
+
return SAFE_GLOBAL_CALLS.has(callee.object.name);
|
|
229
|
+
}
|
|
230
|
+
if (t.isIdentifier(callee)) {
|
|
231
|
+
return SAFE_GLOBAL_CALLS.has(callee.name);
|
|
232
|
+
}
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
function isUncertainReactive(expr, signalNames, importedIds) {
|
|
236
|
+
if (!signalNames) return false;
|
|
237
|
+
if (t.isCallExpression(expr)) {
|
|
238
|
+
if (t.isIdentifier(expr.callee) && isSignalIdentifier(expr.callee.name, signalNames)) {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
if (importedIds && t.isIdentifier(expr.callee) && importedIds.has(expr.callee.name) && !SAFE_GLOBAL_CALLS.has(expr.callee.name)) {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
if (t.isMemberExpression(expr.callee) && t.isIdentifier(expr.callee.object) && isSignalIdentifier(expr.callee.object.name, signalNames)) {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
if (isSafeGlobalCall(expr)) return false;
|
|
248
|
+
if (expr.arguments.some((arg) => isPotentiallyReactive(arg, signalNames, importedIds))) {
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
function isPotentiallyReactive(expr, signalNames, importedIds) {
|
|
255
|
+
if (!signalNames) signalNames = /* @__PURE__ */ new Set();
|
|
256
|
+
if (t.isCallExpression(expr)) {
|
|
257
|
+
if (t.isIdentifier(expr.callee) && isSignalIdentifier(expr.callee.name, signalNames)) {
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
if (importedIds && t.isIdentifier(expr.callee) && importedIds.has(expr.callee.name)) {
|
|
261
|
+
if (!SAFE_GLOBAL_CALLS.has(expr.callee.name)) {
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (t.isMemberExpression(expr.callee)) {
|
|
266
|
+
if (t.isIdentifier(expr.callee.object) && isSignalIdentifier(expr.callee.object.name, signalNames)) {
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (isSafeGlobalCall(expr)) {
|
|
271
|
+
return expr.arguments.some((arg) => isPotentiallyReactive(arg, signalNames, importedIds));
|
|
272
|
+
}
|
|
273
|
+
if (t.isIdentifier(expr.callee)) {
|
|
274
|
+
return expr.arguments.some((arg) => isPotentiallyReactive(arg, signalNames, importedIds));
|
|
275
|
+
}
|
|
276
|
+
return isPotentiallyReactive(expr.callee, signalNames, importedIds) || expr.arguments.some((arg) => isPotentiallyReactive(arg, signalNames, importedIds));
|
|
277
|
+
}
|
|
278
|
+
if (t.isIdentifier(expr)) {
|
|
279
|
+
return isSignalIdentifier(expr.name, signalNames);
|
|
280
|
+
}
|
|
281
|
+
if (t.isMemberExpression(expr)) {
|
|
282
|
+
return isPotentiallyReactive(expr.object, signalNames, importedIds);
|
|
283
|
+
}
|
|
284
|
+
if (t.isConditionalExpression(expr)) {
|
|
285
|
+
return isPotentiallyReactive(expr.test, signalNames, importedIds) || isPotentiallyReactive(expr.consequent, signalNames, importedIds) || isPotentiallyReactive(expr.alternate, signalNames, importedIds);
|
|
286
|
+
}
|
|
287
|
+
if (t.isBinaryExpression(expr) || t.isLogicalExpression(expr)) {
|
|
288
|
+
return isPotentiallyReactive(expr.left, signalNames, importedIds) || isPotentiallyReactive(expr.right, signalNames, importedIds);
|
|
289
|
+
}
|
|
290
|
+
if (t.isUnaryExpression(expr)) {
|
|
291
|
+
return isPotentiallyReactive(expr.argument, signalNames, importedIds);
|
|
292
|
+
}
|
|
293
|
+
if (t.isTemplateLiteral(expr)) {
|
|
294
|
+
return expr.expressions.some((e) => isPotentiallyReactive(e, signalNames, importedIds));
|
|
295
|
+
}
|
|
296
|
+
if (t.isObjectExpression(expr)) {
|
|
297
|
+
return expr.properties.some(
|
|
298
|
+
(prop) => t.isObjectProperty(prop) && isPotentiallyReactive(prop.value, signalNames, importedIds)
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
if (t.isArrayExpression(expr)) {
|
|
302
|
+
return expr.elements.some((el) => el && isPotentiallyReactive(el, signalNames, importedIds));
|
|
303
|
+
}
|
|
304
|
+
if (t.isArrowFunctionExpression(expr) || t.isFunctionExpression(expr)) {
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
function isStaticChild(child) {
|
|
310
|
+
if (t.isJSXText(child)) return true;
|
|
311
|
+
if (t.isJSXExpressionContainer(child)) return false;
|
|
312
|
+
if (t.isJSXElement(child)) {
|
|
313
|
+
const el = child.openingElement;
|
|
314
|
+
const tagName = el.name.name;
|
|
315
|
+
if (isComponent(tagName)) return false;
|
|
316
|
+
for (const attr of el.attributes) {
|
|
317
|
+
if (t.isJSXSpreadAttribute(attr)) return false;
|
|
318
|
+
const value = attr.value;
|
|
319
|
+
if (t.isJSXExpressionContainer(value)) return false;
|
|
320
|
+
}
|
|
321
|
+
return child.children.every(isStaticChild);
|
|
322
|
+
}
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
function isDynamicAttr(attr) {
|
|
326
|
+
if (t.isJSXSpreadAttribute(attr)) return true;
|
|
327
|
+
if (!attr.value) return false;
|
|
328
|
+
return t.isJSXExpressionContainer(attr.value);
|
|
329
|
+
}
|
|
330
|
+
function extractStaticHTML(node) {
|
|
331
|
+
if (t.isJSXText(node)) {
|
|
332
|
+
const text = node.value.replace(/\n\s+/g, " ").trim();
|
|
333
|
+
return text ? escapeHTML(text) : "";
|
|
334
|
+
}
|
|
335
|
+
if (t.isJSXExpressionContainer(node)) {
|
|
336
|
+
if (t.isJSXEmptyExpression(node.expression)) return "";
|
|
337
|
+
return "<!--$-->";
|
|
338
|
+
}
|
|
339
|
+
if (!t.isJSXElement(node)) return "";
|
|
340
|
+
const el = node.openingElement;
|
|
341
|
+
const tagName = el.name.name;
|
|
342
|
+
if (isComponent(tagName)) return "";
|
|
343
|
+
let html = `<${tagName}`;
|
|
344
|
+
for (const attr of el.attributes) {
|
|
345
|
+
if (t.isJSXSpreadAttribute(attr)) continue;
|
|
346
|
+
const name = getAttrName(attr);
|
|
347
|
+
if (name.startsWith("on") || name.startsWith("bind:") || name.includes("|")) continue;
|
|
348
|
+
let domName = name;
|
|
349
|
+
if (name === "className") domName = "class";
|
|
350
|
+
if (name === "htmlFor") domName = "for";
|
|
351
|
+
if (!attr.value) {
|
|
352
|
+
html += ` ${domName}`;
|
|
353
|
+
} else if (t.isStringLiteral(attr.value)) {
|
|
354
|
+
html += ` ${domName}="${escapeAttr(attr.value.value)}"`;
|
|
355
|
+
} else if (t.isJSXExpressionContainer(attr.value)) {
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
const selfClosing = node.openingElement.selfClosing;
|
|
360
|
+
if (selfClosing && isVoidHtmlElement(tagName)) {
|
|
361
|
+
html += ">";
|
|
362
|
+
return html;
|
|
363
|
+
}
|
|
364
|
+
if (selfClosing) {
|
|
365
|
+
html += `></${tagName}>`;
|
|
366
|
+
return html;
|
|
367
|
+
}
|
|
368
|
+
html += ">";
|
|
369
|
+
for (const child of node.children) {
|
|
370
|
+
if (t.isJSXText(child)) {
|
|
371
|
+
const text = child.value.replace(/\n\s+/g, " ").trim();
|
|
372
|
+
if (text) html += escapeHTML(text);
|
|
373
|
+
} else if (t.isJSXExpressionContainer(child)) {
|
|
374
|
+
if (!t.isJSXEmptyExpression(child.expression)) {
|
|
375
|
+
html += "<!--$-->";
|
|
376
|
+
}
|
|
377
|
+
} else if (t.isJSXElement(child)) {
|
|
378
|
+
if (isComponent(child.openingElement.name.name)) {
|
|
379
|
+
html += "<!--$-->";
|
|
380
|
+
} else {
|
|
381
|
+
html += extractStaticHTML(child);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
html += `</${tagName}>`;
|
|
386
|
+
return html;
|
|
387
|
+
}
|
|
388
|
+
function escapeHTML(str) {
|
|
389
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
390
|
+
}
|
|
391
|
+
function escapeAttr(str) {
|
|
392
|
+
return str.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
393
|
+
}
|
|
394
|
+
function transformElementFineGrained(path, state) {
|
|
395
|
+
const { node } = path;
|
|
396
|
+
const openingElement = node.openingElement;
|
|
397
|
+
const tagName = openingElement.name.name;
|
|
398
|
+
if (isComponent(tagName)) {
|
|
399
|
+
return transformComponentFineGrained(path, state);
|
|
400
|
+
}
|
|
401
|
+
if (tagName === "For") {
|
|
402
|
+
return transformForFineGrained(path, state);
|
|
403
|
+
}
|
|
404
|
+
if (tagName === "Show") {
|
|
405
|
+
return transformShowFineGrained(path, state);
|
|
406
|
+
}
|
|
407
|
+
const attributes = openingElement.attributes;
|
|
408
|
+
const children = node.children;
|
|
409
|
+
const allChildrenStatic = children.every(isStaticChild);
|
|
410
|
+
const allAttrsStatic = attributes.every((attr) => !isDynamicAttr(attr));
|
|
411
|
+
const noEvents = attributes.every((attr) => {
|
|
412
|
+
if (t.isJSXSpreadAttribute(attr)) return false;
|
|
413
|
+
const name = getAttrName(attr);
|
|
414
|
+
return !name?.startsWith("on") && !name?.startsWith("bind:");
|
|
415
|
+
});
|
|
416
|
+
if (allChildrenStatic && allAttrsStatic && noEvents) {
|
|
417
|
+
const html2 = extractStaticHTML(node);
|
|
418
|
+
if (html2) {
|
|
419
|
+
const tmplId2 = getOrCreateTemplate(state, html2);
|
|
420
|
+
state.needsTemplate = true;
|
|
421
|
+
return t.callExpression(t.identifier(tmplId2), []);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
const html = extractStaticHTML(node);
|
|
425
|
+
if (!html) {
|
|
426
|
+
const loc = node.loc;
|
|
427
|
+
const fileName = state.filename || state.file?.opts?.filename || "<unknown>";
|
|
428
|
+
const lineInfo = loc ? `:${loc.start.line}:${loc.start.column}` : "";
|
|
429
|
+
console.warn(
|
|
430
|
+
`[what-compiler] Could not extract template for <${tagName}> at ${fileName}${lineInfo}. Falling back to h() for this element. This element could not be statically analyzed. Consider simplifying the JSX.`
|
|
431
|
+
);
|
|
432
|
+
state.needsH = true;
|
|
433
|
+
return transformElementAsH(path, state);
|
|
434
|
+
}
|
|
435
|
+
const tmplId = getOrCreateTemplate(state, html);
|
|
436
|
+
state.needsTemplate = true;
|
|
437
|
+
const elId = state.nextVarId();
|
|
438
|
+
const statements = [
|
|
439
|
+
t.variableDeclaration("const", [
|
|
440
|
+
t.variableDeclarator(t.identifier(elId), t.callExpression(t.identifier(tmplId), []))
|
|
441
|
+
])
|
|
442
|
+
];
|
|
443
|
+
applyDynamicAttrs(statements, elId, attributes, state);
|
|
444
|
+
applyDynamicChildren(statements, elId, children, node, state);
|
|
445
|
+
if (!state._pendingSetup) state._pendingSetup = [];
|
|
446
|
+
state._pendingSetup.push(...statements);
|
|
447
|
+
return t.identifier(elId);
|
|
448
|
+
}
|
|
449
|
+
function transformElementAsH(path, state) {
|
|
450
|
+
const { node } = path;
|
|
451
|
+
const openingElement = node.openingElement;
|
|
452
|
+
const tagName = openingElement.name.name;
|
|
453
|
+
const attributes = openingElement.attributes;
|
|
454
|
+
const children = node.children;
|
|
455
|
+
const props = [];
|
|
456
|
+
for (const attr of attributes) {
|
|
457
|
+
if (t.isJSXSpreadAttribute(attr)) continue;
|
|
458
|
+
const attrName = getAttrName(attr);
|
|
459
|
+
const value = getAttributeValue(attr.value);
|
|
460
|
+
let domAttrName = normalizeAttrName(attrName);
|
|
461
|
+
props.push(
|
|
462
|
+
t.objectProperty(
|
|
463
|
+
/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(domAttrName) ? t.identifier(domAttrName) : t.stringLiteral(domAttrName),
|
|
464
|
+
value
|
|
465
|
+
)
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
const transformedChildren = [];
|
|
469
|
+
for (const child of children) {
|
|
470
|
+
if (t.isJSXText(child)) {
|
|
471
|
+
const text = child.value.replace(/\n\s+/g, " ").trim();
|
|
472
|
+
if (text) transformedChildren.push(t.stringLiteral(text));
|
|
473
|
+
} else if (t.isJSXExpressionContainer(child)) {
|
|
474
|
+
if (!t.isJSXEmptyExpression(child.expression)) {
|
|
475
|
+
transformedChildren.push(child.expression);
|
|
476
|
+
}
|
|
477
|
+
} else if (t.isJSXElement(child)) {
|
|
478
|
+
transformedChildren.push(transformElementFineGrained({ node: child }, state));
|
|
479
|
+
} else if (t.isJSXFragment(child)) {
|
|
480
|
+
transformedChildren.push(transformFragmentFineGrained({ node: child }, state));
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
const propsExpr = props.length > 0 ? t.objectExpression(props) : t.nullLiteral();
|
|
484
|
+
return t.callExpression(t.identifier("h"), [t.stringLiteral(tagName), propsExpr, ...transformedChildren]);
|
|
485
|
+
}
|
|
486
|
+
function applyDynamicAttrs(statements, elId, attributes, state) {
|
|
487
|
+
function buildSetPropCall(propName, valueExpr) {
|
|
488
|
+
state.needsSetProp = true;
|
|
489
|
+
return t.callExpression(t.identifier("_$setProp"), [
|
|
490
|
+
t.identifier(elId),
|
|
491
|
+
t.stringLiteral(propName),
|
|
492
|
+
valueExpr
|
|
493
|
+
]);
|
|
494
|
+
}
|
|
495
|
+
for (const attr of attributes) {
|
|
496
|
+
if (t.isJSXSpreadAttribute(attr)) {
|
|
497
|
+
state.needsSpread = true;
|
|
498
|
+
statements.push(
|
|
499
|
+
t.expressionStatement(
|
|
500
|
+
t.callExpression(t.identifier("_$spread"), [t.identifier(elId), attr.argument])
|
|
501
|
+
)
|
|
502
|
+
);
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
const attrName = getAttrName(attr);
|
|
506
|
+
if (attrName.startsWith("on") && !attrName.includes("|")) {
|
|
507
|
+
const event = attrName.slice(2).toLowerCase();
|
|
508
|
+
const handler = getAttributeValue(attr.value);
|
|
509
|
+
if (DELEGATED_EVENTS.has(event)) {
|
|
510
|
+
state.needsDelegation = true;
|
|
511
|
+
if (!state.delegatedEvents) state.delegatedEvents = /* @__PURE__ */ new Set();
|
|
512
|
+
state.delegatedEvents.add(event);
|
|
513
|
+
statements.push(
|
|
514
|
+
t.expressionStatement(
|
|
515
|
+
t.assignmentExpression(
|
|
516
|
+
"=",
|
|
517
|
+
t.memberExpression(
|
|
518
|
+
t.identifier(elId),
|
|
519
|
+
t.identifier(`__${event}`)
|
|
520
|
+
),
|
|
521
|
+
handler
|
|
522
|
+
)
|
|
523
|
+
)
|
|
524
|
+
);
|
|
525
|
+
} else {
|
|
526
|
+
statements.push(
|
|
527
|
+
t.expressionStatement(
|
|
528
|
+
t.callExpression(
|
|
529
|
+
t.memberExpression(t.identifier(elId), t.identifier("addEventListener")),
|
|
530
|
+
[t.stringLiteral(event), handler]
|
|
531
|
+
)
|
|
532
|
+
)
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
537
|
+
if (attrName.startsWith("on") && attrName.includes("|")) {
|
|
538
|
+
const { eventName, modifiers } = parseEventModifiers(attrName);
|
|
539
|
+
const handler = getAttributeValue(attr.value);
|
|
540
|
+
const wrappedHandler = createEventHandler(handler, modifiers);
|
|
541
|
+
const event = eventName.slice(2).toLowerCase();
|
|
542
|
+
const optionMods = modifiers.filter((m) => EVENT_OPTION_MODIFIERS.has(m));
|
|
543
|
+
const addEventArgs = [t.stringLiteral(event), wrappedHandler];
|
|
544
|
+
if (optionMods.length > 0) {
|
|
545
|
+
const optsProps = optionMods.map(
|
|
546
|
+
(m) => t.objectProperty(t.identifier(m), t.booleanLiteral(true))
|
|
547
|
+
);
|
|
548
|
+
addEventArgs.push(t.objectExpression(optsProps));
|
|
549
|
+
}
|
|
550
|
+
statements.push(
|
|
551
|
+
t.expressionStatement(
|
|
552
|
+
t.callExpression(
|
|
553
|
+
t.memberExpression(t.identifier(elId), t.identifier("addEventListener")),
|
|
554
|
+
addEventArgs
|
|
555
|
+
)
|
|
556
|
+
)
|
|
557
|
+
);
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
560
|
+
if (isBindingAttribute(attrName)) {
|
|
561
|
+
const bindProp = getBindingProperty(attrName);
|
|
562
|
+
const signalExpr = attr.value.expression;
|
|
563
|
+
state.needsEffect = true;
|
|
564
|
+
if (bindProp === "value") {
|
|
565
|
+
statements.push(
|
|
566
|
+
t.expressionStatement(
|
|
567
|
+
t.callExpression(t.identifier("_$effect"), [
|
|
568
|
+
t.arrowFunctionExpression([], t.assignmentExpression(
|
|
569
|
+
"=",
|
|
570
|
+
t.memberExpression(t.identifier(elId), t.identifier("value")),
|
|
571
|
+
t.callExpression(t.cloneNode(signalExpr), [])
|
|
572
|
+
))
|
|
573
|
+
])
|
|
574
|
+
)
|
|
575
|
+
);
|
|
576
|
+
statements.push(
|
|
577
|
+
t.expressionStatement(
|
|
578
|
+
t.callExpression(
|
|
579
|
+
t.memberExpression(t.identifier(elId), t.identifier("addEventListener")),
|
|
580
|
+
[
|
|
581
|
+
t.stringLiteral("input"),
|
|
582
|
+
t.arrowFunctionExpression(
|
|
583
|
+
[t.identifier("e")],
|
|
584
|
+
t.callExpression(
|
|
585
|
+
t.memberExpression(t.cloneNode(signalExpr), t.identifier("set")),
|
|
586
|
+
[t.memberExpression(
|
|
587
|
+
t.memberExpression(t.identifier("e"), t.identifier("target")),
|
|
588
|
+
t.identifier("value")
|
|
589
|
+
)]
|
|
590
|
+
)
|
|
591
|
+
)
|
|
592
|
+
]
|
|
593
|
+
)
|
|
594
|
+
)
|
|
595
|
+
);
|
|
596
|
+
} else if (bindProp === "checked") {
|
|
597
|
+
state.needsEffect = true;
|
|
598
|
+
statements.push(
|
|
599
|
+
t.expressionStatement(
|
|
600
|
+
t.callExpression(t.identifier("_$effect"), [
|
|
601
|
+
t.arrowFunctionExpression([], t.assignmentExpression(
|
|
602
|
+
"=",
|
|
603
|
+
t.memberExpression(t.identifier(elId), t.identifier("checked")),
|
|
604
|
+
t.callExpression(t.cloneNode(signalExpr), [])
|
|
605
|
+
))
|
|
606
|
+
])
|
|
607
|
+
)
|
|
608
|
+
);
|
|
609
|
+
statements.push(
|
|
610
|
+
t.expressionStatement(
|
|
611
|
+
t.callExpression(
|
|
612
|
+
t.memberExpression(t.identifier(elId), t.identifier("addEventListener")),
|
|
613
|
+
[
|
|
614
|
+
t.stringLiteral("change"),
|
|
615
|
+
t.arrowFunctionExpression(
|
|
616
|
+
[t.identifier("e")],
|
|
617
|
+
t.callExpression(
|
|
618
|
+
t.memberExpression(t.cloneNode(signalExpr), t.identifier("set")),
|
|
619
|
+
[t.memberExpression(
|
|
620
|
+
t.memberExpression(t.identifier("e"), t.identifier("target")),
|
|
621
|
+
t.identifier("checked")
|
|
622
|
+
)]
|
|
623
|
+
)
|
|
624
|
+
)
|
|
625
|
+
]
|
|
626
|
+
)
|
|
627
|
+
)
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
continue;
|
|
631
|
+
}
|
|
632
|
+
if (t.isJSXExpressionContainer(attr.value)) {
|
|
633
|
+
const expr = attr.value.expression;
|
|
634
|
+
const domName = normalizeAttrName(attrName);
|
|
635
|
+
if (isPotentiallyReactive(expr, state.signalNames, state.importedIdentifiers)) {
|
|
636
|
+
state.needsEffect = true;
|
|
637
|
+
const effectCall = t.callExpression(t.identifier("_$effect"), [
|
|
638
|
+
t.arrowFunctionExpression([], buildSetPropCall(domName, expr))
|
|
639
|
+
]);
|
|
640
|
+
if (isUncertainReactive(expr, state.signalNames, state.importedIdentifiers)) {
|
|
641
|
+
t.addComment(
|
|
642
|
+
effectCall,
|
|
643
|
+
"leading",
|
|
644
|
+
" @what-dev: effect wrapping may be unnecessary \u2014 expression contains a non-signal function call with reactive args ",
|
|
645
|
+
false
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
statements.push(t.expressionStatement(effectCall));
|
|
649
|
+
} else {
|
|
650
|
+
statements.push(t.expressionStatement(buildSetPropCall(domName, expr)));
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
function applyDynamicChildren(statements, elId, children, parentNode, state) {
|
|
656
|
+
let childIndex = 0;
|
|
657
|
+
for (const child of children) {
|
|
658
|
+
if (t.isJSXText(child)) {
|
|
659
|
+
const text = child.value.replace(/\n\s+/g, " ").trim();
|
|
660
|
+
if (text) childIndex++;
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
663
|
+
if (t.isJSXExpressionContainer(child)) {
|
|
664
|
+
if (t.isJSXEmptyExpression(child.expression)) continue;
|
|
665
|
+
const expr = child.expression;
|
|
666
|
+
const marker = buildChildAccess(elId, childIndex);
|
|
667
|
+
state.needsInsert = true;
|
|
668
|
+
if (isPotentiallyReactive(expr, state.signalNames, state.importedIdentifiers)) {
|
|
669
|
+
const insertCall = t.callExpression(t.identifier("_$insert"), [
|
|
670
|
+
t.identifier(elId),
|
|
671
|
+
t.arrowFunctionExpression([], expr),
|
|
672
|
+
marker
|
|
673
|
+
]);
|
|
674
|
+
if (isUncertainReactive(expr, state.signalNames, state.importedIdentifiers)) {
|
|
675
|
+
t.addComment(
|
|
676
|
+
insertCall,
|
|
677
|
+
"leading",
|
|
678
|
+
" @what-dev: reactive wrapping may be unnecessary \u2014 expression contains a non-signal function call with reactive args ",
|
|
679
|
+
false
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
statements.push(t.expressionStatement(insertCall));
|
|
683
|
+
} else {
|
|
684
|
+
statements.push(
|
|
685
|
+
t.expressionStatement(
|
|
686
|
+
t.callExpression(t.identifier("_$insert"), [
|
|
687
|
+
t.identifier(elId),
|
|
688
|
+
expr,
|
|
689
|
+
marker
|
|
690
|
+
])
|
|
691
|
+
)
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
childIndex++;
|
|
695
|
+
continue;
|
|
696
|
+
}
|
|
697
|
+
if (t.isJSXElement(child)) {
|
|
698
|
+
const childTag = child.openingElement.name.name;
|
|
699
|
+
if (isComponent(childTag) || childTag === "For" || childTag === "Show") {
|
|
700
|
+
const transformed = transformElementFineGrained({ node: child }, state);
|
|
701
|
+
const marker = buildChildAccess(elId, childIndex);
|
|
702
|
+
state.needsInsert = true;
|
|
703
|
+
statements.push(
|
|
704
|
+
t.expressionStatement(
|
|
705
|
+
t.callExpression(t.identifier("_$insert"), [
|
|
706
|
+
t.identifier(elId),
|
|
707
|
+
transformed,
|
|
708
|
+
marker
|
|
709
|
+
])
|
|
710
|
+
)
|
|
711
|
+
);
|
|
712
|
+
childIndex++;
|
|
713
|
+
} else {
|
|
714
|
+
const hasAnythingDynamic = child.openingElement.attributes.some(isDynamicAttr) || child.openingElement.attributes.some((a) => !t.isJSXSpreadAttribute(a) && getAttrName(a)?.startsWith("on")) || !child.children.every(isStaticChild);
|
|
715
|
+
if (hasAnythingDynamic) {
|
|
716
|
+
const childElId = state.nextVarId();
|
|
717
|
+
statements.push(
|
|
718
|
+
t.variableDeclaration("const", [
|
|
719
|
+
t.variableDeclarator(
|
|
720
|
+
t.identifier(childElId),
|
|
721
|
+
buildChildAccess(elId, childIndex)
|
|
722
|
+
)
|
|
723
|
+
])
|
|
724
|
+
);
|
|
725
|
+
applyDynamicAttrs(statements, childElId, child.openingElement.attributes, state);
|
|
726
|
+
applyDynamicChildren(statements, childElId, child.children, child, state);
|
|
727
|
+
}
|
|
728
|
+
childIndex++;
|
|
729
|
+
}
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
732
|
+
if (t.isJSXFragment(child)) {
|
|
733
|
+
for (const fChild of child.children) {
|
|
734
|
+
if (t.isJSXExpressionContainer(fChild) && !t.isJSXEmptyExpression(fChild.expression)) {
|
|
735
|
+
state.needsInsert = true;
|
|
736
|
+
const expr = fChild.expression;
|
|
737
|
+
if (isPotentiallyReactive(expr, state.signalNames, state.importedIdentifiers)) {
|
|
738
|
+
statements.push(
|
|
739
|
+
t.expressionStatement(
|
|
740
|
+
t.callExpression(t.identifier("_$insert"), [
|
|
741
|
+
t.identifier(elId),
|
|
742
|
+
t.arrowFunctionExpression([], expr)
|
|
743
|
+
])
|
|
744
|
+
)
|
|
745
|
+
);
|
|
746
|
+
} else {
|
|
747
|
+
statements.push(
|
|
748
|
+
t.expressionStatement(
|
|
749
|
+
t.callExpression(t.identifier("_$insert"), [
|
|
750
|
+
t.identifier(elId),
|
|
751
|
+
expr
|
|
752
|
+
])
|
|
753
|
+
)
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
function buildChildAccess(elId, index) {
|
|
762
|
+
if (index === 0) {
|
|
763
|
+
return t.memberExpression(t.identifier(elId), t.identifier("firstChild"));
|
|
764
|
+
}
|
|
765
|
+
let expr = t.memberExpression(t.identifier(elId), t.identifier("firstChild"));
|
|
766
|
+
for (let i = 0; i < index; i++) {
|
|
767
|
+
expr = t.memberExpression(expr, t.identifier("nextSibling"));
|
|
768
|
+
}
|
|
769
|
+
return expr;
|
|
770
|
+
}
|
|
771
|
+
function transformComponentFineGrained(path, state) {
|
|
772
|
+
const { node } = path;
|
|
773
|
+
const openingElement = node.openingElement;
|
|
774
|
+
const componentName = openingElement.name.name;
|
|
775
|
+
const attributes = openingElement.attributes;
|
|
776
|
+
const children = node.children;
|
|
777
|
+
let clientDirective = null;
|
|
778
|
+
const filteredAttrs = [];
|
|
779
|
+
for (const attr of attributes) {
|
|
780
|
+
if (t.isJSXAttribute(attr)) {
|
|
781
|
+
let name;
|
|
782
|
+
if (t.isJSXNamespacedName(attr.name)) {
|
|
783
|
+
name = `${attr.name.namespace.name}:${attr.name.name.name}`;
|
|
784
|
+
} else {
|
|
785
|
+
name = attr.name.name;
|
|
786
|
+
}
|
|
787
|
+
if (name && typeof name === "string" && name.startsWith("client:")) {
|
|
788
|
+
const mode = name.slice(7);
|
|
789
|
+
if (attr.value) {
|
|
790
|
+
clientDirective = { type: mode, value: attr.value.value };
|
|
791
|
+
} else {
|
|
792
|
+
clientDirective = { type: mode };
|
|
793
|
+
}
|
|
794
|
+
continue;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
filteredAttrs.push(attr);
|
|
798
|
+
}
|
|
799
|
+
if (clientDirective) {
|
|
800
|
+
state.needsCreateComponent = true;
|
|
801
|
+
state.needsIsland = true;
|
|
802
|
+
const islandProps = [
|
|
803
|
+
t.objectProperty(t.identifier("component"), t.identifier(componentName)),
|
|
804
|
+
t.objectProperty(t.identifier("mode"), t.stringLiteral(clientDirective.type))
|
|
805
|
+
];
|
|
806
|
+
if (clientDirective.value) {
|
|
807
|
+
islandProps.push(
|
|
808
|
+
t.objectProperty(t.identifier("mediaQuery"), t.stringLiteral(clientDirective.value))
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
for (const attr of filteredAttrs) {
|
|
812
|
+
if (t.isJSXSpreadAttribute(attr)) continue;
|
|
813
|
+
const attrName = getAttrName(attr);
|
|
814
|
+
const value = getAttributeValue(attr.value);
|
|
815
|
+
islandProps.push(t.objectProperty(t.identifier(attrName), value));
|
|
816
|
+
}
|
|
817
|
+
return t.callExpression(
|
|
818
|
+
t.identifier("_$createComponent"),
|
|
819
|
+
[t.identifier("Island"), t.objectExpression(islandProps), t.arrayExpression([])]
|
|
820
|
+
);
|
|
821
|
+
}
|
|
822
|
+
state.needsCreateComponent = true;
|
|
823
|
+
const props = [];
|
|
824
|
+
let hasSpread = false;
|
|
825
|
+
let spreadExpr = null;
|
|
826
|
+
for (const attr of filteredAttrs) {
|
|
827
|
+
if (t.isJSXSpreadAttribute(attr)) {
|
|
828
|
+
hasSpread = true;
|
|
829
|
+
spreadExpr = attr.argument;
|
|
830
|
+
continue;
|
|
831
|
+
}
|
|
832
|
+
const attrName = getAttrName(attr);
|
|
833
|
+
if (isBindingAttribute(attrName)) {
|
|
834
|
+
const bindProp = getBindingProperty(attrName);
|
|
835
|
+
const signalExpr = attr.value.expression;
|
|
836
|
+
if (bindProp === "value") {
|
|
837
|
+
props.push(
|
|
838
|
+
t.objectProperty(t.identifier("value"), t.callExpression(t.cloneNode(signalExpr), []))
|
|
839
|
+
);
|
|
840
|
+
props.push(
|
|
841
|
+
t.objectProperty(
|
|
842
|
+
t.identifier("onInput"),
|
|
843
|
+
t.arrowFunctionExpression(
|
|
844
|
+
[t.identifier("e")],
|
|
845
|
+
t.callExpression(
|
|
846
|
+
t.memberExpression(t.cloneNode(signalExpr), t.identifier("set")),
|
|
847
|
+
[t.memberExpression(
|
|
848
|
+
t.memberExpression(t.identifier("e"), t.identifier("target")),
|
|
849
|
+
t.identifier("value")
|
|
850
|
+
)]
|
|
851
|
+
)
|
|
852
|
+
)
|
|
853
|
+
)
|
|
854
|
+
);
|
|
855
|
+
} else if (bindProp === "checked") {
|
|
856
|
+
props.push(
|
|
857
|
+
t.objectProperty(t.identifier("checked"), t.callExpression(t.cloneNode(signalExpr), []))
|
|
858
|
+
);
|
|
859
|
+
props.push(
|
|
860
|
+
t.objectProperty(
|
|
861
|
+
t.identifier("onChange"),
|
|
862
|
+
t.arrowFunctionExpression(
|
|
863
|
+
[t.identifier("e")],
|
|
864
|
+
t.callExpression(
|
|
865
|
+
t.memberExpression(t.cloneNode(signalExpr), t.identifier("set")),
|
|
866
|
+
[t.memberExpression(
|
|
867
|
+
t.memberExpression(t.identifier("e"), t.identifier("target")),
|
|
868
|
+
t.identifier("checked")
|
|
869
|
+
)]
|
|
870
|
+
)
|
|
871
|
+
)
|
|
872
|
+
)
|
|
873
|
+
);
|
|
874
|
+
}
|
|
875
|
+
continue;
|
|
876
|
+
}
|
|
877
|
+
if (attrName.startsWith("on") && attrName.includes("|")) {
|
|
878
|
+
const { eventName, modifiers } = parseEventModifiers(attrName);
|
|
879
|
+
const handler = getAttributeValue(attr.value);
|
|
880
|
+
const wrappedHandler = createEventHandler(handler, modifiers);
|
|
881
|
+
props.push(t.objectProperty(t.identifier(eventName), wrappedHandler));
|
|
882
|
+
continue;
|
|
883
|
+
}
|
|
884
|
+
const value = getAttributeValue(attr.value);
|
|
885
|
+
props.push(
|
|
886
|
+
t.objectProperty(
|
|
887
|
+
/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(attrName) ? t.identifier(attrName) : t.stringLiteral(attrName),
|
|
888
|
+
value
|
|
889
|
+
)
|
|
890
|
+
);
|
|
891
|
+
}
|
|
892
|
+
const transformedChildren = [];
|
|
893
|
+
for (const child of children) {
|
|
894
|
+
if (t.isJSXText(child)) {
|
|
895
|
+
const text = child.value.replace(/\n\s+/g, " ").trim();
|
|
896
|
+
if (text) transformedChildren.push(t.stringLiteral(text));
|
|
897
|
+
} else if (t.isJSXExpressionContainer(child)) {
|
|
898
|
+
if (!t.isJSXEmptyExpression(child.expression)) {
|
|
899
|
+
transformedChildren.push(child.expression);
|
|
900
|
+
}
|
|
901
|
+
} else if (t.isJSXElement(child)) {
|
|
902
|
+
transformedChildren.push(transformElementFineGrained({ node: child }, state));
|
|
903
|
+
} else if (t.isJSXFragment(child)) {
|
|
904
|
+
transformedChildren.push(transformFragmentFineGrained({ node: child }, state));
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
let propsExpr;
|
|
908
|
+
if (hasSpread) {
|
|
909
|
+
if (props.length > 0) {
|
|
910
|
+
propsExpr = t.callExpression(
|
|
911
|
+
t.memberExpression(t.identifier("Object"), t.identifier("assign")),
|
|
912
|
+
[t.objectExpression([]), spreadExpr, t.objectExpression(props)]
|
|
913
|
+
);
|
|
914
|
+
} else {
|
|
915
|
+
propsExpr = spreadExpr;
|
|
916
|
+
}
|
|
917
|
+
} else if (props.length > 0) {
|
|
918
|
+
propsExpr = t.objectExpression(props);
|
|
919
|
+
} else {
|
|
920
|
+
propsExpr = t.nullLiteral();
|
|
921
|
+
}
|
|
922
|
+
const childrenArray = transformedChildren.length > 0 ? t.arrayExpression(transformedChildren) : t.arrayExpression([]);
|
|
923
|
+
return t.callExpression(t.identifier("_$createComponent"), [t.identifier(componentName), propsExpr, childrenArray]);
|
|
924
|
+
}
|
|
925
|
+
function transformForFineGrained(path, state) {
|
|
926
|
+
const { node } = path;
|
|
927
|
+
const attributes = node.openingElement.attributes;
|
|
928
|
+
const children = node.children;
|
|
929
|
+
let eachExpr = null;
|
|
930
|
+
for (const attr of attributes) {
|
|
931
|
+
if (t.isJSXAttribute(attr) && getAttrName(attr) === "each") {
|
|
932
|
+
eachExpr = getAttributeValue(attr.value);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
if (!eachExpr) {
|
|
936
|
+
console.warn('[what-compiler] <For> element missing "each" attribute.');
|
|
937
|
+
state.needsH = true;
|
|
938
|
+
return transformElementAsH(path, state);
|
|
939
|
+
}
|
|
940
|
+
let renderFn = null;
|
|
941
|
+
for (const child of children) {
|
|
942
|
+
if (t.isJSXExpressionContainer(child) && !t.isJSXEmptyExpression(child.expression)) {
|
|
943
|
+
renderFn = child.expression;
|
|
944
|
+
break;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
if (!renderFn) {
|
|
948
|
+
console.warn("[what-compiler] <For> element missing render function child.");
|
|
949
|
+
state.needsH = true;
|
|
950
|
+
return transformElementAsH(path, state);
|
|
951
|
+
}
|
|
952
|
+
state.needsMapArray = true;
|
|
953
|
+
return t.callExpression(t.identifier("_$mapArray"), [eachExpr, renderFn]);
|
|
954
|
+
}
|
|
955
|
+
function transformShowFineGrained(path, state) {
|
|
956
|
+
state.needsCreateComponent = true;
|
|
957
|
+
return transformComponentFineGrained(path, state);
|
|
958
|
+
}
|
|
959
|
+
function transformFragmentFineGrained(path, state) {
|
|
960
|
+
const { node } = path;
|
|
961
|
+
const children = node.children;
|
|
962
|
+
const transformed = [];
|
|
963
|
+
for (const child of children) {
|
|
964
|
+
if (t.isJSXText(child)) {
|
|
965
|
+
const text = child.value.replace(/\n\s+/g, " ").trim();
|
|
966
|
+
if (text) transformed.push(t.stringLiteral(text));
|
|
967
|
+
} else if (t.isJSXExpressionContainer(child)) {
|
|
968
|
+
if (!t.isJSXEmptyExpression(child.expression)) {
|
|
969
|
+
transformed.push(child.expression);
|
|
970
|
+
}
|
|
971
|
+
} else if (t.isJSXElement(child)) {
|
|
972
|
+
transformed.push(transformElementFineGrained({ node: child }, state));
|
|
973
|
+
} else if (t.isJSXFragment(child)) {
|
|
974
|
+
transformed.push(transformFragmentFineGrained({ node: child }, state));
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
if (transformed.length === 1) return transformed[0];
|
|
978
|
+
return t.arrayExpression(transformed);
|
|
979
|
+
}
|
|
980
|
+
function getOrCreateTemplate(state, html) {
|
|
981
|
+
if (state.templateMap.has(html)) {
|
|
982
|
+
return state.templateMap.get(html);
|
|
983
|
+
}
|
|
984
|
+
const id = `_tmpl$${state.templateCount++}`;
|
|
985
|
+
state.templateMap.set(html, id);
|
|
986
|
+
state.templates.push({ id, html });
|
|
987
|
+
return id;
|
|
988
|
+
}
|
|
989
|
+
return {
|
|
990
|
+
name: "what-jsx-transform",
|
|
991
|
+
visitor: {
|
|
992
|
+
Program: {
|
|
993
|
+
enter(path, state) {
|
|
994
|
+
state.needsTemplate = false;
|
|
995
|
+
state.needsInsert = false;
|
|
996
|
+
state.needsEffect = false;
|
|
997
|
+
state.needsMapArray = false;
|
|
998
|
+
state.needsSpread = false;
|
|
999
|
+
state.needsSetProp = false;
|
|
1000
|
+
state.needsH = false;
|
|
1001
|
+
state.needsCreateComponent = false;
|
|
1002
|
+
state.needsFragment = false;
|
|
1003
|
+
state.needsIsland = false;
|
|
1004
|
+
state.needsDelegation = false;
|
|
1005
|
+
state.delegatedEvents = /* @__PURE__ */ new Set();
|
|
1006
|
+
state.templates = [];
|
|
1007
|
+
state.templateMap = /* @__PURE__ */ new Map();
|
|
1008
|
+
state.templateCount = 0;
|
|
1009
|
+
state._varCounter = 0;
|
|
1010
|
+
state._pendingSetup = [];
|
|
1011
|
+
state.nextVarId = () => `_el$${state._varCounter++}`;
|
|
1012
|
+
state.signalNames = /* @__PURE__ */ new Set();
|
|
1013
|
+
state.importedIdentifiers = /* @__PURE__ */ new Set();
|
|
1014
|
+
for (const node of path.node.body) {
|
|
1015
|
+
if (t.isImportDeclaration(node)) {
|
|
1016
|
+
const source = node.source.value;
|
|
1017
|
+
const isReactiveSource = source === "what-framework" || source.startsWith("what-framework/") || source === "what-core" || source.startsWith("what-core/") || source.startsWith("./") || source.startsWith("../");
|
|
1018
|
+
for (const spec of node.specifiers) {
|
|
1019
|
+
let localName = null;
|
|
1020
|
+
if (t.isImportSpecifier(spec) && t.isIdentifier(spec.local)) {
|
|
1021
|
+
localName = spec.local.name;
|
|
1022
|
+
} else if (t.isImportDefaultSpecifier(spec) && t.isIdentifier(spec.local)) {
|
|
1023
|
+
localName = spec.local.name;
|
|
1024
|
+
} else if (t.isImportNamespaceSpecifier(spec) && t.isIdentifier(spec.local)) {
|
|
1025
|
+
localName = spec.local.name;
|
|
1026
|
+
}
|
|
1027
|
+
if (localName) {
|
|
1028
|
+
if (isReactiveSource || /^(use|create)[A-Z]/.test(localName)) {
|
|
1029
|
+
state.importedIdentifiers.add(localName);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
path.traverse({
|
|
1036
|
+
VariableDeclarator(declPath) {
|
|
1037
|
+
const init = declPath.node.init;
|
|
1038
|
+
if (!init || !t.isCallExpression(init)) return;
|
|
1039
|
+
const callee = init.callee;
|
|
1040
|
+
let calleeName = "";
|
|
1041
|
+
if (t.isIdentifier(callee)) {
|
|
1042
|
+
calleeName = callee.name;
|
|
1043
|
+
} else if (t.isMemberExpression(callee) && t.isIdentifier(callee.property)) {
|
|
1044
|
+
calleeName = callee.property.name;
|
|
1045
|
+
}
|
|
1046
|
+
if (SIGNAL_CREATORS.has(calleeName)) {
|
|
1047
|
+
const id = declPath.node.id;
|
|
1048
|
+
if (t.isIdentifier(id)) {
|
|
1049
|
+
state.signalNames.add(id.name);
|
|
1050
|
+
} else if (t.isArrayPattern(id)) {
|
|
1051
|
+
for (const el of id.elements) {
|
|
1052
|
+
if (t.isIdentifier(el)) state.signalNames.add(el.name);
|
|
1053
|
+
}
|
|
1054
|
+
} else if (t.isObjectPattern(id)) {
|
|
1055
|
+
for (const prop of id.properties) {
|
|
1056
|
+
if (t.isObjectProperty(prop) && t.isIdentifier(prop.value)) {
|
|
1057
|
+
state.signalNames.add(prop.value.name);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
});
|
|
1064
|
+
},
|
|
1065
|
+
exit(path, state) {
|
|
1066
|
+
for (const tmpl of state.templates.reverse()) {
|
|
1067
|
+
path.unshiftContainer(
|
|
1068
|
+
"body",
|
|
1069
|
+
t.variableDeclaration("const", [
|
|
1070
|
+
t.variableDeclarator(
|
|
1071
|
+
t.identifier(tmpl.id),
|
|
1072
|
+
t.callExpression(t.identifier("_$template"), [t.stringLiteral(tmpl.html)])
|
|
1073
|
+
)
|
|
1074
|
+
])
|
|
1075
|
+
);
|
|
1076
|
+
}
|
|
1077
|
+
const fgSpecifiers = [];
|
|
1078
|
+
if (state.needsTemplate) {
|
|
1079
|
+
fgSpecifiers.push(
|
|
1080
|
+
t.importSpecifier(t.identifier("_$template"), t.identifier("template"))
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
if (state.needsInsert) {
|
|
1084
|
+
fgSpecifiers.push(
|
|
1085
|
+
t.importSpecifier(t.identifier("_$insert"), t.identifier("insert"))
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1088
|
+
if (state.needsEffect) {
|
|
1089
|
+
fgSpecifiers.push(
|
|
1090
|
+
t.importSpecifier(t.identifier("_$effect"), t.identifier("effect"))
|
|
1091
|
+
);
|
|
1092
|
+
}
|
|
1093
|
+
if (state.needsMapArray) {
|
|
1094
|
+
fgSpecifiers.push(
|
|
1095
|
+
t.importSpecifier(t.identifier("_$mapArray"), t.identifier("mapArray"))
|
|
1096
|
+
);
|
|
1097
|
+
}
|
|
1098
|
+
if (state.needsSpread) {
|
|
1099
|
+
fgSpecifiers.push(
|
|
1100
|
+
t.importSpecifier(t.identifier("_$spread"), t.identifier("spread"))
|
|
1101
|
+
);
|
|
1102
|
+
}
|
|
1103
|
+
if (state.needsSetProp) {
|
|
1104
|
+
fgSpecifiers.push(
|
|
1105
|
+
t.importSpecifier(t.identifier("_$setProp"), t.identifier("setProp"))
|
|
1106
|
+
);
|
|
1107
|
+
}
|
|
1108
|
+
if (state.needsCreateComponent) {
|
|
1109
|
+
fgSpecifiers.push(
|
|
1110
|
+
t.importSpecifier(t.identifier("_$createComponent"), t.identifier("_$createComponent"))
|
|
1111
|
+
);
|
|
1112
|
+
}
|
|
1113
|
+
if (state.needsDelegation) {
|
|
1114
|
+
fgSpecifiers.push(
|
|
1115
|
+
t.importSpecifier(t.identifier("_$delegateEvents"), t.identifier("delegateEvents"))
|
|
1116
|
+
);
|
|
1117
|
+
}
|
|
1118
|
+
const coreSpecifiers = [];
|
|
1119
|
+
if (state.needsH) {
|
|
1120
|
+
coreSpecifiers.push(
|
|
1121
|
+
t.importSpecifier(t.identifier("h"), t.identifier("h"))
|
|
1122
|
+
);
|
|
1123
|
+
}
|
|
1124
|
+
if (state.needsFragment) {
|
|
1125
|
+
coreSpecifiers.push(
|
|
1126
|
+
t.importSpecifier(t.identifier("Fragment"), t.identifier("Fragment"))
|
|
1127
|
+
);
|
|
1128
|
+
}
|
|
1129
|
+
if (state.needsIsland) {
|
|
1130
|
+
coreSpecifiers.push(
|
|
1131
|
+
t.importSpecifier(t.identifier("Island"), t.identifier("Island"))
|
|
1132
|
+
);
|
|
1133
|
+
}
|
|
1134
|
+
if (fgSpecifiers.length > 0) {
|
|
1135
|
+
let existingRenderImport = null;
|
|
1136
|
+
for (const node of path.node.body) {
|
|
1137
|
+
if (t.isImportDeclaration(node) && (node.source.value === "what-framework/render" || node.source.value === "what-core/render")) {
|
|
1138
|
+
existingRenderImport = node;
|
|
1139
|
+
break;
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
if (existingRenderImport) {
|
|
1143
|
+
const existingNames = new Set(
|
|
1144
|
+
existingRenderImport.specifiers.filter((s) => t.isImportSpecifier(s)).map((s) => s.imported.name)
|
|
1145
|
+
);
|
|
1146
|
+
for (const spec of fgSpecifiers) {
|
|
1147
|
+
if (!existingNames.has(spec.imported.name)) {
|
|
1148
|
+
existingRenderImport.specifiers.push(spec);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
} else {
|
|
1152
|
+
path.unshiftContainer(
|
|
1153
|
+
"body",
|
|
1154
|
+
t.importDeclaration(fgSpecifiers, t.stringLiteral("what-framework/render"))
|
|
1155
|
+
);
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
if (coreSpecifiers.length > 0) {
|
|
1159
|
+
addCoreImports(path, t, coreSpecifiers);
|
|
1160
|
+
}
|
|
1161
|
+
if (state.needsDelegation && state.delegatedEvents && state.delegatedEvents.size > 0) {
|
|
1162
|
+
const eventArray = t.arrayExpression(
|
|
1163
|
+
[...state.delegatedEvents].map((e) => t.stringLiteral(e))
|
|
1164
|
+
);
|
|
1165
|
+
path.pushContainer(
|
|
1166
|
+
"body",
|
|
1167
|
+
t.expressionStatement(
|
|
1168
|
+
t.callExpression(t.identifier("_$delegateEvents"), [eventArray])
|
|
1169
|
+
)
|
|
1170
|
+
);
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
},
|
|
1174
|
+
JSXElement(path, state) {
|
|
1175
|
+
state.signalNames = collectSignalNamesFromScope(path);
|
|
1176
|
+
state._pendingSetup = [];
|
|
1177
|
+
const transformed = transformElementFineGrained(path, state);
|
|
1178
|
+
const pending = state._pendingSetup;
|
|
1179
|
+
state._pendingSetup = [];
|
|
1180
|
+
if (pending.length > 0) {
|
|
1181
|
+
let stmtPath = path;
|
|
1182
|
+
while (stmtPath && !stmtPath.isStatement()) {
|
|
1183
|
+
stmtPath = stmtPath.parentPath;
|
|
1184
|
+
}
|
|
1185
|
+
if (stmtPath && stmtPath.isStatement()) {
|
|
1186
|
+
for (const stmt of pending) {
|
|
1187
|
+
stmtPath.insertBefore(stmt);
|
|
1188
|
+
}
|
|
1189
|
+
path.replaceWith(transformed);
|
|
1190
|
+
} else {
|
|
1191
|
+
pending.push(t.returnStatement(transformed));
|
|
1192
|
+
path.replaceWith(
|
|
1193
|
+
t.callExpression(
|
|
1194
|
+
t.arrowFunctionExpression([], t.blockStatement(pending)),
|
|
1195
|
+
[]
|
|
1196
|
+
)
|
|
1197
|
+
);
|
|
1198
|
+
}
|
|
1199
|
+
} else {
|
|
1200
|
+
path.replaceWith(transformed);
|
|
1201
|
+
}
|
|
1202
|
+
},
|
|
1203
|
+
JSXFragment(path, state) {
|
|
1204
|
+
const transformed = transformFragmentFineGrained(path, state);
|
|
1205
|
+
path.replaceWith(transformed);
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
function addCoreImports(path, t, coreSpecifiers) {
|
|
1211
|
+
let existingImport = null;
|
|
1212
|
+
for (const node of path.node.body) {
|
|
1213
|
+
if (t.isImportDeclaration(node) && (node.source.value === "what-core" || node.source.value === "what-framework")) {
|
|
1214
|
+
existingImport = node;
|
|
1215
|
+
break;
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
if (existingImport) {
|
|
1219
|
+
const existingNames = new Set(
|
|
1220
|
+
existingImport.specifiers.filter((s) => t.isImportSpecifier(s)).map((s) => s.imported.name)
|
|
1221
|
+
);
|
|
1222
|
+
for (const spec of coreSpecifiers) {
|
|
1223
|
+
if (!existingNames.has(spec.imported.name)) {
|
|
1224
|
+
existingImport.specifiers.push(spec);
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
} else {
|
|
1228
|
+
const importDecl = t.importDeclaration(
|
|
1229
|
+
coreSpecifiers,
|
|
1230
|
+
t.stringLiteral("what-framework")
|
|
1231
|
+
);
|
|
1232
|
+
path.unshiftContainer("body", importDecl);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
export {
|
|
1236
|
+
whatBabelPlugin as default
|
|
1237
|
+
};
|
|
1238
|
+
//# sourceMappingURL=babel-plugin.js.map
|