@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
|
@@ -251,6 +251,7 @@ var INTRINSICS = {
|
|
|
251
251
|
$effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
|
|
252
252
|
$store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
|
|
253
253
|
$resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
|
|
254
|
+
$cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
|
|
254
255
|
// No short runtime form for these yet, so the intrinsic stands for the `create*`
|
|
255
256
|
// name. Still worth having: no import to write, and when a shorter form is
|
|
256
257
|
// designed, the change is this line rather than a compiler pass.
|
|
@@ -260,7 +261,22 @@ var INTRINSICS = {
|
|
|
260
261
|
// does. `untrack` suppresses dependency collection, which is exactly the kind of thing
|
|
261
262
|
// dependency analysis must not guess at.
|
|
262
263
|
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
263
|
-
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" }
|
|
264
|
+
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
|
|
265
|
+
// Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
|
|
266
|
+
// no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
|
|
267
|
+
$batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
|
|
268
|
+
$cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
|
|
269
|
+
$root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
|
|
270
|
+
$context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
|
|
271
|
+
$renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
|
|
272
|
+
$computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
|
|
273
|
+
// Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
|
|
274
|
+
// handle as far as reads go: the same accessor with `set` on it.
|
|
275
|
+
$storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
276
|
+
$asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
277
|
+
$persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
|
|
278
|
+
// An environment variable, public or server-only; the browser build refuses a secret.
|
|
279
|
+
$env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
|
|
264
280
|
};
|
|
265
281
|
var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join("|")})\\s*\\(`);
|
|
266
282
|
|
|
@@ -436,13 +452,16 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
436
452
|
used.add("createComponent");
|
|
437
453
|
const spreads = props.filter((p) => p.kind === "spread");
|
|
438
454
|
const normal = props.filter((p) => p.kind !== "spread");
|
|
439
|
-
const
|
|
440
|
-
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
441
|
-
);
|
|
455
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
442
456
|
if (children.length > 0) {
|
|
443
|
-
|
|
457
|
+
bodies.push(["children", emitChildren(children, used, templates)]);
|
|
458
|
+
}
|
|
459
|
+
let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
|
|
460
|
+
if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
|
|
461
|
+
const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
|
|
462
|
+
const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
|
|
463
|
+
obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
|
|
444
464
|
}
|
|
445
|
-
let obj = `{ ${entries.join(", ")} }`;
|
|
446
465
|
if (spreads.length > 0) {
|
|
447
466
|
used.add("mergeProps");
|
|
448
467
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
@@ -524,7 +543,8 @@ function emitNode(node, used, templates) {
|
|
|
524
543
|
} else {
|
|
525
544
|
used.add("spread");
|
|
526
545
|
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
527
|
-
const
|
|
546
|
+
const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
|
|
547
|
+
const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
|
|
528
548
|
parts.push(
|
|
529
549
|
`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
|
|
530
550
|
);
|
|
@@ -651,6 +671,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
|
|
|
651
671
|
function escapeTemplate(s) {
|
|
652
672
|
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
653
673
|
}
|
|
674
|
+
var CONTAINER = "container";
|
|
654
675
|
function capitalize(s) {
|
|
655
676
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
656
677
|
}
|
|
@@ -705,10 +726,6 @@ var Lowerer = class {
|
|
|
705
726
|
if (hasElse) i = elseIdx;
|
|
706
727
|
continue;
|
|
707
728
|
}
|
|
708
|
-
if (child.attributes.some((a) => a.kind === "EachDirective")) {
|
|
709
|
-
out.push(this.lowerEach(child));
|
|
710
|
-
continue;
|
|
711
|
-
}
|
|
712
729
|
}
|
|
713
730
|
const node = this.lowerNode(child);
|
|
714
731
|
if (node) out.push(node);
|
|
@@ -750,13 +767,16 @@ var Lowerer = class {
|
|
|
750
767
|
node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
|
|
751
768
|
return node;
|
|
752
769
|
}
|
|
770
|
+
const each = this.lowerEach(el);
|
|
771
|
+
const children = each ? [each] : this.lowerChildren(el.children);
|
|
772
|
+
if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
753
773
|
const at = this.tagAt(el);
|
|
754
774
|
return {
|
|
755
775
|
kind: "element",
|
|
756
776
|
tag: el.tag,
|
|
757
777
|
svg: el.namespace === "svg",
|
|
758
778
|
props: this.lowerAttributes(el.attributes),
|
|
759
|
-
children
|
|
779
|
+
children,
|
|
760
780
|
static: false,
|
|
761
781
|
...at ? { at } : {}
|
|
762
782
|
};
|
|
@@ -828,10 +848,14 @@ var Lowerer = class {
|
|
|
828
848
|
const at = this.tagAt(el);
|
|
829
849
|
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
830
850
|
}
|
|
831
|
-
/**
|
|
851
|
+
/**
|
|
852
|
+
* `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
|
|
853
|
+
* content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
|
|
854
|
+
* it is. Null when the element has no `each`.
|
|
855
|
+
*/
|
|
832
856
|
lowerEach(el) {
|
|
833
857
|
const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
|
|
834
|
-
if (!eachDir || eachDir.kind !== "EachDirective") return
|
|
858
|
+
if (!eachDir || eachDir.kind !== "EachDirective") return null;
|
|
835
859
|
const each = this.hole(eachDir.hole);
|
|
836
860
|
const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
|
|
837
861
|
if (eachDir.key) {
|
|
@@ -841,12 +865,12 @@ var Lowerer = class {
|
|
|
841
865
|
const arrow = this.itemArrow(el);
|
|
842
866
|
let childrenCode;
|
|
843
867
|
if (arrow) {
|
|
844
|
-
const
|
|
845
|
-
|
|
846
|
-
childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
|
|
868
|
+
const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
|
|
869
|
+
childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
|
|
847
870
|
} else {
|
|
848
|
-
const
|
|
849
|
-
|
|
871
|
+
const content = this.lowerChildren(el.children);
|
|
872
|
+
const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
|
|
873
|
+
childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
|
|
850
874
|
}
|
|
851
875
|
const children = [{ kind: "expr", code: childrenCode, reactive: false }];
|
|
852
876
|
const at = this.tagAt(el);
|
|
@@ -863,25 +887,6 @@ var Lowerer = class {
|
|
|
863
887
|
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
864
888
|
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
865
889
|
}
|
|
866
|
-
/** Same element (minus each/key) but with explicit IR children. */
|
|
867
|
-
rebuildWithChildren(el, children) {
|
|
868
|
-
const props = this.lowerAttributes(
|
|
869
|
-
el.attributes.filter((a) => a.kind !== "EachDirective")
|
|
870
|
-
);
|
|
871
|
-
if (el.kind === "Element") {
|
|
872
|
-
const at = this.tagAt(el);
|
|
873
|
-
return {
|
|
874
|
-
kind: "element",
|
|
875
|
-
tag: el.tag,
|
|
876
|
-
svg: el.namespace === "svg",
|
|
877
|
-
props,
|
|
878
|
-
children,
|
|
879
|
-
static: false,
|
|
880
|
-
...at ? { at } : {}
|
|
881
|
-
};
|
|
882
|
-
}
|
|
883
|
-
return { kind: "component", name: el.tag, props, children };
|
|
884
|
-
}
|
|
885
890
|
// --- attributes ---------------------------------------------------------
|
|
886
891
|
lowerAttributes(attrs) {
|
|
887
892
|
const props = [];
|
|
@@ -1293,6 +1298,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
|
|
|
1293
1298
|
"mouseup"
|
|
1294
1299
|
]);
|
|
1295
1300
|
var isReactive = (node) => isReactiveShape(shapeOf(node));
|
|
1301
|
+
var CONTAINER2 = "container";
|
|
1302
|
+
function directive(node, name) {
|
|
1303
|
+
if (node.type !== "JSXElement") return void 0;
|
|
1304
|
+
const opening = node.openingElement;
|
|
1305
|
+
if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
|
|
1306
|
+
return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
|
|
1307
|
+
}
|
|
1308
|
+
function directiveExpr(attr) {
|
|
1309
|
+
const v = attr.value;
|
|
1310
|
+
return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
|
|
1311
|
+
}
|
|
1312
|
+
function emitIR(ir, ctx) {
|
|
1313
|
+
const { code, imports, templates } = imperativeBackend.emit(ir, {});
|
|
1314
|
+
for (const s of imports) ctx.used.add(s);
|
|
1315
|
+
return inlineTemplates(code, templates);
|
|
1316
|
+
}
|
|
1296
1317
|
var posOf = (node, ctx) => {
|
|
1297
1318
|
if (!ctx.sourceFile) return void 0;
|
|
1298
1319
|
const at = node.loc?.start;
|
|
@@ -1401,7 +1422,16 @@ function buildProps(attrs, ctx) {
|
|
|
1401
1422
|
}
|
|
1402
1423
|
function buildChildren(children, ctx) {
|
|
1403
1424
|
const out = [];
|
|
1404
|
-
for (
|
|
1425
|
+
for (let i = 0; i < children.length; i++) {
|
|
1426
|
+
const child = children[i];
|
|
1427
|
+
if (directive(child, "if")) {
|
|
1428
|
+
let next = i + 1;
|
|
1429
|
+
while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
|
|
1430
|
+
const fallback = directive(children[next], "else") ? children[next] : null;
|
|
1431
|
+
out.push(buildIf(child, fallback, ctx));
|
|
1432
|
+
if (fallback) i = next;
|
|
1433
|
+
continue;
|
|
1434
|
+
}
|
|
1405
1435
|
if (child.type === "JSXText") {
|
|
1406
1436
|
const text = cleanTemplateText(child.value);
|
|
1407
1437
|
if (text) out.push({ kind: "text", value: text });
|
|
@@ -1438,19 +1468,68 @@ function readLoad(attrs) {
|
|
|
1438
1468
|
}
|
|
1439
1469
|
return void 0;
|
|
1440
1470
|
}
|
|
1471
|
+
function buildIf(node, fallback, ctx) {
|
|
1472
|
+
const cond = directiveExpr(directive(node, "if"));
|
|
1473
|
+
const props = [
|
|
1474
|
+
cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
|
|
1475
|
+
];
|
|
1476
|
+
if (fallback) {
|
|
1477
|
+
props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
|
|
1478
|
+
}
|
|
1479
|
+
return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
|
|
1480
|
+
}
|
|
1481
|
+
function buildEach(node, ctx) {
|
|
1482
|
+
const attr = directive(node, "each");
|
|
1483
|
+
if (!attr) return null;
|
|
1484
|
+
const list = directiveExpr(attr);
|
|
1485
|
+
const props = [
|
|
1486
|
+
list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
|
|
1487
|
+
];
|
|
1488
|
+
const key = directive(node, "key");
|
|
1489
|
+
if (key) {
|
|
1490
|
+
const keyExpr = directiveExpr(key);
|
|
1491
|
+
const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
|
|
1492
|
+
if (by) props.push({ name: "by", kind: "attr", expr: by });
|
|
1493
|
+
}
|
|
1494
|
+
const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
|
|
1495
|
+
const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
|
|
1496
|
+
let render;
|
|
1497
|
+
if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
|
|
1498
|
+
const fn = ctx.code(only);
|
|
1499
|
+
const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
|
|
1500
|
+
render = live ? `(...args) => () => (${fn})(...args)` : fn;
|
|
1501
|
+
} else {
|
|
1502
|
+
const built = buildChildren(node.children, ctx);
|
|
1503
|
+
const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
|
|
1504
|
+
render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
|
|
1505
|
+
}
|
|
1506
|
+
return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
|
|
1507
|
+
}
|
|
1508
|
+
function buildElement(node, ctx, handled) {
|
|
1509
|
+
const skip = new Set(handled);
|
|
1510
|
+
const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
|
|
1511
|
+
const attributes = node.openingElement.attributes.filter(
|
|
1512
|
+
(a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
|
|
1513
|
+
);
|
|
1514
|
+
const { tag } = tagInfo(node.openingElement.name, ctx);
|
|
1515
|
+
const each = buildEach(node, ctx);
|
|
1516
|
+
const children = each ? [each] : buildChildren(node.children, ctx);
|
|
1517
|
+
if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
1518
|
+
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1519
|
+
return { kind: "element", tag, svg: SVG_TAGS.has(tag), props: buildProps(attributes, ctx), children, static: false, ...at ? { at } : {} };
|
|
1520
|
+
}
|
|
1441
1521
|
function buildNode(node, ctx) {
|
|
1442
1522
|
if (node.type === "JSXFragment") {
|
|
1443
1523
|
return { kind: "fragment", children: buildChildren(node.children, ctx) };
|
|
1444
1524
|
}
|
|
1525
|
+
if (directive(node, "if")) return buildIf(node, null, ctx);
|
|
1445
1526
|
const { tag, component } = tagInfo(node.openingElement.name, ctx);
|
|
1527
|
+
if (!component) return buildElement(node, ctx, []);
|
|
1446
1528
|
const props = buildProps(node.openingElement.attributes, ctx);
|
|
1447
1529
|
const children = buildChildren(node.children, ctx);
|
|
1448
1530
|
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1452
|
-
}
|
|
1453
|
-
return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
|
|
1531
|
+
const load = readLoad(node.openingElement.attributes);
|
|
1532
|
+
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1454
1533
|
}
|
|
1455
1534
|
function buildJsxIR(node, ctx) {
|
|
1456
1535
|
const ir = buildNode(node, ctx);
|
|
@@ -214,6 +214,7 @@ var INTRINSICS = {
|
|
|
214
214
|
$effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
|
|
215
215
|
$store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
|
|
216
216
|
$resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
|
|
217
|
+
$cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
|
|
217
218
|
// No short runtime form for these yet, so the intrinsic stands for the `create*`
|
|
218
219
|
// name. Still worth having: no import to write, and when a shorter form is
|
|
219
220
|
// designed, the change is this line rather than a compiler pass.
|
|
@@ -223,7 +224,22 @@ var INTRINSICS = {
|
|
|
223
224
|
// does. `untrack` suppresses dependency collection, which is exactly the kind of thing
|
|
224
225
|
// dependency analysis must not guess at.
|
|
225
226
|
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
226
|
-
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" }
|
|
227
|
+
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
|
|
228
|
+
// Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
|
|
229
|
+
// no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
|
|
230
|
+
$batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
|
|
231
|
+
$cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
|
|
232
|
+
$root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
|
|
233
|
+
$context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
|
|
234
|
+
$renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
|
|
235
|
+
$computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
|
|
236
|
+
// Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
|
|
237
|
+
// handle as far as reads go: the same accessor with `set` on it.
|
|
238
|
+
$storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
239
|
+
$asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
240
|
+
$persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
|
|
241
|
+
// An environment variable, public or server-only; the browser build refuses a secret.
|
|
242
|
+
$env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
|
|
227
243
|
};
|
|
228
244
|
var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join("|")})\\s*\\(`);
|
|
229
245
|
|
|
@@ -399,13 +415,16 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
399
415
|
used.add("createComponent");
|
|
400
416
|
const spreads = props.filter((p) => p.kind === "spread");
|
|
401
417
|
const normal = props.filter((p) => p.kind !== "spread");
|
|
402
|
-
const
|
|
403
|
-
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
404
|
-
);
|
|
418
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
405
419
|
if (children.length > 0) {
|
|
406
|
-
|
|
420
|
+
bodies.push(["children", emitChildren(children, used, templates)]);
|
|
421
|
+
}
|
|
422
|
+
let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
|
|
423
|
+
if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
|
|
424
|
+
const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
|
|
425
|
+
const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
|
|
426
|
+
obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
|
|
407
427
|
}
|
|
408
|
-
let obj = `{ ${entries.join(", ")} }`;
|
|
409
428
|
if (spreads.length > 0) {
|
|
410
429
|
used.add("mergeProps");
|
|
411
430
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
@@ -487,7 +506,8 @@ function emitNode(node, used, templates) {
|
|
|
487
506
|
} else {
|
|
488
507
|
used.add("spread");
|
|
489
508
|
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
490
|
-
const
|
|
509
|
+
const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
|
|
510
|
+
const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
|
|
491
511
|
parts.push(
|
|
492
512
|
`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
|
|
493
513
|
);
|
|
@@ -616,6 +636,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
|
|
|
616
636
|
function escapeTemplate(s) {
|
|
617
637
|
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
618
638
|
}
|
|
639
|
+
var CONTAINER = "container";
|
|
619
640
|
function capitalize(s) {
|
|
620
641
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
621
642
|
}
|
|
@@ -670,10 +691,6 @@ var Lowerer = class {
|
|
|
670
691
|
if (hasElse) i = elseIdx;
|
|
671
692
|
continue;
|
|
672
693
|
}
|
|
673
|
-
if (child.attributes.some((a) => a.kind === "EachDirective")) {
|
|
674
|
-
out.push(this.lowerEach(child));
|
|
675
|
-
continue;
|
|
676
|
-
}
|
|
677
694
|
}
|
|
678
695
|
const node = this.lowerNode(child);
|
|
679
696
|
if (node) out.push(node);
|
|
@@ -715,13 +732,16 @@ var Lowerer = class {
|
|
|
715
732
|
node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
|
|
716
733
|
return node;
|
|
717
734
|
}
|
|
735
|
+
const each = this.lowerEach(el);
|
|
736
|
+
const children = each ? [each] : this.lowerChildren(el.children);
|
|
737
|
+
if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
718
738
|
const at = this.tagAt(el);
|
|
719
739
|
return {
|
|
720
740
|
kind: "element",
|
|
721
741
|
tag: el.tag,
|
|
722
742
|
svg: el.namespace === "svg",
|
|
723
743
|
props: this.lowerAttributes(el.attributes),
|
|
724
|
-
children
|
|
744
|
+
children,
|
|
725
745
|
static: false,
|
|
726
746
|
...at ? { at } : {}
|
|
727
747
|
};
|
|
@@ -793,10 +813,14 @@ var Lowerer = class {
|
|
|
793
813
|
const at = this.tagAt(el);
|
|
794
814
|
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
795
815
|
}
|
|
796
|
-
/**
|
|
816
|
+
/**
|
|
817
|
+
* `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
|
|
818
|
+
* content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
|
|
819
|
+
* it is. Null when the element has no `each`.
|
|
820
|
+
*/
|
|
797
821
|
lowerEach(el) {
|
|
798
822
|
const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
|
|
799
|
-
if (!eachDir || eachDir.kind !== "EachDirective") return
|
|
823
|
+
if (!eachDir || eachDir.kind !== "EachDirective") return null;
|
|
800
824
|
const each = this.hole(eachDir.hole);
|
|
801
825
|
const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
|
|
802
826
|
if (eachDir.key) {
|
|
@@ -806,12 +830,12 @@ var Lowerer = class {
|
|
|
806
830
|
const arrow = this.itemArrow(el);
|
|
807
831
|
let childrenCode;
|
|
808
832
|
if (arrow) {
|
|
809
|
-
const
|
|
810
|
-
|
|
811
|
-
childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
|
|
833
|
+
const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
|
|
834
|
+
childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
|
|
812
835
|
} else {
|
|
813
|
-
const
|
|
814
|
-
|
|
836
|
+
const content = this.lowerChildren(el.children);
|
|
837
|
+
const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
|
|
838
|
+
childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
|
|
815
839
|
}
|
|
816
840
|
const children = [{ kind: "expr", code: childrenCode, reactive: false }];
|
|
817
841
|
const at = this.tagAt(el);
|
|
@@ -828,25 +852,6 @@ var Lowerer = class {
|
|
|
828
852
|
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
829
853
|
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
830
854
|
}
|
|
831
|
-
/** Same element (minus each/key) but with explicit IR children. */
|
|
832
|
-
rebuildWithChildren(el, children) {
|
|
833
|
-
const props = this.lowerAttributes(
|
|
834
|
-
el.attributes.filter((a) => a.kind !== "EachDirective")
|
|
835
|
-
);
|
|
836
|
-
if (el.kind === "Element") {
|
|
837
|
-
const at = this.tagAt(el);
|
|
838
|
-
return {
|
|
839
|
-
kind: "element",
|
|
840
|
-
tag: el.tag,
|
|
841
|
-
svg: el.namespace === "svg",
|
|
842
|
-
props,
|
|
843
|
-
children,
|
|
844
|
-
static: false,
|
|
845
|
-
...at ? { at } : {}
|
|
846
|
-
};
|
|
847
|
-
}
|
|
848
|
-
return { kind: "component", name: el.tag, props, children };
|
|
849
|
-
}
|
|
850
855
|
// --- attributes ---------------------------------------------------------
|
|
851
856
|
lowerAttributes(attrs) {
|
|
852
857
|
const props = [];
|
|
@@ -1258,6 +1263,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
|
|
|
1258
1263
|
"mouseup"
|
|
1259
1264
|
]);
|
|
1260
1265
|
var isReactive = (node) => isReactiveShape(shapeOf(node));
|
|
1266
|
+
var CONTAINER2 = "container";
|
|
1267
|
+
function directive(node, name) {
|
|
1268
|
+
if (node.type !== "JSXElement") return void 0;
|
|
1269
|
+
const opening = node.openingElement;
|
|
1270
|
+
if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
|
|
1271
|
+
return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
|
|
1272
|
+
}
|
|
1273
|
+
function directiveExpr(attr) {
|
|
1274
|
+
const v = attr.value;
|
|
1275
|
+
return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
|
|
1276
|
+
}
|
|
1277
|
+
function emitIR(ir, ctx) {
|
|
1278
|
+
const { code, imports, templates } = imperativeBackend.emit(ir, {});
|
|
1279
|
+
for (const s of imports) ctx.used.add(s);
|
|
1280
|
+
return inlineTemplates(code, templates);
|
|
1281
|
+
}
|
|
1261
1282
|
var posOf = (node, ctx) => {
|
|
1262
1283
|
if (!ctx.sourceFile) return void 0;
|
|
1263
1284
|
const at = node.loc?.start;
|
|
@@ -1366,7 +1387,16 @@ function buildProps(attrs, ctx) {
|
|
|
1366
1387
|
}
|
|
1367
1388
|
function buildChildren(children, ctx) {
|
|
1368
1389
|
const out = [];
|
|
1369
|
-
for (
|
|
1390
|
+
for (let i = 0; i < children.length; i++) {
|
|
1391
|
+
const child = children[i];
|
|
1392
|
+
if (directive(child, "if")) {
|
|
1393
|
+
let next = i + 1;
|
|
1394
|
+
while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
|
|
1395
|
+
const fallback = directive(children[next], "else") ? children[next] : null;
|
|
1396
|
+
out.push(buildIf(child, fallback, ctx));
|
|
1397
|
+
if (fallback) i = next;
|
|
1398
|
+
continue;
|
|
1399
|
+
}
|
|
1370
1400
|
if (child.type === "JSXText") {
|
|
1371
1401
|
const text = cleanTemplateText(child.value);
|
|
1372
1402
|
if (text) out.push({ kind: "text", value: text });
|
|
@@ -1403,19 +1433,68 @@ function readLoad(attrs) {
|
|
|
1403
1433
|
}
|
|
1404
1434
|
return void 0;
|
|
1405
1435
|
}
|
|
1436
|
+
function buildIf(node, fallback, ctx) {
|
|
1437
|
+
const cond = directiveExpr(directive(node, "if"));
|
|
1438
|
+
const props = [
|
|
1439
|
+
cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
|
|
1440
|
+
];
|
|
1441
|
+
if (fallback) {
|
|
1442
|
+
props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
|
|
1443
|
+
}
|
|
1444
|
+
return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
|
|
1445
|
+
}
|
|
1446
|
+
function buildEach(node, ctx) {
|
|
1447
|
+
const attr = directive(node, "each");
|
|
1448
|
+
if (!attr) return null;
|
|
1449
|
+
const list = directiveExpr(attr);
|
|
1450
|
+
const props = [
|
|
1451
|
+
list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
|
|
1452
|
+
];
|
|
1453
|
+
const key = directive(node, "key");
|
|
1454
|
+
if (key) {
|
|
1455
|
+
const keyExpr = directiveExpr(key);
|
|
1456
|
+
const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
|
|
1457
|
+
if (by) props.push({ name: "by", kind: "attr", expr: by });
|
|
1458
|
+
}
|
|
1459
|
+
const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
|
|
1460
|
+
const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
|
|
1461
|
+
let render;
|
|
1462
|
+
if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
|
|
1463
|
+
const fn = ctx.code(only);
|
|
1464
|
+
const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
|
|
1465
|
+
render = live ? `(...args) => () => (${fn})(...args)` : fn;
|
|
1466
|
+
} else {
|
|
1467
|
+
const built = buildChildren(node.children, ctx);
|
|
1468
|
+
const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
|
|
1469
|
+
render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
|
|
1470
|
+
}
|
|
1471
|
+
return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
|
|
1472
|
+
}
|
|
1473
|
+
function buildElement(node, ctx, handled) {
|
|
1474
|
+
const skip = new Set(handled);
|
|
1475
|
+
const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
|
|
1476
|
+
const attributes = node.openingElement.attributes.filter(
|
|
1477
|
+
(a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
|
|
1478
|
+
);
|
|
1479
|
+
const { tag } = tagInfo(node.openingElement.name, ctx);
|
|
1480
|
+
const each = buildEach(node, ctx);
|
|
1481
|
+
const children = each ? [each] : buildChildren(node.children, ctx);
|
|
1482
|
+
if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
1483
|
+
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1484
|
+
return { kind: "element", tag, svg: SVG_TAGS.has(tag), props: buildProps(attributes, ctx), children, static: false, ...at ? { at } : {} };
|
|
1485
|
+
}
|
|
1406
1486
|
function buildNode(node, ctx) {
|
|
1407
1487
|
if (node.type === "JSXFragment") {
|
|
1408
1488
|
return { kind: "fragment", children: buildChildren(node.children, ctx) };
|
|
1409
1489
|
}
|
|
1490
|
+
if (directive(node, "if")) return buildIf(node, null, ctx);
|
|
1410
1491
|
const { tag, component } = tagInfo(node.openingElement.name, ctx);
|
|
1492
|
+
if (!component) return buildElement(node, ctx, []);
|
|
1411
1493
|
const props = buildProps(node.openingElement.attributes, ctx);
|
|
1412
1494
|
const children = buildChildren(node.children, ctx);
|
|
1413
1495
|
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1417
|
-
}
|
|
1418
|
-
return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
|
|
1496
|
+
const load = readLoad(node.openingElement.attributes);
|
|
1497
|
+
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1419
1498
|
}
|
|
1420
1499
|
function buildJsxIR(node, ctx) {
|
|
1421
1500
|
const ir = buildNode(node, ctx);
|