what-compiler 0.5.5 → 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/dist/index.js ADDED
@@ -0,0 +1,1996 @@
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(path3) {
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 = path3.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(path3) {
222
+ return collectSignalNamesFromScope(path3);
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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
390
+ }
391
+ function escapeAttr(str) {
392
+ return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
393
+ }
394
+ function transformElementFineGrained(path3, state) {
395
+ const { node } = path3;
396
+ const openingElement = node.openingElement;
397
+ const tagName = openingElement.name.name;
398
+ if (isComponent(tagName)) {
399
+ return transformComponentFineGrained(path3, state);
400
+ }
401
+ if (tagName === "For") {
402
+ return transformForFineGrained(path3, state);
403
+ }
404
+ if (tagName === "Show") {
405
+ return transformShowFineGrained(path3, 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(path3, 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(path3, state) {
450
+ const { node } = path3;
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(path3, state) {
772
+ const { node } = path3;
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(path3, state) {
926
+ const { node } = path3;
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(path3, 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(path3, state);
951
+ }
952
+ state.needsMapArray = true;
953
+ return t.callExpression(t.identifier("_$mapArray"), [eachExpr, renderFn]);
954
+ }
955
+ function transformShowFineGrained(path3, state) {
956
+ state.needsCreateComponent = true;
957
+ return transformComponentFineGrained(path3, state);
958
+ }
959
+ function transformFragmentFineGrained(path3, state) {
960
+ const { node } = path3;
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(path3, 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 path3.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
+ path3.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(path3, state) {
1066
+ for (const tmpl of state.templates.reverse()) {
1067
+ path3.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 path3.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
+ path3.unshiftContainer(
1153
+ "body",
1154
+ t.importDeclaration(fgSpecifiers, t.stringLiteral("what-framework/render"))
1155
+ );
1156
+ }
1157
+ }
1158
+ if (coreSpecifiers.length > 0) {
1159
+ addCoreImports(path3, 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
+ path3.pushContainer(
1166
+ "body",
1167
+ t.expressionStatement(
1168
+ t.callExpression(t.identifier("_$delegateEvents"), [eventArray])
1169
+ )
1170
+ );
1171
+ }
1172
+ }
1173
+ },
1174
+ JSXElement(path3, state) {
1175
+ state.signalNames = collectSignalNamesFromScope(path3);
1176
+ state._pendingSetup = [];
1177
+ const transformed = transformElementFineGrained(path3, state);
1178
+ const pending = state._pendingSetup;
1179
+ state._pendingSetup = [];
1180
+ if (pending.length > 0) {
1181
+ let stmtPath = path3;
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
+ path3.replaceWith(transformed);
1190
+ } else {
1191
+ pending.push(t.returnStatement(transformed));
1192
+ path3.replaceWith(
1193
+ t.callExpression(
1194
+ t.arrowFunctionExpression([], t.blockStatement(pending)),
1195
+ []
1196
+ )
1197
+ );
1198
+ }
1199
+ } else {
1200
+ path3.replaceWith(transformed);
1201
+ }
1202
+ },
1203
+ JSXFragment(path3, state) {
1204
+ const transformed = transformFragmentFineGrained(path3, state);
1205
+ path3.replaceWith(transformed);
1206
+ }
1207
+ }
1208
+ };
1209
+ }
1210
+ function addCoreImports(path3, t, coreSpecifiers) {
1211
+ let existingImport = null;
1212
+ for (const node of path3.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
+ path3.unshiftContainer("body", importDecl);
1233
+ }
1234
+ }
1235
+
1236
+ // packages/compiler/src/vite-plugin.js
1237
+ import path2 from "path";
1238
+ import { transformSync } from "@babel/core";
1239
+
1240
+ // packages/compiler/src/file-router.js
1241
+ import fs from "fs";
1242
+ import path from "path";
1243
+ var PAGE_EXTENSIONS = /* @__PURE__ */ new Set([".jsx", ".tsx", ".js", ".ts"]);
1244
+ var IGNORED_FILES = /* @__PURE__ */ new Set(["_layout", "_error", "_loading", "_404"]);
1245
+ function scanPages(pagesDir) {
1246
+ const pages = [];
1247
+ const layouts = [];
1248
+ const apiRoutes = [];
1249
+ function walk(dir, urlPrefix = "") {
1250
+ if (!fs.existsSync(dir)) return;
1251
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
1252
+ for (const entry of entries) {
1253
+ const fullPath = path.join(dir, entry.name);
1254
+ if (entry.isDirectory()) {
1255
+ const groupMatch = entry.name.match(/^\((.+)\)$/);
1256
+ if (groupMatch) {
1257
+ walk(fullPath, urlPrefix);
1258
+ continue;
1259
+ }
1260
+ if (entry.name === "api" && urlPrefix === "") {
1261
+ walkApi(fullPath, "/api");
1262
+ continue;
1263
+ }
1264
+ walk(fullPath, urlPrefix + "/" + fileNameToSegment(entry.name));
1265
+ continue;
1266
+ }
1267
+ const ext = path.extname(entry.name);
1268
+ if (!PAGE_EXTENSIONS.has(ext)) continue;
1269
+ const baseName = path.basename(entry.name, ext);
1270
+ if (baseName === "_layout") {
1271
+ layouts.push({
1272
+ filePath: fullPath,
1273
+ urlPrefix: urlPrefix || "/"
1274
+ });
1275
+ continue;
1276
+ }
1277
+ if (IGNORED_FILES.has(baseName)) continue;
1278
+ const urlSegment = fileNameToSegment(baseName);
1279
+ const routePath = baseName === "index" ? urlPrefix || "/" : urlPrefix + "/" + urlSegment;
1280
+ pages.push({
1281
+ filePath: fullPath,
1282
+ routePath: normalizePath(routePath),
1283
+ isDynamic: routePath.includes(":") || routePath.includes("*")
1284
+ });
1285
+ }
1286
+ }
1287
+ function walkApi(dir, urlPrefix) {
1288
+ if (!fs.existsSync(dir)) return;
1289
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
1290
+ for (const entry of entries) {
1291
+ const fullPath = path.join(dir, entry.name);
1292
+ if (entry.isDirectory()) {
1293
+ walkApi(fullPath, urlPrefix + "/" + fileNameToSegment(entry.name));
1294
+ continue;
1295
+ }
1296
+ const ext = path.extname(entry.name);
1297
+ if (!PAGE_EXTENSIONS.has(ext)) continue;
1298
+ const baseName = path.basename(entry.name, ext);
1299
+ const segment = fileNameToSegment(baseName);
1300
+ const routePath = baseName === "index" ? urlPrefix : urlPrefix + "/" + segment;
1301
+ apiRoutes.push({
1302
+ filePath: fullPath,
1303
+ routePath: normalizePath(routePath)
1304
+ });
1305
+ }
1306
+ }
1307
+ walk(pagesDir);
1308
+ pages.sort((a, b) => {
1309
+ const aWeight = routeWeight(a.routePath);
1310
+ const bWeight = routeWeight(b.routePath);
1311
+ return aWeight - bWeight;
1312
+ });
1313
+ return { pages, layouts, apiRoutes };
1314
+ }
1315
+ function fileNameToSegment(name) {
1316
+ const catchAll = name.match(/^\[\.\.\.(\w+)\]$/);
1317
+ if (catchAll) return "*" + catchAll[1];
1318
+ const dynamic = name.match(/^\[(\w+)\]$/);
1319
+ if (dynamic) return ":" + dynamic[1];
1320
+ return name.toLowerCase();
1321
+ }
1322
+ function normalizePath(p) {
1323
+ let result = p.replace(/\/+/g, "/");
1324
+ if (result.length > 1 && result.endsWith("/")) {
1325
+ result = result.slice(0, -1);
1326
+ }
1327
+ return result || "/";
1328
+ }
1329
+ function routeWeight(path3) {
1330
+ if (path3.includes("*")) return 100;
1331
+ if (path3.includes(":")) return 10;
1332
+ return 0;
1333
+ }
1334
+ function extractPageConfig(source) {
1335
+ const match = source.match(
1336
+ /export\s+const\s+page\s*=\s*(\{[^}]*\})/s
1337
+ );
1338
+ if (!match) {
1339
+ return { mode: "client" };
1340
+ }
1341
+ try {
1342
+ const obj = match[1].replace(/'/g, '"').replace(/(\w+)\s*:/g, '"$1":').replace(/,\s*}/g, "}").replace(/\/\/[^\n]*/g, "");
1343
+ return { mode: "client", ...JSON.parse(obj) };
1344
+ } catch {
1345
+ return { mode: "client" };
1346
+ }
1347
+ }
1348
+ function generateRoutesModule(pagesDir, rootDir) {
1349
+ const { pages, layouts, apiRoutes } = scanPages(pagesDir);
1350
+ const imports = [];
1351
+ const routeEntries = [];
1352
+ const layoutMap = /* @__PURE__ */ new Map();
1353
+ layouts.forEach((layout, i) => {
1354
+ const varName = `_layout${i}`;
1355
+ const relPath = toImportPath(layout.filePath, rootDir);
1356
+ imports.push(`import ${varName} from '${relPath}';`);
1357
+ layoutMap.set(layout.urlPrefix, varName);
1358
+ });
1359
+ pages.forEach((page, i) => {
1360
+ const varName = `_page${i}`;
1361
+ const relPath = toImportPath(page.filePath, rootDir);
1362
+ imports.push(`import ${varName} from '${relPath}';`);
1363
+ let pageConfig = { mode: "client" };
1364
+ try {
1365
+ const source = fs.readFileSync(page.filePath, "utf-8");
1366
+ pageConfig = extractPageConfig(source);
1367
+ } catch {
1368
+ }
1369
+ const layoutVar = findLayout(page.routePath, layoutMap);
1370
+ const entry = {
1371
+ path: page.routePath,
1372
+ component: varName,
1373
+ mode: pageConfig.mode || "client",
1374
+ layout: layoutVar || null
1375
+ };
1376
+ routeEntries.push(entry);
1377
+ });
1378
+ const apiEntries = [];
1379
+ apiRoutes.forEach((route, i) => {
1380
+ const varName = `_api${i}`;
1381
+ const relPath = toImportPath(route.filePath, rootDir);
1382
+ imports.push(`import * as ${varName} from '${relPath}';`);
1383
+ apiEntries.push({
1384
+ path: route.routePath,
1385
+ handlers: varName
1386
+ });
1387
+ });
1388
+ const lines = [
1389
+ "// Auto-generated by What Framework file router",
1390
+ "// Do not edit \u2014 changes will be overwritten",
1391
+ "",
1392
+ ...imports,
1393
+ "",
1394
+ "export const routes = [",
1395
+ ...routeEntries.map(
1396
+ (r) => ` { path: '${r.path}', component: ${r.component}, mode: '${r.mode}'${r.layout ? `, layout: ${r.layout}` : ""} },`
1397
+ ),
1398
+ "];",
1399
+ "",
1400
+ `export const apiRoutes = [`,
1401
+ ...apiEntries.map(
1402
+ (r) => ` { path: '${r.path}', handlers: ${r.handlers} },`
1403
+ ),
1404
+ "];",
1405
+ "",
1406
+ // Export page modes for the build system
1407
+ "export const pageModes = {",
1408
+ ...routeEntries.map(
1409
+ (r) => ` '${r.path}': '${r.mode}',`
1410
+ ),
1411
+ "};"
1412
+ ];
1413
+ return lines.join("\n");
1414
+ }
1415
+ function toImportPath(filePath, rootDir) {
1416
+ const rel = path.relative(rootDir, filePath);
1417
+ return "/" + rel.split(path.sep).join("/");
1418
+ }
1419
+ function findLayout(routePath, layoutMap) {
1420
+ const segments = routePath.split("/").filter(Boolean);
1421
+ while (segments.length > 0) {
1422
+ const prefix = "/" + segments.join("/");
1423
+ if (layoutMap.has(prefix)) return layoutMap.get(prefix);
1424
+ segments.pop();
1425
+ }
1426
+ if (layoutMap.has("/")) return layoutMap.get("/");
1427
+ return null;
1428
+ }
1429
+
1430
+ // packages/compiler/src/error-overlay.js
1431
+ var OVERLAY_STYLES = `
1432
+ :host {
1433
+ position: fixed;
1434
+ inset: 0;
1435
+ z-index: 99999;
1436
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
1437
+ }
1438
+
1439
+ .backdrop {
1440
+ position: fixed;
1441
+ inset: 0;
1442
+ background: rgba(0, 0, 0, 0.66);
1443
+ }
1444
+
1445
+ .panel {
1446
+ position: fixed;
1447
+ inset: 2rem;
1448
+ overflow: auto;
1449
+ background: #1a1a2e;
1450
+ border: 1px solid #2a2a4a;
1451
+ border-radius: 12px;
1452
+ box-shadow: 0 25px 80px rgba(0, 0, 0, 0.5);
1453
+ color: #e0e0e0;
1454
+ }
1455
+
1456
+ .header {
1457
+ display: flex;
1458
+ align-items: center;
1459
+ justify-content: space-between;
1460
+ padding: 1rem 1.5rem;
1461
+ border-bottom: 1px solid #2a2a4a;
1462
+ background: #16163a;
1463
+ border-radius: 12px 12px 0 0;
1464
+ }
1465
+
1466
+ .header-left {
1467
+ display: flex;
1468
+ align-items: center;
1469
+ gap: 0.75rem;
1470
+ }
1471
+
1472
+ .header-right {
1473
+ display: flex;
1474
+ align-items: center;
1475
+ gap: 0.5rem;
1476
+ }
1477
+
1478
+ .logo {
1479
+ width: 28px;
1480
+ height: 28px;
1481
+ background: linear-gradient(135deg, #2563eb, #1d4ed8);
1482
+ border-radius: 6px;
1483
+ display: grid;
1484
+ place-items: center;
1485
+ font-weight: 800;
1486
+ font-size: 14px;
1487
+ color: #fff;
1488
+ }
1489
+
1490
+ .brand {
1491
+ font-size: 14px;
1492
+ font-weight: 600;
1493
+ color: #a0a0c0;
1494
+ }
1495
+
1496
+ .tag {
1497
+ font-size: 11px;
1498
+ padding: 2px 8px;
1499
+ border-radius: 4px;
1500
+ font-weight: 600;
1501
+ }
1502
+
1503
+ .tag-error {
1504
+ background: #3b1219;
1505
+ color: #f87171;
1506
+ }
1507
+
1508
+ .tag-warning {
1509
+ background: #3b2f19;
1510
+ color: #fbbf24;
1511
+ }
1512
+
1513
+ .close-btn, .copy-btn {
1514
+ background: none;
1515
+ border: 1px solid #3a3a5a;
1516
+ color: #a0a0c0;
1517
+ border-radius: 6px;
1518
+ padding: 4px 12px;
1519
+ cursor: pointer;
1520
+ font-family: inherit;
1521
+ font-size: 12px;
1522
+ }
1523
+
1524
+ .close-btn:hover, .copy-btn:hover {
1525
+ background: #2a2a4a;
1526
+ color: #fff;
1527
+ }
1528
+
1529
+ .copy-btn.copied {
1530
+ border-color: #22c55e;
1531
+ color: #22c55e;
1532
+ }
1533
+
1534
+ .body {
1535
+ padding: 1.5rem;
1536
+ }
1537
+
1538
+ .error-title {
1539
+ font-size: 16px;
1540
+ font-weight: 700;
1541
+ color: #f87171;
1542
+ margin: 0 0 0.5rem;
1543
+ }
1544
+
1545
+ .error-message {
1546
+ font-size: 14px;
1547
+ color: #e0e0e0;
1548
+ margin: 0 0 1rem;
1549
+ line-height: 1.6;
1550
+ white-space: pre-wrap;
1551
+ }
1552
+
1553
+ .file-path {
1554
+ display: inline-flex;
1555
+ align-items: center;
1556
+ gap: 0.5rem;
1557
+ font-size: 12px;
1558
+ color: #818cf8;
1559
+ margin-bottom: 1rem;
1560
+ padding: 0.25rem 0;
1561
+ }
1562
+
1563
+ .code-frame {
1564
+ background: #0d0d1a;
1565
+ border: 1px solid #2a2a4a;
1566
+ border-radius: 8px;
1567
+ overflow-x: auto;
1568
+ margin-bottom: 1rem;
1569
+ }
1570
+
1571
+ .code-line {
1572
+ display: flex;
1573
+ padding: 0 1rem;
1574
+ font-size: 13px;
1575
+ line-height: 1.7;
1576
+ }
1577
+
1578
+ .code-line.highlight {
1579
+ background: rgba(248, 113, 113, 0.1);
1580
+ }
1581
+
1582
+ .line-number {
1583
+ color: #4a4a6a;
1584
+ min-width: 3ch;
1585
+ text-align: right;
1586
+ margin-right: 1rem;
1587
+ user-select: none;
1588
+ }
1589
+
1590
+ .line-content {
1591
+ white-space: pre;
1592
+ }
1593
+
1594
+ .tip {
1595
+ margin-top: 1rem;
1596
+ padding: 0.75rem 1rem;
1597
+ background: #1a2744;
1598
+ border: 1px solid #1e3a5f;
1599
+ border-radius: 8px;
1600
+ font-size: 13px;
1601
+ color: #93c5fd;
1602
+ line-height: 1.5;
1603
+ }
1604
+
1605
+ .tip-label {
1606
+ font-weight: 700;
1607
+ color: #60a5fa;
1608
+ }
1609
+
1610
+ .stack {
1611
+ margin-top: 1rem;
1612
+ font-size: 12px;
1613
+ color: #6a6a8a;
1614
+ white-space: pre-wrap;
1615
+ line-height: 1.5;
1616
+ }
1617
+ `;
1618
+ var OVERLAY_ELEMENT = `
1619
+ class WhatErrorOverlay extends HTMLElement {
1620
+ constructor(err) {
1621
+ super();
1622
+ this.root = this.attachShadow({ mode: 'open' });
1623
+ this.root.innerHTML = '<style>${OVERLAY_STYLES}</style>';
1624
+ this._err = err;
1625
+ this.show(err);
1626
+ }
1627
+
1628
+ // --- Inlined helper: escapeHTML ---
1629
+ _escapeHTML(str) {
1630
+ return String(str)
1631
+ .replace(/&/g, '&amp;')
1632
+ .replace(/</g, '&lt;')
1633
+ .replace(/>/g, '&gt;')
1634
+ .replace(/"/g, '&quot;');
1635
+ }
1636
+
1637
+ // --- Inlined helper: cleanStack ---
1638
+ _cleanStack(stack) {
1639
+ return stack
1640
+ .split('\\n')
1641
+ .filter(function(line) { return line.indexOf('node_modules') === -1; })
1642
+ .slice(0, 10)
1643
+ .join('\\n');
1644
+ }
1645
+
1646
+ // --- Inlined helper: getTip ---
1647
+ _getTip(err) {
1648
+ var msg = (err.message || '').toLowerCase();
1649
+
1650
+ if (msg.indexOf('infinite') !== -1 && msg.indexOf('effect') !== -1) {
1651
+ return 'An effect is writing to a signal it also reads. Use untrack() to read without subscribing, or move the write to a different effect.';
1652
+ }
1653
+ if (msg.indexOf('jsx') !== -1 && msg.indexOf('unexpected') !== -1) {
1654
+ return 'Make sure your vite.config includes the What compiler plugin: import what from "what-compiler/vite"';
1655
+ }
1656
+ if (msg.indexOf('not a function') !== -1 && msg.indexOf('signal') !== -1) {
1657
+ return 'Signals are functions: call sig() to read, sig(value) to write. Check you are not destructuring a signal.';
1658
+ }
1659
+ if (msg.indexOf('hydrat') !== -1) {
1660
+ return 'Hydration mismatches happen when SSR output differs from client render. Ensure server and client see the same initial state.';
1661
+ }
1662
+ // New tips for common mistakes
1663
+ if (msg.indexOf('signal') !== -1 && msg.indexOf('without') !== -1 && msg.indexOf('call') !== -1) {
1664
+ return 'Signals must be called to read their value. Use {count()} in JSX, not {count}. The parentheses trigger the reactive subscription.';
1665
+ }
1666
+ if (msg.indexOf('innerhtml') !== -1 && msg.indexOf('__html') !== -1) {
1667
+ return 'Raw innerHTML is blocked for security. Use innerHTML={{ __html: trustedString }} or dangerouslySetInnerHTML={{ __html: trustedString }} instead.';
1668
+ }
1669
+ if ((msg.indexOf('innerhtml') !== -1 || msg.indexOf('xss') !== -1) && msg.indexOf('raw string') !== -1) {
1670
+ return 'Raw innerHTML is a security risk (XSS). Wrap your HTML in an object: innerHTML={{ __html: yourString }}.';
1671
+ }
1672
+ if (msg.indexOf('cleanup') !== -1 && (msg.indexOf('effect') !== -1 || msg.indexOf('listener') !== -1)) {
1673
+ return 'Effects that add event listeners or timers should return a cleanup function: effect(() => { el.addEventListener(...); return () => el.removeEventListener(...); })';
1674
+ }
1675
+ if (msg.indexOf('route') !== -1 && (msg.indexOf('not found') !== -1 || msg.indexOf('404') !== -1 || msg.indexOf('no match') !== -1)) {
1676
+ return 'No route matched the current URL. Check that your route paths are correct and you have a catch-all or 404 route defined.';
1677
+ }
1678
+ if (msg.indexOf('key') !== -1 && (msg.indexOf('missing') !== -1 || msg.indexOf('list') !== -1 || msg.indexOf('each') !== -1)) {
1679
+ return 'Lists need unique keys for efficient DOM updates. Add a key prop: items.map(item => <Item key={item.id} />)';
1680
+ }
1681
+ return '';
1682
+ }
1683
+
1684
+ // --- Build overlay HTML ---
1685
+ _buildHTML(err) {
1686
+ var isCompilerError = err._isCompilerError || err.plugin === 'vite-plugin-what';
1687
+ var type = isCompilerError ? 'Compiler Error' : 'Runtime Error';
1688
+ var tagClass = isCompilerError ? 'tag-error' : 'tag-warning';
1689
+
1690
+ var codeFrame = '';
1691
+ var rawFrame = err.frame || err._frame;
1692
+ if (rawFrame) {
1693
+ var lines = rawFrame.split('\\n');
1694
+ var frameLines = '';
1695
+ for (var i = 0; i < lines.length; i++) {
1696
+ var line = lines[i];
1697
+ var isHighlight = line.trimStart().startsWith('>');
1698
+ var cleaned = line.replace(/^\\s*>\\s?/, ' ').replace(/^\\s{2}/, '');
1699
+ var match = cleaned.match(/^(\\s*\\d+)\\s*\\|(.*)$/);
1700
+ if (match) {
1701
+ frameLines += '<div class="code-line' + (isHighlight ? ' highlight' : '') + '"><span class="line-number">' + match[1].trim() + '</span><span class="line-content">' + this._escapeHTML(match[2]) + '</span></div>';
1702
+ } else if (cleaned.trim().startsWith('|')) {
1703
+ frameLines += '<div class="code-line highlight"><span class="line-number"></span><span class="line-content" style="color:#f87171">' + this._escapeHTML(cleaned.replace(/^\\s*\\|/, '')) + '</span></div>';
1704
+ }
1705
+ }
1706
+ if (frameLines) {
1707
+ codeFrame = '<div class="code-frame">' + frameLines + '</div>';
1708
+ }
1709
+ }
1710
+
1711
+ var filePath = err.id || (err.loc && err.loc.file) || '';
1712
+ var lineNum = (err.loc && err.loc.line != null) ? err.loc.line : '';
1713
+ var col = (err.loc && err.loc.column != null) ? err.loc.column : '';
1714
+ var location = filePath
1715
+ ? '<div class="file-path">' + this._escapeHTML(filePath) + (lineNum ? ':' + lineNum : '') + (col ? ':' + col : '') + '</div>'
1716
+ : '';
1717
+
1718
+ var tip = this._getTip(err);
1719
+ var tipHTML = tip ? '<div class="tip"><span class="tip-label">Tip: </span>' + this._escapeHTML(tip) + '</div>' : '';
1720
+
1721
+ var stack = (err.stack && !isCompilerError)
1722
+ ? '<div class="stack">' + this._escapeHTML(this._cleanStack(err.stack)) + '</div>'
1723
+ : '';
1724
+
1725
+ return '<div class="backdrop"></div>'
1726
+ + '<div class="panel">'
1727
+ + '<div class="header">'
1728
+ + '<div class="header-left">'
1729
+ + '<div class="logo">W</div>'
1730
+ + '<span class="brand">What Framework</span>'
1731
+ + '<span class="tag ' + tagClass + '">' + type + '</span>'
1732
+ + '</div>'
1733
+ + '<div class="header-right">'
1734
+ + '<button class="copy-btn">Copy Error</button>'
1735
+ + '<button class="close-btn">Dismiss (Esc)</button>'
1736
+ + '</div>'
1737
+ + '</div>'
1738
+ + '<div class="body">'
1739
+ + '<h2 class="error-title">' + this._escapeHTML(err.name || 'Error') + '</h2>'
1740
+ + location
1741
+ + '<pre class="error-message">' + this._escapeHTML(err.message || String(err)) + '</pre>'
1742
+ + codeFrame
1743
+ + tipHTML
1744
+ + stack
1745
+ + '</div>'
1746
+ + '</div>';
1747
+ }
1748
+
1749
+ show(err) {
1750
+ var template = document.createElement('template');
1751
+ template.innerHTML = this._buildHTML(err);
1752
+ this.root.appendChild(template.content.cloneNode(true));
1753
+
1754
+ // Close handlers
1755
+ var self = this;
1756
+ var closeBtn = this.root.querySelector('.close-btn');
1757
+ if (closeBtn) closeBtn.addEventListener('click', function() { self.close(); });
1758
+ var backdrop = this.root.querySelector('.backdrop');
1759
+ if (backdrop) backdrop.addEventListener('click', function() { self.close(); });
1760
+ document.addEventListener('keydown', this._onKey = function(e) {
1761
+ if (e.key === 'Escape') self.close();
1762
+ });
1763
+
1764
+ // Copy Error button
1765
+ var copyBtn = this.root.querySelector('.copy-btn');
1766
+ if (copyBtn) {
1767
+ copyBtn.addEventListener('click', function() {
1768
+ self._copyError(copyBtn);
1769
+ });
1770
+ }
1771
+ }
1772
+
1773
+ _copyError(btn) {
1774
+ var err = this._err;
1775
+ var data = {
1776
+ name: err.name || 'Error',
1777
+ message: err.message || String(err),
1778
+ file: err.id || (err.loc && err.loc.file) || null,
1779
+ line: (err.loc && err.loc.line != null) ? err.loc.line : null,
1780
+ column: (err.loc && err.loc.column != null) ? err.loc.column : null,
1781
+ stack: err.stack ? this._cleanStack(err.stack) : null,
1782
+ framework: 'What Framework',
1783
+ timestamp: new Date().toISOString()
1784
+ };
1785
+
1786
+ var text = JSON.stringify(data, null, 2);
1787
+ if (navigator.clipboard && navigator.clipboard.writeText) {
1788
+ navigator.clipboard.writeText(text).then(function() {
1789
+ btn.textContent = 'Copied!';
1790
+ btn.classList.add('copied');
1791
+ setTimeout(function() {
1792
+ btn.textContent = 'Copy Error';
1793
+ btn.classList.remove('copied');
1794
+ }, 2000);
1795
+ }).catch(function() {
1796
+ // Fallback: select text
1797
+ prompt('Copy error details:', text);
1798
+ });
1799
+ } else {
1800
+ prompt('Copy error details:', text);
1801
+ }
1802
+ }
1803
+
1804
+ close() {
1805
+ document.removeEventListener('keydown', this._onKey);
1806
+ this.remove();
1807
+ }
1808
+ }
1809
+
1810
+ if (!customElements.get('what-error-overlay')) {
1811
+ customElements.define('what-error-overlay', WhatErrorOverlay);
1812
+ }
1813
+ `;
1814
+ function setupErrorOverlay(server) {
1815
+ const origSend = server.ws.send.bind(server.ws);
1816
+ server.ws.send = function(payload) {
1817
+ if (payload?.type === "error") {
1818
+ if (payload.err?.plugin === "vite-plugin-what") {
1819
+ payload.err._isCompilerError = true;
1820
+ }
1821
+ }
1822
+ return origSend(payload);
1823
+ };
1824
+ }
1825
+
1826
+ // packages/compiler/src/vite-plugin.js
1827
+ var VIRTUAL_ROUTES_ID = "virtual:what-routes";
1828
+ var RESOLVED_VIRTUAL_ID = "\0" + VIRTUAL_ROUTES_ID;
1829
+ var COMPONENT_EXPORT_RE = /export\s+(?:default\s+)?function\s+([A-Z]\w*)/;
1830
+ var UTILITY_FILE_RE = /(?:store|signal|state|context|util|helper|lib|config)\b/i;
1831
+ function whatVitePlugin(options = {}) {
1832
+ const {
1833
+ // File extensions to process
1834
+ include = /\.[jt]sx$/,
1835
+ // Files to exclude
1836
+ exclude = /node_modules/,
1837
+ // Enable source maps
1838
+ sourceMaps = true,
1839
+ // Production optimizations
1840
+ production = false,
1841
+ // Pages directory (relative to project root)
1842
+ pages = "src/pages",
1843
+ // HMR: enabled by default in dev, disabled in production
1844
+ hot = !production
1845
+ } = options;
1846
+ let rootDir = "";
1847
+ let pagesDir = "";
1848
+ let server = null;
1849
+ let isDevMode = false;
1850
+ return {
1851
+ name: "vite-plugin-what",
1852
+ configResolved(config) {
1853
+ rootDir = config.root;
1854
+ pagesDir = path2.resolve(rootDir, pages);
1855
+ isDevMode = config.command === "serve";
1856
+ },
1857
+ configureServer(devServer) {
1858
+ server = devServer;
1859
+ setupErrorOverlay(devServer);
1860
+ devServer.watcher.on("add", (file) => {
1861
+ if (file.startsWith(pagesDir)) {
1862
+ const mod = devServer.moduleGraph.getModuleById(RESOLVED_VIRTUAL_ID);
1863
+ if (mod) {
1864
+ devServer.moduleGraph.invalidateModule(mod);
1865
+ devServer.ws.send({ type: "full-reload" });
1866
+ }
1867
+ }
1868
+ });
1869
+ devServer.watcher.on("unlink", (file) => {
1870
+ if (file.startsWith(pagesDir)) {
1871
+ const mod = devServer.moduleGraph.getModuleById(RESOLVED_VIRTUAL_ID);
1872
+ if (mod) {
1873
+ devServer.moduleGraph.invalidateModule(mod);
1874
+ devServer.ws.send({ type: "full-reload" });
1875
+ }
1876
+ }
1877
+ });
1878
+ },
1879
+ // Resolve virtual module
1880
+ resolveId(id) {
1881
+ if (id === VIRTUAL_ROUTES_ID) {
1882
+ return RESOLVED_VIRTUAL_ID;
1883
+ }
1884
+ },
1885
+ // Generate the routes module
1886
+ load(id) {
1887
+ if (id === RESOLVED_VIRTUAL_ID) {
1888
+ return generateRoutesModule(pagesDir, rootDir);
1889
+ }
1890
+ },
1891
+ // Transform JSX files
1892
+ transform(code, id) {
1893
+ if (!include.test(id)) return null;
1894
+ if (exclude && exclude.test(id)) return null;
1895
+ try {
1896
+ const result = transformSync(code, {
1897
+ filename: id,
1898
+ sourceMaps,
1899
+ plugins: [
1900
+ [whatBabelPlugin, { production }]
1901
+ ],
1902
+ parserOpts: {
1903
+ plugins: ["jsx", "typescript"]
1904
+ }
1905
+ });
1906
+ if (!result || !result.code) {
1907
+ return null;
1908
+ }
1909
+ let outputCode = result.code;
1910
+ if (hot && isDevMode && !production) {
1911
+ const isComponentFile = isComponentModule(code, id);
1912
+ if (isComponentFile) {
1913
+ outputCode += generateHMRBoundary(id);
1914
+ }
1915
+ }
1916
+ return {
1917
+ code: outputCode,
1918
+ map: result.map
1919
+ };
1920
+ } catch (error) {
1921
+ error.plugin = "vite-plugin-what";
1922
+ if (!error.id) error.id = id;
1923
+ if (error.loc === void 0 && error._loc) {
1924
+ error.loc = { file: id, line: error._loc.line, column: error._loc.column };
1925
+ }
1926
+ console.error(`[what] Error transforming ${id}:`, error.message);
1927
+ throw error;
1928
+ }
1929
+ },
1930
+ // HMR: detect component vs utility files and handle accordingly
1931
+ handleHotUpdate({ file, server: devServer, modules }) {
1932
+ if (!hot) return;
1933
+ if (!include.test(file)) return;
1934
+ if (exclude && exclude.test(file)) return;
1935
+ if (isUtilityFile(file)) {
1936
+ devServer.ws.send({ type: "full-reload" });
1937
+ return [];
1938
+ }
1939
+ return;
1940
+ },
1941
+ // Configure for development
1942
+ config(config, { mode }) {
1943
+ return {
1944
+ esbuild: {
1945
+ // Preserve JSX so our babel plugin handles it -- don't let esbuild transform it
1946
+ jsx: "preserve"
1947
+ },
1948
+ optimizeDeps: {
1949
+ // Pre-bundle the framework
1950
+ include: ["what-framework"]
1951
+ }
1952
+ };
1953
+ }
1954
+ };
1955
+ }
1956
+ function isComponentModule(source, filePath) {
1957
+ if (COMPONENT_EXPORT_RE.test(source)) return true;
1958
+ if (filePath.includes("/pages/") || filePath.includes("\\pages\\")) return true;
1959
+ return false;
1960
+ }
1961
+ function isUtilityFile(filePath) {
1962
+ const basename = path2.basename(filePath, path2.extname(filePath));
1963
+ return UTILITY_FILE_RE.test(basename);
1964
+ }
1965
+ function generateHMRBoundary(filePath) {
1966
+ return `
1967
+
1968
+ // --- What Framework HMR Boundary ---
1969
+ if (import.meta.hot) {
1970
+ import.meta.hot.accept((newModule) => {
1971
+ if (newModule) {
1972
+ // Signal to the What runtime that this module was hot-updated
1973
+ if (window.__WHAT_HMR_ACCEPT__) {
1974
+ window.__WHAT_HMR_ACCEPT__(${JSON.stringify(filePath)}, newModule);
1975
+ }
1976
+ }
1977
+ });
1978
+ }
1979
+ `;
1980
+ }
1981
+
1982
+ // packages/compiler/src/runtime.js
1983
+ import { h, Fragment, mount, Island } from "what-core";
1984
+ export {
1985
+ Fragment,
1986
+ Island,
1987
+ whatBabelPlugin as babelPlugin,
1988
+ extractPageConfig,
1989
+ generateRoutesModule,
1990
+ h,
1991
+ mount,
1992
+ scanPages,
1993
+ whatVitePlugin as vitePlugin,
1994
+ whatVitePlugin as what
1995
+ };
1996
+ //# sourceMappingURL=index.js.map