@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.
- package/dist/analyze/intrinsics.cjs +21 -1
- package/dist/analyze/intrinsics.d.ts.map +1 -1
- package/dist/analyze/intrinsics.js +20 -0
- package/dist/analyze/intrinsics.mjs +21 -1
- package/dist/analyze/reactive-bindings.cjs +21 -1
- package/dist/analyze/reactive-bindings.mjs +21 -1
- package/dist/analyze/static-graph.cjs +25 -5
- package/dist/analyze/static-graph.d.ts.map +1 -1
- package/dist/analyze/static-graph.js +6 -4
- package/dist/analyze/static-graph.mjs +25 -5
- package/dist/{babel-VBLQRHQ2.mjs → babel-RJHJQVEM.mjs} +4 -4
- package/dist/{chunk-D53J7ICY.mjs → chunk-56QSOVVL.mjs} +1 -1
- package/dist/{chunk-S4J4OCWK.mjs → chunk-OD53TMER.mjs} +22 -2
- package/dist/{chunk-TWDJUXWZ.mjs → chunk-R7PPFBCL.mjs} +1 -1
- package/dist/{chunk-QX6Y3OAR.mjs → chunk-TJIH7TLX.mjs} +1 -1
- package/dist/{chunk-3JWMCK4A.mjs → chunk-U7HCFLZ2.mjs} +153 -48
- package/dist/codegen/backends/imperative.cjs +27 -7
- package/dist/codegen/backends/imperative.d.ts.map +1 -1
- package/dist/codegen/backends/imperative.js +19 -4
- package/dist/codegen/backends/imperative.mjs +27 -7
- package/dist/frontend/babel/build-ir-lit.cjs +123 -44
- package/dist/frontend/babel/build-ir-lit.mjs +123 -44
- package/dist/frontend/babel/build-ir.cjs +123 -44
- package/dist/frontend/babel/build-ir.mjs +123 -44
- package/dist/frontend/babel/index.cjs +124 -45
- package/dist/frontend/babel/index.mjs +124 -45
- package/dist/frontend/babel/lower-template.cjs +123 -44
- package/dist/frontend/babel/lower-template.mjs +123 -44
- package/dist/frontend/babel/plugin.cjs +123 -44
- package/dist/frontend/babel/plugin.mjs +123 -44
- package/dist/index.cjs +86 -46
- package/dist/index.mjs +88 -48
- package/dist/integrations.cjs +246 -61
- package/dist/integrations.mjs +53 -19
- package/dist/lower/compile-template.cjs +44 -39
- package/dist/lower/compile-template.mjs +44 -39
- package/dist/lower/jsx.cjs +509 -6
- package/dist/lower/jsx.d.ts.map +1 -1
- package/dist/lower/jsx.js +114 -6
- package/dist/lower/jsx.mjs +509 -6
- package/dist/lower/template.cjs +43 -38
- package/dist/lower/template.d.ts.map +1 -1
- package/dist/lower/template.js +22 -30
- package/dist/lower/template.mjs +43 -38
- package/dist/resolve/builtins.cjs +1 -1
- package/dist/resolve/builtins.mjs +1 -1
- package/dist/resolve/component-globals.cjs +33 -8
- package/dist/resolve/component-globals.d.cts +8 -0
- package/dist/resolve/component-globals.d.ts +8 -0
- package/dist/resolve/component-globals.d.ts.map +1 -1
- package/dist/resolve/component-globals.js +21 -12
- package/dist/resolve/component-globals.mjs +33 -8
- package/dist/resolve/write-globals.cjs +59 -8
- package/dist/resolve/write-globals.d.cts +6 -0
- package/dist/resolve/write-globals.d.ts +6 -0
- package/dist/resolve/write-globals.d.ts.map +1 -1
- package/dist/resolve/write-globals.js +35 -2
- package/dist/resolve/write-globals.mjs +61 -10
- package/dist/{server-fns-J56G3PFP.mjs → server-fns-ZFRDIQ2X.mjs} +4 -4
- package/dist/{server-functions-7PUFLRGF.mjs → server-functions-CTDXX67X.mjs} +3 -3
- package/dist/transform/index.cjs +199 -49
- package/dist/transform/index.mjs +199 -49
- package/dist/transform/intrinsics.cjs +47 -2
- package/dist/transform/intrinsics.d.cts +12 -0
- package/dist/transform/intrinsics.d.ts +12 -0
- package/dist/transform/intrinsics.d.ts.map +1 -1
- package/dist/transform/intrinsics.js +43 -1
- package/dist/transform/intrinsics.mjs +47 -2
- package/dist/transform/source-locations.cjs +67 -4
- package/dist/transform/source-locations.d.cts +7 -2
- package/dist/transform/source-locations.d.ts +7 -2
- package/dist/transform/source-locations.d.ts.map +1 -1
- package/dist/transform/source-locations.js +68 -3
- package/dist/transform/source-locations.mjs +67 -4
- package/dist/transform/templates.cjs +198 -48
- package/dist/transform/templates.d.ts.map +1 -1
- package/dist/transform/templates.js +5 -3
- package/dist/transform/templates.mjs +198 -48
- package/dist/{transform-D536WXGM.mjs → transform-QWWQDCXE.mjs} +33 -8
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +9 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @fluixi/compiler v1.0.0-alpha.
|
|
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;
|
|
@@ -476,6 +476,7 @@ var INTRINSICS = {
|
|
|
476
476
|
$effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
|
|
477
477
|
$store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
|
|
478
478
|
$resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
|
|
479
|
+
$cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
|
|
479
480
|
// No short runtime form for these yet, so the intrinsic stands for the `create*`
|
|
480
481
|
// name. Still worth having: no import to write, and when a shorter form is
|
|
481
482
|
// designed, the change is this line rather than a compiler pass.
|
|
@@ -485,7 +486,22 @@ var INTRINSICS = {
|
|
|
485
486
|
// does. `untrack` suppresses dependency collection, which is exactly the kind of thing
|
|
486
487
|
// dependency analysis must not guess at.
|
|
487
488
|
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
488
|
-
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" }
|
|
489
|
+
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
|
|
490
|
+
// Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
|
|
491
|
+
// no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
|
|
492
|
+
$batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
|
|
493
|
+
$cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
|
|
494
|
+
$root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
|
|
495
|
+
$context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
|
|
496
|
+
$renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
|
|
497
|
+
$computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
|
|
498
|
+
// Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
|
|
499
|
+
// handle as far as reads go: the same accessor with `set` on it.
|
|
500
|
+
$storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
501
|
+
$asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
502
|
+
$persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
|
|
503
|
+
// An environment variable, public or server-only; the browser build refuses a secret.
|
|
504
|
+
$env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
|
|
489
505
|
};
|
|
490
506
|
var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join("|")})\\s*\\(`);
|
|
491
507
|
|
|
@@ -661,13 +677,16 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
661
677
|
used.add("createComponent");
|
|
662
678
|
const spreads = props.filter((p) => p.kind === "spread");
|
|
663
679
|
const normal = props.filter((p) => p.kind !== "spread");
|
|
664
|
-
const
|
|
665
|
-
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
666
|
-
);
|
|
680
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
667
681
|
if (children.length > 0) {
|
|
668
|
-
|
|
682
|
+
bodies.push(["children", emitChildren(children, used, templates)]);
|
|
683
|
+
}
|
|
684
|
+
let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
|
|
685
|
+
if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
|
|
686
|
+
const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
|
|
687
|
+
const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
|
|
688
|
+
obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
|
|
669
689
|
}
|
|
670
|
-
let obj = `{ ${entries.join(", ")} }`;
|
|
671
690
|
if (spreads.length > 0) {
|
|
672
691
|
used.add("mergeProps");
|
|
673
692
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
@@ -749,7 +768,8 @@ function emitNode(node, used, templates) {
|
|
|
749
768
|
} else {
|
|
750
769
|
used.add("spread");
|
|
751
770
|
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
752
|
-
const
|
|
771
|
+
const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
|
|
772
|
+
const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
|
|
753
773
|
parts.push(
|
|
754
774
|
`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
|
|
755
775
|
);
|
|
@@ -827,6 +847,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
|
|
|
827
847
|
function escapeTemplate(s) {
|
|
828
848
|
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
829
849
|
}
|
|
850
|
+
var CONTAINER = "container";
|
|
830
851
|
function capitalize(s) {
|
|
831
852
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
832
853
|
}
|
|
@@ -881,10 +902,6 @@ var Lowerer = class {
|
|
|
881
902
|
if (hasElse) i = elseIdx;
|
|
882
903
|
continue;
|
|
883
904
|
}
|
|
884
|
-
if (child.attributes.some((a) => a.kind === "EachDirective")) {
|
|
885
|
-
out.push(this.lowerEach(child));
|
|
886
|
-
continue;
|
|
887
|
-
}
|
|
888
905
|
}
|
|
889
906
|
const node = this.lowerNode(child);
|
|
890
907
|
if (node) out.push(node);
|
|
@@ -926,13 +943,16 @@ var Lowerer = class {
|
|
|
926
943
|
node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
|
|
927
944
|
return node;
|
|
928
945
|
}
|
|
946
|
+
const each = this.lowerEach(el);
|
|
947
|
+
const children = each ? [each] : this.lowerChildren(el.children);
|
|
948
|
+
if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
929
949
|
const at = this.tagAt(el);
|
|
930
950
|
return {
|
|
931
951
|
kind: "element",
|
|
932
952
|
tag: el.tag,
|
|
933
953
|
svg: el.namespace === "svg",
|
|
934
954
|
props: this.lowerAttributes(el.attributes),
|
|
935
|
-
children
|
|
955
|
+
children,
|
|
936
956
|
static: false,
|
|
937
957
|
...at ? { at } : {}
|
|
938
958
|
};
|
|
@@ -1004,10 +1024,14 @@ var Lowerer = class {
|
|
|
1004
1024
|
const at = this.tagAt(el);
|
|
1005
1025
|
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
1006
1026
|
}
|
|
1007
|
-
/**
|
|
1027
|
+
/**
|
|
1028
|
+
* `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
|
|
1029
|
+
* content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
|
|
1030
|
+
* it is. Null when the element has no `each`.
|
|
1031
|
+
*/
|
|
1008
1032
|
lowerEach(el) {
|
|
1009
1033
|
const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
|
|
1010
|
-
if (!eachDir || eachDir.kind !== "EachDirective") return
|
|
1034
|
+
if (!eachDir || eachDir.kind !== "EachDirective") return null;
|
|
1011
1035
|
const each = this.hole(eachDir.hole);
|
|
1012
1036
|
const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
|
|
1013
1037
|
if (eachDir.key) {
|
|
@@ -1017,12 +1041,12 @@ var Lowerer = class {
|
|
|
1017
1041
|
const arrow = this.itemArrow(el);
|
|
1018
1042
|
let childrenCode;
|
|
1019
1043
|
if (arrow) {
|
|
1020
|
-
const
|
|
1021
|
-
|
|
1022
|
-
childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
|
|
1044
|
+
const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
|
|
1045
|
+
childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
|
|
1023
1046
|
} else {
|
|
1024
|
-
const
|
|
1025
|
-
|
|
1047
|
+
const content = this.lowerChildren(el.children);
|
|
1048
|
+
const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
|
|
1049
|
+
childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
|
|
1026
1050
|
}
|
|
1027
1051
|
const children = [{ kind: "expr", code: childrenCode, reactive: false }];
|
|
1028
1052
|
const at = this.tagAt(el);
|
|
@@ -1039,25 +1063,6 @@ var Lowerer = class {
|
|
|
1039
1063
|
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
1040
1064
|
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
1041
1065
|
}
|
|
1042
|
-
/** Same element (minus each/key) but with explicit IR children. */
|
|
1043
|
-
rebuildWithChildren(el, children) {
|
|
1044
|
-
const props = this.lowerAttributes(
|
|
1045
|
-
el.attributes.filter((a) => a.kind !== "EachDirective")
|
|
1046
|
-
);
|
|
1047
|
-
if (el.kind === "Element") {
|
|
1048
|
-
const at = this.tagAt(el);
|
|
1049
|
-
return {
|
|
1050
|
-
kind: "element",
|
|
1051
|
-
tag: el.tag,
|
|
1052
|
-
svg: el.namespace === "svg",
|
|
1053
|
-
props,
|
|
1054
|
-
children,
|
|
1055
|
-
static: false,
|
|
1056
|
-
...at ? { at } : {}
|
|
1057
|
-
};
|
|
1058
|
-
}
|
|
1059
|
-
return { kind: "component", name: el.tag, props, children };
|
|
1060
|
-
}
|
|
1061
1066
|
// --- attributes ---------------------------------------------------------
|
|
1062
1067
|
lowerAttributes(attrs) {
|
|
1063
1068
|
const props = [];
|
|
@@ -1469,6 +1474,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
|
|
|
1469
1474
|
"mouseup"
|
|
1470
1475
|
]);
|
|
1471
1476
|
var isReactive = (node) => isReactiveShape(shapeOf(node));
|
|
1477
|
+
var CONTAINER2 = "container";
|
|
1478
|
+
function directive(node, name) {
|
|
1479
|
+
if (node.type !== "JSXElement") return void 0;
|
|
1480
|
+
const opening = node.openingElement;
|
|
1481
|
+
if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
|
|
1482
|
+
return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
|
|
1483
|
+
}
|
|
1484
|
+
function directiveExpr(attr) {
|
|
1485
|
+
const v = attr.value;
|
|
1486
|
+
return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
|
|
1487
|
+
}
|
|
1488
|
+
function emitIR(ir, ctx) {
|
|
1489
|
+
const { code, imports, templates } = imperativeBackend.emit(ir, {});
|
|
1490
|
+
for (const s of imports) ctx.used.add(s);
|
|
1491
|
+
return inlineTemplates(code, templates);
|
|
1492
|
+
}
|
|
1472
1493
|
var posOf = (node, ctx) => {
|
|
1473
1494
|
if (!ctx.sourceFile) return void 0;
|
|
1474
1495
|
const at = node.loc?.start;
|
|
@@ -1577,7 +1598,16 @@ function buildProps(attrs, ctx) {
|
|
|
1577
1598
|
}
|
|
1578
1599
|
function buildChildren(children, ctx) {
|
|
1579
1600
|
const out = [];
|
|
1580
|
-
for (
|
|
1601
|
+
for (let i = 0; i < children.length; i++) {
|
|
1602
|
+
const child = children[i];
|
|
1603
|
+
if (directive(child, "if")) {
|
|
1604
|
+
let next = i + 1;
|
|
1605
|
+
while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
|
|
1606
|
+
const fallback = directive(children[next], "else") ? children[next] : null;
|
|
1607
|
+
out.push(buildIf(child, fallback, ctx));
|
|
1608
|
+
if (fallback) i = next;
|
|
1609
|
+
continue;
|
|
1610
|
+
}
|
|
1581
1611
|
if (child.type === "JSXText") {
|
|
1582
1612
|
const text = cleanTemplateText(child.value);
|
|
1583
1613
|
if (text) out.push({ kind: "text", value: text });
|
|
@@ -1614,19 +1644,68 @@ function readLoad(attrs) {
|
|
|
1614
1644
|
}
|
|
1615
1645
|
return void 0;
|
|
1616
1646
|
}
|
|
1647
|
+
function buildIf(node, fallback, ctx) {
|
|
1648
|
+
const cond = directiveExpr(directive(node, "if"));
|
|
1649
|
+
const props = [
|
|
1650
|
+
cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
|
|
1651
|
+
];
|
|
1652
|
+
if (fallback) {
|
|
1653
|
+
props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
|
|
1654
|
+
}
|
|
1655
|
+
return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
|
|
1656
|
+
}
|
|
1657
|
+
function buildEach(node, ctx) {
|
|
1658
|
+
const attr = directive(node, "each");
|
|
1659
|
+
if (!attr) return null;
|
|
1660
|
+
const list = directiveExpr(attr);
|
|
1661
|
+
const props = [
|
|
1662
|
+
list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
|
|
1663
|
+
];
|
|
1664
|
+
const key = directive(node, "key");
|
|
1665
|
+
if (key) {
|
|
1666
|
+
const keyExpr = directiveExpr(key);
|
|
1667
|
+
const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
|
|
1668
|
+
if (by) props.push({ name: "by", kind: "attr", expr: by });
|
|
1669
|
+
}
|
|
1670
|
+
const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
|
|
1671
|
+
const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
|
|
1672
|
+
let render;
|
|
1673
|
+
if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
|
|
1674
|
+
const fn = ctx.code(only);
|
|
1675
|
+
const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
|
|
1676
|
+
render = live ? `(...args) => () => (${fn})(...args)` : fn;
|
|
1677
|
+
} else {
|
|
1678
|
+
const built = buildChildren(node.children, ctx);
|
|
1679
|
+
const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
|
|
1680
|
+
render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
|
|
1681
|
+
}
|
|
1682
|
+
return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
|
|
1683
|
+
}
|
|
1684
|
+
function buildElement(node, ctx, handled) {
|
|
1685
|
+
const skip = new Set(handled);
|
|
1686
|
+
const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
|
|
1687
|
+
const attributes = node.openingElement.attributes.filter(
|
|
1688
|
+
(a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
|
|
1689
|
+
);
|
|
1690
|
+
const { tag } = tagInfo(node.openingElement.name, ctx);
|
|
1691
|
+
const each = buildEach(node, ctx);
|
|
1692
|
+
const children = each ? [each] : buildChildren(node.children, ctx);
|
|
1693
|
+
if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
1694
|
+
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1695
|
+
return { kind: "element", tag, svg: SVG_TAGS.has(tag), props: buildProps(attributes, ctx), children, static: false, ...at ? { at } : {} };
|
|
1696
|
+
}
|
|
1617
1697
|
function buildNode(node, ctx) {
|
|
1618
1698
|
if (node.type === "JSXFragment") {
|
|
1619
1699
|
return { kind: "fragment", children: buildChildren(node.children, ctx) };
|
|
1620
1700
|
}
|
|
1701
|
+
if (directive(node, "if")) return buildIf(node, null, ctx);
|
|
1621
1702
|
const { tag, component } = tagInfo(node.openingElement.name, ctx);
|
|
1703
|
+
if (!component) return buildElement(node, ctx, []);
|
|
1622
1704
|
const props = buildProps(node.openingElement.attributes, ctx);
|
|
1623
1705
|
const children = buildChildren(node.children, ctx);
|
|
1624
1706
|
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1628
|
-
}
|
|
1629
|
-
return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
|
|
1707
|
+
const load = readLoad(node.openingElement.attributes);
|
|
1708
|
+
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1630
1709
|
}
|
|
1631
1710
|
function buildJsxIR(node, ctx) {
|
|
1632
1711
|
const ir = buildNode(node, ctx);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @fluixi/compiler v1.0.0-alpha.
|
|
1
|
+
/*! @fluixi/compiler v1.0.0-alpha.93 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
2
|
|
|
3
3
|
// src/frontend/babel/plugin.ts
|
|
4
4
|
import { declare } from "@babel/helper-plugin-utils";
|
|
@@ -437,6 +437,7 @@ var INTRINSICS = {
|
|
|
437
437
|
$effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
|
|
438
438
|
$store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
|
|
439
439
|
$resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
|
|
440
|
+
$cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
|
|
440
441
|
// No short runtime form for these yet, so the intrinsic stands for the `create*`
|
|
441
442
|
// name. Still worth having: no import to write, and when a shorter form is
|
|
442
443
|
// designed, the change is this line rather than a compiler pass.
|
|
@@ -446,7 +447,22 @@ var INTRINSICS = {
|
|
|
446
447
|
// does. `untrack` suppresses dependency collection, which is exactly the kind of thing
|
|
447
448
|
// dependency analysis must not guess at.
|
|
448
449
|
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
449
|
-
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" }
|
|
450
|
+
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
|
|
451
|
+
// Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
|
|
452
|
+
// no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
|
|
453
|
+
$batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
|
|
454
|
+
$cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
|
|
455
|
+
$root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
|
|
456
|
+
$context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
|
|
457
|
+
$renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
|
|
458
|
+
$computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
|
|
459
|
+
// Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
|
|
460
|
+
// handle as far as reads go: the same accessor with `set` on it.
|
|
461
|
+
$storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
462
|
+
$asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
463
|
+
$persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
|
|
464
|
+
// An environment variable, public or server-only; the browser build refuses a secret.
|
|
465
|
+
$env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
|
|
450
466
|
};
|
|
451
467
|
var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join("|")})\\s*\\(`);
|
|
452
468
|
|
|
@@ -622,13 +638,16 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
622
638
|
used.add("createComponent");
|
|
623
639
|
const spreads = props.filter((p) => p.kind === "spread");
|
|
624
640
|
const normal = props.filter((p) => p.kind !== "spread");
|
|
625
|
-
const
|
|
626
|
-
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
627
|
-
);
|
|
641
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
628
642
|
if (children.length > 0) {
|
|
629
|
-
|
|
643
|
+
bodies.push(["children", emitChildren(children, used, templates)]);
|
|
644
|
+
}
|
|
645
|
+
let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
|
|
646
|
+
if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
|
|
647
|
+
const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
|
|
648
|
+
const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
|
|
649
|
+
obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
|
|
630
650
|
}
|
|
631
|
-
let obj = `{ ${entries.join(", ")} }`;
|
|
632
651
|
if (spreads.length > 0) {
|
|
633
652
|
used.add("mergeProps");
|
|
634
653
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
@@ -710,7 +729,8 @@ function emitNode(node, used, templates) {
|
|
|
710
729
|
} else {
|
|
711
730
|
used.add("spread");
|
|
712
731
|
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
713
|
-
const
|
|
732
|
+
const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
|
|
733
|
+
const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
|
|
714
734
|
parts.push(
|
|
715
735
|
`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
|
|
716
736
|
);
|
|
@@ -790,6 +810,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
|
|
|
790
810
|
function escapeTemplate(s) {
|
|
791
811
|
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
792
812
|
}
|
|
813
|
+
var CONTAINER = "container";
|
|
793
814
|
function capitalize(s) {
|
|
794
815
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
795
816
|
}
|
|
@@ -844,10 +865,6 @@ var Lowerer = class {
|
|
|
844
865
|
if (hasElse) i = elseIdx;
|
|
845
866
|
continue;
|
|
846
867
|
}
|
|
847
|
-
if (child.attributes.some((a) => a.kind === "EachDirective")) {
|
|
848
|
-
out.push(this.lowerEach(child));
|
|
849
|
-
continue;
|
|
850
|
-
}
|
|
851
868
|
}
|
|
852
869
|
const node = this.lowerNode(child);
|
|
853
870
|
if (node) out.push(node);
|
|
@@ -889,13 +906,16 @@ var Lowerer = class {
|
|
|
889
906
|
node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
|
|
890
907
|
return node;
|
|
891
908
|
}
|
|
909
|
+
const each = this.lowerEach(el);
|
|
910
|
+
const children = each ? [each] : this.lowerChildren(el.children);
|
|
911
|
+
if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
892
912
|
const at = this.tagAt(el);
|
|
893
913
|
return {
|
|
894
914
|
kind: "element",
|
|
895
915
|
tag: el.tag,
|
|
896
916
|
svg: el.namespace === "svg",
|
|
897
917
|
props: this.lowerAttributes(el.attributes),
|
|
898
|
-
children
|
|
918
|
+
children,
|
|
899
919
|
static: false,
|
|
900
920
|
...at ? { at } : {}
|
|
901
921
|
};
|
|
@@ -967,10 +987,14 @@ var Lowerer = class {
|
|
|
967
987
|
const at = this.tagAt(el);
|
|
968
988
|
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
969
989
|
}
|
|
970
|
-
/**
|
|
990
|
+
/**
|
|
991
|
+
* `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
|
|
992
|
+
* content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
|
|
993
|
+
* it is. Null when the element has no `each`.
|
|
994
|
+
*/
|
|
971
995
|
lowerEach(el) {
|
|
972
996
|
const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
|
|
973
|
-
if (!eachDir || eachDir.kind !== "EachDirective") return
|
|
997
|
+
if (!eachDir || eachDir.kind !== "EachDirective") return null;
|
|
974
998
|
const each = this.hole(eachDir.hole);
|
|
975
999
|
const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
|
|
976
1000
|
if (eachDir.key) {
|
|
@@ -980,12 +1004,12 @@ var Lowerer = class {
|
|
|
980
1004
|
const arrow = this.itemArrow(el);
|
|
981
1005
|
let childrenCode;
|
|
982
1006
|
if (arrow) {
|
|
983
|
-
const
|
|
984
|
-
|
|
985
|
-
childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
|
|
1007
|
+
const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
|
|
1008
|
+
childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
|
|
986
1009
|
} else {
|
|
987
|
-
const
|
|
988
|
-
|
|
1010
|
+
const content = this.lowerChildren(el.children);
|
|
1011
|
+
const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
|
|
1012
|
+
childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
|
|
989
1013
|
}
|
|
990
1014
|
const children = [{ kind: "expr", code: childrenCode, reactive: false }];
|
|
991
1015
|
const at = this.tagAt(el);
|
|
@@ -1002,25 +1026,6 @@ var Lowerer = class {
|
|
|
1002
1026
|
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
1003
1027
|
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
1004
1028
|
}
|
|
1005
|
-
/** Same element (minus each/key) but with explicit IR children. */
|
|
1006
|
-
rebuildWithChildren(el, children) {
|
|
1007
|
-
const props = this.lowerAttributes(
|
|
1008
|
-
el.attributes.filter((a) => a.kind !== "EachDirective")
|
|
1009
|
-
);
|
|
1010
|
-
if (el.kind === "Element") {
|
|
1011
|
-
const at = this.tagAt(el);
|
|
1012
|
-
return {
|
|
1013
|
-
kind: "element",
|
|
1014
|
-
tag: el.tag,
|
|
1015
|
-
svg: el.namespace === "svg",
|
|
1016
|
-
props,
|
|
1017
|
-
children,
|
|
1018
|
-
static: false,
|
|
1019
|
-
...at ? { at } : {}
|
|
1020
|
-
};
|
|
1021
|
-
}
|
|
1022
|
-
return { kind: "component", name: el.tag, props, children };
|
|
1023
|
-
}
|
|
1024
1029
|
// --- attributes ---------------------------------------------------------
|
|
1025
1030
|
lowerAttributes(attrs) {
|
|
1026
1031
|
const props = [];
|
|
@@ -1432,6 +1437,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
|
|
|
1432
1437
|
"mouseup"
|
|
1433
1438
|
]);
|
|
1434
1439
|
var isReactive = (node) => isReactiveShape(shapeOf(node));
|
|
1440
|
+
var CONTAINER2 = "container";
|
|
1441
|
+
function directive(node, name) {
|
|
1442
|
+
if (node.type !== "JSXElement") return void 0;
|
|
1443
|
+
const opening = node.openingElement;
|
|
1444
|
+
if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
|
|
1445
|
+
return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
|
|
1446
|
+
}
|
|
1447
|
+
function directiveExpr(attr) {
|
|
1448
|
+
const v = attr.value;
|
|
1449
|
+
return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
|
|
1450
|
+
}
|
|
1451
|
+
function emitIR(ir, ctx) {
|
|
1452
|
+
const { code, imports, templates } = imperativeBackend.emit(ir, {});
|
|
1453
|
+
for (const s of imports) ctx.used.add(s);
|
|
1454
|
+
return inlineTemplates(code, templates);
|
|
1455
|
+
}
|
|
1435
1456
|
var posOf = (node, ctx) => {
|
|
1436
1457
|
if (!ctx.sourceFile) return void 0;
|
|
1437
1458
|
const at = node.loc?.start;
|
|
@@ -1540,7 +1561,16 @@ function buildProps(attrs, ctx) {
|
|
|
1540
1561
|
}
|
|
1541
1562
|
function buildChildren(children, ctx) {
|
|
1542
1563
|
const out = [];
|
|
1543
|
-
for (
|
|
1564
|
+
for (let i = 0; i < children.length; i++) {
|
|
1565
|
+
const child = children[i];
|
|
1566
|
+
if (directive(child, "if")) {
|
|
1567
|
+
let next = i + 1;
|
|
1568
|
+
while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
|
|
1569
|
+
const fallback = directive(children[next], "else") ? children[next] : null;
|
|
1570
|
+
out.push(buildIf(child, fallback, ctx));
|
|
1571
|
+
if (fallback) i = next;
|
|
1572
|
+
continue;
|
|
1573
|
+
}
|
|
1544
1574
|
if (child.type === "JSXText") {
|
|
1545
1575
|
const text = cleanTemplateText(child.value);
|
|
1546
1576
|
if (text) out.push({ kind: "text", value: text });
|
|
@@ -1577,19 +1607,68 @@ function readLoad(attrs) {
|
|
|
1577
1607
|
}
|
|
1578
1608
|
return void 0;
|
|
1579
1609
|
}
|
|
1610
|
+
function buildIf(node, fallback, ctx) {
|
|
1611
|
+
const cond = directiveExpr(directive(node, "if"));
|
|
1612
|
+
const props = [
|
|
1613
|
+
cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
|
|
1614
|
+
];
|
|
1615
|
+
if (fallback) {
|
|
1616
|
+
props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
|
|
1617
|
+
}
|
|
1618
|
+
return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
|
|
1619
|
+
}
|
|
1620
|
+
function buildEach(node, ctx) {
|
|
1621
|
+
const attr = directive(node, "each");
|
|
1622
|
+
if (!attr) return null;
|
|
1623
|
+
const list = directiveExpr(attr);
|
|
1624
|
+
const props = [
|
|
1625
|
+
list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
|
|
1626
|
+
];
|
|
1627
|
+
const key = directive(node, "key");
|
|
1628
|
+
if (key) {
|
|
1629
|
+
const keyExpr = directiveExpr(key);
|
|
1630
|
+
const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
|
|
1631
|
+
if (by) props.push({ name: "by", kind: "attr", expr: by });
|
|
1632
|
+
}
|
|
1633
|
+
const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
|
|
1634
|
+
const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
|
|
1635
|
+
let render;
|
|
1636
|
+
if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
|
|
1637
|
+
const fn = ctx.code(only);
|
|
1638
|
+
const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
|
|
1639
|
+
render = live ? `(...args) => () => (${fn})(...args)` : fn;
|
|
1640
|
+
} else {
|
|
1641
|
+
const built = buildChildren(node.children, ctx);
|
|
1642
|
+
const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
|
|
1643
|
+
render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
|
|
1644
|
+
}
|
|
1645
|
+
return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
|
|
1646
|
+
}
|
|
1647
|
+
function buildElement(node, ctx, handled) {
|
|
1648
|
+
const skip = new Set(handled);
|
|
1649
|
+
const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
|
|
1650
|
+
const attributes = node.openingElement.attributes.filter(
|
|
1651
|
+
(a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
|
|
1652
|
+
);
|
|
1653
|
+
const { tag } = tagInfo(node.openingElement.name, ctx);
|
|
1654
|
+
const each = buildEach(node, ctx);
|
|
1655
|
+
const children = each ? [each] : buildChildren(node.children, ctx);
|
|
1656
|
+
if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
1657
|
+
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1658
|
+
return { kind: "element", tag, svg: SVG_TAGS.has(tag), props: buildProps(attributes, ctx), children, static: false, ...at ? { at } : {} };
|
|
1659
|
+
}
|
|
1580
1660
|
function buildNode(node, ctx) {
|
|
1581
1661
|
if (node.type === "JSXFragment") {
|
|
1582
1662
|
return { kind: "fragment", children: buildChildren(node.children, ctx) };
|
|
1583
1663
|
}
|
|
1664
|
+
if (directive(node, "if")) return buildIf(node, null, ctx);
|
|
1584
1665
|
const { tag, component } = tagInfo(node.openingElement.name, ctx);
|
|
1666
|
+
if (!component) return buildElement(node, ctx, []);
|
|
1585
1667
|
const props = buildProps(node.openingElement.attributes, ctx);
|
|
1586
1668
|
const children = buildChildren(node.children, ctx);
|
|
1587
1669
|
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1591
|
-
}
|
|
1592
|
-
return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
|
|
1670
|
+
const load = readLoad(node.openingElement.attributes);
|
|
1671
|
+
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1593
1672
|
}
|
|
1594
1673
|
function buildJsxIR(node, ctx) {
|
|
1595
1674
|
const ir = buildNode(node, ctx);
|