@fluixi/compiler 1.0.0-alpha.91 → 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 (81) hide show
  1. package/dist/analyze/intrinsics.cjs +21 -1
  2. package/dist/analyze/intrinsics.d.ts.map +1 -1
  3. package/dist/analyze/intrinsics.js +20 -0
  4. package/dist/analyze/intrinsics.mjs +21 -1
  5. package/dist/analyze/reactive-bindings.cjs +21 -1
  6. package/dist/analyze/reactive-bindings.mjs +21 -1
  7. package/dist/analyze/static-graph.cjs +25 -5
  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 +25 -5
  11. package/dist/{babel-VBLQRHQ2.mjs → babel-RJHJQVEM.mjs} +4 -4
  12. package/dist/{chunk-D53J7ICY.mjs → chunk-56QSOVVL.mjs} +1 -1
  13. package/dist/{chunk-S4J4OCWK.mjs → chunk-OD53TMER.mjs} +22 -2
  14. package/dist/{chunk-TWDJUXWZ.mjs → chunk-R7PPFBCL.mjs} +1 -1
  15. package/dist/{chunk-QX6Y3OAR.mjs → chunk-TJIH7TLX.mjs} +1 -1
  16. package/dist/{chunk-3JWMCK4A.mjs → chunk-U7HCFLZ2.mjs} +153 -48
  17. package/dist/codegen/backends/imperative.cjs +27 -7
  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 +27 -7
  21. package/dist/frontend/babel/build-ir-lit.cjs +123 -44
  22. package/dist/frontend/babel/build-ir-lit.mjs +123 -44
  23. package/dist/frontend/babel/build-ir.cjs +123 -44
  24. package/dist/frontend/babel/build-ir.mjs +123 -44
  25. package/dist/frontend/babel/index.cjs +124 -45
  26. package/dist/frontend/babel/index.mjs +124 -45
  27. package/dist/frontend/babel/lower-template.cjs +123 -44
  28. package/dist/frontend/babel/lower-template.mjs +123 -44
  29. package/dist/frontend/babel/plugin.cjs +123 -44
  30. package/dist/frontend/babel/plugin.mjs +123 -44
  31. package/dist/index.cjs +86 -46
  32. package/dist/index.mjs +88 -48
  33. package/dist/integrations.cjs +246 -61
  34. package/dist/integrations.mjs +53 -19
  35. package/dist/lower/compile-template.cjs +44 -39
  36. package/dist/lower/compile-template.mjs +44 -39
  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 +43 -38
  42. package/dist/lower/template.d.ts.map +1 -1
  43. package/dist/lower/template.js +22 -30
  44. package/dist/lower/template.mjs +43 -38
  45. package/dist/resolve/builtins.cjs +1 -1
  46. package/dist/resolve/builtins.mjs +1 -1
  47. package/dist/resolve/component-globals.cjs +33 -8
  48. package/dist/resolve/component-globals.d.cts +8 -0
  49. package/dist/resolve/component-globals.d.ts +8 -0
  50. package/dist/resolve/component-globals.d.ts.map +1 -1
  51. package/dist/resolve/component-globals.js +21 -12
  52. package/dist/resolve/component-globals.mjs +33 -8
  53. package/dist/resolve/write-globals.cjs +59 -8
  54. package/dist/resolve/write-globals.d.cts +6 -0
  55. package/dist/resolve/write-globals.d.ts +6 -0
  56. package/dist/resolve/write-globals.d.ts.map +1 -1
  57. package/dist/resolve/write-globals.js +35 -2
  58. package/dist/resolve/write-globals.mjs +61 -10
  59. package/dist/{server-fns-J56G3PFP.mjs → server-fns-ZFRDIQ2X.mjs} +4 -4
  60. package/dist/{server-functions-7PUFLRGF.mjs → server-functions-CTDXX67X.mjs} +3 -3
  61. package/dist/transform/index.cjs +199 -49
  62. package/dist/transform/index.mjs +199 -49
  63. package/dist/transform/intrinsics.cjs +47 -2
  64. package/dist/transform/intrinsics.d.cts +12 -0
  65. package/dist/transform/intrinsics.d.ts +12 -0
  66. package/dist/transform/intrinsics.d.ts.map +1 -1
  67. package/dist/transform/intrinsics.js +43 -1
  68. package/dist/transform/intrinsics.mjs +47 -2
  69. package/dist/transform/source-locations.cjs +67 -4
  70. package/dist/transform/source-locations.d.cts +7 -2
  71. package/dist/transform/source-locations.d.ts +7 -2
  72. package/dist/transform/source-locations.d.ts.map +1 -1
  73. package/dist/transform/source-locations.js +68 -3
  74. package/dist/transform/source-locations.mjs +67 -4
  75. package/dist/transform/templates.cjs +198 -48
  76. package/dist/transform/templates.d.ts.map +1 -1
  77. package/dist/transform/templates.js +5 -3
  78. package/dist/transform/templates.mjs +198 -48
  79. package/dist/{transform-D536WXGM.mjs → transform-QWWQDCXE.mjs} +33 -8
  80. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  81. package/package.json +9 -3
@@ -1,4 +1,4 @@
1
- /*! @fluixi/compiler v1.0.0-alpha.91 | (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
  "use strict";
3
3
  var __create = Object.create;
4
4
  var __defProp = Object.defineProperty;
@@ -156,6 +156,7 @@ var init_intrinsics = __esm({
156
156
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
157
157
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
158
158
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
159
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
159
160
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
160
161
  // name. Still worth having: no import to write, and when a shorter form is
161
162
  // designed, the change is this line rather than a compiler pass.
@@ -165,7 +166,22 @@ var init_intrinsics = __esm({
165
166
  // does. `untrack` suppresses dependency collection, which is exactly the kind of thing
166
167
  // dependency analysis must not guess at.
167
168
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
168
- $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" }
169
+ $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
170
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
171
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
172
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
173
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
174
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
175
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
176
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
177
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
178
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
179
+ // handle as far as reads go: the same accessor with `set` on it.
180
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
181
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
182
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
183
+ // An environment variable, public or server-only; the browser build refuses a secret.
184
+ $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
169
185
  };
170
186
  RUNTIME_PRIMITIVES = {
171
187
  signal: "signal-handle",
@@ -176,8 +192,11 @@ var init_intrinsics = __esm({
176
192
  createMemo: "memo-accessor",
177
193
  createEffect: "effect",
178
194
  createRenderEffect: "effect",
195
+ renderEffect: "effect",
196
+ computed: "effect",
179
197
  createStore: "reactive-object",
180
198
  resource: "resource-handle",
199
+ cachedResource: "resource-handle",
181
200
  createResource: "reactive-object",
182
201
  createSelector: "memo-accessor",
183
202
  createDeferred: "memo-accessor"
@@ -186,6 +205,7 @@ var init_intrinsics = __esm({
186
205
  "@fluixi/reactive",
187
206
  "@fluixi/reactive/signal",
188
207
  "@fluixi/reactive/store",
208
+ "@fluixi/reactive/cached-resource",
189
209
  "@fluixi/core"
190
210
  ];
191
211
  INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join("|")})\\s*\\(`);
@@ -684,11 +704,12 @@ var init_reactive_bindings = __esm({
684
704
  });
685
705
 
686
706
  // src/transform/source-locations.ts
687
- function mayHaveReactiveCall(code) {
707
+ function mayHaveSourceMarks(code) {
688
708
  for (const name of RUNTIME_PRIMITIVES2) {
689
709
  if (code.includes(name)) return true;
690
710
  }
691
- return code.includes("$signal") || code.includes("$memo") || code.includes("$effect") || code.includes("$store");
711
+ if (code.includes("$signal") || code.includes("$memo") || code.includes("$effect") || code.includes("$store")) return true;
712
+ return /\bclass\b[^{;]*\bextends\b/.test(code);
692
713
  }
693
714
  function isLoneBranch(statement, parent) {
694
715
  if (!parent || statement.type === "BlockStatement") return false;
@@ -717,6 +738,14 @@ function walk3(node, visit, stack = []) {
717
738
  }
718
739
  function nameFor(stack) {
719
740
  const declarator = stack[stack.length - 1];
741
+ if (declarator && CLASS_FIELDS.has(declarator.type)) {
742
+ const key = declarator.key;
743
+ if (key?.type === "Identifier" || key?.type === "PrivateIdentifier" || key?.type === "PrivateName") {
744
+ const name = key.name ?? key.id?.name;
745
+ return name;
746
+ }
747
+ return void 0;
748
+ }
720
749
  if (declarator?.type !== "VariableDeclarator") return void 0;
721
750
  const id = declarator.id;
722
751
  if (id?.type === "Identifier") return id.name;
@@ -765,6 +794,17 @@ function collectSourceEdits(program, filename) {
765
794
  });
766
795
  }
767
796
  }
797
+ if ((node.type === "ClassDeclaration" || node.type === "ClassExpression") && node.superClass && !node.declare) {
798
+ const at = node.loc?.start;
799
+ const body = node.body;
800
+ if (at && body) {
801
+ edits.push({
802
+ start: body.start + 1,
803
+ end: body.start + 1,
804
+ code: `static{globalThis.${CLASS_MARKER}?.(this,${JSON.stringify(filename)},${at.line},${at.column});}`
805
+ });
806
+ }
807
+ }
768
808
  if (node.type !== "CallExpression") return;
769
809
  const callee = node.callee;
770
810
  if (callee?.type !== "Identifier") return;
@@ -774,6 +814,26 @@ function collectSourceEdits(program, filename) {
774
814
  if (!intrinsic && !imported) return;
775
815
  const position = node.loc?.start;
776
816
  if (!position) return;
817
+ for (let i = stack.length - 1; i >= 0; i--) {
818
+ const up = stack[i];
819
+ if (isStatement(up)) break;
820
+ if (CLASS_FIELDS.has(up.type)) {
821
+ if (claimed.has(node.start)) return;
822
+ claimed.add(node.start);
823
+ const label2 = nameFor(stack);
824
+ const own = !up.static;
825
+ const args2 = [
826
+ JSON.stringify(filename),
827
+ String(position.line),
828
+ String(position.column),
829
+ label2 ? JSON.stringify(label2) : own ? "undefined" : void 0,
830
+ own ? "this" : void 0
831
+ ].filter((a) => a !== void 0).join(",");
832
+ edits.push({ start: node.start, end: node.start, code: `(globalThis.${SOURCE_MARKER}?.(${args2}),` });
833
+ edits.push({ start: node.end, end: node.end, code: ")" });
834
+ return;
835
+ }
836
+ }
777
837
  let statement;
778
838
  let parent;
779
839
  let index = -1;
@@ -813,7 +873,7 @@ function collectSourceEdits(program, filename) {
813
873
  });
814
874
  return { edits, marked: edits.length };
815
875
  }
816
- var SOURCE_MARKER, COMPONENT_MARKER, HOLE_MARKER, markHole, PROPS_MARKER, markProps, RUNTIME_PRIMITIVES2, SINGLE_STATEMENT_PARENTS;
876
+ var SOURCE_MARKER, COMPONENT_MARKER, CLASS_MARKER, HOLE_MARKER, markHole, PROPS_MARKER, markProps, RUNTIME_PRIMITIVES2, SINGLE_STATEMENT_PARENTS, CLASS_FIELDS;
817
877
  var init_source_locations = __esm({
818
878
  "src/transform/source-locations.ts"() {
819
879
  "use strict";
@@ -821,6 +881,7 @@ var init_source_locations = __esm({
821
881
  init_reactive_bindings();
822
882
  SOURCE_MARKER = "__fx_source";
823
883
  COMPONENT_MARKER = "__fx_component";
884
+ CLASS_MARKER = "__fx_class";
824
885
  HOLE_MARKER = "__fx_hole";
825
886
  markHole = (accessor, line, column, name) => `(globalThis.${HOLE_MARKER} ?? ((f) => f))(${accessor},${line},${column}${name === void 0 ? "" : `,${JSON.stringify(name)}`})`;
826
887
  PROPS_MARKER = "__fx_props";
@@ -848,6 +909,7 @@ var init_source_locations = __esm({
848
909
  "LabeledStatement",
849
910
  "WithStatement"
850
911
  ]);
912
+ CLASS_FIELDS = /* @__PURE__ */ new Set(["PropertyDefinition", "ClassProperty", "ClassPrivateProperty", "AccessorProperty"]);
851
913
  }
852
914
  });
853
915
 
@@ -1011,13 +1073,16 @@ function emitComponent(name, props, children, used, templates, at) {
1011
1073
  used.add("createComponent");
1012
1074
  const spreads = props.filter((p) => p.kind === "spread");
1013
1075
  const normal = props.filter((p) => p.kind !== "spread");
1014
- const entries = normal.map(
1015
- (p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
1016
- );
1076
+ const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
1017
1077
  if (children.length > 0) {
1018
- entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
1078
+ bodies.push(["children", emitChildren(children, used, templates)]);
1079
+ }
1080
+ let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
1081
+ if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
1082
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
1083
+ const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
1084
+ obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
1019
1085
  }
1020
- let obj = `{ ${entries.join(", ")} }`;
1021
1086
  if (spreads.length > 0) {
1022
1087
  used.add("mergeProps");
1023
1088
  obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
@@ -1098,7 +1163,8 @@ function emitNode(node, used, templates) {
1098
1163
  } else {
1099
1164
  used.add("spread");
1100
1165
  const svgFlag = node.svg ? ", isSVG: true" : "";
1101
- const customFlag = node.tag.includes("-") ? ", isCustomElement: true" : "";
1166
+ const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
1167
+ const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
1102
1168
  parts.push(
1103
1169
  `spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
1104
1170
  );
@@ -1189,7 +1255,7 @@ function lowerTemplate(pieces, holes, used, opts = {}) {
1189
1255
  const { root } = (0, import_template_parser.parseTemplate)(pieces, { svg: opts.svg });
1190
1256
  return new Lowerer(holes, used, opts.sourceFile, opts.positionAt).lowerRoot(root.children);
1191
1257
  }
1192
- var import_template_parser, DELEGATED, Lowerer;
1258
+ var import_template_parser, DELEGATED, CONTAINER, Lowerer;
1193
1259
  var init_template = __esm({
1194
1260
  "src/lower/template.ts"() {
1195
1261
  "use strict";
@@ -1212,6 +1278,7 @@ var init_template = __esm({
1212
1278
  "mousedown",
1213
1279
  "mouseup"
1214
1280
  ]);
1281
+ CONTAINER = "container";
1215
1282
  Lowerer = class {
1216
1283
  constructor(holes, used, sourceFile, positionAt) {
1217
1284
  this.holes = holes;
@@ -1263,10 +1330,6 @@ var init_template = __esm({
1263
1330
  if (hasElse) i = elseIdx;
1264
1331
  continue;
1265
1332
  }
1266
- if (child.attributes.some((a) => a.kind === "EachDirective")) {
1267
- out.push(this.lowerEach(child));
1268
- continue;
1269
- }
1270
1333
  }
1271
1334
  const node = this.lowerNode(child);
1272
1335
  if (node) out.push(node);
@@ -1308,13 +1371,16 @@ var init_template = __esm({
1308
1371
  node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
1309
1372
  return node;
1310
1373
  }
1374
+ const each = this.lowerEach(el);
1375
+ const children = each ? [each] : this.lowerChildren(el.children);
1376
+ if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
1311
1377
  const at = this.tagAt(el);
1312
1378
  return {
1313
1379
  kind: "element",
1314
1380
  tag: el.tag,
1315
1381
  svg: el.namespace === "svg",
1316
1382
  props: this.lowerAttributes(el.attributes),
1317
- children: this.lowerChildren(el.children),
1383
+ children,
1318
1384
  static: false,
1319
1385
  ...at ? { at } : {}
1320
1386
  };
@@ -1386,10 +1452,14 @@ var init_template = __esm({
1386
1452
  const at = this.tagAt(el);
1387
1453
  return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
1388
1454
  }
1389
- /** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
1455
+ /**
1456
+ * `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
1457
+ * content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
1458
+ * it is. Null when the element has no `each`.
1459
+ */
1390
1460
  lowerEach(el) {
1391
1461
  const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
1392
- if (!eachDir || eachDir.kind !== "EachDirective") return this.lowerNode(el);
1462
+ if (!eachDir || eachDir.kind !== "EachDirective") return null;
1393
1463
  const each = this.hole(eachDir.hole);
1394
1464
  const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
1395
1465
  if (eachDir.key) {
@@ -1399,12 +1469,12 @@ var init_template = __esm({
1399
1469
  const arrow = this.itemArrow(el);
1400
1470
  let childrenCode;
1401
1471
  if (arrow) {
1402
- const bodyIR = { kind: "expr", code: arrow.body, reactive: arrow.bodyReactive };
1403
- const repeated = this.rebuildWithChildren(el, [bodyIR]);
1404
- childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
1472
+ const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
1473
+ childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
1405
1474
  } else {
1406
- const repeated = this.stripAndLower(el, (a) => a.kind === "EachDirective");
1407
- childrenCode = `() => ${paren(this.emit(repeated), "arrowBody")}`;
1475
+ const content = this.lowerChildren(el.children);
1476
+ const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
1477
+ childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
1408
1478
  }
1409
1479
  const children = [{ kind: "expr", code: childrenCode, reactive: false }];
1410
1480
  const at = this.tagAt(el);
@@ -1421,25 +1491,6 @@ var init_template = __esm({
1421
1491
  const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
1422
1492
  return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
1423
1493
  }
1424
- /** Same element (minus each/key) but with explicit IR children. */
1425
- rebuildWithChildren(el, children) {
1426
- const props = this.lowerAttributes(
1427
- el.attributes.filter((a) => a.kind !== "EachDirective")
1428
- );
1429
- if (el.kind === "Element") {
1430
- const at = this.tagAt(el);
1431
- return {
1432
- kind: "element",
1433
- tag: el.tag,
1434
- svg: el.namespace === "svg",
1435
- props,
1436
- children,
1437
- static: false,
1438
- ...at ? { at } : {}
1439
- };
1440
- }
1441
- return { kind: "component", name: el.tag, props, children };
1442
- }
1443
1494
  // --- attributes ---------------------------------------------------------
1444
1495
  lowerAttributes(attrs) {
1445
1496
  const props = [];
@@ -1853,6 +1904,21 @@ var init_expr_shape = __esm({
1853
1904
  });
1854
1905
 
1855
1906
  // src/lower/jsx.ts
1907
+ function directive(node, name) {
1908
+ if (node.type !== "JSXElement") return void 0;
1909
+ const opening = node.openingElement;
1910
+ if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
1911
+ return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
1912
+ }
1913
+ function directiveExpr(attr) {
1914
+ const v = attr.value;
1915
+ return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
1916
+ }
1917
+ function emitIR(ir, ctx) {
1918
+ const { code, imports, templates } = imperativeBackend.emit(ir, {});
1919
+ for (const s of imports) ctx.used.add(s);
1920
+ return inlineTemplates(code, templates);
1921
+ }
1856
1922
  function literalOf(node) {
1857
1923
  if (node.type === "StringLiteral" || node.type === "NumericLiteral" || node.type === "BooleanLiteral") {
1858
1924
  return node.value;
@@ -1955,7 +2021,16 @@ function buildProps(attrs, ctx) {
1955
2021
  }
1956
2022
  function buildChildren(children, ctx) {
1957
2023
  const out = [];
1958
- for (const child of children) {
2024
+ for (let i = 0; i < children.length; i++) {
2025
+ const child = children[i];
2026
+ if (directive(child, "if")) {
2027
+ let next = i + 1;
2028
+ while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
2029
+ const fallback = directive(children[next], "else") ? children[next] : null;
2030
+ out.push(buildIf(child, fallback, ctx));
2031
+ if (fallback) i = next;
2032
+ continue;
2033
+ }
1959
2034
  if (child.type === "JSXText") {
1960
2035
  const text = cleanTemplateText(child.value);
1961
2036
  if (text) out.push({ kind: "text", value: text });
@@ -1992,26 +2067,75 @@ function readLoad(attrs) {
1992
2067
  }
1993
2068
  return void 0;
1994
2069
  }
2070
+ function buildIf(node, fallback, ctx) {
2071
+ const cond = directiveExpr(directive(node, "if"));
2072
+ const props = [
2073
+ cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
2074
+ ];
2075
+ if (fallback) {
2076
+ props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
2077
+ }
2078
+ return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
2079
+ }
2080
+ function buildEach(node, ctx) {
2081
+ const attr = directive(node, "each");
2082
+ if (!attr) return null;
2083
+ const list = directiveExpr(attr);
2084
+ const props = [
2085
+ list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
2086
+ ];
2087
+ const key = directive(node, "key");
2088
+ if (key) {
2089
+ const keyExpr = directiveExpr(key);
2090
+ const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
2091
+ if (by) props.push({ name: "by", kind: "attr", expr: by });
2092
+ }
2093
+ const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
2094
+ const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
2095
+ let render;
2096
+ if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
2097
+ const fn = ctx.code(only);
2098
+ const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
2099
+ render = live ? `(...args) => () => (${fn})(...args)` : fn;
2100
+ } else {
2101
+ const built = buildChildren(node.children, ctx);
2102
+ const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
2103
+ render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
2104
+ }
2105
+ return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
2106
+ }
2107
+ function buildElement(node, ctx, handled) {
2108
+ const skip = new Set(handled);
2109
+ const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
2110
+ const attributes = node.openingElement.attributes.filter(
2111
+ (a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
2112
+ );
2113
+ const { tag } = tagInfo(node.openingElement.name, ctx);
2114
+ const each = buildEach(node, ctx);
2115
+ const children = each ? [each] : buildChildren(node.children, ctx);
2116
+ if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
2117
+ const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
2118
+ return { kind: "element", tag, svg: SVG_TAGS.has(tag), props: buildProps(attributes, ctx), children, static: false, ...at ? { at } : {} };
2119
+ }
1995
2120
  function buildNode(node, ctx) {
1996
2121
  if (node.type === "JSXFragment") {
1997
2122
  return { kind: "fragment", children: buildChildren(node.children, ctx) };
1998
2123
  }
2124
+ if (directive(node, "if")) return buildIf(node, null, ctx);
1999
2125
  const { tag, component } = tagInfo(node.openingElement.name, ctx);
2126
+ if (!component) return buildElement(node, ctx, []);
2000
2127
  const props = buildProps(node.openingElement.attributes, ctx);
2001
2128
  const children = buildChildren(node.children, ctx);
2002
2129
  const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
2003
- if (component) {
2004
- const load = readLoad(node.openingElement.attributes);
2005
- return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
2006
- }
2007
- return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
2130
+ const load = readLoad(node.openingElement.attributes);
2131
+ return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
2008
2132
  }
2009
2133
  function buildJsxIR(node, ctx) {
2010
2134
  const ir = buildNode(node, ctx);
2011
2135
  analyzeStatic(ir);
2012
2136
  return ir;
2013
2137
  }
2014
- var SVG_TAGS, EVENT_RE, DELEGATED2, isReactive, posOf, isString;
2138
+ var SVG_TAGS, EVENT_RE, DELEGATED2, isReactive, CONTAINER2, posOf, isString;
2015
2139
  var init_jsx = __esm({
2016
2140
  "src/lower/jsx.ts"() {
2017
2141
  "use strict";
@@ -2021,6 +2145,7 @@ var init_jsx = __esm({
2021
2145
  init_analyze_static();
2022
2146
  init_text();
2023
2147
  init_paren();
2148
+ init_imperative();
2024
2149
  SVG_TAGS = /* @__PURE__ */ new Set([
2025
2150
  "svg",
2026
2151
  "path",
@@ -2051,6 +2176,7 @@ var init_jsx = __esm({
2051
2176
  "mouseup"
2052
2177
  ]);
2053
2178
  isReactive = (node) => isReactiveShape(shapeOf(node));
2179
+ CONTAINER2 = "container";
2054
2180
  posOf = (node, ctx) => {
2055
2181
  if (!ctx.sourceFile) return void 0;
2056
2182
  const at = node.loc?.start;
@@ -3171,10 +3297,34 @@ function collectIntrinsicEdits(program) {
3171
3297
  });
3172
3298
  return { edits, diagnostics, imports };
3173
3299
  }
3300
+ function avoidBindingClashes(program, result, bound) {
3301
+ const importedAsIs = /* @__PURE__ */ new Set();
3302
+ for (const stmt of program.body ?? []) {
3303
+ if (stmt.type !== "ImportDeclaration") continue;
3304
+ const module2 = stmt.source.value;
3305
+ for (const spec of stmt.specifiers ?? []) {
3306
+ if (spec.type !== "ImportSpecifier") continue;
3307
+ const imported = spec.imported.name ?? spec.imported.value;
3308
+ if (imported === spec.local.name && result.imports.get(module2)?.has(imported)) {
3309
+ importedAsIs.add(imported);
3310
+ }
3311
+ }
3312
+ }
3313
+ const aliases = /* @__PURE__ */ new Map();
3314
+ for (const names of result.imports.values()) {
3315
+ for (const name of names) if (bound.has(name) && !importedAsIs.has(name)) aliases.set(name, `__fx_${name}`);
3316
+ }
3317
+ if (!aliases.size) return;
3318
+ for (const edit of result.edits) {
3319
+ const alias = aliases.get(edit.code);
3320
+ if (alias) edit.code = alias;
3321
+ }
3322
+ result.aliases = aliases;
3323
+ }
3174
3324
  function intrinsicImports(result, bound) {
3175
3325
  const lines = [];
3176
3326
  for (const [module2, names] of [...result.imports].sort(([a], [b]) => a.localeCompare(b))) {
3177
- const wanted = [...names].filter((name) => !bound.has(name)).sort();
3327
+ const wanted = [...names].filter((name) => result.aliases?.has(name) || !bound.has(name)).sort().map((name) => result.aliases?.has(name) ? `${name} as ${result.aliases.get(name)}` : name);
3178
3328
  if (wanted.length) lines.push(`import { ${wanted.join(", ")} } from ${JSON.stringify(module2)};`);
3179
3329
  }
3180
3330
  return lines.length ? `${lines.join("\n")}
@@ -3352,7 +3502,7 @@ function transformTemplates(code, id, options = {}) {
3352
3502
  const wantsJsx = format !== "lit";
3353
3503
  const mayHaveTemplate = wantsLit && (code.includes(`${litTag}\``) || code.includes("svg`"));
3354
3504
  const wantsIntrinsics = options.intrinsics !== false && mayHaveIntrinsic(code);
3355
- const wantsSources = options.sourceLocations === true && mayHaveReactiveCall(code);
3505
+ const wantsSources = options.sourceLocations === true && mayHaveSourceMarks(code);
3356
3506
  const sourceName = relativeToRoot(id, options.sourceRoot);
3357
3507
  if (!mayHaveTemplate && !(wantsJsx && code.includes("<")) && !wantsIntrinsics && !wantsSources) return null;
3358
3508
  const ast = (0, import_parser2.parse)(code, {
@@ -3367,6 +3517,7 @@ function transformTemplates(code, id, options = {}) {
3367
3517
  });
3368
3518
  const props = options.propsDestructure === false ? { edits: [], diagnostics: [], used: /* @__PURE__ */ new Set() } : collectPropsEdits(ast.program, code, { runtimeModule: options.runtimeModule });
3369
3519
  const intrinsics = options.intrinsics === false ? { edits: [], diagnostics: [], imports: /* @__PURE__ */ new Map() } : collectIntrinsicEdits(ast.program);
3520
+ if (intrinsics.edits.length) avoidBindingClashes(ast.program, intrinsics, moduleBindings(ast.program));
3370
3521
  const sources = options.sourceLocations === true ? collectSourceEdits(ast.program, sourceName) : { edits: [], marked: 0 };
3371
3522
  const silent = found.length === 0 && props.edits.length === 0 && sources.edits.length === 0 && intrinsics.edits.length === 0 && intrinsics.diagnostics.length === 0 && props.diagnostics.length === 0;
3372
3523
  if (silent) return null;
@@ -3706,7 +3857,7 @@ var server_fns_exports = {};
3706
3857
  __export(server_fns_exports, {
3707
3858
  transformServerFns: () => transformServerFns
3708
3859
  });
3709
- function directive(node) {
3860
+ function directive2(node) {
3710
3861
  if (!FUNCTION_TYPES.has(node.type)) return null;
3711
3862
  const body = node.body;
3712
3863
  if (!body || body.type !== "BlockStatement") return null;
@@ -3722,7 +3873,7 @@ function findServerFns(node, out) {
3722
3873
  return;
3723
3874
  }
3724
3875
  if (typeof node.type === "string") {
3725
- const found = directive(node);
3876
+ const found = directive2(node);
3726
3877
  if (found) {
3727
3878
  out.push({ fn: node, directive: found });
3728
3879
  return;
@@ -3839,7 +3990,8 @@ function componentGlobals(rules, options = {}) {
3839
3990
  builtins = true,
3840
3991
  intrinsics = true,
3841
3992
  generator = "@fluixi/compiler",
3842
- specifier = (module2) => module2
3993
+ specifier = (module2) => module2,
3994
+ candidates = []
3843
3995
  } = options;
3844
3996
  const all = [...rules];
3845
3997
  if (builtins) {
@@ -3851,7 +4003,8 @@ function componentGlobals(rules, options = {}) {
3851
4003
  const shadowedByDom = [];
3852
4004
  const unavailable = [];
3853
4005
  const resolved = /* @__PURE__ */ new Map();
3854
- for (const name of [...resolver.names()].sort()) {
4006
+ const names = /* @__PURE__ */ new Set([...resolver.names(), ...[...candidates].filter((n) => /^[A-Za-z_$][\w$]*$/.test(n))]);
4007
+ for (const name of [...names].sort()) {
3855
4008
  const target = resolver.resolve(name);
3856
4009
  if (!target) continue;
3857
4010
  const pkg = packageOf(target.module);
@@ -3862,7 +4015,11 @@ function componentGlobals(rules, options = {}) {
3862
4015
  resolved.set(name, target);
3863
4016
  }
3864
4017
  }
3865
- const intrinsicModules = intrinsics ? [...new Set(Object.values(INTRINSICS).map((i) => i.module))] : [];
4018
+ const usableIntrinsics = intrinsics ? Object.entries(INTRINSICS).filter(([, i]) => {
4019
+ const pkg = packageOf(i.module);
4020
+ return !available || !pkg || available.has(pkg);
4021
+ }) : [];
4022
+ const intrinsicModules = [...new Set(usableIntrinsics.map(([, i]) => i.module))];
3866
4023
  const modules = [
3867
4024
  .../* @__PURE__ */ new Set([...declared2.map((name) => resolved.get(name).module), ...intrinsicModules])
3868
4025
  ].sort();
@@ -3882,15 +4039,18 @@ function componentGlobals(rules, options = {}) {
3882
4039
  return `// import { ${n} } from '${module2 ? specifier(module2) : ""}';
3883
4040
  `;
3884
4041
  }).join("") : "";
3885
- const intrinsicLines = intrinsics ? Object.entries(INTRINSICS).map(
4042
+ const intrinsicLines = usableIntrinsics.map(
3886
4043
  ([name, intrinsic]) => ` const ${name}: typeof ${namespaceAlias(intrinsic.module)}.${intrinsic.export};`
3887
- ) : [];
4044
+ );
3888
4045
  const patternRules = rules.filter((rule) => "match" in rule).length;
3889
- const patternNote = patternRules > 0 ? `//
4046
+ const patternNote = patternRules === 0 ? "" : options.candidates ? `//
4047
+ // ${patternRules} pattern rule${patternRules === 1 ? "" : "s"}: declared for the files the module path
4048
+ // points at. A name with no such file still resolves at build time; declare it by hand.
4049
+ ` : `//
3890
4050
  // ${patternRules} pattern rule${patternRules === 1 ? "" : "s"} cannot be declared:
3891
4051
  // a pattern matches unboundedly many names, so there is no list to emit. Those
3892
4052
  // tags still resolve at build time; declare them by hand if the editor needs them.
3893
- ` : "";
4053
+ `;
3894
4054
  const source = `// GENERATED by ${generator}, do not edit.
3895
4055
  //
3896
4056
  // The components \`resolve\` supplies without an import. This describes what the
@@ -3947,6 +4107,30 @@ function projectDependencies(root) {
3947
4107
  return void 0;
3948
4108
  }
3949
4109
  }
4110
+ function patternCandidates(rules, root) {
4111
+ const out = [];
4112
+ for (const rule of rules) {
4113
+ if (!("match" in rule) || !rule.module.startsWith("/")) continue;
4114
+ const dir = (0, import_node_path.dirname)(rule.module);
4115
+ const file = (0, import_node_path.basename)(rule.module);
4116
+ const group = file.match(/\$\d+/);
4117
+ if (dir.includes("$") || !group || file.indexOf("$", group.index + 1) !== -1) continue;
4118
+ const before = file.slice(0, group.index);
4119
+ const after = file.slice(group.index + group[0].length);
4120
+ let entries;
4121
+ try {
4122
+ entries = (0, import_node_fs.readdirSync)((0, import_node_path.resolve)(root, dir.slice(1)));
4123
+ } catch {
4124
+ continue;
4125
+ }
4126
+ for (const entry of entries) {
4127
+ if (entry.length > before.length + after.length && entry.startsWith(before) && entry.endsWith(after)) {
4128
+ out.push(entry.slice(before.length, entry.length - after.length));
4129
+ }
4130
+ }
4131
+ }
4132
+ return out;
4133
+ }
3950
4134
  function writeComponentGlobals(options, root) {
3951
4135
  if (options.globals === false) return { status: "skipped", reason: "disabled" };
3952
4136
  const rules = options.resolve ?? [];
@@ -3962,6 +4146,7 @@ function writeComponentGlobals(options, root) {
3962
4146
  }
3963
4147
  const { source, declared: declared2 } = componentGlobals(rules, {
3964
4148
  available: projectDependencies(root),
4149
+ candidates: patternCandidates(rules, root),
3965
4150
  specifier: (module2) => forTypeScript(module2, (0, import_node_path.dirname)(target), root)
3966
4151
  });
3967
4152
  if (existing === source) {