@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
|
@@ -469,6 +469,7 @@ var INTRINSICS = {
|
|
|
469
469
|
$effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
|
|
470
470
|
$store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
|
|
471
471
|
$resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
|
|
472
|
+
$cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
|
|
472
473
|
// No short runtime form for these yet, so the intrinsic stands for the `create*`
|
|
473
474
|
// name. Still worth having: no import to write, and when a shorter form is
|
|
474
475
|
// designed, the change is this line rather than a compiler pass.
|
|
@@ -479,6 +480,19 @@ var INTRINSICS = {
|
|
|
479
480
|
// dependency analysis must not guess at.
|
|
480
481
|
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
481
482
|
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
|
|
483
|
+
// Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
|
|
484
|
+
// no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
|
|
485
|
+
$batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
|
|
486
|
+
$cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
|
|
487
|
+
$root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
|
|
488
|
+
$context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
|
|
489
|
+
$renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
|
|
490
|
+
$computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
|
|
491
|
+
// Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
|
|
492
|
+
// handle as far as reads go: the same accessor with `set` on it.
|
|
493
|
+
$storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
494
|
+
$asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
495
|
+
$persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
|
|
482
496
|
// An environment variable, public or server-only; the browser build refuses a secret.
|
|
483
497
|
$env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
|
|
484
498
|
};
|
|
@@ -656,13 +670,16 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
656
670
|
used.add("createComponent");
|
|
657
671
|
const spreads = props.filter((p) => p.kind === "spread");
|
|
658
672
|
const normal = props.filter((p) => p.kind !== "spread");
|
|
659
|
-
const
|
|
660
|
-
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
661
|
-
);
|
|
673
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
662
674
|
if (children.length > 0) {
|
|
663
|
-
|
|
675
|
+
bodies.push(["children", emitChildren(children, used, templates)]);
|
|
676
|
+
}
|
|
677
|
+
let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
|
|
678
|
+
if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
|
|
679
|
+
const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
|
|
680
|
+
const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
|
|
681
|
+
obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
|
|
664
682
|
}
|
|
665
|
-
let obj = `{ ${entries.join(", ")} }`;
|
|
666
683
|
if (spreads.length > 0) {
|
|
667
684
|
used.add("mergeProps");
|
|
668
685
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
@@ -744,7 +761,8 @@ function emitNode(node, used, templates) {
|
|
|
744
761
|
} else {
|
|
745
762
|
used.add("spread");
|
|
746
763
|
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
747
|
-
const
|
|
764
|
+
const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
|
|
765
|
+
const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
|
|
748
766
|
parts.push(
|
|
749
767
|
`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
|
|
750
768
|
);
|
|
@@ -822,6 +840,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
|
|
|
822
840
|
function escapeTemplate(s) {
|
|
823
841
|
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
824
842
|
}
|
|
843
|
+
var CONTAINER = "container";
|
|
825
844
|
function capitalize(s) {
|
|
826
845
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
827
846
|
}
|
|
@@ -876,10 +895,6 @@ var Lowerer = class {
|
|
|
876
895
|
if (hasElse) i = elseIdx;
|
|
877
896
|
continue;
|
|
878
897
|
}
|
|
879
|
-
if (child.attributes.some((a) => a.kind === "EachDirective")) {
|
|
880
|
-
out.push(this.lowerEach(child));
|
|
881
|
-
continue;
|
|
882
|
-
}
|
|
883
898
|
}
|
|
884
899
|
const node = this.lowerNode(child);
|
|
885
900
|
if (node) out.push(node);
|
|
@@ -921,13 +936,16 @@ var Lowerer = class {
|
|
|
921
936
|
node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
|
|
922
937
|
return node;
|
|
923
938
|
}
|
|
939
|
+
const each = this.lowerEach(el);
|
|
940
|
+
const children = each ? [each] : this.lowerChildren(el.children);
|
|
941
|
+
if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
924
942
|
const at = this.tagAt(el);
|
|
925
943
|
return {
|
|
926
944
|
kind: "element",
|
|
927
945
|
tag: el.tag,
|
|
928
946
|
svg: el.namespace === "svg",
|
|
929
947
|
props: this.lowerAttributes(el.attributes),
|
|
930
|
-
children
|
|
948
|
+
children,
|
|
931
949
|
static: false,
|
|
932
950
|
...at ? { at } : {}
|
|
933
951
|
};
|
|
@@ -999,10 +1017,14 @@ var Lowerer = class {
|
|
|
999
1017
|
const at = this.tagAt(el);
|
|
1000
1018
|
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
1001
1019
|
}
|
|
1002
|
-
/**
|
|
1020
|
+
/**
|
|
1021
|
+
* `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
|
|
1022
|
+
* content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
|
|
1023
|
+
* it is. Null when the element has no `each`.
|
|
1024
|
+
*/
|
|
1003
1025
|
lowerEach(el) {
|
|
1004
1026
|
const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
|
|
1005
|
-
if (!eachDir || eachDir.kind !== "EachDirective") return
|
|
1027
|
+
if (!eachDir || eachDir.kind !== "EachDirective") return null;
|
|
1006
1028
|
const each = this.hole(eachDir.hole);
|
|
1007
1029
|
const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
|
|
1008
1030
|
if (eachDir.key) {
|
|
@@ -1012,12 +1034,12 @@ var Lowerer = class {
|
|
|
1012
1034
|
const arrow = this.itemArrow(el);
|
|
1013
1035
|
let childrenCode;
|
|
1014
1036
|
if (arrow) {
|
|
1015
|
-
const
|
|
1016
|
-
|
|
1017
|
-
childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
|
|
1037
|
+
const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
|
|
1038
|
+
childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
|
|
1018
1039
|
} else {
|
|
1019
|
-
const
|
|
1020
|
-
|
|
1040
|
+
const content = this.lowerChildren(el.children);
|
|
1041
|
+
const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
|
|
1042
|
+
childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
|
|
1021
1043
|
}
|
|
1022
1044
|
const children = [{ kind: "expr", code: childrenCode, reactive: false }];
|
|
1023
1045
|
const at = this.tagAt(el);
|
|
@@ -1034,25 +1056,6 @@ var Lowerer = class {
|
|
|
1034
1056
|
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
1035
1057
|
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
1036
1058
|
}
|
|
1037
|
-
/** Same element (minus each/key) but with explicit IR children. */
|
|
1038
|
-
rebuildWithChildren(el, children) {
|
|
1039
|
-
const props = this.lowerAttributes(
|
|
1040
|
-
el.attributes.filter((a) => a.kind !== "EachDirective")
|
|
1041
|
-
);
|
|
1042
|
-
if (el.kind === "Element") {
|
|
1043
|
-
const at = this.tagAt(el);
|
|
1044
|
-
return {
|
|
1045
|
-
kind: "element",
|
|
1046
|
-
tag: el.tag,
|
|
1047
|
-
svg: el.namespace === "svg",
|
|
1048
|
-
props,
|
|
1049
|
-
children,
|
|
1050
|
-
static: false,
|
|
1051
|
-
...at ? { at } : {}
|
|
1052
|
-
};
|
|
1053
|
-
}
|
|
1054
|
-
return { kind: "component", name: el.tag, props, children };
|
|
1055
|
-
}
|
|
1056
1059
|
// --- attributes ---------------------------------------------------------
|
|
1057
1060
|
lowerAttributes(attrs) {
|
|
1058
1061
|
const props = [];
|
|
@@ -1464,6 +1467,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
|
|
|
1464
1467
|
"mouseup"
|
|
1465
1468
|
]);
|
|
1466
1469
|
var isReactive = (node) => isReactiveShape(shapeOf(node));
|
|
1470
|
+
var CONTAINER2 = "container";
|
|
1471
|
+
function directive(node, name) {
|
|
1472
|
+
if (node.type !== "JSXElement") return void 0;
|
|
1473
|
+
const opening = node.openingElement;
|
|
1474
|
+
if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
|
|
1475
|
+
return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
|
|
1476
|
+
}
|
|
1477
|
+
function directiveExpr(attr) {
|
|
1478
|
+
const v = attr.value;
|
|
1479
|
+
return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
|
|
1480
|
+
}
|
|
1481
|
+
function emitIR(ir, ctx) {
|
|
1482
|
+
const { code, imports, templates } = imperativeBackend.emit(ir, {});
|
|
1483
|
+
for (const s of imports) ctx.used.add(s);
|
|
1484
|
+
return inlineTemplates(code, templates);
|
|
1485
|
+
}
|
|
1467
1486
|
var posOf = (node, ctx) => {
|
|
1468
1487
|
if (!ctx.sourceFile) return void 0;
|
|
1469
1488
|
const at = node.loc?.start;
|
|
@@ -1572,7 +1591,16 @@ function buildProps(attrs, ctx) {
|
|
|
1572
1591
|
}
|
|
1573
1592
|
function buildChildren(children, ctx) {
|
|
1574
1593
|
const out = [];
|
|
1575
|
-
for (
|
|
1594
|
+
for (let i = 0; i < children.length; i++) {
|
|
1595
|
+
const child = children[i];
|
|
1596
|
+
if (directive(child, "if")) {
|
|
1597
|
+
let next = i + 1;
|
|
1598
|
+
while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
|
|
1599
|
+
const fallback = directive(children[next], "else") ? children[next] : null;
|
|
1600
|
+
out.push(buildIf(child, fallback, ctx));
|
|
1601
|
+
if (fallback) i = next;
|
|
1602
|
+
continue;
|
|
1603
|
+
}
|
|
1576
1604
|
if (child.type === "JSXText") {
|
|
1577
1605
|
const text = cleanTemplateText(child.value);
|
|
1578
1606
|
if (text) out.push({ kind: "text", value: text });
|
|
@@ -1609,19 +1637,68 @@ function readLoad(attrs) {
|
|
|
1609
1637
|
}
|
|
1610
1638
|
return void 0;
|
|
1611
1639
|
}
|
|
1640
|
+
function buildIf(node, fallback, ctx) {
|
|
1641
|
+
const cond = directiveExpr(directive(node, "if"));
|
|
1642
|
+
const props = [
|
|
1643
|
+
cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
|
|
1644
|
+
];
|
|
1645
|
+
if (fallback) {
|
|
1646
|
+
props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
|
|
1647
|
+
}
|
|
1648
|
+
return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
|
|
1649
|
+
}
|
|
1650
|
+
function buildEach(node, ctx) {
|
|
1651
|
+
const attr = directive(node, "each");
|
|
1652
|
+
if (!attr) return null;
|
|
1653
|
+
const list = directiveExpr(attr);
|
|
1654
|
+
const props = [
|
|
1655
|
+
list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
|
|
1656
|
+
];
|
|
1657
|
+
const key = directive(node, "key");
|
|
1658
|
+
if (key) {
|
|
1659
|
+
const keyExpr = directiveExpr(key);
|
|
1660
|
+
const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
|
|
1661
|
+
if (by) props.push({ name: "by", kind: "attr", expr: by });
|
|
1662
|
+
}
|
|
1663
|
+
const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
|
|
1664
|
+
const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
|
|
1665
|
+
let render;
|
|
1666
|
+
if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
|
|
1667
|
+
const fn = ctx.code(only);
|
|
1668
|
+
const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
|
|
1669
|
+
render = live ? `(...args) => () => (${fn})(...args)` : fn;
|
|
1670
|
+
} else {
|
|
1671
|
+
const built = buildChildren(node.children, ctx);
|
|
1672
|
+
const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
|
|
1673
|
+
render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
|
|
1674
|
+
}
|
|
1675
|
+
return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
|
|
1676
|
+
}
|
|
1677
|
+
function buildElement(node, ctx, handled) {
|
|
1678
|
+
const skip = new Set(handled);
|
|
1679
|
+
const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
|
|
1680
|
+
const attributes = node.openingElement.attributes.filter(
|
|
1681
|
+
(a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
|
|
1682
|
+
);
|
|
1683
|
+
const { tag } = tagInfo(node.openingElement.name, ctx);
|
|
1684
|
+
const each = buildEach(node, ctx);
|
|
1685
|
+
const children = each ? [each] : buildChildren(node.children, ctx);
|
|
1686
|
+
if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
1687
|
+
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1688
|
+
return { kind: "element", tag, svg: SVG_TAGS.has(tag), props: buildProps(attributes, ctx), children, static: false, ...at ? { at } : {} };
|
|
1689
|
+
}
|
|
1612
1690
|
function buildNode(node, ctx) {
|
|
1613
1691
|
if (node.type === "JSXFragment") {
|
|
1614
1692
|
return { kind: "fragment", children: buildChildren(node.children, ctx) };
|
|
1615
1693
|
}
|
|
1694
|
+
if (directive(node, "if")) return buildIf(node, null, ctx);
|
|
1616
1695
|
const { tag, component } = tagInfo(node.openingElement.name, ctx);
|
|
1696
|
+
if (!component) return buildElement(node, ctx, []);
|
|
1617
1697
|
const props = buildProps(node.openingElement.attributes, ctx);
|
|
1618
1698
|
const children = buildChildren(node.children, ctx);
|
|
1619
1699
|
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1623
|
-
}
|
|
1624
|
-
return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
|
|
1700
|
+
const load = readLoad(node.openingElement.attributes);
|
|
1701
|
+
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1625
1702
|
}
|
|
1626
1703
|
function buildJsxIR(node, ctx) {
|
|
1627
1704
|
const ir = buildNode(node, ctx);
|
|
@@ -435,6 +435,7 @@ var INTRINSICS = {
|
|
|
435
435
|
$effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
|
|
436
436
|
$store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
|
|
437
437
|
$resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
|
|
438
|
+
$cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
|
|
438
439
|
// No short runtime form for these yet, so the intrinsic stands for the `create*`
|
|
439
440
|
// name. Still worth having: no import to write, and when a shorter form is
|
|
440
441
|
// designed, the change is this line rather than a compiler pass.
|
|
@@ -445,6 +446,19 @@ var INTRINSICS = {
|
|
|
445
446
|
// dependency analysis must not guess at.
|
|
446
447
|
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
447
448
|
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
|
|
449
|
+
// Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
|
|
450
|
+
// no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
|
|
451
|
+
$batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
|
|
452
|
+
$cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
|
|
453
|
+
$root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
|
|
454
|
+
$context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
|
|
455
|
+
$renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
|
|
456
|
+
$computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
|
|
457
|
+
// Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
|
|
458
|
+
// handle as far as reads go: the same accessor with `set` on it.
|
|
459
|
+
$storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
460
|
+
$asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
461
|
+
$persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
|
|
448
462
|
// An environment variable, public or server-only; the browser build refuses a secret.
|
|
449
463
|
$env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
|
|
450
464
|
};
|
|
@@ -622,13 +636,16 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
622
636
|
used.add("createComponent");
|
|
623
637
|
const spreads = props.filter((p) => p.kind === "spread");
|
|
624
638
|
const normal = props.filter((p) => p.kind !== "spread");
|
|
625
|
-
const
|
|
626
|
-
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
627
|
-
);
|
|
639
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
628
640
|
if (children.length > 0) {
|
|
629
|
-
|
|
641
|
+
bodies.push(["children", emitChildren(children, used, templates)]);
|
|
642
|
+
}
|
|
643
|
+
let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
|
|
644
|
+
if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
|
|
645
|
+
const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
|
|
646
|
+
const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
|
|
647
|
+
obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
|
|
630
648
|
}
|
|
631
|
-
let obj = `{ ${entries.join(", ")} }`;
|
|
632
649
|
if (spreads.length > 0) {
|
|
633
650
|
used.add("mergeProps");
|
|
634
651
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
@@ -710,7 +727,8 @@ function emitNode(node, used, templates) {
|
|
|
710
727
|
} else {
|
|
711
728
|
used.add("spread");
|
|
712
729
|
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
713
|
-
const
|
|
730
|
+
const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
|
|
731
|
+
const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
|
|
714
732
|
parts.push(
|
|
715
733
|
`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
|
|
716
734
|
);
|
|
@@ -790,6 +808,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
|
|
|
790
808
|
function escapeTemplate(s) {
|
|
791
809
|
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
792
810
|
}
|
|
811
|
+
var CONTAINER = "container";
|
|
793
812
|
function capitalize(s) {
|
|
794
813
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
795
814
|
}
|
|
@@ -844,10 +863,6 @@ var Lowerer = class {
|
|
|
844
863
|
if (hasElse) i = elseIdx;
|
|
845
864
|
continue;
|
|
846
865
|
}
|
|
847
|
-
if (child.attributes.some((a) => a.kind === "EachDirective")) {
|
|
848
|
-
out.push(this.lowerEach(child));
|
|
849
|
-
continue;
|
|
850
|
-
}
|
|
851
866
|
}
|
|
852
867
|
const node = this.lowerNode(child);
|
|
853
868
|
if (node) out.push(node);
|
|
@@ -889,13 +904,16 @@ var Lowerer = class {
|
|
|
889
904
|
node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
|
|
890
905
|
return node;
|
|
891
906
|
}
|
|
907
|
+
const each = this.lowerEach(el);
|
|
908
|
+
const children = each ? [each] : this.lowerChildren(el.children);
|
|
909
|
+
if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
892
910
|
const at = this.tagAt(el);
|
|
893
911
|
return {
|
|
894
912
|
kind: "element",
|
|
895
913
|
tag: el.tag,
|
|
896
914
|
svg: el.namespace === "svg",
|
|
897
915
|
props: this.lowerAttributes(el.attributes),
|
|
898
|
-
children
|
|
916
|
+
children,
|
|
899
917
|
static: false,
|
|
900
918
|
...at ? { at } : {}
|
|
901
919
|
};
|
|
@@ -967,10 +985,14 @@ var Lowerer = class {
|
|
|
967
985
|
const at = this.tagAt(el);
|
|
968
986
|
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
969
987
|
}
|
|
970
|
-
/**
|
|
988
|
+
/**
|
|
989
|
+
* `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
|
|
990
|
+
* content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
|
|
991
|
+
* it is. Null when the element has no `each`.
|
|
992
|
+
*/
|
|
971
993
|
lowerEach(el) {
|
|
972
994
|
const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
|
|
973
|
-
if (!eachDir || eachDir.kind !== "EachDirective") return
|
|
995
|
+
if (!eachDir || eachDir.kind !== "EachDirective") return null;
|
|
974
996
|
const each = this.hole(eachDir.hole);
|
|
975
997
|
const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
|
|
976
998
|
if (eachDir.key) {
|
|
@@ -980,12 +1002,12 @@ var Lowerer = class {
|
|
|
980
1002
|
const arrow = this.itemArrow(el);
|
|
981
1003
|
let childrenCode;
|
|
982
1004
|
if (arrow) {
|
|
983
|
-
const
|
|
984
|
-
|
|
985
|
-
childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
|
|
1005
|
+
const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
|
|
1006
|
+
childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
|
|
986
1007
|
} else {
|
|
987
|
-
const
|
|
988
|
-
|
|
1008
|
+
const content = this.lowerChildren(el.children);
|
|
1009
|
+
const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
|
|
1010
|
+
childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
|
|
989
1011
|
}
|
|
990
1012
|
const children = [{ kind: "expr", code: childrenCode, reactive: false }];
|
|
991
1013
|
const at = this.tagAt(el);
|
|
@@ -1002,25 +1024,6 @@ var Lowerer = class {
|
|
|
1002
1024
|
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
1003
1025
|
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
1004
1026
|
}
|
|
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
1027
|
// --- attributes ---------------------------------------------------------
|
|
1025
1028
|
lowerAttributes(attrs) {
|
|
1026
1029
|
const props = [];
|
|
@@ -1432,6 +1435,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
|
|
|
1432
1435
|
"mouseup"
|
|
1433
1436
|
]);
|
|
1434
1437
|
var isReactive = (node) => isReactiveShape(shapeOf(node));
|
|
1438
|
+
var CONTAINER2 = "container";
|
|
1439
|
+
function directive(node, name) {
|
|
1440
|
+
if (node.type !== "JSXElement") return void 0;
|
|
1441
|
+
const opening = node.openingElement;
|
|
1442
|
+
if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
|
|
1443
|
+
return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
|
|
1444
|
+
}
|
|
1445
|
+
function directiveExpr(attr) {
|
|
1446
|
+
const v = attr.value;
|
|
1447
|
+
return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
|
|
1448
|
+
}
|
|
1449
|
+
function emitIR(ir, ctx) {
|
|
1450
|
+
const { code, imports, templates } = imperativeBackend.emit(ir, {});
|
|
1451
|
+
for (const s of imports) ctx.used.add(s);
|
|
1452
|
+
return inlineTemplates(code, templates);
|
|
1453
|
+
}
|
|
1435
1454
|
var posOf = (node, ctx) => {
|
|
1436
1455
|
if (!ctx.sourceFile) return void 0;
|
|
1437
1456
|
const at = node.loc?.start;
|
|
@@ -1540,7 +1559,16 @@ function buildProps(attrs, ctx) {
|
|
|
1540
1559
|
}
|
|
1541
1560
|
function buildChildren(children, ctx) {
|
|
1542
1561
|
const out = [];
|
|
1543
|
-
for (
|
|
1562
|
+
for (let i = 0; i < children.length; i++) {
|
|
1563
|
+
const child = children[i];
|
|
1564
|
+
if (directive(child, "if")) {
|
|
1565
|
+
let next = i + 1;
|
|
1566
|
+
while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
|
|
1567
|
+
const fallback = directive(children[next], "else") ? children[next] : null;
|
|
1568
|
+
out.push(buildIf(child, fallback, ctx));
|
|
1569
|
+
if (fallback) i = next;
|
|
1570
|
+
continue;
|
|
1571
|
+
}
|
|
1544
1572
|
if (child.type === "JSXText") {
|
|
1545
1573
|
const text = cleanTemplateText(child.value);
|
|
1546
1574
|
if (text) out.push({ kind: "text", value: text });
|
|
@@ -1577,19 +1605,68 @@ function readLoad(attrs) {
|
|
|
1577
1605
|
}
|
|
1578
1606
|
return void 0;
|
|
1579
1607
|
}
|
|
1608
|
+
function buildIf(node, fallback, ctx) {
|
|
1609
|
+
const cond = directiveExpr(directive(node, "if"));
|
|
1610
|
+
const props = [
|
|
1611
|
+
cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
|
|
1612
|
+
];
|
|
1613
|
+
if (fallback) {
|
|
1614
|
+
props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
|
|
1615
|
+
}
|
|
1616
|
+
return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
|
|
1617
|
+
}
|
|
1618
|
+
function buildEach(node, ctx) {
|
|
1619
|
+
const attr = directive(node, "each");
|
|
1620
|
+
if (!attr) return null;
|
|
1621
|
+
const list = directiveExpr(attr);
|
|
1622
|
+
const props = [
|
|
1623
|
+
list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
|
|
1624
|
+
];
|
|
1625
|
+
const key = directive(node, "key");
|
|
1626
|
+
if (key) {
|
|
1627
|
+
const keyExpr = directiveExpr(key);
|
|
1628
|
+
const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
|
|
1629
|
+
if (by) props.push({ name: "by", kind: "attr", expr: by });
|
|
1630
|
+
}
|
|
1631
|
+
const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
|
|
1632
|
+
const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
|
|
1633
|
+
let render;
|
|
1634
|
+
if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
|
|
1635
|
+
const fn = ctx.code(only);
|
|
1636
|
+
const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
|
|
1637
|
+
render = live ? `(...args) => () => (${fn})(...args)` : fn;
|
|
1638
|
+
} else {
|
|
1639
|
+
const built = buildChildren(node.children, ctx);
|
|
1640
|
+
const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
|
|
1641
|
+
render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
|
|
1642
|
+
}
|
|
1643
|
+
return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
|
|
1644
|
+
}
|
|
1645
|
+
function buildElement(node, ctx, handled) {
|
|
1646
|
+
const skip = new Set(handled);
|
|
1647
|
+
const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
|
|
1648
|
+
const attributes = node.openingElement.attributes.filter(
|
|
1649
|
+
(a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
|
|
1650
|
+
);
|
|
1651
|
+
const { tag } = tagInfo(node.openingElement.name, ctx);
|
|
1652
|
+
const each = buildEach(node, ctx);
|
|
1653
|
+
const children = each ? [each] : buildChildren(node.children, ctx);
|
|
1654
|
+
if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
1655
|
+
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1656
|
+
return { kind: "element", tag, svg: SVG_TAGS.has(tag), props: buildProps(attributes, ctx), children, static: false, ...at ? { at } : {} };
|
|
1657
|
+
}
|
|
1580
1658
|
function buildNode(node, ctx) {
|
|
1581
1659
|
if (node.type === "JSXFragment") {
|
|
1582
1660
|
return { kind: "fragment", children: buildChildren(node.children, ctx) };
|
|
1583
1661
|
}
|
|
1662
|
+
if (directive(node, "if")) return buildIf(node, null, ctx);
|
|
1584
1663
|
const { tag, component } = tagInfo(node.openingElement.name, ctx);
|
|
1664
|
+
if (!component) return buildElement(node, ctx, []);
|
|
1585
1665
|
const props = buildProps(node.openingElement.attributes, ctx);
|
|
1586
1666
|
const children = buildChildren(node.children, ctx);
|
|
1587
1667
|
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 } : {} };
|
|
1668
|
+
const load = readLoad(node.openingElement.attributes);
|
|
1669
|
+
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1593
1670
|
}
|
|
1594
1671
|
function buildJsxIR(node, ctx) {
|
|
1595
1672
|
const ir = buildNode(node, ctx);
|