@fluixi/compiler 1.0.0-alpha.92 → 1.0.0-alpha.93
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analyze/intrinsics.cjs +18 -0
- package/dist/analyze/intrinsics.d.ts.map +1 -1
- package/dist/analyze/intrinsics.js +18 -0
- package/dist/analyze/intrinsics.mjs +18 -0
- package/dist/analyze/reactive-bindings.cjs +18 -0
- package/dist/analyze/reactive-bindings.mjs +18 -0
- package/dist/analyze/static-graph.cjs +22 -4
- package/dist/analyze/static-graph.d.ts.map +1 -1
- package/dist/analyze/static-graph.js +6 -4
- package/dist/analyze/static-graph.mjs +22 -4
- package/dist/{babel-GI375YZ2.mjs → babel-RJHJQVEM.mjs} +4 -4
- package/dist/{chunk-QZ4CJPYP.mjs → chunk-56QSOVVL.mjs} +1 -1
- package/dist/{chunk-XJ5H2MXY.mjs → chunk-OD53TMER.mjs} +19 -1
- package/dist/{chunk-DWNWN5RH.mjs → chunk-R7PPFBCL.mjs} +1 -1
- package/dist/{chunk-VOUI6XPV.mjs → chunk-TJIH7TLX.mjs} +1 -1
- package/dist/{chunk-VC6X44FS.mjs → chunk-U7HCFLZ2.mjs} +153 -48
- package/dist/codegen/backends/imperative.cjs +24 -6
- package/dist/codegen/backends/imperative.d.ts.map +1 -1
- package/dist/codegen/backends/imperative.js +19 -4
- package/dist/codegen/backends/imperative.mjs +24 -6
- package/dist/frontend/babel/build-ir-lit.cjs +120 -43
- package/dist/frontend/babel/build-ir-lit.mjs +120 -43
- package/dist/frontend/babel/build-ir.cjs +120 -43
- package/dist/frontend/babel/build-ir.mjs +120 -43
- package/dist/frontend/babel/index.cjs +121 -44
- package/dist/frontend/babel/index.mjs +121 -44
- package/dist/frontend/babel/lower-template.cjs +120 -43
- package/dist/frontend/babel/lower-template.mjs +120 -43
- package/dist/frontend/babel/plugin.cjs +120 -43
- package/dist/frontend/babel/plugin.mjs +120 -43
- package/dist/index.cjs +41 -38
- package/dist/index.mjs +41 -38
- package/dist/integrations.cjs +176 -52
- package/dist/integrations.mjs +8 -8
- package/dist/lower/compile-template.cjs +41 -38
- package/dist/lower/compile-template.mjs +41 -38
- package/dist/lower/jsx.cjs +509 -6
- package/dist/lower/jsx.d.ts.map +1 -1
- package/dist/lower/jsx.js +114 -6
- package/dist/lower/jsx.mjs +509 -6
- package/dist/lower/template.cjs +40 -37
- package/dist/lower/template.d.ts.map +1 -1
- package/dist/lower/template.js +22 -30
- package/dist/lower/template.mjs +40 -37
- package/dist/resolve/builtins.cjs +1 -1
- package/dist/resolve/builtins.mjs +1 -1
- package/dist/resolve/component-globals.cjs +14 -0
- package/dist/resolve/component-globals.mjs +14 -0
- package/dist/resolve/write-globals.cjs +14 -0
- package/dist/resolve/write-globals.mjs +14 -0
- package/dist/{server-fns-BBZRZUPP.mjs → server-fns-ZFRDIQ2X.mjs} +4 -4
- package/dist/{server-functions-VEGLIFCE.mjs → server-functions-CTDXX67X.mjs} +3 -3
- package/dist/transform/index.cjs +170 -47
- package/dist/transform/index.mjs +170 -47
- package/dist/transform/intrinsics.cjs +18 -0
- package/dist/transform/intrinsics.mjs +18 -0
- package/dist/transform/source-locations.cjs +64 -3
- package/dist/transform/source-locations.d.cts +7 -2
- package/dist/transform/source-locations.d.ts +7 -2
- package/dist/transform/source-locations.d.ts.map +1 -1
- package/dist/transform/source-locations.js +68 -3
- package/dist/transform/source-locations.mjs +64 -3
- package/dist/transform/templates.cjs +169 -46
- package/dist/transform/templates.js +2 -2
- package/dist/transform/templates.mjs +169 -46
- package/dist/{transform-UWZ7ZGJ2.mjs → transform-QWWQDCXE.mjs} +7 -7
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +9 -3
|
@@ -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
|
import {
|
|
3
3
|
INTRINSICS,
|
|
4
4
|
RESERVED_COMPONENTS,
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
builtinComponentMap,
|
|
8
8
|
isIntrinsicName,
|
|
9
9
|
isPrimitiveModule
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-OD53TMER.mjs";
|
|
11
11
|
|
|
12
12
|
// src/analyze/expr-shape.ts
|
|
13
13
|
var CALL = /* @__PURE__ */ new Set(["CallExpression", "OptionalCallExpression"]);
|
|
@@ -293,6 +293,7 @@ function resolveReactiveBindings(program) {
|
|
|
293
293
|
// src/transform/source-locations.ts
|
|
294
294
|
var SOURCE_MARKER = "__fx_source";
|
|
295
295
|
var COMPONENT_MARKER = "__fx_component";
|
|
296
|
+
var CLASS_MARKER = "__fx_class";
|
|
296
297
|
var HOLE_MARKER = "__fx_hole";
|
|
297
298
|
var markHole = (accessor, line, column, name) => `(globalThis.${HOLE_MARKER} ?? ((f) => f))(${accessor},${line},${column}${name === void 0 ? "" : `,${JSON.stringify(name)}`})`;
|
|
298
299
|
var PROPS_MARKER = "__fx_props";
|
|
@@ -310,11 +311,12 @@ var RUNTIME_PRIMITIVES2 = /* @__PURE__ */ new Set([
|
|
|
310
311
|
"createStore",
|
|
311
312
|
"createResource"
|
|
312
313
|
]);
|
|
313
|
-
function
|
|
314
|
+
function mayHaveSourceMarks(code) {
|
|
314
315
|
for (const name of RUNTIME_PRIMITIVES2) {
|
|
315
316
|
if (code.includes(name)) return true;
|
|
316
317
|
}
|
|
317
|
-
|
|
318
|
+
if (code.includes("$signal") || code.includes("$memo") || code.includes("$effect") || code.includes("$store")) return true;
|
|
319
|
+
return /\bclass\b[^{;]*\bextends\b/.test(code);
|
|
318
320
|
}
|
|
319
321
|
var SINGLE_STATEMENT_PARENTS = /* @__PURE__ */ new Set([
|
|
320
322
|
"IfStatement",
|
|
@@ -333,6 +335,7 @@ function isLoneBranch(statement, parent) {
|
|
|
333
335
|
function isExport(node) {
|
|
334
336
|
return node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration";
|
|
335
337
|
}
|
|
338
|
+
var CLASS_FIELDS = /* @__PURE__ */ new Set(["PropertyDefinition", "ClassProperty", "ClassPrivateProperty", "AccessorProperty"]);
|
|
336
339
|
function isStatement(node) {
|
|
337
340
|
return node.type.endsWith("Statement") || node.type === "VariableDeclaration";
|
|
338
341
|
}
|
|
@@ -353,6 +356,14 @@ function walk3(node, visit, stack = []) {
|
|
|
353
356
|
}
|
|
354
357
|
function nameFor(stack) {
|
|
355
358
|
const declarator = stack[stack.length - 1];
|
|
359
|
+
if (declarator && CLASS_FIELDS.has(declarator.type)) {
|
|
360
|
+
const key = declarator.key;
|
|
361
|
+
if (key?.type === "Identifier" || key?.type === "PrivateIdentifier" || key?.type === "PrivateName") {
|
|
362
|
+
const name = key.name ?? key.id?.name;
|
|
363
|
+
return name;
|
|
364
|
+
}
|
|
365
|
+
return void 0;
|
|
366
|
+
}
|
|
356
367
|
if (declarator?.type !== "VariableDeclarator") return void 0;
|
|
357
368
|
const id = declarator.id;
|
|
358
369
|
if (id?.type === "Identifier") return id.name;
|
|
@@ -401,6 +412,17 @@ function collectSourceEdits(program, filename) {
|
|
|
401
412
|
});
|
|
402
413
|
}
|
|
403
414
|
}
|
|
415
|
+
if ((node.type === "ClassDeclaration" || node.type === "ClassExpression") && node.superClass && !node.declare) {
|
|
416
|
+
const at = node.loc?.start;
|
|
417
|
+
const body = node.body;
|
|
418
|
+
if (at && body) {
|
|
419
|
+
edits.push({
|
|
420
|
+
start: body.start + 1,
|
|
421
|
+
end: body.start + 1,
|
|
422
|
+
code: `static{globalThis.${CLASS_MARKER}?.(this,${JSON.stringify(filename)},${at.line},${at.column});}`
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
}
|
|
404
426
|
if (node.type !== "CallExpression") return;
|
|
405
427
|
const callee = node.callee;
|
|
406
428
|
if (callee?.type !== "Identifier") return;
|
|
@@ -410,6 +432,26 @@ function collectSourceEdits(program, filename) {
|
|
|
410
432
|
if (!intrinsic && !imported) return;
|
|
411
433
|
const position = node.loc?.start;
|
|
412
434
|
if (!position) return;
|
|
435
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
436
|
+
const up = stack[i];
|
|
437
|
+
if (isStatement(up)) break;
|
|
438
|
+
if (CLASS_FIELDS.has(up.type)) {
|
|
439
|
+
if (claimed.has(node.start)) return;
|
|
440
|
+
claimed.add(node.start);
|
|
441
|
+
const label2 = nameFor(stack);
|
|
442
|
+
const own = !up.static;
|
|
443
|
+
const args2 = [
|
|
444
|
+
JSON.stringify(filename),
|
|
445
|
+
String(position.line),
|
|
446
|
+
String(position.column),
|
|
447
|
+
label2 ? JSON.stringify(label2) : own ? "undefined" : void 0,
|
|
448
|
+
own ? "this" : void 0
|
|
449
|
+
].filter((a) => a !== void 0).join(",");
|
|
450
|
+
edits.push({ start: node.start, end: node.start, code: `(globalThis.${SOURCE_MARKER}?.(${args2}),` });
|
|
451
|
+
edits.push({ start: node.end, end: node.end, code: ")" });
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
413
455
|
let statement;
|
|
414
456
|
let parent;
|
|
415
457
|
let index = -1;
|
|
@@ -819,13 +861,16 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
819
861
|
used.add("createComponent");
|
|
820
862
|
const spreads = props.filter((p) => p.kind === "spread");
|
|
821
863
|
const normal = props.filter((p) => p.kind !== "spread");
|
|
822
|
-
const
|
|
823
|
-
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
824
|
-
);
|
|
864
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
825
865
|
if (children.length > 0) {
|
|
826
|
-
|
|
866
|
+
bodies.push(["children", emitChildren(children, used, templates)]);
|
|
867
|
+
}
|
|
868
|
+
let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
|
|
869
|
+
if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
|
|
870
|
+
const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
|
|
871
|
+
const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
|
|
872
|
+
obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
|
|
827
873
|
}
|
|
828
|
-
let obj = `{ ${entries.join(", ")} }`;
|
|
829
874
|
if (spreads.length > 0) {
|
|
830
875
|
used.add("mergeProps");
|
|
831
876
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
@@ -907,7 +952,8 @@ function emitNode(node, used, templates) {
|
|
|
907
952
|
} else {
|
|
908
953
|
used.add("spread");
|
|
909
954
|
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
910
|
-
const
|
|
955
|
+
const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
|
|
956
|
+
const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
|
|
911
957
|
parts.push(
|
|
912
958
|
`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
|
|
913
959
|
);
|
|
@@ -1081,6 +1127,22 @@ var DELEGATED = /* @__PURE__ */ new Set([
|
|
|
1081
1127
|
"mouseup"
|
|
1082
1128
|
]);
|
|
1083
1129
|
var isReactive = (node) => isReactiveShape(shapeOf(node));
|
|
1130
|
+
var CONTAINER = "container";
|
|
1131
|
+
function directive(node, name) {
|
|
1132
|
+
if (node.type !== "JSXElement") return void 0;
|
|
1133
|
+
const opening = node.openingElement;
|
|
1134
|
+
if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
|
|
1135
|
+
return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
|
|
1136
|
+
}
|
|
1137
|
+
function directiveExpr(attr) {
|
|
1138
|
+
const v = attr.value;
|
|
1139
|
+
return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
|
|
1140
|
+
}
|
|
1141
|
+
function emitIR(ir, ctx) {
|
|
1142
|
+
const { code, imports, templates } = imperativeBackend.emit(ir, {});
|
|
1143
|
+
for (const s of imports) ctx.used.add(s);
|
|
1144
|
+
return inlineTemplates(code, templates);
|
|
1145
|
+
}
|
|
1084
1146
|
var posOf = (node, ctx) => {
|
|
1085
1147
|
if (!ctx.sourceFile) return void 0;
|
|
1086
1148
|
const at = node.loc?.start;
|
|
@@ -1189,7 +1251,16 @@ function buildProps(attrs, ctx) {
|
|
|
1189
1251
|
}
|
|
1190
1252
|
function buildChildren(children, ctx) {
|
|
1191
1253
|
const out = [];
|
|
1192
|
-
for (
|
|
1254
|
+
for (let i = 0; i < children.length; i++) {
|
|
1255
|
+
const child = children[i];
|
|
1256
|
+
if (directive(child, "if")) {
|
|
1257
|
+
let next = i + 1;
|
|
1258
|
+
while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
|
|
1259
|
+
const fallback = directive(children[next], "else") ? children[next] : null;
|
|
1260
|
+
out.push(buildIf(child, fallback, ctx));
|
|
1261
|
+
if (fallback) i = next;
|
|
1262
|
+
continue;
|
|
1263
|
+
}
|
|
1193
1264
|
if (child.type === "JSXText") {
|
|
1194
1265
|
const text = cleanTemplateText(child.value);
|
|
1195
1266
|
if (text) out.push({ kind: "text", value: text });
|
|
@@ -1226,19 +1297,68 @@ function readLoad(attrs) {
|
|
|
1226
1297
|
}
|
|
1227
1298
|
return void 0;
|
|
1228
1299
|
}
|
|
1300
|
+
function buildIf(node, fallback, ctx) {
|
|
1301
|
+
const cond = directiveExpr(directive(node, "if"));
|
|
1302
|
+
const props = [
|
|
1303
|
+
cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
|
|
1304
|
+
];
|
|
1305
|
+
if (fallback) {
|
|
1306
|
+
props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
|
|
1307
|
+
}
|
|
1308
|
+
return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
|
|
1309
|
+
}
|
|
1310
|
+
function buildEach(node, ctx) {
|
|
1311
|
+
const attr = directive(node, "each");
|
|
1312
|
+
if (!attr) return null;
|
|
1313
|
+
const list = directiveExpr(attr);
|
|
1314
|
+
const props = [
|
|
1315
|
+
list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
|
|
1316
|
+
];
|
|
1317
|
+
const key = directive(node, "key");
|
|
1318
|
+
if (key) {
|
|
1319
|
+
const keyExpr = directiveExpr(key);
|
|
1320
|
+
const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
|
|
1321
|
+
if (by) props.push({ name: "by", kind: "attr", expr: by });
|
|
1322
|
+
}
|
|
1323
|
+
const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
|
|
1324
|
+
const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
|
|
1325
|
+
let render;
|
|
1326
|
+
if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
|
|
1327
|
+
const fn = ctx.code(only);
|
|
1328
|
+
const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
|
|
1329
|
+
render = live ? `(...args) => () => (${fn})(...args)` : fn;
|
|
1330
|
+
} else {
|
|
1331
|
+
const built = buildChildren(node.children, ctx);
|
|
1332
|
+
const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
|
|
1333
|
+
render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
|
|
1334
|
+
}
|
|
1335
|
+
return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
|
|
1336
|
+
}
|
|
1337
|
+
function buildElement(node, ctx, handled) {
|
|
1338
|
+
const skip = new Set(handled);
|
|
1339
|
+
const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
|
|
1340
|
+
const attributes = node.openingElement.attributes.filter(
|
|
1341
|
+
(a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
|
|
1342
|
+
);
|
|
1343
|
+
const { tag } = tagInfo(node.openingElement.name, ctx);
|
|
1344
|
+
const each = buildEach(node, ctx);
|
|
1345
|
+
const children = each ? [each] : buildChildren(node.children, ctx);
|
|
1346
|
+
if (tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
1347
|
+
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1348
|
+
return { kind: "element", tag, svg: SVG_TAGS.has(tag), props: buildProps(attributes, ctx), children, static: false, ...at ? { at } : {} };
|
|
1349
|
+
}
|
|
1229
1350
|
function buildNode(node, ctx) {
|
|
1230
1351
|
if (node.type === "JSXFragment") {
|
|
1231
1352
|
return { kind: "fragment", children: buildChildren(node.children, ctx) };
|
|
1232
1353
|
}
|
|
1354
|
+
if (directive(node, "if")) return buildIf(node, null, ctx);
|
|
1233
1355
|
const { tag, component } = tagInfo(node.openingElement.name, ctx);
|
|
1356
|
+
if (!component) return buildElement(node, ctx, []);
|
|
1234
1357
|
const props = buildProps(node.openingElement.attributes, ctx);
|
|
1235
1358
|
const children = buildChildren(node.children, ctx);
|
|
1236
1359
|
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1240
|
-
}
|
|
1241
|
-
return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
|
|
1360
|
+
const load = readLoad(node.openingElement.attributes);
|
|
1361
|
+
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
1242
1362
|
}
|
|
1243
1363
|
function buildJsxIR(node, ctx) {
|
|
1244
1364
|
const ir = buildNode(node, ctx);
|
|
@@ -1267,6 +1387,7 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
|
|
|
1267
1387
|
function escapeTemplate(s) {
|
|
1268
1388
|
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
1269
1389
|
}
|
|
1390
|
+
var CONTAINER2 = "container";
|
|
1270
1391
|
function capitalize(s) {
|
|
1271
1392
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
1272
1393
|
}
|
|
@@ -1321,10 +1442,6 @@ var Lowerer = class {
|
|
|
1321
1442
|
if (hasElse) i = elseIdx;
|
|
1322
1443
|
continue;
|
|
1323
1444
|
}
|
|
1324
|
-
if (child.attributes.some((a) => a.kind === "EachDirective")) {
|
|
1325
|
-
out.push(this.lowerEach(child));
|
|
1326
|
-
continue;
|
|
1327
|
-
}
|
|
1328
1445
|
}
|
|
1329
1446
|
const node = this.lowerNode(child);
|
|
1330
1447
|
if (node) out.push(node);
|
|
@@ -1366,13 +1483,16 @@ var Lowerer = class {
|
|
|
1366
1483
|
node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
|
|
1367
1484
|
return node;
|
|
1368
1485
|
}
|
|
1486
|
+
const each = this.lowerEach(el);
|
|
1487
|
+
const children = each ? [each] : this.lowerChildren(el.children);
|
|
1488
|
+
if (el.tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
1369
1489
|
const at = this.tagAt(el);
|
|
1370
1490
|
return {
|
|
1371
1491
|
kind: "element",
|
|
1372
1492
|
tag: el.tag,
|
|
1373
1493
|
svg: el.namespace === "svg",
|
|
1374
1494
|
props: this.lowerAttributes(el.attributes),
|
|
1375
|
-
children
|
|
1495
|
+
children,
|
|
1376
1496
|
static: false,
|
|
1377
1497
|
...at ? { at } : {}
|
|
1378
1498
|
};
|
|
@@ -1444,10 +1564,14 @@ var Lowerer = class {
|
|
|
1444
1564
|
const at = this.tagAt(el);
|
|
1445
1565
|
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
1446
1566
|
}
|
|
1447
|
-
/**
|
|
1567
|
+
/**
|
|
1568
|
+
* `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
|
|
1569
|
+
* content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
|
|
1570
|
+
* it is. Null when the element has no `each`.
|
|
1571
|
+
*/
|
|
1448
1572
|
lowerEach(el) {
|
|
1449
1573
|
const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
|
|
1450
|
-
if (!eachDir || eachDir.kind !== "EachDirective") return
|
|
1574
|
+
if (!eachDir || eachDir.kind !== "EachDirective") return null;
|
|
1451
1575
|
const each = this.hole(eachDir.hole);
|
|
1452
1576
|
const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
|
|
1453
1577
|
if (eachDir.key) {
|
|
@@ -1457,12 +1581,12 @@ var Lowerer = class {
|
|
|
1457
1581
|
const arrow = this.itemArrow(el);
|
|
1458
1582
|
let childrenCode;
|
|
1459
1583
|
if (arrow) {
|
|
1460
|
-
const
|
|
1461
|
-
|
|
1462
|
-
childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
|
|
1584
|
+
const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
|
|
1585
|
+
childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
|
|
1463
1586
|
} else {
|
|
1464
|
-
const
|
|
1465
|
-
|
|
1587
|
+
const content = this.lowerChildren(el.children);
|
|
1588
|
+
const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
|
|
1589
|
+
childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
|
|
1466
1590
|
}
|
|
1467
1591
|
const children = [{ kind: "expr", code: childrenCode, reactive: false }];
|
|
1468
1592
|
const at = this.tagAt(el);
|
|
@@ -1479,25 +1603,6 @@ var Lowerer = class {
|
|
|
1479
1603
|
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
1480
1604
|
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
1481
1605
|
}
|
|
1482
|
-
/** Same element (minus each/key) but with explicit IR children. */
|
|
1483
|
-
rebuildWithChildren(el, children) {
|
|
1484
|
-
const props = this.lowerAttributes(
|
|
1485
|
-
el.attributes.filter((a) => a.kind !== "EachDirective")
|
|
1486
|
-
);
|
|
1487
|
-
if (el.kind === "Element") {
|
|
1488
|
-
const at = this.tagAt(el);
|
|
1489
|
-
return {
|
|
1490
|
-
kind: "element",
|
|
1491
|
-
tag: el.tag,
|
|
1492
|
-
svg: el.namespace === "svg",
|
|
1493
|
-
props,
|
|
1494
|
-
children,
|
|
1495
|
-
static: false,
|
|
1496
|
-
...at ? { at } : {}
|
|
1497
|
-
};
|
|
1498
|
-
}
|
|
1499
|
-
return { kind: "component", name: el.tag, props, children };
|
|
1500
|
-
}
|
|
1501
1606
|
// --- attributes ---------------------------------------------------------
|
|
1502
1607
|
lowerAttributes(attrs) {
|
|
1503
1608
|
const props = [];
|
|
@@ -1709,7 +1814,7 @@ export {
|
|
|
1709
1814
|
collapseStrategies,
|
|
1710
1815
|
declaredNames,
|
|
1711
1816
|
resolveReactiveBindings,
|
|
1712
|
-
|
|
1817
|
+
mayHaveSourceMarks,
|
|
1713
1818
|
collectSourceEdits,
|
|
1714
1819
|
inlineTemplates,
|
|
1715
1820
|
imperativeBackend,
|
|
@@ -237,6 +237,7 @@ var INTRINSICS = {
|
|
|
237
237
|
$effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
|
|
238
238
|
$store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
|
|
239
239
|
$resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
|
|
240
|
+
$cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
|
|
240
241
|
// No short runtime form for these yet, so the intrinsic stands for the `create*`
|
|
241
242
|
// name. Still worth having: no import to write, and when a shorter form is
|
|
242
243
|
// designed, the change is this line rather than a compiler pass.
|
|
@@ -247,6 +248,19 @@ var INTRINSICS = {
|
|
|
247
248
|
// dependency analysis must not guess at.
|
|
248
249
|
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
249
250
|
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
|
|
251
|
+
// Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
|
|
252
|
+
// no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
|
|
253
|
+
$batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
|
|
254
|
+
$cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
|
|
255
|
+
$root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
|
|
256
|
+
$context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
|
|
257
|
+
$renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
|
|
258
|
+
$computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
|
|
259
|
+
// Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
|
|
260
|
+
// handle as far as reads go: the same accessor with `set` on it.
|
|
261
|
+
$storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
262
|
+
$asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
263
|
+
$persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
|
|
250
264
|
// An environment variable, public or server-only; the browser build refuses a secret.
|
|
251
265
|
$env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
|
|
252
266
|
};
|
|
@@ -424,13 +438,16 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
424
438
|
used.add("createComponent");
|
|
425
439
|
const spreads = props.filter((p) => p.kind === "spread");
|
|
426
440
|
const normal = props.filter((p) => p.kind !== "spread");
|
|
427
|
-
const
|
|
428
|
-
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
429
|
-
);
|
|
441
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
430
442
|
if (children.length > 0) {
|
|
431
|
-
|
|
443
|
+
bodies.push(["children", emitChildren(children, used, templates)]);
|
|
444
|
+
}
|
|
445
|
+
let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
|
|
446
|
+
if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
|
|
447
|
+
const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
|
|
448
|
+
const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
|
|
449
|
+
obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
|
|
432
450
|
}
|
|
433
|
-
let obj = `{ ${entries.join(", ")} }`;
|
|
434
451
|
if (spreads.length > 0) {
|
|
435
452
|
used.add("mergeProps");
|
|
436
453
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
@@ -512,7 +529,8 @@ function emitNode(node, used, templates) {
|
|
|
512
529
|
} else {
|
|
513
530
|
used.add("spread");
|
|
514
531
|
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
515
|
-
const
|
|
532
|
+
const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
|
|
533
|
+
const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
|
|
516
534
|
parts.push(
|
|
517
535
|
`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
|
|
518
536
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imperative.d.ts","sourceRoot":"","sources":["../../../src/codegen/backends/imperative.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAiB,cAAc,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"imperative.d.ts","sourceRoot":"","sources":["../../../src/codegen/backends/imperative.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAiB,cAAc,EAAE,MAAM,eAAe,CAAC;AAkVnF;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,GAAG,SAAS,GAAG,MAAM,CAK7F;AAED,eAAO,MAAM,iBAAiB,EAAE,cAgB/B,CAAC"}
|
|
@@ -101,15 +101,25 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
101
101
|
// on every change (a <Portal> then piles up). As getters the component is built ONCE and
|
|
102
102
|
// reactivity flows through the getter into its own effects. Static props as getters are
|
|
103
103
|
// harmless (they return the same value). Matches lazyProps() in the inline codegen.
|
|
104
|
-
|
|
104
|
+
// Each body emitted once: emitting children registers their templates.
|
|
105
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
105
106
|
if (children.length > 0) {
|
|
106
107
|
// Children are a LAZY getter: evaluated inside the component's execution, so
|
|
107
108
|
// child components are created in the component's owner scope (preserves owner
|
|
108
109
|
// nesting for context/Suspense), after the component runs (correct provider
|
|
109
110
|
// ordering), and lazily (ErrorBoundary can still catch a throwing child).
|
|
110
|
-
|
|
111
|
+
bodies.push(['children', emitChildren(children, used, templates)]);
|
|
112
|
+
}
|
|
113
|
+
let obj = `{ ${bodies.map(([name, body]) => `get ${propKey(name)}() { return ${body}; }`).join(', ')} }`;
|
|
114
|
+
// A getter is a method: `this` inside it is the props object. Written in a class method
|
|
115
|
+
// (a custom element's render), `title={() => this.name}` then read the props, not the
|
|
116
|
+
// element. Arrows keep the enclosing `this`, so those props go out as arrow thunks and the
|
|
117
|
+
// getters call them. Only when some prop mentions `this`; everything else is unchanged.
|
|
118
|
+
if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
|
|
119
|
+
const thunks = bodies.map(([name, body]) => `${JSON.stringify(name)}: () => ${body}`);
|
|
120
|
+
const getters = bodies.map(([name]) => `get ${propKey(name)}() { return _$t[${JSON.stringify(name)}](); }`);
|
|
121
|
+
obj = `((_$t) => ({ ${getters.join(', ')} }))({ ${thunks.join(', ')} })`;
|
|
111
122
|
}
|
|
112
|
-
let obj = `{ ${entries.join(', ')} }`;
|
|
113
123
|
if (spreads.length > 0) {
|
|
114
124
|
used.add('mergeProps');
|
|
115
125
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(', ')}, ${obj})`;
|
|
@@ -268,7 +278,12 @@ function emitNode(node, used, templates) {
|
|
|
268
278
|
// here, so the runtime does not have to read it back off the node. It falls
|
|
269
279
|
// back to that check when the flag is absent, which is how a hand-written
|
|
270
280
|
// `spread` call still behaves.
|
|
271
|
-
|
|
281
|
+
// For a custom element it also names the props wrapped as accessors: any other
|
|
282
|
+
// function is a property value (an action, a callback) and must not be called.
|
|
283
|
+
const accessors = node.props
|
|
284
|
+
.filter((p) => p.kind !== 'spread' && p.expr !== undefined && p.reactive && p.name !== 'ref')
|
|
285
|
+
.map((p) => p.name);
|
|
286
|
+
const customFlag = node.tag.includes('-') ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : '';
|
|
272
287
|
parts.push(`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`);
|
|
273
288
|
}
|
|
274
289
|
}
|
|
@@ -210,6 +210,7 @@ var INTRINSICS = {
|
|
|
210
210
|
$effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
|
|
211
211
|
$store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
|
|
212
212
|
$resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
|
|
213
|
+
$cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
|
|
213
214
|
// No short runtime form for these yet, so the intrinsic stands for the `create*`
|
|
214
215
|
// name. Still worth having: no import to write, and when a shorter form is
|
|
215
216
|
// designed, the change is this line rather than a compiler pass.
|
|
@@ -220,6 +221,19 @@ var INTRINSICS = {
|
|
|
220
221
|
// dependency analysis must not guess at.
|
|
221
222
|
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
222
223
|
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
|
|
224
|
+
// Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
|
|
225
|
+
// no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
|
|
226
|
+
$batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
|
|
227
|
+
$cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
|
|
228
|
+
$root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
|
|
229
|
+
$context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
|
|
230
|
+
$renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
|
|
231
|
+
$computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
|
|
232
|
+
// Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
|
|
233
|
+
// handle as far as reads go: the same accessor with `set` on it.
|
|
234
|
+
$storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
235
|
+
$asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
236
|
+
$persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
|
|
223
237
|
// An environment variable, public or server-only; the browser build refuses a secret.
|
|
224
238
|
$env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
|
|
225
239
|
};
|
|
@@ -397,13 +411,16 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
397
411
|
used.add("createComponent");
|
|
398
412
|
const spreads = props.filter((p) => p.kind === "spread");
|
|
399
413
|
const normal = props.filter((p) => p.kind !== "spread");
|
|
400
|
-
const
|
|
401
|
-
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
402
|
-
);
|
|
414
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
403
415
|
if (children.length > 0) {
|
|
404
|
-
|
|
416
|
+
bodies.push(["children", emitChildren(children, used, templates)]);
|
|
417
|
+
}
|
|
418
|
+
let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
|
|
419
|
+
if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
|
|
420
|
+
const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
|
|
421
|
+
const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
|
|
422
|
+
obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
|
|
405
423
|
}
|
|
406
|
-
let obj = `{ ${entries.join(", ")} }`;
|
|
407
424
|
if (spreads.length > 0) {
|
|
408
425
|
used.add("mergeProps");
|
|
409
426
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
@@ -485,7 +502,8 @@ function emitNode(node, used, templates) {
|
|
|
485
502
|
} else {
|
|
486
503
|
used.add("spread");
|
|
487
504
|
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
488
|
-
const
|
|
505
|
+
const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
|
|
506
|
+
const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
|
|
489
507
|
parts.push(
|
|
490
508
|
`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
|
|
491
509
|
);
|