@fluixi/compiler 1.0.0-alpha.91 → 1.0.0-alpha.93
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analyze/intrinsics.cjs +21 -1
- package/dist/analyze/intrinsics.d.ts.map +1 -1
- package/dist/analyze/intrinsics.js +20 -0
- package/dist/analyze/intrinsics.mjs +21 -1
- package/dist/analyze/reactive-bindings.cjs +21 -1
- package/dist/analyze/reactive-bindings.mjs +21 -1
- package/dist/analyze/static-graph.cjs +25 -5
- package/dist/analyze/static-graph.d.ts.map +1 -1
- package/dist/analyze/static-graph.js +6 -4
- package/dist/analyze/static-graph.mjs +25 -5
- package/dist/{babel-VBLQRHQ2.mjs → babel-RJHJQVEM.mjs} +4 -4
- package/dist/{chunk-D53J7ICY.mjs → chunk-56QSOVVL.mjs} +1 -1
- package/dist/{chunk-S4J4OCWK.mjs → chunk-OD53TMER.mjs} +22 -2
- package/dist/{chunk-TWDJUXWZ.mjs → chunk-R7PPFBCL.mjs} +1 -1
- package/dist/{chunk-QX6Y3OAR.mjs → chunk-TJIH7TLX.mjs} +1 -1
- package/dist/{chunk-3JWMCK4A.mjs → chunk-U7HCFLZ2.mjs} +153 -48
- package/dist/codegen/backends/imperative.cjs +27 -7
- package/dist/codegen/backends/imperative.d.ts.map +1 -1
- package/dist/codegen/backends/imperative.js +19 -4
- package/dist/codegen/backends/imperative.mjs +27 -7
- package/dist/frontend/babel/build-ir-lit.cjs +123 -44
- package/dist/frontend/babel/build-ir-lit.mjs +123 -44
- package/dist/frontend/babel/build-ir.cjs +123 -44
- package/dist/frontend/babel/build-ir.mjs +123 -44
- package/dist/frontend/babel/index.cjs +124 -45
- package/dist/frontend/babel/index.mjs +124 -45
- package/dist/frontend/babel/lower-template.cjs +123 -44
- package/dist/frontend/babel/lower-template.mjs +123 -44
- package/dist/frontend/babel/plugin.cjs +123 -44
- package/dist/frontend/babel/plugin.mjs +123 -44
- package/dist/index.cjs +86 -46
- package/dist/index.mjs +88 -48
- package/dist/integrations.cjs +246 -61
- package/dist/integrations.mjs +53 -19
- package/dist/lower/compile-template.cjs +44 -39
- package/dist/lower/compile-template.mjs +44 -39
- package/dist/lower/jsx.cjs +509 -6
- package/dist/lower/jsx.d.ts.map +1 -1
- package/dist/lower/jsx.js +114 -6
- package/dist/lower/jsx.mjs +509 -6
- package/dist/lower/template.cjs +43 -38
- package/dist/lower/template.d.ts.map +1 -1
- package/dist/lower/template.js +22 -30
- package/dist/lower/template.mjs +43 -38
- package/dist/resolve/builtins.cjs +1 -1
- package/dist/resolve/builtins.mjs +1 -1
- package/dist/resolve/component-globals.cjs +33 -8
- package/dist/resolve/component-globals.d.cts +8 -0
- package/dist/resolve/component-globals.d.ts +8 -0
- package/dist/resolve/component-globals.d.ts.map +1 -1
- package/dist/resolve/component-globals.js +21 -12
- package/dist/resolve/component-globals.mjs +33 -8
- package/dist/resolve/write-globals.cjs +59 -8
- package/dist/resolve/write-globals.d.cts +6 -0
- package/dist/resolve/write-globals.d.ts +6 -0
- package/dist/resolve/write-globals.d.ts.map +1 -1
- package/dist/resolve/write-globals.js +35 -2
- package/dist/resolve/write-globals.mjs +61 -10
- package/dist/{server-fns-J56G3PFP.mjs → server-fns-ZFRDIQ2X.mjs} +4 -4
- package/dist/{server-functions-7PUFLRGF.mjs → server-functions-CTDXX67X.mjs} +3 -3
- package/dist/transform/index.cjs +199 -49
- package/dist/transform/index.mjs +199 -49
- package/dist/transform/intrinsics.cjs +47 -2
- package/dist/transform/intrinsics.d.cts +12 -0
- package/dist/transform/intrinsics.d.ts +12 -0
- package/dist/transform/intrinsics.d.ts.map +1 -1
- package/dist/transform/intrinsics.js +43 -1
- package/dist/transform/intrinsics.mjs +47 -2
- package/dist/transform/source-locations.cjs +67 -4
- package/dist/transform/source-locations.d.cts +7 -2
- package/dist/transform/source-locations.d.ts +7 -2
- package/dist/transform/source-locations.d.ts.map +1 -1
- package/dist/transform/source-locations.js +68 -3
- package/dist/transform/source-locations.mjs +67 -4
- package/dist/transform/templates.cjs +198 -48
- package/dist/transform/templates.d.ts.map +1 -1
- package/dist/transform/templates.js +5 -3
- package/dist/transform/templates.mjs +198 -48
- package/dist/{transform-D536WXGM.mjs → transform-QWWQDCXE.mjs} +33 -8
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +9 -3
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @fluixi/compiler v1.0.0-alpha.
|
|
1
|
+
/*! @fluixi/compiler v1.0.0-alpha.93 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
2
|
"use strict";
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -41,6 +41,7 @@ __export(index_exports, {
|
|
|
41
41
|
imperativeBackend: () => imperativeBackend,
|
|
42
42
|
isReactiveShape: () => isReactiveShape,
|
|
43
43
|
lowerTemplate: () => lowerTemplate,
|
|
44
|
+
patternCandidates: () => patternCandidates,
|
|
44
45
|
resolveComponentSources: () => resolveComponentSources,
|
|
45
46
|
routerComponents: () => routerComponents,
|
|
46
47
|
walk: () => walk,
|
|
@@ -327,6 +328,7 @@ var INTRINSICS = {
|
|
|
327
328
|
$effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
|
|
328
329
|
$store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
|
|
329
330
|
$resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
|
|
331
|
+
$cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
|
|
330
332
|
// No short runtime form for these yet, so the intrinsic stands for the `create*`
|
|
331
333
|
// name. Still worth having: no import to write, and when a shorter form is
|
|
332
334
|
// designed, the change is this line rather than a compiler pass.
|
|
@@ -336,7 +338,22 @@ var INTRINSICS = {
|
|
|
336
338
|
// does. `untrack` suppresses dependency collection, which is exactly the kind of thing
|
|
337
339
|
// dependency analysis must not guess at.
|
|
338
340
|
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
339
|
-
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" }
|
|
341
|
+
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
|
|
342
|
+
// Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
|
|
343
|
+
// no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
|
|
344
|
+
$batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
|
|
345
|
+
$cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
|
|
346
|
+
$root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
|
|
347
|
+
$context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
|
|
348
|
+
$renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
|
|
349
|
+
$computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
|
|
350
|
+
// Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
|
|
351
|
+
// handle as far as reads go: the same accessor with `set` on it.
|
|
352
|
+
$storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
353
|
+
$asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
354
|
+
$persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
|
|
355
|
+
// An environment variable, public or server-only; the browser build refuses a secret.
|
|
356
|
+
$env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
|
|
340
357
|
};
|
|
341
358
|
var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join("|")})\\s*\\(`);
|
|
342
359
|
|
|
@@ -512,13 +529,16 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
512
529
|
used.add("createComponent");
|
|
513
530
|
const spreads = props.filter((p) => p.kind === "spread");
|
|
514
531
|
const normal = props.filter((p) => p.kind !== "spread");
|
|
515
|
-
const
|
|
516
|
-
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
517
|
-
);
|
|
532
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
518
533
|
if (children.length > 0) {
|
|
519
|
-
|
|
534
|
+
bodies.push(["children", emitChildren(children, used, templates)]);
|
|
535
|
+
}
|
|
536
|
+
let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
|
|
537
|
+
if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
|
|
538
|
+
const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
|
|
539
|
+
const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
|
|
540
|
+
obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
|
|
520
541
|
}
|
|
521
|
-
let obj = `{ ${entries.join(", ")} }`;
|
|
522
542
|
if (spreads.length > 0) {
|
|
523
543
|
used.add("mergeProps");
|
|
524
544
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
@@ -600,7 +620,8 @@ function emitNode(node, used, templates) {
|
|
|
600
620
|
} else {
|
|
601
621
|
used.add("spread");
|
|
602
622
|
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
603
|
-
const
|
|
623
|
+
const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
|
|
624
|
+
const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
|
|
604
625
|
parts.push(
|
|
605
626
|
`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
|
|
606
627
|
);
|
|
@@ -671,6 +692,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
|
|
|
671
692
|
function escapeTemplate(s) {
|
|
672
693
|
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
673
694
|
}
|
|
695
|
+
var CONTAINER = "container";
|
|
674
696
|
function capitalize(s) {
|
|
675
697
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
676
698
|
}
|
|
@@ -725,10 +747,6 @@ var Lowerer = class {
|
|
|
725
747
|
if (hasElse) i = elseIdx;
|
|
726
748
|
continue;
|
|
727
749
|
}
|
|
728
|
-
if (child.attributes.some((a) => a.kind === "EachDirective")) {
|
|
729
|
-
out.push(this.lowerEach(child));
|
|
730
|
-
continue;
|
|
731
|
-
}
|
|
732
750
|
}
|
|
733
751
|
const node = this.lowerNode(child);
|
|
734
752
|
if (node) out.push(node);
|
|
@@ -770,13 +788,16 @@ var Lowerer = class {
|
|
|
770
788
|
node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
|
|
771
789
|
return node;
|
|
772
790
|
}
|
|
791
|
+
const each = this.lowerEach(el);
|
|
792
|
+
const children = each ? [each] : this.lowerChildren(el.children);
|
|
793
|
+
if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
773
794
|
const at = this.tagAt(el);
|
|
774
795
|
return {
|
|
775
796
|
kind: "element",
|
|
776
797
|
tag: el.tag,
|
|
777
798
|
svg: el.namespace === "svg",
|
|
778
799
|
props: this.lowerAttributes(el.attributes),
|
|
779
|
-
children
|
|
800
|
+
children,
|
|
780
801
|
static: false,
|
|
781
802
|
...at ? { at } : {}
|
|
782
803
|
};
|
|
@@ -848,10 +869,14 @@ var Lowerer = class {
|
|
|
848
869
|
const at = this.tagAt(el);
|
|
849
870
|
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
850
871
|
}
|
|
851
|
-
/**
|
|
872
|
+
/**
|
|
873
|
+
* `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
|
|
874
|
+
* content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
|
|
875
|
+
* it is. Null when the element has no `each`.
|
|
876
|
+
*/
|
|
852
877
|
lowerEach(el) {
|
|
853
878
|
const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
|
|
854
|
-
if (!eachDir || eachDir.kind !== "EachDirective") return
|
|
879
|
+
if (!eachDir || eachDir.kind !== "EachDirective") return null;
|
|
855
880
|
const each = this.hole(eachDir.hole);
|
|
856
881
|
const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
|
|
857
882
|
if (eachDir.key) {
|
|
@@ -861,12 +886,12 @@ var Lowerer = class {
|
|
|
861
886
|
const arrow = this.itemArrow(el);
|
|
862
887
|
let childrenCode;
|
|
863
888
|
if (arrow) {
|
|
864
|
-
const
|
|
865
|
-
|
|
866
|
-
childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
|
|
889
|
+
const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
|
|
890
|
+
childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
|
|
867
891
|
} else {
|
|
868
|
-
const
|
|
869
|
-
|
|
892
|
+
const content = this.lowerChildren(el.children);
|
|
893
|
+
const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
|
|
894
|
+
childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
|
|
870
895
|
}
|
|
871
896
|
const children = [{ kind: "expr", code: childrenCode, reactive: false }];
|
|
872
897
|
const at = this.tagAt(el);
|
|
@@ -883,25 +908,6 @@ var Lowerer = class {
|
|
|
883
908
|
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
884
909
|
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
885
910
|
}
|
|
886
|
-
/** Same element (minus each/key) but with explicit IR children. */
|
|
887
|
-
rebuildWithChildren(el, children) {
|
|
888
|
-
const props = this.lowerAttributes(
|
|
889
|
-
el.attributes.filter((a) => a.kind !== "EachDirective")
|
|
890
|
-
);
|
|
891
|
-
if (el.kind === "Element") {
|
|
892
|
-
const at = this.tagAt(el);
|
|
893
|
-
return {
|
|
894
|
-
kind: "element",
|
|
895
|
-
tag: el.tag,
|
|
896
|
-
svg: el.namespace === "svg",
|
|
897
|
-
props,
|
|
898
|
-
children,
|
|
899
|
-
static: false,
|
|
900
|
-
...at ? { at } : {}
|
|
901
|
-
};
|
|
902
|
-
}
|
|
903
|
-
return { kind: "component", name: el.tag, props, children };
|
|
904
|
-
}
|
|
905
911
|
// --- attributes ---------------------------------------------------------
|
|
906
912
|
lowerAttributes(attrs) {
|
|
907
913
|
const props = [];
|
|
@@ -1440,7 +1446,8 @@ function componentGlobals(rules, options = {}) {
|
|
|
1440
1446
|
builtins = true,
|
|
1441
1447
|
intrinsics = true,
|
|
1442
1448
|
generator = "@fluixi/compiler",
|
|
1443
|
-
specifier = (module2) => module2
|
|
1449
|
+
specifier = (module2) => module2,
|
|
1450
|
+
candidates = []
|
|
1444
1451
|
} = options;
|
|
1445
1452
|
const all = [...rules];
|
|
1446
1453
|
if (builtins) {
|
|
@@ -1452,7 +1459,8 @@ function componentGlobals(rules, options = {}) {
|
|
|
1452
1459
|
const shadowedByDom = [];
|
|
1453
1460
|
const unavailable = [];
|
|
1454
1461
|
const resolved = /* @__PURE__ */ new Map();
|
|
1455
|
-
|
|
1462
|
+
const names = /* @__PURE__ */ new Set([...resolver.names(), ...[...candidates].filter((n) => /^[A-Za-z_$][\w$]*$/.test(n))]);
|
|
1463
|
+
for (const name of [...names].sort()) {
|
|
1456
1464
|
const target = resolver.resolve(name);
|
|
1457
1465
|
if (!target) continue;
|
|
1458
1466
|
const pkg = packageOf(target.module);
|
|
@@ -1463,7 +1471,11 @@ function componentGlobals(rules, options = {}) {
|
|
|
1463
1471
|
resolved.set(name, target);
|
|
1464
1472
|
}
|
|
1465
1473
|
}
|
|
1466
|
-
const
|
|
1474
|
+
const usableIntrinsics = intrinsics ? Object.entries(INTRINSICS).filter(([, i]) => {
|
|
1475
|
+
const pkg = packageOf(i.module);
|
|
1476
|
+
return !available || !pkg || available.has(pkg);
|
|
1477
|
+
}) : [];
|
|
1478
|
+
const intrinsicModules = [...new Set(usableIntrinsics.map(([, i]) => i.module))];
|
|
1467
1479
|
const modules = [
|
|
1468
1480
|
.../* @__PURE__ */ new Set([...declared.map((name) => resolved.get(name).module), ...intrinsicModules])
|
|
1469
1481
|
].sort();
|
|
@@ -1483,15 +1495,18 @@ function componentGlobals(rules, options = {}) {
|
|
|
1483
1495
|
return `// import { ${n} } from '${module2 ? specifier(module2) : ""}';
|
|
1484
1496
|
`;
|
|
1485
1497
|
}).join("") : "";
|
|
1486
|
-
const intrinsicLines =
|
|
1498
|
+
const intrinsicLines = usableIntrinsics.map(
|
|
1487
1499
|
([name, intrinsic]) => ` const ${name}: typeof ${namespaceAlias(intrinsic.module)}.${intrinsic.export};`
|
|
1488
|
-
)
|
|
1500
|
+
);
|
|
1489
1501
|
const patternRules = rules.filter((rule) => "match" in rule).length;
|
|
1490
|
-
const patternNote = patternRules
|
|
1502
|
+
const patternNote = patternRules === 0 ? "" : options.candidates ? `//
|
|
1503
|
+
// ${patternRules} pattern rule${patternRules === 1 ? "" : "s"}: declared for the files the module path
|
|
1504
|
+
// points at. A name with no such file still resolves at build time; declare it by hand.
|
|
1505
|
+
` : `//
|
|
1491
1506
|
// ${patternRules} pattern rule${patternRules === 1 ? "" : "s"} cannot be declared:
|
|
1492
1507
|
// a pattern matches unboundedly many names, so there is no list to emit. Those
|
|
1493
1508
|
// tags still resolve at build time; declare them by hand if the editor needs them.
|
|
1494
|
-
|
|
1509
|
+
`;
|
|
1495
1510
|
const source = `// GENERATED by ${generator}, do not edit.
|
|
1496
1511
|
//
|
|
1497
1512
|
// The components \`resolve\` supplies without an import. This describes what the
|
|
@@ -1550,6 +1565,30 @@ function projectDependencies(root) {
|
|
|
1550
1565
|
return void 0;
|
|
1551
1566
|
}
|
|
1552
1567
|
}
|
|
1568
|
+
function patternCandidates(rules, root) {
|
|
1569
|
+
const out = [];
|
|
1570
|
+
for (const rule of rules) {
|
|
1571
|
+
if (!("match" in rule) || !rule.module.startsWith("/")) continue;
|
|
1572
|
+
const dir = (0, import_node_path.dirname)(rule.module);
|
|
1573
|
+
const file = (0, import_node_path.basename)(rule.module);
|
|
1574
|
+
const group = file.match(/\$\d+/);
|
|
1575
|
+
if (dir.includes("$") || !group || file.indexOf("$", group.index + 1) !== -1) continue;
|
|
1576
|
+
const before = file.slice(0, group.index);
|
|
1577
|
+
const after = file.slice(group.index + group[0].length);
|
|
1578
|
+
let entries;
|
|
1579
|
+
try {
|
|
1580
|
+
entries = (0, import_node_fs.readdirSync)((0, import_node_path.resolve)(root, dir.slice(1)));
|
|
1581
|
+
} catch {
|
|
1582
|
+
continue;
|
|
1583
|
+
}
|
|
1584
|
+
for (const entry of entries) {
|
|
1585
|
+
if (entry.length > before.length + after.length && entry.startsWith(before) && entry.endsWith(after)) {
|
|
1586
|
+
out.push(entry.slice(before.length, entry.length - after.length));
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
return out;
|
|
1591
|
+
}
|
|
1553
1592
|
function writeComponentGlobals(options, root) {
|
|
1554
1593
|
if (options.globals === false) return { status: "skipped", reason: "disabled" };
|
|
1555
1594
|
const rules = options.resolve ?? [];
|
|
@@ -1565,6 +1604,7 @@ function writeComponentGlobals(options, root) {
|
|
|
1565
1604
|
}
|
|
1566
1605
|
const { source, declared } = componentGlobals(rules, {
|
|
1567
1606
|
available: projectDependencies(root),
|
|
1607
|
+
candidates: patternCandidates(rules, root),
|
|
1568
1608
|
specifier: (module2) => forTypeScript(module2, (0, import_node_path.dirname)(target), root)
|
|
1569
1609
|
});
|
|
1570
1610
|
if (existing === source) {
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @fluixi/compiler v1.0.0-alpha.
|
|
1
|
+
/*! @fluixi/compiler v1.0.0-alpha.93 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
2
|
|
|
3
3
|
// src/analyze/reactive-expr.ts
|
|
4
4
|
var STABLE_MEMBER = "children";
|
|
@@ -281,6 +281,7 @@ var INTRINSICS = {
|
|
|
281
281
|
$effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
|
|
282
282
|
$store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
|
|
283
283
|
$resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
|
|
284
|
+
$cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
|
|
284
285
|
// No short runtime form for these yet, so the intrinsic stands for the `create*`
|
|
285
286
|
// name. Still worth having: no import to write, and when a shorter form is
|
|
286
287
|
// designed, the change is this line rather than a compiler pass.
|
|
@@ -290,7 +291,22 @@ var INTRINSICS = {
|
|
|
290
291
|
// does. `untrack` suppresses dependency collection, which is exactly the kind of thing
|
|
291
292
|
// dependency analysis must not guess at.
|
|
292
293
|
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
293
|
-
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" }
|
|
294
|
+
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
|
|
295
|
+
// Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
|
|
296
|
+
// no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
|
|
297
|
+
$batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
|
|
298
|
+
$cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
|
|
299
|
+
$root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
|
|
300
|
+
$context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
|
|
301
|
+
$renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
|
|
302
|
+
$computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
|
|
303
|
+
// Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
|
|
304
|
+
// handle as far as reads go: the same accessor with `set` on it.
|
|
305
|
+
$storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
306
|
+
$asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
307
|
+
$persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
|
|
308
|
+
// An environment variable, public or server-only; the browser build refuses a secret.
|
|
309
|
+
$env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
|
|
294
310
|
};
|
|
295
311
|
var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join("|")})\\s*\\(`);
|
|
296
312
|
|
|
@@ -466,13 +482,16 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
466
482
|
used.add("createComponent");
|
|
467
483
|
const spreads = props.filter((p) => p.kind === "spread");
|
|
468
484
|
const normal = props.filter((p) => p.kind !== "spread");
|
|
469
|
-
const
|
|
470
|
-
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
471
|
-
);
|
|
485
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
472
486
|
if (children.length > 0) {
|
|
473
|
-
|
|
487
|
+
bodies.push(["children", emitChildren(children, used, templates)]);
|
|
488
|
+
}
|
|
489
|
+
let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
|
|
490
|
+
if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
|
|
491
|
+
const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
|
|
492
|
+
const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
|
|
493
|
+
obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
|
|
474
494
|
}
|
|
475
|
-
let obj = `{ ${entries.join(", ")} }`;
|
|
476
495
|
if (spreads.length > 0) {
|
|
477
496
|
used.add("mergeProps");
|
|
478
497
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
@@ -554,7 +573,8 @@ function emitNode(node, used, templates) {
|
|
|
554
573
|
} else {
|
|
555
574
|
used.add("spread");
|
|
556
575
|
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
557
|
-
const
|
|
576
|
+
const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
|
|
577
|
+
const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
|
|
558
578
|
parts.push(
|
|
559
579
|
`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
|
|
560
580
|
);
|
|
@@ -625,6 +645,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
|
|
|
625
645
|
function escapeTemplate(s) {
|
|
626
646
|
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
627
647
|
}
|
|
648
|
+
var CONTAINER = "container";
|
|
628
649
|
function capitalize(s) {
|
|
629
650
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
630
651
|
}
|
|
@@ -679,10 +700,6 @@ var Lowerer = class {
|
|
|
679
700
|
if (hasElse) i = elseIdx;
|
|
680
701
|
continue;
|
|
681
702
|
}
|
|
682
|
-
if (child.attributes.some((a) => a.kind === "EachDirective")) {
|
|
683
|
-
out.push(this.lowerEach(child));
|
|
684
|
-
continue;
|
|
685
|
-
}
|
|
686
703
|
}
|
|
687
704
|
const node = this.lowerNode(child);
|
|
688
705
|
if (node) out.push(node);
|
|
@@ -724,13 +741,16 @@ var Lowerer = class {
|
|
|
724
741
|
node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
|
|
725
742
|
return node;
|
|
726
743
|
}
|
|
744
|
+
const each = this.lowerEach(el);
|
|
745
|
+
const children = each ? [each] : this.lowerChildren(el.children);
|
|
746
|
+
if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
727
747
|
const at = this.tagAt(el);
|
|
728
748
|
return {
|
|
729
749
|
kind: "element",
|
|
730
750
|
tag: el.tag,
|
|
731
751
|
svg: el.namespace === "svg",
|
|
732
752
|
props: this.lowerAttributes(el.attributes),
|
|
733
|
-
children
|
|
753
|
+
children,
|
|
734
754
|
static: false,
|
|
735
755
|
...at ? { at } : {}
|
|
736
756
|
};
|
|
@@ -802,10 +822,14 @@ var Lowerer = class {
|
|
|
802
822
|
const at = this.tagAt(el);
|
|
803
823
|
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
804
824
|
}
|
|
805
|
-
/**
|
|
825
|
+
/**
|
|
826
|
+
* `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
|
|
827
|
+
* content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
|
|
828
|
+
* it is. Null when the element has no `each`.
|
|
829
|
+
*/
|
|
806
830
|
lowerEach(el) {
|
|
807
831
|
const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
|
|
808
|
-
if (!eachDir || eachDir.kind !== "EachDirective") return
|
|
832
|
+
if (!eachDir || eachDir.kind !== "EachDirective") return null;
|
|
809
833
|
const each = this.hole(eachDir.hole);
|
|
810
834
|
const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
|
|
811
835
|
if (eachDir.key) {
|
|
@@ -815,12 +839,12 @@ var Lowerer = class {
|
|
|
815
839
|
const arrow = this.itemArrow(el);
|
|
816
840
|
let childrenCode;
|
|
817
841
|
if (arrow) {
|
|
818
|
-
const
|
|
819
|
-
|
|
820
|
-
childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
|
|
842
|
+
const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
|
|
843
|
+
childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
|
|
821
844
|
} else {
|
|
822
|
-
const
|
|
823
|
-
|
|
845
|
+
const content = this.lowerChildren(el.children);
|
|
846
|
+
const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
|
|
847
|
+
childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
|
|
824
848
|
}
|
|
825
849
|
const children = [{ kind: "expr", code: childrenCode, reactive: false }];
|
|
826
850
|
const at = this.tagAt(el);
|
|
@@ -837,25 +861,6 @@ var Lowerer = class {
|
|
|
837
861
|
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
838
862
|
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
839
863
|
}
|
|
840
|
-
/** Same element (minus each/key) but with explicit IR children. */
|
|
841
|
-
rebuildWithChildren(el, children) {
|
|
842
|
-
const props = this.lowerAttributes(
|
|
843
|
-
el.attributes.filter((a) => a.kind !== "EachDirective")
|
|
844
|
-
);
|
|
845
|
-
if (el.kind === "Element") {
|
|
846
|
-
const at = this.tagAt(el);
|
|
847
|
-
return {
|
|
848
|
-
kind: "element",
|
|
849
|
-
tag: el.tag,
|
|
850
|
-
svg: el.namespace === "svg",
|
|
851
|
-
props,
|
|
852
|
-
children,
|
|
853
|
-
static: false,
|
|
854
|
-
...at ? { at } : {}
|
|
855
|
-
};
|
|
856
|
-
}
|
|
857
|
-
return { kind: "component", name: el.tag, props, children };
|
|
858
|
-
}
|
|
859
864
|
// --- attributes ---------------------------------------------------------
|
|
860
865
|
lowerAttributes(attrs) {
|
|
861
866
|
const props = [];
|
|
@@ -1394,7 +1399,8 @@ function componentGlobals(rules, options = {}) {
|
|
|
1394
1399
|
builtins = true,
|
|
1395
1400
|
intrinsics = true,
|
|
1396
1401
|
generator = "@fluixi/compiler",
|
|
1397
|
-
specifier = (module) => module
|
|
1402
|
+
specifier = (module) => module,
|
|
1403
|
+
candidates = []
|
|
1398
1404
|
} = options;
|
|
1399
1405
|
const all = [...rules];
|
|
1400
1406
|
if (builtins) {
|
|
@@ -1406,7 +1412,8 @@ function componentGlobals(rules, options = {}) {
|
|
|
1406
1412
|
const shadowedByDom = [];
|
|
1407
1413
|
const unavailable = [];
|
|
1408
1414
|
const resolved = /* @__PURE__ */ new Map();
|
|
1409
|
-
|
|
1415
|
+
const names = /* @__PURE__ */ new Set([...resolver.names(), ...[...candidates].filter((n) => /^[A-Za-z_$][\w$]*$/.test(n))]);
|
|
1416
|
+
for (const name of [...names].sort()) {
|
|
1410
1417
|
const target = resolver.resolve(name);
|
|
1411
1418
|
if (!target) continue;
|
|
1412
1419
|
const pkg = packageOf(target.module);
|
|
@@ -1417,7 +1424,11 @@ function componentGlobals(rules, options = {}) {
|
|
|
1417
1424
|
resolved.set(name, target);
|
|
1418
1425
|
}
|
|
1419
1426
|
}
|
|
1420
|
-
const
|
|
1427
|
+
const usableIntrinsics = intrinsics ? Object.entries(INTRINSICS).filter(([, i]) => {
|
|
1428
|
+
const pkg = packageOf(i.module);
|
|
1429
|
+
return !available || !pkg || available.has(pkg);
|
|
1430
|
+
}) : [];
|
|
1431
|
+
const intrinsicModules = [...new Set(usableIntrinsics.map(([, i]) => i.module))];
|
|
1421
1432
|
const modules = [
|
|
1422
1433
|
.../* @__PURE__ */ new Set([...declared.map((name) => resolved.get(name).module), ...intrinsicModules])
|
|
1423
1434
|
].sort();
|
|
@@ -1437,15 +1448,18 @@ function componentGlobals(rules, options = {}) {
|
|
|
1437
1448
|
return `// import { ${n} } from '${module ? specifier(module) : ""}';
|
|
1438
1449
|
`;
|
|
1439
1450
|
}).join("") : "";
|
|
1440
|
-
const intrinsicLines =
|
|
1451
|
+
const intrinsicLines = usableIntrinsics.map(
|
|
1441
1452
|
([name, intrinsic]) => ` const ${name}: typeof ${namespaceAlias(intrinsic.module)}.${intrinsic.export};`
|
|
1442
|
-
)
|
|
1453
|
+
);
|
|
1443
1454
|
const patternRules = rules.filter((rule) => "match" in rule).length;
|
|
1444
|
-
const patternNote = patternRules
|
|
1455
|
+
const patternNote = patternRules === 0 ? "" : options.candidates ? `//
|
|
1456
|
+
// ${patternRules} pattern rule${patternRules === 1 ? "" : "s"}: declared for the files the module path
|
|
1457
|
+
// points at. A name with no such file still resolves at build time; declare it by hand.
|
|
1458
|
+
` : `//
|
|
1445
1459
|
// ${patternRules} pattern rule${patternRules === 1 ? "" : "s"} cannot be declared:
|
|
1446
1460
|
// a pattern matches unboundedly many names, so there is no list to emit. Those
|
|
1447
1461
|
// tags still resolve at build time; declare them by hand if the editor needs them.
|
|
1448
|
-
|
|
1462
|
+
`;
|
|
1449
1463
|
const source = `// GENERATED by ${generator}, do not edit.
|
|
1450
1464
|
//
|
|
1451
1465
|
// The components \`resolve\` supplies without an import. This describes what the
|
|
@@ -1461,8 +1475,8 @@ ${[...lines, ...intrinsicLines].join("\n")}
|
|
|
1461
1475
|
}
|
|
1462
1476
|
|
|
1463
1477
|
// src/resolve/write-globals.ts
|
|
1464
|
-
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
1465
|
-
import { dirname, relative, sep, resolve as resolvePath } from "node:path";
|
|
1478
|
+
import { mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
1479
|
+
import { basename, dirname, relative, sep, resolve as resolvePath } from "node:path";
|
|
1466
1480
|
var GENERATED_MARKER = "GENERATED by @fluixi/compiler";
|
|
1467
1481
|
var DEFAULT_GLOBALS_PATH = "fluixi.d.ts";
|
|
1468
1482
|
function coveredByInclude(root, target) {
|
|
@@ -1504,6 +1518,30 @@ function projectDependencies(root) {
|
|
|
1504
1518
|
return void 0;
|
|
1505
1519
|
}
|
|
1506
1520
|
}
|
|
1521
|
+
function patternCandidates(rules, root) {
|
|
1522
|
+
const out = [];
|
|
1523
|
+
for (const rule of rules) {
|
|
1524
|
+
if (!("match" in rule) || !rule.module.startsWith("/")) continue;
|
|
1525
|
+
const dir = dirname(rule.module);
|
|
1526
|
+
const file = basename(rule.module);
|
|
1527
|
+
const group = file.match(/\$\d+/);
|
|
1528
|
+
if (dir.includes("$") || !group || file.indexOf("$", group.index + 1) !== -1) continue;
|
|
1529
|
+
const before = file.slice(0, group.index);
|
|
1530
|
+
const after = file.slice(group.index + group[0].length);
|
|
1531
|
+
let entries;
|
|
1532
|
+
try {
|
|
1533
|
+
entries = readdirSync(resolvePath(root, dir.slice(1)));
|
|
1534
|
+
} catch {
|
|
1535
|
+
continue;
|
|
1536
|
+
}
|
|
1537
|
+
for (const entry of entries) {
|
|
1538
|
+
if (entry.length > before.length + after.length && entry.startsWith(before) && entry.endsWith(after)) {
|
|
1539
|
+
out.push(entry.slice(before.length, entry.length - after.length));
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
return out;
|
|
1544
|
+
}
|
|
1507
1545
|
function writeComponentGlobals(options, root) {
|
|
1508
1546
|
if (options.globals === false) return { status: "skipped", reason: "disabled" };
|
|
1509
1547
|
const rules = options.resolve ?? [];
|
|
@@ -1519,6 +1557,7 @@ function writeComponentGlobals(options, root) {
|
|
|
1519
1557
|
}
|
|
1520
1558
|
const { source, declared } = componentGlobals(rules, {
|
|
1521
1559
|
available: projectDependencies(root),
|
|
1560
|
+
candidates: patternCandidates(rules, root),
|
|
1522
1561
|
specifier: (module) => forTypeScript(module, dirname(target), root)
|
|
1523
1562
|
});
|
|
1524
1563
|
if (existing === source) {
|
|
@@ -1579,6 +1618,7 @@ export {
|
|
|
1579
1618
|
imperativeBackend,
|
|
1580
1619
|
isReactiveShape,
|
|
1581
1620
|
lowerTemplate,
|
|
1621
|
+
patternCandidates,
|
|
1582
1622
|
resolveComponentSources,
|
|
1583
1623
|
routerComponents,
|
|
1584
1624
|
walk,
|