@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
@@ -36,6 +36,563 @@ module.exports = __toCommonJS(templates_exports);
36
36
  var import_parser2 = require("@babel/parser");
37
37
  var import_magic_string2 = __toESM(require("magic-string"), 1);
38
38
 
39
+ // src/transform/styles.ts
40
+ var import_sourcemap_codec = require("@jridgewell/sourcemap-codec");
41
+ var SCOPE_ATTR = "data-fx-s";
42
+ var FUNCTIONS = /* @__PURE__ */ new Set(["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression", "ObjectMethod", "ClassMethod"]);
43
+ function collectStyles(program, sourceName, options, tags = { html: /* @__PURE__ */ new Map([["html", "html"]]) }, module2) {
44
+ const plan = {
45
+ scopes: [],
46
+ templateOwners: /* @__PURE__ */ new Map(),
47
+ drop: /* @__PURE__ */ new Set(),
48
+ styles: [],
49
+ diagnostics: [],
50
+ edits: [],
51
+ literals: [],
52
+ references: [],
53
+ shadowClasses: /* @__PURE__ */ new Map()
54
+ };
55
+ if (!options) return plan;
56
+ const literalsOf = findLiterals(program, options.cssModules ?? ["@fluixi/core", "@fluixi/dom"], plan, tags.css ?? []);
57
+ plan.names = literalsOf;
58
+ const classOf = /* @__PURE__ */ new Map();
59
+ const shadowOf = (owner) => {
60
+ const cls = classOf.get(owner);
61
+ return cls ? plan.shadowClasses.get(cls) : void 0;
62
+ };
63
+ const resolved = /* @__PURE__ */ new Map();
64
+ const piecesToNodes = (quasis, expressions, at, stack) => {
65
+ const out = [];
66
+ for (let i = 0; i < quasis.length; i++) {
67
+ const text = quasis[i].value.cooked ?? quasis[i].value.raw;
68
+ if (text.trim()) {
69
+ try {
70
+ const start = quasis[i].loc?.start;
71
+ out.push(...options.process(text, sourceName).map((n) => start ? shiftLoc(n, sourceName, start) : n));
72
+ } catch (error) {
73
+ plan.diagnostics.push({ ...at, message: `CSS could not be processed: ${error.message}` });
74
+ return null;
75
+ }
76
+ }
77
+ const expr = expressions[i];
78
+ if (!expr) continue;
79
+ const value = expr.type === "Identifier" ? resolveLiteral(expr.name, at, stack) : void 0;
80
+ if (value === void 0) {
81
+ plan.diagnostics.push({
82
+ ...at,
83
+ 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))."
84
+ });
85
+ return null;
86
+ }
87
+ if (value === null) return null;
88
+ out.push(...value);
89
+ }
90
+ return out;
91
+ };
92
+ function resolveLiteral(name, at, stack) {
93
+ const imported = literalsOf.imports.get(name);
94
+ if (imported) {
95
+ const nodes = options?.imported?.get(`${imported.source}#${imported.name}`);
96
+ if (nodes) return nodes.map(dropLoc);
97
+ plan.references.push({ name: imported.name, source: imported.source, ...at });
98
+ plan.diagnostics.push({
99
+ ...at,
100
+ 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.`
101
+ });
102
+ return null;
103
+ }
104
+ const literal = literalsOf.literals.get(name);
105
+ if (!literal) return void 0;
106
+ if (stack.includes(name)) {
107
+ plan.diagnostics.push({ ...at, message: `css\`\` values include each other: ${[...stack, name].join(" → ")}.` });
108
+ return null;
109
+ }
110
+ if (!resolved.has(name)) {
111
+ resolved.set(name, piecesToNodes(literal.quasi.quasis, literal.quasi.expressions, at, [...stack, name]));
112
+ }
113
+ return resolved.get(name);
114
+ }
115
+ for (const [name, literal] of literalsOf.literals) {
116
+ const at = { line: literal.loc?.start.line ?? 0, column: literal.loc?.start.column ?? 0 };
117
+ const nodes = resolveLiteral(name, at, []);
118
+ const id = hash(`${sourceName}:css:${name}`);
119
+ plan.edits.push({ start: literal.start, end: literal.end, code: `({ $fxStyle: ${JSON.stringify(id)} })` });
120
+ if (nodes) plan.literals.push({ id, name, css: printStyle(nodes, options.minify) });
121
+ }
122
+ const scopeOf = /* @__PURE__ */ new Map();
123
+ const names = /* @__PURE__ */ new Map();
124
+ const named = /* @__PURE__ */ new Map();
125
+ const visit = (node, owners) => {
126
+ if (!node || typeof node !== "object") return;
127
+ if (node.type === "VariableDeclarator" && node.id.type === "Identifier" && node.init && FUNCTIONS.has(node.init.type)) {
128
+ named.set(node.init, node.id.name);
129
+ }
130
+ if ((node.type === "ClassDeclaration" || node.type === "ClassExpression") && node.id) {
131
+ for (const member of node.body.body) if (member.type === "ClassMethod") named.set(member, node.id.name);
132
+ }
133
+ if (node.type === "ClassDeclaration" || node.type === "ClassExpression") {
134
+ for (const member of node.body.body) if (member.type === "ClassMethod") classOf.set(member, node);
135
+ const shadow = staticMember(node, "shadow");
136
+ if (shadow?.value && !(shadow.value.type === "BooleanLiteral" && shadow.value.value === false)) {
137
+ plan.shadowClasses.set(node, { node, styles: staticMember(node, "styles"), indexes: [] });
138
+ }
139
+ }
140
+ if (node.type === "TaggedTemplateExpression" && node.tag.type === "Identifier" && tags.html.get(node.tag.name) === "html") {
141
+ const text = node.quasi.quasis.map((q) => q.value.raw).join("");
142
+ if (/<style[\s>]/.test(text)) {
143
+ const at = { line: node.loc?.start.line ?? 0, column: node.loc?.start.column ?? 0 };
144
+ const owner = owners[0];
145
+ if (!owner) plan.diagnostics.push({ ...at, message: "<style> has to be written inside a component." });
146
+ else {
147
+ const id = idFor(owner);
148
+ const shadow = shadowOf(owner);
149
+ plan.templateOwners.set(node.start, { id, component: functionName(owner, named) });
150
+ for (const block of templateBlocks(node.quasi)) compileBlock(block, block.scoped, owner, at);
151
+ }
152
+ }
153
+ }
154
+ const inner = FUNCTIONS.has(node.type) ? [...owners, node] : owners;
155
+ if (node.type === "JSXElement" && isStyle(node)) {
156
+ handle(node, owners);
157
+ return;
158
+ }
159
+ for (const key of Object.keys(node)) {
160
+ if (key === "loc" || key === "leadingComments" || key === "trailingComments" || key === "innerComments") continue;
161
+ const value = node[key];
162
+ if (Array.isArray(value)) for (const v of value) visit(v, inner);
163
+ else if (value && typeof value === "object" && "type" in value) visit(value, inner);
164
+ }
165
+ };
166
+ const handle = (node, owners) => {
167
+ const at = { line: node.loc?.start.line ?? 0, column: node.loc?.start.column ?? 0 };
168
+ const pieces = blockPieces(node);
169
+ if (pieces === null) {
170
+ plan.diagnostics.push({
171
+ ...at,
172
+ message: "<style> needs its CSS as one template literal, <style>{`...`}</style>: JSX reads a bare `{` as an expression."
173
+ });
174
+ return;
175
+ }
176
+ const owner = owners[0];
177
+ if (!owner) {
178
+ plan.diagnostics.push({ ...at, message: "<style> has to be written inside a component." });
179
+ return;
180
+ }
181
+ compileBlock(pieces, isScoped(node), owner, at);
182
+ plan.drop.add(node.start);
183
+ };
184
+ const compileBlock = (pieces, scoped, owner, at) => {
185
+ const component = functionName(owner, named);
186
+ const id = idFor(owner);
187
+ const processed = piecesToNodes(pieces.quasis, pieces.expressions, at, []);
188
+ if (!processed) return;
189
+ let nodes = processed;
190
+ const shadow = shadowOf(owner);
191
+ if (scoped && !shadow) {
192
+ if (!plan.scopes.some((s) => s.value === id)) plan.scopes.push({ start: owner.start, end: owner.end, value: id });
193
+ nodes = scopeNodes(nodes, id, (message) => plan.diagnostics.push({ ...at, message }));
194
+ }
195
+ const printed = module2 ? printStyleWithMap(nodes, options.minify, sourceName, module2) : { css: printStyle(nodes, options.minify) };
196
+ plan.styles.push({ id, scoped: scoped && !shadow, component, ...printed, ...shadow ? { shadow: true } : {}, ...at });
197
+ if (shadow) shadow.indexes.push(plan.styles.length - 1);
198
+ };
199
+ function idFor(owner) {
200
+ let id = scopeOf.get(owner);
201
+ if (!id) {
202
+ const component = functionName(owner, named);
203
+ const seen = names.get(component) ?? 0;
204
+ names.set(component, seen + 1);
205
+ id = hash(`${sourceName}:${component}${seen ? ":" + seen : ""}`);
206
+ scopeOf.set(owner, id);
207
+ }
208
+ return id;
209
+ }
210
+ visit(program, []);
211
+ return plan;
212
+ }
213
+ function applyTemplateStyles(ir, t) {
214
+ const walk8 = (node) => {
215
+ const children = node.children;
216
+ if (children) {
217
+ for (let i = children.length - 1; i >= 0; i--) {
218
+ const child = children[i];
219
+ if (child.kind === "element" && child.tag === "style" && t.owner) children.splice(i, 1);
220
+ else walk8(child);
221
+ }
222
+ }
223
+ if (t.scope && node.kind === "element" && node.tag !== "style" && node.props && !node.props.some((p) => p.name === SCOPE_ATTR)) {
224
+ node.props.unshift({ name: SCOPE_ATTR, kind: "attr", literal: t.scope });
225
+ }
226
+ };
227
+ walk8(ir);
228
+ }
229
+ function staticMember(cls, name) {
230
+ return cls.body.body.find(
231
+ (m) => m.type === "ClassProperty" && m.static && (m.key.type === "Identifier" && m.key.name === name || m.key.type === "StringLiteral" && m.key.value === name)
232
+ );
233
+ }
234
+ function finishShadowStyles(plan, code, moduleId) {
235
+ const edits = [];
236
+ const imports = [];
237
+ let next = 0;
238
+ const importOf = (specifier) => {
239
+ const local = `__fxShadow${next++}`;
240
+ imports.push(`import ${local} from ${JSON.stringify(specifier)};
241
+ `);
242
+ return local;
243
+ };
244
+ for (const shadow of plan.shadowClasses.values()) {
245
+ const blocks = shadow.indexes.map((i) => importOf(`${moduleId}?fx-style&index=${i}&inline&lang.css`));
246
+ let value;
247
+ const prop = shadow.styles;
248
+ if (prop?.value) {
249
+ const start = prop.value.start;
250
+ let text = code.slice(start, prop.value.end);
251
+ const replacements = [];
252
+ walkIdentifiers(prop.value, (id) => {
253
+ 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;
254
+ if (local) replacements.push({ at: id.start - start, length: id.name.length, with: local });
255
+ });
256
+ 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);
257
+ value = text;
258
+ }
259
+ if (!blocks.length && value === void 0) continue;
260
+ if (prop?.value) {
261
+ const merged = blocks.length ? `[${value}, ${blocks.join(", ")}]` : value;
262
+ edits.push({ start: prop.value.start, end: prop.value.end, code: merged });
263
+ } else if (blocks.length) {
264
+ const open = shadow.node.body.start + 1;
265
+ edits.push({ start: open, end: open, code: ` static styles = [${blocks.join(", ")}];` });
266
+ }
267
+ }
268
+ if (imports.length) edits.push({ start: 0, end: 0, code: imports.join("") });
269
+ return edits;
270
+ }
271
+ function walkIdentifiers(node, visit) {
272
+ if (!node || typeof node !== "object") return;
273
+ if (node.type === "Identifier") return visit(node);
274
+ if (node.type === "MemberExpression") return walkIdentifiers(node.object, visit);
275
+ if (node.type === "ObjectProperty") return walkIdentifiers(node.value, visit);
276
+ for (const key of Object.keys(node)) {
277
+ if (key === "loc") continue;
278
+ const value = node[key];
279
+ if (Array.isArray(value)) for (const v of value) walkIdentifiers(v, visit);
280
+ else if (value && typeof value === "object" && "type" in value) walkIdentifiers(value, visit);
281
+ }
282
+ }
283
+ function isStyle(node) {
284
+ const name = node.openingElement.name;
285
+ return name.type === "JSXIdentifier" && name.name === "style";
286
+ }
287
+ function isScoped(node) {
288
+ return node.openingElement.attributes.some((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === "scoped");
289
+ }
290
+ function templateBlocks(quasi) {
291
+ const blocks = [];
292
+ let open = null;
293
+ quasi.quasis.forEach((q, i) => {
294
+ const raw = q.value.raw;
295
+ let from = 0;
296
+ while (from <= raw.length) {
297
+ if (!open) {
298
+ const tag = /<style\b([^>]*)>/g;
299
+ tag.lastIndex = from;
300
+ const m = tag.exec(raw);
301
+ if (!m) break;
302
+ open = { quasis: [], expressions: [], scoped: /\bscoped\b/.test(m[1]) };
303
+ from = m.index + m[0].length;
304
+ }
305
+ const close = raw.indexOf("</style", from);
306
+ open.quasis.push(slicePiece(q, raw, from, close === -1 ? raw.length : close));
307
+ if (close === -1) break;
308
+ blocks.push(open);
309
+ open = null;
310
+ from = close;
311
+ }
312
+ const expr = quasi.expressions[i];
313
+ if (open && expr) open.expressions.push(expr);
314
+ });
315
+ return blocks;
316
+ }
317
+ function slicePiece(q, raw, from, to) {
318
+ const text = raw.slice(from, to);
319
+ const start = q.loc?.start;
320
+ if (!start) return { type: "TemplateElement", value: { raw: text, cooked: text } };
321
+ const before = raw.slice(0, from).split("\n");
322
+ const line = start.line + before.length - 1;
323
+ const column = before.length > 1 ? before[before.length - 1].length : start.column + from;
324
+ return { type: "TemplateElement", value: { raw: text, cooked: text }, loc: { start: { line, column } } };
325
+ }
326
+ function blockPieces(node) {
327
+ const meaningful = node.children.filter((c) => !(c.type === "JSXText" && !c.value.trim()));
328
+ if (meaningful.length === 0) return { quasis: [], expressions: [] };
329
+ if (meaningful.length !== 1) return null;
330
+ const only = meaningful[0];
331
+ if (only.type !== "JSXExpressionContainer") return null;
332
+ const expr = only.expression;
333
+ if (expr.type === "StringLiteral") return { quasis: [{ type: "TemplateElement", value: { raw: expr.value, cooked: expr.value } }], expressions: [] };
334
+ if (expr.type === "TemplateLiteral") return { quasis: expr.quasis, expressions: expr.expressions };
335
+ return null;
336
+ }
337
+ function findLiterals(program, modules, plan, intrinsic) {
338
+ const literals = /* @__PURE__ */ new Map();
339
+ const imports = /* @__PURE__ */ new Map();
340
+ const tags = new Set(intrinsic);
341
+ for (const statement of program.body ?? []) {
342
+ if (statement.type !== "ImportDeclaration") continue;
343
+ const source = statement.source.value;
344
+ const specifiers = statement.specifiers;
345
+ for (const sp of specifiers) {
346
+ if (sp.type === "ImportSpecifier") imports.set(sp.local.name, { source, name: sp.imported.name ?? sp.imported.value });
347
+ }
348
+ if (!modules.includes(source)) continue;
349
+ const css = specifiers.find((sp) => sp.type === "ImportSpecifier" && (sp.imported.name ?? sp.imported.value) === "css");
350
+ if (!css) continue;
351
+ const local = css.local.name;
352
+ tags.add(local);
353
+ imports.delete(local);
354
+ plan.edits.push({ start: statement.start, end: statement.end, code: importWithout(statement, css) });
355
+ }
356
+ if (tags.size === 0) return { literals, imports };
357
+ const visit = (node) => {
358
+ if (!node || typeof node !== "object") return;
359
+ if (node.type === "VariableDeclarator" && node.id.type === "Identifier" && node.init?.type === "TaggedTemplateExpression" && node.init.tag.type === "Identifier" && tags.has(node.init.tag.name)) {
360
+ literals.set(node.id.name, node.init);
361
+ }
362
+ for (const key of Object.keys(node)) {
363
+ if (key === "loc") continue;
364
+ const value = node[key];
365
+ if (Array.isArray(value)) for (const v of value) visit(v);
366
+ else if (value && typeof value === "object" && "type" in value) visit(value);
367
+ }
368
+ };
369
+ visit(program);
370
+ return { literals, imports };
371
+ }
372
+ function importWithout(statement, removed) {
373
+ const rest = statement.specifiers.filter((sp) => sp !== removed);
374
+ const source = JSON.stringify(statement.source.value);
375
+ if (rest.length === 0) return "";
376
+ const def = rest.find((sp) => sp.type === "ImportDefaultSpecifier");
377
+ const ns = rest.find((sp) => sp.type === "ImportNamespaceSpecifier");
378
+ const named = rest.filter((sp) => sp.type === "ImportSpecifier").map((sp) => {
379
+ const imported = sp.imported.name ?? JSON.stringify(sp.imported.value);
380
+ return imported === sp.local.name ? imported : `${imported} as ${sp.local.name}`;
381
+ });
382
+ const parts = [def?.local.name, ns ? `* as ${ns.local.name}` : void 0, named.length ? `{ ${named.join(", ")} }` : void 0].filter(Boolean);
383
+ return `import ${parts.join(", ")} from ${source};`;
384
+ }
385
+ function functionName(fn, named) {
386
+ if ((fn.type === "FunctionDeclaration" || fn.type === "FunctionExpression") && fn.id) return fn.id.name;
387
+ return named.get(fn) ?? "anonymous";
388
+ }
389
+ function hash(text) {
390
+ let h = 2166136261;
391
+ for (let i = 0; i < text.length; i++) {
392
+ h ^= text.charCodeAt(i);
393
+ h = Math.imul(h, 16777619);
394
+ }
395
+ return (h >>> 0).toString(36);
396
+ }
397
+ var OPAQUE = /(^|-)keyframes$|^font-face$|^property$|^counter-style$|^page$|^font-feature-values$/;
398
+ function scopeNodes(nodes, id, warn = () => {
399
+ }) {
400
+ return nodes.map((node) => {
401
+ if (node.kind === "rule") return { ...node, selector: scopeSelector(node.selector, id, warn) };
402
+ if (node.kind === "at-rule" && !OPAQUE.test(node.name)) return { ...node, nodes: scopeNodes(node.nodes, id, warn) };
403
+ return node;
404
+ });
405
+ }
406
+ function scopeSelector(selector, id, warn = () => {
407
+ }) {
408
+ return splitTop(selector, ",").map((complex) => scopeComplex(complex.trim(), `[${SCOPE_ATTR}="${id}"]`, warn)).join(", ");
409
+ }
410
+ function scopeComplex(selector, attr, warn) {
411
+ if (/:host\b|::slotted\(/.test(selector)) {
412
+ warn(`\`${selector}\` only means something in a shadow root; in a light-DOM component it is left as written.`);
413
+ return selector;
414
+ }
415
+ const parts = compounds(selector);
416
+ const deep = parts.findIndex((p) => p.text.startsWith(":deep("));
417
+ if (deep !== -1) {
418
+ const inner = unwrap(parts[deep].text, ":deep(");
419
+ const before = parts.slice(0, deep);
420
+ if (before.length === 0) return `${attr} ${inner}`;
421
+ const scopedBefore = scopeAt(before, before.length - 1, attr);
422
+ return `${join(scopedBefore)} ${inner}`;
423
+ }
424
+ let target = -1;
425
+ for (let i = parts.length - 1; i >= 0; i--) {
426
+ if (!parts[i].text.startsWith(":global(")) {
427
+ target = i;
428
+ break;
429
+ }
430
+ }
431
+ const out = target === -1 ? parts : scopeAt(parts, target, attr);
432
+ return join(out.map((p) => p.text.startsWith(":global(") ? { ...p, text: unwrap(p.text, ":global(") } : p));
433
+ }
434
+ function scopeAt(parts, index, attr) {
435
+ return parts.map((p, i) => i === index ? { ...p, text: insertAttr(p.text, attr) } : p);
436
+ }
437
+ function insertAttr(compound, attr) {
438
+ let depth = 0;
439
+ let quote = "";
440
+ for (let i = 0; i < compound.length; i++) {
441
+ const c = compound[i];
442
+ if (quote) {
443
+ if (c === "\\") i++;
444
+ else if (c === quote) quote = "";
445
+ continue;
446
+ }
447
+ if (c === "\\") {
448
+ i++;
449
+ continue;
450
+ }
451
+ if (c === '"' || c === "'") quote = c;
452
+ else if (c === "(" || c === "[") depth++;
453
+ else if (c === ")" || c === "]") depth--;
454
+ else if (c === ":" && depth === 0 && i > 0) return compound.slice(0, i) + attr + compound.slice(i);
455
+ }
456
+ return compound + attr;
457
+ }
458
+ function compounds(selector) {
459
+ const out = [];
460
+ let depth = 0;
461
+ let quote = "";
462
+ let current = "";
463
+ let combinator = "";
464
+ const push = () => {
465
+ if (current.trim()) out.push({ combinator: out.length ? combinator || " " : "", text: current.trim() });
466
+ current = "";
467
+ combinator = "";
468
+ };
469
+ for (let i = 0; i < selector.length; i++) {
470
+ const c = selector[i];
471
+ if (quote) {
472
+ current += c;
473
+ if (c === "\\") current += selector[++i] ?? "";
474
+ else if (c === quote) quote = "";
475
+ continue;
476
+ }
477
+ if (c === "\\") {
478
+ current += c + (selector[++i] ?? "");
479
+ continue;
480
+ }
481
+ if (c === '"' || c === "'") quote = c;
482
+ if (c === "(" || c === "[") depth++;
483
+ if (c === ")" || c === "]") depth--;
484
+ if (depth === 0 && (c === ">" || c === "+" || c === "~")) {
485
+ push();
486
+ combinator = c;
487
+ continue;
488
+ }
489
+ if (depth === 0 && /\s/.test(c)) {
490
+ if (current.trim()) {
491
+ const keep = combinator;
492
+ push();
493
+ combinator = keep;
494
+ }
495
+ continue;
496
+ }
497
+ current += c;
498
+ }
499
+ push();
500
+ return out;
501
+ }
502
+ function join(parts) {
503
+ return parts.map((p, i) => i === 0 ? p.text : p.combinator === " " ? ` ${p.text}` : ` ${p.combinator} ${p.text}`).join("");
504
+ }
505
+ function unwrap(text, prefix) {
506
+ return text.slice(prefix.length, text.lastIndexOf(")")).trim() + text.slice(text.lastIndexOf(")") + 1);
507
+ }
508
+ function splitTop(selector, separator) {
509
+ const parts = [];
510
+ let depth = 0;
511
+ let quote = "";
512
+ let start = 0;
513
+ for (let i = 0; i < selector.length; i++) {
514
+ const c = selector[i];
515
+ if (quote) {
516
+ if (c === "\\") i++;
517
+ else if (c === quote) quote = "";
518
+ } else if (c === '"' || c === "'") quote = c;
519
+ else if (c === "\\") i++;
520
+ else if (c === "(" || c === "[") depth++;
521
+ else if (c === ")" || c === "]") depth--;
522
+ else if (c === separator && depth === 0) {
523
+ parts.push(selector.slice(start, i));
524
+ start = i + 1;
525
+ }
526
+ }
527
+ parts.push(selector.slice(start));
528
+ return parts.filter((p) => p.trim());
529
+ }
530
+ function shiftLoc(node, source, start) {
531
+ 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;
532
+ if (node.kind === "rule" || node.kind === "at-rule") return { ...node, loc, nodes: node.nodes.map((n) => shiftLoc(n, source, start)) };
533
+ return { ...node, loc };
534
+ }
535
+ function dropLoc(node) {
536
+ const { loc: _loc, ...rest } = node;
537
+ if (rest.kind === "rule" || rest.kind === "at-rule") return { ...rest, nodes: rest.nodes.map(dropLoc) };
538
+ return rest;
539
+ }
540
+ function printStyleWithMap(nodes, minify, sourceName, module2) {
541
+ const lines = [[]];
542
+ let css = "";
543
+ let column = 0;
544
+ const emit = (text) => {
545
+ for (const ch of text) {
546
+ css += ch;
547
+ if (ch === "\n") {
548
+ lines.push([]);
549
+ column = 0;
550
+ } else column++;
551
+ }
552
+ };
553
+ const mark = (loc) => {
554
+ if (loc && loc.source === sourceName) lines[lines.length - 1].push([column, 0, loc.line - 1, loc.column]);
555
+ };
556
+ const write = (node, depth) => {
557
+ const pad = minify ? "" : " ".repeat(depth);
558
+ const nl = minify ? "" : "\n";
559
+ const sp = minify ? "" : " ";
560
+ emit(pad);
561
+ mark(node.loc);
562
+ if (node.kind === "declaration") {
563
+ emit(`${node.property}:${sp}${node.value}${node.important ? `${sp}!important` : ""};${nl}`);
564
+ } else if (node.kind === "raw") {
565
+ emit(`${node.text}${nl}`);
566
+ } else {
567
+ emit(`${node.kind === "rule" ? node.selector : `@${node.name}${node.params ? " " + node.params : ""}`}${sp}{${nl}`);
568
+ for (const child of node.nodes) write(child, depth + 1);
569
+ emit(`${pad}}${nl}`);
570
+ }
571
+ };
572
+ for (const node of nodes) write(node, 0);
573
+ return { css, map: { version: 3, sources: [module2.id], sourcesContent: [module2.code], names: [], mappings: (0, import_sourcemap_codec.encode)(lines) } };
574
+ }
575
+ function printStyle(nodes, minify = false) {
576
+ const out = [];
577
+ const write = (node, depth) => {
578
+ const pad = minify ? "" : " ".repeat(depth);
579
+ const nl = minify ? "" : "\n";
580
+ const sp = minify ? "" : " ";
581
+ if (node.kind === "declaration") {
582
+ out.push(`${pad}${node.property}:${sp}${node.value}${node.important ? `${sp}!important` : ""};${nl}`);
583
+ } else if (node.kind === "raw") {
584
+ out.push(`${pad}${node.text}${nl}`);
585
+ } else {
586
+ const header = node.kind === "rule" ? node.selector : `@${node.name}${node.params ? " " + node.params : ""}`;
587
+ out.push(`${pad}${header}${sp}{${nl}`);
588
+ for (const child of node.nodes) write(child, depth + 1);
589
+ out.push(`${pad}}${nl}`);
590
+ }
591
+ };
592
+ for (const node of nodes) write(node, 0);
593
+ return out.join("");
594
+ }
595
+
39
596
  // src/optimize/analyze-static.ts
40
597
  function propReason(prop) {
41
598
  switch (prop.kind) {
@@ -533,6 +1090,16 @@ var PRIMITIVE_MODULES = [
533
1090
  "@fluixi/reactive/cached-resource",
534
1091
  "@fluixi/core"
535
1092
  ];
1093
+ var TAG_INTRINSICS = {
1094
+ $html: { export: "html", module: "@fluixi/core" },
1095
+ $svg: { export: "svg", module: "@fluixi/core" },
1096
+ $css: { export: "css", module: "@fluixi/core" }
1097
+ };
1098
+ function tagIntrinsics(bound) {
1099
+ const tags = /* @__PURE__ */ new Map();
1100
+ for (const [name, tag] of Object.entries(TAG_INTRINSICS)) if (!bound.has(name)) tags.set(name, tag.export);
1101
+ return tags;
1102
+ }
536
1103
  var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join("|")})\\s*\\(`);
537
1104
  function mayHaveIntrinsic(code) {
538
1105
  return INTRINSIC_CALL.test(code);
@@ -1036,7 +1603,7 @@ function emitComponent(name, props, children, used, templates, at) {
1036
1603
  }
1037
1604
  let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
1038
1605
  if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
1039
- const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
1606
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => (${body})`);
1040
1607
  const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
1041
1608
  obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
1042
1609
  }
@@ -1235,6 +1802,271 @@ function cleanTemplateText(value) {
1235
1802
  return out;
1236
1803
  }
1237
1804
 
1805
+ // src/lower/entities.ts
1806
+ var NAMED = {
1807
+ quot: 34,
1808
+ amp: 38,
1809
+ apos: 39,
1810
+ lt: 60,
1811
+ gt: 62,
1812
+ nbsp: 160,
1813
+ iexcl: 161,
1814
+ cent: 162,
1815
+ pound: 163,
1816
+ curren: 164,
1817
+ yen: 165,
1818
+ brvbar: 166,
1819
+ sect: 167,
1820
+ uml: 168,
1821
+ copy: 169,
1822
+ ordf: 170,
1823
+ laquo: 171,
1824
+ not: 172,
1825
+ shy: 173,
1826
+ reg: 174,
1827
+ macr: 175,
1828
+ deg: 176,
1829
+ plusmn: 177,
1830
+ sup2: 178,
1831
+ sup3: 179,
1832
+ acute: 180,
1833
+ micro: 181,
1834
+ para: 182,
1835
+ middot: 183,
1836
+ cedil: 184,
1837
+ sup1: 185,
1838
+ ordm: 186,
1839
+ raquo: 187,
1840
+ frac14: 188,
1841
+ frac12: 189,
1842
+ frac34: 190,
1843
+ iquest: 191,
1844
+ Agrave: 192,
1845
+ Aacute: 193,
1846
+ Acirc: 194,
1847
+ Atilde: 195,
1848
+ Auml: 196,
1849
+ Aring: 197,
1850
+ AElig: 198,
1851
+ Ccedil: 199,
1852
+ Egrave: 200,
1853
+ Eacute: 201,
1854
+ Ecirc: 202,
1855
+ Euml: 203,
1856
+ Igrave: 204,
1857
+ Iacute: 205,
1858
+ Icirc: 206,
1859
+ Iuml: 207,
1860
+ ETH: 208,
1861
+ Ntilde: 209,
1862
+ Ograve: 210,
1863
+ Oacute: 211,
1864
+ Ocirc: 212,
1865
+ Otilde: 213,
1866
+ Ouml: 214,
1867
+ times: 215,
1868
+ Oslash: 216,
1869
+ Ugrave: 217,
1870
+ Uacute: 218,
1871
+ Ucirc: 219,
1872
+ Uuml: 220,
1873
+ Yacute: 221,
1874
+ THORN: 222,
1875
+ szlig: 223,
1876
+ agrave: 224,
1877
+ aacute: 225,
1878
+ acirc: 226,
1879
+ atilde: 227,
1880
+ auml: 228,
1881
+ aring: 229,
1882
+ aelig: 230,
1883
+ ccedil: 231,
1884
+ egrave: 232,
1885
+ eacute: 233,
1886
+ ecirc: 234,
1887
+ euml: 235,
1888
+ igrave: 236,
1889
+ iacute: 237,
1890
+ icirc: 238,
1891
+ iuml: 239,
1892
+ eth: 240,
1893
+ ntilde: 241,
1894
+ ograve: 242,
1895
+ oacute: 243,
1896
+ ocirc: 244,
1897
+ otilde: 245,
1898
+ ouml: 246,
1899
+ divide: 247,
1900
+ oslash: 248,
1901
+ ugrave: 249,
1902
+ uacute: 250,
1903
+ ucirc: 251,
1904
+ uuml: 252,
1905
+ yacute: 253,
1906
+ thorn: 254,
1907
+ yuml: 255,
1908
+ OElig: 338,
1909
+ oelig: 339,
1910
+ Scaron: 352,
1911
+ scaron: 353,
1912
+ Yuml: 376,
1913
+ fnof: 402,
1914
+ circ: 710,
1915
+ tilde: 732,
1916
+ Alpha: 913,
1917
+ Beta: 914,
1918
+ Gamma: 915,
1919
+ Delta: 916,
1920
+ Epsilon: 917,
1921
+ Zeta: 918,
1922
+ Eta: 919,
1923
+ Theta: 920,
1924
+ Iota: 921,
1925
+ Kappa: 922,
1926
+ Lambda: 923,
1927
+ Mu: 924,
1928
+ Nu: 925,
1929
+ Xi: 926,
1930
+ Omicron: 927,
1931
+ Pi: 928,
1932
+ Rho: 929,
1933
+ Sigma: 931,
1934
+ Tau: 932,
1935
+ Upsilon: 933,
1936
+ Phi: 934,
1937
+ Chi: 935,
1938
+ Psi: 936,
1939
+ Omega: 937,
1940
+ alpha: 945,
1941
+ beta: 946,
1942
+ gamma: 947,
1943
+ delta: 948,
1944
+ epsilon: 949,
1945
+ zeta: 950,
1946
+ eta: 951,
1947
+ theta: 952,
1948
+ iota: 953,
1949
+ kappa: 954,
1950
+ lambda: 955,
1951
+ mu: 956,
1952
+ nu: 957,
1953
+ xi: 958,
1954
+ omicron: 959,
1955
+ pi: 960,
1956
+ rho: 961,
1957
+ sigmaf: 962,
1958
+ sigma: 963,
1959
+ tau: 964,
1960
+ upsilon: 965,
1961
+ phi: 966,
1962
+ chi: 967,
1963
+ psi: 968,
1964
+ omega: 969,
1965
+ thetasym: 977,
1966
+ upsih: 978,
1967
+ piv: 982,
1968
+ ensp: 8194,
1969
+ emsp: 8195,
1970
+ thinsp: 8201,
1971
+ zwnj: 8204,
1972
+ zwj: 8205,
1973
+ lrm: 8206,
1974
+ rlm: 8207,
1975
+ ndash: 8211,
1976
+ mdash: 8212,
1977
+ lsquo: 8216,
1978
+ rsquo: 8217,
1979
+ sbquo: 8218,
1980
+ ldquo: 8220,
1981
+ rdquo: 8221,
1982
+ bdquo: 8222,
1983
+ dagger: 8224,
1984
+ Dagger: 8225,
1985
+ bull: 8226,
1986
+ hellip: 8230,
1987
+ permil: 8240,
1988
+ prime: 8242,
1989
+ Prime: 8243,
1990
+ lsaquo: 8249,
1991
+ rsaquo: 8250,
1992
+ oline: 8254,
1993
+ frasl: 8260,
1994
+ euro: 8364,
1995
+ image: 8465,
1996
+ weierp: 8472,
1997
+ real: 8476,
1998
+ trade: 8482,
1999
+ alefsym: 8501,
2000
+ larr: 8592,
2001
+ uarr: 8593,
2002
+ rarr: 8594,
2003
+ darr: 8595,
2004
+ harr: 8596,
2005
+ crarr: 8629,
2006
+ lArr: 8656,
2007
+ uArr: 8657,
2008
+ rArr: 8658,
2009
+ dArr: 8659,
2010
+ hArr: 8660,
2011
+ forall: 8704,
2012
+ part: 8706,
2013
+ exist: 8707,
2014
+ empty: 8709,
2015
+ nabla: 8711,
2016
+ isin: 8712,
2017
+ notin: 8713,
2018
+ ni: 8715,
2019
+ prod: 8719,
2020
+ sum: 8721,
2021
+ minus: 8722,
2022
+ lowast: 8727,
2023
+ radic: 8730,
2024
+ prop: 8733,
2025
+ infin: 8734,
2026
+ ang: 8736,
2027
+ and: 8743,
2028
+ or: 8744,
2029
+ cap: 8745,
2030
+ cup: 8746,
2031
+ int: 8747,
2032
+ there4: 8756,
2033
+ sim: 8764,
2034
+ cong: 8773,
2035
+ asymp: 8776,
2036
+ ne: 8800,
2037
+ equiv: 8801,
2038
+ le: 8804,
2039
+ ge: 8805,
2040
+ sub: 8834,
2041
+ sup: 8835,
2042
+ nsub: 8836,
2043
+ sube: 8838,
2044
+ supe: 8839,
2045
+ oplus: 8853,
2046
+ otimes: 8855,
2047
+ perp: 8869,
2048
+ sdot: 8901,
2049
+ lceil: 8968,
2050
+ rceil: 8969,
2051
+ lfloor: 8970,
2052
+ rfloor: 8971,
2053
+ lang: 9001,
2054
+ rang: 9002,
2055
+ loz: 9674,
2056
+ spades: 9824,
2057
+ clubs: 9827,
2058
+ hearts: 9829,
2059
+ diams: 9830
2060
+ };
2061
+ var REFERENCE = /&(?:#[xX]([0-9a-fA-F]{1,6})|#([0-9]{1,7})|([a-zA-Z][a-zA-Z0-9]{1,31}));/g;
2062
+ function decodeEntities(value) {
2063
+ if (!value.includes("&")) return value;
2064
+ return value.replace(REFERENCE, (match, hex, dec, name) => {
2065
+ const code = hex ? parseInt(hex, 16) : dec ? parseInt(dec, 10) : Object.prototype.hasOwnProperty.call(NAMED, name) ? NAMED[name] : void 0;
2066
+ return code !== void 0 && code <= 1114111 ? String.fromCodePoint(code) : match;
2067
+ });
2068
+ }
2069
+
1238
2070
  // src/lower/template.ts
1239
2071
  var DELEGATED = /* @__PURE__ */ new Set([
1240
2072
  "click",
@@ -1321,7 +2153,7 @@ var Lowerer = class {
1321
2153
  switch (node.kind) {
1322
2154
  case "Text": {
1323
2155
  if (node.raw) return { kind: "text", value: node.value };
1324
- const cleaned = cleanTemplateText(node.value);
2156
+ const cleaned = decodeEntities(cleanTemplateText(node.value));
1325
2157
  return cleaned ? { kind: "text", value: cleaned } : null;
1326
2158
  }
1327
2159
  case "Comment":
@@ -1357,7 +2189,7 @@ var Lowerer = class {
1357
2189
  kind: "element",
1358
2190
  tag: el.tag,
1359
2191
  svg: el.namespace === "svg",
1360
- props: this.lowerAttributes(el.attributes),
2192
+ props: this.lowerAttributes(el.attributes, true),
1361
2193
  children,
1362
2194
  static: false,
1363
2195
  ...at ? { at } : {}
@@ -1470,13 +2302,22 @@ var Lowerer = class {
1470
2302
  return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
1471
2303
  }
1472
2304
  // --- attributes ---------------------------------------------------------
1473
- lowerAttributes(attrs) {
2305
+ /**
2306
+ * `element`: a DOM element, where each class:/style: value that moves is its own accessor.
2307
+ * A component gets the object as before, rebuilt whole when any value moves.
2308
+ */
2309
+ lowerAttributes(attrs, element = false) {
2310
+ let anyReactive = false;
2311
+ const entry = (name, h) => {
2312
+ anyReactive ||= h.reactive;
2313
+ if (!element || !h.reactive) return `${JSON.stringify(name)}: ${h.code}`;
2314
+ const accessor = `() => ${paren(h.code, "arrowBody")}`;
2315
+ return `${JSON.stringify(name)}: ${h.at ? markHole(accessor, h.at.line, h.at.column) : accessor}`;
2316
+ };
1474
2317
  const props = [];
1475
2318
  const classEntries = [];
1476
- let classReactive = false;
1477
2319
  let classAt;
1478
2320
  const styleEntries = [];
1479
- let styleReactive = false;
1480
2321
  let styleAt;
1481
2322
  const usePairs = [];
1482
2323
  for (const a of attrs) {
@@ -1516,15 +2357,13 @@ var Lowerer = class {
1516
2357
  break;
1517
2358
  case "ClassDirective": {
1518
2359
  const h = this.hole(a.hole);
1519
- classEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
1520
- classReactive = classReactive || h.reactive;
2360
+ classEntries.push(entry(a.name, h));
1521
2361
  classAt ??= h.at;
1522
2362
  break;
1523
2363
  }
1524
2364
  case "StyleDirective": {
1525
2365
  const h = this.hole(a.hole);
1526
- styleEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
1527
- styleReactive = styleReactive || h.reactive;
2366
+ styleEntries.push(entry(a.name, h));
1528
2367
  styleAt ??= h.at;
1529
2368
  break;
1530
2369
  }
@@ -1544,18 +2383,19 @@ var Lowerer = class {
1544
2383
  name: "classList",
1545
2384
  kind: "attr",
1546
2385
  expr: `{ ${classEntries.join(", ")} }`,
1547
- reactive: classReactive,
2386
+ reactive: !element && anyReactive,
1548
2387
  ...classAt ? { at: classAt } : {}
1549
2388
  });
1550
2389
  }
1551
2390
  if (styleEntries.length > 0) {
1552
- props.push({
1553
- name: "style",
1554
- kind: "attr",
1555
- expr: `{ ${styleEntries.join(", ")} }`,
1556
- reactive: styleReactive,
1557
- ...styleAt ? { at: styleAt } : {}
1558
- });
2391
+ const entries = `{ ${styleEntries.join(", ")} }`;
2392
+ const base = props.findIndex((p) => p.name === "style");
2393
+ if (base === -1) props.push({ name: "style", kind: "attr", expr: entries, reactive: !element && anyReactive, ...styleAt ? { at: styleAt } : {} });
2394
+ else {
2395
+ const prop = props[base];
2396
+ const value = prop.expr === void 0 ? JSON.stringify(prop.literal) : prop.reactive ? `() => ${paren(prop.expr, "arrowBody")}` : prop.expr;
2397
+ props[base] = { name: "style", kind: "attr", expr: `[${value}, ${entries}]`, reactive: false, ...prop.at ? { at: prop.at } : {} };
2398
+ }
1559
2399
  }
1560
2400
  if (usePairs.length > 0) {
1561
2401
  props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
@@ -1644,7 +2484,7 @@ var Lowerer = class {
1644
2484
  return base;
1645
2485
  }
1646
2486
  if (v.kind === "static") {
1647
- base.literal = v.value;
2487
+ base.literal = decodeEntities(v.value);
1648
2488
  return base;
1649
2489
  }
1650
2490
  if (v.kind === "hole") {
@@ -1657,7 +2497,7 @@ var Lowerer = class {
1657
2497
  let reactive = false;
1658
2498
  let at;
1659
2499
  const body = v.parts.map((p) => {
1660
- if ("text" in p) return escapeTemplate(p.text);
2500
+ if ("text" in p) return escapeTemplate(decodeEntities(p.text));
1661
2501
  const h = this.hole(p.hole);
1662
2502
  reactive = reactive || h.reactive;
1663
2503
  at ??= h.at;
@@ -1682,6 +2522,7 @@ function compilePieces(pieces, holes, options = {}) {
1682
2522
  ...options.sourceFile ? { sourceFile: options.sourceFile } : {},
1683
2523
  ...options.positionAt ? { positionAt: options.positionAt } : {}
1684
2524
  });
2525
+ if (options.templateStyles) applyTemplateStyles(ir, options.templateStyles);
1685
2526
  analyzeStatic(ir);
1686
2527
  resolveComponentSources(ir, {
1687
2528
  resolver: createComponentResolver(options.resolve ?? []),
@@ -1880,9 +2721,12 @@ function attrExprCode(attr, ctx) {
1880
2721
  }
1881
2722
  return null;
1882
2723
  }
1883
- function buildProps(attrs, ctx) {
2724
+ function buildProps(attrs, ctx, element = false) {
1884
2725
  const props = [];
1885
2726
  const usePairs = [];
2727
+ const classEntries = [];
2728
+ const styleEntries = [];
2729
+ let styleAt;
1886
2730
  for (const a of attrs) {
1887
2731
  if (a.type === "JSXSpreadAttribute") {
1888
2732
  props.push({ name: "", kind: "spread", expr: ctx.code(a.argument) });
@@ -1898,6 +2742,27 @@ function buildProps(attrs, ctx) {
1898
2742
  usePairs.push(opts != null ? `[${dir}, () => ${paren(opts, "arrowBody")}]` : `[${dir}]`);
1899
2743
  continue;
1900
2744
  }
2745
+ if (element && (ns === "class" || ns === "style")) {
2746
+ const key = JSON.stringify(a.name.name.name);
2747
+ const v = a.value;
2748
+ let value;
2749
+ if (v == null) value = "true";
2750
+ else if (isString(v)) value = JSON.stringify(literalOf(v));
2751
+ else if (v.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression") {
2752
+ value = keyValue(v.expression, ctx);
2753
+ if (ns === "style") styleAt ??= posOf(v.expression, ctx);
2754
+ } else continue;
2755
+ (ns === "class" ? classEntries : styleEntries).push(`${key}: ${value}`);
2756
+ continue;
2757
+ }
2758
+ if (element && a.name.type === "JSXIdentifier" && a.name.name === "style" && a.value?.type === "JSXExpressionContainer") {
2759
+ const entries = objectEntries(a.value.expression, ctx);
2760
+ if (entries) {
2761
+ styleEntries.unshift(...entries);
2762
+ styleAt = posOf(a.value.expression, ctx);
2763
+ continue;
2764
+ }
2765
+ }
1901
2766
  if (ns === "oncapture") {
1902
2767
  const evt = a.name.name.name.toLowerCase();
1903
2768
  const h = attrExprCode(a, ctx) ?? "undefined";
@@ -1906,9 +2771,37 @@ function buildProps(attrs, ctx) {
1906
2771
  }
1907
2772
  props.push(buildAttr(a, ctx));
1908
2773
  }
2774
+ if (classEntries.length > 0) props.push({ name: "classList", kind: "attr", expr: `{ ${classEntries.join(", ")} }`, reactive: false });
2775
+ if (styleEntries.length > 0) {
2776
+ const entries = `{ ${styleEntries.join(", ")} }`;
2777
+ const base = props.findIndex((p) => p.name === "style" && p.kind === "attr");
2778
+ if (base === -1) props.push({ name: "style", kind: "attr", expr: entries, reactive: false, ...styleAt ? { at: styleAt } : {} });
2779
+ else {
2780
+ const prop = props[base];
2781
+ const value = prop.literal !== void 0 ? JSON.stringify(prop.literal) : prop.reactive ? `() => ${paren(prop.expr, "arrowBody")}` : prop.expr;
2782
+ props[base] = { name: "style", kind: "attr", expr: `[${value}, ${entries}]`, reactive: false, ...prop.at ? { at: prop.at } : {} };
2783
+ }
2784
+ }
1909
2785
  if (usePairs.length > 0) props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
1910
2786
  return props;
1911
2787
  }
2788
+ function keyValue(expr, ctx) {
2789
+ const code = ctx.code(expr);
2790
+ if (!isReactive(expr)) return code;
2791
+ const accessor = `() => ${paren(code, "arrowBody")}`;
2792
+ const at = posOf(expr, ctx);
2793
+ return at ? markHole(accessor, at.line, at.column) : accessor;
2794
+ }
2795
+ function objectEntries(expr, ctx) {
2796
+ if (expr?.type !== "ObjectExpression") return null;
2797
+ const entries = [];
2798
+ for (const p of expr.properties) {
2799
+ if (p.type !== "ObjectProperty") return null;
2800
+ const key = p.computed ? `[${ctx.code(p.key)}]` : p.key.type === "Identifier" ? JSON.stringify(p.key.name) : JSON.stringify(String(p.key.value));
2801
+ entries.push(`${key}: ${keyValue(p.value, ctx)}`);
2802
+ }
2803
+ return entries;
2804
+ }
1912
2805
  function buildChildren(children, ctx) {
1913
2806
  const out = [];
1914
2807
  for (let i = 0; i < children.length; i++) {
@@ -1936,6 +2829,7 @@ function buildChildren(children, ctx) {
1936
2829
  });
1937
2830
  }
1938
2831
  } else if (child.type === "JSXElement" || child.type === "JSXFragment") {
2832
+ if (ctx.dropStyles?.has(child.start)) continue;
1939
2833
  out.push(buildNode(child, ctx));
1940
2834
  } else if (child.type === "JSXSpreadChild") {
1941
2835
  out.push({ kind: "expr", code: ctx.code(child.expression), reactive: false });
@@ -2005,13 +2899,17 @@ function buildElement(node, ctx, handled) {
2005
2899
  const children = each ? [each] : buildChildren(node.children, ctx);
2006
2900
  if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
2007
2901
  const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
2008
- return { kind: "element", tag, svg: SVG_TAGS.has(tag), props: buildProps(attributes, ctx), children, static: false, ...at ? { at } : {} };
2902
+ const props = buildProps(attributes, ctx, true);
2903
+ const scope = ctx.scopes?.find((s) => node.start >= s.start && node.end <= s.end);
2904
+ if (scope) props.unshift({ name: "data-fx-s", kind: "attr", literal: scope.value });
2905
+ return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
2009
2906
  }
2010
2907
  function buildNode(node, ctx) {
2011
2908
  if (node.type === "JSXFragment") {
2012
2909
  return { kind: "fragment", children: buildChildren(node.children, ctx) };
2013
2910
  }
2014
2911
  if (directive(node, "if")) return buildIf(node, null, ctx);
2912
+ if (ctx.dropStyles?.has(node.start)) return { kind: "fragment", children: [] };
2015
2913
  const { tag, component } = tagInfo(node.openingElement.name, ctx);
2016
2914
  if (!component) return buildElement(node, ctx, []);
2017
2915
  const props = buildProps(node.openingElement.attributes, ctx);
@@ -2517,8 +3415,8 @@ function isJsxRoot(node, parent, key) {
2517
3415
  if (!isJsx(node)) return false;
2518
3416
  return !(parent && isJsx(parent) && key === "children");
2519
3417
  }
2520
- function isTemplateTag(node, litTag) {
2521
- return node.type === "TaggedTemplateExpression" && node.tag?.type === "Identifier" && (node.tag.name === litTag || node.tag.name === "svg");
3418
+ function isTemplateTag(node, tags) {
3419
+ return node.type === "TaggedTemplateExpression" && node.tag?.type === "Identifier" && tags.has(node.tag.name);
2522
3420
  }
2523
3421
  function outermost(list) {
2524
3422
  return list.filter(
@@ -2666,22 +3564,28 @@ function transformTemplates(code, id, options = {}) {
2666
3564
  const wantsIntrinsics = options.intrinsics !== false && mayHaveIntrinsic(code);
2667
3565
  const wantsSources = options.sourceLocations === true && mayHaveSourceMarks(code);
2668
3566
  const sourceName = relativeToRoot(id, options.sourceRoot);
2669
- if (!mayHaveTemplate && !(wantsJsx && code.includes("<")) && !wantsIntrinsics && !wantsSources) return null;
3567
+ const wantsStyles = options.styles !== void 0 && (code.includes("<style") || code.includes("css`"));
3568
+ if (!mayHaveTemplate && !(wantsJsx && code.includes("<")) && !wantsIntrinsics && !wantsSources && !wantsStyles) return null;
2670
3569
  const ast = (0, import_parser2.parse)(code, {
2671
3570
  sourceType: "module",
2672
3571
  plugins: ["jsx", "typescript"],
2673
3572
  errorRecovery: true
2674
3573
  });
3574
+ const intrinsicTags = options.intrinsics === false ? /* @__PURE__ */ new Map() : tagIntrinsics(moduleBindings(ast.program));
3575
+ const templateTags = /* @__PURE__ */ new Map([[litTag, "html"], ["svg", "svg"]]);
3576
+ for (const [name, tag] of intrinsicTags) if (tag !== "css") templateTags.set(name, tag);
3577
+ const cssTags = [...intrinsicTags].filter(([, tag]) => tag === "css").map(([name]) => name);
2675
3578
  const found = [];
2676
3579
  walk7(ast.program, (n, parent, key) => {
2677
- if (wantsLit && isTemplateTag(n, litTag)) found.push(n);
3580
+ if (wantsLit && isTemplateTag(n, templateTags)) found.push(n);
2678
3581
  else if (wantsJsx && isJsxRoot(n, parent, key)) found.push(n);
2679
3582
  });
2680
3583
  const props = options.propsDestructure === false ? { edits: [], diagnostics: [], used: /* @__PURE__ */ new Set() } : collectPropsEdits(ast.program, code, { runtimeModule: options.runtimeModule });
2681
3584
  const intrinsics = options.intrinsics === false ? { edits: [], diagnostics: [], imports: /* @__PURE__ */ new Map() } : collectIntrinsicEdits(ast.program);
2682
3585
  if (intrinsics.edits.length) avoidBindingClashes(ast.program, intrinsics, moduleBindings(ast.program));
2683
3586
  const sources = options.sourceLocations === true ? collectSourceEdits(ast.program, sourceName) : { edits: [], marked: 0 };
2684
- 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;
3587
+ const styles = wantsStyles ? collectStyles(ast.program, sourceName, options.styles, { html: templateTags, css: cssTags }, { id, code }) : void 0;
3588
+ 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;
2685
3589
  if (silent) return null;
2686
3590
  found.sort((a, b) => b.start - a.start || a.end - b.end);
2687
3591
  const used = new Set(props.used);
@@ -2695,20 +3599,32 @@ function transformTemplates(code, id, options = {}) {
2695
3599
  declared: declaredNames(ast.program)
2696
3600
  };
2697
3601
  const hoisted = /* @__PURE__ */ new Map();
2698
- const edits = [...props.edits, ...intrinsics.edits, ...sources.edits];
3602
+ const edits = [...props.edits, ...intrinsics.edits, ...sources.edits, ...styles?.edits ?? []];
2699
3603
  for (const node of found) {
2700
3604
  const nested = found.some(
2701
3605
  (o) => o !== node && o.start <= node.start && o.end >= node.end && o.end - o.start > node.end - node.start
2702
3606
  );
2703
3607
  if (isJsx(node)) {
2704
- edits.push({ start: node.start, end: node.end, code: compileJsx(code, node, edits, nested, used, needs, hoisted, options, sourceName) });
3608
+ edits.push({ start: node.start, end: node.end, code: compileJsx(code, node, edits, nested, used, needs, hoisted, options, sourceName, styles) });
2705
3609
  continue;
2706
3610
  }
2707
3611
  const holes = node.quasi.expressions.map(
2708
3612
  (e) => bindHole(code, e, edits, options.sourceLocations === true)
2709
3613
  );
3614
+ const scope = styles?.scopes.find((s) => node.start >= s.start && node.end <= s.end)?.value;
3615
+ const owner = styles?.templateOwners.get(node.start);
3616
+ const templateStyles = options.styles && styles && (scope || owner) ? {
3617
+ options: options.styles,
3618
+ ...scope ? { scope } : {},
3619
+ ...owner ? { owner } : {},
3620
+ line: node.loc?.start.line ?? 0,
3621
+ column: node.loc?.start.column ?? 0,
3622
+ styles: styles.styles,
3623
+ diagnostics: styles.diagnostics
3624
+ } : void 0;
2710
3625
  const result = compilePieces(toPieces(node), holes, {
2711
3626
  ...options,
3627
+ ...templateStyles ? { templateStyles } : {},
2712
3628
  hoistTemplates: true,
2713
3629
  // A tag inside a template gets the same mark a JSX tag does. Without one, the nodes it
2714
3630
  // makes are the only ones in the module carrying no mark, and they take whichever was
@@ -2717,12 +3633,13 @@ function transformTemplates(code, id, options = {}) {
2717
3633
  // Same defaults as the plugin, so moving a build over is not a rendering change.
2718
3634
  templateClone: options.templateClone ?? true,
2719
3635
  partialTemplates: nested ? false : options.partialTemplates ?? true,
2720
- svg: node.tag.name === "svg"
3636
+ svg: templateTags.get(node.tag.name) === "svg"
2721
3637
  });
2722
3638
  for (const symbol of result.imports) used.add(symbol);
2723
3639
  collectNeeds(result.ir, needs);
2724
3640
  edits.push({ start: node.start, end: node.end, code: adoptTemplates(result.code, result.templates, hoisted) });
2725
3641
  }
3642
+ if (styles && options.styles) edits.push(...finishShadowStyles(styles, code, id));
2726
3643
  const out = new import_magic_string2.default(code);
2727
3644
  for (const e of outermost(edits)) {
2728
3645
  if (e.start === e.end) out.appendLeft(e.start, e.code);
@@ -2780,13 +3697,19 @@ function transformTemplates(code, id, options = {}) {
2780
3697
  ...loadDiagnostics.length ? { loadDiagnostics } : {},
2781
3698
  ...unresolvedDiagnostics.length ? { unresolvedDiagnostics } : {},
2782
3699
  ...props.diagnostics.length ? { propsDiagnostics: props.diagnostics } : {},
2783
- ...intrinsics.diagnostics.length ? { intrinsicDiagnostics: intrinsics.diagnostics } : {}
3700
+ ...intrinsics.diagnostics.length ? { intrinsicDiagnostics: intrinsics.diagnostics } : {},
3701
+ ...styles?.styles.length ? { styles: styles.styles } : {},
3702
+ ...styles?.diagnostics.length ? { styleDiagnostics: styles.diagnostics } : {},
3703
+ ...styles?.literals.length ? { styleLiterals: styles.literals } : {},
3704
+ ...styles?.references.length ? { styleReferences: styles.references } : {}
2784
3705
  };
2785
3706
  }
2786
- function compileJsx(code, node, edits, nested, used, needs, hoisted, options, sourceFile) {
3707
+ function compileJsx(code, node, edits, nested, used, needs, hoisted, options, sourceFile, styles) {
2787
3708
  const ir = buildJsxIR(node, {
2788
3709
  code: (n) => slice(code, n.start, n.end, edits),
2789
3710
  used,
3711
+ ...styles?.scopes.length ? { scopes: styles.scopes } : {},
3712
+ ...styles?.drop.size ? { dropStyles: styles.drop } : {},
2790
3713
  ...options.sourceLocations === true && sourceFile ? { sourceFile } : {}
2791
3714
  });
2792
3715
  analyzeStatic(ir);