@zerodep-dev/vue-tsx-pro-tsx 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/styles.js ADDED
@@ -0,0 +1,1282 @@
1
+ import { r as untrack } from "./state-YxL7IqUO.js";
2
+ import { ReactiveEffect, getCurrentInstance } from "vue";
3
+ //#region src/styles/priority.ts
4
+ /**
5
+ * 组合类里声明与条件块的排序依据(declarations.ts 的 serialize):沿用 StyleX 的 getPriority(@stylexjs/shared 0.19.1),
6
+ * 省掉了逻辑长属性那张 318 项的表——它们的优先级就是默认的 3000,查表与不查表结果一样。
7
+ *
8
+ * 规则:简写的简写 1000 < 简写 2000 < 长属性 3000 < 物理长属性 4000(后注入的压过先注入的);
9
+ * 条件加在属性上:@supports 30、@media 200、@container 300,伪类按表(:hover 130、:active 170…),伪元素 5000。
10
+ */
11
+ const SHORTHANDS_OF_SHORTHANDS = new Set("animation background border border-block border-inline margin padding font grid grid-template grid-area all inset scroll-margin scroll-padding".split(" "));
12
+ const SHORTHANDS_OF_LONGHANDS = new Set("animation-range scroll-timeline view-timeline background-position border-color border-style border-width border-block-start border-top border-block-end border-bottom border-inline-color border-inline-style border-inline-width border-inline-start border-left border-inline-end border-right border-image border-radius corner-shape caret outline grid-gap gap place-content place-items place-self margin-block margin-inline overscroll-behavior padding-block padding-inline columns column-rule contain-intrinsic-size container flex flex-flow font-variant grid-template-areas grid-row grid-column list-style mask mask-border offset overflow inset-block inset-inline scroll-margin-block scroll-margin-inline scroll-padding-block scroll-padding-inline scroll-snap-type text-decoration text-emphasis transition".split(" "));
13
+ const LONGHANDS_PHYSICAL = new Set("border-top-color border-top-style border-top-width border-bottom-color border-bottom-style border-bottom-width border-left-color border-left-style border-left-width border-right-color border-right-style border-right-width border-top-left-radius border-top-right-radius border-bottom-left-radius border-bottom-right-radius corner-top-left-shape corner-top-right-shape corner-bottom-left-shape corner-bottom-right-shape height width max-height max-width min-height min-width margin-top margin-bottom margin-left margin-right overscroll-behavior-y overscroll-behavior-x padding-top padding-bottom padding-left padding-right overflow-y overflow-x top bottom left right scroll-margin-top scroll-margin-bottom scroll-margin-left scroll-margin-right scroll-padding-top scroll-padding-bottom scroll-padding-left scroll-padding-right".split(" "));
14
+ /** 伪类的优先级(不在表里的 40);:focusWithin、:focusVisible 的驼峰写法照抄 StyleX */
15
+ const PSEUDO_CLASSES = {
16
+ ":is": 40,
17
+ ":where": 40,
18
+ ":not": 40,
19
+ ":has": 45,
20
+ ":dir": 50,
21
+ ":lang": 51,
22
+ ":first-child": 52,
23
+ ":first-of-type": 53,
24
+ ":last-child": 54,
25
+ ":last-of-type": 55,
26
+ ":only-child": 56,
27
+ ":only-of-type": 57,
28
+ ":nth-child": 60,
29
+ ":nth-last-child": 61,
30
+ ":nth-of-type": 62,
31
+ ":nth-last-of-type": 63,
32
+ ":empty": 70,
33
+ ":link": 80,
34
+ ":any-link": 81,
35
+ ":local-link": 82,
36
+ ":target-within": 83,
37
+ ":target": 84,
38
+ ":visited": 85,
39
+ ":enabled": 91,
40
+ ":disabled": 92,
41
+ ":required": 93,
42
+ ":optional": 94,
43
+ ":read-only": 95,
44
+ ":read-write": 96,
45
+ ":placeholder-shown": 97,
46
+ ":in-range": 98,
47
+ ":out-of-range": 99,
48
+ ":default": 100,
49
+ ":checked": 101,
50
+ ":indeterminate": 101,
51
+ ":blank": 102,
52
+ ":valid": 103,
53
+ ":invalid": 104,
54
+ ":user-invalid": 105,
55
+ ":autofill": 110,
56
+ ":picture-in-picture": 120,
57
+ ":modal": 121,
58
+ ":fullscreen": 122,
59
+ ":paused": 123,
60
+ ":playing": 124,
61
+ ":current": 125,
62
+ ":past": 126,
63
+ ":future": 127,
64
+ ":hover": 130,
65
+ ":focusWithin": 140,
66
+ ":focus": 150,
67
+ ":focusVisible": 160,
68
+ ":active": 170
69
+ };
70
+ const PSEUDO_ELEMENT = 5e3;
71
+ /** when.* 生成的关系选择器:基数 + 被观察元素那个伪类的优先级 / 100 */
72
+ const RELATIONAL = [
73
+ [/^:where\(\.[0-9a-zA-Z_-]+(:[a-zA-Z-]+)\s+\*\)$/, 10],
74
+ [/^:where\(:has\(\.[0-9a-zA-Z_-]+(:[a-zA-Z-]+)\)\)$/, 15],
75
+ [/^:where\(\.[0-9a-zA-Z_-]+(:[a-zA-Z-]+)\s+~\s+\*\)$/, 30],
76
+ [/^:where\(:has\(~\s\.[0-9a-zA-Z_-]+(:[a-zA-Z-]+)\)\)$/, 40]
77
+ ];
78
+ const ANY_SIBLING = /^:where\(\.[0-9a-zA-Z_-]+(:[a-zA-Z-]+)\s+~\s+\*,\s+:has\(~\s\.[0-9a-zA-Z_-]+(:[a-zA-Z-]+)\)\)$/;
79
+ const PSEUDO_PART = /::[a-zA-Z-]+|:[a-zA-Z-]+(?:\([^)]*\))?/g;
80
+ const pseudoClass = (name) => PSEUDO_CLASSES[name] ?? 40;
81
+ /** 属性名(连字符)、伪类、伪元素或 @ 规则的优先级 */
82
+ function getPriority(key) {
83
+ if (key.startsWith("--")) return 1;
84
+ if (key.startsWith("@supports")) return 30;
85
+ if (key.startsWith("@media")) return 200;
86
+ if (key.startsWith("@container")) return 300;
87
+ const parts = key.match(PSEUDO_PART);
88
+ if (parts && parts.length > 1 && !parts.some((part) => part.includes("("))) return parts.reduce((sum, part) => sum + (part.startsWith("::") ? PSEUDO_ELEMENT : pseudoClass(part)), 0);
89
+ if (key.startsWith("::")) return PSEUDO_ELEMENT;
90
+ if (key.startsWith(":")) {
91
+ for (const [pattern, base] of RELATIONAL) {
92
+ const match = pattern.exec(key);
93
+ if (match) return base + pseudoClass(match[1]) / 100;
94
+ }
95
+ const sibling = ANY_SIBLING.exec(key);
96
+ if (sibling) return 20 + Math.max(pseudoClass(sibling[1]), pseudoClass(sibling[2])) / 100;
97
+ return pseudoClass(key.split("(")[0]);
98
+ }
99
+ if (SHORTHANDS_OF_SHORTHANDS.has(key)) return 1e3;
100
+ if (SHORTHANDS_OF_LONGHANDS.has(key)) return 2e3;
101
+ if (LONGHANDS_PHYSICAL.has(key)) return 4e3;
102
+ return 3e3;
103
+ }
104
+ //#endregion
105
+ //#region src/styles/token-map.ts
106
+ /**
107
+ * 主题 tokens 字符串写法的对照表:属性 → token 组(参考 Stitches 的 themeMap)。
108
+ *
109
+ * 表里的属性可以写短写 `fontSize: '_xs'`(= fontSizes.xs);其他属性、或要跨组取值时写带组名的 `'_space.md'`。
110
+ * 组名沿用 Stitches / styled-system 的约定,主题库按这些名字导出 defineVars。
111
+ * 用到它的地方:编译器(compiler/tokens.ts)改写字符串,scripts/css-docs 给这些属性生成短写的取值类型。
112
+ */
113
+ const sides = (prefix, suffix = "") => [
114
+ "",
115
+ "Top",
116
+ "Right",
117
+ "Bottom",
118
+ "Left",
119
+ "Block",
120
+ "BlockStart",
121
+ "BlockEnd",
122
+ "Inline",
123
+ "InlineStart",
124
+ "InlineEnd"
125
+ ].map((side) => `${prefix}${side}${suffix}`);
126
+ const groups = {
127
+ space: [
128
+ "gap",
129
+ "rowGap",
130
+ "columnGap",
131
+ ...sides("padding"),
132
+ ...sides("margin"),
133
+ ...sides("scrollMargin"),
134
+ ...sides("scrollPadding"),
135
+ "inset",
136
+ "insetBlock",
137
+ "insetBlockStart",
138
+ "insetBlockEnd",
139
+ "insetInline",
140
+ "insetInlineStart",
141
+ "insetInlineEnd",
142
+ "top",
143
+ "right",
144
+ "bottom",
145
+ "left"
146
+ ],
147
+ colors: [
148
+ "color",
149
+ "backgroundColor",
150
+ ...sides("border", "Color"),
151
+ "outlineColor",
152
+ "columnRuleColor",
153
+ "textDecorationColor",
154
+ "textEmphasisColor",
155
+ "caretColor",
156
+ "accentColor",
157
+ "fill",
158
+ "stroke",
159
+ "WebkitTextFillColor",
160
+ "WebkitTextStrokeColor"
161
+ ],
162
+ fonts: ["fontFamily"],
163
+ fontSizes: ["fontSize"],
164
+ fontWeights: ["fontWeight"],
165
+ lineHeights: ["lineHeight"],
166
+ letterSpacings: ["letterSpacing"],
167
+ sizes: [
168
+ "width",
169
+ "height",
170
+ "minWidth",
171
+ "minHeight",
172
+ "maxWidth",
173
+ "maxHeight",
174
+ "inlineSize",
175
+ "blockSize",
176
+ "minInlineSize",
177
+ "minBlockSize",
178
+ "maxInlineSize",
179
+ "maxBlockSize",
180
+ "flexBasis"
181
+ ],
182
+ borderWidths: [
183
+ ...sides("border", "Width"),
184
+ "outlineWidth",
185
+ "outlineOffset",
186
+ "columnRuleWidth",
187
+ "textDecorationThickness"
188
+ ],
189
+ opacities: ["opacity"],
190
+ strokeWidths: ["strokeWidth"],
191
+ radii: [
192
+ "borderRadius",
193
+ "borderTopLeftRadius",
194
+ "borderTopRightRadius",
195
+ "borderBottomLeftRadius",
196
+ "borderBottomRightRadius",
197
+ "borderStartStartRadius",
198
+ "borderStartEndRadius",
199
+ "borderEndStartRadius",
200
+ "borderEndEndRadius"
201
+ ],
202
+ shadows: ["boxShadow"],
203
+ durations: [
204
+ "transitionDuration",
205
+ "transitionDelay",
206
+ "animationDuration",
207
+ "animationDelay"
208
+ ],
209
+ easings: ["transitionTimingFunction", "animationTimingFunction"]
210
+ };
211
+ /** CSS 属性(StyleX 的写法,如 backgroundColor)→ 短写取值的 token 组 */
212
+ const TOKEN_GROUP_OF = new Map(Object.entries(groups).flatMap(([group, properties]) => properties.map((property) => [property, group])));
213
+ //#endregion
214
+ //#region src/styles/tokens.ts
215
+ /**
216
+ * 主题 tokens 的短写:样式里整个值写 '_xs'(按属性查 token-map.ts 的对照表找组)或 '_space.md'(带组名),
217
+ * 展开声明时换成 token 变量 `var(--…)`。tokens 由主题库用 defineTokens 登记(@zerodep-dev/vue-tsx-pro-ui 的 config/theme/tokens.ts)。
218
+ * 补全、悬停靠类型(主题库用 declare module 填入的 token 接口);写错的 token 在第一次用到时报错。
219
+ */
220
+ /** 组名(colors、space…)→ 这组 token 的变量引用 */
221
+ const registry = /* @__PURE__ */ new Map();
222
+ const TOKEN = /^_(?:(\w+)\.)?(\w+)$/;
223
+ /** 值中间夹着的 token 短写('calc(-1 * _xs)'):只认整个值,夹在中间的不会换成变量 */
224
+ const EMBEDDED = /(?:^|[\s(,])(_[A-Za-z]\w*(?:\.\w+)?)(?=$|[\s),])/;
225
+ /**
226
+ * 登记主题 tokens:组名 → defineVars 的结果,返回原对象(主题库把它导出成 `t`,`t.space.md` 用在模板字符串里)。
227
+ * 同名的组后登记的为准
228
+ */
229
+ function defineTokens$1(groups) {
230
+ for (const [name, group] of Object.entries(groups)) registry.set(name, group);
231
+ return groups;
232
+ }
233
+ /** 整个值是 token 的短写时换成 token 变量;不是时原样返回,夹在值中间的短写报错 */
234
+ function resolveToken(property, text) {
235
+ const match = text.charCodeAt(0) === 95 ? TOKEN.exec(text) : null;
236
+ if (!match) {
237
+ const embedded = text.includes("_") ? EMBEDDED.exec(text)?.[1] : void 0;
238
+ if (embedded) {
239
+ const path = embedded.includes(".") ? embedded.slice(1) : `组.${embedded.slice(1)}`;
240
+ throw new Error(`样式:${property} 的值 '${text}' 中间夹着 token ${embedded},只有整个值是 token 时才换成变量;请写成模板字符串里的 \${t.${path}}`);
241
+ }
242
+ return text;
243
+ }
244
+ const [, qualified, name] = match;
245
+ const group = qualified ?? TOKEN_GROUP_OF.get(property);
246
+ if (!group) throw new Error(`样式:${property} 没有短写对应的 token 组,'${text}' 要写带组名的 '_组.${name}'`);
247
+ const value = registry.get(group)?.[name];
248
+ if (value === void 0) {
249
+ const hint = registry.size ? "" : ";主题 tokens 还没登记(defineTokens)";
250
+ throw new Error(`样式:找不到 token '${text}'(${property},${group} 组)${hint}`);
251
+ }
252
+ return value;
253
+ }
254
+ //#endregion
255
+ //#region src/styles/units.ts
256
+ /**
257
+ * 样式里数字值的单位规则(与 StyleX 相同):样式引擎(declarations.ts 的 cssValue)与 CSS 注释生成(scripts/css-docs)共用。
258
+ * 不依赖别的模块:生成脚本直接用 Node 读这个 .ts 文件
259
+ */
260
+ /** 数字值不补单位的属性(其余补 px) */
261
+ const UNITLESS = new Set("WebkitLineClamp animationIterationCount aspectRatio borderImageOutset borderImageSlice borderImageWidth counterSet counterReset columnCount flex flexGrow flexShrink flexOrder gridRow gridRowStart gridRowEnd gridColumn gridColumnStart gridColumnEnd gridArea fontWeight hyphenateLimitChars lineClamp lineHeight maskBorderOutset maskBorderSlice maskBorderWidth opacity order orphans tabSize widows zIndex fillOpacity floodOpacity rotate scale shapeImageThreshold stopOpacity strokeDasharray strokeDashoffset strokeMiterlimit strokeOpacity strokeWidth mathDepth zoom".split(" "));
262
+ /** 数字值补 ms 的属性 */
263
+ const MILLISECONDS = /* @__PURE__ */ new Set([
264
+ "animationDelay",
265
+ "animationDuration",
266
+ "transitionDelay",
267
+ "transitionDuration",
268
+ "voiceDuration"
269
+ ]);
270
+ /** camelCase → kebab-case;自定义属性(--x)原样 */
271
+ const dashify = (property) => property.startsWith("--") ? property : property.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
272
+ /** 值转成 CSS 文本:token 短写换成 token 变量(tokens.ts),数字按属性补单位(四位小数) */
273
+ function cssValue(property, value) {
274
+ if (typeof value === "string") return resolveToken(property, value.trim());
275
+ const rounded = String(Math.round(value * 1e4) / 1e4);
276
+ if (UNITLESS.has(property) || property.startsWith("--")) return rounded;
277
+ return rounded + (MILLISECONDS.has(property) ? "ms" : "px");
278
+ }
279
+ /** 条件别名表(见 ALIASES);值保留字面量类型,伪类别名可以直接交给 `when.*`(它要 `:` 开头的选择器) */
280
+ const CONDITIONS = {
281
+ _hover: ":hover",
282
+ _active: ":active",
283
+ _focus: ":focus",
284
+ _focusVisible: ":focus-visible",
285
+ _focusWithin: ":focus-within",
286
+ _disabled: ":is(:disabled,[aria-disabled=\"true\"],[data-disabled])",
287
+ _selected: ":is([aria-selected=\"true\"],[data-selected])",
288
+ _checked: ":is(:checked,[aria-checked=\"true\"],[data-state=\"checked\"])",
289
+ _indeterminate: ":is(:indeterminate,[aria-checked=\"mixed\"],[data-state=\"indeterminate\"])",
290
+ _pressed: ":is([aria-pressed=\"true\"],[data-pressed])",
291
+ _expanded: ":is([aria-expanded=\"true\"],[data-expanded])",
292
+ _open: ":is([open],[data-state=\"open\"])",
293
+ _current: ":is([aria-current]:not([aria-current=\"false\"]),[data-current])",
294
+ _invalid: ":is([aria-invalid=\"true\"],[data-invalid])",
295
+ _readonly: ":is([aria-readonly=\"true\"],[data-readonly])",
296
+ _highlighted: ":is([data-highlighted])",
297
+ _dragging: ":is([data-dragging])",
298
+ _placeholder: "::placeholder",
299
+ _forcedColors: "@media (forced-colors: active)",
300
+ _reducedMotion: "@media (prefers-reduced-motion: reduce)",
301
+ _rtl: ":dir(rtl)"
302
+ };
303
+ /** 是普通对象(条件对象、命名空间),不是数组 */
304
+ const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
305
+ /** 条件键 → 选择器片段或 @ 规则;写错的别名、裸的属性选择器直接报错(否则会变成无条件样式) */
306
+ function condition(property, key) {
307
+ const resolved = conditionOf(key);
308
+ if (!resolved) throw new Error(`样式:${property} 的条件 "${key}" 不是伪类、属性选择器、@ 规则或已知的别名`);
309
+ return resolved;
310
+ }
311
+ /**
312
+ * 条件键 → 选择器片段或 @ 规则;不是条件时 undefined。属性选择器('[data-hot]'、'[data-state="open"]')
313
+ * 包成 :is(…):状态写在 DOM 属性上,状态变化只改属性、类名不变(同条件别名)
314
+ */
315
+ function conditionOf(key) {
316
+ const resolved = CONDITIONS[key] ?? key;
317
+ if (resolved.startsWith(":") || resolved.startsWith("@")) return resolved;
318
+ if (resolved.startsWith("[") && resolved.endsWith("]")) return `:is(${resolved})`;
319
+ }
320
+ const texts = (property, value) => (Array.isArray(value) ? value : [value]).map((v) => cssValue(property, v));
321
+ /** 属性键:变量组的 token(`[shimmerVars.highlight]: …`,键是 var(--…))设置的是那个变量 */
322
+ const propertyOf = (key) => key.startsWith("var(--") ? key.slice(4, -1) : key;
323
+ /** 命名空间的每个叶子。顶层的条件块('::before': {…}、':hover': {…})展开成各属性的条件值 */
324
+ function eachLeaf(namespace, visit) {
325
+ for (const [name, value] of Object.entries(namespace)) {
326
+ const resolved = conditionOf(name);
327
+ const key = propertyOf(name);
328
+ if (resolved && isObject(value)) for (const [nestedName, nested] of Object.entries(value)) {
329
+ const property = propertyOf(nestedName);
330
+ walk(property, nested, [resolved], (chain, leaf) => visit({
331
+ property,
332
+ replace: false,
333
+ chain,
334
+ texts: texts(property, leaf)
335
+ }));
336
+ }
337
+ else if (value === null || value === void 0) visit({
338
+ property: key,
339
+ replace: true,
340
+ chain: [],
341
+ texts: null
342
+ });
343
+ else {
344
+ const replace = !isObject(value);
345
+ walk(key, value, [], (chain, leaf) => visit({
346
+ property: key,
347
+ replace,
348
+ chain,
349
+ texts: texts(key, leaf)
350
+ }));
351
+ }
352
+ }
353
+ }
354
+ /** 叶子 → 声明表 */
355
+ function tableOf(leaves) {
356
+ const table = /* @__PURE__ */ new Map();
357
+ for (const { property, replace, chain, texts } of leaves) {
358
+ const dashed = dashify(property);
359
+ let entry = table.get(dashed);
360
+ if (!entry) table.set(dashed, entry = {
361
+ replace,
362
+ values: /* @__PURE__ */ new Map()
363
+ });
364
+ if (texts) entry.values.set(chain.join("\0"), texts);
365
+ }
366
+ return table;
367
+ }
368
+ /** 命名空间 → 声明表 */
369
+ function declarations(namespace) {
370
+ const leaves = [];
371
+ eachLeaf(namespace, (leaf) => leaves.push(leaf));
372
+ return tableOf(leaves);
373
+ }
374
+ /** 条件对象逐层展开到叶子;null / undefined 不产出(条件里的 null 表示“这个条件不改”) */
375
+ function walk(property, value, chain, visit) {
376
+ if (value === null || value === void 0) return;
377
+ if (!isObject(value)) return visit(chain, value);
378
+ for (const [key, nested] of Object.entries(value)) walk(property, nested, key === "default" ? chain : [...chain, condition(property, key)], visit);
379
+ }
380
+ /** 合并几个片段的声明表(规则见文件开头),不改动片段本身 */
381
+ function merge(tables) {
382
+ if (tables.length === 1) return tables[0];
383
+ const merged = /* @__PURE__ */ new Map();
384
+ for (const table of tables) for (const [property, entry] of table) {
385
+ const previous = merged.get(property);
386
+ if (!previous || entry.replace) merged.set(property, entry);
387
+ else merged.set(property, {
388
+ replace: false,
389
+ values: new Map([...previous.values, ...entry.values])
390
+ });
391
+ }
392
+ return merged;
393
+ }
394
+ /** 条件链的排序权重:伪类、@ 规则的优先级相加(同 StyleX 的规则顺序) */
395
+ const chainWeight = (chain) => chain ? chain.split("\0").reduce((sum, part) => sum + getPriority(part), 0) : 0;
396
+ /**
397
+ * 声明表 → 规则文本(类名用 SELF 占位)。每个条件链一条规则:
398
+ * @ 规则里的选择器按层数重复类名(.c.c),压过同级的伪类;when.*(:where)再重复一次,同 StyleX
399
+ */
400
+ function serialize(table) {
401
+ const byChain = /* @__PURE__ */ new Map();
402
+ for (const [property, entry] of table) for (const [chain, values] of entry.values) {
403
+ let list = byChain.get(chain);
404
+ if (!list) byChain.set(chain, list = []);
405
+ list.push([property, values]);
406
+ }
407
+ return [...byChain].sort(([a], [b]) => chainWeight(a) - chainWeight(b)).map(([chain, list]) => {
408
+ const body = list.sort(([a], [b]) => getPriority(a) - getPriority(b)).flatMap(([property, values]) => values.map((value) => `${property}:${value}`)).join(";");
409
+ const parts = chain ? chain.split("\0") : [];
410
+ const atRules = parts.filter((part) => part.startsWith("@"));
411
+ const pseudos = parts.filter((part) => part.startsWith(":"));
412
+ const classes = pseudos.filter((part) => !part.startsWith("::")).join("");
413
+ const elements = pseudos.filter((part) => part.startsWith("::")).join("");
414
+ const selector = `.`.repeat(1 + atRules.length + (classes.includes(":where(") ? 1 : 0)) + classes + elements;
415
+ return atRules.reduceRight((rule, atRule) => `${atRule}{${rule}}`, `${selector}{${body}}`);
416
+ });
417
+ }
418
+ //#endregion
419
+ //#region src/styles/sheet.ts
420
+ /**
421
+ * 运行时样式引擎的样式表:一张 <style>,规则按名字(类名、关键帧名)登记,同样的内容只插一次;
422
+ * 记下规则原文(SSR 输出、测试断言用),热更新时按名字换掉。
423
+ */
424
+ /**
425
+ * 插过的规则:类名(关键帧名、变量名)→ 规则原文与 CSSOM 里的规则。同样的内容只插一次;
426
+ * 原文给 SSR 输出 <style>,也不依赖 CSSOM 的序列化(浏览器、测试用的 DOM 会丢掉不认识的声明);
427
+ * CSSOM 的规则留着,热更新时按类名换掉
428
+ */
429
+ const inserted = /* @__PURE__ */ new Map();
430
+ let sheet;
431
+ /** 引擎自己的样式表:放在 head 最前面,宿主页面后加载的同等特异性的 CSS 能盖过组件库 */
432
+ function styleSheet() {
433
+ if (sheet !== void 0) return sheet;
434
+ if (typeof document === "undefined") return sheet = null;
435
+ const element = document.createElement("style");
436
+ element.dataset.vueTsxPro = "";
437
+ document.head.prepend(element);
438
+ return sheet = element.sheet;
439
+ }
440
+ /** 插入一个名字的规则(已有时先删掉旧的:热更新改写规则) */
441
+ function insert(name, texts) {
442
+ const target = styleSheet();
443
+ const previous = inserted.get(name);
444
+ if (previous && target) for (const rule of previous.rules) {
445
+ const index = Array.prototype.indexOf.call(target.cssRules, rule);
446
+ if (index >= 0) target.deleteRule(index);
447
+ }
448
+ const rules = [];
449
+ if (target) for (const text of texts) try {
450
+ rules.push(target.cssRules[target.insertRule(text, target.cssRules.length)]);
451
+ } catch {}
452
+ inserted.set(name, {
453
+ texts,
454
+ rules
455
+ });
456
+ }
457
+ /** 这个名字的规则插过没有 */
458
+ const isInserted = (name) => inserted.has(name);
459
+ /** 已插入的全部规则原文,一条一行(SSR 输出 <style> 用) */
460
+ function styleText() {
461
+ return [...inserted.values()].flatMap((record) => record.texts).join("\n");
462
+ }
463
+ /** murmurhash2(同 @emotion/hash、StyleX 的 createHash),转 36 进制 */
464
+ function hash(text) {
465
+ let h = 0;
466
+ let i = 0;
467
+ let length = text.length;
468
+ for (; length >= 4; ++i, length -= 4) {
469
+ let k = text.charCodeAt(i) & 255 | (text.charCodeAt(++i) & 255) << 8 | (text.charCodeAt(++i) & 255) << 16 | (text.charCodeAt(++i) & 255) << 24;
470
+ k = (k & 65535) * 1540483477 + ((k >>> 16) * 59797 << 16);
471
+ k ^= k >>> 24;
472
+ h = (k & 65535) * 1540483477 + ((k >>> 16) * 59797 << 16) ^ (h & 65535) * 1540483477 + ((h >>> 16) * 59797 << 16);
473
+ }
474
+ if (length >= 3) h ^= (text.charCodeAt(i + 2) & 255) << 16;
475
+ if (length >= 2) h ^= (text.charCodeAt(i + 1) & 255) << 8;
476
+ if (length >= 1) {
477
+ h ^= text.charCodeAt(i) & 255;
478
+ h = (h & 65535) * 1540483477 + ((h >>> 16) * 59797 << 16);
479
+ }
480
+ h ^= h >>> 13;
481
+ h = (h & 65535) * 1540483477 + ((h >>> 16) * 59797 << 16);
482
+ return ((h ^ h >>> 15) >>> 0).toString(36);
483
+ }
484
+ //#endregion
485
+ //#region src/styles/fragment.ts
486
+ /**
487
+ * 片段与组合类:create 的每个命名空间是一个片段(声明表);attrs 把一个元素的片段合成一个类,第一次出现时插入规则。
488
+ * 记下“组合类 ← 片段”的关系,热更新换了片段的声明时按原类名改写规则。
489
+ */
490
+ /** 片段上的声明表 */
491
+ const FRAGMENT = Symbol("style fragment");
492
+ /** 固定类(createTheme 的主题类、defineMarker 的标记类)上的 [去重键, 类名] */
493
+ const CLASS = Symbol("style class");
494
+ /** 动态样式的结果([片段, 行内变量])的标记 */
495
+ const DYNAMIC = Symbol("style dynamic");
496
+ /** css.bind 的结果的标记 */
497
+ const BINDING = Symbol("style binding");
498
+ /** 动态样式函数上的求值入口 */
499
+ const EVALUATE = Symbol("style evaluate");
500
+ /** 新的片段;声明表可写:热更新时换成新的(hotStyles),片段对象本身不变,用到它的缓存、已渲染的类照样有效 */
501
+ const fragment = (table) => Object.defineProperty({}, FRAGMENT, {
502
+ value: table,
503
+ writable: true
504
+ });
505
+ /** 是 create 产出的片段 */
506
+ const isFragment = (value) => typeof value === "object" && value !== null && FRAGMENT in value;
507
+ /** 组合类 → 组成它的片段;片段 → 用到它的组合类(热更新改了片段时据此改写规则) */
508
+ const composites = /* @__PURE__ */ new Map();
509
+ const usedBy = /* @__PURE__ */ new WeakMap();
510
+ /** 组合一组片段:类名是规则内容的哈希,第一次出现时插入 */
511
+ function composite(fragments) {
512
+ const rules = serialize(merge(fragments.map((part) => part[FRAGMENT])));
513
+ if (!rules.length) return "";
514
+ const name = `x${hash(rules.join(""))}`;
515
+ if (!isInserted(name)) insert(name, rules.map((rule) => rule.replaceAll("", name)));
516
+ if (!composites.has(name)) {
517
+ composites.set(name, fragments);
518
+ for (const part of fragments) {
519
+ let names = usedBy.get(part);
520
+ if (!names) usedBy.set(part, names = /* @__PURE__ */ new Set());
521
+ names.add(name);
522
+ }
523
+ }
524
+ return name;
525
+ }
526
+ /** 片段换成新的声明表,用到它的组合类按原类名重写规则 */
527
+ function replaceFragment(target, table) {
528
+ target[FRAGMENT] = table;
529
+ for (const name of usedBy.get(target) ?? []) insert(name, serialize(merge(composites.get(name).map((part) => part[FRAGMENT]))).map((rule) => rule.replaceAll("", name)));
530
+ }
531
+ //#endregion
532
+ //#region src/styles/compose.ts
533
+ /**
534
+ * 一个元素的样式组合成类:片段合成一个组合类,固定类(主题、标记)原样挂上。
535
+ * attrs 渲染时用它;元素上的绑定换了其中一段时也用它就地算出新类(bindings.ts)。
536
+ */
537
+ const cache = {};
538
+ /**
539
+ * 一组样式(按书写顺序)→ 元素的类。按参与的对象逐层查缓存,命中时不拼字符串、不算哈希,
540
+ * 同样的组合返回同一个结果对象(Vue 比对 class 相同就不碰 DOM)
541
+ */
542
+ function compose(parts) {
543
+ let node = cache;
544
+ for (const part of parts) {
545
+ node.next ??= /* @__PURE__ */ new WeakMap();
546
+ let child = node.next.get(part);
547
+ if (!child) node.next.set(part, child = {});
548
+ node = child;
549
+ }
550
+ return node.result ??= resolve(parts);
551
+ }
552
+ /** 缓存没命中:片段合成一个类;固定类(主题、标记)按去重键后者覆盖前者 */
553
+ function resolve(parts) {
554
+ const fragments = [];
555
+ const fixed = /* @__PURE__ */ new Map();
556
+ for (const part of parts) if (FRAGMENT in part) fragments.push(part);
557
+ else if (CLASS in part) {
558
+ const [key, className] = part[CLASS];
559
+ fixed.delete(key);
560
+ fixed.set(key, className);
561
+ } else throw new Error("attrs:不认识的样式,只收 create、createTheme、defineMarker 的结果");
562
+ const classes = [fragments.length ? composite(fragments) : "", ...fixed.values()].filter(Boolean);
563
+ return Object.freeze(classes.length ? { class: classes.join(" ") } : {});
564
+ }
565
+ //#endregion
566
+ //#region src/styles/devtools.ts
567
+ /** 开发模式(打包工具把 process.env.NODE_ENV 换成字面量,生产构建里这段代码被去掉) */
568
+ const DEV = process.env.NODE_ENV !== "production";
569
+ const LAYER = "vue-tsx-pro:style-bindings";
570
+ /** 每个应用的时间线接口;载入中时为 Promise */
571
+ const apis = /* @__PURE__ */ new WeakMap();
572
+ /** 页面装了 devtools(扩展或独立版注入的全局钩子) */
573
+ const hasDevtools = () => typeof window !== "undefined" && !!window.__VUE_DEVTOOLS_GLOBAL_HOOK__;
574
+ function timeline(app) {
575
+ if (apis.has(app)) return apis.get(app);
576
+ const loading = import("@vue/devtools-api").then(({ setupDevtoolsPlugin }) => new Promise((resolve) => setupDevtoolsPlugin({
577
+ id: LAYER,
578
+ label: "vue-tsx-pro 样式",
579
+ packageName: "@zerodep-dev/vue-tsx-pro-tsx",
580
+ app,
581
+ enableEarlyProxy: true
582
+ }, (api) => {
583
+ api.addTimelineLayer({
584
+ id: LAYER,
585
+ label: "样式绑定",
586
+ color: 4372611
587
+ });
588
+ resolve(api);
589
+ }))).catch(() => null).then((api) => {
590
+ apis.set(app, api);
591
+ return api;
592
+ });
593
+ apis.set(app, loading);
594
+ return loading;
595
+ }
596
+ /** 记一条“样式绑定更新”:只在开发模式、装了 devtools 时调用有效 */
597
+ function recordBindingUpdate(instance, element, change) {
598
+ if (!instance || !hasDevtools()) return;
599
+ const name = instance.type.name ?? instance.type.__name ?? "匿名组件";
600
+ const changed = Object.keys(change.variables).length;
601
+ const event = {
602
+ title: name,
603
+ subtitle: [change.className && "换类", changed && `改变量 ×${changed}`].filter(Boolean).join(","),
604
+ data: {
605
+ 组件: name,
606
+ 元素: describe(element),
607
+ ...change.className && { 换类: `${change.className[0]} → ${change.className[1]}` },
608
+ ...changed && { 变量: change.variables }
609
+ }
610
+ };
611
+ const api = timeline(instance.appContext.app);
612
+ const add = (target) => target?.addTimelineEvent({
613
+ layerId: LAYER,
614
+ event: {
615
+ time: target.now(),
616
+ ...event
617
+ }
618
+ });
619
+ if (api instanceof Promise) api.then(add);
620
+ else add(api);
621
+ }
622
+ /** 元素的简短描述:标签、id、使用方给的类(不含引擎生成的 x 开头的类) */
623
+ function describe(element) {
624
+ const id = element.id ? `#${element.id}` : "";
625
+ const classes = [...element.classList].filter((name) => !/^x[0-9a-z]+$/.test(name)).map((name) => `.${name}`).join("");
626
+ return `<${element.tagName.toLowerCase()}${id}${classes}>`;
627
+ }
628
+ //#endregion
629
+ //#region src/styles/bindings.ts
630
+ /**
631
+ * 元素上的响应式绑定(css.bind、css.select 交给 attrs 之后):元素挂上时起一个 effect,参数变化时只改元素上的
632
+ * CSS 变量;选中的样式或动态样式的形状变了时就地换组合类。组件不重新渲染;组件因别的原因重新渲染时复用元素上的
633
+ * effect,卸载时停掉。开发模式下每次更新记到 Vue devtools 的时间线(devtools.ts)。
634
+ */
635
+ const boundElements = /* @__PURE__ */ new WeakMap();
636
+ /** 变了的绑定攒到同一个微任务里批量更新(同一轮里改几次参数只改一次 DOM) */
637
+ const dirty = /* @__PURE__ */ new Set();
638
+ function schedule(bound) {
639
+ if (!dirty.size) queueMicrotask(flush);
640
+ dirty.add(bound);
641
+ }
642
+ function flush() {
643
+ const batch = [...dirty];
644
+ dirty.clear();
645
+ for (const bound of batch) if (!bound.stopped) bound.effect.run();
646
+ }
647
+ /**
648
+ * 读绑定的参数(effect 借此收集依赖);参数没变的不再求值,变了的只改元素上变了的变量。
649
+ * 选中的样式或形状变了(多出、少了属性)时按换过的那组样式重新组合,就地换掉元素上的类
650
+ */
651
+ function apply(element, bound) {
652
+ let reshaped = false;
653
+ const change = DEV ? { variables: {} } : void 0;
654
+ for (const entry of bound.bindings) {
655
+ const args = entry.binding.args();
656
+ if (sameArgs(args, entry.args)) continue;
657
+ const { part, inline } = entry.binding.style[EVALUATE](args);
658
+ entry.args = args;
659
+ if (part !== bound.parts[entry.index]) {
660
+ bound.parts[entry.index] = part;
661
+ reshaped = true;
662
+ }
663
+ const previous = entry.inline;
664
+ entry.inline = inline;
665
+ for (const name in inline) {
666
+ if (previous[name] === inline[name]) continue;
667
+ element.style.setProperty(name, inline[name]);
668
+ if (change) change.variables[name] = inline[name];
669
+ }
670
+ for (const name in previous) {
671
+ if (name in inline || ownedByOthers(bound, entry, name)) continue;
672
+ element.style.removeProperty(name);
673
+ if (change) change.variables[name] = null;
674
+ }
675
+ }
676
+ if (reshaped) {
677
+ const before = bound.className;
678
+ swapClass(element, bound, compose(bound.parts).class ?? "");
679
+ if (change && before !== bound.className) change.className = [before, bound.className];
680
+ }
681
+ if (change && (change.className || Object.keys(change.variables).length)) recordBindingUpdate(bound.instance, element, change);
682
+ }
683
+ /** 同一元素上别的绑定也写着这个变量(同一个动态样式绑了两次):不删 */
684
+ const ownedByOthers = (bound, self, name) => bound.bindings.some((entry) => entry !== self && name in entry.inline);
685
+ /**
686
+ * 就地换类:只动引擎给的那几个类,元素上别的类(使用方的 class、过渡类)不碰。
687
+ * Vue 的虚拟节点里还是渲染时的类;组件之后因别的原因重新渲染时按当时的状态算出类,与元素上的一致
688
+ */
689
+ function swapClass(element, bound, next) {
690
+ const before = bound.className ? bound.className.split(" ") : [];
691
+ const after = next ? next.split(" ") : [];
692
+ const removed = before.filter((name) => !after.includes(name));
693
+ const added = after.filter((name) => !before.includes(name));
694
+ const pairs = Math.min(removed.length, added.length);
695
+ for (let index = 0; index < pairs; index++) element.classList.replace(removed[index], added[index]);
696
+ for (const name of removed.slice(pairs)) element.classList.remove(name);
697
+ for (const name of added.slice(pairs)) element.classList.add(name);
698
+ bound.className = next;
699
+ }
700
+ /**
701
+ * 参数没变:每一项相同,并且都是原始值或样式本身。对象参数(动态样式读它的属性)即使是同一个也要重新求值,
702
+ * 读它属性的那些依赖是求值时收集的
703
+ */
704
+ function sameArgs(next, previous) {
705
+ if (next.length !== previous.length) return false;
706
+ for (let index = 0; index < next.length; index++) {
707
+ const value = next[index];
708
+ if (!Object.is(value, previous[index])) return false;
709
+ if (typeof value === "object" && value !== null && !(FRAGMENT in value) && !(CLASS in value)) return false;
710
+ if (typeof value === "function") return false;
711
+ }
712
+ return true;
713
+ }
714
+ /**
715
+ * 元素的 ref:挂上时起一个 effect(Vue 公开的 ReactiveEffect,依赖变化时排进微任务)。第一次运行只为收集依赖:
716
+ * 读到的参数与渲染时相同,就不再求值、不碰 DOM(挂载时参数只算一次)。
717
+ * 组件重新渲染会给一个新的 ref 函数,同一个元素就复用原来的 effect,换上这次的样式与读取函数;读取函数读的来源
718
+ * 可能变了(不稳定的绑定)时重跑一次重新收集依赖。卸载时 Vue 用 null 调用 ref,停掉 effect。
719
+ * (ref 函数换了时 Vue 不会用 null 调旧的那个,所以状态按元素存)
720
+ */
721
+ function watchBindings(bindings, parts, className) {
722
+ let current;
723
+ const instance = DEV ? getCurrentInstance() : null;
724
+ return (element) => {
725
+ if (!element) {
726
+ if (!current) return;
727
+ const bound = boundElements.get(current);
728
+ if (bound) {
729
+ bound.stopped = true;
730
+ bound.effect.stop();
731
+ }
732
+ boundElements.delete(current);
733
+ current = void 0;
734
+ return;
735
+ }
736
+ current = element;
737
+ const existing = boundElements.get(current);
738
+ if (existing) {
739
+ const same = sameSources(existing.bindings, bindings);
740
+ existing.bindings = bindings;
741
+ existing.parts = parts;
742
+ existing.className = className;
743
+ if (!same) existing.effect.run();
744
+ return;
745
+ }
746
+ const target = current;
747
+ const bound = {
748
+ bindings,
749
+ parts,
750
+ className,
751
+ stopped: false,
752
+ effect: new ReactiveEffect(() => apply(target, bound)),
753
+ instance: DEV ? instance : null
754
+ };
755
+ bound.effect.scheduler = () => schedule(bound);
756
+ boundElements.set(target, bound);
757
+ bound.effect.run();
758
+ };
759
+ }
760
+ /** 两次渲染的绑定读的是同一批来源:个数、求值入口都相同,而且读取函数都是稳定的 */
761
+ function sameSources(previous, next) {
762
+ return previous.length === next.length && next.every(({ binding }, index) => binding.stable && previous[index].binding.style === binding.style);
763
+ }
764
+ //#endregion
765
+ //#region src/styles/dynamic.ts
766
+ /**
767
+ * 动态样式(create 里写成函数的命名空间)与响应式绑定:css.bind 绑定动态样式的参数,css.select 绑定选哪段样式。
768
+ */
769
+ /**
770
+ * 动态样式的变量名:按属性与条件(同 StyleX 的 --x-属性)。不注册 @property:类里引用变量的元素,
771
+ * 自己的行内一定写着这个变量(同一次求值产出类与值),不会用到祖先继承下来的值;注册反而让样式重算慢 15–20%
772
+ */
773
+ const dynamicVar = (property, chain) => chain.length ? `--x-${property}-${hash(chain.join(""))}` : `--x-${property}`;
774
+ /**
775
+ * 动态样式:一直没变过的属性直接写进类里(多半是常量,同手写,元素上不挂变量);
776
+ * 值一旦和第一次不同,这个属性就永久转成变量(类里写 var(--x-属性)、值进行内样式)。
777
+ * 一个动态命名空间最多产生“属性数 + 1”种类,不随取值增长;转成变量之后值怎么变类都不变。
778
+ * StyleX 把动态命名空间的属性一律变成行内变量,每个元素多挂几个,样式重算更慢。
779
+ * 响应式绑定遇到“这个属性刚开始变”(类还是写死的那种)时就地换成变量版的类(bindings.ts),之后只改变量
780
+ */
781
+ function dynamic(factory) {
782
+ const first = /* @__PURE__ */ new Map();
783
+ const varying = /* @__PURE__ */ new Set();
784
+ const shapes = /* @__PURE__ */ new Map();
785
+ const memo = /* @__PURE__ */ new Map();
786
+ const evaluate = (args) => {
787
+ const key = memoKey(args);
788
+ const hit = key === void 0 ? void 0 : memo.get(key);
789
+ if (hit) return hit;
790
+ const before = varying.size;
791
+ const result = compute(args);
792
+ if (varying.size !== before || memo.size >= MEMO_LIMIT) memo.clear();
793
+ if (key !== void 0) memo.set(key, result);
794
+ return result;
795
+ };
796
+ const compute = (args) => {
797
+ const inline = {};
798
+ const leaves = [];
799
+ let shape = "";
800
+ eachLeaf(factory(...args), (leaf) => {
801
+ const key = leaf.chain.length ? `${leaf.property}\0${leaf.chain.join("\0")}` : leaf.property;
802
+ const text = leaf.texts?.join(", ") ?? "";
803
+ const seen = first.get(key);
804
+ if (seen === void 0) first.set(key, text);
805
+ else if (seen !== text) varying.add(key);
806
+ if (!leaf.texts || !varying.has(key)) {
807
+ shape += `${key}=${text};`;
808
+ leaves.push(leaf);
809
+ return;
810
+ }
811
+ const variable = dynamicVar(leaf.property, leaf.chain);
812
+ inline[variable] = text;
813
+ shape += `${key};`;
814
+ leaves.push({
815
+ ...leaf,
816
+ texts: [`var(${variable})`]
817
+ });
818
+ });
819
+ let shared = shapes.get(shape);
820
+ if (!shared) shapes.set(shape, shared = fragment(tableOf(leaves)));
821
+ return {
822
+ part: shared,
823
+ inline
824
+ };
825
+ };
826
+ const style = (...args) => {
827
+ const { part, inline } = evaluate(args);
828
+ const result = [part, inline];
829
+ Object.defineProperty(result, DYNAMIC, { value: true });
830
+ return result;
831
+ };
832
+ return Object.assign(style, { [EVALUATE]: evaluate });
833
+ }
834
+ /** 每个动态样式最多记住多少组参数的结果 */
835
+ const MEMO_LIMIT = 512;
836
+ /** 参数都是原始值时的记忆键(带上类型,1 与 '1' 不同);有对象参数时不记忆 */
837
+ function memoKey(args) {
838
+ let key = "";
839
+ for (const arg of args) {
840
+ const type = typeof arg;
841
+ if (arg !== null && type !== "string" && type !== "number" && type !== "boolean" && type !== "undefined") return void 0;
842
+ key += `${type[0]}${String(arg)}\u0001`;
843
+ }
844
+ return key;
845
+ }
846
+ /**
847
+ * 动态样式的响应式绑定:`css.bind(styles.bar, () => [item.width])`。交给 attrs / sx 后,
848
+ * 参数变化时只改元素上的变量,不重新渲染组件。JSX 里写 `styles.bar(item.width)` 时编译器自动改写成它
849
+ */
850
+ function bind$1(style, args, stable = false) {
851
+ return {
852
+ [BINDING]: true,
853
+ style,
854
+ args,
855
+ stable
856
+ };
857
+ }
858
+ const EMPTY = Object.freeze({});
859
+ /** 没选中任何样式:空片段,参与组合但不产生声明 */
860
+ const NONE = {
861
+ part: fragment(/* @__PURE__ */ new Map()),
862
+ inline: EMPTY
863
+ };
864
+ const selected = /* @__PURE__ */ new WeakMap();
865
+ /** css.select 的求值:读到的样式原样参与组合,假值不选 */
866
+ const SELECT = { [EVALUATE]: ([style]) => {
867
+ if (!style) return NONE;
868
+ if (typeof style !== "object" || !(FRAGMENT in style || CLASS in style)) throw new Error("css.select:读取函数要返回 create、createTheme、defineMarker 的结果,或假值");
869
+ let result = selected.get(style);
870
+ if (!result) selected.set(style, result = {
871
+ part: style,
872
+ inline: EMPTY
873
+ });
874
+ return result;
875
+ } };
876
+ /**
877
+ * 按状态选样式的响应式绑定:`css.select(() => item.hot && styles.hot)`。交给 attrs / sx 后,渲染时不追踪读到的状态,
878
+ * 状态变化时由元素上的 effect 就地换组合类,组件不重新渲染。JSX 里写 `item.hot && styles.hot`、
879
+ * `active ? styles.on : styles.off`、`sizeStyles[size]` 时编译器自动改写成它(只在状态没有别处读、换类能省下渲染时)
880
+ */
881
+ function select$1(read, stable = false) {
882
+ return {
883
+ [BINDING]: true,
884
+ style: SELECT,
885
+ args: () => [read()],
886
+ stable
887
+ };
888
+ }
889
+ //#endregion
890
+ //#region src/styles/hot.ts
891
+ /**
892
+ * 样式的热更新原地生效:编译器把顶层的 create / recipe 包上 hotStyles(compiler/style-hot.ts),
893
+ * 模块重新执行时新内容写进旧对象,用到的组合类按原类名改写规则,组件不重新渲染。
894
+ */
895
+ /** 配方等自己管理状态的样式对象实现它:拿到同一 id 的新对象时把自己原地换成新的 */
896
+ const HOT = Symbol("style hot");
897
+ const hotObjects = /* @__PURE__ */ new Map();
898
+ /**
899
+ * 编译器包在顶层 `const 名 = css.create(…)` / `recipe(…)` 外面(开发服务器,compiler/style-hot.ts):
900
+ * 第一次登记;模块热更新重新执行时把新内容写进旧对象并返回旧对象——导入方手里拿的还是它,
901
+ * 已插入的规则按原类名改写,组件不用重新渲染
902
+ */
903
+ function hotStyles(id, value) {
904
+ const previous = hotObjects.get(id);
905
+ if (!previous) {
906
+ hotObjects.set(id, value);
907
+ return value;
908
+ }
909
+ if (HOT in previous) previous[HOT](value);
910
+ else hotUpdate(previous, value);
911
+ return previous;
912
+ }
913
+ /** create 的结果原地换成新的:已展开的片段换声明表并改写用到它的规则;动态样式、新加的命名空间直接换上 */
914
+ function hotUpdate(previous, next) {
915
+ for (const name of Object.keys(next)) {
916
+ const value = next[name];
917
+ const current = Object.getOwnPropertyDescriptor(previous, name);
918
+ if (current && !current.get && isFragment(current.value) && isFragment(value)) replaceFragment(current.value, value[FRAGMENT]);
919
+ else Object.defineProperty(previous, name, {
920
+ value,
921
+ enumerable: true,
922
+ configurable: true
923
+ });
924
+ }
925
+ }
926
+ //#endregion
927
+ //#region src/styles/vars.ts
928
+ /**
929
+ * CSS 变量组与主题、标记:defineVars 在 :root 上声明一组变量(主题 tokens、组件之间约定的变量),
930
+ * createTheme 生成只改这组变量的类,defineMarker 给 when.* 标出被观察的祖先、兄弟。
931
+ * 变量名、标记类名都由调用方给的名字决定(`--vtp-colors-accent`),源码、产物、SSR 一致,调试时也认得出来。
932
+ */
933
+ /** 变量组上的组名(不可枚举:遍历 token 时不出现) */
934
+ const VAR_GROUP = Symbol("style var group");
935
+ /** 条件值按 @ 规则分开的声明:@ 规则链(无条件时为空串)→ 声明 */
936
+ function byAtRule(entries) {
937
+ const blocks = /* @__PURE__ */ new Map();
938
+ for (const [variable, value] of entries) walk(variable, value, [], (chain, leaf) => {
939
+ const atRule = chain.join("");
940
+ let list = blocks.get(atRule);
941
+ if (!list) blocks.set(atRule, list = []);
942
+ list.push(`${variable}:${cssValue(variable, leaf)}`);
943
+ });
944
+ return blocks;
945
+ }
946
+ /**
947
+ * 声明一组 CSS 变量:`defineVars('vtp-colors', { accent: '#3e63dd' })` → `:root{--vtp-colors-accent:#3e63dd}`,
948
+ * 返回 `{ accent: 'var(--vtp-colors-accent)' }`。值可以按条件取(`{ default: '200ms', '@media (…)': '0ms' }`)。
949
+ * 规则在调用时插入;同名再调用(热更新重新执行模块)时换成新的取值
950
+ */
951
+ function defineVars$1(name, tokens) {
952
+ const group = {};
953
+ const values = [];
954
+ for (const [key, value] of Object.entries(tokens)) {
955
+ const variable = `--${name}-${dashify(key).replace(/^-/, "")}`;
956
+ group[key] = `var(${variable})`;
957
+ values.push([variable, value]);
958
+ }
959
+ const rules = [...byAtRule(values)].map(([atRule, list]) => {
960
+ const rule = `:root{${list.join(";")}}`;
961
+ return atRule ? `${atRule}{${rule}}` : rule;
962
+ });
963
+ insert(`--${name}`, rules);
964
+ Object.defineProperty(group, VAR_GROUP, { value: name });
965
+ return Object.freeze(group);
966
+ }
967
+ /**
968
+ * 一套主题:只改一组变量的类(`.t.t, .t.t:root { --变量: 值 }`,压过 :root 上的默认值),值可以按条件取。
969
+ * 同一组的主题在一次 attrs 里后者覆盖前者
970
+ */
971
+ function createTheme$1(group, overrides) {
972
+ const values = [];
973
+ for (const [key, value] of Object.entries(overrides)) {
974
+ const variable = /^var\((--[^)]+)\)$/.exec(group[key] ?? "")?.[1];
975
+ if (!variable) throw new Error(`createTheme:${group[VAR_GROUP]} 组里没有 token '${key}'`);
976
+ values.push([variable, value]);
977
+ }
978
+ const rules = [...byAtRule(values)].map(([atRule, list]) => {
979
+ const rule = `..,..:root{${list.join(";")}}`;
980
+ return atRule ? `${atRule}{${rule}}` : rule;
981
+ });
982
+ const name = `x${hash(group[VAR_GROUP] + rules.join(""))}`;
983
+ if (!isInserted(name)) insert(name, rules.map((rule) => rule.replaceAll("", name)));
984
+ return Object.freeze({ [CLASS]: [group[VAR_GROUP], name] });
985
+ }
986
+ /** 定义标记:`defineMarker('vtp-heading')`,类名就是这个名字 */
987
+ const defineMarker$1 = (name) => Object.freeze({ [CLASS]: [name, name] });
988
+ const markerClass = (marker) => marker[CLASS][1];
989
+ /** when.*:按祖先、后代、兄弟的状态取样式的条件键(:where 不加特异性,同 StyleX) */
990
+ const when$1 = {
991
+ /** 祖先处于某状态时:`[when.ancestor(':hover', marker)]: 1` */
992
+ ancestor: (pseudo, marker) => `:where(.${markerClass(marker)}${pseudo} *)`,
993
+ /** 有后代处于某状态时 */
994
+ descendant: (pseudo, marker) => `:where(:has(.${markerClass(marker)}${pseudo}))`,
995
+ /** 前面的兄弟处于某状态时 */
996
+ siblingBefore: (pseudo, marker) => `:where(.${markerClass(marker)}${pseudo} ~ *)`,
997
+ /** 后面的兄弟处于某状态时 */
998
+ siblingAfter: (pseudo, marker) => `:where(:has(~ .${markerClass(marker)}${pseudo}))`,
999
+ /** 任一兄弟处于某状态时 */
1000
+ anySibling: (pseudo, marker) => `:where(.${markerClass(marker)}${pseudo} ~ *, :has(~ .${markerClass(marker)}${pseudo}))`
1001
+ };
1002
+ //#endregion
1003
+ //#region src/styles/runtime.ts
1004
+ /**
1005
+ * 运行时样式引擎:写法同 StyleX(create、attrs、createTheme、keyframes、when),实现同 Emotion / zerodep-css:
1006
+ * 一个元素一次 attrs 合成一个类,规则在第一次用到时插入;状态变化只改该改的那一处。
1007
+ *
1008
+ * - create 只产出“片段”(声明表),不注入;命名空间第一次被读到时才展开。
1009
+ * - attrs(片段…) 按片段对象逐层查缓存(WeakMap 链),命中时不拼字符串、不算哈希,返回同一个结果对象,
1010
+ * Vue 比对 class 相同就不碰 DOM。未命中时合并成一个组合类:`.c{…} .c:hover{…} @media(…){.c.c{…}}`,
1011
+ * 类名是内容的哈希,同样的组合只插一次。一个元素只挂一个生成的类,没有原子类之间的优先级问题;
1012
+ * 块内按 StyleX 的属性优先级排序(简写在前、长属性在后),条件块按条件优先级排序(:hover < :active < @media)。
1013
+ * - 合并语义:后面的普通值整体替换该属性(同 StyleX,含它的条件值);条件对象按条件逐项合并,
1014
+ * 其中的 null 表示“这个条件不改”(不会像 StyleX 那样把前面的默认值清掉);顶层的 null 去掉该属性。
1015
+ * - 动态样式(create 里的函数):一直没变过的属性写进类里;值变过的属性永久转成变量(类里写 var(--x-属性)、
1016
+ * 值进元素的行内样式),之后值怎么变类名都不变、不插新规则。
1017
+ * - 响应式绑定(css.bind,编译器把 JSX 里的 `styles.bar(item.width)` 自动改写成它):渲染时只算类名、不追踪依赖,
1018
+ * 元素挂上后由一个响应式 effect 直接改元素上的变量。值变化不重新渲染组件、不做 vdom 比对,只改那一个变量。
1019
+ * - 按状态选样式(css.select,编译器把 `item.hot && styles.hot` 这类只在样式里读的状态改写成它):同样不追踪,
1020
+ * 状态变化时 effect 换掉那一段样式、重新组合,就地换元素上的组合类(bindings.ts)。
1021
+ * - 状态样式用条件别名(_selected、_disabled…)跟着 DOM 上的 aria / data 属性走:状态变化只改属性,类名不变。
1022
+ * - 主题(createTheme,vars.ts)是一个只改变量的类,换主题只换这个类,组件不用重新渲染。
1023
+ * - 热更新(hotStyles,编译器包在顶层 create / recipe 外面):模块重新执行时新声明写进旧片段,
1024
+ * 用到它的组合类按原类名改写规则,组件不重新渲染。
1025
+ *
1026
+ * 分成几个模块:declarations(样式对象 → 声明表 → 规则文本)、sheet(样式表)、fragment(片段与组合类)、
1027
+ * compose(一个元素的样式 → 类,带缓存)、dynamic(动态样式与 bind、select)、bindings(元素上的响应式绑定)、vars(变量组、主题、标记)、tokens(主题 tokens 的短写)、
1028
+ * hot(热更新);这里是对外的入口。
1029
+ */
1030
+ /** css.create:命名空间第一次被读时展开(惰性);函数是动态样式 */
1031
+ function create$1(namespaces) {
1032
+ const result = {};
1033
+ for (const [name, namespace] of Object.entries(namespaces)) {
1034
+ if (typeof namespace === "function") {
1035
+ result[name] = dynamic(namespace);
1036
+ continue;
1037
+ }
1038
+ Object.defineProperty(result, name, {
1039
+ enumerable: true,
1040
+ configurable: true,
1041
+ get() {
1042
+ const value = fragment(declarations(namespace));
1043
+ Object.defineProperty(result, name, {
1044
+ value,
1045
+ enumerable: true,
1046
+ configurable: true
1047
+ });
1048
+ return value;
1049
+ }
1050
+ });
1051
+ }
1052
+ return result;
1053
+ }
1054
+ /** 直接写的样式对象(`sx={{ marginTop: '_md' }}`)→ 片段:同一个对象按对象记住,每次渲染新建的按内容记住 */
1055
+ const literalByObject = /* @__PURE__ */ new WeakMap();
1056
+ const literalByContent = /* @__PURE__ */ new Map();
1057
+ /** 按内容最多记住多少个(值一直在变的写法会不断出新内容,不无限增长;记不住时重新展开,规则照旧只插一次) */
1058
+ const LITERAL_LIMIT = 512;
1059
+ function literal(value) {
1060
+ let result = literalByObject.get(value);
1061
+ if (result) return result;
1062
+ const key = JSON.stringify(value);
1063
+ result = literalByContent.get(key);
1064
+ if (!result) {
1065
+ if (literalByContent.size >= LITERAL_LIMIT) literalByContent.clear();
1066
+ literalByContent.set(key, result = fragment(declarations(value)));
1067
+ }
1068
+ literalByObject.set(value, result);
1069
+ return result;
1070
+ }
1071
+ /**
1072
+ * 合并样式成元素属性 `{ class, style }`,后面的覆盖前面的;假值忽略,可以嵌套成数组,也可以直接写样式对象。
1073
+ * 只有静态片段时返回缓存的同一个对象;有动态样式时 class 走缓存,style 是这次的变量值;
1074
+ * 有响应式绑定(bind、select)时再带一个 ref:元素挂上后由 effect 改变量、换类(见 watchBindings)
1075
+ */
1076
+ function attrs$1(...styles) {
1077
+ const parts = [];
1078
+ let inline;
1079
+ let bindings;
1080
+ const addInline = (values) => {
1081
+ for (const _ in values) {
1082
+ inline = Object.assign(inline ?? {}, values);
1083
+ return;
1084
+ }
1085
+ };
1086
+ const collect = (value) => {
1087
+ if (!value || typeof value !== "object") return;
1088
+ if (BINDING in value) {
1089
+ const binding = value;
1090
+ let args = [];
1091
+ const { part, inline: values } = untrack(() => binding.style[EVALUATE](args = binding.args()));
1092
+ (bindings ??= []).push({
1093
+ binding,
1094
+ index: parts.length,
1095
+ args,
1096
+ inline: values
1097
+ });
1098
+ parts.push(part);
1099
+ addInline(values);
1100
+ return;
1101
+ }
1102
+ if (Array.isArray(value)) {
1103
+ if (DYNAMIC in value) {
1104
+ parts.push(value[0]);
1105
+ addInline(value[1]);
1106
+ } else value.forEach(collect);
1107
+ return;
1108
+ }
1109
+ parts.push(FRAGMENT in value || CLASS in value ? value : literal(value));
1110
+ };
1111
+ styles.forEach(collect);
1112
+ const composed = compose(parts);
1113
+ if (!inline && !bindings) return composed;
1114
+ const result = inline ? {
1115
+ ...composed,
1116
+ style: inline
1117
+ } : { ...composed };
1118
+ if (bindings) result.ref = watchBindings(bindings, parts, composed.class ?? "");
1119
+ return result;
1120
+ }
1121
+ /** css.keyframes:返回动画名,规则第一次调用时插入 */
1122
+ function keyframes$1(frames) {
1123
+ const body = Object.entries(frames).map(([step, declarations]) => `${step}{${Object.entries(declarations).filter(([, value]) => value !== null && value !== void 0).map(([property, value]) => `${dashify(property)}:${cssValue(property, value)}`).join(";")}}`).join("");
1124
+ const name = `x${hash(body)}-B`;
1125
+ if (!isInserted(name)) insert(name, [`@keyframes ${name}{${body}}`]);
1126
+ return name;
1127
+ }
1128
+ //#endregion
1129
+ //#region src/styles/recipe.ts
1130
+ /** 定义配方(见文件开头) */
1131
+ function recipe(config) {
1132
+ let state = build(config);
1133
+ let cache = { results: /* @__PURE__ */ new Map() };
1134
+ const resolve = (selection = {}, ...extensions) => {
1135
+ let node = cache;
1136
+ const added = [];
1137
+ for (const extension of extensions) {
1138
+ if (!extension) continue;
1139
+ node.next ??= /* @__PURE__ */ new WeakMap();
1140
+ let child = node.next.get(extension);
1141
+ if (!child) node.next.set(extension, child = { results: /* @__PURE__ */ new Map() });
1142
+ node = child;
1143
+ added.push(extensionState(extension));
1144
+ }
1145
+ const names = added.length ? [.../* @__PURE__ */ new Set([...state.names, ...added.flatMap((extension) => extension.names)])] : state.names;
1146
+ const picked = names.map((name) => {
1147
+ const value = selection[name] ?? state.defaults[name];
1148
+ return value === void 0 || value === null ? void 0 : String(value);
1149
+ });
1150
+ const key = picked.join("\0");
1151
+ const hit = node.results.get(key);
1152
+ if (hit) return hit;
1153
+ const result = Object.fromEntries(state.parts.map((part) => [part, []]));
1154
+ const push = (styles) => {
1155
+ if (!styles) return;
1156
+ for (const part of state.parts) if (part in styles) result[part].push(styles[part]);
1157
+ };
1158
+ const pushVariants = (segments) => names.forEach((name, index) => {
1159
+ const value = picked[index];
1160
+ if (value !== void 0) push(segments.get(`variant\0${name}\0${value}`));
1161
+ });
1162
+ push(state.segments.get("base"));
1163
+ pushVariants(state.segments);
1164
+ state.compound.forEach(({ when }, index) => {
1165
+ if (Object.entries(when).every(([name, value]) => picked[names.indexOf(name)] === String(value))) push(state.segments.get(`compound\0${index}`));
1166
+ });
1167
+ for (const extension of added) {
1168
+ push(extension.segments.get("base"));
1169
+ pushVariants(extension.segments);
1170
+ }
1171
+ node.results.set(key, result);
1172
+ return result;
1173
+ };
1174
+ Object.defineProperty(resolve, "parts", { get: () => state.parts });
1175
+ Object.defineProperty(resolve, "name", {
1176
+ value: config.name,
1177
+ configurable: true
1178
+ });
1179
+ Object.defineProperty(resolve, HOT, { value: (next) => {
1180
+ const incoming = next[STATE];
1181
+ for (const [path, styles] of incoming.segments) {
1182
+ const previous = state.segments.get(path);
1183
+ if (!previous) continue;
1184
+ hotUpdate(previous, styles);
1185
+ incoming.segments.set(path, previous);
1186
+ }
1187
+ state = incoming;
1188
+ cache = { results: /* @__PURE__ */ new Map() };
1189
+ } });
1190
+ Object.defineProperty(resolve, STATE, { get: () => state });
1191
+ return resolve;
1192
+ }
1193
+ const STATE = Symbol("recipe state");
1194
+ const compile = (styles) => create$1(styles);
1195
+ /** 档位的每个取值各 create 一次(惰性:第一次用到时才展开) */
1196
+ function variantSegments(variants, segments) {
1197
+ for (const [name, values] of Object.entries(variants)) for (const [value, styles] of Object.entries(values)) segments.set(`variant\0${name}\0${value}`, compile(styles));
1198
+ }
1199
+ /** 每段样式各 create 一次,之后按档位组合查表 */
1200
+ function build(config) {
1201
+ const { parts, base = {}, variants = {}, compound = [], defaults = {} } = config;
1202
+ const segments = /* @__PURE__ */ new Map([["base", compile(base)]]);
1203
+ variantSegments(variants, segments);
1204
+ compound.forEach((entry, index) => {
1205
+ const { when, ...styles } = entry;
1206
+ segments.set(`compound\0${index}`, compile(styles));
1207
+ });
1208
+ return {
1209
+ parts,
1210
+ names: Object.keys(variants),
1211
+ defaults,
1212
+ segments,
1213
+ compound: compound.map(({ when }) => ({ when }))
1214
+ };
1215
+ }
1216
+ const extensions = /* @__PURE__ */ new WeakMap();
1217
+ /** 扩展按对象展开一次 */
1218
+ function extensionState(extension) {
1219
+ let result = extensions.get(extension);
1220
+ if (!result) {
1221
+ const segments = /* @__PURE__ */ new Map();
1222
+ if (extension.base) segments.set("base", compile(extension.base));
1223
+ variantSegments(extension.variants ?? {}, segments);
1224
+ result = {
1225
+ names: Object.keys(extension.variants ?? {}),
1226
+ segments
1227
+ };
1228
+ extensions.set(extension, result);
1229
+ }
1230
+ return result;
1231
+ }
1232
+ //#endregion
1233
+ //#region src/styles/index.ts
1234
+ /**
1235
+ * 样式入口:运行时样式引擎(runtime.ts)加上中文注释的类型。
1236
+ *
1237
+ * import * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';
1238
+ *
1239
+ * create 的参数只让样式组名参与推导,组内属性的上下文类型固定为带注释的 DocumentedStyles,
1240
+ * 悬停、补全、跳转都能看到说明(整个参数做泛型推导时,TypeScript 悬停只显示推导出的字面量)。
1241
+ */
1242
+ /** 定义样式(见 {@link Create}):产出片段,attrs 合成一个类时才插入规则(runtime.ts) */
1243
+ const create = create$1;
1244
+ /**
1245
+ * 把多个样式合并成元素属性 `{ class, style }`,后面的覆盖前面的;
1246
+ * 假值(false、null、undefined)会被忽略,便于按条件组合。Vue 的 JSX 用它:
1247
+ * `<div {...css.attrs(styles.base, active && styles.active, sx)} />`
1248
+ */
1249
+ const attrs = attrs$1;
1250
+ /**
1251
+ * 声明一组 CSS 变量(主题 tokens、组件之间约定的变量):`defineVars('vtp-colors', { accent: '#3e63dd' })`
1252
+ * 在 :root 上声明 `--vtp-colors-accent`,返回 `{ accent: 'var(--vtp-colors-accent)' }`。
1253
+ * 名字决定变量名,带上自己的前缀免得和宿主页面的变量重名;值可以按条件取
1254
+ */
1255
+ const defineVars = defineVars$1;
1256
+ /**
1257
+ * 登记主题 tokens,样式里才能写短写 `'_md'` 与 `'_space.md'`:组名 → defineVars 的结果,返回原对象。
1258
+ * 组名按 token-map.ts 的约定(space、colors、fontSizes…);主题库调用一次,使用方一般不用
1259
+ */
1260
+ const defineTokens = defineTokens$1;
1261
+ /** 基于 defineVars 定义的变量创建一套主题,作为样式应用在容器上即可覆盖内部的变量值。 */
1262
+ const createTheme = createTheme$1;
1263
+ /** 定义标记:`defineMarker('vtp-heading')`,挂到元素上(放进 attrs),子元素用 when.* 按它的状态取样式 */
1264
+ const defineMarker = defineMarker$1;
1265
+ /** 定义关键帧动画,返回动画名,作为 animationName 的值使用。 */
1266
+ const keyframes = keyframes$1;
1267
+ /** 按祖先、后代、兄弟(用 defineMarker 标记)的状态取样式:`[css.when.ancestor(':hover', marker)]: …` */
1268
+ const when = when$1;
1269
+ /**
1270
+ * 动态样式的响应式绑定:参数变化时只改元素上的变量,不重新渲染组件。
1271
+ * JSX 里写 `styles.bar(item.width)` 时编译器自动改写成 `css.bind(styles.bar, () => [item.width])`,
1272
+ * 一般不用手写;类型与直接调用动态样式的结果相同,能放进 attrs 与 sx
1273
+ */
1274
+ const bind = bind$1;
1275
+ /**
1276
+ * 按状态选样式的响应式绑定:状态变化时由元素上的 effect 就地换类,不重新渲染组件。
1277
+ * JSX 里写 `item.hot && styles.hot`、`active ? styles.on : styles.off`、`sizeStyles[size]`(本文件顶层 create 的样式)
1278
+ * 且读到的状态在渲染里没有别处用到时,编译器自动改写成 `css.select(() => item.hot && styles.hot)`,一般不用手写
1279
+ */
1280
+ const select = select$1;
1281
+ //#endregion
1282
+ export { attrs, bind, CONDITIONS as conditions, create, createTheme, defineMarker, defineTokens, defineVars, hotStyles, keyframes, recipe, select, styleText, when };