@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
|
@@ -300,6 +300,7 @@ var INTRINSICS = {
|
|
|
300
300
|
$effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
|
|
301
301
|
$store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
|
|
302
302
|
$resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
|
|
303
|
+
$cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
|
|
303
304
|
// No short runtime form for these yet, so the intrinsic stands for the `create*`
|
|
304
305
|
// name. Still worth having: no import to write, and when a shorter form is
|
|
305
306
|
// designed, the change is this line rather than a compiler pass.
|
|
@@ -309,7 +310,22 @@ var INTRINSICS = {
|
|
|
309
310
|
// does. `untrack` suppresses dependency collection, which is exactly the kind of thing
|
|
310
311
|
// dependency analysis must not guess at.
|
|
311
312
|
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
312
|
-
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" }
|
|
313
|
+
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
|
|
314
|
+
// Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
|
|
315
|
+
// no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
|
|
316
|
+
$batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
|
|
317
|
+
$cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
|
|
318
|
+
$root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
|
|
319
|
+
$context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
|
|
320
|
+
$renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
|
|
321
|
+
$computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
|
|
322
|
+
// Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
|
|
323
|
+
// handle as far as reads go: the same accessor with `set` on it.
|
|
324
|
+
$storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
325
|
+
$asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
326
|
+
$persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
|
|
327
|
+
// An environment variable, public or server-only; the browser build refuses a secret.
|
|
328
|
+
$env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
|
|
313
329
|
};
|
|
314
330
|
var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join("|")})\\s*\\(`);
|
|
315
331
|
|
|
@@ -485,13 +501,16 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
485
501
|
used.add("createComponent");
|
|
486
502
|
const spreads = props.filter((p) => p.kind === "spread");
|
|
487
503
|
const normal = props.filter((p) => p.kind !== "spread");
|
|
488
|
-
const
|
|
489
|
-
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
490
|
-
);
|
|
504
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
491
505
|
if (children.length > 0) {
|
|
492
|
-
|
|
506
|
+
bodies.push(["children", emitChildren(children, used, templates)]);
|
|
507
|
+
}
|
|
508
|
+
let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
|
|
509
|
+
if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
|
|
510
|
+
const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
|
|
511
|
+
const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
|
|
512
|
+
obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
|
|
493
513
|
}
|
|
494
|
-
let obj = `{ ${entries.join(", ")} }`;
|
|
495
514
|
if (spreads.length > 0) {
|
|
496
515
|
used.add("mergeProps");
|
|
497
516
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
@@ -573,7 +592,8 @@ function emitNode(node, used, templates) {
|
|
|
573
592
|
} else {
|
|
574
593
|
used.add("spread");
|
|
575
594
|
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
576
|
-
const
|
|
595
|
+
const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
|
|
596
|
+
const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
|
|
577
597
|
parts.push(
|
|
578
598
|
`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
|
|
579
599
|
);
|
|
@@ -644,6 +664,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
|
|
|
644
664
|
function escapeTemplate(s) {
|
|
645
665
|
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
646
666
|
}
|
|
667
|
+
var CONTAINER = "container";
|
|
647
668
|
function capitalize(s) {
|
|
648
669
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
649
670
|
}
|
|
@@ -698,10 +719,6 @@ var Lowerer = class {
|
|
|
698
719
|
if (hasElse) i = elseIdx;
|
|
699
720
|
continue;
|
|
700
721
|
}
|
|
701
|
-
if (child.attributes.some((a) => a.kind === "EachDirective")) {
|
|
702
|
-
out.push(this.lowerEach(child));
|
|
703
|
-
continue;
|
|
704
|
-
}
|
|
705
722
|
}
|
|
706
723
|
const node = this.lowerNode(child);
|
|
707
724
|
if (node) out.push(node);
|
|
@@ -743,13 +760,16 @@ var Lowerer = class {
|
|
|
743
760
|
node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
|
|
744
761
|
return node;
|
|
745
762
|
}
|
|
763
|
+
const each = this.lowerEach(el);
|
|
764
|
+
const children = each ? [each] : this.lowerChildren(el.children);
|
|
765
|
+
if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
746
766
|
const at = this.tagAt(el);
|
|
747
767
|
return {
|
|
748
768
|
kind: "element",
|
|
749
769
|
tag: el.tag,
|
|
750
770
|
svg: el.namespace === "svg",
|
|
751
771
|
props: this.lowerAttributes(el.attributes),
|
|
752
|
-
children
|
|
772
|
+
children,
|
|
753
773
|
static: false,
|
|
754
774
|
...at ? { at } : {}
|
|
755
775
|
};
|
|
@@ -821,10 +841,14 @@ var Lowerer = class {
|
|
|
821
841
|
const at = this.tagAt(el);
|
|
822
842
|
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
823
843
|
}
|
|
824
|
-
/**
|
|
844
|
+
/**
|
|
845
|
+
* `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
|
|
846
|
+
* content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
|
|
847
|
+
* it is. Null when the element has no `each`.
|
|
848
|
+
*/
|
|
825
849
|
lowerEach(el) {
|
|
826
850
|
const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
|
|
827
|
-
if (!eachDir || eachDir.kind !== "EachDirective") return
|
|
851
|
+
if (!eachDir || eachDir.kind !== "EachDirective") return null;
|
|
828
852
|
const each = this.hole(eachDir.hole);
|
|
829
853
|
const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
|
|
830
854
|
if (eachDir.key) {
|
|
@@ -834,12 +858,12 @@ var Lowerer = class {
|
|
|
834
858
|
const arrow = this.itemArrow(el);
|
|
835
859
|
let childrenCode;
|
|
836
860
|
if (arrow) {
|
|
837
|
-
const
|
|
838
|
-
|
|
839
|
-
childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
|
|
861
|
+
const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
|
|
862
|
+
childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
|
|
840
863
|
} else {
|
|
841
|
-
const
|
|
842
|
-
|
|
864
|
+
const content = this.lowerChildren(el.children);
|
|
865
|
+
const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
|
|
866
|
+
childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
|
|
843
867
|
}
|
|
844
868
|
const children = [{ kind: "expr", code: childrenCode, reactive: false }];
|
|
845
869
|
const at = this.tagAt(el);
|
|
@@ -856,25 +880,6 @@ var Lowerer = class {
|
|
|
856
880
|
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
857
881
|
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
858
882
|
}
|
|
859
|
-
/** Same element (minus each/key) but with explicit IR children. */
|
|
860
|
-
rebuildWithChildren(el, children) {
|
|
861
|
-
const props = this.lowerAttributes(
|
|
862
|
-
el.attributes.filter((a) => a.kind !== "EachDirective")
|
|
863
|
-
);
|
|
864
|
-
if (el.kind === "Element") {
|
|
865
|
-
const at = this.tagAt(el);
|
|
866
|
-
return {
|
|
867
|
-
kind: "element",
|
|
868
|
-
tag: el.tag,
|
|
869
|
-
svg: el.namespace === "svg",
|
|
870
|
-
props,
|
|
871
|
-
children,
|
|
872
|
-
static: false,
|
|
873
|
-
...at ? { at } : {}
|
|
874
|
-
};
|
|
875
|
-
}
|
|
876
|
-
return { kind: "component", name: el.tag, props, children };
|
|
877
|
-
}
|
|
878
883
|
// --- attributes ---------------------------------------------------------
|
|
879
884
|
lowerAttributes(attrs) {
|
|
880
885
|
const props = [];
|
|
@@ -1251,6 +1256,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
|
|
|
1251
1256
|
"mouseup"
|
|
1252
1257
|
]);
|
|
1253
1258
|
var isReactive = (node) => isReactiveShape(shapeOf(node));
|
|
1259
|
+
var CONTAINER2 = "container";
|
|
1260
|
+
function directive(node, name) {
|
|
1261
|
+
if (node.type !== "JSXElement") return void 0;
|
|
1262
|
+
const opening = node.openingElement;
|
|
1263
|
+
if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
|
|
1264
|
+
return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
|
|
1265
|
+
}
|
|
1266
|
+
function directiveExpr(attr) {
|
|
1267
|
+
const v = attr.value;
|
|
1268
|
+
return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
|
|
1269
|
+
}
|
|
1270
|
+
function emitIR(ir, ctx) {
|
|
1271
|
+
const { code, imports, templates } = imperativeBackend.emit(ir, {});
|
|
1272
|
+
for (const s of imports) ctx.used.add(s);
|
|
1273
|
+
return inlineTemplates(code, templates);
|
|
1274
|
+
}
|
|
1254
1275
|
var posOf = (node, ctx) => {
|
|
1255
1276
|
if (!ctx.sourceFile) return void 0;
|
|
1256
1277
|
const at = node.loc?.start;
|
|
@@ -1359,7 +1380,16 @@ function buildProps(attrs, ctx) {
|
|
|
1359
1380
|
}
|
|
1360
1381
|
function buildChildren(children, ctx) {
|
|
1361
1382
|
const out = [];
|
|
1362
|
-
for (
|
|
1383
|
+
for (let i = 0; i < children.length; i++) {
|
|
1384
|
+
const child = children[i];
|
|
1385
|
+
if (directive(child, "if")) {
|
|
1386
|
+
let next = i + 1;
|
|
1387
|
+
while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
|
|
1388
|
+
const fallback = directive(children[next], "else") ? children[next] : null;
|
|
1389
|
+
out.push(buildIf(child, fallback, ctx));
|
|
1390
|
+
if (fallback) i = next;
|
|
1391
|
+
continue;
|
|
1392
|
+
}
|
|
1363
1393
|
if (child.type === "JSXText") {
|
|
1364
1394
|
const text = cleanTemplateText(child.value);
|
|
1365
1395
|
if (text) out.push({ kind: "text", value: text });
|
|
@@ -1396,19 +1426,68 @@ function readLoad(attrs) {
|
|
|
1396
1426
|
}
|
|
1397
1427
|
return void 0;
|
|
1398
1428
|
}
|
|
1429
|
+
function buildIf(node, fallback, ctx) {
|
|
1430
|
+
const cond = directiveExpr(directive(node, "if"));
|
|
1431
|
+
const props = [
|
|
1432
|
+
cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
|
|
1433
|
+
];
|
|
1434
|
+
if (fallback) {
|
|
1435
|
+
props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
|
|
1436
|
+
}
|
|
1437
|
+
return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
|
|
1438
|
+
}
|
|
1439
|
+
function buildEach(node, ctx) {
|
|
1440
|
+
const attr = directive(node, "each");
|
|
1441
|
+
if (!attr) return null;
|
|
1442
|
+
const list = directiveExpr(attr);
|
|
1443
|
+
const props = [
|
|
1444
|
+
list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
|
|
1445
|
+
];
|
|
1446
|
+
const key = directive(node, "key");
|
|
1447
|
+
if (key) {
|
|
1448
|
+
const keyExpr = directiveExpr(key);
|
|
1449
|
+
const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
|
|
1450
|
+
if (by) props.push({ name: "by", kind: "attr", expr: by });
|
|
1451
|
+
}
|
|
1452
|
+
const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
|
|
1453
|
+
const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
|
|
1454
|
+
let render;
|
|
1455
|
+
if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
|
|
1456
|
+
const fn = ctx.code(only);
|
|
1457
|
+
const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
|
|
1458
|
+
render = live ? `(...args) => () => (${fn})(...args)` : fn;
|
|
1459
|
+
} else {
|
|
1460
|
+
const built = buildChildren(node.children, ctx);
|
|
1461
|
+
const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
|
|
1462
|
+
render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
|
|
1463
|
+
}
|
|
1464
|
+
return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
|
|
1465
|
+
}
|
|
1466
|
+
function buildElement(node, ctx, handled) {
|
|
1467
|
+
const skip = new Set(handled);
|
|
1468
|
+
const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
|
|
1469
|
+
const attributes = node.openingElement.attributes.filter(
|
|
1470
|
+
(a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
|
|
1471
|
+
);
|
|
1472
|
+
const { tag } = tagInfo(node.openingElement.name, ctx);
|
|
1473
|
+
const each = buildEach(node, ctx);
|
|
1474
|
+
const children = each ? [each] : buildChildren(node.children, ctx);
|
|
1475
|
+
if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
1476
|
+
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1477
|
+
return { kind: "element", tag, svg: SVG_TAGS.has(tag), props: buildProps(attributes, ctx), children, static: false, ...at ? { at } : {} };
|
|
1478
|
+
}
|
|
1399
1479
|
function buildNode(node, ctx) {
|
|
1400
1480
|
if (node.type === "JSXFragment") {
|
|
1401
1481
|
return { kind: "fragment", children: buildChildren(node.children, ctx) };
|
|
1402
1482
|
}
|
|
1483
|
+
if (directive(node, "if")) return buildIf(node, null, ctx);
|
|
1403
1484
|
const { tag, component } = tagInfo(node.openingElement.name, ctx);
|
|
1485
|
+
if (!component) return buildElement(node, ctx, []);
|
|
1404
1486
|
const props = buildProps(node.openingElement.attributes, ctx);
|
|
1405
1487
|
const children = buildChildren(node.children, ctx);
|
|
1406
1488
|
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1410
|
-
}
|
|
1411
|
-
return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
|
|
1489
|
+
const load = readLoad(node.openingElement.attributes);
|
|
1490
|
+
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1412
1491
|
}
|
|
1413
1492
|
function buildJsxIR(node, ctx) {
|
|
1414
1493
|
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 = [];
|
|
@@ -1219,6 +1224,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
|
|
|
1219
1224
|
"mouseup"
|
|
1220
1225
|
]);
|
|
1221
1226
|
var isReactive = (node) => isReactiveShape(shapeOf(node));
|
|
1227
|
+
var CONTAINER2 = "container";
|
|
1228
|
+
function directive(node, name) {
|
|
1229
|
+
if (node.type !== "JSXElement") return void 0;
|
|
1230
|
+
const opening = node.openingElement;
|
|
1231
|
+
if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
|
|
1232
|
+
return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
|
|
1233
|
+
}
|
|
1234
|
+
function directiveExpr(attr) {
|
|
1235
|
+
const v = attr.value;
|
|
1236
|
+
return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
|
|
1237
|
+
}
|
|
1238
|
+
function emitIR(ir, ctx) {
|
|
1239
|
+
const { code, imports, templates } = imperativeBackend.emit(ir, {});
|
|
1240
|
+
for (const s of imports) ctx.used.add(s);
|
|
1241
|
+
return inlineTemplates(code, templates);
|
|
1242
|
+
}
|
|
1222
1243
|
var posOf = (node, ctx) => {
|
|
1223
1244
|
if (!ctx.sourceFile) return void 0;
|
|
1224
1245
|
const at = node.loc?.start;
|
|
@@ -1327,7 +1348,16 @@ function buildProps(attrs, ctx) {
|
|
|
1327
1348
|
}
|
|
1328
1349
|
function buildChildren(children, ctx) {
|
|
1329
1350
|
const out = [];
|
|
1330
|
-
for (
|
|
1351
|
+
for (let i = 0; i < children.length; i++) {
|
|
1352
|
+
const child = children[i];
|
|
1353
|
+
if (directive(child, "if")) {
|
|
1354
|
+
let next = i + 1;
|
|
1355
|
+
while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
|
|
1356
|
+
const fallback = directive(children[next], "else") ? children[next] : null;
|
|
1357
|
+
out.push(buildIf(child, fallback, ctx));
|
|
1358
|
+
if (fallback) i = next;
|
|
1359
|
+
continue;
|
|
1360
|
+
}
|
|
1331
1361
|
if (child.type === "JSXText") {
|
|
1332
1362
|
const text = cleanTemplateText(child.value);
|
|
1333
1363
|
if (text) out.push({ kind: "text", value: text });
|
|
@@ -1364,19 +1394,68 @@ function readLoad(attrs) {
|
|
|
1364
1394
|
}
|
|
1365
1395
|
return void 0;
|
|
1366
1396
|
}
|
|
1397
|
+
function buildIf(node, fallback, ctx) {
|
|
1398
|
+
const cond = directiveExpr(directive(node, "if"));
|
|
1399
|
+
const props = [
|
|
1400
|
+
cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
|
|
1401
|
+
];
|
|
1402
|
+
if (fallback) {
|
|
1403
|
+
props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
|
|
1404
|
+
}
|
|
1405
|
+
return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
|
|
1406
|
+
}
|
|
1407
|
+
function buildEach(node, ctx) {
|
|
1408
|
+
const attr = directive(node, "each");
|
|
1409
|
+
if (!attr) return null;
|
|
1410
|
+
const list = directiveExpr(attr);
|
|
1411
|
+
const props = [
|
|
1412
|
+
list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
|
|
1413
|
+
];
|
|
1414
|
+
const key = directive(node, "key");
|
|
1415
|
+
if (key) {
|
|
1416
|
+
const keyExpr = directiveExpr(key);
|
|
1417
|
+
const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
|
|
1418
|
+
if (by) props.push({ name: "by", kind: "attr", expr: by });
|
|
1419
|
+
}
|
|
1420
|
+
const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
|
|
1421
|
+
const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
|
|
1422
|
+
let render;
|
|
1423
|
+
if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
|
|
1424
|
+
const fn = ctx.code(only);
|
|
1425
|
+
const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
|
|
1426
|
+
render = live ? `(...args) => () => (${fn})(...args)` : fn;
|
|
1427
|
+
} else {
|
|
1428
|
+
const built = buildChildren(node.children, ctx);
|
|
1429
|
+
const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
|
|
1430
|
+
render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
|
|
1431
|
+
}
|
|
1432
|
+
return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
|
|
1433
|
+
}
|
|
1434
|
+
function buildElement(node, ctx, handled) {
|
|
1435
|
+
const skip = new Set(handled);
|
|
1436
|
+
const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
|
|
1437
|
+
const attributes = node.openingElement.attributes.filter(
|
|
1438
|
+
(a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
|
|
1439
|
+
);
|
|
1440
|
+
const { tag } = tagInfo(node.openingElement.name, ctx);
|
|
1441
|
+
const each = buildEach(node, ctx);
|
|
1442
|
+
const children = each ? [each] : buildChildren(node.children, ctx);
|
|
1443
|
+
if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
1444
|
+
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1445
|
+
return { kind: "element", tag, svg: SVG_TAGS.has(tag), props: buildProps(attributes, ctx), children, static: false, ...at ? { at } : {} };
|
|
1446
|
+
}
|
|
1367
1447
|
function buildNode(node, ctx) {
|
|
1368
1448
|
if (node.type === "JSXFragment") {
|
|
1369
1449
|
return { kind: "fragment", children: buildChildren(node.children, ctx) };
|
|
1370
1450
|
}
|
|
1451
|
+
if (directive(node, "if")) return buildIf(node, null, ctx);
|
|
1371
1452
|
const { tag, component } = tagInfo(node.openingElement.name, ctx);
|
|
1453
|
+
if (!component) return buildElement(node, ctx, []);
|
|
1372
1454
|
const props = buildProps(node.openingElement.attributes, ctx);
|
|
1373
1455
|
const children = buildChildren(node.children, ctx);
|
|
1374
1456
|
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1378
|
-
}
|
|
1379
|
-
return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
|
|
1457
|
+
const load = readLoad(node.openingElement.attributes);
|
|
1458
|
+
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1380
1459
|
}
|
|
1381
1460
|
function buildJsxIR(node, ctx) {
|
|
1382
1461
|
const ir = buildNode(node, ctx);
|