@fluixi/compiler 1.0.0-alpha.93 → 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 (90) hide show
  1. package/dist/analyze/intrinsics.cjs +13 -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 +17 -0
  6. package/dist/analyze/intrinsics.mjs +13 -1
  7. package/dist/analyze/static-graph.cjs +1 -1
  8. package/dist/analyze/static-graph.mjs +1 -1
  9. package/dist/{babel-RJHJQVEM.mjs → babel-RJFQVFJW.mjs} +4 -4
  10. package/dist/{chunk-56QSOVVL.mjs → chunk-DP2QDWWH.mjs} +1 -1
  11. package/dist/{chunk-R7PPFBCL.mjs → chunk-E2RWTIYE.mjs} +21 -15
  12. package/dist/{chunk-OD53TMER.mjs → chunk-HCCBEJEZ.mjs} +13 -1
  13. package/dist/{chunk-TJIH7TLX.mjs → chunk-HIZU7WI2.mjs} +1 -1
  14. package/dist/{chunk-U7HCFLZ2.mjs → chunk-ZR2QHTEX.mjs} +356 -24
  15. package/dist/codegen/backends/imperative.cjs +1 -1
  16. package/dist/codegen/backends/imperative.d.ts.map +1 -1
  17. package/dist/codegen/backends/imperative.js +2 -1
  18. package/dist/codegen/backends/imperative.mjs +1 -1
  19. package/dist/frontend/babel/build-ir-lit.cjs +352 -22
  20. package/dist/frontend/babel/build-ir-lit.mjs +352 -22
  21. package/dist/frontend/babel/build-ir.cjs +352 -22
  22. package/dist/frontend/babel/build-ir.mjs +352 -22
  23. package/dist/frontend/babel/index.cjs +353 -23
  24. package/dist/frontend/babel/index.mjs +353 -23
  25. package/dist/frontend/babel/lower-template.cjs +352 -22
  26. package/dist/frontend/babel/lower-template.mjs +352 -22
  27. package/dist/frontend/babel/plugin.cjs +352 -22
  28. package/dist/frontend/babel/plugin.mjs +352 -22
  29. package/dist/index.cjs +321 -22
  30. package/dist/index.mjs +333 -22
  31. package/dist/integrations.cjs +1174 -49
  32. package/dist/integrations.d.ts.map +1 -1
  33. package/dist/integrations.js +44 -2
  34. package/dist/integrations.mjs +205 -19
  35. package/dist/lower/compile-template.cjs +315 -21
  36. package/dist/lower/compile-template.d.cts +13 -0
  37. package/dist/lower/compile-template.d.ts +13 -0
  38. package/dist/lower/compile-template.d.ts.map +1 -1
  39. package/dist/lower/compile-template.js +3 -0
  40. package/dist/lower/compile-template.mjs +327 -21
  41. package/dist/lower/entities.cjs +288 -0
  42. package/dist/lower/entities.d.cts +1 -0
  43. package/dist/lower/entities.d.ts +2 -0
  44. package/dist/lower/entities.d.ts.map +1 -0
  45. package/dist/lower/entities.js +54 -0
  46. package/dist/lower/entities.mjs +267 -0
  47. package/dist/lower/jsx.cjs +104 -47
  48. package/dist/lower/jsx.d.cts +8 -0
  49. package/dist/lower/jsx.d.ts +8 -0
  50. package/dist/lower/jsx.d.ts.map +1 -1
  51. package/dist/lower/jsx.js +92 -2
  52. package/dist/lower/jsx.mjs +104 -47
  53. package/dist/lower/template.cjs +293 -20
  54. package/dist/lower/template.d.ts.map +1 -1
  55. package/dist/lower/template.js +35 -19
  56. package/dist/lower/template.mjs +293 -20
  57. package/dist/resolve/builtins.cjs +1 -1
  58. package/dist/resolve/builtins.mjs +1 -1
  59. package/dist/resolve/component-globals.cjs +6 -1
  60. package/dist/resolve/component-globals.js +2 -2
  61. package/dist/resolve/component-globals.mjs +6 -1
  62. package/dist/resolve/write-globals.cjs +6 -1
  63. package/dist/resolve/write-globals.mjs +6 -1
  64. package/dist/{server-fns-ZFRDIQ2X.mjs → server-fns-JLSTKWPS.mjs} +4 -4
  65. package/dist/{server-functions-CTDXX67X.mjs → server-functions-BGY3RD4R.mjs} +3 -3
  66. package/dist/transform/index.cjs +956 -33
  67. package/dist/transform/index.mjs +974 -47
  68. package/dist/transform/props.mjs +19 -14
  69. package/dist/transform/server-fns.mjs +19 -14
  70. package/dist/transform/styles.cjs +588 -0
  71. package/dist/transform/styles.d.cts +211 -0
  72. package/dist/transform/styles.d.ts +212 -0
  73. package/dist/transform/styles.d.ts.map +1 -0
  74. package/dist/transform/styles.js +714 -0
  75. package/dist/transform/styles.mjs +653 -0
  76. package/dist/transform/templates.cjs +955 -32
  77. package/dist/transform/templates.d.cts +13 -0
  78. package/dist/transform/templates.d.ts +13 -0
  79. package/dist/transform/templates.d.ts.map +1 -1
  80. package/dist/transform/templates.js +49 -12
  81. package/dist/transform/templates.mjs +973 -46
  82. package/dist/{transform-QWWQDCXE.mjs → transform-GBP6Y4UI.mjs} +601 -17
  83. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  84. package/dist/vite-styles.cjs +180 -0
  85. package/dist/vite-styles.d.cts +59 -0
  86. package/dist/vite-styles.d.ts +60 -0
  87. package/dist/vite-styles.d.ts.map +1 -0
  88. package/dist/vite-styles.js +198 -0
  89. package/dist/vite-styles.mjs +159 -0
  90. package/package.json +4 -3
@@ -1,4 +1,4 @@
1
- /*! @fluixi/compiler v1.0.0-alpha.93 | (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) {
@@ -541,6 +1098,16 @@ var PRIMITIVE_MODULES = [
541
1098
  "@fluixi/reactive/cached-resource",
542
1099
  "@fluixi/core"
543
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
+ }
544
1111
  var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join("|")})\\s*\\(`);
545
1112
  function mayHaveIntrinsic(code) {
546
1113
  return INTRINSIC_CALL.test(code);
@@ -1044,7 +1611,7 @@ function emitComponent(name, props, children, used, templates, at) {
1044
1611
  }
1045
1612
  let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
1046
1613
  if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
1047
- const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
1614
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => (${body})`);
1048
1615
  const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
1049
1616
  obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
1050
1617
  }
@@ -1243,6 +1810,271 @@ function cleanTemplateText(value) {
1243
1810
  return out;
1244
1811
  }
1245
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
+
1246
2078
  // src/lower/template.ts
1247
2079
  var DELEGATED = /* @__PURE__ */ new Set([
1248
2080
  "click",
@@ -1329,7 +2161,7 @@ var Lowerer = class {
1329
2161
  switch (node.kind) {
1330
2162
  case "Text": {
1331
2163
  if (node.raw) return { kind: "text", value: node.value };
1332
- const cleaned = cleanTemplateText(node.value);
2164
+ const cleaned = decodeEntities(cleanTemplateText(node.value));
1333
2165
  return cleaned ? { kind: "text", value: cleaned } : null;
1334
2166
  }
1335
2167
  case "Comment":
@@ -1365,7 +2197,7 @@ var Lowerer = class {
1365
2197
  kind: "element",
1366
2198
  tag: el.tag,
1367
2199
  svg: el.namespace === "svg",
1368
- props: this.lowerAttributes(el.attributes),
2200
+ props: this.lowerAttributes(el.attributes, true),
1369
2201
  children,
1370
2202
  static: false,
1371
2203
  ...at ? { at } : {}
@@ -1478,13 +2310,22 @@ var Lowerer = class {
1478
2310
  return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
1479
2311
  }
1480
2312
  // --- attributes ---------------------------------------------------------
1481
- 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
+ };
1482
2325
  const props = [];
1483
2326
  const classEntries = [];
1484
- let classReactive = false;
1485
2327
  let classAt;
1486
2328
  const styleEntries = [];
1487
- let styleReactive = false;
1488
2329
  let styleAt;
1489
2330
  const usePairs = [];
1490
2331
  for (const a of attrs) {
@@ -1524,15 +2365,13 @@ var Lowerer = class {
1524
2365
  break;
1525
2366
  case "ClassDirective": {
1526
2367
  const h = this.hole(a.hole);
1527
- classEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
1528
- classReactive = classReactive || h.reactive;
2368
+ classEntries.push(entry(a.name, h));
1529
2369
  classAt ??= h.at;
1530
2370
  break;
1531
2371
  }
1532
2372
  case "StyleDirective": {
1533
2373
  const h = this.hole(a.hole);
1534
- styleEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
1535
- styleReactive = styleReactive || h.reactive;
2374
+ styleEntries.push(entry(a.name, h));
1536
2375
  styleAt ??= h.at;
1537
2376
  break;
1538
2377
  }
@@ -1552,18 +2391,19 @@ var Lowerer = class {
1552
2391
  name: "classList",
1553
2392
  kind: "attr",
1554
2393
  expr: `{ ${classEntries.join(", ")} }`,
1555
- reactive: classReactive,
2394
+ reactive: !element && anyReactive,
1556
2395
  ...classAt ? { at: classAt } : {}
1557
2396
  });
1558
2397
  }
1559
2398
  if (styleEntries.length > 0) {
1560
- props.push({
1561
- name: "style",
1562
- kind: "attr",
1563
- expr: `{ ${styleEntries.join(", ")} }`,
1564
- reactive: styleReactive,
1565
- ...styleAt ? { at: styleAt } : {}
1566
- });
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
+ }
1567
2407
  }
1568
2408
  if (usePairs.length > 0) {
1569
2409
  props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
@@ -1652,7 +2492,7 @@ var Lowerer = class {
1652
2492
  return base;
1653
2493
  }
1654
2494
  if (v.kind === "static") {
1655
- base.literal = v.value;
2495
+ base.literal = decodeEntities(v.value);
1656
2496
  return base;
1657
2497
  }
1658
2498
  if (v.kind === "hole") {
@@ -1665,7 +2505,7 @@ var Lowerer = class {
1665
2505
  let reactive = false;
1666
2506
  let at;
1667
2507
  const body = v.parts.map((p) => {
1668
- if ("text" in p) return escapeTemplate(p.text);
2508
+ if ("text" in p) return escapeTemplate(decodeEntities(p.text));
1669
2509
  const h = this.hole(p.hole);
1670
2510
  reactive = reactive || h.reactive;
1671
2511
  at ??= h.at;
@@ -1690,6 +2530,7 @@ function compilePieces(pieces, holes, options = {}) {
1690
2530
  ...options.sourceFile ? { sourceFile: options.sourceFile } : {},
1691
2531
  ...options.positionAt ? { positionAt: options.positionAt } : {}
1692
2532
  });
2533
+ if (options.templateStyles) applyTemplateStyles(ir, options.templateStyles);
1693
2534
  analyzeStatic(ir);
1694
2535
  resolveComponentSources(ir, {
1695
2536
  resolver: createComponentResolver(options.resolve ?? []),
@@ -1888,9 +2729,12 @@ function attrExprCode(attr, ctx) {
1888
2729
  }
1889
2730
  return null;
1890
2731
  }
1891
- function buildProps(attrs, ctx) {
2732
+ function buildProps(attrs, ctx, element = false) {
1892
2733
  const props = [];
1893
2734
  const usePairs = [];
2735
+ const classEntries = [];
2736
+ const styleEntries = [];
2737
+ let styleAt;
1894
2738
  for (const a of attrs) {
1895
2739
  if (a.type === "JSXSpreadAttribute") {
1896
2740
  props.push({ name: "", kind: "spread", expr: ctx.code(a.argument) });
@@ -1906,6 +2750,27 @@ function buildProps(attrs, ctx) {
1906
2750
  usePairs.push(opts != null ? `[${dir}, () => ${paren(opts, "arrowBody")}]` : `[${dir}]`);
1907
2751
  continue;
1908
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
+ }
1909
2774
  if (ns === "oncapture") {
1910
2775
  const evt = a.name.name.name.toLowerCase();
1911
2776
  const h = attrExprCode(a, ctx) ?? "undefined";
@@ -1914,9 +2779,37 @@ function buildProps(attrs, ctx) {
1914
2779
  }
1915
2780
  props.push(buildAttr(a, ctx));
1916
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
+ }
1917
2793
  if (usePairs.length > 0) props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
1918
2794
  return props;
1919
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
+ }
1920
2813
  function buildChildren(children, ctx) {
1921
2814
  const out = [];
1922
2815
  for (let i = 0; i < children.length; i++) {
@@ -1944,6 +2837,7 @@ function buildChildren(children, ctx) {
1944
2837
  });
1945
2838
  }
1946
2839
  } else if (child.type === "JSXElement" || child.type === "JSXFragment") {
2840
+ if (ctx.dropStyles?.has(child.start)) continue;
1947
2841
  out.push(buildNode(child, ctx));
1948
2842
  } else if (child.type === "JSXSpreadChild") {
1949
2843
  out.push({ kind: "expr", code: ctx.code(child.expression), reactive: false });
@@ -2013,13 +2907,17 @@ function buildElement(node, ctx, handled) {
2013
2907
  const children = each ? [each] : buildChildren(node.children, ctx);
2014
2908
  if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
2015
2909
  const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
2016
- return { kind: "element", tag, svg: SVG_TAGS.has(tag), props: buildProps(attributes, ctx), children, static: false, ...at ? { at } : {} };
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 } : {} };
2017
2914
  }
2018
2915
  function buildNode(node, ctx) {
2019
2916
  if (node.type === "JSXFragment") {
2020
2917
  return { kind: "fragment", children: buildChildren(node.children, ctx) };
2021
2918
  }
2022
2919
  if (directive(node, "if")) return buildIf(node, null, ctx);
2920
+ if (ctx.dropStyles?.has(node.start)) return { kind: "fragment", children: [] };
2023
2921
  const { tag, component } = tagInfo(node.openingElement.name, ctx);
2024
2922
  if (!component) return buildElement(node, ctx, []);
2025
2923
  const props = buildProps(node.openingElement.attributes, ctx);
@@ -2548,8 +3446,8 @@ function isJsxRoot(node, parent, key) {
2548
3446
  if (!isJsx(node)) return false;
2549
3447
  return !(parent && isJsx(parent) && key === "children");
2550
3448
  }
2551
- function isTemplateTag(node, litTag) {
2552
- 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);
2553
3451
  }
2554
3452
  function outermost(list) {
2555
3453
  return list.filter(
@@ -2697,22 +3595,28 @@ function transformTemplates(code, id, options = {}) {
2697
3595
  const wantsIntrinsics = options.intrinsics !== false && mayHaveIntrinsic(code);
2698
3596
  const wantsSources = options.sourceLocations === true && mayHaveSourceMarks(code);
2699
3597
  const sourceName = relativeToRoot(id, options.sourceRoot);
2700
- 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;
2701
3600
  const ast = (0, import_parser2.parse)(code, {
2702
3601
  sourceType: "module",
2703
3602
  plugins: ["jsx", "typescript"],
2704
3603
  errorRecovery: true
2705
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);
2706
3609
  const found = [];
2707
3610
  walk7(ast.program, (n, parent, key) => {
2708
- if (wantsLit && isTemplateTag(n, litTag)) found.push(n);
3611
+ if (wantsLit && isTemplateTag(n, templateTags)) found.push(n);
2709
3612
  else if (wantsJsx && isJsxRoot(n, parent, key)) found.push(n);
2710
3613
  });
2711
3614
  const props = options.propsDestructure === false ? { edits: [], diagnostics: [], used: /* @__PURE__ */ new Set() } : collectPropsEdits(ast.program, code, { runtimeModule: options.runtimeModule });
2712
3615
  const intrinsics = options.intrinsics === false ? { edits: [], diagnostics: [], imports: /* @__PURE__ */ new Map() } : collectIntrinsicEdits(ast.program);
2713
3616
  if (intrinsics.edits.length) avoidBindingClashes(ast.program, intrinsics, moduleBindings(ast.program));
2714
3617
  const sources = options.sourceLocations === true ? collectSourceEdits(ast.program, sourceName) : { edits: [], marked: 0 };
2715
- 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;
2716
3620
  if (silent) return null;
2717
3621
  found.sort((a, b) => b.start - a.start || a.end - b.end);
2718
3622
  const used = new Set(props.used);
@@ -2726,20 +3630,32 @@ function transformTemplates(code, id, options = {}) {
2726
3630
  declared: declaredNames(ast.program)
2727
3631
  };
2728
3632
  const hoisted = /* @__PURE__ */ new Map();
2729
- const edits = [...props.edits, ...intrinsics.edits, ...sources.edits];
3633
+ const edits = [...props.edits, ...intrinsics.edits, ...sources.edits, ...styles?.edits ?? []];
2730
3634
  for (const node of found) {
2731
3635
  const nested = found.some(
2732
3636
  (o) => o !== node && o.start <= node.start && o.end >= node.end && o.end - o.start > node.end - node.start
2733
3637
  );
2734
3638
  if (isJsx(node)) {
2735
- 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) });
2736
3640
  continue;
2737
3641
  }
2738
3642
  const holes = node.quasi.expressions.map(
2739
3643
  (e) => bindHole(code, e, edits, options.sourceLocations === true)
2740
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;
2741
3656
  const result = compilePieces(toPieces(node), holes, {
2742
3657
  ...options,
3658
+ ...templateStyles ? { templateStyles } : {},
2743
3659
  hoistTemplates: true,
2744
3660
  // A tag inside a template gets the same mark a JSX tag does. Without one, the nodes it
2745
3661
  // makes are the only ones in the module carrying no mark, and they take whichever was
@@ -2748,12 +3664,13 @@ function transformTemplates(code, id, options = {}) {
2748
3664
  // Same defaults as the plugin, so moving a build over is not a rendering change.
2749
3665
  templateClone: options.templateClone ?? true,
2750
3666
  partialTemplates: nested ? false : options.partialTemplates ?? true,
2751
- svg: node.tag.name === "svg"
3667
+ svg: templateTags.get(node.tag.name) === "svg"
2752
3668
  });
2753
3669
  for (const symbol of result.imports) used.add(symbol);
2754
3670
  collectNeeds(result.ir, needs);
2755
3671
  edits.push({ start: node.start, end: node.end, code: adoptTemplates(result.code, result.templates, hoisted) });
2756
3672
  }
3673
+ if (styles && options.styles) edits.push(...finishShadowStyles(styles, code, id));
2757
3674
  const out = new import_magic_string2.default(code);
2758
3675
  for (const e of outermost(edits)) {
2759
3676
  if (e.start === e.end) out.appendLeft(e.start, e.code);
@@ -2811,13 +3728,19 @@ function transformTemplates(code, id, options = {}) {
2811
3728
  ...loadDiagnostics.length ? { loadDiagnostics } : {},
2812
3729
  ...unresolvedDiagnostics.length ? { unresolvedDiagnostics } : {},
2813
3730
  ...props.diagnostics.length ? { propsDiagnostics: props.diagnostics } : {},
2814
- ...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 } : {}
2815
3736
  };
2816
3737
  }
2817
- function compileJsx(code, node, edits, nested, used, needs, hoisted, options, sourceFile) {
3738
+ function compileJsx(code, node, edits, nested, used, needs, hoisted, options, sourceFile, styles) {
2818
3739
  const ir = buildJsxIR(node, {
2819
3740
  code: (n) => slice(code, n.start, n.end, edits),
2820
3741
  used,
3742
+ ...styles?.scopes.length ? { scopes: styles.scopes } : {},
3743
+ ...styles?.drop.size ? { dropStyles: styles.drop } : {},
2821
3744
  ...options.sourceLocations === true && sourceFile ? { sourceFile } : {}
2822
3745
  });
2823
3746
  analyzeStatic(ir);