@tenphi/tasty 0.0.0-snapshot.e8f14d4 → 0.0.0-snapshot.e958ba8
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/README.md +1 -1
- package/dist/compute-styles.d.ts +8 -7
- package/dist/compute-styles.js +155 -14
- package/dist/compute-styles.js.map +1 -1
- package/dist/config.d.ts +20 -19
- package/dist/config.js +25 -26
- package/dist/config.js.map +1 -1
- package/dist/core/index.d.ts +3 -3
- package/dist/core/index.js +2 -2
- package/dist/hooks/useCounterStyle.js +3 -4
- package/dist/hooks/useCounterStyle.js.map +1 -1
- package/dist/hooks/useFontFace.js +3 -4
- package/dist/hooks/useFontFace.js.map +1 -1
- package/dist/hooks/useGlobalStyles.js +4 -5
- package/dist/hooks/useGlobalStyles.js.map +1 -1
- package/dist/hooks/useKeyframes.js +3 -4
- package/dist/hooks/useKeyframes.js.map +1 -1
- package/dist/hooks/useProperty.js +3 -4
- package/dist/hooks/useProperty.js.map +1 -1
- package/dist/hooks/useRawCSS.js +3 -4
- package/dist/hooks/useRawCSS.js.map +1 -1
- package/dist/hooks/useStyles.d.ts +4 -4
- package/dist/hooks/useStyles.js +5 -7
- package/dist/hooks/useStyles.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/injector/index.d.ts +18 -2
- package/dist/injector/index.js +20 -1
- package/dist/injector/index.js.map +1 -1
- package/dist/injector/injector.d.ts +30 -3
- package/dist/injector/injector.js +107 -7
- package/dist/injector/injector.js.map +1 -1
- package/dist/injector/sheet-manager.d.ts +9 -13
- package/dist/injector/sheet-manager.js +31 -66
- package/dist/injector/sheet-manager.js.map +1 -1
- package/dist/injector/types.d.ts +43 -19
- package/dist/ssr/collector.js +3 -10
- package/dist/ssr/collector.js.map +1 -1
- package/dist/ssr/index.d.ts +1 -2
- package/dist/ssr/index.js +1 -2
- package/dist/ssr/index.js.map +1 -1
- package/dist/ssr/next.d.ts +1 -3
- package/dist/ssr/next.js +1 -2
- package/dist/ssr/next.js.map +1 -1
- package/dist/tasty.d.ts +1 -1
- package/dist/tasty.js +17 -8
- package/dist/tasty.js.map +1 -1
- package/dist/utils/filter-base-props.d.ts +1 -1
- package/dist/utils/filter-base-props.js.map +1 -1
- package/docs/dsl.md +20 -5
- package/docs/injector.md +32 -18
- package/docs/ssr.md +12 -42
- package/package.json +4 -4
- package/dist/hooks/resolve-ssr-collector.js +0 -14
- package/dist/hooks/resolve-ssr-collector.js.map +0 -1
- package/dist/ssr/context.d.ts +0 -8
- package/dist/ssr/context.js +0 -13
- package/dist/ssr/context.js.map +0 -1
package/README.md
CHANGED
|
@@ -347,7 +347,7 @@ Every style property accepts a state mapping object. Keys can be combined with b
|
|
|
347
347
|
| Feature query | `@supports(display: grid)` | `@supports (display: grid)` |
|
|
348
348
|
| Entry animation | `@starting` | `@starting-style` |
|
|
349
349
|
|
|
350
|
-
Combine with `&` (AND), `|` (OR), `!` (NOT):
|
|
350
|
+
Combine with `&` (AND), `|` (OR), `!` (NOT), `^` (XOR):
|
|
351
351
|
|
|
352
352
|
```tsx
|
|
353
353
|
fill: {
|
package/dist/compute-styles.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { ServerStyleCollector } from "./ssr/collector.js";
|
|
|
4
4
|
//#region src/compute-styles.d.ts
|
|
5
5
|
interface ComputeStylesResult {
|
|
6
6
|
className: string;
|
|
7
|
+
/** CSS text to emit as an inline <style> tag (RSC mode only). */
|
|
8
|
+
css?: string;
|
|
7
9
|
}
|
|
8
10
|
interface ComputeStylesOptions {
|
|
9
11
|
ssrCollector?: ServerStyleCollector | null;
|
|
@@ -12,14 +14,13 @@ interface ComputeStylesOptions {
|
|
|
12
14
|
* Synchronous, hook-free style computation.
|
|
13
15
|
*
|
|
14
16
|
* Resolves recipes, categorizes style keys into chunks, renders CSS rules,
|
|
15
|
-
* allocates class names, and injects
|
|
17
|
+
* allocates class names, and injects / collects / returns the CSS.
|
|
16
18
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* (or passed explicitly via options) and CSS is collected as strings.
|
|
19
|
+
* Three code paths:
|
|
20
|
+
* 1. SSR collector — discovered via ALS or passed explicitly; CSS collected
|
|
21
|
+
* 2. RSC inline — no collector and no `document`; CSS returned as `result.css`
|
|
22
|
+
* for the caller to emit as an inline `<style>` tag
|
|
23
|
+
* 3. Client inject — CSS injected synchronously into the DOM (idempotent)
|
|
23
24
|
*
|
|
24
25
|
* @param styles - Tasty styles object (or undefined for no styles)
|
|
25
26
|
* @param options - Optional SSR collector override
|
package/dist/compute-styles.js
CHANGED
|
@@ -1,31 +1,171 @@
|
|
|
1
1
|
import { extractLocalProperties, hasLocalProperties } from "./properties/index.js";
|
|
2
2
|
import { extractLocalFontFace, fontFaceContentHash, formatFontFaceRule, hasLocalFontFace } from "./font-face/index.js";
|
|
3
3
|
import { extractLocalCounterStyle, formatCounterStyleRule, hasLocalCounterStyle } from "./counter-style/index.js";
|
|
4
|
-
import {
|
|
4
|
+
import { renderStyles } from "./pipeline/index.js";
|
|
5
|
+
import { getConfig, getEffectiveProperties, getGlobalConfigTokens, getGlobalCounterStyle, getGlobalFontFace, getGlobalKeyframes, hasGlobalKeyframes } from "./config.js";
|
|
5
6
|
import { categorizeStyleKeys } from "./chunks/definitions.js";
|
|
6
7
|
import { generateChunkCacheKey } from "./chunks/cacheKey.js";
|
|
7
8
|
import { renderStylesForChunk } from "./chunks/renderChunk.js";
|
|
8
|
-
import { counterStyle, fontFace, inject, keyframes, property } from "./injector/index.js";
|
|
9
|
+
import { counterStyle, fontFace, inject, keyframes, property, touch } from "./injector/index.js";
|
|
9
10
|
import { extractAnimationNamesFromStyles, extractLocalKeyframes, filterUsedKeyframes, hasLocalKeyframes, mergeKeyframes, replaceAnimationNames } from "./keyframes/index.js";
|
|
10
11
|
import { formatPropertyCSS } from "./ssr/format-property.js";
|
|
11
12
|
import { collectAutoInferredProperties } from "./ssr/collect-auto-properties.js";
|
|
12
|
-
import {
|
|
13
|
+
import { formatGlobalRules } from "./ssr/format-global-rules.js";
|
|
13
14
|
import { formatKeyframesCSS } from "./ssr/format-keyframes.js";
|
|
15
|
+
import { formatRules } from "./ssr/format-rules.js";
|
|
16
|
+
import { getRegisteredSSRCollector } from "./ssr/ssr-collector-ref.js";
|
|
14
17
|
import { hasKeys } from "./utils/has-keys.js";
|
|
15
18
|
import { resolveRecipes } from "./utils/resolve-recipes.js";
|
|
19
|
+
import { cache } from "react";
|
|
16
20
|
//#region src/compute-styles.ts
|
|
17
21
|
/**
|
|
18
22
|
* Hook-free, synchronous style computation.
|
|
19
23
|
*
|
|
20
24
|
* Extracts the core logic from useStyles() into a plain function that can
|
|
21
|
-
* be called during React render without any hooks.
|
|
22
|
-
*
|
|
23
|
-
*
|
|
25
|
+
* be called during React render without any hooks. Three code paths:
|
|
26
|
+
*
|
|
27
|
+
* 1. SSR collector — styles collected via ServerStyleCollector
|
|
28
|
+
* 2. Client inject — styles injected synchronously into the DOM
|
|
29
|
+
* 3. RSC inline — styles returned as CSS strings for inline <style> emission
|
|
24
30
|
*
|
|
25
31
|
* This enables tasty() components to work as React Server Components.
|
|
26
32
|
*/
|
|
27
33
|
const EMPTY_RESULT = { className: "" };
|
|
28
34
|
/**
|
|
35
|
+
* Per-request RSC style cache using React.cache.
|
|
36
|
+
* React.cache provides per-request memoization in Server Components,
|
|
37
|
+
* so each request gets its own isolated cache.
|
|
38
|
+
*/
|
|
39
|
+
const getRSCCache = cache(() => ({
|
|
40
|
+
cacheKeyToClassName: /* @__PURE__ */ new Map(),
|
|
41
|
+
classCounter: 0,
|
|
42
|
+
emittedKeys: /* @__PURE__ */ new Set(),
|
|
43
|
+
internalsEmitted: false
|
|
44
|
+
}));
|
|
45
|
+
function rscAllocateClassName(rscCache, cacheKey) {
|
|
46
|
+
const existing = rscCache.cacheKeyToClassName.get(cacheKey);
|
|
47
|
+
if (existing) return {
|
|
48
|
+
className: existing,
|
|
49
|
+
isNew: false
|
|
50
|
+
};
|
|
51
|
+
const className = `r${rscCache.classCounter++}`;
|
|
52
|
+
rscCache.cacheKeyToClassName.set(cacheKey, className);
|
|
53
|
+
return {
|
|
54
|
+
className,
|
|
55
|
+
isNew: true
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Collect internals CSS for RSC — mirrors ServerStyleCollector.collectInternals().
|
|
60
|
+
* Emitted once per request (tracked via rscCache.internalsEmitted).
|
|
61
|
+
*/
|
|
62
|
+
function collectInternalsRSC(rscCache) {
|
|
63
|
+
if (rscCache.internalsEmitted) return "";
|
|
64
|
+
rscCache.internalsEmitted = true;
|
|
65
|
+
const parts = [];
|
|
66
|
+
for (const [token, definition] of Object.entries(getEffectiveProperties())) {
|
|
67
|
+
const css = formatPropertyCSS(token, definition);
|
|
68
|
+
if (css) parts.push(css);
|
|
69
|
+
}
|
|
70
|
+
const tokenStyles = getGlobalConfigTokens();
|
|
71
|
+
if (tokenStyles && Object.keys(tokenStyles).length > 0) {
|
|
72
|
+
const tokenRules = renderStyles(tokenStyles, ":root");
|
|
73
|
+
if (tokenRules.length > 0) {
|
|
74
|
+
const css = formatGlobalRules(tokenRules);
|
|
75
|
+
if (css) parts.push(css);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const globalFF = getGlobalFontFace();
|
|
79
|
+
if (globalFF) for (const [family, input] of Object.entries(globalFF)) {
|
|
80
|
+
const descriptors = Array.isArray(input) ? input : [input];
|
|
81
|
+
for (const desc of descriptors) parts.push(formatFontFaceRule(family, desc));
|
|
82
|
+
}
|
|
83
|
+
const globalCS = getGlobalCounterStyle();
|
|
84
|
+
if (globalCS) for (const [name, descriptors] of Object.entries(globalCS)) parts.push(formatCounterStyleRule(name, descriptors));
|
|
85
|
+
return parts.join("\n");
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Collect per-component ancillary CSS (keyframes, @property, font-face,
|
|
89
|
+
* counter-style) for RSC mode.
|
|
90
|
+
*/
|
|
91
|
+
function collectAncillaryRSC(rscCache, styles) {
|
|
92
|
+
const parts = [];
|
|
93
|
+
const usedKf = getUsedKeyframes(styles);
|
|
94
|
+
if (usedKf) for (const [name, steps] of Object.entries(usedKf)) {
|
|
95
|
+
const key = `__kf:${name}`;
|
|
96
|
+
if (!rscCache.emittedKeys.has(key)) {
|
|
97
|
+
rscCache.emittedKeys.add(key);
|
|
98
|
+
parts.push(formatKeyframesCSS(name, steps));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (hasLocalProperties(styles)) {
|
|
102
|
+
const localProperties = extractLocalProperties(styles);
|
|
103
|
+
if (localProperties) for (const [token, definition] of Object.entries(localProperties)) {
|
|
104
|
+
const key = `__prop:${token}`;
|
|
105
|
+
if (!rscCache.emittedKeys.has(key)) {
|
|
106
|
+
rscCache.emittedKeys.add(key);
|
|
107
|
+
const css = formatPropertyCSS(token, definition);
|
|
108
|
+
if (css) parts.push(css);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (hasLocalFontFace(styles)) {
|
|
113
|
+
const localFontFace = extractLocalFontFace(styles);
|
|
114
|
+
if (localFontFace) for (const [family, input] of Object.entries(localFontFace)) {
|
|
115
|
+
const descriptors = Array.isArray(input) ? input : [input];
|
|
116
|
+
for (const desc of descriptors) {
|
|
117
|
+
const key = `__ff:${fontFaceContentHash(family, desc)}`;
|
|
118
|
+
if (!rscCache.emittedKeys.has(key)) {
|
|
119
|
+
rscCache.emittedKeys.add(key);
|
|
120
|
+
parts.push(formatFontFaceRule(family, desc));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (hasLocalCounterStyle(styles)) {
|
|
126
|
+
const localCounterStyle = extractLocalCounterStyle(styles);
|
|
127
|
+
if (localCounterStyle) for (const [name, descriptors] of Object.entries(localCounterStyle)) {
|
|
128
|
+
const key = `__cs:${name}`;
|
|
129
|
+
if (!rscCache.emittedKeys.has(key)) {
|
|
130
|
+
rscCache.emittedKeys.add(key);
|
|
131
|
+
parts.push(formatCounterStyleRule(name, descriptors));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return parts.join("\n");
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Process all chunks in RSC mode: render CSS to strings, allocate classNames,
|
|
139
|
+
* and return combined { className, css }.
|
|
140
|
+
*/
|
|
141
|
+
function computeStylesRSC(styles, chunkMap) {
|
|
142
|
+
const rscCache = getRSCCache();
|
|
143
|
+
const cssParts = [];
|
|
144
|
+
const classNames = [];
|
|
145
|
+
const internalsCSS = collectInternalsRSC(rscCache);
|
|
146
|
+
if (internalsCSS) cssParts.push(internalsCSS);
|
|
147
|
+
for (const [chunkName, chunkStyleKeys] of chunkMap) {
|
|
148
|
+
if (chunkStyleKeys.length === 0) continue;
|
|
149
|
+
const { className, isNew } = rscAllocateClassName(rscCache, generateChunkCacheKey(styles, chunkName, chunkStyleKeys));
|
|
150
|
+
classNames.push(className);
|
|
151
|
+
if (isNew) {
|
|
152
|
+
const renderResult = renderStylesForChunk(styles, chunkName, chunkStyleKeys);
|
|
153
|
+
if (renderResult.rules.length > 0) {
|
|
154
|
+
const css = formatRules(renderResult.rules, className);
|
|
155
|
+
if (css) cssParts.push(css);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const ancillaryCSS = collectAncillaryRSC(rscCache, styles);
|
|
160
|
+
if (ancillaryCSS) cssParts.push(ancillaryCSS);
|
|
161
|
+
if (classNames.length === 0) return EMPTY_RESULT;
|
|
162
|
+
const css = cssParts.join("\n");
|
|
163
|
+
return {
|
|
164
|
+
className: classNames.join(" "),
|
|
165
|
+
css: css || void 0
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
29
169
|
* Get keyframes that are actually used in styles.
|
|
30
170
|
* Returns null if no keyframes are used (fast path for zero overhead).
|
|
31
171
|
*/
|
|
@@ -171,14 +311,13 @@ function collectAncillarySSR(collector, styles, chunks) {
|
|
|
171
311
|
* Synchronous, hook-free style computation.
|
|
172
312
|
*
|
|
173
313
|
* Resolves recipes, categorizes style keys into chunks, renders CSS rules,
|
|
174
|
-
* allocates class names, and injects
|
|
175
|
-
*
|
|
176
|
-
* On the client, CSS is injected synchronously into the DOM. The injector's
|
|
177
|
-
* content-based cache makes this idempotent — repeated calls with the same
|
|
178
|
-
* styles are essentially free (Map lookup + refCount bump).
|
|
314
|
+
* allocates class names, and injects / collects / returns the CSS.
|
|
179
315
|
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
316
|
+
* Three code paths:
|
|
317
|
+
* 1. SSR collector — discovered via ALS or passed explicitly; CSS collected
|
|
318
|
+
* 2. RSC inline — no collector and no `document`; CSS returned as `result.css`
|
|
319
|
+
* for the caller to emit as an inline `<style>` tag
|
|
320
|
+
* 3. Client inject — CSS injected synchronously into the DOM (idempotent)
|
|
182
321
|
*
|
|
183
322
|
* @param styles - Tasty styles object (or undefined for no styles)
|
|
184
323
|
* @param options - Optional SSR collector override
|
|
@@ -196,7 +335,8 @@ function computeStyles(styles, options) {
|
|
|
196
335
|
if (chunk) chunks.push(chunk);
|
|
197
336
|
}
|
|
198
337
|
collectAncillarySSR(collector, resolved, chunks);
|
|
199
|
-
} else
|
|
338
|
+
} else if (typeof document === "undefined") return computeStylesRSC(resolved, chunkMap);
|
|
339
|
+
else {
|
|
200
340
|
injectAncillarySync(resolved);
|
|
201
341
|
const usedKf = getUsedKeyframes(resolved);
|
|
202
342
|
const nameMap = usedKf ? injectKeyframesSync(usedKf) : null;
|
|
@@ -205,6 +345,7 @@ function computeStyles(styles, options) {
|
|
|
205
345
|
if (chunk) chunks.push(chunk);
|
|
206
346
|
}
|
|
207
347
|
if (nameMap) injectChunkRulesSync(chunks, nameMap);
|
|
348
|
+
for (const chunk of chunks) touch(chunk.className);
|
|
208
349
|
}
|
|
209
350
|
if (chunks.length === 0) return EMPTY_RESULT;
|
|
210
351
|
if (chunks.length === 1) return { className: chunks[0].className };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compute-styles.js","names":[],"sources":["../src/compute-styles.ts"],"sourcesContent":["/**\n * Hook-free, synchronous style computation.\n *\n * Extracts the core logic from useStyles() into a plain function that can\n * be called during React render without any hooks. On the client, styles\n * are injected synchronously (idempotent via the injector cache). On the\n * server, styles are collected via the SSR collector.\n *\n * This enables tasty() components to work as React Server Components.\n */\n\nimport {\n categorizeStyleKeys,\n generateChunkCacheKey,\n renderStylesForChunk,\n} from './chunks';\nimport { getConfig, getGlobalKeyframes, hasGlobalKeyframes } from './config';\nimport {\n counterStyle,\n fontFace,\n inject,\n keyframes,\n property,\n} from './injector';\nimport type { FontFaceDescriptors, KeyframesSteps } from './injector/types';\nimport {\n extractLocalCounterStyle,\n formatCounterStyleRule,\n hasLocalCounterStyle,\n} from './counter-style';\nimport {\n extractLocalFontFace,\n fontFaceContentHash,\n formatFontFaceRule,\n hasLocalFontFace,\n} from './font-face';\nimport {\n extractAnimationNamesFromStyles,\n extractLocalKeyframes,\n filterUsedKeyframes,\n hasLocalKeyframes,\n mergeKeyframes,\n replaceAnimationNames,\n} from './keyframes';\nimport type { RenderResult, StyleResult } from './pipeline';\nimport { extractLocalProperties, hasLocalProperties } from './properties';\nimport { collectAutoInferredProperties } from './ssr/collect-auto-properties';\nimport type { ServerStyleCollector } from './ssr/collector';\nimport { getRegisteredSSRCollector } from './ssr/ssr-collector-ref';\nimport { formatKeyframesCSS } from './ssr/format-keyframes';\nimport { formatPropertyCSS } from './ssr/format-property';\nimport type { Styles } from './styles/types';\nimport { hasKeys } from './utils/has-keys';\nimport { resolveRecipes } from './utils/resolve-recipes';\n\nexport interface ComputeStylesResult {\n className: string;\n}\n\nexport interface ComputeStylesOptions {\n ssrCollector?: ServerStyleCollector | null;\n}\n\ninterface ProcessedChunk {\n name: string;\n styleKeys: string[];\n cacheKey: string;\n renderResult: RenderResult;\n className: string;\n}\n\nconst EMPTY_RESULT: ComputeStylesResult = { className: '' };\n\n/**\n * Get keyframes that are actually used in styles.\n * Returns null if no keyframes are used (fast path for zero overhead).\n */\nfunction getUsedKeyframes(\n styles: Styles,\n): Record<string, KeyframesSteps> | null {\n const hasLocal = hasLocalKeyframes(styles);\n const hasGlobal = hasGlobalKeyframes();\n if (!hasLocal && !hasGlobal) return null;\n\n const usedNames = extractAnimationNamesFromStyles(styles);\n if (usedNames.size === 0) return null;\n\n const local = hasLocal ? extractLocalKeyframes(styles) : null;\n const global = hasGlobal ? getGlobalKeyframes() : null;\n const allKeyframes = mergeKeyframes(local, global);\n\n return filterUsedKeyframes(allKeyframes, usedNames);\n}\n\n/**\n * Process a chunk on the SSR path: allocate via collector, render, collect CSS.\n */\nfunction processChunkSSR(\n collector: ServerStyleCollector,\n styles: Styles,\n chunkName: string,\n styleKeys: string[],\n): ProcessedChunk | null {\n if (styleKeys.length === 0) return null;\n\n const cacheKey = generateChunkCacheKey(styles, chunkName, styleKeys);\n const { className, isNewAllocation } = collector.allocateClassName(cacheKey);\n\n if (isNewAllocation) {\n const renderResult = renderStylesForChunk(styles, chunkName, styleKeys);\n if (renderResult.rules.length > 0) {\n collector.collectChunk(cacheKey, className, renderResult.rules);\n return { name: chunkName, styleKeys, cacheKey, renderResult, className };\n }\n return null;\n }\n\n return {\n name: chunkName,\n styleKeys,\n cacheKey,\n renderResult: { rules: [] },\n className,\n };\n}\n\n/**\n * Process a chunk on the client: render, allocate className, and inject\n * CSS synchronously. The injector's cache makes this idempotent.\n */\nfunction processChunkSync(\n styles: Styles,\n chunkName: string,\n styleKeys: string[],\n): ProcessedChunk | null {\n if (styleKeys.length === 0) return null;\n\n const cacheKey = generateChunkCacheKey(styles, chunkName, styleKeys);\n const renderResult = renderStylesForChunk(\n styles,\n chunkName,\n styleKeys,\n cacheKey,\n );\n if (renderResult.rules.length === 0) return null;\n\n const { className } = inject(renderResult.rules, { cacheKey });\n\n return { name: chunkName, styleKeys, cacheKey, renderResult, className };\n}\n\n/**\n * Inject keyframes synchronously and return a name replacement map.\n * On the client, keyframes are injected into the DOM.\n */\nfunction injectKeyframesSync(\n usedKeyframes: Record<string, KeyframesSteps>,\n): Map<string, string> | null {\n let nameMap: Map<string, string> | null = null;\n\n for (const [name, steps] of Object.entries(usedKeyframes)) {\n const result = keyframes(steps, { name });\n const injectedName = result.toString();\n if (injectedName !== name) {\n if (!nameMap) nameMap = new Map();\n nameMap.set(name, injectedName);\n }\n }\n\n return nameMap;\n}\n\n/**\n * Inject chunk rules synchronously, replacing animation names if needed.\n */\nfunction injectChunkRulesSync(\n chunks: ProcessedChunk[],\n nameMap: Map<string, string> | null,\n): void {\n for (const chunk of chunks) {\n if (chunk.renderResult.rules.length > 0) {\n const rulesToInject: StyleResult[] = nameMap\n ? chunk.renderResult.rules.map((rule) => ({\n ...rule,\n declarations: replaceAnimationNames(rule.declarations, nameMap!),\n }))\n : chunk.renderResult.rules;\n\n inject(rulesToInject, { cacheKey: chunk.cacheKey });\n }\n }\n}\n\n/**\n * Inject all ancillary rules (properties, font-faces, counter-styles) synchronously.\n */\nfunction injectAncillarySync(styles: Styles): void {\n if (hasLocalProperties(styles)) {\n const localProperties = extractLocalProperties(styles);\n if (localProperties) {\n for (const [token, definition] of Object.entries(localProperties)) {\n property(token, definition);\n }\n }\n }\n\n if (hasLocalFontFace(styles)) {\n const localFontFace = extractLocalFontFace(styles);\n if (localFontFace) {\n for (const [family, input] of Object.entries(localFontFace)) {\n const descriptors: FontFaceDescriptors[] = Array.isArray(input)\n ? input\n : [input];\n for (const desc of descriptors) {\n fontFace(family, desc);\n }\n }\n }\n }\n\n if (hasLocalCounterStyle(styles)) {\n const localCounterStyle = extractLocalCounterStyle(styles);\n if (localCounterStyle) {\n for (const [name, descriptors] of Object.entries(localCounterStyle)) {\n counterStyle(name, descriptors);\n }\n }\n }\n}\n\n/**\n * Collect all ancillary rules into the SSR collector.\n */\nfunction collectAncillarySSR(\n collector: ServerStyleCollector,\n styles: Styles,\n chunks: ProcessedChunk[],\n): void {\n const usedKf = getUsedKeyframes(styles);\n if (usedKf) {\n for (const [name, steps] of Object.entries(usedKf)) {\n const css = formatKeyframesCSS(name, steps);\n collector.collectKeyframes(name, css);\n }\n }\n\n if (hasLocalProperties(styles)) {\n const localProperties = extractLocalProperties(styles);\n if (localProperties) {\n for (const [token, definition] of Object.entries(localProperties)) {\n const css = formatPropertyCSS(token, definition);\n if (css) {\n collector.collectProperty(token, css);\n }\n }\n }\n }\n\n if (hasLocalFontFace(styles)) {\n const localFontFace = extractLocalFontFace(styles);\n if (localFontFace) {\n for (const [family, input] of Object.entries(localFontFace)) {\n const descriptors: FontFaceDescriptors[] = Array.isArray(input)\n ? input\n : [input];\n for (const desc of descriptors) {\n const hash = fontFaceContentHash(family, desc);\n const css = formatFontFaceRule(family, desc);\n collector.collectFontFace(hash, css);\n }\n }\n }\n }\n\n if (hasLocalCounterStyle(styles)) {\n const localCounterStyle = extractLocalCounterStyle(styles);\n if (localCounterStyle) {\n for (const [name, descriptors] of Object.entries(localCounterStyle)) {\n const css = formatCounterStyleRule(name, descriptors);\n collector.collectCounterStyle(name, css);\n }\n }\n }\n\n if (getConfig().autoPropertyTypes !== false) {\n const allRules = chunks.flatMap((c) => c.renderResult.rules);\n if (allRules.length > 0) {\n collectAutoInferredProperties(allRules, collector, styles);\n }\n }\n}\n\n/**\n * Synchronous, hook-free style computation.\n *\n * Resolves recipes, categorizes style keys into chunks, renders CSS rules,\n * allocates class names, and injects (client) or collects (SSR) the CSS.\n *\n * On the client, CSS is injected synchronously into the DOM. The injector's\n * content-based cache makes this idempotent — repeated calls with the same\n * styles are essentially free (Map lookup + refCount bump).\n *\n * On the server, an SSR collector is discovered via AsyncLocalStorage\n * (or passed explicitly via options) and CSS is collected as strings.\n *\n * @param styles - Tasty styles object (or undefined for no styles)\n * @param options - Optional SSR collector override\n */\nexport function computeStyles(\n styles: Styles | undefined,\n options?: ComputeStylesOptions,\n): ComputeStylesResult {\n if (!styles || !hasKeys(styles as Record<string, unknown>)) {\n return EMPTY_RESULT;\n }\n\n const resolved = resolveRecipes(styles);\n const chunkMap = categorizeStyleKeys(resolved as Record<string, unknown>);\n\n const collector =\n options?.ssrCollector !== undefined\n ? options.ssrCollector\n : getRegisteredSSRCollector();\n\n const chunks: ProcessedChunk[] = [];\n\n if (collector) {\n collector.collectInternals();\n\n for (const [chunkName, chunkStyleKeys] of chunkMap) {\n const chunk = processChunkSSR(\n collector,\n resolved,\n chunkName,\n chunkStyleKeys,\n );\n if (chunk) chunks.push(chunk);\n }\n\n collectAncillarySSR(collector, resolved, chunks);\n } else {\n injectAncillarySync(resolved);\n\n const usedKf = getUsedKeyframes(resolved);\n const nameMap = usedKf ? injectKeyframesSync(usedKf) : null;\n\n for (const [chunkName, chunkStyleKeys] of chunkMap) {\n const chunk = processChunkSync(resolved, chunkName, chunkStyleKeys);\n if (chunk) chunks.push(chunk);\n }\n\n if (nameMap) {\n injectChunkRulesSync(chunks, nameMap);\n }\n }\n\n if (chunks.length === 0) return EMPTY_RESULT;\n if (chunks.length === 1) return { className: chunks[0].className };\n\n return { className: chunks.map((c) => c.className).join(' ') };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"compute-styles.js","names":[],"sources":["../src/compute-styles.ts"],"sourcesContent":["/**\n * Hook-free, synchronous style computation.\n *\n * Extracts the core logic from useStyles() into a plain function that can\n * be called during React render without any hooks. Three code paths:\n *\n * 1. SSR collector — styles collected via ServerStyleCollector\n * 2. Client inject — styles injected synchronously into the DOM\n * 3. RSC inline — styles returned as CSS strings for inline <style> emission\n *\n * This enables tasty() components to work as React Server Components.\n */\n\nimport { cache } from 'react';\n\nimport {\n categorizeStyleKeys,\n generateChunkCacheKey,\n renderStylesForChunk,\n} from './chunks';\nimport {\n getConfig,\n getGlobalConfigTokens,\n getGlobalCounterStyle,\n getEffectiveProperties,\n getGlobalFontFace,\n getGlobalKeyframes,\n hasGlobalKeyframes,\n} from './config';\nimport {\n counterStyle,\n fontFace,\n inject,\n keyframes,\n property,\n touch,\n} from './injector';\nimport type { FontFaceDescriptors, KeyframesSteps } from './injector/types';\nimport {\n extractLocalCounterStyle,\n formatCounterStyleRule,\n hasLocalCounterStyle,\n} from './counter-style';\nimport {\n extractLocalFontFace,\n fontFaceContentHash,\n formatFontFaceRule,\n hasLocalFontFace,\n} from './font-face';\nimport {\n extractAnimationNamesFromStyles,\n extractLocalKeyframes,\n filterUsedKeyframes,\n hasLocalKeyframes,\n mergeKeyframes,\n replaceAnimationNames,\n} from './keyframes';\nimport type { RenderResult, StyleResult } from './pipeline';\nimport { renderStyles } from './pipeline';\nimport { extractLocalProperties, hasLocalProperties } from './properties';\nimport { collectAutoInferredProperties } from './ssr/collect-auto-properties';\nimport type { ServerStyleCollector } from './ssr/collector';\nimport { formatGlobalRules } from './ssr/format-global-rules';\nimport { formatKeyframesCSS } from './ssr/format-keyframes';\nimport { formatPropertyCSS } from './ssr/format-property';\nimport { formatRules } from './ssr/format-rules';\nimport { getRegisteredSSRCollector } from './ssr/ssr-collector-ref';\nimport type { Styles } from './styles/types';\nimport { hasKeys } from './utils/has-keys';\nimport { resolveRecipes } from './utils/resolve-recipes';\n\nexport interface ComputeStylesResult {\n className: string;\n /** CSS text to emit as an inline <style> tag (RSC mode only). */\n css?: string;\n}\n\nexport interface ComputeStylesOptions {\n ssrCollector?: ServerStyleCollector | null;\n}\n\ninterface ProcessedChunk {\n name: string;\n styleKeys: string[];\n cacheKey: string;\n renderResult: RenderResult;\n className: string;\n}\n\nconst EMPTY_RESULT: ComputeStylesResult = { className: '' };\n\n// ---------------------------------------------------------------------------\n// RSC (React Server Components) inline style support\n// ---------------------------------------------------------------------------\n\ninterface RSCStyleCache {\n cacheKeyToClassName: Map<string, string>;\n classCounter: number;\n emittedKeys: Set<string>;\n internalsEmitted: boolean;\n}\n\n/**\n * Per-request RSC style cache using React.cache.\n * React.cache provides per-request memoization in Server Components,\n * so each request gets its own isolated cache.\n */\nconst getRSCCache = cache(\n (): RSCStyleCache => ({\n cacheKeyToClassName: new Map(),\n classCounter: 0,\n emittedKeys: new Set(),\n internalsEmitted: false,\n }),\n);\n\nfunction rscAllocateClassName(\n rscCache: RSCStyleCache,\n cacheKey: string,\n): { className: string; isNew: boolean } {\n const existing = rscCache.cacheKeyToClassName.get(cacheKey);\n if (existing) return { className: existing, isNew: false };\n\n // Use 'r' prefix to avoid collisions with SSR collector's 't' prefix\n const className = `r${rscCache.classCounter++}`;\n rscCache.cacheKeyToClassName.set(cacheKey, className);\n return { className, isNew: true };\n}\n\n/**\n * Collect internals CSS for RSC — mirrors ServerStyleCollector.collectInternals().\n * Emitted once per request (tracked via rscCache.internalsEmitted).\n */\nfunction collectInternalsRSC(rscCache: RSCStyleCache): string {\n if (rscCache.internalsEmitted) return '';\n rscCache.internalsEmitted = true;\n\n const parts: string[] = [];\n\n for (const [token, definition] of Object.entries(getEffectiveProperties())) {\n const css = formatPropertyCSS(token, definition);\n if (css) parts.push(css);\n }\n\n const tokenStyles = getGlobalConfigTokens();\n if (tokenStyles && Object.keys(tokenStyles).length > 0) {\n const tokenRules = renderStyles(tokenStyles, ':root') as StyleResult[];\n if (tokenRules.length > 0) {\n const css = formatGlobalRules(tokenRules);\n if (css) parts.push(css);\n }\n }\n\n const globalFF = getGlobalFontFace();\n if (globalFF) {\n for (const [family, input] of Object.entries(globalFF)) {\n const descriptors: FontFaceDescriptors[] = Array.isArray(input)\n ? input\n : [input];\n for (const desc of descriptors) {\n parts.push(formatFontFaceRule(family, desc));\n }\n }\n }\n\n const globalCS = getGlobalCounterStyle();\n if (globalCS) {\n for (const [name, descriptors] of Object.entries(globalCS)) {\n parts.push(formatCounterStyleRule(name, descriptors));\n }\n }\n\n return parts.join('\\n');\n}\n\n/**\n * Collect per-component ancillary CSS (keyframes, @property, font-face,\n * counter-style) for RSC mode.\n */\nfunction collectAncillaryRSC(rscCache: RSCStyleCache, styles: Styles): string {\n const parts: string[] = [];\n\n const usedKf = getUsedKeyframes(styles);\n if (usedKf) {\n for (const [name, steps] of Object.entries(usedKf)) {\n const key = `__kf:${name}`;\n if (!rscCache.emittedKeys.has(key)) {\n rscCache.emittedKeys.add(key);\n parts.push(formatKeyframesCSS(name, steps));\n }\n }\n }\n\n if (hasLocalProperties(styles)) {\n const localProperties = extractLocalProperties(styles);\n if (localProperties) {\n for (const [token, definition] of Object.entries(localProperties)) {\n const key = `__prop:${token}`;\n if (!rscCache.emittedKeys.has(key)) {\n rscCache.emittedKeys.add(key);\n const css = formatPropertyCSS(token, definition);\n if (css) parts.push(css);\n }\n }\n }\n }\n\n if (hasLocalFontFace(styles)) {\n const localFontFace = extractLocalFontFace(styles);\n if (localFontFace) {\n for (const [family, input] of Object.entries(localFontFace)) {\n const descriptors: FontFaceDescriptors[] = Array.isArray(input)\n ? input\n : [input];\n for (const desc of descriptors) {\n const hash = fontFaceContentHash(family, desc);\n const key = `__ff:${hash}`;\n if (!rscCache.emittedKeys.has(key)) {\n rscCache.emittedKeys.add(key);\n parts.push(formatFontFaceRule(family, desc));\n }\n }\n }\n }\n }\n\n if (hasLocalCounterStyle(styles)) {\n const localCounterStyle = extractLocalCounterStyle(styles);\n if (localCounterStyle) {\n for (const [name, descriptors] of Object.entries(localCounterStyle)) {\n const key = `__cs:${name}`;\n if (!rscCache.emittedKeys.has(key)) {\n rscCache.emittedKeys.add(key);\n parts.push(formatCounterStyleRule(name, descriptors));\n }\n }\n }\n }\n\n return parts.join('\\n');\n}\n\n/**\n * Process all chunks in RSC mode: render CSS to strings, allocate classNames,\n * and return combined { className, css }.\n */\nfunction computeStylesRSC(\n styles: Styles,\n chunkMap: Map<string, string[]>,\n): ComputeStylesResult {\n const rscCache = getRSCCache();\n const cssParts: string[] = [];\n const classNames: string[] = [];\n\n const internalsCSS = collectInternalsRSC(rscCache);\n if (internalsCSS) cssParts.push(internalsCSS);\n\n for (const [chunkName, chunkStyleKeys] of chunkMap) {\n if (chunkStyleKeys.length === 0) continue;\n\n const cacheKey = generateChunkCacheKey(styles, chunkName, chunkStyleKeys);\n const { className, isNew } = rscAllocateClassName(rscCache, cacheKey);\n classNames.push(className);\n\n if (isNew) {\n const renderResult = renderStylesForChunk(\n styles,\n chunkName,\n chunkStyleKeys,\n );\n if (renderResult.rules.length > 0) {\n const css = formatRules(renderResult.rules, className);\n if (css) cssParts.push(css);\n }\n }\n }\n\n const ancillaryCSS = collectAncillaryRSC(rscCache, styles);\n if (ancillaryCSS) cssParts.push(ancillaryCSS);\n\n if (classNames.length === 0) return EMPTY_RESULT;\n\n const css = cssParts.join('\\n');\n\n return {\n className: classNames.join(' '),\n css: css || undefined,\n };\n}\n\n/**\n * Get keyframes that are actually used in styles.\n * Returns null if no keyframes are used (fast path for zero overhead).\n */\nfunction getUsedKeyframes(\n styles: Styles,\n): Record<string, KeyframesSteps> | null {\n const hasLocal = hasLocalKeyframes(styles);\n const hasGlobal = hasGlobalKeyframes();\n if (!hasLocal && !hasGlobal) return null;\n\n const usedNames = extractAnimationNamesFromStyles(styles);\n if (usedNames.size === 0) return null;\n\n const local = hasLocal ? extractLocalKeyframes(styles) : null;\n const global = hasGlobal ? getGlobalKeyframes() : null;\n const allKeyframes = mergeKeyframes(local, global);\n\n return filterUsedKeyframes(allKeyframes, usedNames);\n}\n\n/**\n * Process a chunk on the SSR path: allocate via collector, render, collect CSS.\n */\nfunction processChunkSSR(\n collector: ServerStyleCollector,\n styles: Styles,\n chunkName: string,\n styleKeys: string[],\n): ProcessedChunk | null {\n if (styleKeys.length === 0) return null;\n\n const cacheKey = generateChunkCacheKey(styles, chunkName, styleKeys);\n const { className, isNewAllocation } = collector.allocateClassName(cacheKey);\n\n if (isNewAllocation) {\n const renderResult = renderStylesForChunk(styles, chunkName, styleKeys);\n if (renderResult.rules.length > 0) {\n collector.collectChunk(cacheKey, className, renderResult.rules);\n return { name: chunkName, styleKeys, cacheKey, renderResult, className };\n }\n return null;\n }\n\n return {\n name: chunkName,\n styleKeys,\n cacheKey,\n renderResult: { rules: [] },\n className,\n };\n}\n\n/**\n * Process a chunk on the client: render, allocate className, and inject\n * CSS synchronously. The injector's cache makes this idempotent.\n */\nfunction processChunkSync(\n styles: Styles,\n chunkName: string,\n styleKeys: string[],\n): ProcessedChunk | null {\n if (styleKeys.length === 0) return null;\n\n const cacheKey = generateChunkCacheKey(styles, chunkName, styleKeys);\n const renderResult = renderStylesForChunk(\n styles,\n chunkName,\n styleKeys,\n cacheKey,\n );\n if (renderResult.rules.length === 0) return null;\n\n const { className } = inject(renderResult.rules, { cacheKey });\n\n return { name: chunkName, styleKeys, cacheKey, renderResult, className };\n}\n\n/**\n * Inject keyframes synchronously and return a name replacement map.\n * On the client, keyframes are injected into the DOM.\n */\nfunction injectKeyframesSync(\n usedKeyframes: Record<string, KeyframesSteps>,\n): Map<string, string> | null {\n let nameMap: Map<string, string> | null = null;\n\n for (const [name, steps] of Object.entries(usedKeyframes)) {\n const result = keyframes(steps, { name });\n const injectedName = result.toString();\n if (injectedName !== name) {\n if (!nameMap) nameMap = new Map();\n nameMap.set(name, injectedName);\n }\n }\n\n return nameMap;\n}\n\n/**\n * Inject chunk rules synchronously, replacing animation names if needed.\n */\nfunction injectChunkRulesSync(\n chunks: ProcessedChunk[],\n nameMap: Map<string, string> | null,\n): void {\n for (const chunk of chunks) {\n if (chunk.renderResult.rules.length > 0) {\n const rulesToInject: StyleResult[] = nameMap\n ? chunk.renderResult.rules.map((rule) => ({\n ...rule,\n declarations: replaceAnimationNames(rule.declarations, nameMap!),\n }))\n : chunk.renderResult.rules;\n\n inject(rulesToInject, { cacheKey: chunk.cacheKey });\n }\n }\n}\n\n/**\n * Inject all ancillary rules (properties, font-faces, counter-styles) synchronously.\n */\nfunction injectAncillarySync(styles: Styles): void {\n if (hasLocalProperties(styles)) {\n const localProperties = extractLocalProperties(styles);\n if (localProperties) {\n for (const [token, definition] of Object.entries(localProperties)) {\n property(token, definition);\n }\n }\n }\n\n if (hasLocalFontFace(styles)) {\n const localFontFace = extractLocalFontFace(styles);\n if (localFontFace) {\n for (const [family, input] of Object.entries(localFontFace)) {\n const descriptors: FontFaceDescriptors[] = Array.isArray(input)\n ? input\n : [input];\n for (const desc of descriptors) {\n fontFace(family, desc);\n }\n }\n }\n }\n\n if (hasLocalCounterStyle(styles)) {\n const localCounterStyle = extractLocalCounterStyle(styles);\n if (localCounterStyle) {\n for (const [name, descriptors] of Object.entries(localCounterStyle)) {\n counterStyle(name, descriptors);\n }\n }\n }\n}\n\n/**\n * Collect all ancillary rules into the SSR collector.\n */\nfunction collectAncillarySSR(\n collector: ServerStyleCollector,\n styles: Styles,\n chunks: ProcessedChunk[],\n): void {\n const usedKf = getUsedKeyframes(styles);\n if (usedKf) {\n for (const [name, steps] of Object.entries(usedKf)) {\n const css = formatKeyframesCSS(name, steps);\n collector.collectKeyframes(name, css);\n }\n }\n\n if (hasLocalProperties(styles)) {\n const localProperties = extractLocalProperties(styles);\n if (localProperties) {\n for (const [token, definition] of Object.entries(localProperties)) {\n const css = formatPropertyCSS(token, definition);\n if (css) {\n collector.collectProperty(token, css);\n }\n }\n }\n }\n\n if (hasLocalFontFace(styles)) {\n const localFontFace = extractLocalFontFace(styles);\n if (localFontFace) {\n for (const [family, input] of Object.entries(localFontFace)) {\n const descriptors: FontFaceDescriptors[] = Array.isArray(input)\n ? input\n : [input];\n for (const desc of descriptors) {\n const hash = fontFaceContentHash(family, desc);\n const css = formatFontFaceRule(family, desc);\n collector.collectFontFace(hash, css);\n }\n }\n }\n }\n\n if (hasLocalCounterStyle(styles)) {\n const localCounterStyle = extractLocalCounterStyle(styles);\n if (localCounterStyle) {\n for (const [name, descriptors] of Object.entries(localCounterStyle)) {\n const css = formatCounterStyleRule(name, descriptors);\n collector.collectCounterStyle(name, css);\n }\n }\n }\n\n if (getConfig().autoPropertyTypes !== false) {\n const allRules = chunks.flatMap((c) => c.renderResult.rules);\n if (allRules.length > 0) {\n collectAutoInferredProperties(allRules, collector, styles);\n }\n }\n}\n\n/**\n * Synchronous, hook-free style computation.\n *\n * Resolves recipes, categorizes style keys into chunks, renders CSS rules,\n * allocates class names, and injects / collects / returns the CSS.\n *\n * Three code paths:\n * 1. SSR collector — discovered via ALS or passed explicitly; CSS collected\n * 2. RSC inline — no collector and no `document`; CSS returned as `result.css`\n * for the caller to emit as an inline `<style>` tag\n * 3. Client inject — CSS injected synchronously into the DOM (idempotent)\n *\n * @param styles - Tasty styles object (or undefined for no styles)\n * @param options - Optional SSR collector override\n */\nexport function computeStyles(\n styles: Styles | undefined,\n options?: ComputeStylesOptions,\n): ComputeStylesResult {\n if (!styles || !hasKeys(styles as Record<string, unknown>)) {\n return EMPTY_RESULT;\n }\n\n const resolved = resolveRecipes(styles);\n const chunkMap = categorizeStyleKeys(resolved as Record<string, unknown>);\n\n const collector =\n options?.ssrCollector !== undefined\n ? options.ssrCollector\n : getRegisteredSSRCollector();\n\n const chunks: ProcessedChunk[] = [];\n\n if (collector) {\n collector.collectInternals();\n\n for (const [chunkName, chunkStyleKeys] of chunkMap) {\n const chunk = processChunkSSR(\n collector,\n resolved,\n chunkName,\n chunkStyleKeys,\n );\n if (chunk) chunks.push(chunk);\n }\n\n collectAncillarySSR(collector, resolved, chunks);\n } else if (typeof document === 'undefined') {\n // RSC path: render CSS to strings for inline <style> emission\n return computeStylesRSC(resolved, chunkMap);\n } else {\n injectAncillarySync(resolved);\n\n const usedKf = getUsedKeyframes(resolved);\n const nameMap = usedKf ? injectKeyframesSync(usedKf) : null;\n\n for (const [chunkName, chunkStyleKeys] of chunkMap) {\n const chunk = processChunkSync(resolved, chunkName, chunkStyleKeys);\n if (chunk) chunks.push(chunk);\n }\n\n if (nameMap) {\n injectChunkRulesSync(chunks, nameMap);\n }\n\n for (const chunk of chunks) {\n touch(chunk.className);\n }\n }\n\n if (chunks.length === 0) return EMPTY_RESULT;\n if (chunks.length === 1) return { className: chunks[0].className };\n\n return { className: chunks.map((c) => c.className).join(' ') };\n}\n"],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CounterStyleDescriptors, FontFaceInput, KeyframesSteps, PropertyDefinition } from "./injector/types.js";
|
|
1
|
+
import { CounterStyleDescriptors, FontFaceInput, GCConfig, KeyframesSteps, PropertyDefinition } from "./injector/types.js";
|
|
2
2
|
import { StyleDetails, UnitHandler } from "./parser/types.js";
|
|
3
3
|
import { StyleHandlerDefinition } from "./utils/styles.js";
|
|
4
4
|
import { ConfigTokens, RecipeStyles } from "./styles/types.js";
|
|
@@ -15,27 +15,10 @@ interface TastyConfig {
|
|
|
15
15
|
nonce?: string;
|
|
16
16
|
/** Maximum rules per stylesheet (default: 8192) */
|
|
17
17
|
maxRulesPerSheet?: number;
|
|
18
|
-
/** Threshold for bulk cleanup of unused styles (default: 500) */
|
|
19
|
-
unusedStylesThreshold?: number;
|
|
20
|
-
/** Delay before bulk cleanup in ms, ignored if idleCleanup is true (default: 5000) */
|
|
21
|
-
bulkCleanupDelay?: number;
|
|
22
|
-
/** Use requestIdleCallback for cleanup when available (default: true) */
|
|
23
|
-
idleCleanup?: boolean;
|
|
24
18
|
/** Force text injection mode, auto-detected in test environments (default: auto) */
|
|
25
19
|
forceTextInjection?: boolean;
|
|
26
20
|
/** Enable development mode features: performance metrics and debug info (default: auto) */
|
|
27
21
|
devMode?: boolean;
|
|
28
|
-
/**
|
|
29
|
-
* Ratio of unused styles to delete per bulk cleanup run (0..1).
|
|
30
|
-
* Defaults to 0.5 (oldest half) to reduce risk of removing styles
|
|
31
|
-
* that may be restored shortly after being marked unused.
|
|
32
|
-
*/
|
|
33
|
-
bulkCleanupBatchRatio?: number;
|
|
34
|
-
/**
|
|
35
|
-
* Minimum age (in ms) a style must remain unused before eligible for deletion.
|
|
36
|
-
* Helps avoid races during rapid mount/unmount cycles. Default: 10000ms.
|
|
37
|
-
*/
|
|
38
|
-
unusedStylesMinAgeMs?: number;
|
|
39
22
|
/**
|
|
40
23
|
* Global predefined states for advanced state mapping.
|
|
41
24
|
* These are state aliases that can be used in any component.
|
|
@@ -78,6 +61,18 @@ interface TastyConfig {
|
|
|
78
61
|
* @default true
|
|
79
62
|
*/
|
|
80
63
|
autoPropertyTypes?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Garbage collection configuration for unused styles.
|
|
66
|
+
* GC is triggered by touch count: every `touchInterval` touches, the
|
|
67
|
+
* oldest unused styles are evicted when their count exceeds `capacity`.
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* configure({
|
|
71
|
+
* gc: { touchInterval: 1000, capacity: 1000 },
|
|
72
|
+
* });
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
gc?: GCConfig;
|
|
81
76
|
/**
|
|
82
77
|
* Plugins that extend tasty with custom functions, units, or states.
|
|
83
78
|
* Plugins are processed in order, with later plugins overriding earlier ones.
|
|
@@ -109,7 +104,11 @@ interface TastyConfig {
|
|
|
109
104
|
/**
|
|
110
105
|
* Global CSS @property definitions for custom properties.
|
|
111
106
|
* Keys use tasty token syntax ($name for properties, #name for colors).
|
|
112
|
-
*
|
|
107
|
+
*
|
|
108
|
+
* Tasty ships with `DEFAULT_PROPERTIES` (e.g. `$gap`, `$radius`, `#white`,
|
|
109
|
+
* `#black`, `#clear`, `#border`, etc.) that are always included.
|
|
110
|
+
* Properties you specify here are merged on top, so you can override any
|
|
111
|
+
* default by using the same key.
|
|
113
112
|
*
|
|
114
113
|
* For color tokens (#name), `syntax: '<color>'` is auto-set and
|
|
115
114
|
* `initialValue` defaults to `'transparent'` if not specified.
|
|
@@ -121,6 +120,8 @@ interface TastyConfig {
|
|
|
121
120
|
* '$rotation': { syntax: '<angle>', initialValue: '0deg' },
|
|
122
121
|
* '$scale': { syntax: '<number>', inherits: false, initialValue: 1 },
|
|
123
122
|
* '#accent': { initialValue: 'purple' }, // syntax: '<color>' auto-set
|
|
123
|
+
* // Override a default property:
|
|
124
|
+
* '$gap': { syntax: '<length>', inherits: true, initialValue: '8px' },
|
|
124
125
|
* },
|
|
125
126
|
* });
|
|
126
127
|
*
|
package/dist/config.js
CHANGED
|
@@ -38,14 +38,14 @@ let globalProperties = null;
|
|
|
38
38
|
let globalRecipes = null;
|
|
39
39
|
let globalConfigTokens = null;
|
|
40
40
|
/**
|
|
41
|
-
*
|
|
42
|
-
* These are always
|
|
41
|
+
* Default properties shipped with tasty.
|
|
42
|
+
* These are always included unless explicitly overridden via `configure({ properties })`.
|
|
43
43
|
* Keys use tasty token syntax (#name for colors, $name for other properties).
|
|
44
44
|
*
|
|
45
45
|
* For properties with CSS @property-compatible types (length, time, number, color),
|
|
46
46
|
* an `initialValue` is provided so the property works even without a project-level token.
|
|
47
47
|
*/
|
|
48
|
-
const
|
|
48
|
+
const DEFAULT_PROPERTIES = {
|
|
49
49
|
"#tasty-second-fill": {
|
|
50
50
|
inherits: false,
|
|
51
51
|
initialValue: "transparent"
|
|
@@ -62,6 +62,14 @@ const INTERNAL_PROPERTIES = {
|
|
|
62
62
|
inherits: true,
|
|
63
63
|
initialValue: "rgb(0 0 0)"
|
|
64
64
|
},
|
|
65
|
+
"#clear": {
|
|
66
|
+
inherits: true,
|
|
67
|
+
initialValue: "transparent"
|
|
68
|
+
},
|
|
69
|
+
"#border": {
|
|
70
|
+
inherits: true,
|
|
71
|
+
initialValue: "rgb(0 0 0)"
|
|
72
|
+
},
|
|
65
73
|
$gap: {
|
|
66
74
|
syntax: "<length>",
|
|
67
75
|
inherits: true,
|
|
@@ -130,13 +138,8 @@ function isTestEnvironment() {
|
|
|
130
138
|
function createDefaultConfig(isTest) {
|
|
131
139
|
return {
|
|
132
140
|
maxRulesPerSheet: 8192,
|
|
133
|
-
unusedStylesThreshold: 500,
|
|
134
|
-
bulkCleanupDelay: 5e3,
|
|
135
|
-
idleCleanup: true,
|
|
136
141
|
forceTextInjection: isTest ?? false,
|
|
137
|
-
devMode: isDevEnv()
|
|
138
|
-
bulkCleanupBatchRatio: .5,
|
|
139
|
-
unusedStylesMinAgeMs: 1e4
|
|
142
|
+
devMode: isDevEnv()
|
|
140
143
|
};
|
|
141
144
|
}
|
|
142
145
|
/**
|
|
@@ -148,8 +151,7 @@ function markStylesGenerated() {
|
|
|
148
151
|
if (stylesGenerated) return;
|
|
149
152
|
stylesGenerated = true;
|
|
150
153
|
const injector = getGlobalInjector();
|
|
151
|
-
for (const [token, definition] of Object.entries(
|
|
152
|
-
if (globalProperties && Object.keys(globalProperties).length > 0) for (const [token, definition] of Object.entries(globalProperties)) injector.property(token, definition);
|
|
154
|
+
for (const [token, definition] of Object.entries(getEffectiveProperties())) injector.property(token, definition);
|
|
153
155
|
if (globalFontFace && Object.keys(globalFontFace).length > 0) for (const [family, input] of Object.entries(globalFontFace)) {
|
|
154
156
|
const descriptors = Array.isArray(input) ? input : [input];
|
|
155
157
|
for (const desc of descriptors) injector.fontFace(family, desc);
|
|
@@ -194,20 +196,6 @@ function setGlobalKeyframes(keyframes) {
|
|
|
194
196
|
_hasGlobalKeyframes = Object.keys(keyframes).length > 0;
|
|
195
197
|
}
|
|
196
198
|
/**
|
|
197
|
-
* Check if any global properties are configured.
|
|
198
|
-
* Fast path: returns false if no properties were ever set.
|
|
199
|
-
*/
|
|
200
|
-
function hasGlobalProperties() {
|
|
201
|
-
return globalProperties !== null && Object.keys(globalProperties).length > 0;
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Get global properties configuration.
|
|
205
|
-
* Returns null if no properties configured (fast path for zero-overhead).
|
|
206
|
-
*/
|
|
207
|
-
function getGlobalProperties() {
|
|
208
|
-
return globalProperties;
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
199
|
* Set global properties (called from configure).
|
|
212
200
|
* Internal use only.
|
|
213
201
|
*/
|
|
@@ -219,6 +207,17 @@ function setGlobalProperties(properties) {
|
|
|
219
207
|
globalProperties = properties;
|
|
220
208
|
}
|
|
221
209
|
/**
|
|
210
|
+
* Get the effective properties: DEFAULT_PROPERTIES merged with user-configured
|
|
211
|
+
* properties. User properties override defaults with matching keys.
|
|
212
|
+
*/
|
|
213
|
+
function getEffectiveProperties() {
|
|
214
|
+
if (!globalProperties) return DEFAULT_PROPERTIES;
|
|
215
|
+
return {
|
|
216
|
+
...DEFAULT_PROPERTIES,
|
|
217
|
+
...globalProperties
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
222
221
|
* Get global font-face configuration.
|
|
223
222
|
* Returns null if no font faces configured.
|
|
224
223
|
*/
|
|
@@ -498,6 +497,6 @@ function resetConfig() {
|
|
|
498
497
|
delete storage[GLOBAL_INJECTOR_KEY];
|
|
499
498
|
}
|
|
500
499
|
//#endregion
|
|
501
|
-
export {
|
|
500
|
+
export { configure, getConfig, getEffectiveProperties, getGlobalConfigTokens, getGlobalCounterStyle, getGlobalFontFace, getGlobalInjector, getGlobalKeyframes, getGlobalRecipes, hasGlobalKeyframes, hasGlobalRecipes, hasStylesGenerated, isConfigLocked, isTestEnvironment, markStylesGenerated, resetConfig };
|
|
502
501
|
|
|
503
502
|
//# sourceMappingURL=config.js.map
|