@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
|
@@ -302,6 +302,7 @@ var INTRINSICS = {
|
|
|
302
302
|
$effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
|
|
303
303
|
$store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
|
|
304
304
|
$resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
|
|
305
|
+
$cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
|
|
305
306
|
// No short runtime form for these yet, so the intrinsic stands for the `create*`
|
|
306
307
|
// name. Still worth having: no import to write, and when a shorter form is
|
|
307
308
|
// designed, the change is this line rather than a compiler pass.
|
|
@@ -311,7 +312,22 @@ var INTRINSICS = {
|
|
|
311
312
|
// does. `untrack` suppresses dependency collection, which is exactly the kind of thing
|
|
312
313
|
// dependency analysis must not guess at.
|
|
313
314
|
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
314
|
-
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" }
|
|
315
|
+
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
|
|
316
|
+
// Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
|
|
317
|
+
// no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
|
|
318
|
+
$batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
|
|
319
|
+
$cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
|
|
320
|
+
$root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
|
|
321
|
+
$context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
|
|
322
|
+
$renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
|
|
323
|
+
$computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
|
|
324
|
+
// Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
|
|
325
|
+
// handle as far as reads go: the same accessor with `set` on it.
|
|
326
|
+
$storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
327
|
+
$asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
328
|
+
$persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
|
|
329
|
+
// An environment variable, public or server-only; the browser build refuses a secret.
|
|
330
|
+
$env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
|
|
315
331
|
};
|
|
316
332
|
var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join("|")})\\s*\\(`);
|
|
317
333
|
|
|
@@ -487,13 +503,16 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
487
503
|
used.add("createComponent");
|
|
488
504
|
const spreads = props.filter((p) => p.kind === "spread");
|
|
489
505
|
const normal = props.filter((p) => p.kind !== "spread");
|
|
490
|
-
const
|
|
491
|
-
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
492
|
-
);
|
|
506
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
493
507
|
if (children.length > 0) {
|
|
494
|
-
|
|
508
|
+
bodies.push(["children", emitChildren(children, used, templates)]);
|
|
509
|
+
}
|
|
510
|
+
let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
|
|
511
|
+
if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
|
|
512
|
+
const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
|
|
513
|
+
const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
|
|
514
|
+
obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
|
|
495
515
|
}
|
|
496
|
-
let obj = `{ ${entries.join(", ")} }`;
|
|
497
516
|
if (spreads.length > 0) {
|
|
498
517
|
used.add("mergeProps");
|
|
499
518
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
@@ -575,7 +594,8 @@ function emitNode(node, used, templates) {
|
|
|
575
594
|
} else {
|
|
576
595
|
used.add("spread");
|
|
577
596
|
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
578
|
-
const
|
|
597
|
+
const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
|
|
598
|
+
const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
|
|
579
599
|
parts.push(
|
|
580
600
|
`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
|
|
581
601
|
);
|
|
@@ -646,6 +666,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
|
|
|
646
666
|
function escapeTemplate(s) {
|
|
647
667
|
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
648
668
|
}
|
|
669
|
+
var CONTAINER = "container";
|
|
649
670
|
function capitalize(s) {
|
|
650
671
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
651
672
|
}
|
|
@@ -700,10 +721,6 @@ var Lowerer = class {
|
|
|
700
721
|
if (hasElse) i = elseIdx;
|
|
701
722
|
continue;
|
|
702
723
|
}
|
|
703
|
-
if (child.attributes.some((a) => a.kind === "EachDirective")) {
|
|
704
|
-
out.push(this.lowerEach(child));
|
|
705
|
-
continue;
|
|
706
|
-
}
|
|
707
724
|
}
|
|
708
725
|
const node = this.lowerNode(child);
|
|
709
726
|
if (node) out.push(node);
|
|
@@ -745,13 +762,16 @@ var Lowerer = class {
|
|
|
745
762
|
node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
|
|
746
763
|
return node;
|
|
747
764
|
}
|
|
765
|
+
const each = this.lowerEach(el);
|
|
766
|
+
const children = each ? [each] : this.lowerChildren(el.children);
|
|
767
|
+
if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
748
768
|
const at = this.tagAt(el);
|
|
749
769
|
return {
|
|
750
770
|
kind: "element",
|
|
751
771
|
tag: el.tag,
|
|
752
772
|
svg: el.namespace === "svg",
|
|
753
773
|
props: this.lowerAttributes(el.attributes),
|
|
754
|
-
children
|
|
774
|
+
children,
|
|
755
775
|
static: false,
|
|
756
776
|
...at ? { at } : {}
|
|
757
777
|
};
|
|
@@ -823,10 +843,14 @@ var Lowerer = class {
|
|
|
823
843
|
const at = this.tagAt(el);
|
|
824
844
|
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
825
845
|
}
|
|
826
|
-
/**
|
|
846
|
+
/**
|
|
847
|
+
* `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
|
|
848
|
+
* content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
|
|
849
|
+
* it is. Null when the element has no `each`.
|
|
850
|
+
*/
|
|
827
851
|
lowerEach(el) {
|
|
828
852
|
const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
|
|
829
|
-
if (!eachDir || eachDir.kind !== "EachDirective") return
|
|
853
|
+
if (!eachDir || eachDir.kind !== "EachDirective") return null;
|
|
830
854
|
const each = this.hole(eachDir.hole);
|
|
831
855
|
const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
|
|
832
856
|
if (eachDir.key) {
|
|
@@ -836,12 +860,12 @@ var Lowerer = class {
|
|
|
836
860
|
const arrow = this.itemArrow(el);
|
|
837
861
|
let childrenCode;
|
|
838
862
|
if (arrow) {
|
|
839
|
-
const
|
|
840
|
-
|
|
841
|
-
childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
|
|
863
|
+
const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
|
|
864
|
+
childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
|
|
842
865
|
} else {
|
|
843
|
-
const
|
|
844
|
-
|
|
866
|
+
const content = this.lowerChildren(el.children);
|
|
867
|
+
const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
|
|
868
|
+
childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
|
|
845
869
|
}
|
|
846
870
|
const children = [{ kind: "expr", code: childrenCode, reactive: false }];
|
|
847
871
|
const at = this.tagAt(el);
|
|
@@ -858,25 +882,6 @@ var Lowerer = class {
|
|
|
858
882
|
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
859
883
|
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
860
884
|
}
|
|
861
|
-
/** Same element (minus each/key) but with explicit IR children. */
|
|
862
|
-
rebuildWithChildren(el, children) {
|
|
863
|
-
const props = this.lowerAttributes(
|
|
864
|
-
el.attributes.filter((a) => a.kind !== "EachDirective")
|
|
865
|
-
);
|
|
866
|
-
if (el.kind === "Element") {
|
|
867
|
-
const at = this.tagAt(el);
|
|
868
|
-
return {
|
|
869
|
-
kind: "element",
|
|
870
|
-
tag: el.tag,
|
|
871
|
-
svg: el.namespace === "svg",
|
|
872
|
-
props,
|
|
873
|
-
children,
|
|
874
|
-
static: false,
|
|
875
|
-
...at ? { at } : {}
|
|
876
|
-
};
|
|
877
|
-
}
|
|
878
|
-
return { kind: "component", name: el.tag, props, children };
|
|
879
|
-
}
|
|
880
885
|
// --- attributes ---------------------------------------------------------
|
|
881
886
|
lowerAttributes(attrs) {
|
|
882
887
|
const props = [];
|
|
@@ -1246,6 +1251,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
|
|
|
1246
1251
|
"mouseup"
|
|
1247
1252
|
]);
|
|
1248
1253
|
var isReactive = (node) => isReactiveShape(shapeOf(node));
|
|
1254
|
+
var CONTAINER2 = "container";
|
|
1255
|
+
function directive(node, name) {
|
|
1256
|
+
if (node.type !== "JSXElement") return void 0;
|
|
1257
|
+
const opening = node.openingElement;
|
|
1258
|
+
if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
|
|
1259
|
+
return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
|
|
1260
|
+
}
|
|
1261
|
+
function directiveExpr(attr) {
|
|
1262
|
+
const v = attr.value;
|
|
1263
|
+
return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
|
|
1264
|
+
}
|
|
1265
|
+
function emitIR(ir, ctx) {
|
|
1266
|
+
const { code, imports, templates } = imperativeBackend.emit(ir, {});
|
|
1267
|
+
for (const s of imports) ctx.used.add(s);
|
|
1268
|
+
return inlineTemplates(code, templates);
|
|
1269
|
+
}
|
|
1249
1270
|
var posOf = (node, ctx) => {
|
|
1250
1271
|
if (!ctx.sourceFile) return void 0;
|
|
1251
1272
|
const at = node.loc?.start;
|
|
@@ -1354,7 +1375,16 @@ function buildProps(attrs, ctx) {
|
|
|
1354
1375
|
}
|
|
1355
1376
|
function buildChildren(children, ctx) {
|
|
1356
1377
|
const out = [];
|
|
1357
|
-
for (
|
|
1378
|
+
for (let i = 0; i < children.length; i++) {
|
|
1379
|
+
const child = children[i];
|
|
1380
|
+
if (directive(child, "if")) {
|
|
1381
|
+
let next = i + 1;
|
|
1382
|
+
while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
|
|
1383
|
+
const fallback = directive(children[next], "else") ? children[next] : null;
|
|
1384
|
+
out.push(buildIf(child, fallback, ctx));
|
|
1385
|
+
if (fallback) i = next;
|
|
1386
|
+
continue;
|
|
1387
|
+
}
|
|
1358
1388
|
if (child.type === "JSXText") {
|
|
1359
1389
|
const text = cleanTemplateText(child.value);
|
|
1360
1390
|
if (text) out.push({ kind: "text", value: text });
|
|
@@ -1391,19 +1421,68 @@ function readLoad(attrs) {
|
|
|
1391
1421
|
}
|
|
1392
1422
|
return void 0;
|
|
1393
1423
|
}
|
|
1424
|
+
function buildIf(node, fallback, ctx) {
|
|
1425
|
+
const cond = directiveExpr(directive(node, "if"));
|
|
1426
|
+
const props = [
|
|
1427
|
+
cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
|
|
1428
|
+
];
|
|
1429
|
+
if (fallback) {
|
|
1430
|
+
props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
|
|
1431
|
+
}
|
|
1432
|
+
return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
|
|
1433
|
+
}
|
|
1434
|
+
function buildEach(node, ctx) {
|
|
1435
|
+
const attr = directive(node, "each");
|
|
1436
|
+
if (!attr) return null;
|
|
1437
|
+
const list = directiveExpr(attr);
|
|
1438
|
+
const props = [
|
|
1439
|
+
list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
|
|
1440
|
+
];
|
|
1441
|
+
const key = directive(node, "key");
|
|
1442
|
+
if (key) {
|
|
1443
|
+
const keyExpr = directiveExpr(key);
|
|
1444
|
+
const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
|
|
1445
|
+
if (by) props.push({ name: "by", kind: "attr", expr: by });
|
|
1446
|
+
}
|
|
1447
|
+
const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
|
|
1448
|
+
const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
|
|
1449
|
+
let render;
|
|
1450
|
+
if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
|
|
1451
|
+
const fn = ctx.code(only);
|
|
1452
|
+
const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
|
|
1453
|
+
render = live ? `(...args) => () => (${fn})(...args)` : fn;
|
|
1454
|
+
} else {
|
|
1455
|
+
const built = buildChildren(node.children, ctx);
|
|
1456
|
+
const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
|
|
1457
|
+
render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
|
|
1458
|
+
}
|
|
1459
|
+
return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
|
|
1460
|
+
}
|
|
1461
|
+
function buildElement(node, ctx, handled) {
|
|
1462
|
+
const skip = new Set(handled);
|
|
1463
|
+
const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
|
|
1464
|
+
const attributes = node.openingElement.attributes.filter(
|
|
1465
|
+
(a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
|
|
1466
|
+
);
|
|
1467
|
+
const { tag } = tagInfo(node.openingElement.name, ctx);
|
|
1468
|
+
const each = buildEach(node, ctx);
|
|
1469
|
+
const children = each ? [each] : buildChildren(node.children, ctx);
|
|
1470
|
+
if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
1471
|
+
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1472
|
+
return { kind: "element", tag, svg: SVG_TAGS.has(tag), props: buildProps(attributes, ctx), children, static: false, ...at ? { at } : {} };
|
|
1473
|
+
}
|
|
1394
1474
|
function buildNode(node, ctx) {
|
|
1395
1475
|
if (node.type === "JSXFragment") {
|
|
1396
1476
|
return { kind: "fragment", children: buildChildren(node.children, ctx) };
|
|
1397
1477
|
}
|
|
1478
|
+
if (directive(node, "if")) return buildIf(node, null, ctx);
|
|
1398
1479
|
const { tag, component } = tagInfo(node.openingElement.name, ctx);
|
|
1480
|
+
if (!component) return buildElement(node, ctx, []);
|
|
1399
1481
|
const props = buildProps(node.openingElement.attributes, ctx);
|
|
1400
1482
|
const children = buildChildren(node.children, ctx);
|
|
1401
1483
|
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1405
|
-
}
|
|
1406
|
-
return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
|
|
1484
|
+
const load = readLoad(node.openingElement.attributes);
|
|
1485
|
+
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1407
1486
|
}
|
|
1408
1487
|
function buildJsxIR(node, ctx) {
|
|
1409
1488
|
const ir = buildNode(node, ctx);
|
|
@@ -268,6 +268,7 @@ var INTRINSICS = {
|
|
|
268
268
|
$effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
|
|
269
269
|
$store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
|
|
270
270
|
$resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
|
|
271
|
+
$cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
|
|
271
272
|
// No short runtime form for these yet, so the intrinsic stands for the `create*`
|
|
272
273
|
// name. Still worth having: no import to write, and when a shorter form is
|
|
273
274
|
// designed, the change is this line rather than a compiler pass.
|
|
@@ -277,7 +278,22 @@ var INTRINSICS = {
|
|
|
277
278
|
// does. `untrack` suppresses dependency collection, which is exactly the kind of thing
|
|
278
279
|
// dependency analysis must not guess at.
|
|
279
280
|
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
280
|
-
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" }
|
|
281
|
+
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
|
|
282
|
+
// Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
|
|
283
|
+
// no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
|
|
284
|
+
$batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
|
|
285
|
+
$cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
|
|
286
|
+
$root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
|
|
287
|
+
$context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
|
|
288
|
+
$renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
|
|
289
|
+
$computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
|
|
290
|
+
// Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
|
|
291
|
+
// handle as far as reads go: the same accessor with `set` on it.
|
|
292
|
+
$storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
293
|
+
$asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
294
|
+
$persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
|
|
295
|
+
// An environment variable, public or server-only; the browser build refuses a secret.
|
|
296
|
+
$env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
|
|
281
297
|
};
|
|
282
298
|
var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join("|")})\\s*\\(`);
|
|
283
299
|
|
|
@@ -453,13 +469,16 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
453
469
|
used.add("createComponent");
|
|
454
470
|
const spreads = props.filter((p) => p.kind === "spread");
|
|
455
471
|
const normal = props.filter((p) => p.kind !== "spread");
|
|
456
|
-
const
|
|
457
|
-
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
458
|
-
);
|
|
472
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
459
473
|
if (children.length > 0) {
|
|
460
|
-
|
|
474
|
+
bodies.push(["children", emitChildren(children, used, templates)]);
|
|
475
|
+
}
|
|
476
|
+
let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
|
|
477
|
+
if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
|
|
478
|
+
const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
|
|
479
|
+
const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
|
|
480
|
+
obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
|
|
461
481
|
}
|
|
462
|
-
let obj = `{ ${entries.join(", ")} }`;
|
|
463
482
|
if (spreads.length > 0) {
|
|
464
483
|
used.add("mergeProps");
|
|
465
484
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
@@ -541,7 +560,8 @@ function emitNode(node, used, templates) {
|
|
|
541
560
|
} else {
|
|
542
561
|
used.add("spread");
|
|
543
562
|
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
544
|
-
const
|
|
563
|
+
const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
|
|
564
|
+
const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
|
|
545
565
|
parts.push(
|
|
546
566
|
`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
|
|
547
567
|
);
|
|
@@ -612,6 +632,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
|
|
|
612
632
|
function escapeTemplate(s) {
|
|
613
633
|
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
614
634
|
}
|
|
635
|
+
var CONTAINER = "container";
|
|
615
636
|
function capitalize(s) {
|
|
616
637
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
617
638
|
}
|
|
@@ -666,10 +687,6 @@ var Lowerer = class {
|
|
|
666
687
|
if (hasElse) i = elseIdx;
|
|
667
688
|
continue;
|
|
668
689
|
}
|
|
669
|
-
if (child.attributes.some((a) => a.kind === "EachDirective")) {
|
|
670
|
-
out.push(this.lowerEach(child));
|
|
671
|
-
continue;
|
|
672
|
-
}
|
|
673
690
|
}
|
|
674
691
|
const node = this.lowerNode(child);
|
|
675
692
|
if (node) out.push(node);
|
|
@@ -711,13 +728,16 @@ var Lowerer = class {
|
|
|
711
728
|
node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
|
|
712
729
|
return node;
|
|
713
730
|
}
|
|
731
|
+
const each = this.lowerEach(el);
|
|
732
|
+
const children = each ? [each] : this.lowerChildren(el.children);
|
|
733
|
+
if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
714
734
|
const at = this.tagAt(el);
|
|
715
735
|
return {
|
|
716
736
|
kind: "element",
|
|
717
737
|
tag: el.tag,
|
|
718
738
|
svg: el.namespace === "svg",
|
|
719
739
|
props: this.lowerAttributes(el.attributes),
|
|
720
|
-
children
|
|
740
|
+
children,
|
|
721
741
|
static: false,
|
|
722
742
|
...at ? { at } : {}
|
|
723
743
|
};
|
|
@@ -789,10 +809,14 @@ var Lowerer = class {
|
|
|
789
809
|
const at = this.tagAt(el);
|
|
790
810
|
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
791
811
|
}
|
|
792
|
-
/**
|
|
812
|
+
/**
|
|
813
|
+
* `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
|
|
814
|
+
* content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
|
|
815
|
+
* it is. Null when the element has no `each`.
|
|
816
|
+
*/
|
|
793
817
|
lowerEach(el) {
|
|
794
818
|
const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
|
|
795
|
-
if (!eachDir || eachDir.kind !== "EachDirective") return
|
|
819
|
+
if (!eachDir || eachDir.kind !== "EachDirective") return null;
|
|
796
820
|
const each = this.hole(eachDir.hole);
|
|
797
821
|
const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
|
|
798
822
|
if (eachDir.key) {
|
|
@@ -802,12 +826,12 @@ var Lowerer = class {
|
|
|
802
826
|
const arrow = this.itemArrow(el);
|
|
803
827
|
let childrenCode;
|
|
804
828
|
if (arrow) {
|
|
805
|
-
const
|
|
806
|
-
|
|
807
|
-
childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
|
|
829
|
+
const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
|
|
830
|
+
childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
|
|
808
831
|
} else {
|
|
809
|
-
const
|
|
810
|
-
|
|
832
|
+
const content = this.lowerChildren(el.children);
|
|
833
|
+
const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
|
|
834
|
+
childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
|
|
811
835
|
}
|
|
812
836
|
const children = [{ kind: "expr", code: childrenCode, reactive: false }];
|
|
813
837
|
const at = this.tagAt(el);
|
|
@@ -824,25 +848,6 @@ var Lowerer = class {
|
|
|
824
848
|
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
825
849
|
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
826
850
|
}
|
|
827
|
-
/** Same element (minus each/key) but with explicit IR children. */
|
|
828
|
-
rebuildWithChildren(el, children) {
|
|
829
|
-
const props = this.lowerAttributes(
|
|
830
|
-
el.attributes.filter((a) => a.kind !== "EachDirective")
|
|
831
|
-
);
|
|
832
|
-
if (el.kind === "Element") {
|
|
833
|
-
const at = this.tagAt(el);
|
|
834
|
-
return {
|
|
835
|
-
kind: "element",
|
|
836
|
-
tag: el.tag,
|
|
837
|
-
svg: el.namespace === "svg",
|
|
838
|
-
props,
|
|
839
|
-
children,
|
|
840
|
-
static: false,
|
|
841
|
-
...at ? { at } : {}
|
|
842
|
-
};
|
|
843
|
-
}
|
|
844
|
-
return { kind: "component", name: el.tag, props, children };
|
|
845
|
-
}
|
|
846
851
|
// --- attributes ---------------------------------------------------------
|
|
847
852
|
lowerAttributes(attrs) {
|
|
848
853
|
const props = [];
|
|
@@ -1212,6 +1217,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
|
|
|
1212
1217
|
"mouseup"
|
|
1213
1218
|
]);
|
|
1214
1219
|
var isReactive = (node) => isReactiveShape(shapeOf(node));
|
|
1220
|
+
var CONTAINER2 = "container";
|
|
1221
|
+
function directive(node, name) {
|
|
1222
|
+
if (node.type !== "JSXElement") return void 0;
|
|
1223
|
+
const opening = node.openingElement;
|
|
1224
|
+
if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
|
|
1225
|
+
return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
|
|
1226
|
+
}
|
|
1227
|
+
function directiveExpr(attr) {
|
|
1228
|
+
const v = attr.value;
|
|
1229
|
+
return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
|
|
1230
|
+
}
|
|
1231
|
+
function emitIR(ir, ctx) {
|
|
1232
|
+
const { code, imports, templates } = imperativeBackend.emit(ir, {});
|
|
1233
|
+
for (const s of imports) ctx.used.add(s);
|
|
1234
|
+
return inlineTemplates(code, templates);
|
|
1235
|
+
}
|
|
1215
1236
|
var posOf = (node, ctx) => {
|
|
1216
1237
|
if (!ctx.sourceFile) return void 0;
|
|
1217
1238
|
const at = node.loc?.start;
|
|
@@ -1320,7 +1341,16 @@ function buildProps(attrs, ctx) {
|
|
|
1320
1341
|
}
|
|
1321
1342
|
function buildChildren(children, ctx) {
|
|
1322
1343
|
const out = [];
|
|
1323
|
-
for (
|
|
1344
|
+
for (let i = 0; i < children.length; i++) {
|
|
1345
|
+
const child = children[i];
|
|
1346
|
+
if (directive(child, "if")) {
|
|
1347
|
+
let next = i + 1;
|
|
1348
|
+
while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
|
|
1349
|
+
const fallback = directive(children[next], "else") ? children[next] : null;
|
|
1350
|
+
out.push(buildIf(child, fallback, ctx));
|
|
1351
|
+
if (fallback) i = next;
|
|
1352
|
+
continue;
|
|
1353
|
+
}
|
|
1324
1354
|
if (child.type === "JSXText") {
|
|
1325
1355
|
const text = cleanTemplateText(child.value);
|
|
1326
1356
|
if (text) out.push({ kind: "text", value: text });
|
|
@@ -1357,19 +1387,68 @@ function readLoad(attrs) {
|
|
|
1357
1387
|
}
|
|
1358
1388
|
return void 0;
|
|
1359
1389
|
}
|
|
1390
|
+
function buildIf(node, fallback, ctx) {
|
|
1391
|
+
const cond = directiveExpr(directive(node, "if"));
|
|
1392
|
+
const props = [
|
|
1393
|
+
cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
|
|
1394
|
+
];
|
|
1395
|
+
if (fallback) {
|
|
1396
|
+
props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
|
|
1397
|
+
}
|
|
1398
|
+
return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
|
|
1399
|
+
}
|
|
1400
|
+
function buildEach(node, ctx) {
|
|
1401
|
+
const attr = directive(node, "each");
|
|
1402
|
+
if (!attr) return null;
|
|
1403
|
+
const list = directiveExpr(attr);
|
|
1404
|
+
const props = [
|
|
1405
|
+
list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
|
|
1406
|
+
];
|
|
1407
|
+
const key = directive(node, "key");
|
|
1408
|
+
if (key) {
|
|
1409
|
+
const keyExpr = directiveExpr(key);
|
|
1410
|
+
const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
|
|
1411
|
+
if (by) props.push({ name: "by", kind: "attr", expr: by });
|
|
1412
|
+
}
|
|
1413
|
+
const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
|
|
1414
|
+
const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
|
|
1415
|
+
let render;
|
|
1416
|
+
if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
|
|
1417
|
+
const fn = ctx.code(only);
|
|
1418
|
+
const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
|
|
1419
|
+
render = live ? `(...args) => () => (${fn})(...args)` : fn;
|
|
1420
|
+
} else {
|
|
1421
|
+
const built = buildChildren(node.children, ctx);
|
|
1422
|
+
const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
|
|
1423
|
+
render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
|
|
1424
|
+
}
|
|
1425
|
+
return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
|
|
1426
|
+
}
|
|
1427
|
+
function buildElement(node, ctx, handled) {
|
|
1428
|
+
const skip = new Set(handled);
|
|
1429
|
+
const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
|
|
1430
|
+
const attributes = node.openingElement.attributes.filter(
|
|
1431
|
+
(a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
|
|
1432
|
+
);
|
|
1433
|
+
const { tag } = tagInfo(node.openingElement.name, ctx);
|
|
1434
|
+
const each = buildEach(node, ctx);
|
|
1435
|
+
const children = each ? [each] : buildChildren(node.children, ctx);
|
|
1436
|
+
if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
1437
|
+
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1438
|
+
return { kind: "element", tag, svg: SVG_TAGS.has(tag), props: buildProps(attributes, ctx), children, static: false, ...at ? { at } : {} };
|
|
1439
|
+
}
|
|
1360
1440
|
function buildNode(node, ctx) {
|
|
1361
1441
|
if (node.type === "JSXFragment") {
|
|
1362
1442
|
return { kind: "fragment", children: buildChildren(node.children, ctx) };
|
|
1363
1443
|
}
|
|
1444
|
+
if (directive(node, "if")) return buildIf(node, null, ctx);
|
|
1364
1445
|
const { tag, component } = tagInfo(node.openingElement.name, ctx);
|
|
1446
|
+
if (!component) return buildElement(node, ctx, []);
|
|
1365
1447
|
const props = buildProps(node.openingElement.attributes, ctx);
|
|
1366
1448
|
const children = buildChildren(node.children, ctx);
|
|
1367
1449
|
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1371
|
-
}
|
|
1372
|
-
return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
|
|
1450
|
+
const load = readLoad(node.openingElement.attributes);
|
|
1451
|
+
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1373
1452
|
}
|
|
1374
1453
|
function buildJsxIR(node, ctx) {
|
|
1375
1454
|
const ir = buildNode(node, ctx);
|