@fluixi/compiler 1.0.0-alpha.92 → 1.0.0-alpha.94

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.
Files changed (102) hide show
  1. package/dist/analyze/intrinsics.cjs +31 -1
  2. package/dist/analyze/intrinsics.d.cts +14 -0
  3. package/dist/analyze/intrinsics.d.ts +14 -0
  4. package/dist/analyze/intrinsics.d.ts.map +1 -1
  5. package/dist/analyze/intrinsics.js +35 -0
  6. package/dist/analyze/intrinsics.mjs +31 -1
  7. package/dist/analyze/reactive-bindings.cjs +18 -0
  8. package/dist/analyze/reactive-bindings.mjs +18 -0
  9. package/dist/analyze/static-graph.cjs +22 -4
  10. package/dist/analyze/static-graph.d.ts.map +1 -1
  11. package/dist/analyze/static-graph.js +6 -4
  12. package/dist/analyze/static-graph.mjs +22 -4
  13. package/dist/{babel-GI375YZ2.mjs → babel-RJFQVFJW.mjs} +4 -4
  14. package/dist/{chunk-QZ4CJPYP.mjs → chunk-DP2QDWWH.mjs} +1 -1
  15. package/dist/{chunk-DWNWN5RH.mjs → chunk-E2RWTIYE.mjs} +21 -15
  16. package/dist/{chunk-XJ5H2MXY.mjs → chunk-HCCBEJEZ.mjs} +31 -1
  17. package/dist/{chunk-VOUI6XPV.mjs → chunk-HIZU7WI2.mjs} +1 -1
  18. package/dist/{chunk-VC6X44FS.mjs → chunk-ZR2QHTEX.mjs} +505 -68
  19. package/dist/codegen/backends/imperative.cjs +24 -6
  20. package/dist/codegen/backends/imperative.d.ts.map +1 -1
  21. package/dist/codegen/backends/imperative.js +20 -4
  22. package/dist/codegen/backends/imperative.mjs +24 -6
  23. package/dist/frontend/babel/build-ir-lit.cjs +470 -63
  24. package/dist/frontend/babel/build-ir-lit.mjs +470 -63
  25. package/dist/frontend/babel/build-ir.cjs +470 -63
  26. package/dist/frontend/babel/build-ir.mjs +470 -63
  27. package/dist/frontend/babel/index.cjs +471 -64
  28. package/dist/frontend/babel/index.mjs +471 -64
  29. package/dist/frontend/babel/lower-template.cjs +470 -63
  30. package/dist/frontend/babel/lower-template.mjs +470 -63
  31. package/dist/frontend/babel/plugin.cjs +470 -63
  32. package/dist/frontend/babel/plugin.mjs +470 -63
  33. package/dist/index.cjs +360 -58
  34. package/dist/index.mjs +372 -58
  35. package/dist/integrations.cjs +1347 -98
  36. package/dist/integrations.d.ts.map +1 -1
  37. package/dist/integrations.js +44 -2
  38. package/dist/integrations.mjs +205 -19
  39. package/dist/lower/compile-template.cjs +354 -57
  40. package/dist/lower/compile-template.d.cts +13 -0
  41. package/dist/lower/compile-template.d.ts +13 -0
  42. package/dist/lower/compile-template.d.ts.map +1 -1
  43. package/dist/lower/compile-template.js +3 -0
  44. package/dist/lower/compile-template.mjs +366 -57
  45. package/dist/lower/entities.cjs +288 -0
  46. package/dist/lower/entities.d.cts +1 -0
  47. package/dist/lower/entities.d.ts +2 -0
  48. package/dist/lower/entities.d.ts.map +1 -0
  49. package/dist/lower/entities.js +54 -0
  50. package/dist/lower/entities.mjs +267 -0
  51. package/dist/lower/jsx.cjs +567 -7
  52. package/dist/lower/jsx.d.cts +8 -0
  53. package/dist/lower/jsx.d.ts +8 -0
  54. package/dist/lower/jsx.d.ts.map +1 -1
  55. package/dist/lower/jsx.js +205 -7
  56. package/dist/lower/jsx.mjs +567 -7
  57. package/dist/lower/template.cjs +332 -56
  58. package/dist/lower/template.d.ts.map +1 -1
  59. package/dist/lower/template.js +57 -49
  60. package/dist/lower/template.mjs +332 -56
  61. package/dist/resolve/builtins.cjs +1 -1
  62. package/dist/resolve/builtins.mjs +1 -1
  63. package/dist/resolve/component-globals.cjs +20 -1
  64. package/dist/resolve/component-globals.js +2 -2
  65. package/dist/resolve/component-globals.mjs +20 -1
  66. package/dist/resolve/write-globals.cjs +20 -1
  67. package/dist/resolve/write-globals.mjs +20 -1
  68. package/dist/{server-fns-BBZRZUPP.mjs → server-fns-JLSTKWPS.mjs} +4 -4
  69. package/dist/{server-functions-VEGLIFCE.mjs → server-functions-BGY3RD4R.mjs} +3 -3
  70. package/dist/transform/index.cjs +1123 -77
  71. package/dist/transform/index.mjs +1141 -91
  72. package/dist/transform/intrinsics.cjs +18 -0
  73. package/dist/transform/intrinsics.mjs +18 -0
  74. package/dist/transform/props.mjs +19 -14
  75. package/dist/transform/server-fns.mjs +19 -14
  76. package/dist/transform/source-locations.cjs +64 -3
  77. package/dist/transform/source-locations.d.cts +7 -2
  78. package/dist/transform/source-locations.d.ts +7 -2
  79. package/dist/transform/source-locations.d.ts.map +1 -1
  80. package/dist/transform/source-locations.js +68 -3
  81. package/dist/transform/source-locations.mjs +64 -3
  82. package/dist/transform/styles.cjs +588 -0
  83. package/dist/transform/styles.d.cts +211 -0
  84. package/dist/transform/styles.d.ts +212 -0
  85. package/dist/transform/styles.d.ts.map +1 -0
  86. package/dist/transform/styles.js +714 -0
  87. package/dist/transform/styles.mjs +653 -0
  88. package/dist/transform/templates.cjs +1122 -76
  89. package/dist/transform/templates.d.cts +13 -0
  90. package/dist/transform/templates.d.ts +13 -0
  91. package/dist/transform/templates.d.ts.map +1 -1
  92. package/dist/transform/templates.js +51 -14
  93. package/dist/transform/templates.mjs +1140 -90
  94. package/dist/{transform-UWZ7ZGJ2.mjs → transform-GBP6Y4UI.mjs} +603 -19
  95. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  96. package/dist/vite-styles.cjs +180 -0
  97. package/dist/vite-styles.d.cts +59 -0
  98. package/dist/vite-styles.d.ts +60 -0
  99. package/dist/vite-styles.d.ts.map +1 -0
  100. package/dist/vite-styles.js +198 -0
  101. package/dist/vite-styles.mjs +159 -0
  102. package/package.json +10 -3
@@ -1,9 +1,9 @@
1
- /*! @fluixi/compiler v1.0.0-alpha.92 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/compiler v1.0.0-alpha.94 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
 
3
3
  // src/transform/templates.ts
4
4
  import { parse as parse2 } from "@babel/parser";
5
5
 
6
- // ../../node_modules/.pnpm/@jridgewell+sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs
6
+ // ../../node_modules/.pnpm/@jridgewell+sourcemap-codec@1.6.0/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs
7
7
  var comma = ",".charCodeAt(0);
8
8
  var semicolon = ";".charCodeAt(0);
9
9
  var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
@@ -14,16 +14,16 @@ for (let i = 0; i < chars.length; i++) {
14
14
  intToChar[i] = c;
15
15
  charToInt[c] = i;
16
16
  }
17
- function encodeInteger(builder, num, relative) {
18
- let delta = num - relative;
19
- delta = delta < 0 ? -delta << 1 | 1 : delta << 1;
17
+ function encodeInteger(builder, num) {
20
18
  do {
21
- let clamped = delta & 31;
22
- delta >>>= 5;
23
- if (delta > 0) clamped |= 32;
19
+ let clamped = num & 31;
20
+ num >>>= 5;
21
+ if (num > 0) clamped |= 32;
24
22
  builder.write(intToChar[clamped]);
25
- } while (delta > 0);
26
- return num;
23
+ } while (num > 0);
24
+ }
25
+ function encodeSign(num) {
26
+ return num < 0 ? -num << 1 | 1 : num << 1;
27
27
  }
28
28
  var bufLength = 1024 * 16;
29
29
  var td = typeof TextDecoder !== "undefined" ? /* @__PURE__ */ new TextDecoder() : typeof Buffer !== "undefined" ? {
@@ -73,13 +73,18 @@ function encode(decoded) {
73
73
  for (let j = 0; j < line.length; j++) {
74
74
  const segment = line[j];
75
75
  if (j > 0) writer.write(comma);
76
- genColumn = encodeInteger(writer, segment[0], genColumn);
76
+ encodeInteger(writer, encodeSign(segment[0] - genColumn));
77
+ genColumn = segment[0];
77
78
  if (segment.length === 1) continue;
78
- sourcesIndex = encodeInteger(writer, segment[1], sourcesIndex);
79
- sourceLine = encodeInteger(writer, segment[2], sourceLine);
80
- sourceColumn = encodeInteger(writer, segment[3], sourceColumn);
79
+ encodeInteger(writer, encodeSign(segment[1] - sourcesIndex));
80
+ encodeInteger(writer, encodeSign(segment[2] - sourceLine));
81
+ encodeInteger(writer, encodeSign(segment[3] - sourceColumn));
82
+ sourcesIndex = segment[1];
83
+ sourceLine = segment[2];
84
+ sourceColumn = segment[3];
81
85
  if (segment.length === 4) continue;
82
- namesIndex = encodeInteger(writer, segment[4], namesIndex);
86
+ encodeInteger(writer, encodeSign(segment[4] - namesIndex));
87
+ namesIndex = segment[4];
83
88
  }
84
89
  }
85
90
  return writer.flush();
@@ -1076,6 +1081,562 @@ var MagicString = class _MagicString {
1076
1081
  }
1077
1082
  };
1078
1083
 
1084
+ // src/transform/styles.ts
1085
+ var SCOPE_ATTR = "data-fx-s";
1086
+ var FUNCTIONS = /* @__PURE__ */ new Set(["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression", "ObjectMethod", "ClassMethod"]);
1087
+ function collectStyles(program, sourceName, options, tags = { html: /* @__PURE__ */ new Map([["html", "html"]]) }, module) {
1088
+ const plan = {
1089
+ scopes: [],
1090
+ templateOwners: /* @__PURE__ */ new Map(),
1091
+ drop: /* @__PURE__ */ new Set(),
1092
+ styles: [],
1093
+ diagnostics: [],
1094
+ edits: [],
1095
+ literals: [],
1096
+ references: [],
1097
+ shadowClasses: /* @__PURE__ */ new Map()
1098
+ };
1099
+ if (!options) return plan;
1100
+ const literalsOf = findLiterals(program, options.cssModules ?? ["@fluixi/core", "@fluixi/dom"], plan, tags.css ?? []);
1101
+ plan.names = literalsOf;
1102
+ const classOf = /* @__PURE__ */ new Map();
1103
+ const shadowOf = (owner) => {
1104
+ const cls = classOf.get(owner);
1105
+ return cls ? plan.shadowClasses.get(cls) : void 0;
1106
+ };
1107
+ const resolved = /* @__PURE__ */ new Map();
1108
+ const piecesToNodes = (quasis, expressions, at, stack) => {
1109
+ const out = [];
1110
+ for (let i = 0; i < quasis.length; i++) {
1111
+ const text = quasis[i].value.cooked ?? quasis[i].value.raw;
1112
+ if (text.trim()) {
1113
+ try {
1114
+ const start = quasis[i].loc?.start;
1115
+ out.push(...options.process(text, sourceName).map((n2) => start ? shiftLoc(n2, sourceName, start) : n2));
1116
+ } catch (error) {
1117
+ plan.diagnostics.push({ ...at, message: `CSS could not be processed: ${error.message}` });
1118
+ return null;
1119
+ }
1120
+ }
1121
+ const expr = expressions[i];
1122
+ if (!expr) continue;
1123
+ const value = expr.type === "Identifier" ? resolveLiteral(expr.name, at, stack) : void 0;
1124
+ if (value === void 0) {
1125
+ plan.diagnostics.push({
1126
+ ...at,
1127
+ message: "Only css`` values can be interpolated into styles, and only by name: ${base}. Dynamic values go through custom properties (style:--x={...} and var(--x))."
1128
+ });
1129
+ return null;
1130
+ }
1131
+ if (value === null) return null;
1132
+ out.push(...value);
1133
+ }
1134
+ return out;
1135
+ };
1136
+ function resolveLiteral(name, at, stack) {
1137
+ const imported = literalsOf.imports.get(name);
1138
+ if (imported) {
1139
+ const nodes = options?.imported?.get(`${imported.source}#${imported.name}`);
1140
+ if (nodes) return nodes.map(dropLoc);
1141
+ plan.references.push({ name: imported.name, source: imported.source, ...at });
1142
+ plan.diagnostics.push({
1143
+ ...at,
1144
+ message: options?.imported ? `${name} from ${imported.source} is not a css\`\` value the build could find.` : `${name} comes from ${imported.source}: style values from another module are resolved by the build.`
1145
+ });
1146
+ return null;
1147
+ }
1148
+ const literal = literalsOf.literals.get(name);
1149
+ if (!literal) return void 0;
1150
+ if (stack.includes(name)) {
1151
+ plan.diagnostics.push({ ...at, message: `css\`\` values include each other: ${[...stack, name].join(" → ")}.` });
1152
+ return null;
1153
+ }
1154
+ if (!resolved.has(name)) {
1155
+ resolved.set(name, piecesToNodes(literal.quasi.quasis, literal.quasi.expressions, at, [...stack, name]));
1156
+ }
1157
+ return resolved.get(name);
1158
+ }
1159
+ for (const [name, literal] of literalsOf.literals) {
1160
+ const at = { line: literal.loc?.start.line ?? 0, column: literal.loc?.start.column ?? 0 };
1161
+ const nodes = resolveLiteral(name, at, []);
1162
+ const id = hash(`${sourceName}:css:${name}`);
1163
+ plan.edits.push({ start: literal.start, end: literal.end, code: `({ $fxStyle: ${JSON.stringify(id)} })` });
1164
+ if (nodes) plan.literals.push({ id, name, css: printStyle(nodes, options.minify) });
1165
+ }
1166
+ const scopeOf = /* @__PURE__ */ new Map();
1167
+ const names = /* @__PURE__ */ new Map();
1168
+ const named = /* @__PURE__ */ new Map();
1169
+ const visit = (node, owners) => {
1170
+ if (!node || typeof node !== "object") return;
1171
+ if (node.type === "VariableDeclarator" && node.id.type === "Identifier" && node.init && FUNCTIONS.has(node.init.type)) {
1172
+ named.set(node.init, node.id.name);
1173
+ }
1174
+ if ((node.type === "ClassDeclaration" || node.type === "ClassExpression") && node.id) {
1175
+ for (const member of node.body.body) if (member.type === "ClassMethod") named.set(member, node.id.name);
1176
+ }
1177
+ if (node.type === "ClassDeclaration" || node.type === "ClassExpression") {
1178
+ for (const member of node.body.body) if (member.type === "ClassMethod") classOf.set(member, node);
1179
+ const shadow = staticMember(node, "shadow");
1180
+ if (shadow?.value && !(shadow.value.type === "BooleanLiteral" && shadow.value.value === false)) {
1181
+ plan.shadowClasses.set(node, { node, styles: staticMember(node, "styles"), indexes: [] });
1182
+ }
1183
+ }
1184
+ if (node.type === "TaggedTemplateExpression" && node.tag.type === "Identifier" && tags.html.get(node.tag.name) === "html") {
1185
+ const text = node.quasi.quasis.map((q) => q.value.raw).join("");
1186
+ if (/<style[\s>]/.test(text)) {
1187
+ const at = { line: node.loc?.start.line ?? 0, column: node.loc?.start.column ?? 0 };
1188
+ const owner = owners[0];
1189
+ if (!owner) plan.diagnostics.push({ ...at, message: "<style> has to be written inside a component." });
1190
+ else {
1191
+ const id = idFor(owner);
1192
+ const shadow = shadowOf(owner);
1193
+ plan.templateOwners.set(node.start, { id, component: functionName(owner, named) });
1194
+ for (const block of templateBlocks(node.quasi)) compileBlock(block, block.scoped, owner, at);
1195
+ }
1196
+ }
1197
+ }
1198
+ const inner = FUNCTIONS.has(node.type) ? [...owners, node] : owners;
1199
+ if (node.type === "JSXElement" && isStyle(node)) {
1200
+ handle(node, owners);
1201
+ return;
1202
+ }
1203
+ for (const key of Object.keys(node)) {
1204
+ if (key === "loc" || key === "leadingComments" || key === "trailingComments" || key === "innerComments") continue;
1205
+ const value = node[key];
1206
+ if (Array.isArray(value)) for (const v of value) visit(v, inner);
1207
+ else if (value && typeof value === "object" && "type" in value) visit(value, inner);
1208
+ }
1209
+ };
1210
+ const handle = (node, owners) => {
1211
+ const at = { line: node.loc?.start.line ?? 0, column: node.loc?.start.column ?? 0 };
1212
+ const pieces = blockPieces(node);
1213
+ if (pieces === null) {
1214
+ plan.diagnostics.push({
1215
+ ...at,
1216
+ message: "<style> needs its CSS as one template literal, <style>{`...`}</style>: JSX reads a bare `{` as an expression."
1217
+ });
1218
+ return;
1219
+ }
1220
+ const owner = owners[0];
1221
+ if (!owner) {
1222
+ plan.diagnostics.push({ ...at, message: "<style> has to be written inside a component." });
1223
+ return;
1224
+ }
1225
+ compileBlock(pieces, isScoped(node), owner, at);
1226
+ plan.drop.add(node.start);
1227
+ };
1228
+ const compileBlock = (pieces, scoped, owner, at) => {
1229
+ const component = functionName(owner, named);
1230
+ const id = idFor(owner);
1231
+ const processed = piecesToNodes(pieces.quasis, pieces.expressions, at, []);
1232
+ if (!processed) return;
1233
+ let nodes = processed;
1234
+ const shadow = shadowOf(owner);
1235
+ if (scoped && !shadow) {
1236
+ if (!plan.scopes.some((s) => s.value === id)) plan.scopes.push({ start: owner.start, end: owner.end, value: id });
1237
+ nodes = scopeNodes(nodes, id, (message) => plan.diagnostics.push({ ...at, message }));
1238
+ }
1239
+ const printed = module ? printStyleWithMap(nodes, options.minify, sourceName, module) : { css: printStyle(nodes, options.minify) };
1240
+ plan.styles.push({ id, scoped: scoped && !shadow, component, ...printed, ...shadow ? { shadow: true } : {}, ...at });
1241
+ if (shadow) shadow.indexes.push(plan.styles.length - 1);
1242
+ };
1243
+ function idFor(owner) {
1244
+ let id = scopeOf.get(owner);
1245
+ if (!id) {
1246
+ const component = functionName(owner, named);
1247
+ const seen = names.get(component) ?? 0;
1248
+ names.set(component, seen + 1);
1249
+ id = hash(`${sourceName}:${component}${seen ? ":" + seen : ""}`);
1250
+ scopeOf.set(owner, id);
1251
+ }
1252
+ return id;
1253
+ }
1254
+ visit(program, []);
1255
+ return plan;
1256
+ }
1257
+ function applyTemplateStyles(ir, t) {
1258
+ const walk8 = (node) => {
1259
+ const children = node.children;
1260
+ if (children) {
1261
+ for (let i = children.length - 1; i >= 0; i--) {
1262
+ const child = children[i];
1263
+ if (child.kind === "element" && child.tag === "style" && t.owner) children.splice(i, 1);
1264
+ else walk8(child);
1265
+ }
1266
+ }
1267
+ if (t.scope && node.kind === "element" && node.tag !== "style" && node.props && !node.props.some((p) => p.name === SCOPE_ATTR)) {
1268
+ node.props.unshift({ name: SCOPE_ATTR, kind: "attr", literal: t.scope });
1269
+ }
1270
+ };
1271
+ walk8(ir);
1272
+ }
1273
+ function staticMember(cls, name) {
1274
+ return cls.body.body.find(
1275
+ (m) => m.type === "ClassProperty" && m.static && (m.key.type === "Identifier" && m.key.name === name || m.key.type === "StringLiteral" && m.key.value === name)
1276
+ );
1277
+ }
1278
+ function finishShadowStyles(plan, code, moduleId) {
1279
+ const edits = [];
1280
+ const imports = [];
1281
+ let next = 0;
1282
+ const importOf = (specifier) => {
1283
+ const local = `__fxShadow${next++}`;
1284
+ imports.push(`import ${local} from ${JSON.stringify(specifier)};
1285
+ `);
1286
+ return local;
1287
+ };
1288
+ for (const shadow of plan.shadowClasses.values()) {
1289
+ const blocks = shadow.indexes.map((i) => importOf(`${moduleId}?fx-style&index=${i}&inline&lang.css`));
1290
+ let value;
1291
+ const prop = shadow.styles;
1292
+ if (prop?.value) {
1293
+ const start = prop.value.start;
1294
+ let text = code.slice(start, prop.value.end);
1295
+ const replacements = [];
1296
+ walkIdentifiers(prop.value, (id) => {
1297
+ const local = plan.names?.literals.has(id.name) ? importOf(`${moduleId}?fx-literal=${id.name}&inline&lang.css`) : plan.names?.imports.has(id.name) && !/\.css(\?|$)/.test(plan.names.imports.get(id.name).source) ? importOf(`${plan.names.imports.get(id.name).source}?fx-literal=${plan.names.imports.get(id.name).name}&inline&lang.css`) : void 0;
1298
+ if (local) replacements.push({ at: id.start - start, length: id.name.length, with: local });
1299
+ });
1300
+ for (const r of replacements.sort((a, b) => b.at - a.at)) text = text.slice(0, r.at) + r.with + text.slice(r.at + r.length);
1301
+ value = text;
1302
+ }
1303
+ if (!blocks.length && value === void 0) continue;
1304
+ if (prop?.value) {
1305
+ const merged = blocks.length ? `[${value}, ${blocks.join(", ")}]` : value;
1306
+ edits.push({ start: prop.value.start, end: prop.value.end, code: merged });
1307
+ } else if (blocks.length) {
1308
+ const open = shadow.node.body.start + 1;
1309
+ edits.push({ start: open, end: open, code: ` static styles = [${blocks.join(", ")}];` });
1310
+ }
1311
+ }
1312
+ if (imports.length) edits.push({ start: 0, end: 0, code: imports.join("") });
1313
+ return edits;
1314
+ }
1315
+ function walkIdentifiers(node, visit) {
1316
+ if (!node || typeof node !== "object") return;
1317
+ if (node.type === "Identifier") return visit(node);
1318
+ if (node.type === "MemberExpression") return walkIdentifiers(node.object, visit);
1319
+ if (node.type === "ObjectProperty") return walkIdentifiers(node.value, visit);
1320
+ for (const key of Object.keys(node)) {
1321
+ if (key === "loc") continue;
1322
+ const value = node[key];
1323
+ if (Array.isArray(value)) for (const v of value) walkIdentifiers(v, visit);
1324
+ else if (value && typeof value === "object" && "type" in value) walkIdentifiers(value, visit);
1325
+ }
1326
+ }
1327
+ function isStyle(node) {
1328
+ const name = node.openingElement.name;
1329
+ return name.type === "JSXIdentifier" && name.name === "style";
1330
+ }
1331
+ function isScoped(node) {
1332
+ return node.openingElement.attributes.some((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === "scoped");
1333
+ }
1334
+ function templateBlocks(quasi) {
1335
+ const blocks = [];
1336
+ let open = null;
1337
+ quasi.quasis.forEach((q, i) => {
1338
+ const raw = q.value.raw;
1339
+ let from = 0;
1340
+ while (from <= raw.length) {
1341
+ if (!open) {
1342
+ const tag = /<style\b([^>]*)>/g;
1343
+ tag.lastIndex = from;
1344
+ const m = tag.exec(raw);
1345
+ if (!m) break;
1346
+ open = { quasis: [], expressions: [], scoped: /\bscoped\b/.test(m[1]) };
1347
+ from = m.index + m[0].length;
1348
+ }
1349
+ const close = raw.indexOf("</style", from);
1350
+ open.quasis.push(slicePiece(q, raw, from, close === -1 ? raw.length : close));
1351
+ if (close === -1) break;
1352
+ blocks.push(open);
1353
+ open = null;
1354
+ from = close;
1355
+ }
1356
+ const expr = quasi.expressions[i];
1357
+ if (open && expr) open.expressions.push(expr);
1358
+ });
1359
+ return blocks;
1360
+ }
1361
+ function slicePiece(q, raw, from, to) {
1362
+ const text = raw.slice(from, to);
1363
+ const start = q.loc?.start;
1364
+ if (!start) return { type: "TemplateElement", value: { raw: text, cooked: text } };
1365
+ const before = raw.slice(0, from).split("\n");
1366
+ const line = start.line + before.length - 1;
1367
+ const column = before.length > 1 ? before[before.length - 1].length : start.column + from;
1368
+ return { type: "TemplateElement", value: { raw: text, cooked: text }, loc: { start: { line, column } } };
1369
+ }
1370
+ function blockPieces(node) {
1371
+ const meaningful = node.children.filter((c) => !(c.type === "JSXText" && !c.value.trim()));
1372
+ if (meaningful.length === 0) return { quasis: [], expressions: [] };
1373
+ if (meaningful.length !== 1) return null;
1374
+ const only = meaningful[0];
1375
+ if (only.type !== "JSXExpressionContainer") return null;
1376
+ const expr = only.expression;
1377
+ if (expr.type === "StringLiteral") return { quasis: [{ type: "TemplateElement", value: { raw: expr.value, cooked: expr.value } }], expressions: [] };
1378
+ if (expr.type === "TemplateLiteral") return { quasis: expr.quasis, expressions: expr.expressions };
1379
+ return null;
1380
+ }
1381
+ function findLiterals(program, modules, plan, intrinsic) {
1382
+ const literals = /* @__PURE__ */ new Map();
1383
+ const imports = /* @__PURE__ */ new Map();
1384
+ const tags = new Set(intrinsic);
1385
+ for (const statement of program.body ?? []) {
1386
+ if (statement.type !== "ImportDeclaration") continue;
1387
+ const source = statement.source.value;
1388
+ const specifiers = statement.specifiers;
1389
+ for (const sp of specifiers) {
1390
+ if (sp.type === "ImportSpecifier") imports.set(sp.local.name, { source, name: sp.imported.name ?? sp.imported.value });
1391
+ }
1392
+ if (!modules.includes(source)) continue;
1393
+ const css = specifiers.find((sp) => sp.type === "ImportSpecifier" && (sp.imported.name ?? sp.imported.value) === "css");
1394
+ if (!css) continue;
1395
+ const local = css.local.name;
1396
+ tags.add(local);
1397
+ imports.delete(local);
1398
+ plan.edits.push({ start: statement.start, end: statement.end, code: importWithout(statement, css) });
1399
+ }
1400
+ if (tags.size === 0) return { literals, imports };
1401
+ const visit = (node) => {
1402
+ if (!node || typeof node !== "object") return;
1403
+ if (node.type === "VariableDeclarator" && node.id.type === "Identifier" && node.init?.type === "TaggedTemplateExpression" && node.init.tag.type === "Identifier" && tags.has(node.init.tag.name)) {
1404
+ literals.set(node.id.name, node.init);
1405
+ }
1406
+ for (const key of Object.keys(node)) {
1407
+ if (key === "loc") continue;
1408
+ const value = node[key];
1409
+ if (Array.isArray(value)) for (const v of value) visit(v);
1410
+ else if (value && typeof value === "object" && "type" in value) visit(value);
1411
+ }
1412
+ };
1413
+ visit(program);
1414
+ return { literals, imports };
1415
+ }
1416
+ function importWithout(statement, removed) {
1417
+ const rest = statement.specifiers.filter((sp) => sp !== removed);
1418
+ const source = JSON.stringify(statement.source.value);
1419
+ if (rest.length === 0) return "";
1420
+ const def = rest.find((sp) => sp.type === "ImportDefaultSpecifier");
1421
+ const ns = rest.find((sp) => sp.type === "ImportNamespaceSpecifier");
1422
+ const named = rest.filter((sp) => sp.type === "ImportSpecifier").map((sp) => {
1423
+ const imported = sp.imported.name ?? JSON.stringify(sp.imported.value);
1424
+ return imported === sp.local.name ? imported : `${imported} as ${sp.local.name}`;
1425
+ });
1426
+ const parts = [def?.local.name, ns ? `* as ${ns.local.name}` : void 0, named.length ? `{ ${named.join(", ")} }` : void 0].filter(Boolean);
1427
+ return `import ${parts.join(", ")} from ${source};`;
1428
+ }
1429
+ function functionName(fn, named) {
1430
+ if ((fn.type === "FunctionDeclaration" || fn.type === "FunctionExpression") && fn.id) return fn.id.name;
1431
+ return named.get(fn) ?? "anonymous";
1432
+ }
1433
+ function hash(text) {
1434
+ let h = 2166136261;
1435
+ for (let i = 0; i < text.length; i++) {
1436
+ h ^= text.charCodeAt(i);
1437
+ h = Math.imul(h, 16777619);
1438
+ }
1439
+ return (h >>> 0).toString(36);
1440
+ }
1441
+ var OPAQUE = /(^|-)keyframes$|^font-face$|^property$|^counter-style$|^page$|^font-feature-values$/;
1442
+ function scopeNodes(nodes, id, warn = () => {
1443
+ }) {
1444
+ return nodes.map((node) => {
1445
+ if (node.kind === "rule") return { ...node, selector: scopeSelector(node.selector, id, warn) };
1446
+ if (node.kind === "at-rule" && !OPAQUE.test(node.name)) return { ...node, nodes: scopeNodes(node.nodes, id, warn) };
1447
+ return node;
1448
+ });
1449
+ }
1450
+ function scopeSelector(selector, id, warn = () => {
1451
+ }) {
1452
+ return splitTop(selector, ",").map((complex) => scopeComplex(complex.trim(), `[${SCOPE_ATTR}="${id}"]`, warn)).join(", ");
1453
+ }
1454
+ function scopeComplex(selector, attr, warn) {
1455
+ if (/:host\b|::slotted\(/.test(selector)) {
1456
+ warn(`\`${selector}\` only means something in a shadow root; in a light-DOM component it is left as written.`);
1457
+ return selector;
1458
+ }
1459
+ const parts = compounds(selector);
1460
+ const deep = parts.findIndex((p) => p.text.startsWith(":deep("));
1461
+ if (deep !== -1) {
1462
+ const inner = unwrap(parts[deep].text, ":deep(");
1463
+ const before = parts.slice(0, deep);
1464
+ if (before.length === 0) return `${attr} ${inner}`;
1465
+ const scopedBefore = scopeAt(before, before.length - 1, attr);
1466
+ return `${join(scopedBefore)} ${inner}`;
1467
+ }
1468
+ let target = -1;
1469
+ for (let i = parts.length - 1; i >= 0; i--) {
1470
+ if (!parts[i].text.startsWith(":global(")) {
1471
+ target = i;
1472
+ break;
1473
+ }
1474
+ }
1475
+ const out = target === -1 ? parts : scopeAt(parts, target, attr);
1476
+ return join(out.map((p) => p.text.startsWith(":global(") ? { ...p, text: unwrap(p.text, ":global(") } : p));
1477
+ }
1478
+ function scopeAt(parts, index, attr) {
1479
+ return parts.map((p, i) => i === index ? { ...p, text: insertAttr(p.text, attr) } : p);
1480
+ }
1481
+ function insertAttr(compound, attr) {
1482
+ let depth = 0;
1483
+ let quote = "";
1484
+ for (let i = 0; i < compound.length; i++) {
1485
+ const c = compound[i];
1486
+ if (quote) {
1487
+ if (c === "\\") i++;
1488
+ else if (c === quote) quote = "";
1489
+ continue;
1490
+ }
1491
+ if (c === "\\") {
1492
+ i++;
1493
+ continue;
1494
+ }
1495
+ if (c === '"' || c === "'") quote = c;
1496
+ else if (c === "(" || c === "[") depth++;
1497
+ else if (c === ")" || c === "]") depth--;
1498
+ else if (c === ":" && depth === 0 && i > 0) return compound.slice(0, i) + attr + compound.slice(i);
1499
+ }
1500
+ return compound + attr;
1501
+ }
1502
+ function compounds(selector) {
1503
+ const out = [];
1504
+ let depth = 0;
1505
+ let quote = "";
1506
+ let current = "";
1507
+ let combinator = "";
1508
+ const push = () => {
1509
+ if (current.trim()) out.push({ combinator: out.length ? combinator || " " : "", text: current.trim() });
1510
+ current = "";
1511
+ combinator = "";
1512
+ };
1513
+ for (let i = 0; i < selector.length; i++) {
1514
+ const c = selector[i];
1515
+ if (quote) {
1516
+ current += c;
1517
+ if (c === "\\") current += selector[++i] ?? "";
1518
+ else if (c === quote) quote = "";
1519
+ continue;
1520
+ }
1521
+ if (c === "\\") {
1522
+ current += c + (selector[++i] ?? "");
1523
+ continue;
1524
+ }
1525
+ if (c === '"' || c === "'") quote = c;
1526
+ if (c === "(" || c === "[") depth++;
1527
+ if (c === ")" || c === "]") depth--;
1528
+ if (depth === 0 && (c === ">" || c === "+" || c === "~")) {
1529
+ push();
1530
+ combinator = c;
1531
+ continue;
1532
+ }
1533
+ if (depth === 0 && /\s/.test(c)) {
1534
+ if (current.trim()) {
1535
+ const keep = combinator;
1536
+ push();
1537
+ combinator = keep;
1538
+ }
1539
+ continue;
1540
+ }
1541
+ current += c;
1542
+ }
1543
+ push();
1544
+ return out;
1545
+ }
1546
+ function join(parts) {
1547
+ return parts.map((p, i) => i === 0 ? p.text : p.combinator === " " ? ` ${p.text}` : ` ${p.combinator} ${p.text}`).join("");
1548
+ }
1549
+ function unwrap(text, prefix) {
1550
+ return text.slice(prefix.length, text.lastIndexOf(")")).trim() + text.slice(text.lastIndexOf(")") + 1);
1551
+ }
1552
+ function splitTop(selector, separator) {
1553
+ const parts = [];
1554
+ let depth = 0;
1555
+ let quote = "";
1556
+ let start = 0;
1557
+ for (let i = 0; i < selector.length; i++) {
1558
+ const c = selector[i];
1559
+ if (quote) {
1560
+ if (c === "\\") i++;
1561
+ else if (c === quote) quote = "";
1562
+ } else if (c === '"' || c === "'") quote = c;
1563
+ else if (c === "\\") i++;
1564
+ else if (c === "(" || c === "[") depth++;
1565
+ else if (c === ")" || c === "]") depth--;
1566
+ else if (c === separator && depth === 0) {
1567
+ parts.push(selector.slice(start, i));
1568
+ start = i + 1;
1569
+ }
1570
+ }
1571
+ parts.push(selector.slice(start));
1572
+ return parts.filter((p) => p.trim());
1573
+ }
1574
+ function shiftLoc(node, source, start) {
1575
+ const loc = node.loc && node.loc.source === source ? { source, line: start.line + node.loc.line - 1, column: node.loc.line === 1 ? start.column + node.loc.column : node.loc.column } : node.loc;
1576
+ if (node.kind === "rule" || node.kind === "at-rule") return { ...node, loc, nodes: node.nodes.map((n2) => shiftLoc(n2, source, start)) };
1577
+ return { ...node, loc };
1578
+ }
1579
+ function dropLoc(node) {
1580
+ const { loc: _loc, ...rest } = node;
1581
+ if (rest.kind === "rule" || rest.kind === "at-rule") return { ...rest, nodes: rest.nodes.map(dropLoc) };
1582
+ return rest;
1583
+ }
1584
+ function printStyleWithMap(nodes, minify, sourceName, module) {
1585
+ const lines = [[]];
1586
+ let css = "";
1587
+ let column = 0;
1588
+ const emit = (text) => {
1589
+ for (const ch of text) {
1590
+ css += ch;
1591
+ if (ch === "\n") {
1592
+ lines.push([]);
1593
+ column = 0;
1594
+ } else column++;
1595
+ }
1596
+ };
1597
+ const mark = (loc) => {
1598
+ if (loc && loc.source === sourceName) lines[lines.length - 1].push([column, 0, loc.line - 1, loc.column]);
1599
+ };
1600
+ const write = (node, depth) => {
1601
+ const pad = minify ? "" : " ".repeat(depth);
1602
+ const nl = minify ? "" : "\n";
1603
+ const sp = minify ? "" : " ";
1604
+ emit(pad);
1605
+ mark(node.loc);
1606
+ if (node.kind === "declaration") {
1607
+ emit(`${node.property}:${sp}${node.value}${node.important ? `${sp}!important` : ""};${nl}`);
1608
+ } else if (node.kind === "raw") {
1609
+ emit(`${node.text}${nl}`);
1610
+ } else {
1611
+ emit(`${node.kind === "rule" ? node.selector : `@${node.name}${node.params ? " " + node.params : ""}`}${sp}{${nl}`);
1612
+ for (const child of node.nodes) write(child, depth + 1);
1613
+ emit(`${pad}}${nl}`);
1614
+ }
1615
+ };
1616
+ for (const node of nodes) write(node, 0);
1617
+ return { css, map: { version: 3, sources: [module.id], sourcesContent: [module.code], names: [], mappings: encode(lines) } };
1618
+ }
1619
+ function printStyle(nodes, minify = false) {
1620
+ const out = [];
1621
+ const write = (node, depth) => {
1622
+ const pad = minify ? "" : " ".repeat(depth);
1623
+ const nl = minify ? "" : "\n";
1624
+ const sp = minify ? "" : " ";
1625
+ if (node.kind === "declaration") {
1626
+ out.push(`${pad}${node.property}:${sp}${node.value}${node.important ? `${sp}!important` : ""};${nl}`);
1627
+ } else if (node.kind === "raw") {
1628
+ out.push(`${pad}${node.text}${nl}`);
1629
+ } else {
1630
+ const header = node.kind === "rule" ? node.selector : `@${node.name}${node.params ? " " + node.params : ""}`;
1631
+ out.push(`${pad}${header}${sp}{${nl}`);
1632
+ for (const child of node.nodes) write(child, depth + 1);
1633
+ out.push(`${pad}}${nl}`);
1634
+ }
1635
+ };
1636
+ for (const node of nodes) write(node, 0);
1637
+ return out.join("");
1638
+ }
1639
+
1079
1640
  // src/optimize/analyze-static.ts
1080
1641
  function propReason(prop) {
1081
1642
  switch (prop.kind) {
@@ -1521,6 +2082,7 @@ var INTRINSICS = {
1521
2082
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
1522
2083
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
1523
2084
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
2085
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
1524
2086
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
1525
2087
  // name. Still worth having: no import to write, and when a shorter form is
1526
2088
  // designed, the change is this line rather than a compiler pass.
@@ -1531,6 +2093,19 @@ var INTRINSICS = {
1531
2093
  // dependency analysis must not guess at.
1532
2094
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
1533
2095
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
2096
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
2097
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
2098
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
2099
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
2100
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
2101
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
2102
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
2103
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
2104
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
2105
+ // handle as far as reads go: the same accessor with `set` on it.
2106
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
2107
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
2108
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
1534
2109
  // An environment variable, public or server-only; the browser build refuses a secret.
1535
2110
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
1536
2111
  };
@@ -1543,8 +2118,11 @@ var RUNTIME_PRIMITIVES = {
1543
2118
  createMemo: "memo-accessor",
1544
2119
  createEffect: "effect",
1545
2120
  createRenderEffect: "effect",
2121
+ renderEffect: "effect",
2122
+ computed: "effect",
1546
2123
  createStore: "reactive-object",
1547
2124
  resource: "resource-handle",
2125
+ cachedResource: "resource-handle",
1548
2126
  createResource: "reactive-object",
1549
2127
  createSelector: "memo-accessor",
1550
2128
  createDeferred: "memo-accessor"
@@ -1553,8 +2131,19 @@ var PRIMITIVE_MODULES = [
1553
2131
  "@fluixi/reactive",
1554
2132
  "@fluixi/reactive/signal",
1555
2133
  "@fluixi/reactive/store",
2134
+ "@fluixi/reactive/cached-resource",
1556
2135
  "@fluixi/core"
1557
2136
  ];
2137
+ var TAG_INTRINSICS = {
2138
+ $html: { export: "html", module: "@fluixi/core" },
2139
+ $svg: { export: "svg", module: "@fluixi/core" },
2140
+ $css: { export: "css", module: "@fluixi/core" }
2141
+ };
2142
+ function tagIntrinsics(bound) {
2143
+ const tags = /* @__PURE__ */ new Map();
2144
+ for (const [name, tag] of Object.entries(TAG_INTRINSICS)) if (!bound.has(name)) tags.set(name, tag.export);
2145
+ return tags;
2146
+ }
1558
2147
  var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n2) => n2.slice(1)).join("|")})\\s*\\(`);
1559
2148
  function mayHaveIntrinsic(code) {
1560
2149
  return INTRINSIC_CALL.test(code);
@@ -1688,6 +2277,7 @@ function resolveReactiveBindings(program) {
1688
2277
  // src/transform/source-locations.ts
1689
2278
  var SOURCE_MARKER = "__fx_source";
1690
2279
  var COMPONENT_MARKER = "__fx_component";
2280
+ var CLASS_MARKER = "__fx_class";
1691
2281
  var HOLE_MARKER = "__fx_hole";
1692
2282
  var markHole = (accessor, line, column, name) => `(globalThis.${HOLE_MARKER} ?? ((f) => f))(${accessor},${line},${column}${name === void 0 ? "" : `,${JSON.stringify(name)}`})`;
1693
2283
  var PROPS_MARKER = "__fx_props";
@@ -1705,11 +2295,12 @@ var RUNTIME_PRIMITIVES2 = /* @__PURE__ */ new Set([
1705
2295
  "createStore",
1706
2296
  "createResource"
1707
2297
  ]);
1708
- function mayHaveReactiveCall(code) {
2298
+ function mayHaveSourceMarks(code) {
1709
2299
  for (const name of RUNTIME_PRIMITIVES2) {
1710
2300
  if (code.includes(name)) return true;
1711
2301
  }
1712
- return code.includes("$signal") || code.includes("$memo") || code.includes("$effect") || code.includes("$store");
2302
+ if (code.includes("$signal") || code.includes("$memo") || code.includes("$effect") || code.includes("$store")) return true;
2303
+ return /\bclass\b[^{;]*\bextends\b/.test(code);
1713
2304
  }
1714
2305
  var SINGLE_STATEMENT_PARENTS = /* @__PURE__ */ new Set([
1715
2306
  "IfStatement",
@@ -1728,6 +2319,7 @@ function isLoneBranch(statement, parent) {
1728
2319
  function isExport(node) {
1729
2320
  return node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration";
1730
2321
  }
2322
+ var CLASS_FIELDS = /* @__PURE__ */ new Set(["PropertyDefinition", "ClassProperty", "ClassPrivateProperty", "AccessorProperty"]);
1731
2323
  function isStatement(node) {
1732
2324
  return node.type.endsWith("Statement") || node.type === "VariableDeclaration";
1733
2325
  }
@@ -1748,6 +2340,14 @@ function walk3(node, visit, stack = []) {
1748
2340
  }
1749
2341
  function nameFor(stack) {
1750
2342
  const declarator = stack[stack.length - 1];
2343
+ if (declarator && CLASS_FIELDS.has(declarator.type)) {
2344
+ const key = declarator.key;
2345
+ if (key?.type === "Identifier" || key?.type === "PrivateIdentifier" || key?.type === "PrivateName") {
2346
+ const name = key.name ?? key.id?.name;
2347
+ return name;
2348
+ }
2349
+ return void 0;
2350
+ }
1751
2351
  if (declarator?.type !== "VariableDeclarator") return void 0;
1752
2352
  const id = declarator.id;
1753
2353
  if (id?.type === "Identifier") return id.name;
@@ -1796,6 +2396,17 @@ function collectSourceEdits(program, filename) {
1796
2396
  });
1797
2397
  }
1798
2398
  }
2399
+ if ((node.type === "ClassDeclaration" || node.type === "ClassExpression") && node.superClass && !node.declare) {
2400
+ const at = node.loc?.start;
2401
+ const body = node.body;
2402
+ if (at && body) {
2403
+ edits.push({
2404
+ start: body.start + 1,
2405
+ end: body.start + 1,
2406
+ code: `static{globalThis.${CLASS_MARKER}?.(this,${JSON.stringify(filename)},${at.line},${at.column});}`
2407
+ });
2408
+ }
2409
+ }
1799
2410
  if (node.type !== "CallExpression") return;
1800
2411
  const callee = node.callee;
1801
2412
  if (callee?.type !== "Identifier") return;
@@ -1805,6 +2416,26 @@ function collectSourceEdits(program, filename) {
1805
2416
  if (!intrinsic && !imported) return;
1806
2417
  const position = node.loc?.start;
1807
2418
  if (!position) return;
2419
+ for (let i = stack.length - 1; i >= 0; i--) {
2420
+ const up = stack[i];
2421
+ if (isStatement(up)) break;
2422
+ if (CLASS_FIELDS.has(up.type)) {
2423
+ if (claimed.has(node.start)) return;
2424
+ claimed.add(node.start);
2425
+ const label2 = nameFor(stack);
2426
+ const own = !up.static;
2427
+ const args2 = [
2428
+ JSON.stringify(filename),
2429
+ String(position.line),
2430
+ String(position.column),
2431
+ label2 ? JSON.stringify(label2) : own ? "undefined" : void 0,
2432
+ own ? "this" : void 0
2433
+ ].filter((a) => a !== void 0).join(",");
2434
+ edits.push({ start: node.start, end: node.start, code: `(globalThis.${SOURCE_MARKER}?.(${args2}),` });
2435
+ edits.push({ start: node.end, end: node.end, code: ")" });
2436
+ return;
2437
+ }
2438
+ }
1808
2439
  let statement;
1809
2440
  let parent;
1810
2441
  let index = -1;
@@ -2010,13 +2641,16 @@ function emitComponent(name, props, children, used, templates, at) {
2010
2641
  used.add("createComponent");
2011
2642
  const spreads = props.filter((p) => p.kind === "spread");
2012
2643
  const normal = props.filter((p) => p.kind !== "spread");
2013
- const entries = normal.map(
2014
- (p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
2015
- );
2644
+ const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
2016
2645
  if (children.length > 0) {
2017
- entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
2646
+ bodies.push(["children", emitChildren(children, used, templates)]);
2647
+ }
2648
+ let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
2649
+ if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
2650
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => (${body})`);
2651
+ const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
2652
+ obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
2018
2653
  }
2019
- let obj = `{ ${entries.join(", ")} }`;
2020
2654
  if (spreads.length > 0) {
2021
2655
  used.add("mergeProps");
2022
2656
  obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
@@ -2098,7 +2732,8 @@ function emitNode(node, used, templates) {
2098
2732
  } else {
2099
2733
  used.add("spread");
2100
2734
  const svgFlag = node.svg ? ", isSVG: true" : "";
2101
- const customFlag = node.tag.includes("-") ? ", isCustomElement: true" : "";
2735
+ const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
2736
+ const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
2102
2737
  parts.push(
2103
2738
  `spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
2104
2739
  );
@@ -2213,6 +2848,271 @@ function cleanTemplateText(value) {
2213
2848
  return out;
2214
2849
  }
2215
2850
 
2851
+ // src/lower/entities.ts
2852
+ var NAMED = {
2853
+ quot: 34,
2854
+ amp: 38,
2855
+ apos: 39,
2856
+ lt: 60,
2857
+ gt: 62,
2858
+ nbsp: 160,
2859
+ iexcl: 161,
2860
+ cent: 162,
2861
+ pound: 163,
2862
+ curren: 164,
2863
+ yen: 165,
2864
+ brvbar: 166,
2865
+ sect: 167,
2866
+ uml: 168,
2867
+ copy: 169,
2868
+ ordf: 170,
2869
+ laquo: 171,
2870
+ not: 172,
2871
+ shy: 173,
2872
+ reg: 174,
2873
+ macr: 175,
2874
+ deg: 176,
2875
+ plusmn: 177,
2876
+ sup2: 178,
2877
+ sup3: 179,
2878
+ acute: 180,
2879
+ micro: 181,
2880
+ para: 182,
2881
+ middot: 183,
2882
+ cedil: 184,
2883
+ sup1: 185,
2884
+ ordm: 186,
2885
+ raquo: 187,
2886
+ frac14: 188,
2887
+ frac12: 189,
2888
+ frac34: 190,
2889
+ iquest: 191,
2890
+ Agrave: 192,
2891
+ Aacute: 193,
2892
+ Acirc: 194,
2893
+ Atilde: 195,
2894
+ Auml: 196,
2895
+ Aring: 197,
2896
+ AElig: 198,
2897
+ Ccedil: 199,
2898
+ Egrave: 200,
2899
+ Eacute: 201,
2900
+ Ecirc: 202,
2901
+ Euml: 203,
2902
+ Igrave: 204,
2903
+ Iacute: 205,
2904
+ Icirc: 206,
2905
+ Iuml: 207,
2906
+ ETH: 208,
2907
+ Ntilde: 209,
2908
+ Ograve: 210,
2909
+ Oacute: 211,
2910
+ Ocirc: 212,
2911
+ Otilde: 213,
2912
+ Ouml: 214,
2913
+ times: 215,
2914
+ Oslash: 216,
2915
+ Ugrave: 217,
2916
+ Uacute: 218,
2917
+ Ucirc: 219,
2918
+ Uuml: 220,
2919
+ Yacute: 221,
2920
+ THORN: 222,
2921
+ szlig: 223,
2922
+ agrave: 224,
2923
+ aacute: 225,
2924
+ acirc: 226,
2925
+ atilde: 227,
2926
+ auml: 228,
2927
+ aring: 229,
2928
+ aelig: 230,
2929
+ ccedil: 231,
2930
+ egrave: 232,
2931
+ eacute: 233,
2932
+ ecirc: 234,
2933
+ euml: 235,
2934
+ igrave: 236,
2935
+ iacute: 237,
2936
+ icirc: 238,
2937
+ iuml: 239,
2938
+ eth: 240,
2939
+ ntilde: 241,
2940
+ ograve: 242,
2941
+ oacute: 243,
2942
+ ocirc: 244,
2943
+ otilde: 245,
2944
+ ouml: 246,
2945
+ divide: 247,
2946
+ oslash: 248,
2947
+ ugrave: 249,
2948
+ uacute: 250,
2949
+ ucirc: 251,
2950
+ uuml: 252,
2951
+ yacute: 253,
2952
+ thorn: 254,
2953
+ yuml: 255,
2954
+ OElig: 338,
2955
+ oelig: 339,
2956
+ Scaron: 352,
2957
+ scaron: 353,
2958
+ Yuml: 376,
2959
+ fnof: 402,
2960
+ circ: 710,
2961
+ tilde: 732,
2962
+ Alpha: 913,
2963
+ Beta: 914,
2964
+ Gamma: 915,
2965
+ Delta: 916,
2966
+ Epsilon: 917,
2967
+ Zeta: 918,
2968
+ Eta: 919,
2969
+ Theta: 920,
2970
+ Iota: 921,
2971
+ Kappa: 922,
2972
+ Lambda: 923,
2973
+ Mu: 924,
2974
+ Nu: 925,
2975
+ Xi: 926,
2976
+ Omicron: 927,
2977
+ Pi: 928,
2978
+ Rho: 929,
2979
+ Sigma: 931,
2980
+ Tau: 932,
2981
+ Upsilon: 933,
2982
+ Phi: 934,
2983
+ Chi: 935,
2984
+ Psi: 936,
2985
+ Omega: 937,
2986
+ alpha: 945,
2987
+ beta: 946,
2988
+ gamma: 947,
2989
+ delta: 948,
2990
+ epsilon: 949,
2991
+ zeta: 950,
2992
+ eta: 951,
2993
+ theta: 952,
2994
+ iota: 953,
2995
+ kappa: 954,
2996
+ lambda: 955,
2997
+ mu: 956,
2998
+ nu: 957,
2999
+ xi: 958,
3000
+ omicron: 959,
3001
+ pi: 960,
3002
+ rho: 961,
3003
+ sigmaf: 962,
3004
+ sigma: 963,
3005
+ tau: 964,
3006
+ upsilon: 965,
3007
+ phi: 966,
3008
+ chi: 967,
3009
+ psi: 968,
3010
+ omega: 969,
3011
+ thetasym: 977,
3012
+ upsih: 978,
3013
+ piv: 982,
3014
+ ensp: 8194,
3015
+ emsp: 8195,
3016
+ thinsp: 8201,
3017
+ zwnj: 8204,
3018
+ zwj: 8205,
3019
+ lrm: 8206,
3020
+ rlm: 8207,
3021
+ ndash: 8211,
3022
+ mdash: 8212,
3023
+ lsquo: 8216,
3024
+ rsquo: 8217,
3025
+ sbquo: 8218,
3026
+ ldquo: 8220,
3027
+ rdquo: 8221,
3028
+ bdquo: 8222,
3029
+ dagger: 8224,
3030
+ Dagger: 8225,
3031
+ bull: 8226,
3032
+ hellip: 8230,
3033
+ permil: 8240,
3034
+ prime: 8242,
3035
+ Prime: 8243,
3036
+ lsaquo: 8249,
3037
+ rsaquo: 8250,
3038
+ oline: 8254,
3039
+ frasl: 8260,
3040
+ euro: 8364,
3041
+ image: 8465,
3042
+ weierp: 8472,
3043
+ real: 8476,
3044
+ trade: 8482,
3045
+ alefsym: 8501,
3046
+ larr: 8592,
3047
+ uarr: 8593,
3048
+ rarr: 8594,
3049
+ darr: 8595,
3050
+ harr: 8596,
3051
+ crarr: 8629,
3052
+ lArr: 8656,
3053
+ uArr: 8657,
3054
+ rArr: 8658,
3055
+ dArr: 8659,
3056
+ hArr: 8660,
3057
+ forall: 8704,
3058
+ part: 8706,
3059
+ exist: 8707,
3060
+ empty: 8709,
3061
+ nabla: 8711,
3062
+ isin: 8712,
3063
+ notin: 8713,
3064
+ ni: 8715,
3065
+ prod: 8719,
3066
+ sum: 8721,
3067
+ minus: 8722,
3068
+ lowast: 8727,
3069
+ radic: 8730,
3070
+ prop: 8733,
3071
+ infin: 8734,
3072
+ ang: 8736,
3073
+ and: 8743,
3074
+ or: 8744,
3075
+ cap: 8745,
3076
+ cup: 8746,
3077
+ int: 8747,
3078
+ there4: 8756,
3079
+ sim: 8764,
3080
+ cong: 8773,
3081
+ asymp: 8776,
3082
+ ne: 8800,
3083
+ equiv: 8801,
3084
+ le: 8804,
3085
+ ge: 8805,
3086
+ sub: 8834,
3087
+ sup: 8835,
3088
+ nsub: 8836,
3089
+ sube: 8838,
3090
+ supe: 8839,
3091
+ oplus: 8853,
3092
+ otimes: 8855,
3093
+ perp: 8869,
3094
+ sdot: 8901,
3095
+ lceil: 8968,
3096
+ rceil: 8969,
3097
+ lfloor: 8970,
3098
+ rfloor: 8971,
3099
+ lang: 9001,
3100
+ rang: 9002,
3101
+ loz: 9674,
3102
+ spades: 9824,
3103
+ clubs: 9827,
3104
+ hearts: 9829,
3105
+ diams: 9830
3106
+ };
3107
+ var REFERENCE = /&(?:#[xX]([0-9a-fA-F]{1,6})|#([0-9]{1,7})|([a-zA-Z][a-zA-Z0-9]{1,31}));/g;
3108
+ function decodeEntities(value) {
3109
+ if (!value.includes("&")) return value;
3110
+ return value.replace(REFERENCE, (match, hex, dec, name) => {
3111
+ const code = hex ? parseInt(hex, 16) : dec ? parseInt(dec, 10) : Object.prototype.hasOwnProperty.call(NAMED, name) ? NAMED[name] : void 0;
3112
+ return code !== void 0 && code <= 1114111 ? String.fromCodePoint(code) : match;
3113
+ });
3114
+ }
3115
+
2216
3116
  // src/lower/template.ts
2217
3117
  var DELEGATED = /* @__PURE__ */ new Set([
2218
3118
  "click",
@@ -2231,6 +3131,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
2231
3131
  function escapeTemplate(s) {
2232
3132
  return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
2233
3133
  }
3134
+ var CONTAINER = "container";
2234
3135
  function capitalize(s) {
2235
3136
  return s.charAt(0).toUpperCase() + s.slice(1);
2236
3137
  }
@@ -2285,10 +3186,6 @@ var Lowerer = class {
2285
3186
  if (hasElse) i = elseIdx;
2286
3187
  continue;
2287
3188
  }
2288
- if (child.attributes.some((a) => a.kind === "EachDirective")) {
2289
- out.push(this.lowerEach(child));
2290
- continue;
2291
- }
2292
3189
  }
2293
3190
  const node = this.lowerNode(child);
2294
3191
  if (node) out.push(node);
@@ -2302,7 +3199,7 @@ var Lowerer = class {
2302
3199
  switch (node.kind) {
2303
3200
  case "Text": {
2304
3201
  if (node.raw) return { kind: "text", value: node.value };
2305
- const cleaned = cleanTemplateText(node.value);
3202
+ const cleaned = decodeEntities(cleanTemplateText(node.value));
2306
3203
  return cleaned ? { kind: "text", value: cleaned } : null;
2307
3204
  }
2308
3205
  case "Comment":
@@ -2330,13 +3227,16 @@ var Lowerer = class {
2330
3227
  node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
2331
3228
  return node;
2332
3229
  }
3230
+ const each = this.lowerEach(el);
3231
+ const children = each ? [each] : this.lowerChildren(el.children);
3232
+ if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
2333
3233
  const at = this.tagAt(el);
2334
3234
  return {
2335
3235
  kind: "element",
2336
3236
  tag: el.tag,
2337
3237
  svg: el.namespace === "svg",
2338
- props: this.lowerAttributes(el.attributes),
2339
- children: this.lowerChildren(el.children),
3238
+ props: this.lowerAttributes(el.attributes, true),
3239
+ children,
2340
3240
  static: false,
2341
3241
  ...at ? { at } : {}
2342
3242
  };
@@ -2408,10 +3308,14 @@ var Lowerer = class {
2408
3308
  const at = this.tagAt(el);
2409
3309
  return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
2410
3310
  }
2411
- /** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
3311
+ /**
3312
+ * `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
3313
+ * content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
3314
+ * it is. Null when the element has no `each`.
3315
+ */
2412
3316
  lowerEach(el) {
2413
3317
  const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
2414
- if (!eachDir || eachDir.kind !== "EachDirective") return this.lowerNode(el);
3318
+ if (!eachDir || eachDir.kind !== "EachDirective") return null;
2415
3319
  const each = this.hole(eachDir.hole);
2416
3320
  const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
2417
3321
  if (eachDir.key) {
@@ -2421,12 +3325,12 @@ var Lowerer = class {
2421
3325
  const arrow = this.itemArrow(el);
2422
3326
  let childrenCode;
2423
3327
  if (arrow) {
2424
- const bodyIR = { kind: "expr", code: arrow.body, reactive: arrow.bodyReactive };
2425
- const repeated = this.rebuildWithChildren(el, [bodyIR]);
2426
- childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
3328
+ const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
3329
+ childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
2427
3330
  } else {
2428
- const repeated = this.stripAndLower(el, (a) => a.kind === "EachDirective");
2429
- childrenCode = `() => ${paren(this.emit(repeated), "arrowBody")}`;
3331
+ const content = this.lowerChildren(el.children);
3332
+ const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
3333
+ childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
2430
3334
  }
2431
3335
  const children = [{ kind: "expr", code: childrenCode, reactive: false }];
2432
3336
  const at = this.tagAt(el);
@@ -2443,33 +3347,23 @@ var Lowerer = class {
2443
3347
  const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
2444
3348
  return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
2445
3349
  }
2446
- /** Same element (minus each/key) but with explicit IR children. */
2447
- rebuildWithChildren(el, children) {
2448
- const props = this.lowerAttributes(
2449
- el.attributes.filter((a) => a.kind !== "EachDirective")
2450
- );
2451
- if (el.kind === "Element") {
2452
- const at = this.tagAt(el);
2453
- return {
2454
- kind: "element",
2455
- tag: el.tag,
2456
- svg: el.namespace === "svg",
2457
- props,
2458
- children,
2459
- static: false,
2460
- ...at ? { at } : {}
2461
- };
2462
- }
2463
- return { kind: "component", name: el.tag, props, children };
2464
- }
2465
3350
  // --- attributes ---------------------------------------------------------
2466
- lowerAttributes(attrs) {
3351
+ /**
3352
+ * `element`: a DOM element, where each class:/style: value that moves is its own accessor.
3353
+ * A component gets the object as before, rebuilt whole when any value moves.
3354
+ */
3355
+ lowerAttributes(attrs, element = false) {
3356
+ let anyReactive = false;
3357
+ const entry = (name, h) => {
3358
+ anyReactive ||= h.reactive;
3359
+ if (!element || !h.reactive) return `${JSON.stringify(name)}: ${h.code}`;
3360
+ const accessor = `() => ${paren(h.code, "arrowBody")}`;
3361
+ return `${JSON.stringify(name)}: ${h.at ? markHole(accessor, h.at.line, h.at.column) : accessor}`;
3362
+ };
2467
3363
  const props = [];
2468
3364
  const classEntries = [];
2469
- let classReactive = false;
2470
3365
  let classAt;
2471
3366
  const styleEntries = [];
2472
- let styleReactive = false;
2473
3367
  let styleAt;
2474
3368
  const usePairs = [];
2475
3369
  for (const a of attrs) {
@@ -2509,15 +3403,13 @@ var Lowerer = class {
2509
3403
  break;
2510
3404
  case "ClassDirective": {
2511
3405
  const h = this.hole(a.hole);
2512
- classEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
2513
- classReactive = classReactive || h.reactive;
3406
+ classEntries.push(entry(a.name, h));
2514
3407
  classAt ??= h.at;
2515
3408
  break;
2516
3409
  }
2517
3410
  case "StyleDirective": {
2518
3411
  const h = this.hole(a.hole);
2519
- styleEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
2520
- styleReactive = styleReactive || h.reactive;
3412
+ styleEntries.push(entry(a.name, h));
2521
3413
  styleAt ??= h.at;
2522
3414
  break;
2523
3415
  }
@@ -2537,18 +3429,19 @@ var Lowerer = class {
2537
3429
  name: "classList",
2538
3430
  kind: "attr",
2539
3431
  expr: `{ ${classEntries.join(", ")} }`,
2540
- reactive: classReactive,
3432
+ reactive: !element && anyReactive,
2541
3433
  ...classAt ? { at: classAt } : {}
2542
3434
  });
2543
3435
  }
2544
3436
  if (styleEntries.length > 0) {
2545
- props.push({
2546
- name: "style",
2547
- kind: "attr",
2548
- expr: `{ ${styleEntries.join(", ")} }`,
2549
- reactive: styleReactive,
2550
- ...styleAt ? { at: styleAt } : {}
2551
- });
3437
+ const entries = `{ ${styleEntries.join(", ")} }`;
3438
+ const base = props.findIndex((p) => p.name === "style");
3439
+ if (base === -1) props.push({ name: "style", kind: "attr", expr: entries, reactive: !element && anyReactive, ...styleAt ? { at: styleAt } : {} });
3440
+ else {
3441
+ const prop = props[base];
3442
+ const value = prop.expr === void 0 ? JSON.stringify(prop.literal) : prop.reactive ? `() => ${paren(prop.expr, "arrowBody")}` : prop.expr;
3443
+ props[base] = { name: "style", kind: "attr", expr: `[${value}, ${entries}]`, reactive: false, ...prop.at ? { at: prop.at } : {} };
3444
+ }
2552
3445
  }
2553
3446
  if (usePairs.length > 0) {
2554
3447
  props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
@@ -2637,7 +3530,7 @@ var Lowerer = class {
2637
3530
  return base;
2638
3531
  }
2639
3532
  if (v.kind === "static") {
2640
- base.literal = v.value;
3533
+ base.literal = decodeEntities(v.value);
2641
3534
  return base;
2642
3535
  }
2643
3536
  if (v.kind === "hole") {
@@ -2650,7 +3543,7 @@ var Lowerer = class {
2650
3543
  let reactive = false;
2651
3544
  let at;
2652
3545
  const body = v.parts.map((p) => {
2653
- if ("text" in p) return escapeTemplate(p.text);
3546
+ if ("text" in p) return escapeTemplate(decodeEntities(p.text));
2654
3547
  const h = this.hole(p.hole);
2655
3548
  reactive = reactive || h.reactive;
2656
3549
  at ??= h.at;
@@ -2675,6 +3568,7 @@ function compilePieces(pieces, holes, options = {}) {
2675
3568
  ...options.sourceFile ? { sourceFile: options.sourceFile } : {},
2676
3569
  ...options.positionAt ? { positionAt: options.positionAt } : {}
2677
3570
  });
3571
+ if (options.templateStyles) applyTemplateStyles(ir, options.templateStyles);
2678
3572
  analyzeStatic(ir);
2679
3573
  resolveComponentSources(ir, {
2680
3574
  resolver: createComponentResolver(options.resolve ?? []),
@@ -2780,6 +3674,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
2780
3674
  "mouseup"
2781
3675
  ]);
2782
3676
  var isReactive = (node) => isReactiveShape(shapeOf(node));
3677
+ var CONTAINER2 = "container";
3678
+ function directive(node, name) {
3679
+ if (node.type !== "JSXElement") return void 0;
3680
+ const opening = node.openingElement;
3681
+ if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
3682
+ return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
3683
+ }
3684
+ function directiveExpr(attr) {
3685
+ const v = attr.value;
3686
+ return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
3687
+ }
3688
+ function emitIR(ir, ctx) {
3689
+ const { code, imports, templates } = imperativeBackend.emit(ir, {});
3690
+ for (const s of imports) ctx.used.add(s);
3691
+ return inlineTemplates(code, templates);
3692
+ }
2783
3693
  var posOf = (node, ctx) => {
2784
3694
  if (!ctx.sourceFile) return void 0;
2785
3695
  const at = node.loc?.start;
@@ -2857,9 +3767,12 @@ function attrExprCode(attr, ctx) {
2857
3767
  }
2858
3768
  return null;
2859
3769
  }
2860
- function buildProps(attrs, ctx) {
3770
+ function buildProps(attrs, ctx, element = false) {
2861
3771
  const props = [];
2862
3772
  const usePairs = [];
3773
+ const classEntries = [];
3774
+ const styleEntries = [];
3775
+ let styleAt;
2863
3776
  for (const a of attrs) {
2864
3777
  if (a.type === "JSXSpreadAttribute") {
2865
3778
  props.push({ name: "", kind: "spread", expr: ctx.code(a.argument) });
@@ -2875,6 +3788,27 @@ function buildProps(attrs, ctx) {
2875
3788
  usePairs.push(opts != null ? `[${dir}, () => ${paren(opts, "arrowBody")}]` : `[${dir}]`);
2876
3789
  continue;
2877
3790
  }
3791
+ if (element && (ns === "class" || ns === "style")) {
3792
+ const key = JSON.stringify(a.name.name.name);
3793
+ const v = a.value;
3794
+ let value;
3795
+ if (v == null) value = "true";
3796
+ else if (isString(v)) value = JSON.stringify(literalOf(v));
3797
+ else if (v.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression") {
3798
+ value = keyValue(v.expression, ctx);
3799
+ if (ns === "style") styleAt ??= posOf(v.expression, ctx);
3800
+ } else continue;
3801
+ (ns === "class" ? classEntries : styleEntries).push(`${key}: ${value}`);
3802
+ continue;
3803
+ }
3804
+ if (element && a.name.type === "JSXIdentifier" && a.name.name === "style" && a.value?.type === "JSXExpressionContainer") {
3805
+ const entries = objectEntries(a.value.expression, ctx);
3806
+ if (entries) {
3807
+ styleEntries.unshift(...entries);
3808
+ styleAt = posOf(a.value.expression, ctx);
3809
+ continue;
3810
+ }
3811
+ }
2878
3812
  if (ns === "oncapture") {
2879
3813
  const evt = a.name.name.name.toLowerCase();
2880
3814
  const h = attrExprCode(a, ctx) ?? "undefined";
@@ -2883,12 +3817,49 @@ function buildProps(attrs, ctx) {
2883
3817
  }
2884
3818
  props.push(buildAttr(a, ctx));
2885
3819
  }
3820
+ if (classEntries.length > 0) props.push({ name: "classList", kind: "attr", expr: `{ ${classEntries.join(", ")} }`, reactive: false });
3821
+ if (styleEntries.length > 0) {
3822
+ const entries = `{ ${styleEntries.join(", ")} }`;
3823
+ const base = props.findIndex((p) => p.name === "style" && p.kind === "attr");
3824
+ if (base === -1) props.push({ name: "style", kind: "attr", expr: entries, reactive: false, ...styleAt ? { at: styleAt } : {} });
3825
+ else {
3826
+ const prop = props[base];
3827
+ const value = prop.literal !== void 0 ? JSON.stringify(prop.literal) : prop.reactive ? `() => ${paren(prop.expr, "arrowBody")}` : prop.expr;
3828
+ props[base] = { name: "style", kind: "attr", expr: `[${value}, ${entries}]`, reactive: false, ...prop.at ? { at: prop.at } : {} };
3829
+ }
3830
+ }
2886
3831
  if (usePairs.length > 0) props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
2887
3832
  return props;
2888
3833
  }
3834
+ function keyValue(expr, ctx) {
3835
+ const code = ctx.code(expr);
3836
+ if (!isReactive(expr)) return code;
3837
+ const accessor = `() => ${paren(code, "arrowBody")}`;
3838
+ const at = posOf(expr, ctx);
3839
+ return at ? markHole(accessor, at.line, at.column) : accessor;
3840
+ }
3841
+ function objectEntries(expr, ctx) {
3842
+ if (expr?.type !== "ObjectExpression") return null;
3843
+ const entries = [];
3844
+ for (const p of expr.properties) {
3845
+ if (p.type !== "ObjectProperty") return null;
3846
+ const key = p.computed ? `[${ctx.code(p.key)}]` : p.key.type === "Identifier" ? JSON.stringify(p.key.name) : JSON.stringify(String(p.key.value));
3847
+ entries.push(`${key}: ${keyValue(p.value, ctx)}`);
3848
+ }
3849
+ return entries;
3850
+ }
2889
3851
  function buildChildren(children, ctx) {
2890
3852
  const out = [];
2891
- for (const child of children) {
3853
+ for (let i = 0; i < children.length; i++) {
3854
+ const child = children[i];
3855
+ if (directive(child, "if")) {
3856
+ let next = i + 1;
3857
+ while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
3858
+ const fallback = directive(children[next], "else") ? children[next] : null;
3859
+ out.push(buildIf(child, fallback, ctx));
3860
+ if (fallback) i = next;
3861
+ continue;
3862
+ }
2892
3863
  if (child.type === "JSXText") {
2893
3864
  const text = cleanTemplateText(child.value);
2894
3865
  if (text) out.push({ kind: "text", value: text });
@@ -2904,6 +3875,7 @@ function buildChildren(children, ctx) {
2904
3875
  });
2905
3876
  }
2906
3877
  } else if (child.type === "JSXElement" || child.type === "JSXFragment") {
3878
+ if (ctx.dropStyles?.has(child.start)) continue;
2907
3879
  out.push(buildNode(child, ctx));
2908
3880
  } else if (child.type === "JSXSpreadChild") {
2909
3881
  out.push({ kind: "expr", code: ctx.code(child.expression), reactive: false });
@@ -2925,19 +3897,72 @@ function readLoad(attrs) {
2925
3897
  }
2926
3898
  return void 0;
2927
3899
  }
3900
+ function buildIf(node, fallback, ctx) {
3901
+ const cond = directiveExpr(directive(node, "if"));
3902
+ const props = [
3903
+ cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
3904
+ ];
3905
+ if (fallback) {
3906
+ props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
3907
+ }
3908
+ return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
3909
+ }
3910
+ function buildEach(node, ctx) {
3911
+ const attr = directive(node, "each");
3912
+ if (!attr) return null;
3913
+ const list = directiveExpr(attr);
3914
+ const props = [
3915
+ list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
3916
+ ];
3917
+ const key = directive(node, "key");
3918
+ if (key) {
3919
+ const keyExpr = directiveExpr(key);
3920
+ const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
3921
+ if (by) props.push({ name: "by", kind: "attr", expr: by });
3922
+ }
3923
+ const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
3924
+ const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
3925
+ let render;
3926
+ if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
3927
+ const fn = ctx.code(only);
3928
+ const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
3929
+ render = live ? `(...args) => () => (${fn})(...args)` : fn;
3930
+ } else {
3931
+ const built = buildChildren(node.children, ctx);
3932
+ const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
3933
+ render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
3934
+ }
3935
+ return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
3936
+ }
3937
+ function buildElement(node, ctx, handled) {
3938
+ const skip = new Set(handled);
3939
+ const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
3940
+ const attributes = node.openingElement.attributes.filter(
3941
+ (a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
3942
+ );
3943
+ const { tag } = tagInfo(node.openingElement.name, ctx);
3944
+ const each = buildEach(node, ctx);
3945
+ const children = each ? [each] : buildChildren(node.children, ctx);
3946
+ if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
3947
+ const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
3948
+ const props = buildProps(attributes, ctx, true);
3949
+ const scope = ctx.scopes?.find((s) => node.start >= s.start && node.end <= s.end);
3950
+ if (scope) props.unshift({ name: "data-fx-s", kind: "attr", literal: scope.value });
3951
+ return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
3952
+ }
2928
3953
  function buildNode(node, ctx) {
2929
3954
  if (node.type === "JSXFragment") {
2930
3955
  return { kind: "fragment", children: buildChildren(node.children, ctx) };
2931
3956
  }
3957
+ if (directive(node, "if")) return buildIf(node, null, ctx);
3958
+ if (ctx.dropStyles?.has(node.start)) return { kind: "fragment", children: [] };
2932
3959
  const { tag, component } = tagInfo(node.openingElement.name, ctx);
3960
+ if (!component) return buildElement(node, ctx, []);
2933
3961
  const props = buildProps(node.openingElement.attributes, ctx);
2934
3962
  const children = buildChildren(node.children, ctx);
2935
3963
  const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
2936
- if (component) {
2937
- const load = readLoad(node.openingElement.attributes);
2938
- return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
2939
- }
2940
- return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
3964
+ const load = readLoad(node.openingElement.attributes);
3965
+ return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
2941
3966
  }
2942
3967
  function buildJsxIR(node, ctx) {
2943
3968
  const ir = buildNode(node, ctx);
@@ -3458,8 +4483,8 @@ function isJsxRoot(node, parent, key) {
3458
4483
  if (!isJsx(node)) return false;
3459
4484
  return !(parent && isJsx(parent) && key === "children");
3460
4485
  }
3461
- function isTemplateTag(node, litTag) {
3462
- return node.type === "TaggedTemplateExpression" && node.tag?.type === "Identifier" && (node.tag.name === litTag || node.tag.name === "svg");
4486
+ function isTemplateTag(node, tags) {
4487
+ return node.type === "TaggedTemplateExpression" && node.tag?.type === "Identifier" && tags.has(node.tag.name);
3463
4488
  }
3464
4489
  function outermost(list) {
3465
4490
  return list.filter(
@@ -3605,24 +4630,30 @@ function transformTemplates(code, id, options = {}) {
3605
4630
  const wantsJsx = format !== "lit";
3606
4631
  const mayHaveTemplate = wantsLit && (code.includes(`${litTag}\``) || code.includes("svg`"));
3607
4632
  const wantsIntrinsics = options.intrinsics !== false && mayHaveIntrinsic(code);
3608
- const wantsSources = options.sourceLocations === true && mayHaveReactiveCall(code);
4633
+ const wantsSources = options.sourceLocations === true && mayHaveSourceMarks(code);
3609
4634
  const sourceName = relativeToRoot(id, options.sourceRoot);
3610
- if (!mayHaveTemplate && !(wantsJsx && code.includes("<")) && !wantsIntrinsics && !wantsSources) return null;
4635
+ const wantsStyles = options.styles !== void 0 && (code.includes("<style") || code.includes("css`"));
4636
+ if (!mayHaveTemplate && !(wantsJsx && code.includes("<")) && !wantsIntrinsics && !wantsSources && !wantsStyles) return null;
3611
4637
  const ast = parse2(code, {
3612
4638
  sourceType: "module",
3613
4639
  plugins: ["jsx", "typescript"],
3614
4640
  errorRecovery: true
3615
4641
  });
4642
+ const intrinsicTags = options.intrinsics === false ? /* @__PURE__ */ new Map() : tagIntrinsics(moduleBindings(ast.program));
4643
+ const templateTags = /* @__PURE__ */ new Map([[litTag, "html"], ["svg", "svg"]]);
4644
+ for (const [name, tag] of intrinsicTags) if (tag !== "css") templateTags.set(name, tag);
4645
+ const cssTags = [...intrinsicTags].filter(([, tag]) => tag === "css").map(([name]) => name);
3616
4646
  const found = [];
3617
4647
  walk7(ast.program, (n2, parent, key) => {
3618
- if (wantsLit && isTemplateTag(n2, litTag)) found.push(n2);
4648
+ if (wantsLit && isTemplateTag(n2, templateTags)) found.push(n2);
3619
4649
  else if (wantsJsx && isJsxRoot(n2, parent, key)) found.push(n2);
3620
4650
  });
3621
4651
  const props = options.propsDestructure === false ? { edits: [], diagnostics: [], used: /* @__PURE__ */ new Set() } : collectPropsEdits(ast.program, code, { runtimeModule: options.runtimeModule });
3622
4652
  const intrinsics = options.intrinsics === false ? { edits: [], diagnostics: [], imports: /* @__PURE__ */ new Map() } : collectIntrinsicEdits(ast.program);
3623
4653
  if (intrinsics.edits.length) avoidBindingClashes(ast.program, intrinsics, moduleBindings(ast.program));
3624
4654
  const sources = options.sourceLocations === true ? collectSourceEdits(ast.program, sourceName) : { edits: [], marked: 0 };
3625
- const silent = found.length === 0 && props.edits.length === 0 && sources.edits.length === 0 && intrinsics.edits.length === 0 && intrinsics.diagnostics.length === 0 && props.diagnostics.length === 0;
4655
+ const styles = wantsStyles ? collectStyles(ast.program, sourceName, options.styles, { html: templateTags, css: cssTags }, { id, code }) : void 0;
4656
+ const silent = found.length === 0 && props.edits.length === 0 && sources.edits.length === 0 && intrinsics.edits.length === 0 && intrinsics.diagnostics.length === 0 && props.diagnostics.length === 0 && !styles?.edits.length && !styles?.diagnostics.length;
3626
4657
  if (silent) return null;
3627
4658
  found.sort((a, b) => b.start - a.start || a.end - b.end);
3628
4659
  const used = new Set(props.used);
@@ -3636,20 +4667,32 @@ function transformTemplates(code, id, options = {}) {
3636
4667
  declared: declaredNames(ast.program)
3637
4668
  };
3638
4669
  const hoisted = /* @__PURE__ */ new Map();
3639
- const edits = [...props.edits, ...intrinsics.edits, ...sources.edits];
4670
+ const edits = [...props.edits, ...intrinsics.edits, ...sources.edits, ...styles?.edits ?? []];
3640
4671
  for (const node of found) {
3641
4672
  const nested = found.some(
3642
4673
  (o) => o !== node && o.start <= node.start && o.end >= node.end && o.end - o.start > node.end - node.start
3643
4674
  );
3644
4675
  if (isJsx(node)) {
3645
- edits.push({ start: node.start, end: node.end, code: compileJsx(code, node, edits, nested, used, needs, hoisted, options, sourceName) });
4676
+ edits.push({ start: node.start, end: node.end, code: compileJsx(code, node, edits, nested, used, needs, hoisted, options, sourceName, styles) });
3646
4677
  continue;
3647
4678
  }
3648
4679
  const holes = node.quasi.expressions.map(
3649
4680
  (e) => bindHole(code, e, edits, options.sourceLocations === true)
3650
4681
  );
4682
+ const scope = styles?.scopes.find((s) => node.start >= s.start && node.end <= s.end)?.value;
4683
+ const owner = styles?.templateOwners.get(node.start);
4684
+ const templateStyles = options.styles && styles && (scope || owner) ? {
4685
+ options: options.styles,
4686
+ ...scope ? { scope } : {},
4687
+ ...owner ? { owner } : {},
4688
+ line: node.loc?.start.line ?? 0,
4689
+ column: node.loc?.start.column ?? 0,
4690
+ styles: styles.styles,
4691
+ diagnostics: styles.diagnostics
4692
+ } : void 0;
3651
4693
  const result = compilePieces(toPieces(node), holes, {
3652
4694
  ...options,
4695
+ ...templateStyles ? { templateStyles } : {},
3653
4696
  hoistTemplates: true,
3654
4697
  // A tag inside a template gets the same mark a JSX tag does. Without one, the nodes it
3655
4698
  // makes are the only ones in the module carrying no mark, and they take whichever was
@@ -3658,12 +4701,13 @@ function transformTemplates(code, id, options = {}) {
3658
4701
  // Same defaults as the plugin, so moving a build over is not a rendering change.
3659
4702
  templateClone: options.templateClone ?? true,
3660
4703
  partialTemplates: nested ? false : options.partialTemplates ?? true,
3661
- svg: node.tag.name === "svg"
4704
+ svg: templateTags.get(node.tag.name) === "svg"
3662
4705
  });
3663
4706
  for (const symbol of result.imports) used.add(symbol);
3664
4707
  collectNeeds(result.ir, needs);
3665
4708
  edits.push({ start: node.start, end: node.end, code: adoptTemplates(result.code, result.templates, hoisted) });
3666
4709
  }
4710
+ if (styles && options.styles) edits.push(...finishShadowStyles(styles, code, id));
3667
4711
  const out = new MagicString(code);
3668
4712
  for (const e of outermost(edits)) {
3669
4713
  if (e.start === e.end) out.appendLeft(e.start, e.code);
@@ -3721,13 +4765,19 @@ function transformTemplates(code, id, options = {}) {
3721
4765
  ...loadDiagnostics.length ? { loadDiagnostics } : {},
3722
4766
  ...unresolvedDiagnostics.length ? { unresolvedDiagnostics } : {},
3723
4767
  ...props.diagnostics.length ? { propsDiagnostics: props.diagnostics } : {},
3724
- ...intrinsics.diagnostics.length ? { intrinsicDiagnostics: intrinsics.diagnostics } : {}
4768
+ ...intrinsics.diagnostics.length ? { intrinsicDiagnostics: intrinsics.diagnostics } : {},
4769
+ ...styles?.styles.length ? { styles: styles.styles } : {},
4770
+ ...styles?.diagnostics.length ? { styleDiagnostics: styles.diagnostics } : {},
4771
+ ...styles?.literals.length ? { styleLiterals: styles.literals } : {},
4772
+ ...styles?.references.length ? { styleReferences: styles.references } : {}
3725
4773
  };
3726
4774
  }
3727
- function compileJsx(code, node, edits, nested, used, needs, hoisted, options, sourceFile) {
4775
+ function compileJsx(code, node, edits, nested, used, needs, hoisted, options, sourceFile, styles) {
3728
4776
  const ir = buildJsxIR(node, {
3729
4777
  code: (n2) => slice(code, n2.start, n2.end, edits),
3730
4778
  used,
4779
+ ...styles?.scopes.length ? { scopes: styles.scopes } : {},
4780
+ ...styles?.drop.size ? { dropStyles: styles.drop } : {},
3731
4781
  ...options.sourceLocations === true && sourceFile ? { sourceFile } : {}
3732
4782
  });
3733
4783
  analyzeStatic(ir);