@fluixi/compiler 1.0.0-alpha.104 → 1.0.0-alpha.106
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/static-graph.cjs +1 -1
- package/dist/analyze/static-graph.mjs +1 -1
- package/dist/{babel-5SX36G76.mjs → babel-HJY4RVAV.mjs} +33 -20
- package/dist/{chunk-NWF66YTU.mjs → chunk-4XEOMQ57.mjs} +1 -1
- package/dist/{chunk-FMQPBKTW.mjs → chunk-JUFI6IR3.mjs} +1 -1
- package/dist/{chunk-NUET3BJB.mjs → chunk-LCQHL2VQ.mjs} +132 -10
- package/dist/{chunk-2O2YW5ZO.mjs → chunk-LGUZRHYN.mjs} +2 -2
- package/dist/{chunk-RBIYCE6M.mjs → chunk-TDN5MYWI.mjs} +1 -1
- package/dist/{chunk-ZDZKJQ5N.mjs → chunk-W7DYOMXJ.mjs} +2 -2
- package/dist/{chunk-DN33T5GJ.mjs → chunk-WHEDRMNR.mjs} +1 -1
- package/dist/codegen/backends/imperative.cjs +130 -8
- package/dist/codegen/backends/imperative.d.ts.map +1 -1
- package/dist/codegen/backends/imperative.js +62 -0
- package/dist/codegen/backends/imperative.mjs +130 -8
- package/dist/codegen/partial-template.cjs +80 -8
- package/dist/codegen/partial-template.d.cts +21 -4
- package/dist/codegen/partial-template.d.ts +21 -4
- package/dist/codegen/partial-template.d.ts.map +1 -1
- package/dist/codegen/partial-template.js +0 -0
- package/dist/codegen/partial-template.mjs +80 -8
- package/dist/codegen/serialize-static.cjs +1 -0
- package/dist/codegen/serialize-static.d.cts +8 -0
- package/dist/codegen/serialize-static.d.ts +8 -0
- package/dist/codegen/serialize-static.d.ts.map +1 -1
- package/dist/codegen/serialize-static.js +1 -1
- package/dist/codegen/serialize-static.mjs +1 -0
- package/dist/frontend/babel/build-ir-html.cjs +149 -21
- package/dist/frontend/babel/build-ir-html.d.cts +3 -1
- package/dist/frontend/babel/build-ir-html.d.ts +3 -1
- package/dist/frontend/babel/build-ir-html.d.ts.map +1 -1
- package/dist/frontend/babel/build-ir-html.mjs +149 -21
- package/dist/frontend/babel/build-ir.cjs +149 -21
- package/dist/frontend/babel/build-ir.d.cts +11 -2
- package/dist/frontend/babel/build-ir.d.ts +11 -2
- package/dist/frontend/babel/build-ir.d.ts.map +1 -1
- package/dist/frontend/babel/build-ir.js +15 -9
- package/dist/frontend/babel/build-ir.mjs +149 -21
- package/dist/frontend/babel/index.cjs +160 -25
- package/dist/frontend/babel/index.mjs +160 -25
- package/dist/frontend/babel/lower-template.cjs +149 -21
- package/dist/frontend/babel/lower-template.d.cts +2 -1
- package/dist/frontend/babel/lower-template.d.ts +2 -1
- package/dist/frontend/babel/lower-template.d.ts.map +1 -1
- package/dist/frontend/babel/lower-template.js +4 -4
- package/dist/frontend/babel/lower-template.mjs +149 -21
- package/dist/frontend/babel/plugin.cjs +158 -23
- package/dist/frontend/babel/plugin.js +9 -2
- package/dist/frontend/babel/plugin.mjs +158 -23
- package/dist/index.cjs +131 -9
- package/dist/index.mjs +131 -9
- package/dist/integrations.cjs +165 -33
- package/dist/integrations.mjs +11 -11
- package/dist/lower/compile-template.cjs +131 -9
- package/dist/lower/compile-template.mjs +131 -9
- package/dist/lower/jsx.cjs +130 -8
- package/dist/lower/jsx.mjs +130 -8
- package/dist/lower/template.cjs +130 -8
- package/dist/lower/template.mjs +130 -8
- package/dist/resolve/builtins.cjs +1 -1
- package/dist/resolve/builtins.mjs +1 -1
- package/dist/{server-fns-3Z3ZKJLB.mjs → server-fns-WZJK4BJE.mjs} +5 -5
- package/dist/{server-functions-DB7AHWE6.mjs → server-functions-4KTFFEO3.mjs} +3 -3
- package/dist/transform/index.cjs +135 -16
- package/dist/transform/index.mjs +135 -16
- package/dist/transform/templates.cjs +134 -15
- package/dist/transform/templates.d.ts.map +1 -1
- package/dist/transform/templates.js +4 -8
- package/dist/transform/templates.mjs +134 -15
- package/dist/{transform-VMK2URDB.mjs → transform-NO46NFQH.mjs} +11 -14
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +4 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @fluixi/compiler v1.0.0-alpha.
|
|
1
|
+
/*! @fluixi/compiler v1.0.0-alpha.106 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
2
|
"use strict";
|
|
3
3
|
var __create = Object.create;
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -370,10 +370,64 @@ function isHole(node) {
|
|
|
370
370
|
function isTemplateChild(node) {
|
|
371
371
|
return node.kind === "text" || node.kind === "element" && isTemplateElement(node);
|
|
372
372
|
}
|
|
373
|
+
var PROPERTY_BINDS = /* @__PURE__ */ new Set(["indeterminate"]);
|
|
374
|
+
function disposition(prop) {
|
|
375
|
+
if (prop.kind === "spread") return null;
|
|
376
|
+
if (prop.name === "ref") return "ref";
|
|
377
|
+
if (prop.name === "use") return "use";
|
|
378
|
+
if (prop.kind === "event") return prop.event ? "event" : null;
|
|
379
|
+
if (prop.kind === "prop") return "property";
|
|
380
|
+
if (prop.kind !== "attr") return null;
|
|
381
|
+
const name = prop.name;
|
|
382
|
+
if (name.startsWith("on:")) return "nativeEvent";
|
|
383
|
+
if (name.startsWith("prop:")) return "property";
|
|
384
|
+
if (name.startsWith("attr:")) return "attr";
|
|
385
|
+
if (name.startsWith("bool:")) return "boolAttr";
|
|
386
|
+
if (name === "className" || name === "class") {
|
|
387
|
+
return isPlainLiteralAttr(prop) ? "serialize" : "class";
|
|
388
|
+
}
|
|
389
|
+
if (name === "classList") {
|
|
390
|
+
if (isPlainLiteralAttr(prop)) return "serialize";
|
|
391
|
+
return prop.reactive ? "classListDynamic" : "classList";
|
|
392
|
+
}
|
|
393
|
+
if (name === "style") {
|
|
394
|
+
if (isPlainLiteralAttr(prop)) return "serialize";
|
|
395
|
+
return prop.reactive ? "styleDynamic" : "style";
|
|
396
|
+
}
|
|
397
|
+
if (name.includes(":")) return null;
|
|
398
|
+
if (PROPERTY_ONLY.has(name)) return null;
|
|
399
|
+
if (PROPERTY_BINDS.has(name)) return "property";
|
|
400
|
+
if (prop.expr === void 0) return isPlainLiteralAttr(prop) ? "serialize" : null;
|
|
401
|
+
return "attr";
|
|
402
|
+
}
|
|
403
|
+
function effectiveProps(el) {
|
|
404
|
+
const order = [];
|
|
405
|
+
const byName = /* @__PURE__ */ new Map();
|
|
406
|
+
for (const prop of el.props) {
|
|
407
|
+
if (!byName.has(prop.name)) order.push(prop.name);
|
|
408
|
+
byName.set(prop.name, prop);
|
|
409
|
+
}
|
|
410
|
+
return order.map((name) => byName.get(name));
|
|
411
|
+
}
|
|
412
|
+
function propPlan(el) {
|
|
413
|
+
const custom = el.tag.includes("-");
|
|
414
|
+
const plan = { serialize: [], bindings: [] };
|
|
415
|
+
for (const prop of effectiveProps(el)) {
|
|
416
|
+
const kind = disposition(prop);
|
|
417
|
+
if (kind === null) return null;
|
|
418
|
+
if (kind === "serialize") {
|
|
419
|
+
plan.serialize.push(prop);
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
if (custom) return null;
|
|
423
|
+
plan.bindings.push({ prop, kind });
|
|
424
|
+
}
|
|
425
|
+
return plan;
|
|
426
|
+
}
|
|
373
427
|
function isTemplateElement(node) {
|
|
374
428
|
if (node.svg) return false;
|
|
375
429
|
if (isRawTextElement(node.tag)) return false;
|
|
376
|
-
if (!node
|
|
430
|
+
if (!propPlan(node)) return false;
|
|
377
431
|
return node.children.every((c) => isTemplateChild(c) || isHole(c));
|
|
378
432
|
}
|
|
379
433
|
function containsHole(node) {
|
|
@@ -381,6 +435,15 @@ function containsHole(node) {
|
|
|
381
435
|
const children = node.children;
|
|
382
436
|
return children ? children.some(containsHole) : false;
|
|
383
437
|
}
|
|
438
|
+
function containsBinding(node) {
|
|
439
|
+
if (node.kind !== "element") return false;
|
|
440
|
+
const plan = propPlan(node);
|
|
441
|
+
if (plan && plan.bindings.length > 0) return true;
|
|
442
|
+
return node.children.some(containsBinding);
|
|
443
|
+
}
|
|
444
|
+
function interesting(node) {
|
|
445
|
+
return containsHole(node) || containsBinding(node);
|
|
446
|
+
}
|
|
384
447
|
function addressable(el) {
|
|
385
448
|
for (let i = 0; i < el.children.length; i++) {
|
|
386
449
|
const child = el.children[i];
|
|
@@ -394,9 +457,10 @@ function addressable(el) {
|
|
|
394
457
|
function planPartialTemplate(root) {
|
|
395
458
|
if (!isTemplateElement(root)) return null;
|
|
396
459
|
if (!addressable(root)) return null;
|
|
397
|
-
if (!root
|
|
460
|
+
if (!interesting(root)) return null;
|
|
398
461
|
const steps = [];
|
|
399
462
|
const holes = [];
|
|
463
|
+
const bindings = [];
|
|
400
464
|
let refCount = 0;
|
|
401
465
|
const declare2 = (expr) => {
|
|
402
466
|
const ref = `_n$${refCount++}`;
|
|
@@ -404,11 +468,17 @@ function planPartialTemplate(root) {
|
|
|
404
468
|
return ref;
|
|
405
469
|
};
|
|
406
470
|
walk2(root, "_el$");
|
|
407
|
-
return { html: serializeWithMarkers(root), tag: root.tag, steps, holes };
|
|
471
|
+
return { html: serializeWithMarkers(root), tag: root.tag, steps, holes, bindings };
|
|
408
472
|
function walk2(el, elementRef) {
|
|
473
|
+
const plan = propPlan(el);
|
|
474
|
+
if (plan) {
|
|
475
|
+
for (const binding of plan.bindings) {
|
|
476
|
+
bindings.push({ ref: elementRef, prop: binding.prop, kind: binding.kind });
|
|
477
|
+
}
|
|
478
|
+
}
|
|
409
479
|
let last = -1;
|
|
410
480
|
el.children.forEach((child, i) => {
|
|
411
|
-
if (
|
|
481
|
+
if (interesting(child)) last = i;
|
|
412
482
|
});
|
|
413
483
|
let previous = null;
|
|
414
484
|
for (let i = 0; i <= last; i++) {
|
|
@@ -422,19 +492,21 @@ function planPartialTemplate(root) {
|
|
|
422
492
|
continue;
|
|
423
493
|
}
|
|
424
494
|
const ref = declare2(expr);
|
|
425
|
-
if (child.kind === "element" &&
|
|
495
|
+
if (child.kind === "element" && interesting(child)) walk2(child, ref);
|
|
426
496
|
previous = ref;
|
|
427
497
|
}
|
|
428
498
|
}
|
|
429
499
|
}
|
|
430
500
|
function serializeWithMarkers(el) {
|
|
431
|
-
return serializeStatic(
|
|
501
|
+
return serializeStatic(templateTree(el)).split(SENTINEL).join(HOLE_START + HOLE_END);
|
|
432
502
|
}
|
|
433
|
-
function
|
|
503
|
+
function templateTree(el) {
|
|
504
|
+
const plan = propPlan(el);
|
|
434
505
|
return {
|
|
435
506
|
...el,
|
|
507
|
+
props: plan ? plan.serialize : [],
|
|
436
508
|
children: el.children.map(
|
|
437
|
-
(child) => isHole(child) ? { kind: "text", value: SENTINEL } : child.kind === "element" ?
|
|
509
|
+
(child) => isHole(child) ? { kind: "text", value: SENTINEL } : child.kind === "element" ? templateTree(child) : child
|
|
438
510
|
)
|
|
439
511
|
};
|
|
440
512
|
}
|
|
@@ -707,6 +779,55 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
707
779
|
const mark = at ? `globalThis.${SOURCE_MARKER}?.(${JSON.stringify(at.file)},${at.line},${at.column},${JSON.stringify(name)}), ` : "";
|
|
708
780
|
return `createMemo(${paren(`${mark}() => createComponent(${name}, ${obj})`, "value")})`;
|
|
709
781
|
}
|
|
782
|
+
function bindingTarget(binding) {
|
|
783
|
+
const name = binding.prop.name;
|
|
784
|
+
if (binding.kind === "boolAttr") return name.slice("bool:".length);
|
|
785
|
+
if (name.startsWith("prop:")) return name.slice("prop:".length);
|
|
786
|
+
if (name.startsWith("attr:")) return name.slice("attr:".length);
|
|
787
|
+
return htmlAttrName(name);
|
|
788
|
+
}
|
|
789
|
+
function bindingStatement(binding, used) {
|
|
790
|
+
const { ref, prop, kind } = binding;
|
|
791
|
+
const value = propValue(prop);
|
|
792
|
+
switch (kind) {
|
|
793
|
+
case "class":
|
|
794
|
+
used.add("setClassName");
|
|
795
|
+
return `setClassName(${ref}, ${value});`;
|
|
796
|
+
case "classList":
|
|
797
|
+
used.add("setClassList");
|
|
798
|
+
return `setClassList(${ref}, ${value}, []);`;
|
|
799
|
+
case "classListDynamic":
|
|
800
|
+
used.add("setDynamicClassList");
|
|
801
|
+
return `setDynamicClassList(${ref}, ${value});`;
|
|
802
|
+
case "style":
|
|
803
|
+
used.add("setStyle");
|
|
804
|
+
return `setStyle(${ref}, ${value}, []);`;
|
|
805
|
+
case "styleDynamic":
|
|
806
|
+
used.add("setDynamicStyle");
|
|
807
|
+
return `setDynamicStyle(${ref}, ${value});`;
|
|
808
|
+
case "attr":
|
|
809
|
+
used.add("setDynamicAttribute");
|
|
810
|
+
return `setDynamicAttribute(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
|
|
811
|
+
case "property":
|
|
812
|
+
used.add("setDynamicProperty");
|
|
813
|
+
return `setDynamicProperty(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
|
|
814
|
+
case "boolAttr":
|
|
815
|
+
used.add("setDynamicBoolAttribute");
|
|
816
|
+
return `setDynamicBoolAttribute(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
|
|
817
|
+
case "event":
|
|
818
|
+
used.add("addDelegatedEventListener");
|
|
819
|
+
return `addDelegatedEventListener(${ref}, ${JSON.stringify(prop.event.name)}, ${value});`;
|
|
820
|
+
case "nativeEvent":
|
|
821
|
+
used.add("addNativeEventListener");
|
|
822
|
+
return `addNativeEventListener(${ref}, ${JSON.stringify(prop.name.slice(3))}, ${value});`;
|
|
823
|
+
case "ref":
|
|
824
|
+
used.add("handleRef");
|
|
825
|
+
return `handleRef(${value}, ${ref});`;
|
|
826
|
+
case "use":
|
|
827
|
+
used.add("applyUse");
|
|
828
|
+
return `applyUse(${ref}, ${value});`;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
710
831
|
function staticAttrAssignment(el, prop) {
|
|
711
832
|
const name = htmlAttrName(prop.name);
|
|
712
833
|
const value = prop.literal;
|
|
@@ -757,6 +878,7 @@ function emitNode(node, used, templates) {
|
|
|
757
878
|
`const _el$ = templateNode(${id}, ${JSON.stringify(plan.tag)}, true${at ? `, false, ${JSON.stringify(at)}` : ""});`
|
|
758
879
|
];
|
|
759
880
|
for (const step of plan.steps) lines.push(`const ${step.ref} = ${step.expr};`);
|
|
881
|
+
for (const binding of plan.bindings) lines.push(bindingStatement(binding, used));
|
|
760
882
|
for (const hole of plan.holes) {
|
|
761
883
|
lines.push(
|
|
762
884
|
`insert(${hole.parentRef}, holeScope(${hole.startRef}, () => ${paren(emitNode(hole.node, used, templates), "arrowBody")}), ${hole.endRef}, holeContent(${hole.startRef}, ${hole.endRef}));`
|
|
@@ -1558,16 +1680,16 @@ var generate = import_generator.default.default ?? import_generator.default;
|
|
|
1558
1680
|
function gen(node) {
|
|
1559
1681
|
return generate(node, { concise: true }).code;
|
|
1560
1682
|
}
|
|
1561
|
-
function bindHole(expr, used, onNested) {
|
|
1683
|
+
function bindHole(expr, used, onNested, emit) {
|
|
1562
1684
|
const hole = {
|
|
1563
|
-
code: genExpr(expr, used, onNested),
|
|
1685
|
+
code: genExpr(expr, used, onNested, emit),
|
|
1564
1686
|
reactive: isReactiveExpr(expr)
|
|
1565
1687
|
};
|
|
1566
1688
|
if (import_core.types.isArrowFunctionExpression(expr) && !import_core.types.isBlockStatement(expr.body)) {
|
|
1567
1689
|
const body = expr.body;
|
|
1568
1690
|
hole.arrow = {
|
|
1569
1691
|
params: expr.params.map((p) => gen(p)),
|
|
1570
|
-
body: genExpr(body, used, onNested),
|
|
1692
|
+
body: genExpr(body, used, onNested, emit),
|
|
1571
1693
|
bodyReactive: isReactiveExpr(body)
|
|
1572
1694
|
};
|
|
1573
1695
|
}
|
|
@@ -1576,7 +1698,7 @@ function bindHole(expr, used, onNested) {
|
|
|
1576
1698
|
}
|
|
1577
1699
|
function lowerHtmlTemplate(node, used, opts = {}) {
|
|
1578
1700
|
const quasi = node.quasi;
|
|
1579
|
-
const holes = quasi.expressions.map((e) => bindHole(e, used, opts.onNested));
|
|
1701
|
+
const holes = quasi.expressions.map((e) => bindHole(e, used, opts.onNested, opts.emit));
|
|
1580
1702
|
return lowerTemplate(buildPieces(quasi), holes, used, opts);
|
|
1581
1703
|
}
|
|
1582
1704
|
function buildPieces(quasi) {
|
|
@@ -2064,7 +2186,7 @@ var generate2 = import_generator2.default.default ?? import_generator2.default;
|
|
|
2064
2186
|
function gen2(node) {
|
|
2065
2187
|
return generate2(node, { concise: true }).code;
|
|
2066
2188
|
}
|
|
2067
|
-
function transformNestedJSX(node, used, onNested) {
|
|
2189
|
+
function transformNestedJSX(node, used, onNested, emit) {
|
|
2068
2190
|
const clone = import_core2.types.cloneNode(node, true);
|
|
2069
2191
|
const file = import_core2.types.file(import_core2.types.program([import_core2.types.expressionStatement(clone)]));
|
|
2070
2192
|
let changed = false;
|
|
@@ -2077,9 +2199,12 @@ function transformNestedJSX(node, used, onNested) {
|
|
|
2077
2199
|
(0, import_core2.traverse)(file, {
|
|
2078
2200
|
"JSXElement|JSXFragment"(path) {
|
|
2079
2201
|
const jsx = path.node;
|
|
2080
|
-
const nestedIR = buildIR(jsx, used, onNested);
|
|
2202
|
+
const nestedIR = buildIR(jsx, used, onNested, emit);
|
|
2081
2203
|
onNested?.(nestedIR);
|
|
2082
|
-
const { code, imports, templates } = imperativeBackend.emit(nestedIR, {
|
|
2204
|
+
const { code, imports, templates } = imperativeBackend.emit(nestedIR, {
|
|
2205
|
+
templateClone: emit?.templateClone,
|
|
2206
|
+
partialTemplates: emit?.partialTemplates ?? true
|
|
2207
|
+
});
|
|
2083
2208
|
emitInto(path, inlineTemplates(code, templates), imports);
|
|
2084
2209
|
},
|
|
2085
2210
|
// Nested html templates compose: ${cond ? html`...` : html`...`},
|
|
@@ -2087,26 +2212,29 @@ function transformNestedJSX(node, used, onNested) {
|
|
|
2087
2212
|
TaggedTemplateExpression(path) {
|
|
2088
2213
|
const tag = path.node.tag;
|
|
2089
2214
|
if (!import_core2.types.isIdentifier(tag) || tag.name !== "html" && tag.name !== "svg") return;
|
|
2090
|
-
const ir = buildIRFromHtml(path.node, used, { svg: tag.name === "svg", onNested });
|
|
2215
|
+
const ir = buildIRFromHtml(path.node, used, { svg: tag.name === "svg", onNested, emit });
|
|
2091
2216
|
onNested?.(ir);
|
|
2092
|
-
const { code, imports, templates } = imperativeBackend.emit(ir, {
|
|
2217
|
+
const { code, imports, templates } = imperativeBackend.emit(ir, {
|
|
2218
|
+
templateClone: emit?.templateClone,
|
|
2219
|
+
partialTemplates: emit?.partialTemplates ?? true
|
|
2220
|
+
});
|
|
2093
2221
|
emitInto(path, inlineTemplates(code, templates), imports);
|
|
2094
2222
|
}
|
|
2095
2223
|
});
|
|
2096
2224
|
return changed ? file.program.body[0].expression : node;
|
|
2097
2225
|
}
|
|
2098
|
-
function genExpr(node, used, onNested) {
|
|
2099
|
-
return gen2(transformNestedJSX(node, used, onNested));
|
|
2226
|
+
function genExpr(node, used, onNested, emit) {
|
|
2227
|
+
return gen2(transformNestedJSX(node, used, onNested, emit));
|
|
2100
2228
|
}
|
|
2101
2229
|
function isReactiveExpr(node) {
|
|
2102
2230
|
return isReactiveShape(shapeOf(node));
|
|
2103
2231
|
}
|
|
2104
|
-
function buildIR(node, used = /* @__PURE__ */ new Set(), onNested) {
|
|
2232
|
+
function buildIR(node, used = /* @__PURE__ */ new Set(), onNested, emit) {
|
|
2105
2233
|
const ir = buildJsxIR(node, {
|
|
2106
2234
|
code: (n) => (
|
|
2107
2235
|
// A member-expression tag (`<ns.Foo/>`) is not an Expression, so it cannot go
|
|
2108
2236
|
// through the nested-JSX compile: it only ever needs printing.
|
|
2109
|
-
n.type === "JSXMemberExpression" ? gen2(n) : genExpr(n, used, onNested)
|
|
2237
|
+
n.type === "JSXMemberExpression" ? gen2(n) : genExpr(n, used, onNested, emit)
|
|
2110
2238
|
),
|
|
2111
2239
|
used
|
|
2112
2240
|
});
|
|
@@ -2441,7 +2569,10 @@ function adoptTemplates(code, templates, state) {
|
|
|
2441
2569
|
}
|
|
2442
2570
|
function emitViaIR(node, state) {
|
|
2443
2571
|
const used = /* @__PURE__ */ new Set();
|
|
2444
|
-
const ir = buildIR(node, used, nestedHook(state)
|
|
2572
|
+
const ir = buildIR(node, used, nestedHook(state), {
|
|
2573
|
+
partialTemplates: state.partialTemplates,
|
|
2574
|
+
templateClone: state.templateClone
|
|
2575
|
+
});
|
|
2445
2576
|
resolveComponentSources(ir, {
|
|
2446
2577
|
resolver: state.componentResolver,
|
|
2447
2578
|
modules: {
|
|
@@ -2490,7 +2621,11 @@ function dedupeImports(imports) {
|
|
|
2490
2621
|
function emitHtmlViaIR(node, state) {
|
|
2491
2622
|
const used = /* @__PURE__ */ new Set();
|
|
2492
2623
|
const svg = import_core3.types.isIdentifier(node.tag) && node.tag.name === "svg";
|
|
2493
|
-
const ir = buildIRFromHtml(node, used, {
|
|
2624
|
+
const ir = buildIRFromHtml(node, used, {
|
|
2625
|
+
svg,
|
|
2626
|
+
onNested: nestedHook(state),
|
|
2627
|
+
emit: { partialTemplates: state.partialTemplates, templateClone: state.templateClone }
|
|
2628
|
+
});
|
|
2494
2629
|
resolveComponentSources(ir, {
|
|
2495
2630
|
resolver: state.componentResolver,
|
|
2496
2631
|
modules: {
|
|
@@ -2579,7 +2714,7 @@ function createPreset(options = {}) {
|
|
|
2579
2714
|
{
|
|
2580
2715
|
runtime: "automatic",
|
|
2581
2716
|
importSource: "@fluixi/jsx",
|
|
2582
|
-
development:
|
|
2717
|
+
development: process.env.NODE_ENV === "development",
|
|
2583
2718
|
detectReactivity: true,
|
|
2584
2719
|
useHtmlTag: true,
|
|
2585
2720
|
hoistStatics: true,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @fluixi/compiler v1.0.0-alpha.
|
|
1
|
+
/*! @fluixi/compiler v1.0.0-alpha.106 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
2
|
|
|
3
3
|
// src/frontend/babel/plugin.ts
|
|
4
4
|
import { declare } from "@babel/helper-plugin-utils";
|
|
@@ -331,10 +331,64 @@ function isHole(node) {
|
|
|
331
331
|
function isTemplateChild(node) {
|
|
332
332
|
return node.kind === "text" || node.kind === "element" && isTemplateElement(node);
|
|
333
333
|
}
|
|
334
|
+
var PROPERTY_BINDS = /* @__PURE__ */ new Set(["indeterminate"]);
|
|
335
|
+
function disposition(prop) {
|
|
336
|
+
if (prop.kind === "spread") return null;
|
|
337
|
+
if (prop.name === "ref") return "ref";
|
|
338
|
+
if (prop.name === "use") return "use";
|
|
339
|
+
if (prop.kind === "event") return prop.event ? "event" : null;
|
|
340
|
+
if (prop.kind === "prop") return "property";
|
|
341
|
+
if (prop.kind !== "attr") return null;
|
|
342
|
+
const name = prop.name;
|
|
343
|
+
if (name.startsWith("on:")) return "nativeEvent";
|
|
344
|
+
if (name.startsWith("prop:")) return "property";
|
|
345
|
+
if (name.startsWith("attr:")) return "attr";
|
|
346
|
+
if (name.startsWith("bool:")) return "boolAttr";
|
|
347
|
+
if (name === "className" || name === "class") {
|
|
348
|
+
return isPlainLiteralAttr(prop) ? "serialize" : "class";
|
|
349
|
+
}
|
|
350
|
+
if (name === "classList") {
|
|
351
|
+
if (isPlainLiteralAttr(prop)) return "serialize";
|
|
352
|
+
return prop.reactive ? "classListDynamic" : "classList";
|
|
353
|
+
}
|
|
354
|
+
if (name === "style") {
|
|
355
|
+
if (isPlainLiteralAttr(prop)) return "serialize";
|
|
356
|
+
return prop.reactive ? "styleDynamic" : "style";
|
|
357
|
+
}
|
|
358
|
+
if (name.includes(":")) return null;
|
|
359
|
+
if (PROPERTY_ONLY.has(name)) return null;
|
|
360
|
+
if (PROPERTY_BINDS.has(name)) return "property";
|
|
361
|
+
if (prop.expr === void 0) return isPlainLiteralAttr(prop) ? "serialize" : null;
|
|
362
|
+
return "attr";
|
|
363
|
+
}
|
|
364
|
+
function effectiveProps(el) {
|
|
365
|
+
const order = [];
|
|
366
|
+
const byName = /* @__PURE__ */ new Map();
|
|
367
|
+
for (const prop of el.props) {
|
|
368
|
+
if (!byName.has(prop.name)) order.push(prop.name);
|
|
369
|
+
byName.set(prop.name, prop);
|
|
370
|
+
}
|
|
371
|
+
return order.map((name) => byName.get(name));
|
|
372
|
+
}
|
|
373
|
+
function propPlan(el) {
|
|
374
|
+
const custom = el.tag.includes("-");
|
|
375
|
+
const plan = { serialize: [], bindings: [] };
|
|
376
|
+
for (const prop of effectiveProps(el)) {
|
|
377
|
+
const kind = disposition(prop);
|
|
378
|
+
if (kind === null) return null;
|
|
379
|
+
if (kind === "serialize") {
|
|
380
|
+
plan.serialize.push(prop);
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
if (custom) return null;
|
|
384
|
+
plan.bindings.push({ prop, kind });
|
|
385
|
+
}
|
|
386
|
+
return plan;
|
|
387
|
+
}
|
|
334
388
|
function isTemplateElement(node) {
|
|
335
389
|
if (node.svg) return false;
|
|
336
390
|
if (isRawTextElement(node.tag)) return false;
|
|
337
|
-
if (!node
|
|
391
|
+
if (!propPlan(node)) return false;
|
|
338
392
|
return node.children.every((c) => isTemplateChild(c) || isHole(c));
|
|
339
393
|
}
|
|
340
394
|
function containsHole(node) {
|
|
@@ -342,6 +396,15 @@ function containsHole(node) {
|
|
|
342
396
|
const children = node.children;
|
|
343
397
|
return children ? children.some(containsHole) : false;
|
|
344
398
|
}
|
|
399
|
+
function containsBinding(node) {
|
|
400
|
+
if (node.kind !== "element") return false;
|
|
401
|
+
const plan = propPlan(node);
|
|
402
|
+
if (plan && plan.bindings.length > 0) return true;
|
|
403
|
+
return node.children.some(containsBinding);
|
|
404
|
+
}
|
|
405
|
+
function interesting(node) {
|
|
406
|
+
return containsHole(node) || containsBinding(node);
|
|
407
|
+
}
|
|
345
408
|
function addressable(el) {
|
|
346
409
|
for (let i = 0; i < el.children.length; i++) {
|
|
347
410
|
const child = el.children[i];
|
|
@@ -355,9 +418,10 @@ function addressable(el) {
|
|
|
355
418
|
function planPartialTemplate(root) {
|
|
356
419
|
if (!isTemplateElement(root)) return null;
|
|
357
420
|
if (!addressable(root)) return null;
|
|
358
|
-
if (!root
|
|
421
|
+
if (!interesting(root)) return null;
|
|
359
422
|
const steps = [];
|
|
360
423
|
const holes = [];
|
|
424
|
+
const bindings = [];
|
|
361
425
|
let refCount = 0;
|
|
362
426
|
const declare2 = (expr) => {
|
|
363
427
|
const ref = `_n$${refCount++}`;
|
|
@@ -365,11 +429,17 @@ function planPartialTemplate(root) {
|
|
|
365
429
|
return ref;
|
|
366
430
|
};
|
|
367
431
|
walk2(root, "_el$");
|
|
368
|
-
return { html: serializeWithMarkers(root), tag: root.tag, steps, holes };
|
|
432
|
+
return { html: serializeWithMarkers(root), tag: root.tag, steps, holes, bindings };
|
|
369
433
|
function walk2(el, elementRef) {
|
|
434
|
+
const plan = propPlan(el);
|
|
435
|
+
if (plan) {
|
|
436
|
+
for (const binding of plan.bindings) {
|
|
437
|
+
bindings.push({ ref: elementRef, prop: binding.prop, kind: binding.kind });
|
|
438
|
+
}
|
|
439
|
+
}
|
|
370
440
|
let last = -1;
|
|
371
441
|
el.children.forEach((child, i) => {
|
|
372
|
-
if (
|
|
442
|
+
if (interesting(child)) last = i;
|
|
373
443
|
});
|
|
374
444
|
let previous = null;
|
|
375
445
|
for (let i = 0; i <= last; i++) {
|
|
@@ -383,19 +453,21 @@ function planPartialTemplate(root) {
|
|
|
383
453
|
continue;
|
|
384
454
|
}
|
|
385
455
|
const ref = declare2(expr);
|
|
386
|
-
if (child.kind === "element" &&
|
|
456
|
+
if (child.kind === "element" && interesting(child)) walk2(child, ref);
|
|
387
457
|
previous = ref;
|
|
388
458
|
}
|
|
389
459
|
}
|
|
390
460
|
}
|
|
391
461
|
function serializeWithMarkers(el) {
|
|
392
|
-
return serializeStatic(
|
|
462
|
+
return serializeStatic(templateTree(el)).split(SENTINEL).join(HOLE_START + HOLE_END);
|
|
393
463
|
}
|
|
394
|
-
function
|
|
464
|
+
function templateTree(el) {
|
|
465
|
+
const plan = propPlan(el);
|
|
395
466
|
return {
|
|
396
467
|
...el,
|
|
468
|
+
props: plan ? plan.serialize : [],
|
|
397
469
|
children: el.children.map(
|
|
398
|
-
(child) => isHole(child) ? { kind: "text", value: SENTINEL } : child.kind === "element" ?
|
|
470
|
+
(child) => isHole(child) ? { kind: "text", value: SENTINEL } : child.kind === "element" ? templateTree(child) : child
|
|
399
471
|
)
|
|
400
472
|
};
|
|
401
473
|
}
|
|
@@ -668,6 +740,55 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
668
740
|
const mark = at ? `globalThis.${SOURCE_MARKER}?.(${JSON.stringify(at.file)},${at.line},${at.column},${JSON.stringify(name)}), ` : "";
|
|
669
741
|
return `createMemo(${paren(`${mark}() => createComponent(${name}, ${obj})`, "value")})`;
|
|
670
742
|
}
|
|
743
|
+
function bindingTarget(binding) {
|
|
744
|
+
const name = binding.prop.name;
|
|
745
|
+
if (binding.kind === "boolAttr") return name.slice("bool:".length);
|
|
746
|
+
if (name.startsWith("prop:")) return name.slice("prop:".length);
|
|
747
|
+
if (name.startsWith("attr:")) return name.slice("attr:".length);
|
|
748
|
+
return htmlAttrName(name);
|
|
749
|
+
}
|
|
750
|
+
function bindingStatement(binding, used) {
|
|
751
|
+
const { ref, prop, kind } = binding;
|
|
752
|
+
const value = propValue(prop);
|
|
753
|
+
switch (kind) {
|
|
754
|
+
case "class":
|
|
755
|
+
used.add("setClassName");
|
|
756
|
+
return `setClassName(${ref}, ${value});`;
|
|
757
|
+
case "classList":
|
|
758
|
+
used.add("setClassList");
|
|
759
|
+
return `setClassList(${ref}, ${value}, []);`;
|
|
760
|
+
case "classListDynamic":
|
|
761
|
+
used.add("setDynamicClassList");
|
|
762
|
+
return `setDynamicClassList(${ref}, ${value});`;
|
|
763
|
+
case "style":
|
|
764
|
+
used.add("setStyle");
|
|
765
|
+
return `setStyle(${ref}, ${value}, []);`;
|
|
766
|
+
case "styleDynamic":
|
|
767
|
+
used.add("setDynamicStyle");
|
|
768
|
+
return `setDynamicStyle(${ref}, ${value});`;
|
|
769
|
+
case "attr":
|
|
770
|
+
used.add("setDynamicAttribute");
|
|
771
|
+
return `setDynamicAttribute(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
|
|
772
|
+
case "property":
|
|
773
|
+
used.add("setDynamicProperty");
|
|
774
|
+
return `setDynamicProperty(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
|
|
775
|
+
case "boolAttr":
|
|
776
|
+
used.add("setDynamicBoolAttribute");
|
|
777
|
+
return `setDynamicBoolAttribute(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
|
|
778
|
+
case "event":
|
|
779
|
+
used.add("addDelegatedEventListener");
|
|
780
|
+
return `addDelegatedEventListener(${ref}, ${JSON.stringify(prop.event.name)}, ${value});`;
|
|
781
|
+
case "nativeEvent":
|
|
782
|
+
used.add("addNativeEventListener");
|
|
783
|
+
return `addNativeEventListener(${ref}, ${JSON.stringify(prop.name.slice(3))}, ${value});`;
|
|
784
|
+
case "ref":
|
|
785
|
+
used.add("handleRef");
|
|
786
|
+
return `handleRef(${value}, ${ref});`;
|
|
787
|
+
case "use":
|
|
788
|
+
used.add("applyUse");
|
|
789
|
+
return `applyUse(${ref}, ${value});`;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
671
792
|
function staticAttrAssignment(el, prop) {
|
|
672
793
|
const name = htmlAttrName(prop.name);
|
|
673
794
|
const value = prop.literal;
|
|
@@ -718,6 +839,7 @@ function emitNode(node, used, templates) {
|
|
|
718
839
|
`const _el$ = templateNode(${id}, ${JSON.stringify(plan.tag)}, true${at ? `, false, ${JSON.stringify(at)}` : ""});`
|
|
719
840
|
];
|
|
720
841
|
for (const step of plan.steps) lines.push(`const ${step.ref} = ${step.expr};`);
|
|
842
|
+
for (const binding of plan.bindings) lines.push(bindingStatement(binding, used));
|
|
721
843
|
for (const hole of plan.holes) {
|
|
722
844
|
lines.push(
|
|
723
845
|
`insert(${hole.parentRef}, holeScope(${hole.startRef}, () => ${paren(emitNode(hole.node, used, templates), "arrowBody")}), ${hole.endRef}, holeContent(${hole.startRef}, ${hole.endRef}));`
|
|
@@ -1521,16 +1643,16 @@ var generate = _generate.default ?? _generate;
|
|
|
1521
1643
|
function gen(node) {
|
|
1522
1644
|
return generate(node, { concise: true }).code;
|
|
1523
1645
|
}
|
|
1524
|
-
function bindHole(expr, used, onNested) {
|
|
1646
|
+
function bindHole(expr, used, onNested, emit) {
|
|
1525
1647
|
const hole = {
|
|
1526
|
-
code: genExpr(expr, used, onNested),
|
|
1648
|
+
code: genExpr(expr, used, onNested, emit),
|
|
1527
1649
|
reactive: isReactiveExpr(expr)
|
|
1528
1650
|
};
|
|
1529
1651
|
if (t.isArrowFunctionExpression(expr) && !t.isBlockStatement(expr.body)) {
|
|
1530
1652
|
const body = expr.body;
|
|
1531
1653
|
hole.arrow = {
|
|
1532
1654
|
params: expr.params.map((p) => gen(p)),
|
|
1533
|
-
body: genExpr(body, used, onNested),
|
|
1655
|
+
body: genExpr(body, used, onNested, emit),
|
|
1534
1656
|
bodyReactive: isReactiveExpr(body)
|
|
1535
1657
|
};
|
|
1536
1658
|
}
|
|
@@ -1539,7 +1661,7 @@ function bindHole(expr, used, onNested) {
|
|
|
1539
1661
|
}
|
|
1540
1662
|
function lowerHtmlTemplate(node, used, opts = {}) {
|
|
1541
1663
|
const quasi = node.quasi;
|
|
1542
|
-
const holes = quasi.expressions.map((e) => bindHole(e, used, opts.onNested));
|
|
1664
|
+
const holes = quasi.expressions.map((e) => bindHole(e, used, opts.onNested, opts.emit));
|
|
1543
1665
|
return lowerTemplate(buildPieces(quasi), holes, used, opts);
|
|
1544
1666
|
}
|
|
1545
1667
|
function buildPieces(quasi) {
|
|
@@ -2027,7 +2149,7 @@ var generate2 = _generate2.default ?? _generate2;
|
|
|
2027
2149
|
function gen2(node) {
|
|
2028
2150
|
return generate2(node, { concise: true }).code;
|
|
2029
2151
|
}
|
|
2030
|
-
function transformNestedJSX(node, used, onNested) {
|
|
2152
|
+
function transformNestedJSX(node, used, onNested, emit) {
|
|
2031
2153
|
const clone = t2.cloneNode(node, true);
|
|
2032
2154
|
const file = t2.file(t2.program([t2.expressionStatement(clone)]));
|
|
2033
2155
|
let changed = false;
|
|
@@ -2040,9 +2162,12 @@ function transformNestedJSX(node, used, onNested) {
|
|
|
2040
2162
|
traverse(file, {
|
|
2041
2163
|
"JSXElement|JSXFragment"(path) {
|
|
2042
2164
|
const jsx = path.node;
|
|
2043
|
-
const nestedIR = buildIR(jsx, used, onNested);
|
|
2165
|
+
const nestedIR = buildIR(jsx, used, onNested, emit);
|
|
2044
2166
|
onNested?.(nestedIR);
|
|
2045
|
-
const { code, imports, templates } = imperativeBackend.emit(nestedIR, {
|
|
2167
|
+
const { code, imports, templates } = imperativeBackend.emit(nestedIR, {
|
|
2168
|
+
templateClone: emit?.templateClone,
|
|
2169
|
+
partialTemplates: emit?.partialTemplates ?? true
|
|
2170
|
+
});
|
|
2046
2171
|
emitInto(path, inlineTemplates(code, templates), imports);
|
|
2047
2172
|
},
|
|
2048
2173
|
// Nested html templates compose: ${cond ? html`...` : html`...`},
|
|
@@ -2050,26 +2175,29 @@ function transformNestedJSX(node, used, onNested) {
|
|
|
2050
2175
|
TaggedTemplateExpression(path) {
|
|
2051
2176
|
const tag = path.node.tag;
|
|
2052
2177
|
if (!t2.isIdentifier(tag) || tag.name !== "html" && tag.name !== "svg") return;
|
|
2053
|
-
const ir = buildIRFromHtml(path.node, used, { svg: tag.name === "svg", onNested });
|
|
2178
|
+
const ir = buildIRFromHtml(path.node, used, { svg: tag.name === "svg", onNested, emit });
|
|
2054
2179
|
onNested?.(ir);
|
|
2055
|
-
const { code, imports, templates } = imperativeBackend.emit(ir, {
|
|
2180
|
+
const { code, imports, templates } = imperativeBackend.emit(ir, {
|
|
2181
|
+
templateClone: emit?.templateClone,
|
|
2182
|
+
partialTemplates: emit?.partialTemplates ?? true
|
|
2183
|
+
});
|
|
2056
2184
|
emitInto(path, inlineTemplates(code, templates), imports);
|
|
2057
2185
|
}
|
|
2058
2186
|
});
|
|
2059
2187
|
return changed ? file.program.body[0].expression : node;
|
|
2060
2188
|
}
|
|
2061
|
-
function genExpr(node, used, onNested) {
|
|
2062
|
-
return gen2(transformNestedJSX(node, used, onNested));
|
|
2189
|
+
function genExpr(node, used, onNested, emit) {
|
|
2190
|
+
return gen2(transformNestedJSX(node, used, onNested, emit));
|
|
2063
2191
|
}
|
|
2064
2192
|
function isReactiveExpr(node) {
|
|
2065
2193
|
return isReactiveShape(shapeOf(node));
|
|
2066
2194
|
}
|
|
2067
|
-
function buildIR(node, used = /* @__PURE__ */ new Set(), onNested) {
|
|
2195
|
+
function buildIR(node, used = /* @__PURE__ */ new Set(), onNested, emit) {
|
|
2068
2196
|
const ir = buildJsxIR(node, {
|
|
2069
2197
|
code: (n) => (
|
|
2070
2198
|
// A member-expression tag (`<ns.Foo/>`) is not an Expression, so it cannot go
|
|
2071
2199
|
// through the nested-JSX compile: it only ever needs printing.
|
|
2072
|
-
n.type === "JSXMemberExpression" ? gen2(n) : genExpr(n, used, onNested)
|
|
2200
|
+
n.type === "JSXMemberExpression" ? gen2(n) : genExpr(n, used, onNested, emit)
|
|
2073
2201
|
),
|
|
2074
2202
|
used
|
|
2075
2203
|
});
|
|
@@ -2404,7 +2532,10 @@ function adoptTemplates(code, templates, state) {
|
|
|
2404
2532
|
}
|
|
2405
2533
|
function emitViaIR(node, state) {
|
|
2406
2534
|
const used = /* @__PURE__ */ new Set();
|
|
2407
|
-
const ir = buildIR(node, used, nestedHook(state)
|
|
2535
|
+
const ir = buildIR(node, used, nestedHook(state), {
|
|
2536
|
+
partialTemplates: state.partialTemplates,
|
|
2537
|
+
templateClone: state.templateClone
|
|
2538
|
+
});
|
|
2408
2539
|
resolveComponentSources(ir, {
|
|
2409
2540
|
resolver: state.componentResolver,
|
|
2410
2541
|
modules: {
|
|
@@ -2453,7 +2584,11 @@ function dedupeImports(imports) {
|
|
|
2453
2584
|
function emitHtmlViaIR(node, state) {
|
|
2454
2585
|
const used = /* @__PURE__ */ new Set();
|
|
2455
2586
|
const svg = t3.isIdentifier(node.tag) && node.tag.name === "svg";
|
|
2456
|
-
const ir = buildIRFromHtml(node, used, {
|
|
2587
|
+
const ir = buildIRFromHtml(node, used, {
|
|
2588
|
+
svg,
|
|
2589
|
+
onNested: nestedHook(state),
|
|
2590
|
+
emit: { partialTemplates: state.partialTemplates, templateClone: state.templateClone }
|
|
2591
|
+
});
|
|
2457
2592
|
resolveComponentSources(ir, {
|
|
2458
2593
|
resolver: state.componentResolver,
|
|
2459
2594
|
modules: {
|
|
@@ -2542,7 +2677,7 @@ function createPreset(options = {}) {
|
|
|
2542
2677
|
{
|
|
2543
2678
|
runtime: "automatic",
|
|
2544
2679
|
importSource: "@fluixi/jsx",
|
|
2545
|
-
development:
|
|
2680
|
+
development: process.env.NODE_ENV === "development",
|
|
2546
2681
|
detectReactivity: true,
|
|
2547
2682
|
useHtmlTag: true,
|
|
2548
2683
|
hoistStatics: true,
|