stoop 0.6.1 → 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 -25
- package/dist/create-stoop.js +48 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -17
- package/dist/inject.js +293 -0
- package/dist/types/index.d.ts +4 -4
- package/dist/types/index.js +5 -2
- 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 +10 -14
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Theme Provider component and hook.
|
|
5
|
+
* Manages theme state, localStorage persistence, cookie sync, and centralized theme variable updates.
|
|
6
|
+
* Includes the useTheme hook for accessing theme management context.
|
|
7
|
+
*
|
|
8
|
+
* CLIENT-ONLY: This module uses React hooks (useState, useLayoutEffect, useCallback, useMemo)
|
|
9
|
+
* and MUST have "use client" directive for Next.js App Router compatibility.
|
|
10
|
+
*/
|
|
11
|
+
import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useRef, useState, } from "react";
|
|
12
|
+
import { injectAllThemes } from "../core/theme-manager";
|
|
13
|
+
import { isBrowser, isProduction } from "../utils/helpers";
|
|
14
|
+
import { getCookie, setCookie, getFromStorage, setInStorage } from "../utils/storage";
|
|
15
|
+
/**
|
|
16
|
+
* Syncs a theme value between cookie and localStorage.
|
|
17
|
+
*
|
|
18
|
+
* @param value - Theme value to sync
|
|
19
|
+
* @param cookieKey - Cookie key (if undefined, cookie sync is skipped)
|
|
20
|
+
* @param storageKey - LocalStorage key
|
|
21
|
+
*/
|
|
22
|
+
function syncThemeStorage(value, cookieKey, storageKey) {
|
|
23
|
+
if (!isBrowser()) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const cookieValue = cookieKey ? getCookie(cookieKey) : null;
|
|
27
|
+
const localStorageResult = getFromStorage(storageKey);
|
|
28
|
+
const localStorageValue = localStorageResult.success ? localStorageResult.value : null;
|
|
29
|
+
if (cookieValue === value && localStorageValue !== value) {
|
|
30
|
+
setInStorage(storageKey, value);
|
|
31
|
+
}
|
|
32
|
+
if (localStorageValue === value && cookieKey && cookieValue !== value) {
|
|
33
|
+
setCookie(cookieKey, value);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Reads theme from cookie or localStorage, preferring cookie if available.
|
|
38
|
+
*
|
|
39
|
+
* @param cookieKey - Cookie key (if undefined, cookie is not checked)
|
|
40
|
+
* @param storageKey - LocalStorage key
|
|
41
|
+
* @param themes - Available themes map for validation
|
|
42
|
+
* @returns Theme name or null if not found or invalid
|
|
43
|
+
*/
|
|
44
|
+
function readThemeFromStorage(cookieKey, storageKey, themes) {
|
|
45
|
+
if (!isBrowser()) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
if (cookieKey !== undefined) {
|
|
49
|
+
const cookieValue = getCookie(cookieKey);
|
|
50
|
+
if (cookieValue && themes[cookieValue]) {
|
|
51
|
+
return cookieValue;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const storageResult = getFromStorage(storageKey);
|
|
55
|
+
const stored = storageResult.success ? storageResult.value : null;
|
|
56
|
+
if (stored && themes[stored]) {
|
|
57
|
+
return stored;
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Creates a Provider component for theme management.
|
|
63
|
+
*
|
|
64
|
+
* @param themes - Map of theme names to theme objects
|
|
65
|
+
* @param defaultTheme - Default theme object
|
|
66
|
+
* @param prefix - Optional prefix for CSS variable scoping
|
|
67
|
+
* @param globalCss - Optional global CSS object from config
|
|
68
|
+
* @param globalCssFunction - Optional globalCss function from createStoop
|
|
69
|
+
* @returns Provider component, theme context, and theme management context
|
|
70
|
+
*
|
|
71
|
+
* @remarks
|
|
72
|
+
* To prevent FOUC (Flash of Unstyled Content) when a user has a non-default theme stored,
|
|
73
|
+
* call `preloadTheme()` from your stoop instance in a script tag before React hydrates.
|
|
74
|
+
* Note: `preloadTheme()` takes no parameters and always preloads all configured themes.
|
|
75
|
+
*
|
|
76
|
+
* ```html
|
|
77
|
+
* <script>
|
|
78
|
+
* // Preload all themes before React renders
|
|
79
|
+
* stoopInstance.preloadTheme();
|
|
80
|
+
* </script>
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export function createProvider(themes, defaultTheme, prefix = "stoop", globalCss, globalCssFunction) {
|
|
84
|
+
const ThemeContext = createContext(null);
|
|
85
|
+
const ThemeManagementContext = createContext(null);
|
|
86
|
+
const availableThemeNames = Object.keys(themes);
|
|
87
|
+
const firstThemeName = availableThemeNames[0] || "default";
|
|
88
|
+
const configGlobalStyles = globalCss && globalCssFunction ? globalCssFunction(globalCss) : undefined;
|
|
89
|
+
function Provider({ attribute = "data-theme", children, cookieKey, defaultTheme: defaultThemeProp, storageKey = "stoop-theme", }) {
|
|
90
|
+
const [themeName, setThemeNameState] = useState(defaultThemeProp || firstThemeName);
|
|
91
|
+
const hasHydratedRef = useRef(false);
|
|
92
|
+
useLayoutEffect(() => {
|
|
93
|
+
if (!isBrowser() || hasHydratedRef.current) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const stored = readThemeFromStorage(cookieKey, storageKey, themes);
|
|
97
|
+
if (stored) {
|
|
98
|
+
syncThemeStorage(stored, cookieKey, storageKey);
|
|
99
|
+
if (stored !== themeName) {
|
|
100
|
+
setThemeNameState(stored);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
hasHydratedRef.current = true;
|
|
104
|
+
}, [cookieKey, storageKey, themes]);
|
|
105
|
+
useLayoutEffect(() => {
|
|
106
|
+
if (!isBrowser()) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const handleStorageChange = (e) => {
|
|
110
|
+
if (e.key === storageKey && e.newValue && themes[e.newValue] && e.newValue !== themeName) {
|
|
111
|
+
setThemeNameState(e.newValue);
|
|
112
|
+
syncThemeStorage(e.newValue, cookieKey, storageKey);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
window.addEventListener("storage", handleStorageChange);
|
|
116
|
+
return () => {
|
|
117
|
+
window.removeEventListener("storage", handleStorageChange);
|
|
118
|
+
};
|
|
119
|
+
}, [storageKey, cookieKey, themeName, themes]);
|
|
120
|
+
const currentTheme = useMemo(() => {
|
|
121
|
+
return themes[themeName] || themes[defaultThemeProp || firstThemeName] || defaultTheme;
|
|
122
|
+
}, [themeName, defaultThemeProp, firstThemeName, themes, defaultTheme]);
|
|
123
|
+
const themesInjectedRef = useRef(false);
|
|
124
|
+
const globalStylesInjectedRef = useRef(false);
|
|
125
|
+
useLayoutEffect(() => {
|
|
126
|
+
if (!isBrowser() || themesInjectedRef.current) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
injectAllThemes(themes, prefix, attribute);
|
|
130
|
+
themesInjectedRef.current = true;
|
|
131
|
+
}, [themes, prefix, attribute]);
|
|
132
|
+
useLayoutEffect(() => {
|
|
133
|
+
if (!isBrowser() || globalStylesInjectedRef.current) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (configGlobalStyles) {
|
|
137
|
+
configGlobalStyles();
|
|
138
|
+
globalStylesInjectedRef.current = true;
|
|
139
|
+
}
|
|
140
|
+
}, [configGlobalStyles]);
|
|
141
|
+
useLayoutEffect(() => {
|
|
142
|
+
if (!isBrowser()) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (attribute) {
|
|
146
|
+
document.documentElement.setAttribute(attribute, themeName);
|
|
147
|
+
}
|
|
148
|
+
}, [themeName, attribute]);
|
|
149
|
+
const setTheme = useCallback((newThemeName) => {
|
|
150
|
+
if (themes[newThemeName]) {
|
|
151
|
+
setThemeNameState(newThemeName);
|
|
152
|
+
setInStorage(storageKey, newThemeName);
|
|
153
|
+
syncThemeStorage(newThemeName, cookieKey, storageKey);
|
|
154
|
+
}
|
|
155
|
+
else if (!isProduction()) {
|
|
156
|
+
// eslint-disable-next-line no-console
|
|
157
|
+
console.warn(`[Stoop] Theme "${newThemeName}" not found. Available themes: ${availableThemeNames.join(", ")}`);
|
|
158
|
+
}
|
|
159
|
+
}, [storageKey, cookieKey, themes, availableThemeNames, themeName]);
|
|
160
|
+
const themeContextValue = useMemo(() => ({
|
|
161
|
+
theme: currentTheme,
|
|
162
|
+
themeName,
|
|
163
|
+
}), [currentTheme, themeName]);
|
|
164
|
+
const toggleTheme = useCallback(() => {
|
|
165
|
+
const currentIndex = availableThemeNames.indexOf(themeName);
|
|
166
|
+
const nextIndex = (currentIndex + 1) % availableThemeNames.length;
|
|
167
|
+
const newTheme = availableThemeNames[nextIndex];
|
|
168
|
+
setTheme(newTheme);
|
|
169
|
+
}, [themeName, setTheme, availableThemeNames]);
|
|
170
|
+
const managementContextValue = useMemo(() => ({
|
|
171
|
+
availableThemes: availableThemeNames,
|
|
172
|
+
setTheme,
|
|
173
|
+
theme: currentTheme,
|
|
174
|
+
themeName,
|
|
175
|
+
toggleTheme,
|
|
176
|
+
}), [currentTheme, themeName, setTheme, toggleTheme]);
|
|
177
|
+
return (_jsx(ThemeContext.Provider, { value: themeContextValue, children: _jsx(ThemeManagementContext.Provider, { value: managementContextValue, children: children }) }));
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
Provider,
|
|
181
|
+
ThemeContext,
|
|
182
|
+
ThemeManagementContext,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Creates a useTheme hook for a specific theme management context.
|
|
187
|
+
*
|
|
188
|
+
* @param ThemeManagementContext - React context for theme management
|
|
189
|
+
* @returns Hook function that returns theme management context value
|
|
190
|
+
*/
|
|
191
|
+
export function createUseThemeHook(ThemeManagementContext) {
|
|
192
|
+
return function useTheme() {
|
|
193
|
+
const context = useContext(ThemeManagementContext);
|
|
194
|
+
if (!context) {
|
|
195
|
+
throw new Error("useTheme must be used within a Provider");
|
|
196
|
+
}
|
|
197
|
+
return context;
|
|
198
|
+
};
|
|
199
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared constants used throughout the library.
|
|
3
|
+
* Includes cache size limits and nesting depth limits.
|
|
4
|
+
*/
|
|
5
|
+
export const EMPTY_CSS = Object.freeze({});
|
|
6
|
+
export const MAX_CSS_CACHE_SIZE = 10000;
|
|
7
|
+
export const MAX_CLASS_NAME_CACHE_SIZE = 5000;
|
|
8
|
+
export const MAX_CSS_NESTING_DEPTH = 10;
|
|
9
|
+
/**
|
|
10
|
+
* Cache size limits for various LRU caches.
|
|
11
|
+
*/
|
|
12
|
+
export const KEYFRAME_CACHE_LIMIT = 500;
|
|
13
|
+
export const SANITIZE_CACHE_SIZE_LIMIT = 1000;
|
|
14
|
+
/**
|
|
15
|
+
* Cookie defaults.
|
|
16
|
+
*/
|
|
17
|
+
export const DEFAULT_COOKIE_MAX_AGE = 31536000; // 1 year in seconds
|
|
18
|
+
export const DEFAULT_COOKIE_PATH = "/";
|
|
19
|
+
/**
|
|
20
|
+
* Approved theme scales - only these scales are allowed in theme objects.
|
|
21
|
+
*/
|
|
22
|
+
export const APPROVED_THEME_SCALES = [
|
|
23
|
+
"colors",
|
|
24
|
+
"opacities",
|
|
25
|
+
"space",
|
|
26
|
+
"radii",
|
|
27
|
+
"sizes",
|
|
28
|
+
"fonts",
|
|
29
|
+
"fontWeights",
|
|
30
|
+
"fontSizes",
|
|
31
|
+
"lineHeights",
|
|
32
|
+
"letterSpacings",
|
|
33
|
+
"shadows",
|
|
34
|
+
"zIndices",
|
|
35
|
+
"transitions",
|
|
36
|
+
];
|
|
37
|
+
/**
|
|
38
|
+
* Default themeMap mapping CSS properties to theme scales.
|
|
39
|
+
* Covers common CSS properties for zero-config experience.
|
|
40
|
+
* Missing properties gracefully fallback to pattern-based auto-detection.
|
|
41
|
+
*/
|
|
42
|
+
export const DEFAULT_THEME_MAP = {
|
|
43
|
+
accentColor: "colors",
|
|
44
|
+
animation: "transitions",
|
|
45
|
+
animationDelay: "transitions",
|
|
46
|
+
animationDuration: "transitions",
|
|
47
|
+
animationTimingFunction: "transitions",
|
|
48
|
+
backdropFilter: "shadows",
|
|
49
|
+
background: "colors",
|
|
50
|
+
backgroundColor: "colors",
|
|
51
|
+
blockSize: "sizes",
|
|
52
|
+
border: "colors",
|
|
53
|
+
borderBlockColor: "colors",
|
|
54
|
+
borderBlockEndColor: "colors",
|
|
55
|
+
borderBlockStartColor: "colors",
|
|
56
|
+
borderBottomColor: "colors",
|
|
57
|
+
borderBottomLeftRadius: "radii",
|
|
58
|
+
borderBottomRightRadius: "radii",
|
|
59
|
+
borderColor: "colors",
|
|
60
|
+
borderEndEndRadius: "radii",
|
|
61
|
+
borderEndStartRadius: "radii",
|
|
62
|
+
borderInlineColor: "colors",
|
|
63
|
+
borderInlineEndColor: "colors",
|
|
64
|
+
borderInlineStartColor: "colors",
|
|
65
|
+
borderLeftColor: "colors",
|
|
66
|
+
borderRadius: "radii",
|
|
67
|
+
borderRightColor: "colors",
|
|
68
|
+
borderStartEndRadius: "radii",
|
|
69
|
+
borderStartStartRadius: "radii",
|
|
70
|
+
borderTopColor: "colors",
|
|
71
|
+
borderTopLeftRadius: "radii",
|
|
72
|
+
borderTopRightRadius: "radii",
|
|
73
|
+
bottom: "space",
|
|
74
|
+
boxShadow: "shadows",
|
|
75
|
+
caretColor: "colors",
|
|
76
|
+
color: "colors",
|
|
77
|
+
columnGap: "space",
|
|
78
|
+
columnRuleColor: "colors",
|
|
79
|
+
fill: "colors",
|
|
80
|
+
filter: "shadows",
|
|
81
|
+
flexBasis: "sizes",
|
|
82
|
+
floodColor: "colors",
|
|
83
|
+
font: "fontSizes",
|
|
84
|
+
fontFamily: "fonts",
|
|
85
|
+
fontSize: "fontSizes",
|
|
86
|
+
fontWeight: "fontWeights",
|
|
87
|
+
gap: "space",
|
|
88
|
+
gridColumnGap: "space",
|
|
89
|
+
gridGap: "space",
|
|
90
|
+
gridRowGap: "space",
|
|
91
|
+
height: "sizes",
|
|
92
|
+
inlineSize: "sizes",
|
|
93
|
+
inset: "space",
|
|
94
|
+
insetBlock: "space",
|
|
95
|
+
insetBlockEnd: "space",
|
|
96
|
+
insetBlockStart: "space",
|
|
97
|
+
insetInline: "space",
|
|
98
|
+
insetInlineEnd: "space",
|
|
99
|
+
insetInlineStart: "space",
|
|
100
|
+
left: "space",
|
|
101
|
+
letterSpacing: "letterSpacings",
|
|
102
|
+
lightingColor: "colors",
|
|
103
|
+
lineHeight: "lineHeights",
|
|
104
|
+
margin: "space",
|
|
105
|
+
marginBlock: "space",
|
|
106
|
+
marginBlockEnd: "space",
|
|
107
|
+
marginBlockStart: "space",
|
|
108
|
+
marginBottom: "space",
|
|
109
|
+
marginInline: "space",
|
|
110
|
+
marginInlineEnd: "space",
|
|
111
|
+
marginInlineStart: "space",
|
|
112
|
+
marginLeft: "space",
|
|
113
|
+
marginRight: "space",
|
|
114
|
+
marginTop: "space",
|
|
115
|
+
maxBlockSize: "sizes",
|
|
116
|
+
maxHeight: "sizes",
|
|
117
|
+
maxInlineSize: "sizes",
|
|
118
|
+
maxWidth: "sizes",
|
|
119
|
+
minBlockSize: "sizes",
|
|
120
|
+
minHeight: "sizes",
|
|
121
|
+
minInlineSize: "sizes",
|
|
122
|
+
minWidth: "sizes",
|
|
123
|
+
opacity: "opacities",
|
|
124
|
+
outline: "colors",
|
|
125
|
+
outlineColor: "colors",
|
|
126
|
+
padding: "space",
|
|
127
|
+
paddingBlock: "space",
|
|
128
|
+
paddingBlockEnd: "space",
|
|
129
|
+
paddingBlockStart: "space",
|
|
130
|
+
paddingBottom: "space",
|
|
131
|
+
paddingInline: "space",
|
|
132
|
+
paddingInlineEnd: "space",
|
|
133
|
+
paddingInlineStart: "space",
|
|
134
|
+
paddingLeft: "space",
|
|
135
|
+
paddingRight: "space",
|
|
136
|
+
paddingTop: "space",
|
|
137
|
+
right: "space",
|
|
138
|
+
rowGap: "space",
|
|
139
|
+
size: "sizes",
|
|
140
|
+
stopColor: "colors",
|
|
141
|
+
stroke: "colors",
|
|
142
|
+
textDecorationColor: "colors",
|
|
143
|
+
textEmphasisColor: "colors",
|
|
144
|
+
textShadow: "shadows",
|
|
145
|
+
top: "space",
|
|
146
|
+
transition: "transitions",
|
|
147
|
+
transitionDelay: "transitions",
|
|
148
|
+
transitionDuration: "transitions",
|
|
149
|
+
transitionProperty: "transitions",
|
|
150
|
+
transitionTimingFunction: "transitions",
|
|
151
|
+
width: "sizes",
|
|
152
|
+
zIndex: "zIndices",
|
|
153
|
+
};
|
|
154
|
+
export const STOOP_COMPONENT_SYMBOL = Symbol.for("stoop.component");
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS compilation caching system.
|
|
3
|
+
* Tracks compiled CSS strings and class names to prevent duplicate work.
|
|
4
|
+
* Implements LRU (Least Recently Used) eviction when cache size limits are exceeded.
|
|
5
|
+
*/
|
|
6
|
+
import { MAX_CLASS_NAME_CACHE_SIZE, MAX_CSS_CACHE_SIZE } from "../constants";
|
|
7
|
+
/**
|
|
8
|
+
* LRU Cache implementation for class names and CSS strings.
|
|
9
|
+
* Automatically evicts least recently used entries when size limit is exceeded.
|
|
10
|
+
*/
|
|
11
|
+
export class LRUCache extends Map {
|
|
12
|
+
maxSize;
|
|
13
|
+
constructor(maxSize) {
|
|
14
|
+
super();
|
|
15
|
+
this.maxSize = maxSize;
|
|
16
|
+
}
|
|
17
|
+
get(key) {
|
|
18
|
+
const value = super.get(key);
|
|
19
|
+
if (value !== undefined) {
|
|
20
|
+
super.delete(key);
|
|
21
|
+
super.set(key, value);
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
set(key, value) {
|
|
26
|
+
if (super.has(key)) {
|
|
27
|
+
super.delete(key);
|
|
28
|
+
}
|
|
29
|
+
else if (this.size >= this.maxSize) {
|
|
30
|
+
const firstKey = this.keys().next().value;
|
|
31
|
+
if (firstKey !== undefined) {
|
|
32
|
+
super.delete(firstKey);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
super.set(key, value);
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export const classNameCache = new LRUCache(MAX_CLASS_NAME_CACHE_SIZE);
|
|
40
|
+
export const cssStringCache = new LRUCache(MAX_CSS_CACHE_SIZE);
|
|
41
|
+
const injectedStylesCache = new Set();
|
|
42
|
+
/**
|
|
43
|
+
* Checks if a CSS string has been injected.
|
|
44
|
+
*
|
|
45
|
+
* @param css - CSS string to check
|
|
46
|
+
* @returns True if CSS has been injected
|
|
47
|
+
*/
|
|
48
|
+
export function isCachedStyle(css) {
|
|
49
|
+
return injectedStylesCache.has(css);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Marks a CSS string as injected.
|
|
53
|
+
*
|
|
54
|
+
* @param css - CSS string to cache
|
|
55
|
+
*/
|
|
56
|
+
export function markStyleAsCached(css) {
|
|
57
|
+
injectedStylesCache.add(css);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Clears all cached styles.
|
|
61
|
+
*/
|
|
62
|
+
export function clearStyleCache() {
|
|
63
|
+
classNameCache.clear();
|
|
64
|
+
cssStringCache.clear();
|
|
65
|
+
injectedStylesCache.clear();
|
|
66
|
+
}
|