@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
|
@@ -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.
|
|
@@ -310,6 +311,19 @@ var INTRINSICS = {
|
|
|
310
311
|
// dependency analysis must not guess at.
|
|
311
312
|
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
312
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" },
|
|
313
327
|
// An environment variable, public or server-only; the browser build refuses a secret.
|
|
314
328
|
$env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
|
|
315
329
|
};
|
|
@@ -487,13 +501,16 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
487
501
|
used.add("createComponent");
|
|
488
502
|
const spreads = props.filter((p) => p.kind === "spread");
|
|
489
503
|
const normal = props.filter((p) => p.kind !== "spread");
|
|
490
|
-
const
|
|
491
|
-
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
492
|
-
);
|
|
504
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
493
505
|
if (children.length > 0) {
|
|
494
|
-
|
|
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(", ")} })`;
|
|
495
513
|
}
|
|
496
|
-
let obj = `{ ${entries.join(", ")} }`;
|
|
497
514
|
if (spreads.length > 0) {
|
|
498
515
|
used.add("mergeProps");
|
|
499
516
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
@@ -575,7 +592,8 @@ function emitNode(node, used, templates) {
|
|
|
575
592
|
} else {
|
|
576
593
|
used.add("spread");
|
|
577
594
|
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
578
|
-
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)}` : "";
|
|
579
597
|
parts.push(
|
|
580
598
|
`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
|
|
581
599
|
);
|
|
@@ -646,6 +664,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
|
|
|
646
664
|
function escapeTemplate(s) {
|
|
647
665
|
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
648
666
|
}
|
|
667
|
+
var CONTAINER = "container";
|
|
649
668
|
function capitalize(s) {
|
|
650
669
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
651
670
|
}
|
|
@@ -700,10 +719,6 @@ var Lowerer = class {
|
|
|
700
719
|
if (hasElse) i = elseIdx;
|
|
701
720
|
continue;
|
|
702
721
|
}
|
|
703
|
-
if (child.attributes.some((a) => a.kind === "EachDirective")) {
|
|
704
|
-
out.push(this.lowerEach(child));
|
|
705
|
-
continue;
|
|
706
|
-
}
|
|
707
722
|
}
|
|
708
723
|
const node = this.lowerNode(child);
|
|
709
724
|
if (node) out.push(node);
|
|
@@ -745,13 +760,16 @@ var Lowerer = class {
|
|
|
745
760
|
node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
|
|
746
761
|
return node;
|
|
747
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 };
|
|
748
766
|
const at = this.tagAt(el);
|
|
749
767
|
return {
|
|
750
768
|
kind: "element",
|
|
751
769
|
tag: el.tag,
|
|
752
770
|
svg: el.namespace === "svg",
|
|
753
771
|
props: this.lowerAttributes(el.attributes),
|
|
754
|
-
children
|
|
772
|
+
children,
|
|
755
773
|
static: false,
|
|
756
774
|
...at ? { at } : {}
|
|
757
775
|
};
|
|
@@ -823,10 +841,14 @@ var Lowerer = class {
|
|
|
823
841
|
const at = this.tagAt(el);
|
|
824
842
|
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
825
843
|
}
|
|
826
|
-
/**
|
|
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
|
+
*/
|
|
827
849
|
lowerEach(el) {
|
|
828
850
|
const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
|
|
829
|
-
if (!eachDir || eachDir.kind !== "EachDirective") return
|
|
851
|
+
if (!eachDir || eachDir.kind !== "EachDirective") return null;
|
|
830
852
|
const each = this.hole(eachDir.hole);
|
|
831
853
|
const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
|
|
832
854
|
if (eachDir.key) {
|
|
@@ -836,12 +858,12 @@ var Lowerer = class {
|
|
|
836
858
|
const arrow = this.itemArrow(el);
|
|
837
859
|
let childrenCode;
|
|
838
860
|
if (arrow) {
|
|
839
|
-
const
|
|
840
|
-
|
|
841
|
-
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}`;
|
|
842
863
|
} else {
|
|
843
|
-
const
|
|
844
|
-
|
|
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")}`;
|
|
845
867
|
}
|
|
846
868
|
const children = [{ kind: "expr", code: childrenCode, reactive: false }];
|
|
847
869
|
const at = this.tagAt(el);
|
|
@@ -858,25 +880,6 @@ var Lowerer = class {
|
|
|
858
880
|
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
859
881
|
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
860
882
|
}
|
|
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
883
|
// --- attributes ---------------------------------------------------------
|
|
881
884
|
lowerAttributes(attrs) {
|
|
882
885
|
const props = [];
|
|
@@ -1253,6 +1256,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
|
|
|
1253
1256
|
"mouseup"
|
|
1254
1257
|
]);
|
|
1255
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
|
+
}
|
|
1256
1275
|
var posOf = (node, ctx) => {
|
|
1257
1276
|
if (!ctx.sourceFile) return void 0;
|
|
1258
1277
|
const at = node.loc?.start;
|
|
@@ -1361,7 +1380,16 @@ function buildProps(attrs, ctx) {
|
|
|
1361
1380
|
}
|
|
1362
1381
|
function buildChildren(children, ctx) {
|
|
1363
1382
|
const out = [];
|
|
1364
|
-
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
|
+
}
|
|
1365
1393
|
if (child.type === "JSXText") {
|
|
1366
1394
|
const text = cleanTemplateText(child.value);
|
|
1367
1395
|
if (text) out.push({ kind: "text", value: text });
|
|
@@ -1398,19 +1426,68 @@ function readLoad(attrs) {
|
|
|
1398
1426
|
}
|
|
1399
1427
|
return void 0;
|
|
1400
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
|
+
}
|
|
1401
1479
|
function buildNode(node, ctx) {
|
|
1402
1480
|
if (node.type === "JSXFragment") {
|
|
1403
1481
|
return { kind: "fragment", children: buildChildren(node.children, ctx) };
|
|
1404
1482
|
}
|
|
1483
|
+
if (directive(node, "if")) return buildIf(node, null, ctx);
|
|
1405
1484
|
const { tag, component } = tagInfo(node.openingElement.name, ctx);
|
|
1485
|
+
if (!component) return buildElement(node, ctx, []);
|
|
1406
1486
|
const props = buildProps(node.openingElement.attributes, ctx);
|
|
1407
1487
|
const children = buildChildren(node.children, ctx);
|
|
1408
1488
|
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1412
|
-
}
|
|
1413
|
-
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 } : {} };
|
|
1414
1491
|
}
|
|
1415
1492
|
function buildJsxIR(node, ctx) {
|
|
1416
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.
|
|
@@ -278,6 +279,19 @@ var INTRINSICS = {
|
|
|
278
279
|
// dependency analysis must not guess at.
|
|
279
280
|
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
280
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" },
|
|
281
295
|
// An environment variable, public or server-only; the browser build refuses a secret.
|
|
282
296
|
$env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
|
|
283
297
|
};
|
|
@@ -455,13 +469,16 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
455
469
|
used.add("createComponent");
|
|
456
470
|
const spreads = props.filter((p) => p.kind === "spread");
|
|
457
471
|
const normal = props.filter((p) => p.kind !== "spread");
|
|
458
|
-
const
|
|
459
|
-
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
460
|
-
);
|
|
472
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
461
473
|
if (children.length > 0) {
|
|
462
|
-
|
|
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(", ")} })`;
|
|
463
481
|
}
|
|
464
|
-
let obj = `{ ${entries.join(", ")} }`;
|
|
465
482
|
if (spreads.length > 0) {
|
|
466
483
|
used.add("mergeProps");
|
|
467
484
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
@@ -543,7 +560,8 @@ function emitNode(node, used, templates) {
|
|
|
543
560
|
} else {
|
|
544
561
|
used.add("spread");
|
|
545
562
|
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
546
|
-
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)}` : "";
|
|
547
565
|
parts.push(
|
|
548
566
|
`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
|
|
549
567
|
);
|
|
@@ -614,6 +632,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
|
|
|
614
632
|
function escapeTemplate(s) {
|
|
615
633
|
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
616
634
|
}
|
|
635
|
+
var CONTAINER = "container";
|
|
617
636
|
function capitalize(s) {
|
|
618
637
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
619
638
|
}
|
|
@@ -668,10 +687,6 @@ var Lowerer = class {
|
|
|
668
687
|
if (hasElse) i = elseIdx;
|
|
669
688
|
continue;
|
|
670
689
|
}
|
|
671
|
-
if (child.attributes.some((a) => a.kind === "EachDirective")) {
|
|
672
|
-
out.push(this.lowerEach(child));
|
|
673
|
-
continue;
|
|
674
|
-
}
|
|
675
690
|
}
|
|
676
691
|
const node = this.lowerNode(child);
|
|
677
692
|
if (node) out.push(node);
|
|
@@ -713,13 +728,16 @@ var Lowerer = class {
|
|
|
713
728
|
node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
|
|
714
729
|
return node;
|
|
715
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 };
|
|
716
734
|
const at = this.tagAt(el);
|
|
717
735
|
return {
|
|
718
736
|
kind: "element",
|
|
719
737
|
tag: el.tag,
|
|
720
738
|
svg: el.namespace === "svg",
|
|
721
739
|
props: this.lowerAttributes(el.attributes),
|
|
722
|
-
children
|
|
740
|
+
children,
|
|
723
741
|
static: false,
|
|
724
742
|
...at ? { at } : {}
|
|
725
743
|
};
|
|
@@ -791,10 +809,14 @@ var Lowerer = class {
|
|
|
791
809
|
const at = this.tagAt(el);
|
|
792
810
|
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
793
811
|
}
|
|
794
|
-
/**
|
|
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
|
+
*/
|
|
795
817
|
lowerEach(el) {
|
|
796
818
|
const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
|
|
797
|
-
if (!eachDir || eachDir.kind !== "EachDirective") return
|
|
819
|
+
if (!eachDir || eachDir.kind !== "EachDirective") return null;
|
|
798
820
|
const each = this.hole(eachDir.hole);
|
|
799
821
|
const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
|
|
800
822
|
if (eachDir.key) {
|
|
@@ -804,12 +826,12 @@ var Lowerer = class {
|
|
|
804
826
|
const arrow = this.itemArrow(el);
|
|
805
827
|
let childrenCode;
|
|
806
828
|
if (arrow) {
|
|
807
|
-
const
|
|
808
|
-
|
|
809
|
-
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}`;
|
|
810
831
|
} else {
|
|
811
|
-
const
|
|
812
|
-
|
|
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")}`;
|
|
813
835
|
}
|
|
814
836
|
const children = [{ kind: "expr", code: childrenCode, reactive: false }];
|
|
815
837
|
const at = this.tagAt(el);
|
|
@@ -826,25 +848,6 @@ var Lowerer = class {
|
|
|
826
848
|
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
827
849
|
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
828
850
|
}
|
|
829
|
-
/** Same element (minus each/key) but with explicit IR children. */
|
|
830
|
-
rebuildWithChildren(el, children) {
|
|
831
|
-
const props = this.lowerAttributes(
|
|
832
|
-
el.attributes.filter((a) => a.kind !== "EachDirective")
|
|
833
|
-
);
|
|
834
|
-
if (el.kind === "Element") {
|
|
835
|
-
const at = this.tagAt(el);
|
|
836
|
-
return {
|
|
837
|
-
kind: "element",
|
|
838
|
-
tag: el.tag,
|
|
839
|
-
svg: el.namespace === "svg",
|
|
840
|
-
props,
|
|
841
|
-
children,
|
|
842
|
-
static: false,
|
|
843
|
-
...at ? { at } : {}
|
|
844
|
-
};
|
|
845
|
-
}
|
|
846
|
-
return { kind: "component", name: el.tag, props, children };
|
|
847
|
-
}
|
|
848
851
|
// --- attributes ---------------------------------------------------------
|
|
849
852
|
lowerAttributes(attrs) {
|
|
850
853
|
const props = [];
|
|
@@ -1221,6 +1224,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
|
|
|
1221
1224
|
"mouseup"
|
|
1222
1225
|
]);
|
|
1223
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
|
+
}
|
|
1224
1243
|
var posOf = (node, ctx) => {
|
|
1225
1244
|
if (!ctx.sourceFile) return void 0;
|
|
1226
1245
|
const at = node.loc?.start;
|
|
@@ -1329,7 +1348,16 @@ function buildProps(attrs, ctx) {
|
|
|
1329
1348
|
}
|
|
1330
1349
|
function buildChildren(children, ctx) {
|
|
1331
1350
|
const out = [];
|
|
1332
|
-
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
|
+
}
|
|
1333
1361
|
if (child.type === "JSXText") {
|
|
1334
1362
|
const text = cleanTemplateText(child.value);
|
|
1335
1363
|
if (text) out.push({ kind: "text", value: text });
|
|
@@ -1366,19 +1394,68 @@ function readLoad(attrs) {
|
|
|
1366
1394
|
}
|
|
1367
1395
|
return void 0;
|
|
1368
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
|
+
}
|
|
1369
1447
|
function buildNode(node, ctx) {
|
|
1370
1448
|
if (node.type === "JSXFragment") {
|
|
1371
1449
|
return { kind: "fragment", children: buildChildren(node.children, ctx) };
|
|
1372
1450
|
}
|
|
1451
|
+
if (directive(node, "if")) return buildIf(node, null, ctx);
|
|
1373
1452
|
const { tag, component } = tagInfo(node.openingElement.name, ctx);
|
|
1453
|
+
if (!component) return buildElement(node, ctx, []);
|
|
1374
1454
|
const props = buildProps(node.openingElement.attributes, ctx);
|
|
1375
1455
|
const children = buildChildren(node.children, ctx);
|
|
1376
1456
|
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1380
|
-
}
|
|
1381
|
-
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 } : {} };
|
|
1382
1459
|
}
|
|
1383
1460
|
function buildJsxIR(node, ctx) {
|
|
1384
1461
|
const ir = buildNode(node, ctx);
|