@fluixi/compiler 1.0.0-alpha.92 → 1.0.0-alpha.93

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/analyze/intrinsics.cjs +18 -0
  2. package/dist/analyze/intrinsics.d.ts.map +1 -1
  3. package/dist/analyze/intrinsics.js +18 -0
  4. package/dist/analyze/intrinsics.mjs +18 -0
  5. package/dist/analyze/reactive-bindings.cjs +18 -0
  6. package/dist/analyze/reactive-bindings.mjs +18 -0
  7. package/dist/analyze/static-graph.cjs +22 -4
  8. package/dist/analyze/static-graph.d.ts.map +1 -1
  9. package/dist/analyze/static-graph.js +6 -4
  10. package/dist/analyze/static-graph.mjs +22 -4
  11. package/dist/{babel-GI375YZ2.mjs → babel-RJHJQVEM.mjs} +4 -4
  12. package/dist/{chunk-QZ4CJPYP.mjs → chunk-56QSOVVL.mjs} +1 -1
  13. package/dist/{chunk-XJ5H2MXY.mjs → chunk-OD53TMER.mjs} +19 -1
  14. package/dist/{chunk-DWNWN5RH.mjs → chunk-R7PPFBCL.mjs} +1 -1
  15. package/dist/{chunk-VOUI6XPV.mjs → chunk-TJIH7TLX.mjs} +1 -1
  16. package/dist/{chunk-VC6X44FS.mjs → chunk-U7HCFLZ2.mjs} +153 -48
  17. package/dist/codegen/backends/imperative.cjs +24 -6
  18. package/dist/codegen/backends/imperative.d.ts.map +1 -1
  19. package/dist/codegen/backends/imperative.js +19 -4
  20. package/dist/codegen/backends/imperative.mjs +24 -6
  21. package/dist/frontend/babel/build-ir-lit.cjs +120 -43
  22. package/dist/frontend/babel/build-ir-lit.mjs +120 -43
  23. package/dist/frontend/babel/build-ir.cjs +120 -43
  24. package/dist/frontend/babel/build-ir.mjs +120 -43
  25. package/dist/frontend/babel/index.cjs +121 -44
  26. package/dist/frontend/babel/index.mjs +121 -44
  27. package/dist/frontend/babel/lower-template.cjs +120 -43
  28. package/dist/frontend/babel/lower-template.mjs +120 -43
  29. package/dist/frontend/babel/plugin.cjs +120 -43
  30. package/dist/frontend/babel/plugin.mjs +120 -43
  31. package/dist/index.cjs +41 -38
  32. package/dist/index.mjs +41 -38
  33. package/dist/integrations.cjs +176 -52
  34. package/dist/integrations.mjs +8 -8
  35. package/dist/lower/compile-template.cjs +41 -38
  36. package/dist/lower/compile-template.mjs +41 -38
  37. package/dist/lower/jsx.cjs +509 -6
  38. package/dist/lower/jsx.d.ts.map +1 -1
  39. package/dist/lower/jsx.js +114 -6
  40. package/dist/lower/jsx.mjs +509 -6
  41. package/dist/lower/template.cjs +40 -37
  42. package/dist/lower/template.d.ts.map +1 -1
  43. package/dist/lower/template.js +22 -30
  44. package/dist/lower/template.mjs +40 -37
  45. package/dist/resolve/builtins.cjs +1 -1
  46. package/dist/resolve/builtins.mjs +1 -1
  47. package/dist/resolve/component-globals.cjs +14 -0
  48. package/dist/resolve/component-globals.mjs +14 -0
  49. package/dist/resolve/write-globals.cjs +14 -0
  50. package/dist/resolve/write-globals.mjs +14 -0
  51. package/dist/{server-fns-BBZRZUPP.mjs → server-fns-ZFRDIQ2X.mjs} +4 -4
  52. package/dist/{server-functions-VEGLIFCE.mjs → server-functions-CTDXX67X.mjs} +3 -3
  53. package/dist/transform/index.cjs +170 -47
  54. package/dist/transform/index.mjs +170 -47
  55. package/dist/transform/intrinsics.cjs +18 -0
  56. package/dist/transform/intrinsics.mjs +18 -0
  57. package/dist/transform/source-locations.cjs +64 -3
  58. package/dist/transform/source-locations.d.cts +7 -2
  59. package/dist/transform/source-locations.d.ts +7 -2
  60. package/dist/transform/source-locations.d.ts.map +1 -1
  61. package/dist/transform/source-locations.js +68 -3
  62. package/dist/transform/source-locations.mjs +64 -3
  63. package/dist/transform/templates.cjs +169 -46
  64. package/dist/transform/templates.js +2 -2
  65. package/dist/transform/templates.mjs +169 -46
  66. package/dist/{transform-UWZ7ZGJ2.mjs → transform-QWWQDCXE.mjs} +7 -7
  67. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  68. package/package.json +9 -3
@@ -1,4 +1,4 @@
1
- /*! @fluixi/compiler v1.0.0-alpha.92 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/compiler v1.0.0-alpha.93 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
  import {
3
3
  INTRINSICS,
4
4
  RESERVED_COMPONENTS,
@@ -7,7 +7,7 @@ import {
7
7
  builtinComponentMap,
8
8
  isIntrinsicName,
9
9
  isPrimitiveModule
10
- } from "./chunk-XJ5H2MXY.mjs";
10
+ } from "./chunk-OD53TMER.mjs";
11
11
 
12
12
  // src/analyze/expr-shape.ts
13
13
  var CALL = /* @__PURE__ */ new Set(["CallExpression", "OptionalCallExpression"]);
@@ -293,6 +293,7 @@ function resolveReactiveBindings(program) {
293
293
  // src/transform/source-locations.ts
294
294
  var SOURCE_MARKER = "__fx_source";
295
295
  var COMPONENT_MARKER = "__fx_component";
296
+ var CLASS_MARKER = "__fx_class";
296
297
  var HOLE_MARKER = "__fx_hole";
297
298
  var markHole = (accessor, line, column, name) => `(globalThis.${HOLE_MARKER} ?? ((f) => f))(${accessor},${line},${column}${name === void 0 ? "" : `,${JSON.stringify(name)}`})`;
298
299
  var PROPS_MARKER = "__fx_props";
@@ -310,11 +311,12 @@ var RUNTIME_PRIMITIVES2 = /* @__PURE__ */ new Set([
310
311
  "createStore",
311
312
  "createResource"
312
313
  ]);
313
- function mayHaveReactiveCall(code) {
314
+ function mayHaveSourceMarks(code) {
314
315
  for (const name of RUNTIME_PRIMITIVES2) {
315
316
  if (code.includes(name)) return true;
316
317
  }
317
- return code.includes("$signal") || code.includes("$memo") || code.includes("$effect") || code.includes("$store");
318
+ if (code.includes("$signal") || code.includes("$memo") || code.includes("$effect") || code.includes("$store")) return true;
319
+ return /\bclass\b[^{;]*\bextends\b/.test(code);
318
320
  }
319
321
  var SINGLE_STATEMENT_PARENTS = /* @__PURE__ */ new Set([
320
322
  "IfStatement",
@@ -333,6 +335,7 @@ function isLoneBranch(statement, parent) {
333
335
  function isExport(node) {
334
336
  return node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration";
335
337
  }
338
+ var CLASS_FIELDS = /* @__PURE__ */ new Set(["PropertyDefinition", "ClassProperty", "ClassPrivateProperty", "AccessorProperty"]);
336
339
  function isStatement(node) {
337
340
  return node.type.endsWith("Statement") || node.type === "VariableDeclaration";
338
341
  }
@@ -353,6 +356,14 @@ function walk3(node, visit, stack = []) {
353
356
  }
354
357
  function nameFor(stack) {
355
358
  const declarator = stack[stack.length - 1];
359
+ if (declarator && CLASS_FIELDS.has(declarator.type)) {
360
+ const key = declarator.key;
361
+ if (key?.type === "Identifier" || key?.type === "PrivateIdentifier" || key?.type === "PrivateName") {
362
+ const name = key.name ?? key.id?.name;
363
+ return name;
364
+ }
365
+ return void 0;
366
+ }
356
367
  if (declarator?.type !== "VariableDeclarator") return void 0;
357
368
  const id = declarator.id;
358
369
  if (id?.type === "Identifier") return id.name;
@@ -401,6 +412,17 @@ function collectSourceEdits(program, filename) {
401
412
  });
402
413
  }
403
414
  }
415
+ if ((node.type === "ClassDeclaration" || node.type === "ClassExpression") && node.superClass && !node.declare) {
416
+ const at = node.loc?.start;
417
+ const body = node.body;
418
+ if (at && body) {
419
+ edits.push({
420
+ start: body.start + 1,
421
+ end: body.start + 1,
422
+ code: `static{globalThis.${CLASS_MARKER}?.(this,${JSON.stringify(filename)},${at.line},${at.column});}`
423
+ });
424
+ }
425
+ }
404
426
  if (node.type !== "CallExpression") return;
405
427
  const callee = node.callee;
406
428
  if (callee?.type !== "Identifier") return;
@@ -410,6 +432,26 @@ function collectSourceEdits(program, filename) {
410
432
  if (!intrinsic && !imported) return;
411
433
  const position = node.loc?.start;
412
434
  if (!position) return;
435
+ for (let i = stack.length - 1; i >= 0; i--) {
436
+ const up = stack[i];
437
+ if (isStatement(up)) break;
438
+ if (CLASS_FIELDS.has(up.type)) {
439
+ if (claimed.has(node.start)) return;
440
+ claimed.add(node.start);
441
+ const label2 = nameFor(stack);
442
+ const own = !up.static;
443
+ const args2 = [
444
+ JSON.stringify(filename),
445
+ String(position.line),
446
+ String(position.column),
447
+ label2 ? JSON.stringify(label2) : own ? "undefined" : void 0,
448
+ own ? "this" : void 0
449
+ ].filter((a) => a !== void 0).join(",");
450
+ edits.push({ start: node.start, end: node.start, code: `(globalThis.${SOURCE_MARKER}?.(${args2}),` });
451
+ edits.push({ start: node.end, end: node.end, code: ")" });
452
+ return;
453
+ }
454
+ }
413
455
  let statement;
414
456
  let parent;
415
457
  let index = -1;
@@ -819,13 +861,16 @@ function emitComponent(name, props, children, used, templates, at) {
819
861
  used.add("createComponent");
820
862
  const spreads = props.filter((p) => p.kind === "spread");
821
863
  const normal = props.filter((p) => p.kind !== "spread");
822
- const entries = normal.map(
823
- (p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
824
- );
864
+ const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
825
865
  if (children.length > 0) {
826
- entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
866
+ bodies.push(["children", emitChildren(children, used, templates)]);
867
+ }
868
+ let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
869
+ if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
870
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
871
+ const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
872
+ obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
827
873
  }
828
- let obj = `{ ${entries.join(", ")} }`;
829
874
  if (spreads.length > 0) {
830
875
  used.add("mergeProps");
831
876
  obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
@@ -907,7 +952,8 @@ function emitNode(node, used, templates) {
907
952
  } else {
908
953
  used.add("spread");
909
954
  const svgFlag = node.svg ? ", isSVG: true" : "";
910
- const customFlag = node.tag.includes("-") ? ", isCustomElement: true" : "";
955
+ const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
956
+ const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
911
957
  parts.push(
912
958
  `spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
913
959
  );
@@ -1081,6 +1127,22 @@ var DELEGATED = /* @__PURE__ */ new Set([
1081
1127
  "mouseup"
1082
1128
  ]);
1083
1129
  var isReactive = (node) => isReactiveShape(shapeOf(node));
1130
+ var CONTAINER = "container";
1131
+ function directive(node, name) {
1132
+ if (node.type !== "JSXElement") return void 0;
1133
+ const opening = node.openingElement;
1134
+ if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
1135
+ return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
1136
+ }
1137
+ function directiveExpr(attr) {
1138
+ const v = attr.value;
1139
+ return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
1140
+ }
1141
+ function emitIR(ir, ctx) {
1142
+ const { code, imports, templates } = imperativeBackend.emit(ir, {});
1143
+ for (const s of imports) ctx.used.add(s);
1144
+ return inlineTemplates(code, templates);
1145
+ }
1084
1146
  var posOf = (node, ctx) => {
1085
1147
  if (!ctx.sourceFile) return void 0;
1086
1148
  const at = node.loc?.start;
@@ -1189,7 +1251,16 @@ function buildProps(attrs, ctx) {
1189
1251
  }
1190
1252
  function buildChildren(children, ctx) {
1191
1253
  const out = [];
1192
- for (const child of children) {
1254
+ for (let i = 0; i < children.length; i++) {
1255
+ const child = children[i];
1256
+ if (directive(child, "if")) {
1257
+ let next = i + 1;
1258
+ while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
1259
+ const fallback = directive(children[next], "else") ? children[next] : null;
1260
+ out.push(buildIf(child, fallback, ctx));
1261
+ if (fallback) i = next;
1262
+ continue;
1263
+ }
1193
1264
  if (child.type === "JSXText") {
1194
1265
  const text = cleanTemplateText(child.value);
1195
1266
  if (text) out.push({ kind: "text", value: text });
@@ -1226,19 +1297,68 @@ function readLoad(attrs) {
1226
1297
  }
1227
1298
  return void 0;
1228
1299
  }
1300
+ function buildIf(node, fallback, ctx) {
1301
+ const cond = directiveExpr(directive(node, "if"));
1302
+ const props = [
1303
+ cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
1304
+ ];
1305
+ if (fallback) {
1306
+ props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
1307
+ }
1308
+ return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
1309
+ }
1310
+ function buildEach(node, ctx) {
1311
+ const attr = directive(node, "each");
1312
+ if (!attr) return null;
1313
+ const list = directiveExpr(attr);
1314
+ const props = [
1315
+ list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
1316
+ ];
1317
+ const key = directive(node, "key");
1318
+ if (key) {
1319
+ const keyExpr = directiveExpr(key);
1320
+ const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
1321
+ if (by) props.push({ name: "by", kind: "attr", expr: by });
1322
+ }
1323
+ const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
1324
+ const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
1325
+ let render;
1326
+ if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
1327
+ const fn = ctx.code(only);
1328
+ const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
1329
+ render = live ? `(...args) => () => (${fn})(...args)` : fn;
1330
+ } else {
1331
+ const built = buildChildren(node.children, ctx);
1332
+ const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
1333
+ render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
1334
+ }
1335
+ return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
1336
+ }
1337
+ function buildElement(node, ctx, handled) {
1338
+ const skip = new Set(handled);
1339
+ const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
1340
+ const attributes = node.openingElement.attributes.filter(
1341
+ (a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
1342
+ );
1343
+ const { tag } = tagInfo(node.openingElement.name, ctx);
1344
+ const each = buildEach(node, ctx);
1345
+ const children = each ? [each] : buildChildren(node.children, ctx);
1346
+ if (tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
1347
+ const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1348
+ return { kind: "element", tag, svg: SVG_TAGS.has(tag), props: buildProps(attributes, ctx), children, static: false, ...at ? { at } : {} };
1349
+ }
1229
1350
  function buildNode(node, ctx) {
1230
1351
  if (node.type === "JSXFragment") {
1231
1352
  return { kind: "fragment", children: buildChildren(node.children, ctx) };
1232
1353
  }
1354
+ if (directive(node, "if")) return buildIf(node, null, ctx);
1233
1355
  const { tag, component } = tagInfo(node.openingElement.name, ctx);
1356
+ if (!component) return buildElement(node, ctx, []);
1234
1357
  const props = buildProps(node.openingElement.attributes, ctx);
1235
1358
  const children = buildChildren(node.children, ctx);
1236
1359
  const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1237
- if (component) {
1238
- const load = readLoad(node.openingElement.attributes);
1239
- return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1240
- }
1241
- return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
1360
+ const load = readLoad(node.openingElement.attributes);
1361
+ return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1242
1362
  }
1243
1363
  function buildJsxIR(node, ctx) {
1244
1364
  const ir = buildNode(node, ctx);
@@ -1267,6 +1387,7 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
1267
1387
  function escapeTemplate(s) {
1268
1388
  return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
1269
1389
  }
1390
+ var CONTAINER2 = "container";
1270
1391
  function capitalize(s) {
1271
1392
  return s.charAt(0).toUpperCase() + s.slice(1);
1272
1393
  }
@@ -1321,10 +1442,6 @@ var Lowerer = class {
1321
1442
  if (hasElse) i = elseIdx;
1322
1443
  continue;
1323
1444
  }
1324
- if (child.attributes.some((a) => a.kind === "EachDirective")) {
1325
- out.push(this.lowerEach(child));
1326
- continue;
1327
- }
1328
1445
  }
1329
1446
  const node = this.lowerNode(child);
1330
1447
  if (node) out.push(node);
@@ -1366,13 +1483,16 @@ var Lowerer = class {
1366
1483
  node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
1367
1484
  return node;
1368
1485
  }
1486
+ const each = this.lowerEach(el);
1487
+ const children = each ? [each] : this.lowerChildren(el.children);
1488
+ if (el.tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
1369
1489
  const at = this.tagAt(el);
1370
1490
  return {
1371
1491
  kind: "element",
1372
1492
  tag: el.tag,
1373
1493
  svg: el.namespace === "svg",
1374
1494
  props: this.lowerAttributes(el.attributes),
1375
- children: this.lowerChildren(el.children),
1495
+ children,
1376
1496
  static: false,
1377
1497
  ...at ? { at } : {}
1378
1498
  };
@@ -1444,10 +1564,14 @@ var Lowerer = class {
1444
1564
  const at = this.tagAt(el);
1445
1565
  return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
1446
1566
  }
1447
- /** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
1567
+ /**
1568
+ * `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
1569
+ * content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
1570
+ * it is. Null when the element has no `each`.
1571
+ */
1448
1572
  lowerEach(el) {
1449
1573
  const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
1450
- if (!eachDir || eachDir.kind !== "EachDirective") return this.lowerNode(el);
1574
+ if (!eachDir || eachDir.kind !== "EachDirective") return null;
1451
1575
  const each = this.hole(eachDir.hole);
1452
1576
  const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
1453
1577
  if (eachDir.key) {
@@ -1457,12 +1581,12 @@ var Lowerer = class {
1457
1581
  const arrow = this.itemArrow(el);
1458
1582
  let childrenCode;
1459
1583
  if (arrow) {
1460
- const bodyIR = { kind: "expr", code: arrow.body, reactive: arrow.bodyReactive };
1461
- const repeated = this.rebuildWithChildren(el, [bodyIR]);
1462
- childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
1584
+ const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
1585
+ childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
1463
1586
  } else {
1464
- const repeated = this.stripAndLower(el, (a) => a.kind === "EachDirective");
1465
- childrenCode = `() => ${paren(this.emit(repeated), "arrowBody")}`;
1587
+ const content = this.lowerChildren(el.children);
1588
+ const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
1589
+ childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
1466
1590
  }
1467
1591
  const children = [{ kind: "expr", code: childrenCode, reactive: false }];
1468
1592
  const at = this.tagAt(el);
@@ -1479,25 +1603,6 @@ var Lowerer = class {
1479
1603
  const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
1480
1604
  return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
1481
1605
  }
1482
- /** Same element (minus each/key) but with explicit IR children. */
1483
- rebuildWithChildren(el, children) {
1484
- const props = this.lowerAttributes(
1485
- el.attributes.filter((a) => a.kind !== "EachDirective")
1486
- );
1487
- if (el.kind === "Element") {
1488
- const at = this.tagAt(el);
1489
- return {
1490
- kind: "element",
1491
- tag: el.tag,
1492
- svg: el.namespace === "svg",
1493
- props,
1494
- children,
1495
- static: false,
1496
- ...at ? { at } : {}
1497
- };
1498
- }
1499
- return { kind: "component", name: el.tag, props, children };
1500
- }
1501
1606
  // --- attributes ---------------------------------------------------------
1502
1607
  lowerAttributes(attrs) {
1503
1608
  const props = [];
@@ -1709,7 +1814,7 @@ export {
1709
1814
  collapseStrategies,
1710
1815
  declaredNames,
1711
1816
  resolveReactiveBindings,
1712
- mayHaveReactiveCall,
1817
+ mayHaveSourceMarks,
1713
1818
  collectSourceEdits,
1714
1819
  inlineTemplates,
1715
1820
  imperativeBackend,
@@ -237,6 +237,7 @@ var INTRINSICS = {
237
237
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
238
238
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
239
239
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
240
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
240
241
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
241
242
  // name. Still worth having: no import to write, and when a shorter form is
242
243
  // designed, the change is this line rather than a compiler pass.
@@ -247,6 +248,19 @@ var INTRINSICS = {
247
248
  // dependency analysis must not guess at.
248
249
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
249
250
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
251
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
252
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
253
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
254
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
255
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
256
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
257
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
258
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
259
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
260
+ // handle as far as reads go: the same accessor with `set` on it.
261
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
262
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
263
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
250
264
  // An environment variable, public or server-only; the browser build refuses a secret.
251
265
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
252
266
  };
@@ -424,13 +438,16 @@ function emitComponent(name, props, children, used, templates, at) {
424
438
  used.add("createComponent");
425
439
  const spreads = props.filter((p) => p.kind === "spread");
426
440
  const normal = props.filter((p) => p.kind !== "spread");
427
- const entries = normal.map(
428
- (p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
429
- );
441
+ const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
430
442
  if (children.length > 0) {
431
- entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
443
+ bodies.push(["children", emitChildren(children, used, templates)]);
444
+ }
445
+ let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
446
+ if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
447
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
448
+ const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
449
+ obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
432
450
  }
433
- let obj = `{ ${entries.join(", ")} }`;
434
451
  if (spreads.length > 0) {
435
452
  used.add("mergeProps");
436
453
  obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
@@ -512,7 +529,8 @@ function emitNode(node, used, templates) {
512
529
  } else {
513
530
  used.add("spread");
514
531
  const svgFlag = node.svg ? ", isSVG: true" : "";
515
- const customFlag = node.tag.includes("-") ? ", isCustomElement: true" : "";
532
+ const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
533
+ const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
516
534
  parts.push(
517
535
  `spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
518
536
  );
@@ -1 +1 @@
1
- {"version":3,"file":"imperative.d.ts","sourceRoot":"","sources":["../../../src/codegen/backends/imperative.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAiB,cAAc,EAAE,MAAM,eAAe,CAAC;AAqUnF;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,GAAG,SAAS,GAAG,MAAM,CAK7F;AAED,eAAO,MAAM,iBAAiB,EAAE,cAgB/B,CAAC"}
1
+ {"version":3,"file":"imperative.d.ts","sourceRoot":"","sources":["../../../src/codegen/backends/imperative.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAiB,cAAc,EAAE,MAAM,eAAe,CAAC;AAkVnF;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,GAAG,SAAS,GAAG,MAAM,CAK7F;AAED,eAAO,MAAM,iBAAiB,EAAE,cAgB/B,CAAC"}
@@ -101,15 +101,25 @@ function emitComponent(name, props, children, used, templates, at) {
101
101
  // on every change (a <Portal> then piles up). As getters the component is built ONCE and
102
102
  // reactivity flows through the getter into its own effects. Static props as getters are
103
103
  // harmless (they return the same value). Matches lazyProps() in the inline codegen.
104
- const entries = normal.map((p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`);
104
+ // Each body emitted once: emitting children registers their templates.
105
+ const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
105
106
  if (children.length > 0) {
106
107
  // Children are a LAZY getter: evaluated inside the component's execution, so
107
108
  // child components are created in the component's owner scope (preserves owner
108
109
  // nesting for context/Suspense), after the component runs (correct provider
109
110
  // ordering), and lazily (ErrorBoundary can still catch a throwing child).
110
- entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
111
+ bodies.push(['children', emitChildren(children, used, templates)]);
112
+ }
113
+ let obj = `{ ${bodies.map(([name, body]) => `get ${propKey(name)}() { return ${body}; }`).join(', ')} }`;
114
+ // A getter is a method: `this` inside it is the props object. Written in a class method
115
+ // (a custom element's render), `title={() => this.name}` then read the props, not the
116
+ // element. Arrows keep the enclosing `this`, so those props go out as arrow thunks and the
117
+ // getters call them. Only when some prop mentions `this`; everything else is unchanged.
118
+ if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
119
+ const thunks = bodies.map(([name, body]) => `${JSON.stringify(name)}: () => ${body}`);
120
+ const getters = bodies.map(([name]) => `get ${propKey(name)}() { return _$t[${JSON.stringify(name)}](); }`);
121
+ obj = `((_$t) => ({ ${getters.join(', ')} }))({ ${thunks.join(', ')} })`;
111
122
  }
112
- let obj = `{ ${entries.join(', ')} }`;
113
123
  if (spreads.length > 0) {
114
124
  used.add('mergeProps');
115
125
  obj = `mergeProps(${spreads.map((s) => s.expr).join(', ')}, ${obj})`;
@@ -268,7 +278,12 @@ function emitNode(node, used, templates) {
268
278
  // here, so the runtime does not have to read it back off the node. It falls
269
279
  // back to that check when the flag is absent, which is how a hand-written
270
280
  // `spread` call still behaves.
271
- const customFlag = node.tag.includes('-') ? ', isCustomElement: true' : '';
281
+ // For a custom element it also names the props wrapped as accessors: any other
282
+ // function is a property value (an action, a callback) and must not be called.
283
+ const accessors = node.props
284
+ .filter((p) => p.kind !== 'spread' && p.expr !== undefined && p.reactive && p.name !== 'ref')
285
+ .map((p) => p.name);
286
+ const customFlag = node.tag.includes('-') ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : '';
272
287
  parts.push(`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`);
273
288
  }
274
289
  }
@@ -210,6 +210,7 @@ var INTRINSICS = {
210
210
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
211
211
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
212
212
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
213
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
213
214
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
214
215
  // name. Still worth having: no import to write, and when a shorter form is
215
216
  // designed, the change is this line rather than a compiler pass.
@@ -220,6 +221,19 @@ var INTRINSICS = {
220
221
  // dependency analysis must not guess at.
221
222
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
222
223
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
224
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
225
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
226
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
227
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
228
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
229
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
230
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
231
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
232
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
233
+ // handle as far as reads go: the same accessor with `set` on it.
234
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
235
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
236
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
223
237
  // An environment variable, public or server-only; the browser build refuses a secret.
224
238
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
225
239
  };
@@ -397,13 +411,16 @@ function emitComponent(name, props, children, used, templates, at) {
397
411
  used.add("createComponent");
398
412
  const spreads = props.filter((p) => p.kind === "spread");
399
413
  const normal = props.filter((p) => p.kind !== "spread");
400
- const entries = normal.map(
401
- (p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
402
- );
414
+ const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
403
415
  if (children.length > 0) {
404
- entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
416
+ bodies.push(["children", emitChildren(children, used, templates)]);
417
+ }
418
+ let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
419
+ if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
420
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
421
+ const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
422
+ obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
405
423
  }
406
- let obj = `{ ${entries.join(", ")} }`;
407
424
  if (spreads.length > 0) {
408
425
  used.add("mergeProps");
409
426
  obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
@@ -485,7 +502,8 @@ function emitNode(node, used, templates) {
485
502
  } else {
486
503
  used.add("spread");
487
504
  const svgFlag = node.svg ? ", isSVG: true" : "";
488
- const customFlag = node.tag.includes("-") ? ", isCustomElement: true" : "";
505
+ const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
506
+ const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
489
507
  parts.push(
490
508
  `spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
491
509
  );