@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,4 +1,4 @@
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
  "use strict";
3
3
  var __create = Object.create;
4
4
  var __defProp = Object.defineProperty;
@@ -44,6 +44,563 @@ module.exports = __toCommonJS(index_exports);
44
44
  var import_parser2 = require("@babel/parser");
45
45
  var import_magic_string2 = __toESM(require("magic-string"), 1);
46
46
 
47
+ // src/transform/styles.ts
48
+ var import_sourcemap_codec = require("@jridgewell/sourcemap-codec");
49
+ var SCOPE_ATTR = "data-fx-s";
50
+ var FUNCTIONS = /* @__PURE__ */ new Set(["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression", "ObjectMethod", "ClassMethod"]);
51
+ function collectStyles(program, sourceName, options, tags = { html: /* @__PURE__ */ new Map([["html", "html"]]) }, module2) {
52
+ const plan = {
53
+ scopes: [],
54
+ templateOwners: /* @__PURE__ */ new Map(),
55
+ drop: /* @__PURE__ */ new Set(),
56
+ styles: [],
57
+ diagnostics: [],
58
+ edits: [],
59
+ literals: [],
60
+ references: [],
61
+ shadowClasses: /* @__PURE__ */ new Map()
62
+ };
63
+ if (!options) return plan;
64
+ const literalsOf = findLiterals(program, options.cssModules ?? ["@fluixi/core", "@fluixi/dom"], plan, tags.css ?? []);
65
+ plan.names = literalsOf;
66
+ const classOf = /* @__PURE__ */ new Map();
67
+ const shadowOf = (owner) => {
68
+ const cls = classOf.get(owner);
69
+ return cls ? plan.shadowClasses.get(cls) : void 0;
70
+ };
71
+ const resolved = /* @__PURE__ */ new Map();
72
+ const piecesToNodes = (quasis, expressions, at, stack) => {
73
+ const out = [];
74
+ for (let i = 0; i < quasis.length; i++) {
75
+ const text = quasis[i].value.cooked ?? quasis[i].value.raw;
76
+ if (text.trim()) {
77
+ try {
78
+ const start = quasis[i].loc?.start;
79
+ out.push(...options.process(text, sourceName).map((n) => start ? shiftLoc(n, sourceName, start) : n));
80
+ } catch (error) {
81
+ plan.diagnostics.push({ ...at, message: `CSS could not be processed: ${error.message}` });
82
+ return null;
83
+ }
84
+ }
85
+ const expr = expressions[i];
86
+ if (!expr) continue;
87
+ const value = expr.type === "Identifier" ? resolveLiteral(expr.name, at, stack) : void 0;
88
+ if (value === void 0) {
89
+ plan.diagnostics.push({
90
+ ...at,
91
+ 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))."
92
+ });
93
+ return null;
94
+ }
95
+ if (value === null) return null;
96
+ out.push(...value);
97
+ }
98
+ return out;
99
+ };
100
+ function resolveLiteral(name, at, stack) {
101
+ const imported = literalsOf.imports.get(name);
102
+ if (imported) {
103
+ const nodes = options?.imported?.get(`${imported.source}#${imported.name}`);
104
+ if (nodes) return nodes.map(dropLoc);
105
+ plan.references.push({ name: imported.name, source: imported.source, ...at });
106
+ plan.diagnostics.push({
107
+ ...at,
108
+ 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.`
109
+ });
110
+ return null;
111
+ }
112
+ const literal = literalsOf.literals.get(name);
113
+ if (!literal) return void 0;
114
+ if (stack.includes(name)) {
115
+ plan.diagnostics.push({ ...at, message: `css\`\` values include each other: ${[...stack, name].join(" → ")}.` });
116
+ return null;
117
+ }
118
+ if (!resolved.has(name)) {
119
+ resolved.set(name, piecesToNodes(literal.quasi.quasis, literal.quasi.expressions, at, [...stack, name]));
120
+ }
121
+ return resolved.get(name);
122
+ }
123
+ for (const [name, literal] of literalsOf.literals) {
124
+ const at = { line: literal.loc?.start.line ?? 0, column: literal.loc?.start.column ?? 0 };
125
+ const nodes = resolveLiteral(name, at, []);
126
+ const id = hash(`${sourceName}:css:${name}`);
127
+ plan.edits.push({ start: literal.start, end: literal.end, code: `({ $fxStyle: ${JSON.stringify(id)} })` });
128
+ if (nodes) plan.literals.push({ id, name, css: printStyle(nodes, options.minify) });
129
+ }
130
+ const scopeOf = /* @__PURE__ */ new Map();
131
+ const names = /* @__PURE__ */ new Map();
132
+ const named = /* @__PURE__ */ new Map();
133
+ const visit = (node, owners) => {
134
+ if (!node || typeof node !== "object") return;
135
+ if (node.type === "VariableDeclarator" && node.id.type === "Identifier" && node.init && FUNCTIONS.has(node.init.type)) {
136
+ named.set(node.init, node.id.name);
137
+ }
138
+ if ((node.type === "ClassDeclaration" || node.type === "ClassExpression") && node.id) {
139
+ for (const member of node.body.body) if (member.type === "ClassMethod") named.set(member, node.id.name);
140
+ }
141
+ if (node.type === "ClassDeclaration" || node.type === "ClassExpression") {
142
+ for (const member of node.body.body) if (member.type === "ClassMethod") classOf.set(member, node);
143
+ const shadow = staticMember(node, "shadow");
144
+ if (shadow?.value && !(shadow.value.type === "BooleanLiteral" && shadow.value.value === false)) {
145
+ plan.shadowClasses.set(node, { node, styles: staticMember(node, "styles"), indexes: [] });
146
+ }
147
+ }
148
+ if (node.type === "TaggedTemplateExpression" && node.tag.type === "Identifier" && tags.html.get(node.tag.name) === "html") {
149
+ const text = node.quasi.quasis.map((q) => q.value.raw).join("");
150
+ if (/<style[\s>]/.test(text)) {
151
+ const at = { line: node.loc?.start.line ?? 0, column: node.loc?.start.column ?? 0 };
152
+ const owner = owners[0];
153
+ if (!owner) plan.diagnostics.push({ ...at, message: "<style> has to be written inside a component." });
154
+ else {
155
+ const id = idFor(owner);
156
+ const shadow = shadowOf(owner);
157
+ plan.templateOwners.set(node.start, { id, component: functionName(owner, named) });
158
+ for (const block of templateBlocks(node.quasi)) compileBlock(block, block.scoped, owner, at);
159
+ }
160
+ }
161
+ }
162
+ const inner = FUNCTIONS.has(node.type) ? [...owners, node] : owners;
163
+ if (node.type === "JSXElement" && isStyle(node)) {
164
+ handle(node, owners);
165
+ return;
166
+ }
167
+ for (const key of Object.keys(node)) {
168
+ if (key === "loc" || key === "leadingComments" || key === "trailingComments" || key === "innerComments") continue;
169
+ const value = node[key];
170
+ if (Array.isArray(value)) for (const v of value) visit(v, inner);
171
+ else if (value && typeof value === "object" && "type" in value) visit(value, inner);
172
+ }
173
+ };
174
+ const handle = (node, owners) => {
175
+ const at = { line: node.loc?.start.line ?? 0, column: node.loc?.start.column ?? 0 };
176
+ const pieces = blockPieces(node);
177
+ if (pieces === null) {
178
+ plan.diagnostics.push({
179
+ ...at,
180
+ message: "<style> needs its CSS as one template literal, <style>{`...`}</style>: JSX reads a bare `{` as an expression."
181
+ });
182
+ return;
183
+ }
184
+ const owner = owners[0];
185
+ if (!owner) {
186
+ plan.diagnostics.push({ ...at, message: "<style> has to be written inside a component." });
187
+ return;
188
+ }
189
+ compileBlock(pieces, isScoped(node), owner, at);
190
+ plan.drop.add(node.start);
191
+ };
192
+ const compileBlock = (pieces, scoped, owner, at) => {
193
+ const component = functionName(owner, named);
194
+ const id = idFor(owner);
195
+ const processed = piecesToNodes(pieces.quasis, pieces.expressions, at, []);
196
+ if (!processed) return;
197
+ let nodes = processed;
198
+ const shadow = shadowOf(owner);
199
+ if (scoped && !shadow) {
200
+ if (!plan.scopes.some((s) => s.value === id)) plan.scopes.push({ start: owner.start, end: owner.end, value: id });
201
+ nodes = scopeNodes(nodes, id, (message) => plan.diagnostics.push({ ...at, message }));
202
+ }
203
+ const printed = module2 ? printStyleWithMap(nodes, options.minify, sourceName, module2) : { css: printStyle(nodes, options.minify) };
204
+ plan.styles.push({ id, scoped: scoped && !shadow, component, ...printed, ...shadow ? { shadow: true } : {}, ...at });
205
+ if (shadow) shadow.indexes.push(plan.styles.length - 1);
206
+ };
207
+ function idFor(owner) {
208
+ let id = scopeOf.get(owner);
209
+ if (!id) {
210
+ const component = functionName(owner, named);
211
+ const seen = names.get(component) ?? 0;
212
+ names.set(component, seen + 1);
213
+ id = hash(`${sourceName}:${component}${seen ? ":" + seen : ""}`);
214
+ scopeOf.set(owner, id);
215
+ }
216
+ return id;
217
+ }
218
+ visit(program, []);
219
+ return plan;
220
+ }
221
+ function applyTemplateStyles(ir, t) {
222
+ const walk8 = (node) => {
223
+ const children = node.children;
224
+ if (children) {
225
+ for (let i = children.length - 1; i >= 0; i--) {
226
+ const child = children[i];
227
+ if (child.kind === "element" && child.tag === "style" && t.owner) children.splice(i, 1);
228
+ else walk8(child);
229
+ }
230
+ }
231
+ if (t.scope && node.kind === "element" && node.tag !== "style" && node.props && !node.props.some((p) => p.name === SCOPE_ATTR)) {
232
+ node.props.unshift({ name: SCOPE_ATTR, kind: "attr", literal: t.scope });
233
+ }
234
+ };
235
+ walk8(ir);
236
+ }
237
+ function staticMember(cls, name) {
238
+ return cls.body.body.find(
239
+ (m) => m.type === "ClassProperty" && m.static && (m.key.type === "Identifier" && m.key.name === name || m.key.type === "StringLiteral" && m.key.value === name)
240
+ );
241
+ }
242
+ function finishShadowStyles(plan, code, moduleId) {
243
+ const edits = [];
244
+ const imports = [];
245
+ let next = 0;
246
+ const importOf = (specifier) => {
247
+ const local = `__fxShadow${next++}`;
248
+ imports.push(`import ${local} from ${JSON.stringify(specifier)};
249
+ `);
250
+ return local;
251
+ };
252
+ for (const shadow of plan.shadowClasses.values()) {
253
+ const blocks = shadow.indexes.map((i) => importOf(`${moduleId}?fx-style&index=${i}&inline&lang.css`));
254
+ let value;
255
+ const prop = shadow.styles;
256
+ if (prop?.value) {
257
+ const start = prop.value.start;
258
+ let text = code.slice(start, prop.value.end);
259
+ const replacements = [];
260
+ walkIdentifiers(prop.value, (id) => {
261
+ 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;
262
+ if (local) replacements.push({ at: id.start - start, length: id.name.length, with: local });
263
+ });
264
+ 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);
265
+ value = text;
266
+ }
267
+ if (!blocks.length && value === void 0) continue;
268
+ if (prop?.value) {
269
+ const merged = blocks.length ? `[${value}, ${blocks.join(", ")}]` : value;
270
+ edits.push({ start: prop.value.start, end: prop.value.end, code: merged });
271
+ } else if (blocks.length) {
272
+ const open = shadow.node.body.start + 1;
273
+ edits.push({ start: open, end: open, code: ` static styles = [${blocks.join(", ")}];` });
274
+ }
275
+ }
276
+ if (imports.length) edits.push({ start: 0, end: 0, code: imports.join("") });
277
+ return edits;
278
+ }
279
+ function walkIdentifiers(node, visit) {
280
+ if (!node || typeof node !== "object") return;
281
+ if (node.type === "Identifier") return visit(node);
282
+ if (node.type === "MemberExpression") return walkIdentifiers(node.object, visit);
283
+ if (node.type === "ObjectProperty") return walkIdentifiers(node.value, visit);
284
+ for (const key of Object.keys(node)) {
285
+ if (key === "loc") continue;
286
+ const value = node[key];
287
+ if (Array.isArray(value)) for (const v of value) walkIdentifiers(v, visit);
288
+ else if (value && typeof value === "object" && "type" in value) walkIdentifiers(value, visit);
289
+ }
290
+ }
291
+ function isStyle(node) {
292
+ const name = node.openingElement.name;
293
+ return name.type === "JSXIdentifier" && name.name === "style";
294
+ }
295
+ function isScoped(node) {
296
+ return node.openingElement.attributes.some((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === "scoped");
297
+ }
298
+ function templateBlocks(quasi) {
299
+ const blocks = [];
300
+ let open = null;
301
+ quasi.quasis.forEach((q, i) => {
302
+ const raw = q.value.raw;
303
+ let from = 0;
304
+ while (from <= raw.length) {
305
+ if (!open) {
306
+ const tag = /<style\b([^>]*)>/g;
307
+ tag.lastIndex = from;
308
+ const m = tag.exec(raw);
309
+ if (!m) break;
310
+ open = { quasis: [], expressions: [], scoped: /\bscoped\b/.test(m[1]) };
311
+ from = m.index + m[0].length;
312
+ }
313
+ const close = raw.indexOf("</style", from);
314
+ open.quasis.push(slicePiece(q, raw, from, close === -1 ? raw.length : close));
315
+ if (close === -1) break;
316
+ blocks.push(open);
317
+ open = null;
318
+ from = close;
319
+ }
320
+ const expr = quasi.expressions[i];
321
+ if (open && expr) open.expressions.push(expr);
322
+ });
323
+ return blocks;
324
+ }
325
+ function slicePiece(q, raw, from, to) {
326
+ const text = raw.slice(from, to);
327
+ const start = q.loc?.start;
328
+ if (!start) return { type: "TemplateElement", value: { raw: text, cooked: text } };
329
+ const before = raw.slice(0, from).split("\n");
330
+ const line = start.line + before.length - 1;
331
+ const column = before.length > 1 ? before[before.length - 1].length : start.column + from;
332
+ return { type: "TemplateElement", value: { raw: text, cooked: text }, loc: { start: { line, column } } };
333
+ }
334
+ function blockPieces(node) {
335
+ const meaningful = node.children.filter((c) => !(c.type === "JSXText" && !c.value.trim()));
336
+ if (meaningful.length === 0) return { quasis: [], expressions: [] };
337
+ if (meaningful.length !== 1) return null;
338
+ const only = meaningful[0];
339
+ if (only.type !== "JSXExpressionContainer") return null;
340
+ const expr = only.expression;
341
+ if (expr.type === "StringLiteral") return { quasis: [{ type: "TemplateElement", value: { raw: expr.value, cooked: expr.value } }], expressions: [] };
342
+ if (expr.type === "TemplateLiteral") return { quasis: expr.quasis, expressions: expr.expressions };
343
+ return null;
344
+ }
345
+ function findLiterals(program, modules, plan, intrinsic) {
346
+ const literals = /* @__PURE__ */ new Map();
347
+ const imports = /* @__PURE__ */ new Map();
348
+ const tags = new Set(intrinsic);
349
+ for (const statement of program.body ?? []) {
350
+ if (statement.type !== "ImportDeclaration") continue;
351
+ const source = statement.source.value;
352
+ const specifiers = statement.specifiers;
353
+ for (const sp of specifiers) {
354
+ if (sp.type === "ImportSpecifier") imports.set(sp.local.name, { source, name: sp.imported.name ?? sp.imported.value });
355
+ }
356
+ if (!modules.includes(source)) continue;
357
+ const css = specifiers.find((sp) => sp.type === "ImportSpecifier" && (sp.imported.name ?? sp.imported.value) === "css");
358
+ if (!css) continue;
359
+ const local = css.local.name;
360
+ tags.add(local);
361
+ imports.delete(local);
362
+ plan.edits.push({ start: statement.start, end: statement.end, code: importWithout(statement, css) });
363
+ }
364
+ if (tags.size === 0) return { literals, imports };
365
+ const visit = (node) => {
366
+ if (!node || typeof node !== "object") return;
367
+ if (node.type === "VariableDeclarator" && node.id.type === "Identifier" && node.init?.type === "TaggedTemplateExpression" && node.init.tag.type === "Identifier" && tags.has(node.init.tag.name)) {
368
+ literals.set(node.id.name, node.init);
369
+ }
370
+ for (const key of Object.keys(node)) {
371
+ if (key === "loc") continue;
372
+ const value = node[key];
373
+ if (Array.isArray(value)) for (const v of value) visit(v);
374
+ else if (value && typeof value === "object" && "type" in value) visit(value);
375
+ }
376
+ };
377
+ visit(program);
378
+ return { literals, imports };
379
+ }
380
+ function importWithout(statement, removed) {
381
+ const rest = statement.specifiers.filter((sp) => sp !== removed);
382
+ const source = JSON.stringify(statement.source.value);
383
+ if (rest.length === 0) return "";
384
+ const def = rest.find((sp) => sp.type === "ImportDefaultSpecifier");
385
+ const ns = rest.find((sp) => sp.type === "ImportNamespaceSpecifier");
386
+ const named = rest.filter((sp) => sp.type === "ImportSpecifier").map((sp) => {
387
+ const imported = sp.imported.name ?? JSON.stringify(sp.imported.value);
388
+ return imported === sp.local.name ? imported : `${imported} as ${sp.local.name}`;
389
+ });
390
+ const parts = [def?.local.name, ns ? `* as ${ns.local.name}` : void 0, named.length ? `{ ${named.join(", ")} }` : void 0].filter(Boolean);
391
+ return `import ${parts.join(", ")} from ${source};`;
392
+ }
393
+ function functionName(fn, named) {
394
+ if ((fn.type === "FunctionDeclaration" || fn.type === "FunctionExpression") && fn.id) return fn.id.name;
395
+ return named.get(fn) ?? "anonymous";
396
+ }
397
+ function hash(text) {
398
+ let h = 2166136261;
399
+ for (let i = 0; i < text.length; i++) {
400
+ h ^= text.charCodeAt(i);
401
+ h = Math.imul(h, 16777619);
402
+ }
403
+ return (h >>> 0).toString(36);
404
+ }
405
+ var OPAQUE = /(^|-)keyframes$|^font-face$|^property$|^counter-style$|^page$|^font-feature-values$/;
406
+ function scopeNodes(nodes, id, warn = () => {
407
+ }) {
408
+ return nodes.map((node) => {
409
+ if (node.kind === "rule") return { ...node, selector: scopeSelector(node.selector, id, warn) };
410
+ if (node.kind === "at-rule" && !OPAQUE.test(node.name)) return { ...node, nodes: scopeNodes(node.nodes, id, warn) };
411
+ return node;
412
+ });
413
+ }
414
+ function scopeSelector(selector, id, warn = () => {
415
+ }) {
416
+ return splitTop(selector, ",").map((complex) => scopeComplex(complex.trim(), `[${SCOPE_ATTR}="${id}"]`, warn)).join(", ");
417
+ }
418
+ function scopeComplex(selector, attr, warn) {
419
+ if (/:host\b|::slotted\(/.test(selector)) {
420
+ warn(`\`${selector}\` only means something in a shadow root; in a light-DOM component it is left as written.`);
421
+ return selector;
422
+ }
423
+ const parts = compounds(selector);
424
+ const deep = parts.findIndex((p) => p.text.startsWith(":deep("));
425
+ if (deep !== -1) {
426
+ const inner = unwrap(parts[deep].text, ":deep(");
427
+ const before = parts.slice(0, deep);
428
+ if (before.length === 0) return `${attr} ${inner}`;
429
+ const scopedBefore = scopeAt(before, before.length - 1, attr);
430
+ return `${join(scopedBefore)} ${inner}`;
431
+ }
432
+ let target = -1;
433
+ for (let i = parts.length - 1; i >= 0; i--) {
434
+ if (!parts[i].text.startsWith(":global(")) {
435
+ target = i;
436
+ break;
437
+ }
438
+ }
439
+ const out = target === -1 ? parts : scopeAt(parts, target, attr);
440
+ return join(out.map((p) => p.text.startsWith(":global(") ? { ...p, text: unwrap(p.text, ":global(") } : p));
441
+ }
442
+ function scopeAt(parts, index, attr) {
443
+ return parts.map((p, i) => i === index ? { ...p, text: insertAttr(p.text, attr) } : p);
444
+ }
445
+ function insertAttr(compound, attr) {
446
+ let depth = 0;
447
+ let quote = "";
448
+ for (let i = 0; i < compound.length; i++) {
449
+ const c = compound[i];
450
+ if (quote) {
451
+ if (c === "\\") i++;
452
+ else if (c === quote) quote = "";
453
+ continue;
454
+ }
455
+ if (c === "\\") {
456
+ i++;
457
+ continue;
458
+ }
459
+ if (c === '"' || c === "'") quote = c;
460
+ else if (c === "(" || c === "[") depth++;
461
+ else if (c === ")" || c === "]") depth--;
462
+ else if (c === ":" && depth === 0 && i > 0) return compound.slice(0, i) + attr + compound.slice(i);
463
+ }
464
+ return compound + attr;
465
+ }
466
+ function compounds(selector) {
467
+ const out = [];
468
+ let depth = 0;
469
+ let quote = "";
470
+ let current = "";
471
+ let combinator = "";
472
+ const push = () => {
473
+ if (current.trim()) out.push({ combinator: out.length ? combinator || " " : "", text: current.trim() });
474
+ current = "";
475
+ combinator = "";
476
+ };
477
+ for (let i = 0; i < selector.length; i++) {
478
+ const c = selector[i];
479
+ if (quote) {
480
+ current += c;
481
+ if (c === "\\") current += selector[++i] ?? "";
482
+ else if (c === quote) quote = "";
483
+ continue;
484
+ }
485
+ if (c === "\\") {
486
+ current += c + (selector[++i] ?? "");
487
+ continue;
488
+ }
489
+ if (c === '"' || c === "'") quote = c;
490
+ if (c === "(" || c === "[") depth++;
491
+ if (c === ")" || c === "]") depth--;
492
+ if (depth === 0 && (c === ">" || c === "+" || c === "~")) {
493
+ push();
494
+ combinator = c;
495
+ continue;
496
+ }
497
+ if (depth === 0 && /\s/.test(c)) {
498
+ if (current.trim()) {
499
+ const keep = combinator;
500
+ push();
501
+ combinator = keep;
502
+ }
503
+ continue;
504
+ }
505
+ current += c;
506
+ }
507
+ push();
508
+ return out;
509
+ }
510
+ function join(parts) {
511
+ return parts.map((p, i) => i === 0 ? p.text : p.combinator === " " ? ` ${p.text}` : ` ${p.combinator} ${p.text}`).join("");
512
+ }
513
+ function unwrap(text, prefix) {
514
+ return text.slice(prefix.length, text.lastIndexOf(")")).trim() + text.slice(text.lastIndexOf(")") + 1);
515
+ }
516
+ function splitTop(selector, separator) {
517
+ const parts = [];
518
+ let depth = 0;
519
+ let quote = "";
520
+ let start = 0;
521
+ for (let i = 0; i < selector.length; i++) {
522
+ const c = selector[i];
523
+ if (quote) {
524
+ if (c === "\\") i++;
525
+ else if (c === quote) quote = "";
526
+ } else if (c === '"' || c === "'") quote = c;
527
+ else if (c === "\\") i++;
528
+ else if (c === "(" || c === "[") depth++;
529
+ else if (c === ")" || c === "]") depth--;
530
+ else if (c === separator && depth === 0) {
531
+ parts.push(selector.slice(start, i));
532
+ start = i + 1;
533
+ }
534
+ }
535
+ parts.push(selector.slice(start));
536
+ return parts.filter((p) => p.trim());
537
+ }
538
+ function shiftLoc(node, source, start) {
539
+ 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;
540
+ if (node.kind === "rule" || node.kind === "at-rule") return { ...node, loc, nodes: node.nodes.map((n) => shiftLoc(n, source, start)) };
541
+ return { ...node, loc };
542
+ }
543
+ function dropLoc(node) {
544
+ const { loc: _loc, ...rest } = node;
545
+ if (rest.kind === "rule" || rest.kind === "at-rule") return { ...rest, nodes: rest.nodes.map(dropLoc) };
546
+ return rest;
547
+ }
548
+ function printStyleWithMap(nodes, minify, sourceName, module2) {
549
+ const lines = [[]];
550
+ let css = "";
551
+ let column = 0;
552
+ const emit = (text) => {
553
+ for (const ch of text) {
554
+ css += ch;
555
+ if (ch === "\n") {
556
+ lines.push([]);
557
+ column = 0;
558
+ } else column++;
559
+ }
560
+ };
561
+ const mark = (loc) => {
562
+ if (loc && loc.source === sourceName) lines[lines.length - 1].push([column, 0, loc.line - 1, loc.column]);
563
+ };
564
+ const write = (node, depth) => {
565
+ const pad = minify ? "" : " ".repeat(depth);
566
+ const nl = minify ? "" : "\n";
567
+ const sp = minify ? "" : " ";
568
+ emit(pad);
569
+ mark(node.loc);
570
+ if (node.kind === "declaration") {
571
+ emit(`${node.property}:${sp}${node.value}${node.important ? `${sp}!important` : ""};${nl}`);
572
+ } else if (node.kind === "raw") {
573
+ emit(`${node.text}${nl}`);
574
+ } else {
575
+ emit(`${node.kind === "rule" ? node.selector : `@${node.name}${node.params ? " " + node.params : ""}`}${sp}{${nl}`);
576
+ for (const child of node.nodes) write(child, depth + 1);
577
+ emit(`${pad}}${nl}`);
578
+ }
579
+ };
580
+ for (const node of nodes) write(node, 0);
581
+ return { css, map: { version: 3, sources: [module2.id], sourcesContent: [module2.code], names: [], mappings: (0, import_sourcemap_codec.encode)(lines) } };
582
+ }
583
+ function printStyle(nodes, minify = false) {
584
+ const out = [];
585
+ const write = (node, depth) => {
586
+ const pad = minify ? "" : " ".repeat(depth);
587
+ const nl = minify ? "" : "\n";
588
+ const sp = minify ? "" : " ";
589
+ if (node.kind === "declaration") {
590
+ out.push(`${pad}${node.property}:${sp}${node.value}${node.important ? `${sp}!important` : ""};${nl}`);
591
+ } else if (node.kind === "raw") {
592
+ out.push(`${pad}${node.text}${nl}`);
593
+ } else {
594
+ const header = node.kind === "rule" ? node.selector : `@${node.name}${node.params ? " " + node.params : ""}`;
595
+ out.push(`${pad}${header}${sp}{${nl}`);
596
+ for (const child of node.nodes) write(child, depth + 1);
597
+ out.push(`${pad}}${nl}`);
598
+ }
599
+ };
600
+ for (const node of nodes) write(node, 0);
601
+ return out.join("");
602
+ }
603
+
47
604
  // src/optimize/analyze-static.ts
48
605
  function propReason(prop) {
49
606
  switch (prop.kind) {
@@ -489,6 +1046,7 @@ var INTRINSICS = {
489
1046
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
490
1047
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
491
1048
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
1049
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
492
1050
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
493
1051
  // name. Still worth having: no import to write, and when a shorter form is
494
1052
  // designed, the change is this line rather than a compiler pass.
@@ -499,6 +1057,19 @@ var INTRINSICS = {
499
1057
  // dependency analysis must not guess at.
500
1058
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
501
1059
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
1060
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
1061
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
1062
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
1063
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
1064
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
1065
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
1066
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
1067
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
1068
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
1069
+ // handle as far as reads go: the same accessor with `set` on it.
1070
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
1071
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
1072
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
502
1073
  // An environment variable, public or server-only; the browser build refuses a secret.
503
1074
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
504
1075
  };
@@ -511,8 +1082,11 @@ var RUNTIME_PRIMITIVES = {
511
1082
  createMemo: "memo-accessor",
512
1083
  createEffect: "effect",
513
1084
  createRenderEffect: "effect",
1085
+ renderEffect: "effect",
1086
+ computed: "effect",
514
1087
  createStore: "reactive-object",
515
1088
  resource: "resource-handle",
1089
+ cachedResource: "resource-handle",
516
1090
  createResource: "reactive-object",
517
1091
  createSelector: "memo-accessor",
518
1092
  createDeferred: "memo-accessor"
@@ -521,8 +1095,19 @@ var PRIMITIVE_MODULES = [
521
1095
  "@fluixi/reactive",
522
1096
  "@fluixi/reactive/signal",
523
1097
  "@fluixi/reactive/store",
1098
+ "@fluixi/reactive/cached-resource",
524
1099
  "@fluixi/core"
525
1100
  ];
1101
+ var TAG_INTRINSICS = {
1102
+ $html: { export: "html", module: "@fluixi/core" },
1103
+ $svg: { export: "svg", module: "@fluixi/core" },
1104
+ $css: { export: "css", module: "@fluixi/core" }
1105
+ };
1106
+ function tagIntrinsics(bound) {
1107
+ const tags = /* @__PURE__ */ new Map();
1108
+ for (const [name, tag] of Object.entries(TAG_INTRINSICS)) if (!bound.has(name)) tags.set(name, tag.export);
1109
+ return tags;
1110
+ }
526
1111
  var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join("|")})\\s*\\(`);
527
1112
  function mayHaveIntrinsic(code) {
528
1113
  return INTRINSIC_CALL.test(code);
@@ -656,6 +1241,7 @@ function resolveReactiveBindings(program) {
656
1241
  // src/transform/source-locations.ts
657
1242
  var SOURCE_MARKER = "__fx_source";
658
1243
  var COMPONENT_MARKER = "__fx_component";
1244
+ var CLASS_MARKER = "__fx_class";
659
1245
  var HOLE_MARKER = "__fx_hole";
660
1246
  var markHole = (accessor, line, column, name) => `(globalThis.${HOLE_MARKER} ?? ((f) => f))(${accessor},${line},${column}${name === void 0 ? "" : `,${JSON.stringify(name)}`})`;
661
1247
  var PROPS_MARKER = "__fx_props";
@@ -673,11 +1259,12 @@ var RUNTIME_PRIMITIVES2 = /* @__PURE__ */ new Set([
673
1259
  "createStore",
674
1260
  "createResource"
675
1261
  ]);
676
- function mayHaveReactiveCall(code) {
1262
+ function mayHaveSourceMarks(code) {
677
1263
  for (const name of RUNTIME_PRIMITIVES2) {
678
1264
  if (code.includes(name)) return true;
679
1265
  }
680
- return code.includes("$signal") || code.includes("$memo") || code.includes("$effect") || code.includes("$store");
1266
+ if (code.includes("$signal") || code.includes("$memo") || code.includes("$effect") || code.includes("$store")) return true;
1267
+ return /\bclass\b[^{;]*\bextends\b/.test(code);
681
1268
  }
682
1269
  var SINGLE_STATEMENT_PARENTS = /* @__PURE__ */ new Set([
683
1270
  "IfStatement",
@@ -696,6 +1283,7 @@ function isLoneBranch(statement, parent) {
696
1283
  function isExport(node) {
697
1284
  return node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration";
698
1285
  }
1286
+ var CLASS_FIELDS = /* @__PURE__ */ new Set(["PropertyDefinition", "ClassProperty", "ClassPrivateProperty", "AccessorProperty"]);
699
1287
  function isStatement(node) {
700
1288
  return node.type.endsWith("Statement") || node.type === "VariableDeclaration";
701
1289
  }
@@ -716,6 +1304,14 @@ function walk3(node, visit, stack = []) {
716
1304
  }
717
1305
  function nameFor(stack) {
718
1306
  const declarator = stack[stack.length - 1];
1307
+ if (declarator && CLASS_FIELDS.has(declarator.type)) {
1308
+ const key = declarator.key;
1309
+ if (key?.type === "Identifier" || key?.type === "PrivateIdentifier" || key?.type === "PrivateName") {
1310
+ const name = key.name ?? key.id?.name;
1311
+ return name;
1312
+ }
1313
+ return void 0;
1314
+ }
719
1315
  if (declarator?.type !== "VariableDeclarator") return void 0;
720
1316
  const id = declarator.id;
721
1317
  if (id?.type === "Identifier") return id.name;
@@ -764,6 +1360,17 @@ function collectSourceEdits(program, filename) {
764
1360
  });
765
1361
  }
766
1362
  }
1363
+ if ((node.type === "ClassDeclaration" || node.type === "ClassExpression") && node.superClass && !node.declare) {
1364
+ const at = node.loc?.start;
1365
+ const body = node.body;
1366
+ if (at && body) {
1367
+ edits.push({
1368
+ start: body.start + 1,
1369
+ end: body.start + 1,
1370
+ code: `static{globalThis.${CLASS_MARKER}?.(this,${JSON.stringify(filename)},${at.line},${at.column});}`
1371
+ });
1372
+ }
1373
+ }
767
1374
  if (node.type !== "CallExpression") return;
768
1375
  const callee = node.callee;
769
1376
  if (callee?.type !== "Identifier") return;
@@ -773,6 +1380,26 @@ function collectSourceEdits(program, filename) {
773
1380
  if (!intrinsic && !imported) return;
774
1381
  const position = node.loc?.start;
775
1382
  if (!position) return;
1383
+ for (let i = stack.length - 1; i >= 0; i--) {
1384
+ const up = stack[i];
1385
+ if (isStatement(up)) break;
1386
+ if (CLASS_FIELDS.has(up.type)) {
1387
+ if (claimed.has(node.start)) return;
1388
+ claimed.add(node.start);
1389
+ const label2 = nameFor(stack);
1390
+ const own = !up.static;
1391
+ const args2 = [
1392
+ JSON.stringify(filename),
1393
+ String(position.line),
1394
+ String(position.column),
1395
+ label2 ? JSON.stringify(label2) : own ? "undefined" : void 0,
1396
+ own ? "this" : void 0
1397
+ ].filter((a) => a !== void 0).join(",");
1398
+ edits.push({ start: node.start, end: node.start, code: `(globalThis.${SOURCE_MARKER}?.(${args2}),` });
1399
+ edits.push({ start: node.end, end: node.end, code: ")" });
1400
+ return;
1401
+ }
1402
+ }
776
1403
  let statement;
777
1404
  let parent;
778
1405
  let index = -1;
@@ -978,13 +1605,16 @@ function emitComponent(name, props, children, used, templates, at) {
978
1605
  used.add("createComponent");
979
1606
  const spreads = props.filter((p) => p.kind === "spread");
980
1607
  const normal = props.filter((p) => p.kind !== "spread");
981
- const entries = normal.map(
982
- (p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
983
- );
1608
+ const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
984
1609
  if (children.length > 0) {
985
- entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
1610
+ bodies.push(["children", emitChildren(children, used, templates)]);
1611
+ }
1612
+ let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
1613
+ if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
1614
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => (${body})`);
1615
+ const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
1616
+ obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
986
1617
  }
987
- let obj = `{ ${entries.join(", ")} }`;
988
1618
  if (spreads.length > 0) {
989
1619
  used.add("mergeProps");
990
1620
  obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
@@ -1066,7 +1696,8 @@ function emitNode(node, used, templates) {
1066
1696
  } else {
1067
1697
  used.add("spread");
1068
1698
  const svgFlag = node.svg ? ", isSVG: true" : "";
1069
- const customFlag = node.tag.includes("-") ? ", isCustomElement: true" : "";
1699
+ const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
1700
+ const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
1070
1701
  parts.push(
1071
1702
  `spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
1072
1703
  );
@@ -1179,6 +1810,271 @@ function cleanTemplateText(value) {
1179
1810
  return out;
1180
1811
  }
1181
1812
 
1813
+ // src/lower/entities.ts
1814
+ var NAMED = {
1815
+ quot: 34,
1816
+ amp: 38,
1817
+ apos: 39,
1818
+ lt: 60,
1819
+ gt: 62,
1820
+ nbsp: 160,
1821
+ iexcl: 161,
1822
+ cent: 162,
1823
+ pound: 163,
1824
+ curren: 164,
1825
+ yen: 165,
1826
+ brvbar: 166,
1827
+ sect: 167,
1828
+ uml: 168,
1829
+ copy: 169,
1830
+ ordf: 170,
1831
+ laquo: 171,
1832
+ not: 172,
1833
+ shy: 173,
1834
+ reg: 174,
1835
+ macr: 175,
1836
+ deg: 176,
1837
+ plusmn: 177,
1838
+ sup2: 178,
1839
+ sup3: 179,
1840
+ acute: 180,
1841
+ micro: 181,
1842
+ para: 182,
1843
+ middot: 183,
1844
+ cedil: 184,
1845
+ sup1: 185,
1846
+ ordm: 186,
1847
+ raquo: 187,
1848
+ frac14: 188,
1849
+ frac12: 189,
1850
+ frac34: 190,
1851
+ iquest: 191,
1852
+ Agrave: 192,
1853
+ Aacute: 193,
1854
+ Acirc: 194,
1855
+ Atilde: 195,
1856
+ Auml: 196,
1857
+ Aring: 197,
1858
+ AElig: 198,
1859
+ Ccedil: 199,
1860
+ Egrave: 200,
1861
+ Eacute: 201,
1862
+ Ecirc: 202,
1863
+ Euml: 203,
1864
+ Igrave: 204,
1865
+ Iacute: 205,
1866
+ Icirc: 206,
1867
+ Iuml: 207,
1868
+ ETH: 208,
1869
+ Ntilde: 209,
1870
+ Ograve: 210,
1871
+ Oacute: 211,
1872
+ Ocirc: 212,
1873
+ Otilde: 213,
1874
+ Ouml: 214,
1875
+ times: 215,
1876
+ Oslash: 216,
1877
+ Ugrave: 217,
1878
+ Uacute: 218,
1879
+ Ucirc: 219,
1880
+ Uuml: 220,
1881
+ Yacute: 221,
1882
+ THORN: 222,
1883
+ szlig: 223,
1884
+ agrave: 224,
1885
+ aacute: 225,
1886
+ acirc: 226,
1887
+ atilde: 227,
1888
+ auml: 228,
1889
+ aring: 229,
1890
+ aelig: 230,
1891
+ ccedil: 231,
1892
+ egrave: 232,
1893
+ eacute: 233,
1894
+ ecirc: 234,
1895
+ euml: 235,
1896
+ igrave: 236,
1897
+ iacute: 237,
1898
+ icirc: 238,
1899
+ iuml: 239,
1900
+ eth: 240,
1901
+ ntilde: 241,
1902
+ ograve: 242,
1903
+ oacute: 243,
1904
+ ocirc: 244,
1905
+ otilde: 245,
1906
+ ouml: 246,
1907
+ divide: 247,
1908
+ oslash: 248,
1909
+ ugrave: 249,
1910
+ uacute: 250,
1911
+ ucirc: 251,
1912
+ uuml: 252,
1913
+ yacute: 253,
1914
+ thorn: 254,
1915
+ yuml: 255,
1916
+ OElig: 338,
1917
+ oelig: 339,
1918
+ Scaron: 352,
1919
+ scaron: 353,
1920
+ Yuml: 376,
1921
+ fnof: 402,
1922
+ circ: 710,
1923
+ tilde: 732,
1924
+ Alpha: 913,
1925
+ Beta: 914,
1926
+ Gamma: 915,
1927
+ Delta: 916,
1928
+ Epsilon: 917,
1929
+ Zeta: 918,
1930
+ Eta: 919,
1931
+ Theta: 920,
1932
+ Iota: 921,
1933
+ Kappa: 922,
1934
+ Lambda: 923,
1935
+ Mu: 924,
1936
+ Nu: 925,
1937
+ Xi: 926,
1938
+ Omicron: 927,
1939
+ Pi: 928,
1940
+ Rho: 929,
1941
+ Sigma: 931,
1942
+ Tau: 932,
1943
+ Upsilon: 933,
1944
+ Phi: 934,
1945
+ Chi: 935,
1946
+ Psi: 936,
1947
+ Omega: 937,
1948
+ alpha: 945,
1949
+ beta: 946,
1950
+ gamma: 947,
1951
+ delta: 948,
1952
+ epsilon: 949,
1953
+ zeta: 950,
1954
+ eta: 951,
1955
+ theta: 952,
1956
+ iota: 953,
1957
+ kappa: 954,
1958
+ lambda: 955,
1959
+ mu: 956,
1960
+ nu: 957,
1961
+ xi: 958,
1962
+ omicron: 959,
1963
+ pi: 960,
1964
+ rho: 961,
1965
+ sigmaf: 962,
1966
+ sigma: 963,
1967
+ tau: 964,
1968
+ upsilon: 965,
1969
+ phi: 966,
1970
+ chi: 967,
1971
+ psi: 968,
1972
+ omega: 969,
1973
+ thetasym: 977,
1974
+ upsih: 978,
1975
+ piv: 982,
1976
+ ensp: 8194,
1977
+ emsp: 8195,
1978
+ thinsp: 8201,
1979
+ zwnj: 8204,
1980
+ zwj: 8205,
1981
+ lrm: 8206,
1982
+ rlm: 8207,
1983
+ ndash: 8211,
1984
+ mdash: 8212,
1985
+ lsquo: 8216,
1986
+ rsquo: 8217,
1987
+ sbquo: 8218,
1988
+ ldquo: 8220,
1989
+ rdquo: 8221,
1990
+ bdquo: 8222,
1991
+ dagger: 8224,
1992
+ Dagger: 8225,
1993
+ bull: 8226,
1994
+ hellip: 8230,
1995
+ permil: 8240,
1996
+ prime: 8242,
1997
+ Prime: 8243,
1998
+ lsaquo: 8249,
1999
+ rsaquo: 8250,
2000
+ oline: 8254,
2001
+ frasl: 8260,
2002
+ euro: 8364,
2003
+ image: 8465,
2004
+ weierp: 8472,
2005
+ real: 8476,
2006
+ trade: 8482,
2007
+ alefsym: 8501,
2008
+ larr: 8592,
2009
+ uarr: 8593,
2010
+ rarr: 8594,
2011
+ darr: 8595,
2012
+ harr: 8596,
2013
+ crarr: 8629,
2014
+ lArr: 8656,
2015
+ uArr: 8657,
2016
+ rArr: 8658,
2017
+ dArr: 8659,
2018
+ hArr: 8660,
2019
+ forall: 8704,
2020
+ part: 8706,
2021
+ exist: 8707,
2022
+ empty: 8709,
2023
+ nabla: 8711,
2024
+ isin: 8712,
2025
+ notin: 8713,
2026
+ ni: 8715,
2027
+ prod: 8719,
2028
+ sum: 8721,
2029
+ minus: 8722,
2030
+ lowast: 8727,
2031
+ radic: 8730,
2032
+ prop: 8733,
2033
+ infin: 8734,
2034
+ ang: 8736,
2035
+ and: 8743,
2036
+ or: 8744,
2037
+ cap: 8745,
2038
+ cup: 8746,
2039
+ int: 8747,
2040
+ there4: 8756,
2041
+ sim: 8764,
2042
+ cong: 8773,
2043
+ asymp: 8776,
2044
+ ne: 8800,
2045
+ equiv: 8801,
2046
+ le: 8804,
2047
+ ge: 8805,
2048
+ sub: 8834,
2049
+ sup: 8835,
2050
+ nsub: 8836,
2051
+ sube: 8838,
2052
+ supe: 8839,
2053
+ oplus: 8853,
2054
+ otimes: 8855,
2055
+ perp: 8869,
2056
+ sdot: 8901,
2057
+ lceil: 8968,
2058
+ rceil: 8969,
2059
+ lfloor: 8970,
2060
+ rfloor: 8971,
2061
+ lang: 9001,
2062
+ rang: 9002,
2063
+ loz: 9674,
2064
+ spades: 9824,
2065
+ clubs: 9827,
2066
+ hearts: 9829,
2067
+ diams: 9830
2068
+ };
2069
+ var REFERENCE = /&(?:#[xX]([0-9a-fA-F]{1,6})|#([0-9]{1,7})|([a-zA-Z][a-zA-Z0-9]{1,31}));/g;
2070
+ function decodeEntities(value) {
2071
+ if (!value.includes("&")) return value;
2072
+ return value.replace(REFERENCE, (match, hex, dec, name) => {
2073
+ const code = hex ? parseInt(hex, 16) : dec ? parseInt(dec, 10) : Object.prototype.hasOwnProperty.call(NAMED, name) ? NAMED[name] : void 0;
2074
+ return code !== void 0 && code <= 1114111 ? String.fromCodePoint(code) : match;
2075
+ });
2076
+ }
2077
+
1182
2078
  // src/lower/template.ts
1183
2079
  var DELEGATED = /* @__PURE__ */ new Set([
1184
2080
  "click",
@@ -1197,6 +2093,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
1197
2093
  function escapeTemplate(s) {
1198
2094
  return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
1199
2095
  }
2096
+ var CONTAINER = "container";
1200
2097
  function capitalize(s) {
1201
2098
  return s.charAt(0).toUpperCase() + s.slice(1);
1202
2099
  }
@@ -1251,10 +2148,6 @@ var Lowerer = class {
1251
2148
  if (hasElse) i = elseIdx;
1252
2149
  continue;
1253
2150
  }
1254
- if (child.attributes.some((a) => a.kind === "EachDirective")) {
1255
- out.push(this.lowerEach(child));
1256
- continue;
1257
- }
1258
2151
  }
1259
2152
  const node = this.lowerNode(child);
1260
2153
  if (node) out.push(node);
@@ -1268,7 +2161,7 @@ var Lowerer = class {
1268
2161
  switch (node.kind) {
1269
2162
  case "Text": {
1270
2163
  if (node.raw) return { kind: "text", value: node.value };
1271
- const cleaned = cleanTemplateText(node.value);
2164
+ const cleaned = decodeEntities(cleanTemplateText(node.value));
1272
2165
  return cleaned ? { kind: "text", value: cleaned } : null;
1273
2166
  }
1274
2167
  case "Comment":
@@ -1296,13 +2189,16 @@ var Lowerer = class {
1296
2189
  node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
1297
2190
  return node;
1298
2191
  }
2192
+ const each = this.lowerEach(el);
2193
+ const children = each ? [each] : this.lowerChildren(el.children);
2194
+ if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
1299
2195
  const at = this.tagAt(el);
1300
2196
  return {
1301
2197
  kind: "element",
1302
2198
  tag: el.tag,
1303
2199
  svg: el.namespace === "svg",
1304
- props: this.lowerAttributes(el.attributes),
1305
- children: this.lowerChildren(el.children),
2200
+ props: this.lowerAttributes(el.attributes, true),
2201
+ children,
1306
2202
  static: false,
1307
2203
  ...at ? { at } : {}
1308
2204
  };
@@ -1374,10 +2270,14 @@ var Lowerer = class {
1374
2270
  const at = this.tagAt(el);
1375
2271
  return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
1376
2272
  }
1377
- /** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
2273
+ /**
2274
+ * `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
2275
+ * content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
2276
+ * it is. Null when the element has no `each`.
2277
+ */
1378
2278
  lowerEach(el) {
1379
2279
  const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
1380
- if (!eachDir || eachDir.kind !== "EachDirective") return this.lowerNode(el);
2280
+ if (!eachDir || eachDir.kind !== "EachDirective") return null;
1381
2281
  const each = this.hole(eachDir.hole);
1382
2282
  const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
1383
2283
  if (eachDir.key) {
@@ -1387,12 +2287,12 @@ var Lowerer = class {
1387
2287
  const arrow = this.itemArrow(el);
1388
2288
  let childrenCode;
1389
2289
  if (arrow) {
1390
- const bodyIR = { kind: "expr", code: arrow.body, reactive: arrow.bodyReactive };
1391
- const repeated = this.rebuildWithChildren(el, [bodyIR]);
1392
- childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
2290
+ const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
2291
+ childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
1393
2292
  } else {
1394
- const repeated = this.stripAndLower(el, (a) => a.kind === "EachDirective");
1395
- childrenCode = `() => ${paren(this.emit(repeated), "arrowBody")}`;
2293
+ const content = this.lowerChildren(el.children);
2294
+ const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
2295
+ childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
1396
2296
  }
1397
2297
  const children = [{ kind: "expr", code: childrenCode, reactive: false }];
1398
2298
  const at = this.tagAt(el);
@@ -1409,33 +2309,23 @@ var Lowerer = class {
1409
2309
  const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
1410
2310
  return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
1411
2311
  }
1412
- /** Same element (minus each/key) but with explicit IR children. */
1413
- rebuildWithChildren(el, children) {
1414
- const props = this.lowerAttributes(
1415
- el.attributes.filter((a) => a.kind !== "EachDirective")
1416
- );
1417
- if (el.kind === "Element") {
1418
- const at = this.tagAt(el);
1419
- return {
1420
- kind: "element",
1421
- tag: el.tag,
1422
- svg: el.namespace === "svg",
1423
- props,
1424
- children,
1425
- static: false,
1426
- ...at ? { at } : {}
1427
- };
1428
- }
1429
- return { kind: "component", name: el.tag, props, children };
1430
- }
1431
2312
  // --- attributes ---------------------------------------------------------
1432
- lowerAttributes(attrs) {
2313
+ /**
2314
+ * `element`: a DOM element, where each class:/style: value that moves is its own accessor.
2315
+ * A component gets the object as before, rebuilt whole when any value moves.
2316
+ */
2317
+ lowerAttributes(attrs, element = false) {
2318
+ let anyReactive = false;
2319
+ const entry = (name, h) => {
2320
+ anyReactive ||= h.reactive;
2321
+ if (!element || !h.reactive) return `${JSON.stringify(name)}: ${h.code}`;
2322
+ const accessor = `() => ${paren(h.code, "arrowBody")}`;
2323
+ return `${JSON.stringify(name)}: ${h.at ? markHole(accessor, h.at.line, h.at.column) : accessor}`;
2324
+ };
1433
2325
  const props = [];
1434
2326
  const classEntries = [];
1435
- let classReactive = false;
1436
2327
  let classAt;
1437
2328
  const styleEntries = [];
1438
- let styleReactive = false;
1439
2329
  let styleAt;
1440
2330
  const usePairs = [];
1441
2331
  for (const a of attrs) {
@@ -1475,15 +2365,13 @@ var Lowerer = class {
1475
2365
  break;
1476
2366
  case "ClassDirective": {
1477
2367
  const h = this.hole(a.hole);
1478
- classEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
1479
- classReactive = classReactive || h.reactive;
2368
+ classEntries.push(entry(a.name, h));
1480
2369
  classAt ??= h.at;
1481
2370
  break;
1482
2371
  }
1483
2372
  case "StyleDirective": {
1484
2373
  const h = this.hole(a.hole);
1485
- styleEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
1486
- styleReactive = styleReactive || h.reactive;
2374
+ styleEntries.push(entry(a.name, h));
1487
2375
  styleAt ??= h.at;
1488
2376
  break;
1489
2377
  }
@@ -1503,18 +2391,19 @@ var Lowerer = class {
1503
2391
  name: "classList",
1504
2392
  kind: "attr",
1505
2393
  expr: `{ ${classEntries.join(", ")} }`,
1506
- reactive: classReactive,
2394
+ reactive: !element && anyReactive,
1507
2395
  ...classAt ? { at: classAt } : {}
1508
2396
  });
1509
2397
  }
1510
2398
  if (styleEntries.length > 0) {
1511
- props.push({
1512
- name: "style",
1513
- kind: "attr",
1514
- expr: `{ ${styleEntries.join(", ")} }`,
1515
- reactive: styleReactive,
1516
- ...styleAt ? { at: styleAt } : {}
1517
- });
2399
+ const entries = `{ ${styleEntries.join(", ")} }`;
2400
+ const base = props.findIndex((p) => p.name === "style");
2401
+ if (base === -1) props.push({ name: "style", kind: "attr", expr: entries, reactive: !element && anyReactive, ...styleAt ? { at: styleAt } : {} });
2402
+ else {
2403
+ const prop = props[base];
2404
+ const value = prop.expr === void 0 ? JSON.stringify(prop.literal) : prop.reactive ? `() => ${paren(prop.expr, "arrowBody")}` : prop.expr;
2405
+ props[base] = { name: "style", kind: "attr", expr: `[${value}, ${entries}]`, reactive: false, ...prop.at ? { at: prop.at } : {} };
2406
+ }
1518
2407
  }
1519
2408
  if (usePairs.length > 0) {
1520
2409
  props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
@@ -1603,7 +2492,7 @@ var Lowerer = class {
1603
2492
  return base;
1604
2493
  }
1605
2494
  if (v.kind === "static") {
1606
- base.literal = v.value;
2495
+ base.literal = decodeEntities(v.value);
1607
2496
  return base;
1608
2497
  }
1609
2498
  if (v.kind === "hole") {
@@ -1616,7 +2505,7 @@ var Lowerer = class {
1616
2505
  let reactive = false;
1617
2506
  let at;
1618
2507
  const body = v.parts.map((p) => {
1619
- if ("text" in p) return escapeTemplate(p.text);
2508
+ if ("text" in p) return escapeTemplate(decodeEntities(p.text));
1620
2509
  const h = this.hole(p.hole);
1621
2510
  reactive = reactive || h.reactive;
1622
2511
  at ??= h.at;
@@ -1641,6 +2530,7 @@ function compilePieces(pieces, holes, options = {}) {
1641
2530
  ...options.sourceFile ? { sourceFile: options.sourceFile } : {},
1642
2531
  ...options.positionAt ? { positionAt: options.positionAt } : {}
1643
2532
  });
2533
+ if (options.templateStyles) applyTemplateStyles(ir, options.templateStyles);
1644
2534
  analyzeStatic(ir);
1645
2535
  resolveComponentSources(ir, {
1646
2536
  resolver: createComponentResolver(options.resolve ?? []),
@@ -1746,6 +2636,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
1746
2636
  "mouseup"
1747
2637
  ]);
1748
2638
  var isReactive = (node) => isReactiveShape(shapeOf(node));
2639
+ var CONTAINER2 = "container";
2640
+ function directive(node, name) {
2641
+ if (node.type !== "JSXElement") return void 0;
2642
+ const opening = node.openingElement;
2643
+ if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
2644
+ return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
2645
+ }
2646
+ function directiveExpr(attr) {
2647
+ const v = attr.value;
2648
+ return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
2649
+ }
2650
+ function emitIR(ir, ctx) {
2651
+ const { code, imports, templates } = imperativeBackend.emit(ir, {});
2652
+ for (const s of imports) ctx.used.add(s);
2653
+ return inlineTemplates(code, templates);
2654
+ }
1749
2655
  var posOf = (node, ctx) => {
1750
2656
  if (!ctx.sourceFile) return void 0;
1751
2657
  const at = node.loc?.start;
@@ -1823,9 +2729,12 @@ function attrExprCode(attr, ctx) {
1823
2729
  }
1824
2730
  return null;
1825
2731
  }
1826
- function buildProps(attrs, ctx) {
2732
+ function buildProps(attrs, ctx, element = false) {
1827
2733
  const props = [];
1828
2734
  const usePairs = [];
2735
+ const classEntries = [];
2736
+ const styleEntries = [];
2737
+ let styleAt;
1829
2738
  for (const a of attrs) {
1830
2739
  if (a.type === "JSXSpreadAttribute") {
1831
2740
  props.push({ name: "", kind: "spread", expr: ctx.code(a.argument) });
@@ -1841,6 +2750,27 @@ function buildProps(attrs, ctx) {
1841
2750
  usePairs.push(opts != null ? `[${dir}, () => ${paren(opts, "arrowBody")}]` : `[${dir}]`);
1842
2751
  continue;
1843
2752
  }
2753
+ if (element && (ns === "class" || ns === "style")) {
2754
+ const key = JSON.stringify(a.name.name.name);
2755
+ const v = a.value;
2756
+ let value;
2757
+ if (v == null) value = "true";
2758
+ else if (isString(v)) value = JSON.stringify(literalOf(v));
2759
+ else if (v.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression") {
2760
+ value = keyValue(v.expression, ctx);
2761
+ if (ns === "style") styleAt ??= posOf(v.expression, ctx);
2762
+ } else continue;
2763
+ (ns === "class" ? classEntries : styleEntries).push(`${key}: ${value}`);
2764
+ continue;
2765
+ }
2766
+ if (element && a.name.type === "JSXIdentifier" && a.name.name === "style" && a.value?.type === "JSXExpressionContainer") {
2767
+ const entries = objectEntries(a.value.expression, ctx);
2768
+ if (entries) {
2769
+ styleEntries.unshift(...entries);
2770
+ styleAt = posOf(a.value.expression, ctx);
2771
+ continue;
2772
+ }
2773
+ }
1844
2774
  if (ns === "oncapture") {
1845
2775
  const evt = a.name.name.name.toLowerCase();
1846
2776
  const h = attrExprCode(a, ctx) ?? "undefined";
@@ -1849,12 +2779,49 @@ function buildProps(attrs, ctx) {
1849
2779
  }
1850
2780
  props.push(buildAttr(a, ctx));
1851
2781
  }
2782
+ if (classEntries.length > 0) props.push({ name: "classList", kind: "attr", expr: `{ ${classEntries.join(", ")} }`, reactive: false });
2783
+ if (styleEntries.length > 0) {
2784
+ const entries = `{ ${styleEntries.join(", ")} }`;
2785
+ const base = props.findIndex((p) => p.name === "style" && p.kind === "attr");
2786
+ if (base === -1) props.push({ name: "style", kind: "attr", expr: entries, reactive: false, ...styleAt ? { at: styleAt } : {} });
2787
+ else {
2788
+ const prop = props[base];
2789
+ const value = prop.literal !== void 0 ? JSON.stringify(prop.literal) : prop.reactive ? `() => ${paren(prop.expr, "arrowBody")}` : prop.expr;
2790
+ props[base] = { name: "style", kind: "attr", expr: `[${value}, ${entries}]`, reactive: false, ...prop.at ? { at: prop.at } : {} };
2791
+ }
2792
+ }
1852
2793
  if (usePairs.length > 0) props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
1853
2794
  return props;
1854
2795
  }
2796
+ function keyValue(expr, ctx) {
2797
+ const code = ctx.code(expr);
2798
+ if (!isReactive(expr)) return code;
2799
+ const accessor = `() => ${paren(code, "arrowBody")}`;
2800
+ const at = posOf(expr, ctx);
2801
+ return at ? markHole(accessor, at.line, at.column) : accessor;
2802
+ }
2803
+ function objectEntries(expr, ctx) {
2804
+ if (expr?.type !== "ObjectExpression") return null;
2805
+ const entries = [];
2806
+ for (const p of expr.properties) {
2807
+ if (p.type !== "ObjectProperty") return null;
2808
+ const key = p.computed ? `[${ctx.code(p.key)}]` : p.key.type === "Identifier" ? JSON.stringify(p.key.name) : JSON.stringify(String(p.key.value));
2809
+ entries.push(`${key}: ${keyValue(p.value, ctx)}`);
2810
+ }
2811
+ return entries;
2812
+ }
1855
2813
  function buildChildren(children, ctx) {
1856
2814
  const out = [];
1857
- for (const child of children) {
2815
+ for (let i = 0; i < children.length; i++) {
2816
+ const child = children[i];
2817
+ if (directive(child, "if")) {
2818
+ let next = i + 1;
2819
+ while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
2820
+ const fallback = directive(children[next], "else") ? children[next] : null;
2821
+ out.push(buildIf(child, fallback, ctx));
2822
+ if (fallback) i = next;
2823
+ continue;
2824
+ }
1858
2825
  if (child.type === "JSXText") {
1859
2826
  const text = cleanTemplateText(child.value);
1860
2827
  if (text) out.push({ kind: "text", value: text });
@@ -1870,6 +2837,7 @@ function buildChildren(children, ctx) {
1870
2837
  });
1871
2838
  }
1872
2839
  } else if (child.type === "JSXElement" || child.type === "JSXFragment") {
2840
+ if (ctx.dropStyles?.has(child.start)) continue;
1873
2841
  out.push(buildNode(child, ctx));
1874
2842
  } else if (child.type === "JSXSpreadChild") {
1875
2843
  out.push({ kind: "expr", code: ctx.code(child.expression), reactive: false });
@@ -1891,19 +2859,72 @@ function readLoad(attrs) {
1891
2859
  }
1892
2860
  return void 0;
1893
2861
  }
2862
+ function buildIf(node, fallback, ctx) {
2863
+ const cond = directiveExpr(directive(node, "if"));
2864
+ const props = [
2865
+ cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
2866
+ ];
2867
+ if (fallback) {
2868
+ props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
2869
+ }
2870
+ return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
2871
+ }
2872
+ function buildEach(node, ctx) {
2873
+ const attr = directive(node, "each");
2874
+ if (!attr) return null;
2875
+ const list = directiveExpr(attr);
2876
+ const props = [
2877
+ list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
2878
+ ];
2879
+ const key = directive(node, "key");
2880
+ if (key) {
2881
+ const keyExpr = directiveExpr(key);
2882
+ const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
2883
+ if (by) props.push({ name: "by", kind: "attr", expr: by });
2884
+ }
2885
+ const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
2886
+ const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
2887
+ let render;
2888
+ if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
2889
+ const fn = ctx.code(only);
2890
+ const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
2891
+ render = live ? `(...args) => () => (${fn})(...args)` : fn;
2892
+ } else {
2893
+ const built = buildChildren(node.children, ctx);
2894
+ const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
2895
+ render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
2896
+ }
2897
+ return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
2898
+ }
2899
+ function buildElement(node, ctx, handled) {
2900
+ const skip = new Set(handled);
2901
+ const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
2902
+ const attributes = node.openingElement.attributes.filter(
2903
+ (a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
2904
+ );
2905
+ const { tag } = tagInfo(node.openingElement.name, ctx);
2906
+ const each = buildEach(node, ctx);
2907
+ const children = each ? [each] : buildChildren(node.children, ctx);
2908
+ if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
2909
+ const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
2910
+ const props = buildProps(attributes, ctx, true);
2911
+ const scope = ctx.scopes?.find((s) => node.start >= s.start && node.end <= s.end);
2912
+ if (scope) props.unshift({ name: "data-fx-s", kind: "attr", literal: scope.value });
2913
+ return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
2914
+ }
1894
2915
  function buildNode(node, ctx) {
1895
2916
  if (node.type === "JSXFragment") {
1896
2917
  return { kind: "fragment", children: buildChildren(node.children, ctx) };
1897
2918
  }
2919
+ if (directive(node, "if")) return buildIf(node, null, ctx);
2920
+ if (ctx.dropStyles?.has(node.start)) return { kind: "fragment", children: [] };
1898
2921
  const { tag, component } = tagInfo(node.openingElement.name, ctx);
2922
+ if (!component) return buildElement(node, ctx, []);
1899
2923
  const props = buildProps(node.openingElement.attributes, ctx);
1900
2924
  const children = buildChildren(node.children, ctx);
1901
2925
  const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1902
- if (component) {
1903
- const load = readLoad(node.openingElement.attributes);
1904
- return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1905
- }
1906
- return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
2926
+ const load = readLoad(node.openingElement.attributes);
2927
+ return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1907
2928
  }
1908
2929
  function buildJsxIR(node, ctx) {
1909
2930
  const ir = buildNode(node, ctx);
@@ -2425,8 +3446,8 @@ function isJsxRoot(node, parent, key) {
2425
3446
  if (!isJsx(node)) return false;
2426
3447
  return !(parent && isJsx(parent) && key === "children");
2427
3448
  }
2428
- function isTemplateTag(node, litTag) {
2429
- return node.type === "TaggedTemplateExpression" && node.tag?.type === "Identifier" && (node.tag.name === litTag || node.tag.name === "svg");
3449
+ function isTemplateTag(node, tags) {
3450
+ return node.type === "TaggedTemplateExpression" && node.tag?.type === "Identifier" && tags.has(node.tag.name);
2430
3451
  }
2431
3452
  function outermost(list) {
2432
3453
  return list.filter(
@@ -2572,24 +3593,30 @@ function transformTemplates(code, id, options = {}) {
2572
3593
  const wantsJsx = format !== "lit";
2573
3594
  const mayHaveTemplate = wantsLit && (code.includes(`${litTag}\``) || code.includes("svg`"));
2574
3595
  const wantsIntrinsics = options.intrinsics !== false && mayHaveIntrinsic(code);
2575
- const wantsSources = options.sourceLocations === true && mayHaveReactiveCall(code);
3596
+ const wantsSources = options.sourceLocations === true && mayHaveSourceMarks(code);
2576
3597
  const sourceName = relativeToRoot(id, options.sourceRoot);
2577
- if (!mayHaveTemplate && !(wantsJsx && code.includes("<")) && !wantsIntrinsics && !wantsSources) return null;
3598
+ const wantsStyles = options.styles !== void 0 && (code.includes("<style") || code.includes("css`"));
3599
+ if (!mayHaveTemplate && !(wantsJsx && code.includes("<")) && !wantsIntrinsics && !wantsSources && !wantsStyles) return null;
2578
3600
  const ast = (0, import_parser2.parse)(code, {
2579
3601
  sourceType: "module",
2580
3602
  plugins: ["jsx", "typescript"],
2581
3603
  errorRecovery: true
2582
3604
  });
3605
+ const intrinsicTags = options.intrinsics === false ? /* @__PURE__ */ new Map() : tagIntrinsics(moduleBindings(ast.program));
3606
+ const templateTags = /* @__PURE__ */ new Map([[litTag, "html"], ["svg", "svg"]]);
3607
+ for (const [name, tag] of intrinsicTags) if (tag !== "css") templateTags.set(name, tag);
3608
+ const cssTags = [...intrinsicTags].filter(([, tag]) => tag === "css").map(([name]) => name);
2583
3609
  const found = [];
2584
3610
  walk7(ast.program, (n, parent, key) => {
2585
- if (wantsLit && isTemplateTag(n, litTag)) found.push(n);
3611
+ if (wantsLit && isTemplateTag(n, templateTags)) found.push(n);
2586
3612
  else if (wantsJsx && isJsxRoot(n, parent, key)) found.push(n);
2587
3613
  });
2588
3614
  const props = options.propsDestructure === false ? { edits: [], diagnostics: [], used: /* @__PURE__ */ new Set() } : collectPropsEdits(ast.program, code, { runtimeModule: options.runtimeModule });
2589
3615
  const intrinsics = options.intrinsics === false ? { edits: [], diagnostics: [], imports: /* @__PURE__ */ new Map() } : collectIntrinsicEdits(ast.program);
2590
3616
  if (intrinsics.edits.length) avoidBindingClashes(ast.program, intrinsics, moduleBindings(ast.program));
2591
3617
  const sources = options.sourceLocations === true ? collectSourceEdits(ast.program, sourceName) : { edits: [], marked: 0 };
2592
- 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;
3618
+ const styles = wantsStyles ? collectStyles(ast.program, sourceName, options.styles, { html: templateTags, css: cssTags }, { id, code }) : void 0;
3619
+ 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;
2593
3620
  if (silent) return null;
2594
3621
  found.sort((a, b) => b.start - a.start || a.end - b.end);
2595
3622
  const used = new Set(props.used);
@@ -2603,20 +3630,32 @@ function transformTemplates(code, id, options = {}) {
2603
3630
  declared: declaredNames(ast.program)
2604
3631
  };
2605
3632
  const hoisted = /* @__PURE__ */ new Map();
2606
- const edits = [...props.edits, ...intrinsics.edits, ...sources.edits];
3633
+ const edits = [...props.edits, ...intrinsics.edits, ...sources.edits, ...styles?.edits ?? []];
2607
3634
  for (const node of found) {
2608
3635
  const nested = found.some(
2609
3636
  (o) => o !== node && o.start <= node.start && o.end >= node.end && o.end - o.start > node.end - node.start
2610
3637
  );
2611
3638
  if (isJsx(node)) {
2612
- edits.push({ start: node.start, end: node.end, code: compileJsx(code, node, edits, nested, used, needs, hoisted, options, sourceName) });
3639
+ edits.push({ start: node.start, end: node.end, code: compileJsx(code, node, edits, nested, used, needs, hoisted, options, sourceName, styles) });
2613
3640
  continue;
2614
3641
  }
2615
3642
  const holes = node.quasi.expressions.map(
2616
3643
  (e) => bindHole(code, e, edits, options.sourceLocations === true)
2617
3644
  );
3645
+ const scope = styles?.scopes.find((s) => node.start >= s.start && node.end <= s.end)?.value;
3646
+ const owner = styles?.templateOwners.get(node.start);
3647
+ const templateStyles = options.styles && styles && (scope || owner) ? {
3648
+ options: options.styles,
3649
+ ...scope ? { scope } : {},
3650
+ ...owner ? { owner } : {},
3651
+ line: node.loc?.start.line ?? 0,
3652
+ column: node.loc?.start.column ?? 0,
3653
+ styles: styles.styles,
3654
+ diagnostics: styles.diagnostics
3655
+ } : void 0;
2618
3656
  const result = compilePieces(toPieces(node), holes, {
2619
3657
  ...options,
3658
+ ...templateStyles ? { templateStyles } : {},
2620
3659
  hoistTemplates: true,
2621
3660
  // A tag inside a template gets the same mark a JSX tag does. Without one, the nodes it
2622
3661
  // makes are the only ones in the module carrying no mark, and they take whichever was
@@ -2625,12 +3664,13 @@ function transformTemplates(code, id, options = {}) {
2625
3664
  // Same defaults as the plugin, so moving a build over is not a rendering change.
2626
3665
  templateClone: options.templateClone ?? true,
2627
3666
  partialTemplates: nested ? false : options.partialTemplates ?? true,
2628
- svg: node.tag.name === "svg"
3667
+ svg: templateTags.get(node.tag.name) === "svg"
2629
3668
  });
2630
3669
  for (const symbol of result.imports) used.add(symbol);
2631
3670
  collectNeeds(result.ir, needs);
2632
3671
  edits.push({ start: node.start, end: node.end, code: adoptTemplates(result.code, result.templates, hoisted) });
2633
3672
  }
3673
+ if (styles && options.styles) edits.push(...finishShadowStyles(styles, code, id));
2634
3674
  const out = new import_magic_string2.default(code);
2635
3675
  for (const e of outermost(edits)) {
2636
3676
  if (e.start === e.end) out.appendLeft(e.start, e.code);
@@ -2688,13 +3728,19 @@ function transformTemplates(code, id, options = {}) {
2688
3728
  ...loadDiagnostics.length ? { loadDiagnostics } : {},
2689
3729
  ...unresolvedDiagnostics.length ? { unresolvedDiagnostics } : {},
2690
3730
  ...props.diagnostics.length ? { propsDiagnostics: props.diagnostics } : {},
2691
- ...intrinsics.diagnostics.length ? { intrinsicDiagnostics: intrinsics.diagnostics } : {}
3731
+ ...intrinsics.diagnostics.length ? { intrinsicDiagnostics: intrinsics.diagnostics } : {},
3732
+ ...styles?.styles.length ? { styles: styles.styles } : {},
3733
+ ...styles?.diagnostics.length ? { styleDiagnostics: styles.diagnostics } : {},
3734
+ ...styles?.literals.length ? { styleLiterals: styles.literals } : {},
3735
+ ...styles?.references.length ? { styleReferences: styles.references } : {}
2692
3736
  };
2693
3737
  }
2694
- function compileJsx(code, node, edits, nested, used, needs, hoisted, options, sourceFile) {
3738
+ function compileJsx(code, node, edits, nested, used, needs, hoisted, options, sourceFile, styles) {
2695
3739
  const ir = buildJsxIR(node, {
2696
3740
  code: (n) => slice(code, n.start, n.end, edits),
2697
3741
  used,
3742
+ ...styles?.scopes.length ? { scopes: styles.scopes } : {},
3743
+ ...styles?.drop.size ? { dropStyles: styles.drop } : {},
2698
3744
  ...options.sourceLocations === true && sourceFile ? { sourceFile } : {}
2699
3745
  });
2700
3746
  analyzeStatic(ir);