@fluixi/compiler 1.0.0-alpha.92 → 1.0.0-alpha.93
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analyze/intrinsics.cjs +18 -0
- package/dist/analyze/intrinsics.d.ts.map +1 -1
- package/dist/analyze/intrinsics.js +18 -0
- package/dist/analyze/intrinsics.mjs +18 -0
- package/dist/analyze/reactive-bindings.cjs +18 -0
- package/dist/analyze/reactive-bindings.mjs +18 -0
- package/dist/analyze/static-graph.cjs +22 -4
- package/dist/analyze/static-graph.d.ts.map +1 -1
- package/dist/analyze/static-graph.js +6 -4
- package/dist/analyze/static-graph.mjs +22 -4
- package/dist/{babel-GI375YZ2.mjs → babel-RJHJQVEM.mjs} +4 -4
- package/dist/{chunk-QZ4CJPYP.mjs → chunk-56QSOVVL.mjs} +1 -1
- package/dist/{chunk-XJ5H2MXY.mjs → chunk-OD53TMER.mjs} +19 -1
- package/dist/{chunk-DWNWN5RH.mjs → chunk-R7PPFBCL.mjs} +1 -1
- package/dist/{chunk-VOUI6XPV.mjs → chunk-TJIH7TLX.mjs} +1 -1
- package/dist/{chunk-VC6X44FS.mjs → chunk-U7HCFLZ2.mjs} +153 -48
- package/dist/codegen/backends/imperative.cjs +24 -6
- package/dist/codegen/backends/imperative.d.ts.map +1 -1
- package/dist/codegen/backends/imperative.js +19 -4
- package/dist/codegen/backends/imperative.mjs +24 -6
- package/dist/frontend/babel/build-ir-lit.cjs +120 -43
- package/dist/frontend/babel/build-ir-lit.mjs +120 -43
- package/dist/frontend/babel/build-ir.cjs +120 -43
- package/dist/frontend/babel/build-ir.mjs +120 -43
- package/dist/frontend/babel/index.cjs +121 -44
- package/dist/frontend/babel/index.mjs +121 -44
- package/dist/frontend/babel/lower-template.cjs +120 -43
- package/dist/frontend/babel/lower-template.mjs +120 -43
- package/dist/frontend/babel/plugin.cjs +120 -43
- package/dist/frontend/babel/plugin.mjs +120 -43
- package/dist/index.cjs +41 -38
- package/dist/index.mjs +41 -38
- package/dist/integrations.cjs +176 -52
- package/dist/integrations.mjs +8 -8
- package/dist/lower/compile-template.cjs +41 -38
- package/dist/lower/compile-template.mjs +41 -38
- package/dist/lower/jsx.cjs +509 -6
- package/dist/lower/jsx.d.ts.map +1 -1
- package/dist/lower/jsx.js +114 -6
- package/dist/lower/jsx.mjs +509 -6
- package/dist/lower/template.cjs +40 -37
- package/dist/lower/template.d.ts.map +1 -1
- package/dist/lower/template.js +22 -30
- package/dist/lower/template.mjs +40 -37
- package/dist/resolve/builtins.cjs +1 -1
- package/dist/resolve/builtins.mjs +1 -1
- package/dist/resolve/component-globals.cjs +14 -0
- package/dist/resolve/component-globals.mjs +14 -0
- package/dist/resolve/write-globals.cjs +14 -0
- package/dist/resolve/write-globals.mjs +14 -0
- package/dist/{server-fns-BBZRZUPP.mjs → server-fns-ZFRDIQ2X.mjs} +4 -4
- package/dist/{server-functions-VEGLIFCE.mjs → server-functions-CTDXX67X.mjs} +3 -3
- package/dist/transform/index.cjs +170 -47
- package/dist/transform/index.mjs +170 -47
- package/dist/transform/intrinsics.cjs +18 -0
- package/dist/transform/intrinsics.mjs +18 -0
- package/dist/transform/source-locations.cjs +64 -3
- package/dist/transform/source-locations.d.cts +7 -2
- package/dist/transform/source-locations.d.ts +7 -2
- package/dist/transform/source-locations.d.ts.map +1 -1
- package/dist/transform/source-locations.js +68 -3
- package/dist/transform/source-locations.mjs +64 -3
- package/dist/transform/templates.cjs +169 -46
- package/dist/transform/templates.js +2 -2
- package/dist/transform/templates.mjs +169 -46
- package/dist/{transform-UWZ7ZGJ2.mjs → transform-QWWQDCXE.mjs} +7 -7
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +9 -3
|
@@ -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.
|
|
@@ -261,6 +262,19 @@ var INTRINSICS = {
|
|
|
261
262
|
// dependency analysis must not guess at.
|
|
262
263
|
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
263
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" },
|
|
264
278
|
// An environment variable, public or server-only; the browser build refuses a secret.
|
|
265
279
|
$env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
|
|
266
280
|
};
|
|
@@ -438,13 +452,16 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
438
452
|
used.add("createComponent");
|
|
439
453
|
const spreads = props.filter((p) => p.kind === "spread");
|
|
440
454
|
const normal = props.filter((p) => p.kind !== "spread");
|
|
441
|
-
const
|
|
442
|
-
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
443
|
-
);
|
|
455
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
444
456
|
if (children.length > 0) {
|
|
445
|
-
|
|
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(", ")} })`;
|
|
446
464
|
}
|
|
447
|
-
let obj = `{ ${entries.join(", ")} }`;
|
|
448
465
|
if (spreads.length > 0) {
|
|
449
466
|
used.add("mergeProps");
|
|
450
467
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
@@ -526,7 +543,8 @@ function emitNode(node, used, templates) {
|
|
|
526
543
|
} else {
|
|
527
544
|
used.add("spread");
|
|
528
545
|
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
529
|
-
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)}` : "";
|
|
530
548
|
parts.push(
|
|
531
549
|
`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
|
|
532
550
|
);
|
|
@@ -653,6 +671,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
|
|
|
653
671
|
function escapeTemplate(s) {
|
|
654
672
|
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
655
673
|
}
|
|
674
|
+
var CONTAINER = "container";
|
|
656
675
|
function capitalize(s) {
|
|
657
676
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
658
677
|
}
|
|
@@ -707,10 +726,6 @@ var Lowerer = class {
|
|
|
707
726
|
if (hasElse) i = elseIdx;
|
|
708
727
|
continue;
|
|
709
728
|
}
|
|
710
|
-
if (child.attributes.some((a) => a.kind === "EachDirective")) {
|
|
711
|
-
out.push(this.lowerEach(child));
|
|
712
|
-
continue;
|
|
713
|
-
}
|
|
714
729
|
}
|
|
715
730
|
const node = this.lowerNode(child);
|
|
716
731
|
if (node) out.push(node);
|
|
@@ -752,13 +767,16 @@ var Lowerer = class {
|
|
|
752
767
|
node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
|
|
753
768
|
return node;
|
|
754
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 };
|
|
755
773
|
const at = this.tagAt(el);
|
|
756
774
|
return {
|
|
757
775
|
kind: "element",
|
|
758
776
|
tag: el.tag,
|
|
759
777
|
svg: el.namespace === "svg",
|
|
760
778
|
props: this.lowerAttributes(el.attributes),
|
|
761
|
-
children
|
|
779
|
+
children,
|
|
762
780
|
static: false,
|
|
763
781
|
...at ? { at } : {}
|
|
764
782
|
};
|
|
@@ -830,10 +848,14 @@ var Lowerer = class {
|
|
|
830
848
|
const at = this.tagAt(el);
|
|
831
849
|
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
832
850
|
}
|
|
833
|
-
/**
|
|
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
|
+
*/
|
|
834
856
|
lowerEach(el) {
|
|
835
857
|
const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
|
|
836
|
-
if (!eachDir || eachDir.kind !== "EachDirective") return
|
|
858
|
+
if (!eachDir || eachDir.kind !== "EachDirective") return null;
|
|
837
859
|
const each = this.hole(eachDir.hole);
|
|
838
860
|
const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
|
|
839
861
|
if (eachDir.key) {
|
|
@@ -843,12 +865,12 @@ var Lowerer = class {
|
|
|
843
865
|
const arrow = this.itemArrow(el);
|
|
844
866
|
let childrenCode;
|
|
845
867
|
if (arrow) {
|
|
846
|
-
const
|
|
847
|
-
|
|
848
|
-
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}`;
|
|
849
870
|
} else {
|
|
850
|
-
const
|
|
851
|
-
|
|
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")}`;
|
|
852
874
|
}
|
|
853
875
|
const children = [{ kind: "expr", code: childrenCode, reactive: false }];
|
|
854
876
|
const at = this.tagAt(el);
|
|
@@ -865,25 +887,6 @@ var Lowerer = class {
|
|
|
865
887
|
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
866
888
|
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
867
889
|
}
|
|
868
|
-
/** Same element (minus each/key) but with explicit IR children. */
|
|
869
|
-
rebuildWithChildren(el, children) {
|
|
870
|
-
const props = this.lowerAttributes(
|
|
871
|
-
el.attributes.filter((a) => a.kind !== "EachDirective")
|
|
872
|
-
);
|
|
873
|
-
if (el.kind === "Element") {
|
|
874
|
-
const at = this.tagAt(el);
|
|
875
|
-
return {
|
|
876
|
-
kind: "element",
|
|
877
|
-
tag: el.tag,
|
|
878
|
-
svg: el.namespace === "svg",
|
|
879
|
-
props,
|
|
880
|
-
children,
|
|
881
|
-
static: false,
|
|
882
|
-
...at ? { at } : {}
|
|
883
|
-
};
|
|
884
|
-
}
|
|
885
|
-
return { kind: "component", name: el.tag, props, children };
|
|
886
|
-
}
|
|
887
890
|
// --- attributes ---------------------------------------------------------
|
|
888
891
|
lowerAttributes(attrs) {
|
|
889
892
|
const props = [];
|
|
@@ -1295,6 +1298,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
|
|
|
1295
1298
|
"mouseup"
|
|
1296
1299
|
]);
|
|
1297
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
|
+
}
|
|
1298
1317
|
var posOf = (node, ctx) => {
|
|
1299
1318
|
if (!ctx.sourceFile) return void 0;
|
|
1300
1319
|
const at = node.loc?.start;
|
|
@@ -1403,7 +1422,16 @@ function buildProps(attrs, ctx) {
|
|
|
1403
1422
|
}
|
|
1404
1423
|
function buildChildren(children, ctx) {
|
|
1405
1424
|
const out = [];
|
|
1406
|
-
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
|
+
}
|
|
1407
1435
|
if (child.type === "JSXText") {
|
|
1408
1436
|
const text = cleanTemplateText(child.value);
|
|
1409
1437
|
if (text) out.push({ kind: "text", value: text });
|
|
@@ -1440,19 +1468,68 @@ function readLoad(attrs) {
|
|
|
1440
1468
|
}
|
|
1441
1469
|
return void 0;
|
|
1442
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
|
+
}
|
|
1443
1521
|
function buildNode(node, ctx) {
|
|
1444
1522
|
if (node.type === "JSXFragment") {
|
|
1445
1523
|
return { kind: "fragment", children: buildChildren(node.children, ctx) };
|
|
1446
1524
|
}
|
|
1525
|
+
if (directive(node, "if")) return buildIf(node, null, ctx);
|
|
1447
1526
|
const { tag, component } = tagInfo(node.openingElement.name, ctx);
|
|
1527
|
+
if (!component) return buildElement(node, ctx, []);
|
|
1448
1528
|
const props = buildProps(node.openingElement.attributes, ctx);
|
|
1449
1529
|
const children = buildChildren(node.children, ctx);
|
|
1450
1530
|
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1454
|
-
}
|
|
1455
|
-
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 } : {} };
|
|
1456
1533
|
}
|
|
1457
1534
|
function buildJsxIR(node, ctx) {
|
|
1458
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.
|
|
@@ -224,6 +225,19 @@ var INTRINSICS = {
|
|
|
224
225
|
// dependency analysis must not guess at.
|
|
225
226
|
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
226
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" },
|
|
227
241
|
// An environment variable, public or server-only; the browser build refuses a secret.
|
|
228
242
|
$env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
|
|
229
243
|
};
|
|
@@ -401,13 +415,16 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
401
415
|
used.add("createComponent");
|
|
402
416
|
const spreads = props.filter((p) => p.kind === "spread");
|
|
403
417
|
const normal = props.filter((p) => p.kind !== "spread");
|
|
404
|
-
const
|
|
405
|
-
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
406
|
-
);
|
|
418
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
407
419
|
if (children.length > 0) {
|
|
408
|
-
|
|
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(", ")} })`;
|
|
409
427
|
}
|
|
410
|
-
let obj = `{ ${entries.join(", ")} }`;
|
|
411
428
|
if (spreads.length > 0) {
|
|
412
429
|
used.add("mergeProps");
|
|
413
430
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
@@ -489,7 +506,8 @@ function emitNode(node, used, templates) {
|
|
|
489
506
|
} else {
|
|
490
507
|
used.add("spread");
|
|
491
508
|
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
492
|
-
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)}` : "";
|
|
493
511
|
parts.push(
|
|
494
512
|
`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
|
|
495
513
|
);
|
|
@@ -618,6 +636,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
|
|
|
618
636
|
function escapeTemplate(s) {
|
|
619
637
|
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
620
638
|
}
|
|
639
|
+
var CONTAINER = "container";
|
|
621
640
|
function capitalize(s) {
|
|
622
641
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
623
642
|
}
|
|
@@ -672,10 +691,6 @@ var Lowerer = class {
|
|
|
672
691
|
if (hasElse) i = elseIdx;
|
|
673
692
|
continue;
|
|
674
693
|
}
|
|
675
|
-
if (child.attributes.some((a) => a.kind === "EachDirective")) {
|
|
676
|
-
out.push(this.lowerEach(child));
|
|
677
|
-
continue;
|
|
678
|
-
}
|
|
679
694
|
}
|
|
680
695
|
const node = this.lowerNode(child);
|
|
681
696
|
if (node) out.push(node);
|
|
@@ -717,13 +732,16 @@ var Lowerer = class {
|
|
|
717
732
|
node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
|
|
718
733
|
return node;
|
|
719
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 };
|
|
720
738
|
const at = this.tagAt(el);
|
|
721
739
|
return {
|
|
722
740
|
kind: "element",
|
|
723
741
|
tag: el.tag,
|
|
724
742
|
svg: el.namespace === "svg",
|
|
725
743
|
props: this.lowerAttributes(el.attributes),
|
|
726
|
-
children
|
|
744
|
+
children,
|
|
727
745
|
static: false,
|
|
728
746
|
...at ? { at } : {}
|
|
729
747
|
};
|
|
@@ -795,10 +813,14 @@ var Lowerer = class {
|
|
|
795
813
|
const at = this.tagAt(el);
|
|
796
814
|
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
797
815
|
}
|
|
798
|
-
/**
|
|
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
|
+
*/
|
|
799
821
|
lowerEach(el) {
|
|
800
822
|
const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
|
|
801
|
-
if (!eachDir || eachDir.kind !== "EachDirective") return
|
|
823
|
+
if (!eachDir || eachDir.kind !== "EachDirective") return null;
|
|
802
824
|
const each = this.hole(eachDir.hole);
|
|
803
825
|
const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
|
|
804
826
|
if (eachDir.key) {
|
|
@@ -808,12 +830,12 @@ var Lowerer = class {
|
|
|
808
830
|
const arrow = this.itemArrow(el);
|
|
809
831
|
let childrenCode;
|
|
810
832
|
if (arrow) {
|
|
811
|
-
const
|
|
812
|
-
|
|
813
|
-
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}`;
|
|
814
835
|
} else {
|
|
815
|
-
const
|
|
816
|
-
|
|
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")}`;
|
|
817
839
|
}
|
|
818
840
|
const children = [{ kind: "expr", code: childrenCode, reactive: false }];
|
|
819
841
|
const at = this.tagAt(el);
|
|
@@ -830,25 +852,6 @@ var Lowerer = class {
|
|
|
830
852
|
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
831
853
|
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
832
854
|
}
|
|
833
|
-
/** Same element (minus each/key) but with explicit IR children. */
|
|
834
|
-
rebuildWithChildren(el, children) {
|
|
835
|
-
const props = this.lowerAttributes(
|
|
836
|
-
el.attributes.filter((a) => a.kind !== "EachDirective")
|
|
837
|
-
);
|
|
838
|
-
if (el.kind === "Element") {
|
|
839
|
-
const at = this.tagAt(el);
|
|
840
|
-
return {
|
|
841
|
-
kind: "element",
|
|
842
|
-
tag: el.tag,
|
|
843
|
-
svg: el.namespace === "svg",
|
|
844
|
-
props,
|
|
845
|
-
children,
|
|
846
|
-
static: false,
|
|
847
|
-
...at ? { at } : {}
|
|
848
|
-
};
|
|
849
|
-
}
|
|
850
|
-
return { kind: "component", name: el.tag, props, children };
|
|
851
|
-
}
|
|
852
855
|
// --- attributes ---------------------------------------------------------
|
|
853
856
|
lowerAttributes(attrs) {
|
|
854
857
|
const props = [];
|
|
@@ -1260,6 +1263,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
|
|
|
1260
1263
|
"mouseup"
|
|
1261
1264
|
]);
|
|
1262
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
|
+
}
|
|
1263
1282
|
var posOf = (node, ctx) => {
|
|
1264
1283
|
if (!ctx.sourceFile) return void 0;
|
|
1265
1284
|
const at = node.loc?.start;
|
|
@@ -1368,7 +1387,16 @@ function buildProps(attrs, ctx) {
|
|
|
1368
1387
|
}
|
|
1369
1388
|
function buildChildren(children, ctx) {
|
|
1370
1389
|
const out = [];
|
|
1371
|
-
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
|
+
}
|
|
1372
1400
|
if (child.type === "JSXText") {
|
|
1373
1401
|
const text = cleanTemplateText(child.value);
|
|
1374
1402
|
if (text) out.push({ kind: "text", value: text });
|
|
@@ -1405,19 +1433,68 @@ function readLoad(attrs) {
|
|
|
1405
1433
|
}
|
|
1406
1434
|
return void 0;
|
|
1407
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
|
+
}
|
|
1408
1486
|
function buildNode(node, ctx) {
|
|
1409
1487
|
if (node.type === "JSXFragment") {
|
|
1410
1488
|
return { kind: "fragment", children: buildChildren(node.children, ctx) };
|
|
1411
1489
|
}
|
|
1490
|
+
if (directive(node, "if")) return buildIf(node, null, ctx);
|
|
1412
1491
|
const { tag, component } = tagInfo(node.openingElement.name, ctx);
|
|
1492
|
+
if (!component) return buildElement(node, ctx, []);
|
|
1413
1493
|
const props = buildProps(node.openingElement.attributes, ctx);
|
|
1414
1494
|
const children = buildChildren(node.children, ctx);
|
|
1415
1495
|
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1419
|
-
}
|
|
1420
|
-
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 } : {} };
|
|
1421
1498
|
}
|
|
1422
1499
|
function buildJsxIR(node, ctx) {
|
|
1423
1500
|
const ir = buildNode(node, ctx);
|