@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/integrations.mjs
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
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
|
import {
|
|
3
3
|
INTRINSICS,
|
|
4
4
|
builtinComponentMap,
|
|
5
5
|
createComponentResolver
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-OD53TMER.mjs";
|
|
7
7
|
import {
|
|
8
8
|
__require
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-TJIH7TLX.mjs";
|
|
10
10
|
|
|
11
11
|
// src/resolve/write-globals.ts
|
|
12
|
-
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
13
|
-
import { dirname, relative, sep, resolve as resolvePath } from "node:path";
|
|
12
|
+
import { mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
13
|
+
import { basename, dirname, relative, sep, resolve as resolvePath } from "node:path";
|
|
14
14
|
|
|
15
15
|
// src/resolve/component-globals.ts
|
|
16
16
|
var DOM_GLOBALS = /* @__PURE__ */ new Set([
|
|
@@ -50,7 +50,8 @@ function componentGlobals(rules, options = {}) {
|
|
|
50
50
|
builtins = true,
|
|
51
51
|
intrinsics = true,
|
|
52
52
|
generator = "@fluixi/compiler",
|
|
53
|
-
specifier = (module) => module
|
|
53
|
+
specifier = (module) => module,
|
|
54
|
+
candidates = []
|
|
54
55
|
} = options;
|
|
55
56
|
const all = [...rules];
|
|
56
57
|
if (builtins) {
|
|
@@ -62,7 +63,8 @@ function componentGlobals(rules, options = {}) {
|
|
|
62
63
|
const shadowedByDom = [];
|
|
63
64
|
const unavailable = [];
|
|
64
65
|
const resolved = /* @__PURE__ */ new Map();
|
|
65
|
-
|
|
66
|
+
const names = /* @__PURE__ */ new Set([...resolver.names(), ...[...candidates].filter((n) => /^[A-Za-z_$][\w$]*$/.test(n))]);
|
|
67
|
+
for (const name of [...names].sort()) {
|
|
66
68
|
const target = resolver.resolve(name);
|
|
67
69
|
if (!target) continue;
|
|
68
70
|
const pkg = packageOf(target.module);
|
|
@@ -73,7 +75,11 @@ function componentGlobals(rules, options = {}) {
|
|
|
73
75
|
resolved.set(name, target);
|
|
74
76
|
}
|
|
75
77
|
}
|
|
76
|
-
const
|
|
78
|
+
const usableIntrinsics = intrinsics ? Object.entries(INTRINSICS).filter(([, i]) => {
|
|
79
|
+
const pkg = packageOf(i.module);
|
|
80
|
+
return !available || !pkg || available.has(pkg);
|
|
81
|
+
}) : [];
|
|
82
|
+
const intrinsicModules = [...new Set(usableIntrinsics.map(([, i]) => i.module))];
|
|
77
83
|
const modules = [
|
|
78
84
|
.../* @__PURE__ */ new Set([...declared.map((name) => resolved.get(name).module), ...intrinsicModules])
|
|
79
85
|
].sort();
|
|
@@ -93,15 +99,18 @@ function componentGlobals(rules, options = {}) {
|
|
|
93
99
|
return `// import { ${n} } from '${module ? specifier(module) : ""}';
|
|
94
100
|
`;
|
|
95
101
|
}).join("") : "";
|
|
96
|
-
const intrinsicLines =
|
|
102
|
+
const intrinsicLines = usableIntrinsics.map(
|
|
97
103
|
([name, intrinsic]) => ` const ${name}: typeof ${namespaceAlias(intrinsic.module)}.${intrinsic.export};`
|
|
98
|
-
)
|
|
104
|
+
);
|
|
99
105
|
const patternRules = rules.filter((rule) => "match" in rule).length;
|
|
100
|
-
const patternNote = patternRules
|
|
106
|
+
const patternNote = patternRules === 0 ? "" : options.candidates ? `//
|
|
107
|
+
// ${patternRules} pattern rule${patternRules === 1 ? "" : "s"}: declared for the files the module path
|
|
108
|
+
// points at. A name with no such file still resolves at build time; declare it by hand.
|
|
109
|
+
` : `//
|
|
101
110
|
// ${patternRules} pattern rule${patternRules === 1 ? "" : "s"} cannot be declared:
|
|
102
111
|
// a pattern matches unboundedly many names, so there is no list to emit. Those
|
|
103
112
|
// tags still resolve at build time; declare them by hand if the editor needs them.
|
|
104
|
-
|
|
113
|
+
`;
|
|
105
114
|
const source = `// GENERATED by ${generator}, do not edit.
|
|
106
115
|
//
|
|
107
116
|
// The components \`resolve\` supplies without an import. This describes what the
|
|
@@ -158,6 +167,30 @@ function projectDependencies(root) {
|
|
|
158
167
|
return void 0;
|
|
159
168
|
}
|
|
160
169
|
}
|
|
170
|
+
function patternCandidates(rules, root) {
|
|
171
|
+
const out = [];
|
|
172
|
+
for (const rule of rules) {
|
|
173
|
+
if (!("match" in rule) || !rule.module.startsWith("/")) continue;
|
|
174
|
+
const dir = dirname(rule.module);
|
|
175
|
+
const file = basename(rule.module);
|
|
176
|
+
const group = file.match(/\$\d+/);
|
|
177
|
+
if (dir.includes("$") || !group || file.indexOf("$", group.index + 1) !== -1) continue;
|
|
178
|
+
const before = file.slice(0, group.index);
|
|
179
|
+
const after = file.slice(group.index + group[0].length);
|
|
180
|
+
let entries;
|
|
181
|
+
try {
|
|
182
|
+
entries = readdirSync(resolvePath(root, dir.slice(1)));
|
|
183
|
+
} catch {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
for (const entry of entries) {
|
|
187
|
+
if (entry.length > before.length + after.length && entry.startsWith(before) && entry.endsWith(after)) {
|
|
188
|
+
out.push(entry.slice(before.length, entry.length - after.length));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return out;
|
|
193
|
+
}
|
|
161
194
|
function writeComponentGlobals(options, root) {
|
|
162
195
|
if (options.globals === false) return { status: "skipped", reason: "disabled" };
|
|
163
196
|
const rules = options.resolve ?? [];
|
|
@@ -173,6 +206,7 @@ function writeComponentGlobals(options, root) {
|
|
|
173
206
|
}
|
|
174
207
|
const { source, declared } = componentGlobals(rules, {
|
|
175
208
|
available: projectDependencies(root),
|
|
209
|
+
candidates: patternCandidates(rules, root),
|
|
176
210
|
specifier: (module) => forTypeScript(module, dirname(target), root)
|
|
177
211
|
});
|
|
178
212
|
if (existing === source) {
|
|
@@ -199,12 +233,12 @@ function warnIfUnreachable(root, target) {
|
|
|
199
233
|
}
|
|
200
234
|
|
|
201
235
|
// src/integrations.ts
|
|
202
|
-
import { readdirSync, readFileSync as readFileSync2 } from "node:fs";
|
|
236
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
203
237
|
import { join as _join } from "node:path";
|
|
204
238
|
async function createBabelPlugin(options = {}) {
|
|
205
239
|
let pluginModule = null;
|
|
206
240
|
try {
|
|
207
|
-
pluginModule = await import("./babel-
|
|
241
|
+
pluginModule = await import("./babel-RJHJQVEM.mjs");
|
|
208
242
|
} catch (e) {
|
|
209
243
|
console.warn("Could not load the babel front-end:", e);
|
|
210
244
|
}
|
|
@@ -375,7 +409,7 @@ function transformOptions(options, id) {
|
|
|
375
409
|
}
|
|
376
410
|
async function rewriteProps(code, id, options) {
|
|
377
411
|
if (options.propsDestructure === false) return code;
|
|
378
|
-
const { transformProps } = await import("./transform-
|
|
412
|
+
const { transformProps } = await import("./transform-QWWQDCXE.mjs");
|
|
379
413
|
const result = transformProps(code, id);
|
|
380
414
|
reportDiagnostics({ propsDiagnostics: result.diagnostics }, id);
|
|
381
415
|
return result.rewritten > 0 ? result.code : code;
|
|
@@ -398,7 +432,7 @@ function reportDiagnostics(result, id) {
|
|
|
398
432
|
}
|
|
399
433
|
async function compileModule(code, id, options) {
|
|
400
434
|
if (!options.babel) {
|
|
401
|
-
const { transformTemplates } = await import("./transform-
|
|
435
|
+
const { transformTemplates } = await import("./transform-QWWQDCXE.mjs");
|
|
402
436
|
const result2 = transformTemplates(code, id, transformOptions(options, id));
|
|
403
437
|
reportDiagnostics(result2, id);
|
|
404
438
|
return result2;
|
|
@@ -473,7 +507,7 @@ function hasServerDirective(source) {
|
|
|
473
507
|
function findServerFnModules(dir, out = []) {
|
|
474
508
|
let entries;
|
|
475
509
|
try {
|
|
476
|
-
entries =
|
|
510
|
+
entries = readdirSync2(dir, { withFileTypes: true });
|
|
477
511
|
} catch {
|
|
478
512
|
return out;
|
|
479
513
|
}
|
|
@@ -517,10 +551,10 @@ export { isServerFnRequest, handleServerFn } from '@fluixi/start/server-fn';
|
|
|
517
551
|
if (!code.includes("use server")) return null;
|
|
518
552
|
const args = { ssr: !!opts?.ssr, filename: id, root };
|
|
519
553
|
if (options.babel) {
|
|
520
|
-
const { transformServerFunctions } = await import("./server-functions-
|
|
554
|
+
const { transformServerFunctions } = await import("./server-functions-CTDXX67X.mjs");
|
|
521
555
|
return transformServerFunctions(code, args);
|
|
522
556
|
}
|
|
523
|
-
const { transformServerFns } = await import("./server-fns-
|
|
557
|
+
const { transformServerFns } = await import("./server-fns-ZFRDIQ2X.mjs");
|
|
524
558
|
return transformServerFns(code, args);
|
|
525
559
|
}
|
|
526
560
|
};
|
|
@@ -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;
|
|
@@ -468,6 +468,7 @@ var INTRINSICS = {
|
|
|
468
468
|
$effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
|
|
469
469
|
$store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
|
|
470
470
|
$resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
|
|
471
|
+
$cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
|
|
471
472
|
// No short runtime form for these yet, so the intrinsic stands for the `create*`
|
|
472
473
|
// name. Still worth having: no import to write, and when a shorter form is
|
|
473
474
|
// designed, the change is this line rather than a compiler pass.
|
|
@@ -477,7 +478,22 @@ var INTRINSICS = {
|
|
|
477
478
|
// does. `untrack` suppresses dependency collection, which is exactly the kind of thing
|
|
478
479
|
// dependency analysis must not guess at.
|
|
479
480
|
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
480
|
-
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" }
|
|
481
|
+
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
|
|
482
|
+
// Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
|
|
483
|
+
// no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
|
|
484
|
+
$batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
|
|
485
|
+
$cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
|
|
486
|
+
$root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
|
|
487
|
+
$context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
|
|
488
|
+
$renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
|
|
489
|
+
$computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
|
|
490
|
+
// Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
|
|
491
|
+
// handle as far as reads go: the same accessor with `set` on it.
|
|
492
|
+
$storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
493
|
+
$asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
494
|
+
$persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
|
|
495
|
+
// An environment variable, public or server-only; the browser build refuses a secret.
|
|
496
|
+
$env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
|
|
481
497
|
};
|
|
482
498
|
var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join("|")})\\s*\\(`);
|
|
483
499
|
|
|
@@ -653,13 +669,16 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
653
669
|
used.add("createComponent");
|
|
654
670
|
const spreads = props.filter((p) => p.kind === "spread");
|
|
655
671
|
const normal = props.filter((p) => p.kind !== "spread");
|
|
656
|
-
const
|
|
657
|
-
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
658
|
-
);
|
|
672
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
659
673
|
if (children.length > 0) {
|
|
660
|
-
|
|
674
|
+
bodies.push(["children", emitChildren(children, used, templates)]);
|
|
675
|
+
}
|
|
676
|
+
let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
|
|
677
|
+
if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
|
|
678
|
+
const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
|
|
679
|
+
const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
|
|
680
|
+
obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
|
|
661
681
|
}
|
|
662
|
-
let obj = `{ ${entries.join(", ")} }`;
|
|
663
682
|
if (spreads.length > 0) {
|
|
664
683
|
used.add("mergeProps");
|
|
665
684
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
@@ -741,7 +760,8 @@ function emitNode(node, used, templates) {
|
|
|
741
760
|
} else {
|
|
742
761
|
used.add("spread");
|
|
743
762
|
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
744
|
-
const
|
|
763
|
+
const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
|
|
764
|
+
const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
|
|
745
765
|
parts.push(
|
|
746
766
|
`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
|
|
747
767
|
);
|
|
@@ -872,6 +892,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
|
|
|
872
892
|
function escapeTemplate(s) {
|
|
873
893
|
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
874
894
|
}
|
|
895
|
+
var CONTAINER = "container";
|
|
875
896
|
function capitalize(s) {
|
|
876
897
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
877
898
|
}
|
|
@@ -926,10 +947,6 @@ var Lowerer = class {
|
|
|
926
947
|
if (hasElse) i = elseIdx;
|
|
927
948
|
continue;
|
|
928
949
|
}
|
|
929
|
-
if (child.attributes.some((a) => a.kind === "EachDirective")) {
|
|
930
|
-
out.push(this.lowerEach(child));
|
|
931
|
-
continue;
|
|
932
|
-
}
|
|
933
950
|
}
|
|
934
951
|
const node = this.lowerNode(child);
|
|
935
952
|
if (node) out.push(node);
|
|
@@ -971,13 +988,16 @@ var Lowerer = class {
|
|
|
971
988
|
node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
|
|
972
989
|
return node;
|
|
973
990
|
}
|
|
991
|
+
const each = this.lowerEach(el);
|
|
992
|
+
const children = each ? [each] : this.lowerChildren(el.children);
|
|
993
|
+
if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
974
994
|
const at = this.tagAt(el);
|
|
975
995
|
return {
|
|
976
996
|
kind: "element",
|
|
977
997
|
tag: el.tag,
|
|
978
998
|
svg: el.namespace === "svg",
|
|
979
999
|
props: this.lowerAttributes(el.attributes),
|
|
980
|
-
children
|
|
1000
|
+
children,
|
|
981
1001
|
static: false,
|
|
982
1002
|
...at ? { at } : {}
|
|
983
1003
|
};
|
|
@@ -1049,10 +1069,14 @@ var Lowerer = class {
|
|
|
1049
1069
|
const at = this.tagAt(el);
|
|
1050
1070
|
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
1051
1071
|
}
|
|
1052
|
-
/**
|
|
1072
|
+
/**
|
|
1073
|
+
* `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
|
|
1074
|
+
* content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
|
|
1075
|
+
* it is. Null when the element has no `each`.
|
|
1076
|
+
*/
|
|
1053
1077
|
lowerEach(el) {
|
|
1054
1078
|
const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
|
|
1055
|
-
if (!eachDir || eachDir.kind !== "EachDirective") return
|
|
1079
|
+
if (!eachDir || eachDir.kind !== "EachDirective") return null;
|
|
1056
1080
|
const each = this.hole(eachDir.hole);
|
|
1057
1081
|
const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
|
|
1058
1082
|
if (eachDir.key) {
|
|
@@ -1062,12 +1086,12 @@ var Lowerer = class {
|
|
|
1062
1086
|
const arrow = this.itemArrow(el);
|
|
1063
1087
|
let childrenCode;
|
|
1064
1088
|
if (arrow) {
|
|
1065
|
-
const
|
|
1066
|
-
|
|
1067
|
-
childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
|
|
1089
|
+
const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
|
|
1090
|
+
childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
|
|
1068
1091
|
} else {
|
|
1069
|
-
const
|
|
1070
|
-
|
|
1092
|
+
const content = this.lowerChildren(el.children);
|
|
1093
|
+
const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
|
|
1094
|
+
childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
|
|
1071
1095
|
}
|
|
1072
1096
|
const children = [{ kind: "expr", code: childrenCode, reactive: false }];
|
|
1073
1097
|
const at = this.tagAt(el);
|
|
@@ -1084,25 +1108,6 @@ var Lowerer = class {
|
|
|
1084
1108
|
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
1085
1109
|
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
1086
1110
|
}
|
|
1087
|
-
/** Same element (minus each/key) but with explicit IR children. */
|
|
1088
|
-
rebuildWithChildren(el, children) {
|
|
1089
|
-
const props = this.lowerAttributes(
|
|
1090
|
-
el.attributes.filter((a) => a.kind !== "EachDirective")
|
|
1091
|
-
);
|
|
1092
|
-
if (el.kind === "Element") {
|
|
1093
|
-
const at = this.tagAt(el);
|
|
1094
|
-
return {
|
|
1095
|
-
kind: "element",
|
|
1096
|
-
tag: el.tag,
|
|
1097
|
-
svg: el.namespace === "svg",
|
|
1098
|
-
props,
|
|
1099
|
-
children,
|
|
1100
|
-
static: false,
|
|
1101
|
-
...at ? { at } : {}
|
|
1102
|
-
};
|
|
1103
|
-
}
|
|
1104
|
-
return { kind: "component", name: el.tag, props, children };
|
|
1105
|
-
}
|
|
1106
1111
|
// --- attributes ---------------------------------------------------------
|
|
1107
1112
|
lowerAttributes(attrs) {
|
|
1108
1113
|
const props = [];
|
|
@@ -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/optimize/analyze-static.ts
|
|
4
4
|
function propReason(prop) {
|
|
@@ -442,6 +442,7 @@ var INTRINSICS = {
|
|
|
442
442
|
$effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
|
|
443
443
|
$store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
|
|
444
444
|
$resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
|
|
445
|
+
$cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
|
|
445
446
|
// No short runtime form for these yet, so the intrinsic stands for the `create*`
|
|
446
447
|
// name. Still worth having: no import to write, and when a shorter form is
|
|
447
448
|
// designed, the change is this line rather than a compiler pass.
|
|
@@ -451,7 +452,22 @@ var INTRINSICS = {
|
|
|
451
452
|
// does. `untrack` suppresses dependency collection, which is exactly the kind of thing
|
|
452
453
|
// dependency analysis must not guess at.
|
|
453
454
|
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
454
|
-
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" }
|
|
455
|
+
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
|
|
456
|
+
// Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
|
|
457
|
+
// no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
|
|
458
|
+
$batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
|
|
459
|
+
$cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
|
|
460
|
+
$root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
|
|
461
|
+
$context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
|
|
462
|
+
$renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
|
|
463
|
+
$computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
|
|
464
|
+
// Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
|
|
465
|
+
// handle as far as reads go: the same accessor with `set` on it.
|
|
466
|
+
$storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
467
|
+
$asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
468
|
+
$persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
|
|
469
|
+
// An environment variable, public or server-only; the browser build refuses a secret.
|
|
470
|
+
$env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
|
|
455
471
|
};
|
|
456
472
|
var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join("|")})\\s*\\(`);
|
|
457
473
|
|
|
@@ -627,13 +643,16 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
627
643
|
used.add("createComponent");
|
|
628
644
|
const spreads = props.filter((p) => p.kind === "spread");
|
|
629
645
|
const normal = props.filter((p) => p.kind !== "spread");
|
|
630
|
-
const
|
|
631
|
-
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
632
|
-
);
|
|
646
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
633
647
|
if (children.length > 0) {
|
|
634
|
-
|
|
648
|
+
bodies.push(["children", emitChildren(children, used, templates)]);
|
|
649
|
+
}
|
|
650
|
+
let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
|
|
651
|
+
if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
|
|
652
|
+
const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
|
|
653
|
+
const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
|
|
654
|
+
obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
|
|
635
655
|
}
|
|
636
|
-
let obj = `{ ${entries.join(", ")} }`;
|
|
637
656
|
if (spreads.length > 0) {
|
|
638
657
|
used.add("mergeProps");
|
|
639
658
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
@@ -715,7 +734,8 @@ function emitNode(node, used, templates) {
|
|
|
715
734
|
} else {
|
|
716
735
|
used.add("spread");
|
|
717
736
|
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
718
|
-
const
|
|
737
|
+
const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
|
|
738
|
+
const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
|
|
719
739
|
parts.push(
|
|
720
740
|
`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
|
|
721
741
|
);
|
|
@@ -848,6 +868,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
|
|
|
848
868
|
function escapeTemplate(s) {
|
|
849
869
|
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
850
870
|
}
|
|
871
|
+
var CONTAINER = "container";
|
|
851
872
|
function capitalize(s) {
|
|
852
873
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
853
874
|
}
|
|
@@ -902,10 +923,6 @@ var Lowerer = class {
|
|
|
902
923
|
if (hasElse) i = elseIdx;
|
|
903
924
|
continue;
|
|
904
925
|
}
|
|
905
|
-
if (child.attributes.some((a) => a.kind === "EachDirective")) {
|
|
906
|
-
out.push(this.lowerEach(child));
|
|
907
|
-
continue;
|
|
908
|
-
}
|
|
909
926
|
}
|
|
910
927
|
const node = this.lowerNode(child);
|
|
911
928
|
if (node) out.push(node);
|
|
@@ -947,13 +964,16 @@ var Lowerer = class {
|
|
|
947
964
|
node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
|
|
948
965
|
return node;
|
|
949
966
|
}
|
|
967
|
+
const each = this.lowerEach(el);
|
|
968
|
+
const children = each ? [each] : this.lowerChildren(el.children);
|
|
969
|
+
if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
950
970
|
const at = this.tagAt(el);
|
|
951
971
|
return {
|
|
952
972
|
kind: "element",
|
|
953
973
|
tag: el.tag,
|
|
954
974
|
svg: el.namespace === "svg",
|
|
955
975
|
props: this.lowerAttributes(el.attributes),
|
|
956
|
-
children
|
|
976
|
+
children,
|
|
957
977
|
static: false,
|
|
958
978
|
...at ? { at } : {}
|
|
959
979
|
};
|
|
@@ -1025,10 +1045,14 @@ var Lowerer = class {
|
|
|
1025
1045
|
const at = this.tagAt(el);
|
|
1026
1046
|
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
1027
1047
|
}
|
|
1028
|
-
/**
|
|
1048
|
+
/**
|
|
1049
|
+
* `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
|
|
1050
|
+
* content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
|
|
1051
|
+
* it is. Null when the element has no `each`.
|
|
1052
|
+
*/
|
|
1029
1053
|
lowerEach(el) {
|
|
1030
1054
|
const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
|
|
1031
|
-
if (!eachDir || eachDir.kind !== "EachDirective") return
|
|
1055
|
+
if (!eachDir || eachDir.kind !== "EachDirective") return null;
|
|
1032
1056
|
const each = this.hole(eachDir.hole);
|
|
1033
1057
|
const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
|
|
1034
1058
|
if (eachDir.key) {
|
|
@@ -1038,12 +1062,12 @@ var Lowerer = class {
|
|
|
1038
1062
|
const arrow = this.itemArrow(el);
|
|
1039
1063
|
let childrenCode;
|
|
1040
1064
|
if (arrow) {
|
|
1041
|
-
const
|
|
1042
|
-
|
|
1043
|
-
childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
|
|
1065
|
+
const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
|
|
1066
|
+
childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
|
|
1044
1067
|
} else {
|
|
1045
|
-
const
|
|
1046
|
-
|
|
1068
|
+
const content = this.lowerChildren(el.children);
|
|
1069
|
+
const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
|
|
1070
|
+
childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
|
|
1047
1071
|
}
|
|
1048
1072
|
const children = [{ kind: "expr", code: childrenCode, reactive: false }];
|
|
1049
1073
|
const at = this.tagAt(el);
|
|
@@ -1060,25 +1084,6 @@ var Lowerer = class {
|
|
|
1060
1084
|
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
1061
1085
|
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
1062
1086
|
}
|
|
1063
|
-
/** Same element (minus each/key) but with explicit IR children. */
|
|
1064
|
-
rebuildWithChildren(el, children) {
|
|
1065
|
-
const props = this.lowerAttributes(
|
|
1066
|
-
el.attributes.filter((a) => a.kind !== "EachDirective")
|
|
1067
|
-
);
|
|
1068
|
-
if (el.kind === "Element") {
|
|
1069
|
-
const at = this.tagAt(el);
|
|
1070
|
-
return {
|
|
1071
|
-
kind: "element",
|
|
1072
|
-
tag: el.tag,
|
|
1073
|
-
svg: el.namespace === "svg",
|
|
1074
|
-
props,
|
|
1075
|
-
children,
|
|
1076
|
-
static: false,
|
|
1077
|
-
...at ? { at } : {}
|
|
1078
|
-
};
|
|
1079
|
-
}
|
|
1080
|
-
return { kind: "component", name: el.tag, props, children };
|
|
1081
|
-
}
|
|
1082
1087
|
// --- attributes ---------------------------------------------------------
|
|
1083
1088
|
lowerAttributes(attrs) {
|
|
1084
1089
|
const props = [];
|