stoop 0.6.0 → 0.6.2
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/api/core-api.d.ts +2 -0
- package/dist/api/core-api.js +171 -0
- package/dist/api/styled.d.ts +3 -0
- package/dist/api/styled.js +467 -0
- package/dist/api/theme-provider.d.ts +3 -0
- package/dist/api/theme-provider.js +199 -0
- package/dist/constants.js +154 -0
- package/dist/core/cache.js +66 -0
- package/dist/core/compiler.js +408 -0
- package/dist/core/stringify.js +150 -0
- package/dist/core/theme-manager.js +97 -0
- package/dist/create-stoop-internal.d.ts +2 -2
- package/dist/create-stoop-internal.js +119 -0
- package/dist/create-stoop-ssr.d.ts +2 -0
- package/dist/create-stoop-ssr.js +22 -16
- package/dist/create-stoop.d.ts +6 -26
- package/dist/create-stoop.js +48 -17
- package/dist/index.d.ts +6 -0
- package/dist/index.js +5 -0
- package/dist/inject.js +293 -0
- package/dist/types/index.d.ts +4 -4
- package/dist/types/index.js +5 -0
- package/dist/utils/helpers.js +157 -0
- package/dist/utils/storage.js +130 -0
- package/dist/utils/theme-utils.js +328 -0
- package/dist/utils/theme.js +430 -0
- package/package.json +14 -28
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme token resolution utilities.
|
|
3
|
+
* Converts theme tokens to CSS variables for runtime theme switching.
|
|
4
|
+
*/
|
|
5
|
+
import { isCSSObject, isThemeObject, isProduction } from "./helpers";
|
|
6
|
+
import { escapeCSSVariableValue, getScaleForProperty, sanitizeCSSVariableName, } from "./theme-utils";
|
|
7
|
+
const TOKEN_REGEX = /(-?\$[a-zA-Z][a-zA-Z0-9]*(?:\$[a-zA-Z][a-zA-Z0-9]*)?(?:\.[a-zA-Z][a-zA-Z0-9]*)?)/g;
|
|
8
|
+
// Cache token indices per theme to avoid rebuilding them
|
|
9
|
+
const tokenIndexCache = new WeakMap();
|
|
10
|
+
/**
|
|
11
|
+
* Builds an index of all tokens in a theme for fast lookups.
|
|
12
|
+
* Results are cached per theme instance to avoid rebuilding.
|
|
13
|
+
*
|
|
14
|
+
* @param theme - Theme to index
|
|
15
|
+
* @returns Map of token names to their paths in the theme
|
|
16
|
+
*/
|
|
17
|
+
function buildTokenIndex(theme) {
|
|
18
|
+
const cached = tokenIndexCache.get(theme);
|
|
19
|
+
if (cached) {
|
|
20
|
+
return cached;
|
|
21
|
+
}
|
|
22
|
+
const index = new Map();
|
|
23
|
+
function processThemeObject(obj, path = []) {
|
|
24
|
+
const keys = Object.keys(obj);
|
|
25
|
+
for (const key of keys) {
|
|
26
|
+
const value = obj[key];
|
|
27
|
+
const currentPath = [...path, key];
|
|
28
|
+
if (isThemeObject(value)) {
|
|
29
|
+
processThemeObject(value, currentPath);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
const existing = index.get(key);
|
|
33
|
+
if (existing) {
|
|
34
|
+
existing.push(currentPath);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
index.set(key, [currentPath]);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
processThemeObject(theme);
|
|
43
|
+
for (const [, paths] of index.entries()) {
|
|
44
|
+
if (paths.length > 1) {
|
|
45
|
+
paths.sort((a, b) => {
|
|
46
|
+
const depthDiff = a.length - b.length;
|
|
47
|
+
if (depthDiff !== 0) {
|
|
48
|
+
return depthDiff;
|
|
49
|
+
}
|
|
50
|
+
const pathA = a.join(".");
|
|
51
|
+
const pathB = b.join(".");
|
|
52
|
+
return pathA.localeCompare(pathB);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
tokenIndexCache.set(theme, index);
|
|
57
|
+
return index;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Checks if two themes have the same top-level keys (excluding 'media').
|
|
61
|
+
*
|
|
62
|
+
* @param theme1 - First theme
|
|
63
|
+
* @param theme2 - Second theme
|
|
64
|
+
* @returns True if themes have same keys
|
|
65
|
+
*/
|
|
66
|
+
function themesHaveSameKeys(theme1, theme2) {
|
|
67
|
+
const keys1 = Object.keys(theme1).filter((key) => key !== "media");
|
|
68
|
+
const keys2 = Object.keys(theme2).filter((key) => key !== "media");
|
|
69
|
+
if (keys1.length !== keys2.length) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
for (const key of keys1) {
|
|
73
|
+
if (!(key in theme2)) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Compares two themes for structural and value equality.
|
|
81
|
+
* Excludes 'media' property from comparison.
|
|
82
|
+
*
|
|
83
|
+
* @param theme1 - First theme to compare
|
|
84
|
+
* @param theme2 - Second theme to compare
|
|
85
|
+
* @returns True if themes are equal
|
|
86
|
+
*/
|
|
87
|
+
export function themesAreEqual(theme1, theme2) {
|
|
88
|
+
if (theme1 === theme2) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
if (!theme1 || !theme2) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
if (!themesHaveSameKeys(theme1, theme2)) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
const theme1WithoutMedia = { ...theme1 };
|
|
98
|
+
const theme2WithoutMedia = { ...theme2 };
|
|
99
|
+
delete theme1WithoutMedia.media;
|
|
100
|
+
delete theme2WithoutMedia.media;
|
|
101
|
+
return JSON.stringify(theme1WithoutMedia) === JSON.stringify(theme2WithoutMedia);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Finds a token in the theme, optionally scoped to a specific scale.
|
|
105
|
+
* Uses cached token index for performance.
|
|
106
|
+
*
|
|
107
|
+
* @param theme - Theme to search
|
|
108
|
+
* @param tokenName - Token name to find
|
|
109
|
+
* @param scale - Optional scale to search within first
|
|
110
|
+
* @returns Path to token or null if not found
|
|
111
|
+
*/
|
|
112
|
+
function findTokenInTheme(theme, tokenName, scale) {
|
|
113
|
+
// Fast path: check specific scale first if provided
|
|
114
|
+
if (scale && scale in theme) {
|
|
115
|
+
const scaleValue = theme[scale];
|
|
116
|
+
if (scaleValue &&
|
|
117
|
+
typeof scaleValue === "object" &&
|
|
118
|
+
!Array.isArray(scaleValue) &&
|
|
119
|
+
tokenName in scaleValue) {
|
|
120
|
+
return [scale, tokenName];
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Use cached index (buildTokenIndex uses WeakMap cache)
|
|
124
|
+
const index = buildTokenIndex(theme);
|
|
125
|
+
const paths = index.get(tokenName);
|
|
126
|
+
if (!paths || paths.length === 0) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
return paths[0];
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Converts a theme token string to a CSS variable reference.
|
|
133
|
+
*
|
|
134
|
+
* @param token - Token string (e.g., "$primary" or "$colors$primary")
|
|
135
|
+
* @param theme - Optional theme for token resolution
|
|
136
|
+
* @param property - Optional CSS property name for scale detection
|
|
137
|
+
* @param themeMap - Optional theme scale mappings
|
|
138
|
+
* @returns CSS variable reference string
|
|
139
|
+
*/
|
|
140
|
+
export function tokenToCSSVar(token, theme, property, themeMap) {
|
|
141
|
+
if (!token.startsWith("$")) {
|
|
142
|
+
return token;
|
|
143
|
+
}
|
|
144
|
+
const tokenName = token.slice(1);
|
|
145
|
+
// Fast path: explicit token paths (e.g., "$colors.background", "$space$medium")
|
|
146
|
+
// These don't need theme lookup - convert directly to CSS variable
|
|
147
|
+
if (tokenName.includes("$") || tokenName.includes(".")) {
|
|
148
|
+
const parts = tokenName.includes("$") ? tokenName.split("$") : tokenName.split(".");
|
|
149
|
+
const sanitizedParts = parts.map((part) => sanitizeCSSVariableName(part));
|
|
150
|
+
const cssVarName = `--${sanitizedParts.join("-")}`;
|
|
151
|
+
return `var(${cssVarName})`;
|
|
152
|
+
}
|
|
153
|
+
if (theme && property) {
|
|
154
|
+
const scale = getScaleForProperty(property, themeMap);
|
|
155
|
+
if (scale) {
|
|
156
|
+
const foundPath = findTokenInTheme(theme, tokenName, scale);
|
|
157
|
+
if (foundPath) {
|
|
158
|
+
const sanitizedParts = foundPath.map((part) => sanitizeCSSVariableName(part));
|
|
159
|
+
const cssVarName = `--${sanitizedParts.join("-")}`;
|
|
160
|
+
return `var(${cssVarName})`;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Reuse the index from findTokenInTheme if available, otherwise build it once
|
|
164
|
+
const index = buildTokenIndex(theme);
|
|
165
|
+
const paths = index.get(tokenName);
|
|
166
|
+
if (paths && paths.length > 1) {
|
|
167
|
+
if (!isProduction()) {
|
|
168
|
+
const scaleInfo = scale
|
|
169
|
+
? `Property "${property}" maps to "${scale}" scale, but token not found there. `
|
|
170
|
+
: `No scale mapping found for property "${property}". `;
|
|
171
|
+
// eslint-disable-next-line no-console
|
|
172
|
+
console.warn(`[Stoop] Ambiguous token "$${tokenName}" found in multiple categories: ${paths.map((p) => p.join(".")).join(", ")}. ` +
|
|
173
|
+
`${scaleInfo}` +
|
|
174
|
+
`Using "${paths[0].join(".")}" (deterministic: shorter paths first, then alphabetical). ` +
|
|
175
|
+
`Use full path "$${paths[0].join(".")}" to be explicit.`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Use the already-built index instead of calling findTokenInTheme again
|
|
179
|
+
if (paths && paths.length > 0) {
|
|
180
|
+
const [foundPath] = paths;
|
|
181
|
+
const sanitizedParts = foundPath.map((part) => sanitizeCSSVariableName(part));
|
|
182
|
+
const cssVarName = `--${sanitizedParts.join("-")}`;
|
|
183
|
+
return `var(${cssVarName})`;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
else if (theme) {
|
|
187
|
+
const index = buildTokenIndex(theme);
|
|
188
|
+
const paths = index.get(tokenName);
|
|
189
|
+
if (paths && paths.length > 1) {
|
|
190
|
+
if (!isProduction()) {
|
|
191
|
+
// eslint-disable-next-line no-console
|
|
192
|
+
console.warn(`[Stoop] Ambiguous token "$${tokenName}" found in multiple categories: ${paths.map((p) => p.join(".")).join(", ")}. ` +
|
|
193
|
+
`Using "${paths[0].join(".")}" (deterministic: shorter paths first, then alphabetical). ` +
|
|
194
|
+
`Use full path "$${paths[0].join(".")}" to be explicit, or use with a CSS property for automatic resolution.`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// Use the already-built index instead of calling findTokenInTheme again
|
|
198
|
+
if (paths && paths.length > 0) {
|
|
199
|
+
const [foundPath] = paths;
|
|
200
|
+
const sanitizedParts = foundPath.map((part) => sanitizeCSSVariableName(part));
|
|
201
|
+
const cssVarName = `--${sanitizedParts.join("-")}`;
|
|
202
|
+
return `var(${cssVarName})`;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const sanitizedTokenName = sanitizeCSSVariableName(tokenName);
|
|
206
|
+
const cssVarName = `--${sanitizedTokenName}`;
|
|
207
|
+
return `var(${cssVarName})`;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Generates CSS custom properties from a theme object.
|
|
211
|
+
*
|
|
212
|
+
* @param theme - Theme object to convert to CSS variables
|
|
213
|
+
* @param prefix - Optional prefix for CSS variable names
|
|
214
|
+
* @param attributeSelector - Optional attribute selector (e.g., '[data-theme="light"]'). Defaults to ':root'
|
|
215
|
+
* @returns CSS string with selector and CSS variables
|
|
216
|
+
*/
|
|
217
|
+
export function generateCSSVariables(theme, prefix = "stoop", attributeSelector) {
|
|
218
|
+
const rootSelector = attributeSelector || ":root";
|
|
219
|
+
const variables = [];
|
|
220
|
+
function processThemeObject(obj, path = []) {
|
|
221
|
+
const keys = Object.keys(obj).sort();
|
|
222
|
+
for (const key of keys) {
|
|
223
|
+
if (key === "media") {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
const value = obj[key];
|
|
227
|
+
const currentPath = [...path, key];
|
|
228
|
+
if (isThemeObject(value)) {
|
|
229
|
+
processThemeObject(value, currentPath);
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
const sanitizedParts = currentPath.map((part) => sanitizeCSSVariableName(part));
|
|
233
|
+
const varName = `--${sanitizedParts.join("-")}`;
|
|
234
|
+
const escapedValue = typeof value === "string" || typeof value === "number"
|
|
235
|
+
? escapeCSSVariableValue(value)
|
|
236
|
+
: String(value);
|
|
237
|
+
variables.push(` ${varName}: ${escapedValue};`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
processThemeObject(theme);
|
|
242
|
+
if (variables.length === 0) {
|
|
243
|
+
return "";
|
|
244
|
+
}
|
|
245
|
+
return `${rootSelector} {\n${variables.join("\n")}\n}`;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Generates CSS custom properties for all themes using attribute selectors.
|
|
249
|
+
* All themes are available simultaneously, with theme switching handled by changing the data-theme attribute.
|
|
250
|
+
*
|
|
251
|
+
* @param themes - Map of theme names to theme objects
|
|
252
|
+
* @param prefix - Optional prefix for CSS variable names
|
|
253
|
+
* @param attribute - Attribute name for theme selection (defaults to 'data-theme')
|
|
254
|
+
* @returns CSS string with all theme CSS variables
|
|
255
|
+
*/
|
|
256
|
+
export function generateAllThemeVariables(themes, prefix = "stoop", attribute = "data-theme") {
|
|
257
|
+
const themeBlocks = [];
|
|
258
|
+
for (const [themeName, theme] of Object.entries(themes)) {
|
|
259
|
+
const attributeSelector = `[${attribute}="${themeName}"]`;
|
|
260
|
+
const cssVars = generateCSSVariables(theme, prefix, attributeSelector);
|
|
261
|
+
if (cssVars) {
|
|
262
|
+
themeBlocks.push(cssVars);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return themeBlocks.join("\n\n");
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Recursively replaces theme tokens with CSS variable references in a CSS object.
|
|
269
|
+
*
|
|
270
|
+
* @param obj - CSS object to process
|
|
271
|
+
* @param theme - Optional theme for token resolution
|
|
272
|
+
* @param themeMap - Optional theme scale mappings
|
|
273
|
+
* @param property - Optional CSS property name for scale detection
|
|
274
|
+
* @returns CSS object with tokens replaced by CSS variables
|
|
275
|
+
*/
|
|
276
|
+
export function replaceThemeTokensWithVars(obj, theme, themeMap, property) {
|
|
277
|
+
if (!obj || typeof obj !== "object") {
|
|
278
|
+
return obj;
|
|
279
|
+
}
|
|
280
|
+
// Fast pre-check: scan for tokens before creating new object
|
|
281
|
+
const keys = Object.keys(obj);
|
|
282
|
+
let hasAnyTokens = false;
|
|
283
|
+
// Quick scan to see if we need to process (only check top-level strings)
|
|
284
|
+
for (const key of keys) {
|
|
285
|
+
const value = obj[key];
|
|
286
|
+
if (typeof value === "string" && value.includes("$")) {
|
|
287
|
+
hasAnyTokens = true;
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
// If no tokens found and no theme needed, return original
|
|
292
|
+
if (!hasAnyTokens && !theme) {
|
|
293
|
+
return obj;
|
|
294
|
+
}
|
|
295
|
+
const result = {};
|
|
296
|
+
let hasTokens = false;
|
|
297
|
+
for (const key of keys) {
|
|
298
|
+
const value = obj[key];
|
|
299
|
+
if (isCSSObject(value)) {
|
|
300
|
+
const processed = replaceThemeTokensWithVars(value, theme, themeMap, undefined);
|
|
301
|
+
result[key] = processed;
|
|
302
|
+
if (processed !== value) {
|
|
303
|
+
hasTokens = true;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
else if (typeof value === "string" && value.includes("$")) {
|
|
307
|
+
hasTokens = true;
|
|
308
|
+
const cssProperty = property || key;
|
|
309
|
+
// Fast path: if value is exactly a single token (most common case), skip regex
|
|
310
|
+
// Check if it's a single token by ensuring it starts with $ and has no spaces or calc()
|
|
311
|
+
// This covers cases like "$colors.background", "$primary", "-$space.small"
|
|
312
|
+
const isSingleToken = value.startsWith("$") &&
|
|
313
|
+
!value.includes(" ") &&
|
|
314
|
+
!value.includes("calc(") &&
|
|
315
|
+
value === value.trim(); // No leading/trailing whitespace
|
|
316
|
+
if (isSingleToken) {
|
|
317
|
+
// Ultra-fast path for explicit tokens (e.g., "$colors.background")
|
|
318
|
+
// Convert directly to CSS variable without function call overhead
|
|
319
|
+
if (value.startsWith("-$")) {
|
|
320
|
+
const positiveToken = value.slice(1);
|
|
321
|
+
// Check if it's an explicit token path (contains . or $)
|
|
322
|
+
if (positiveToken.includes(".") || positiveToken.includes("$")) {
|
|
323
|
+
const parts = positiveToken.includes("$")
|
|
324
|
+
? positiveToken.split("$")
|
|
325
|
+
: positiveToken.split(".");
|
|
326
|
+
// Fast inline conversion - most theme scale names are already valid
|
|
327
|
+
// Only sanitize if needed (contains invalid chars) - use fast char check
|
|
328
|
+
let needsSanitization = false;
|
|
329
|
+
for (let i = 0; i < parts.length; i++) {
|
|
330
|
+
const part = parts[i];
|
|
331
|
+
for (let j = 0; j < part.length; j++) {
|
|
332
|
+
const char = part.charCodeAt(j);
|
|
333
|
+
// Check if char is not alphanumeric, dash, or underscore
|
|
334
|
+
if (!((char >= 48 && char <= 57) || // 0-9
|
|
335
|
+
(char >= 65 && char <= 90) || // A-Z
|
|
336
|
+
(char >= 97 && char <= 122) || // a-z
|
|
337
|
+
char === 45 ||
|
|
338
|
+
char === 95)) {
|
|
339
|
+
// - or _
|
|
340
|
+
needsSanitization = true;
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
if (needsSanitization)
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
if (needsSanitization) {
|
|
348
|
+
// Fall back to sanitized version for edge cases
|
|
349
|
+
const sanitizedParts = parts.map((part) => sanitizeCSSVariableName(part));
|
|
350
|
+
const cssVarName = `--${sanitizedParts.join("-")}`;
|
|
351
|
+
result[key] = `calc(-1 * var(${cssVarName}))`;
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
// Fast path: no sanitization needed
|
|
355
|
+
const cssVarName = `--${parts.join("-")}`;
|
|
356
|
+
result[key] = `calc(-1 * var(${cssVarName}))`;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
// Simple token - use existing function
|
|
361
|
+
const cssVar = tokenToCSSVar(positiveToken, theme, cssProperty, themeMap);
|
|
362
|
+
result[key] = `calc(-1 * ${cssVar})`;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
// Check if it's an explicit token path (contains . or $ after the $)
|
|
367
|
+
const tokenName = value.slice(1);
|
|
368
|
+
if (tokenName.includes(".") || tokenName.includes("$")) {
|
|
369
|
+
const parts = tokenName.includes("$") ? tokenName.split("$") : tokenName.split(".");
|
|
370
|
+
// Fast inline conversion - most theme scale names are already valid
|
|
371
|
+
// Only sanitize if needed (contains invalid chars) - use fast char check
|
|
372
|
+
let needsSanitization = false;
|
|
373
|
+
for (let i = 0; i < parts.length; i++) {
|
|
374
|
+
const part = parts[i];
|
|
375
|
+
for (let j = 0; j < part.length; j++) {
|
|
376
|
+
const char = part.charCodeAt(j);
|
|
377
|
+
// Check if char is not alphanumeric, dash, or underscore
|
|
378
|
+
if (!((char >= 48 && char <= 57) || // 0-9
|
|
379
|
+
(char >= 65 && char <= 90) || // A-Z
|
|
380
|
+
(char >= 97 && char <= 122) || // a-z
|
|
381
|
+
char === 45 ||
|
|
382
|
+
char === 95)) {
|
|
383
|
+
// - or _
|
|
384
|
+
needsSanitization = true;
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
if (needsSanitization)
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
391
|
+
if (needsSanitization) {
|
|
392
|
+
// Fall back to sanitized version for edge cases
|
|
393
|
+
const sanitizedParts = parts.map((part) => sanitizeCSSVariableName(part));
|
|
394
|
+
const cssVarName = `--${sanitizedParts.join("-")}`;
|
|
395
|
+
result[key] = `var(${cssVarName})`;
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
// Fast path: no sanitization needed
|
|
399
|
+
const cssVarName = `--${parts.join("-")}`;
|
|
400
|
+
result[key] = `var(${cssVarName})`;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
// Simple token - use existing function
|
|
405
|
+
result[key] = tokenToCSSVar(value, theme, cssProperty, themeMap);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
// Complex case: multiple tokens or tokens in expressions - use regex
|
|
411
|
+
result[key] = value.replace(TOKEN_REGEX, (token) => {
|
|
412
|
+
if (token.startsWith("-$")) {
|
|
413
|
+
const positiveToken = token.slice(1);
|
|
414
|
+
const cssVar = tokenToCSSVar(positiveToken, theme, cssProperty, themeMap);
|
|
415
|
+
return `calc(-1 * ${cssVar})`;
|
|
416
|
+
}
|
|
417
|
+
return tokenToCSSVar(token, theme, cssProperty, themeMap);
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
result[key] = value;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
// Return original object if no tokens were found (avoid unnecessary object creation)
|
|
426
|
+
if (!hasTokens) {
|
|
427
|
+
return obj;
|
|
428
|
+
}
|
|
429
|
+
return result;
|
|
430
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stoop",
|
|
3
3
|
"description": "CSS-in-JS library with type inference, theme creation, and variants support.",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.2",
|
|
5
5
|
"author": "Jackson Dolman <mail@dolmios.com>",
|
|
6
|
-
"main": "./dist/
|
|
7
|
-
"types": "./dist/
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "https://github.com/dolmios/stoop.git"
|
|
@@ -22,34 +22,20 @@
|
|
|
22
22
|
},
|
|
23
23
|
"exports": {
|
|
24
24
|
".": {
|
|
25
|
-
"types": "./dist/
|
|
26
|
-
"
|
|
27
|
-
"require": "./dist/create-stoop.js",
|
|
28
|
-
"default": "./dist/create-stoop.js"
|
|
29
|
-
},
|
|
30
|
-
"./utils/storage": {
|
|
31
|
-
"types": "./dist/utils/storage.d.ts",
|
|
32
|
-
"import": "./dist/utils/storage.js",
|
|
33
|
-
"require": "./dist/utils/storage.js",
|
|
34
|
-
"default": "./dist/utils/storage.js"
|
|
35
|
-
},
|
|
36
|
-
"./types": {
|
|
37
|
-
"types": "./dist/types/index.d.ts",
|
|
38
|
-
"import": "./dist/types/index.js",
|
|
39
|
-
"require": "./dist/types/index.js",
|
|
40
|
-
"default": "./dist/types/index.js"
|
|
41
|
-
},
|
|
42
|
-
"./inject": {
|
|
43
|
-
"types": "./dist/inject.d.ts",
|
|
44
|
-
"import": "./dist/inject.js",
|
|
45
|
-
"require": "./dist/inject.js",
|
|
46
|
-
"default": "./dist/inject.js"
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"default": "./dist/index.js"
|
|
47
27
|
},
|
|
48
28
|
"./ssr": {
|
|
49
29
|
"types": "./dist/create-stoop-ssr.d.ts",
|
|
50
|
-
"import": "./dist/create-stoop-ssr.js",
|
|
51
|
-
"require": "./dist/create-stoop-ssr.js",
|
|
52
30
|
"default": "./dist/create-stoop-ssr.js"
|
|
31
|
+
},
|
|
32
|
+
"./styled": {
|
|
33
|
+
"types": "./dist/api/styled.d.ts",
|
|
34
|
+
"default": "./dist/api/styled.js"
|
|
35
|
+
},
|
|
36
|
+
"./theme-provider": {
|
|
37
|
+
"types": "./dist/api/theme-provider.d.ts",
|
|
38
|
+
"default": "./dist/api/theme-provider.js"
|
|
53
39
|
}
|
|
54
40
|
},
|
|
55
41
|
"files": [
|
|
@@ -87,7 +73,7 @@
|
|
|
87
73
|
"react": ">=16.8.0"
|
|
88
74
|
},
|
|
89
75
|
"scripts": {
|
|
90
|
-
"build": "
|
|
76
|
+
"build": "bunx tsc --project tsconfig.build.json",
|
|
91
77
|
"lint": "eslint src --fix",
|
|
92
78
|
"prepare": "bun run build",
|
|
93
79
|
"test": "bun test",
|