@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.
- package/dist/analyze/intrinsics.cjs +18 -0
- package/dist/analyze/intrinsics.d.ts.map +1 -1
- package/dist/analyze/intrinsics.js +18 -0
- package/dist/analyze/intrinsics.mjs +18 -0
- package/dist/analyze/reactive-bindings.cjs +18 -0
- package/dist/analyze/reactive-bindings.mjs +18 -0
- package/dist/analyze/static-graph.cjs +22 -4
- package/dist/analyze/static-graph.d.ts.map +1 -1
- package/dist/analyze/static-graph.js +6 -4
- package/dist/analyze/static-graph.mjs +22 -4
- package/dist/{babel-GI375YZ2.mjs → babel-RJHJQVEM.mjs} +4 -4
- package/dist/{chunk-QZ4CJPYP.mjs → chunk-56QSOVVL.mjs} +1 -1
- package/dist/{chunk-XJ5H2MXY.mjs → chunk-OD53TMER.mjs} +19 -1
- package/dist/{chunk-DWNWN5RH.mjs → chunk-R7PPFBCL.mjs} +1 -1
- package/dist/{chunk-VOUI6XPV.mjs → chunk-TJIH7TLX.mjs} +1 -1
- package/dist/{chunk-VC6X44FS.mjs → chunk-U7HCFLZ2.mjs} +153 -48
- package/dist/codegen/backends/imperative.cjs +24 -6
- package/dist/codegen/backends/imperative.d.ts.map +1 -1
- package/dist/codegen/backends/imperative.js +19 -4
- package/dist/codegen/backends/imperative.mjs +24 -6
- package/dist/frontend/babel/build-ir-lit.cjs +120 -43
- package/dist/frontend/babel/build-ir-lit.mjs +120 -43
- package/dist/frontend/babel/build-ir.cjs +120 -43
- package/dist/frontend/babel/build-ir.mjs +120 -43
- package/dist/frontend/babel/index.cjs +121 -44
- package/dist/frontend/babel/index.mjs +121 -44
- package/dist/frontend/babel/lower-template.cjs +120 -43
- package/dist/frontend/babel/lower-template.mjs +120 -43
- package/dist/frontend/babel/plugin.cjs +120 -43
- package/dist/frontend/babel/plugin.mjs +120 -43
- package/dist/index.cjs +41 -38
- package/dist/index.mjs +41 -38
- package/dist/integrations.cjs +176 -52
- package/dist/integrations.mjs +8 -8
- package/dist/lower/compile-template.cjs +41 -38
- package/dist/lower/compile-template.mjs +41 -38
- 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 +40 -37
- package/dist/lower/template.d.ts.map +1 -1
- package/dist/lower/template.js +22 -30
- package/dist/lower/template.mjs +40 -37
- package/dist/resolve/builtins.cjs +1 -1
- package/dist/resolve/builtins.mjs +1 -1
- package/dist/resolve/component-globals.cjs +14 -0
- package/dist/resolve/component-globals.mjs +14 -0
- package/dist/resolve/write-globals.cjs +14 -0
- package/dist/resolve/write-globals.mjs +14 -0
- package/dist/{server-fns-BBZRZUPP.mjs → server-fns-ZFRDIQ2X.mjs} +4 -4
- package/dist/{server-functions-VEGLIFCE.mjs → server-functions-CTDXX67X.mjs} +3 -3
- package/dist/transform/index.cjs +170 -47
- package/dist/transform/index.mjs +170 -47
- package/dist/transform/intrinsics.cjs +18 -0
- package/dist/transform/intrinsics.mjs +18 -0
- package/dist/transform/source-locations.cjs +64 -3
- 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 +64 -3
- package/dist/transform/templates.cjs +169 -46
- package/dist/transform/templates.js +2 -2
- package/dist/transform/templates.mjs +169 -46
- package/dist/{transform-UWZ7ZGJ2.mjs → transform-QWWQDCXE.mjs} +7 -7
- 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.
|
|
@@ -486,6 +487,19 @@ var INTRINSICS = {
|
|
|
486
487
|
// dependency analysis must not guess at.
|
|
487
488
|
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
488
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" },
|
|
489
503
|
// An environment variable, public or server-only; the browser build refuses a secret.
|
|
490
504
|
$env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
|
|
491
505
|
};
|
|
@@ -663,13 +677,16 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
663
677
|
used.add("createComponent");
|
|
664
678
|
const spreads = props.filter((p) => p.kind === "spread");
|
|
665
679
|
const normal = props.filter((p) => p.kind !== "spread");
|
|
666
|
-
const
|
|
667
|
-
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
668
|
-
);
|
|
680
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
669
681
|
if (children.length > 0) {
|
|
670
|
-
|
|
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(", ")} })`;
|
|
671
689
|
}
|
|
672
|
-
let obj = `{ ${entries.join(", ")} }`;
|
|
673
690
|
if (spreads.length > 0) {
|
|
674
691
|
used.add("mergeProps");
|
|
675
692
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
@@ -751,7 +768,8 @@ function emitNode(node, used, templates) {
|
|
|
751
768
|
} else {
|
|
752
769
|
used.add("spread");
|
|
753
770
|
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
754
|
-
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)}` : "";
|
|
755
773
|
parts.push(
|
|
756
774
|
`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
|
|
757
775
|
);
|
|
@@ -829,6 +847,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
|
|
|
829
847
|
function escapeTemplate(s) {
|
|
830
848
|
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
831
849
|
}
|
|
850
|
+
var CONTAINER = "container";
|
|
832
851
|
function capitalize(s) {
|
|
833
852
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
834
853
|
}
|
|
@@ -883,10 +902,6 @@ var Lowerer = class {
|
|
|
883
902
|
if (hasElse) i = elseIdx;
|
|
884
903
|
continue;
|
|
885
904
|
}
|
|
886
|
-
if (child.attributes.some((a) => a.kind === "EachDirective")) {
|
|
887
|
-
out.push(this.lowerEach(child));
|
|
888
|
-
continue;
|
|
889
|
-
}
|
|
890
905
|
}
|
|
891
906
|
const node = this.lowerNode(child);
|
|
892
907
|
if (node) out.push(node);
|
|
@@ -928,13 +943,16 @@ var Lowerer = class {
|
|
|
928
943
|
node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
|
|
929
944
|
return node;
|
|
930
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 };
|
|
931
949
|
const at = this.tagAt(el);
|
|
932
950
|
return {
|
|
933
951
|
kind: "element",
|
|
934
952
|
tag: el.tag,
|
|
935
953
|
svg: el.namespace === "svg",
|
|
936
954
|
props: this.lowerAttributes(el.attributes),
|
|
937
|
-
children
|
|
955
|
+
children,
|
|
938
956
|
static: false,
|
|
939
957
|
...at ? { at } : {}
|
|
940
958
|
};
|
|
@@ -1006,10 +1024,14 @@ var Lowerer = class {
|
|
|
1006
1024
|
const at = this.tagAt(el);
|
|
1007
1025
|
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
1008
1026
|
}
|
|
1009
|
-
/**
|
|
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
|
+
*/
|
|
1010
1032
|
lowerEach(el) {
|
|
1011
1033
|
const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
|
|
1012
|
-
if (!eachDir || eachDir.kind !== "EachDirective") return
|
|
1034
|
+
if (!eachDir || eachDir.kind !== "EachDirective") return null;
|
|
1013
1035
|
const each = this.hole(eachDir.hole);
|
|
1014
1036
|
const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
|
|
1015
1037
|
if (eachDir.key) {
|
|
@@ -1019,12 +1041,12 @@ var Lowerer = class {
|
|
|
1019
1041
|
const arrow = this.itemArrow(el);
|
|
1020
1042
|
let childrenCode;
|
|
1021
1043
|
if (arrow) {
|
|
1022
|
-
const
|
|
1023
|
-
|
|
1024
|
-
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}`;
|
|
1025
1046
|
} else {
|
|
1026
|
-
const
|
|
1027
|
-
|
|
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")}`;
|
|
1028
1050
|
}
|
|
1029
1051
|
const children = [{ kind: "expr", code: childrenCode, reactive: false }];
|
|
1030
1052
|
const at = this.tagAt(el);
|
|
@@ -1041,25 +1063,6 @@ var Lowerer = class {
|
|
|
1041
1063
|
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
1042
1064
|
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
1043
1065
|
}
|
|
1044
|
-
/** Same element (minus each/key) but with explicit IR children. */
|
|
1045
|
-
rebuildWithChildren(el, children) {
|
|
1046
|
-
const props = this.lowerAttributes(
|
|
1047
|
-
el.attributes.filter((a) => a.kind !== "EachDirective")
|
|
1048
|
-
);
|
|
1049
|
-
if (el.kind === "Element") {
|
|
1050
|
-
const at = this.tagAt(el);
|
|
1051
|
-
return {
|
|
1052
|
-
kind: "element",
|
|
1053
|
-
tag: el.tag,
|
|
1054
|
-
svg: el.namespace === "svg",
|
|
1055
|
-
props,
|
|
1056
|
-
children,
|
|
1057
|
-
static: false,
|
|
1058
|
-
...at ? { at } : {}
|
|
1059
|
-
};
|
|
1060
|
-
}
|
|
1061
|
-
return { kind: "component", name: el.tag, props, children };
|
|
1062
|
-
}
|
|
1063
1066
|
// --- attributes ---------------------------------------------------------
|
|
1064
1067
|
lowerAttributes(attrs) {
|
|
1065
1068
|
const props = [];
|
|
@@ -1471,6 +1474,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
|
|
|
1471
1474
|
"mouseup"
|
|
1472
1475
|
]);
|
|
1473
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
|
+
}
|
|
1474
1493
|
var posOf = (node, ctx) => {
|
|
1475
1494
|
if (!ctx.sourceFile) return void 0;
|
|
1476
1495
|
const at = node.loc?.start;
|
|
@@ -1579,7 +1598,16 @@ function buildProps(attrs, ctx) {
|
|
|
1579
1598
|
}
|
|
1580
1599
|
function buildChildren(children, ctx) {
|
|
1581
1600
|
const out = [];
|
|
1582
|
-
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
|
+
}
|
|
1583
1611
|
if (child.type === "JSXText") {
|
|
1584
1612
|
const text = cleanTemplateText(child.value);
|
|
1585
1613
|
if (text) out.push({ kind: "text", value: text });
|
|
@@ -1616,19 +1644,68 @@ function readLoad(attrs) {
|
|
|
1616
1644
|
}
|
|
1617
1645
|
return void 0;
|
|
1618
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
|
+
}
|
|
1619
1697
|
function buildNode(node, ctx) {
|
|
1620
1698
|
if (node.type === "JSXFragment") {
|
|
1621
1699
|
return { kind: "fragment", children: buildChildren(node.children, ctx) };
|
|
1622
1700
|
}
|
|
1701
|
+
if (directive(node, "if")) return buildIf(node, null, ctx);
|
|
1623
1702
|
const { tag, component } = tagInfo(node.openingElement.name, ctx);
|
|
1703
|
+
if (!component) return buildElement(node, ctx, []);
|
|
1624
1704
|
const props = buildProps(node.openingElement.attributes, ctx);
|
|
1625
1705
|
const children = buildChildren(node.children, ctx);
|
|
1626
1706
|
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1630
|
-
}
|
|
1631
|
-
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 } : {} };
|
|
1632
1709
|
}
|
|
1633
1710
|
function buildJsxIR(node, ctx) {
|
|
1634
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.
|
|
@@ -447,6 +448,19 @@ var INTRINSICS = {
|
|
|
447
448
|
// dependency analysis must not guess at.
|
|
448
449
|
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
449
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" },
|
|
450
464
|
// An environment variable, public or server-only; the browser build refuses a secret.
|
|
451
465
|
$env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
|
|
452
466
|
};
|
|
@@ -624,13 +638,16 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
624
638
|
used.add("createComponent");
|
|
625
639
|
const spreads = props.filter((p) => p.kind === "spread");
|
|
626
640
|
const normal = props.filter((p) => p.kind !== "spread");
|
|
627
|
-
const
|
|
628
|
-
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
629
|
-
);
|
|
641
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
630
642
|
if (children.length > 0) {
|
|
631
|
-
|
|
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(", ")} })`;
|
|
632
650
|
}
|
|
633
|
-
let obj = `{ ${entries.join(", ")} }`;
|
|
634
651
|
if (spreads.length > 0) {
|
|
635
652
|
used.add("mergeProps");
|
|
636
653
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
@@ -712,7 +729,8 @@ function emitNode(node, used, templates) {
|
|
|
712
729
|
} else {
|
|
713
730
|
used.add("spread");
|
|
714
731
|
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
715
|
-
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)}` : "";
|
|
716
734
|
parts.push(
|
|
717
735
|
`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
|
|
718
736
|
);
|
|
@@ -792,6 +810,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
|
|
|
792
810
|
function escapeTemplate(s) {
|
|
793
811
|
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
794
812
|
}
|
|
813
|
+
var CONTAINER = "container";
|
|
795
814
|
function capitalize(s) {
|
|
796
815
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
797
816
|
}
|
|
@@ -846,10 +865,6 @@ var Lowerer = class {
|
|
|
846
865
|
if (hasElse) i = elseIdx;
|
|
847
866
|
continue;
|
|
848
867
|
}
|
|
849
|
-
if (child.attributes.some((a) => a.kind === "EachDirective")) {
|
|
850
|
-
out.push(this.lowerEach(child));
|
|
851
|
-
continue;
|
|
852
|
-
}
|
|
853
868
|
}
|
|
854
869
|
const node = this.lowerNode(child);
|
|
855
870
|
if (node) out.push(node);
|
|
@@ -891,13 +906,16 @@ var Lowerer = class {
|
|
|
891
906
|
node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
|
|
892
907
|
return node;
|
|
893
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 };
|
|
894
912
|
const at = this.tagAt(el);
|
|
895
913
|
return {
|
|
896
914
|
kind: "element",
|
|
897
915
|
tag: el.tag,
|
|
898
916
|
svg: el.namespace === "svg",
|
|
899
917
|
props: this.lowerAttributes(el.attributes),
|
|
900
|
-
children
|
|
918
|
+
children,
|
|
901
919
|
static: false,
|
|
902
920
|
...at ? { at } : {}
|
|
903
921
|
};
|
|
@@ -969,10 +987,14 @@ var Lowerer = class {
|
|
|
969
987
|
const at = this.tagAt(el);
|
|
970
988
|
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
971
989
|
}
|
|
972
|
-
/**
|
|
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
|
+
*/
|
|
973
995
|
lowerEach(el) {
|
|
974
996
|
const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
|
|
975
|
-
if (!eachDir || eachDir.kind !== "EachDirective") return
|
|
997
|
+
if (!eachDir || eachDir.kind !== "EachDirective") return null;
|
|
976
998
|
const each = this.hole(eachDir.hole);
|
|
977
999
|
const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
|
|
978
1000
|
if (eachDir.key) {
|
|
@@ -982,12 +1004,12 @@ var Lowerer = class {
|
|
|
982
1004
|
const arrow = this.itemArrow(el);
|
|
983
1005
|
let childrenCode;
|
|
984
1006
|
if (arrow) {
|
|
985
|
-
const
|
|
986
|
-
|
|
987
|
-
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}`;
|
|
988
1009
|
} else {
|
|
989
|
-
const
|
|
990
|
-
|
|
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")}`;
|
|
991
1013
|
}
|
|
992
1014
|
const children = [{ kind: "expr", code: childrenCode, reactive: false }];
|
|
993
1015
|
const at = this.tagAt(el);
|
|
@@ -1004,25 +1026,6 @@ var Lowerer = class {
|
|
|
1004
1026
|
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
1005
1027
|
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
1006
1028
|
}
|
|
1007
|
-
/** Same element (minus each/key) but with explicit IR children. */
|
|
1008
|
-
rebuildWithChildren(el, children) {
|
|
1009
|
-
const props = this.lowerAttributes(
|
|
1010
|
-
el.attributes.filter((a) => a.kind !== "EachDirective")
|
|
1011
|
-
);
|
|
1012
|
-
if (el.kind === "Element") {
|
|
1013
|
-
const at = this.tagAt(el);
|
|
1014
|
-
return {
|
|
1015
|
-
kind: "element",
|
|
1016
|
-
tag: el.tag,
|
|
1017
|
-
svg: el.namespace === "svg",
|
|
1018
|
-
props,
|
|
1019
|
-
children,
|
|
1020
|
-
static: false,
|
|
1021
|
-
...at ? { at } : {}
|
|
1022
|
-
};
|
|
1023
|
-
}
|
|
1024
|
-
return { kind: "component", name: el.tag, props, children };
|
|
1025
|
-
}
|
|
1026
1029
|
// --- attributes ---------------------------------------------------------
|
|
1027
1030
|
lowerAttributes(attrs) {
|
|
1028
1031
|
const props = [];
|
|
@@ -1434,6 +1437,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
|
|
|
1434
1437
|
"mouseup"
|
|
1435
1438
|
]);
|
|
1436
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
|
+
}
|
|
1437
1456
|
var posOf = (node, ctx) => {
|
|
1438
1457
|
if (!ctx.sourceFile) return void 0;
|
|
1439
1458
|
const at = node.loc?.start;
|
|
@@ -1542,7 +1561,16 @@ function buildProps(attrs, ctx) {
|
|
|
1542
1561
|
}
|
|
1543
1562
|
function buildChildren(children, ctx) {
|
|
1544
1563
|
const out = [];
|
|
1545
|
-
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
|
+
}
|
|
1546
1574
|
if (child.type === "JSXText") {
|
|
1547
1575
|
const text = cleanTemplateText(child.value);
|
|
1548
1576
|
if (text) out.push({ kind: "text", value: text });
|
|
@@ -1579,19 +1607,68 @@ function readLoad(attrs) {
|
|
|
1579
1607
|
}
|
|
1580
1608
|
return void 0;
|
|
1581
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
|
+
}
|
|
1582
1660
|
function buildNode(node, ctx) {
|
|
1583
1661
|
if (node.type === "JSXFragment") {
|
|
1584
1662
|
return { kind: "fragment", children: buildChildren(node.children, ctx) };
|
|
1585
1663
|
}
|
|
1664
|
+
if (directive(node, "if")) return buildIf(node, null, ctx);
|
|
1586
1665
|
const { tag, component } = tagInfo(node.openingElement.name, ctx);
|
|
1666
|
+
if (!component) return buildElement(node, ctx, []);
|
|
1587
1667
|
const props = buildProps(node.openingElement.attributes, ctx);
|
|
1588
1668
|
const children = buildChildren(node.children, ctx);
|
|
1589
1669
|
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1593
|
-
}
|
|
1594
|
-
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 } : {} };
|
|
1595
1672
|
}
|
|
1596
1673
|
function buildJsxIR(node, ctx) {
|
|
1597
1674
|
const ir = buildNode(node, ctx);
|