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