@barocss/browser 0.0.1
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/LICENSE +21 -0
- package/README.md +273 -0
- package/dist/baro-boot.d.ts +8 -0
- package/dist/browser-runtime.d.ts +119 -0
- package/dist/cdn/barocss.js +8799 -0
- package/dist/cdn/barocss.js.map +1 -0
- package/dist/cdn/barocss.umd.cjs +2 -0
- package/dist/cdn/barocss.umd.cjs.map +1 -0
- package/dist/change-detector.d.ts +80 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.es.js +425 -0
- package/dist/index.umd.js +7 -0
- package/dist/style-partition-manager.d.ts +51 -0
- package/dist/utils.d.ts +2 -0
- package/package.json +41 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { IncrementalParser } from '@barocss/kit';
|
|
2
|
+
import { BrowserRuntime } from './browser-runtime';
|
|
3
|
+
/**
|
|
4
|
+
* Change detection system for DOM mutations
|
|
5
|
+
*
|
|
6
|
+
* ⚠️ BROWSER-ONLY: This class is designed for browser environments only.
|
|
7
|
+
* It uses MutationObserver and DOM APIs that are not available in Node.js.
|
|
8
|
+
*
|
|
9
|
+
* This class monitors DOM changes and automatically processes new CSS classes
|
|
10
|
+
* that are added to elements. It uses MutationObserver to detect:
|
|
11
|
+
* - Class attribute changes on existing elements
|
|
12
|
+
* - New elements being added to the DOM
|
|
13
|
+
* - Changes in child elements
|
|
14
|
+
*
|
|
15
|
+
* For server-side usage, use IncrementalParser directly with processClassesSync()
|
|
16
|
+
* or processClasses() methods.
|
|
17
|
+
*/
|
|
18
|
+
export declare class ChangeDetector {
|
|
19
|
+
/** MutationObserver instance for DOM change detection */
|
|
20
|
+
private observer;
|
|
21
|
+
/** Reference to IncrementalParser for class processing */
|
|
22
|
+
private incrementalParser;
|
|
23
|
+
/** Reference to BrowserRuntime for CSS injection (optional) */
|
|
24
|
+
private BrowserRuntime?;
|
|
25
|
+
/** Set of elements that have already been processed to avoid duplicates */
|
|
26
|
+
private processedElements;
|
|
27
|
+
/**
|
|
28
|
+
* Create a new ChangeDetector instance
|
|
29
|
+
*
|
|
30
|
+
* @param incrementalParser - IncrementalParser instance for class processing
|
|
31
|
+
* @param BrowserRuntime - Optional BrowserRuntime instance for CSS injection
|
|
32
|
+
*/
|
|
33
|
+
constructor(incrementalParser: IncrementalParser, BrowserRuntime?: BrowserRuntime);
|
|
34
|
+
/**
|
|
35
|
+
* Starts observing DOM changes for new CSS classes
|
|
36
|
+
*
|
|
37
|
+
* This method sets up a MutationObserver that monitors:
|
|
38
|
+
* - Attribute changes (specifically class attribute modifications)
|
|
39
|
+
* - Child list changes (new elements being added)
|
|
40
|
+
* - Subtree changes (changes in descendant elements)
|
|
41
|
+
*
|
|
42
|
+
* The observer automatically processes any new classes it discovers
|
|
43
|
+
* by adding them to the IncrementalParser's pending queue.
|
|
44
|
+
*
|
|
45
|
+
* @param root - The root element to observe (defaults to document.body)
|
|
46
|
+
* @param options - Configuration options including initial scan and onReady callback
|
|
47
|
+
* @returns The MutationObserver instance for external control
|
|
48
|
+
*/
|
|
49
|
+
observe(root?: HTMLElement, options?: {
|
|
50
|
+
scan?: boolean;
|
|
51
|
+
onReady?: () => void;
|
|
52
|
+
}): MutationObserver;
|
|
53
|
+
/**
|
|
54
|
+
* Scan existing classes in the DOM and process them
|
|
55
|
+
*/
|
|
56
|
+
private scanExistingClasses;
|
|
57
|
+
/**
|
|
58
|
+
* Processes an individual element and extracts new classes
|
|
59
|
+
*
|
|
60
|
+
* This method is called for each element discovered during DOM mutations.
|
|
61
|
+
* It:
|
|
62
|
+
* - Checks if the element has already been processed
|
|
63
|
+
* - Extracts all class names from the element's className
|
|
64
|
+
* - Filters out already processed classes
|
|
65
|
+
* - Adds new classes to the collection for batch processing
|
|
66
|
+
* - Marks the element as processed to avoid duplicates
|
|
67
|
+
*
|
|
68
|
+
* @param element - The HTML element to process
|
|
69
|
+
* @param newClasses - Set to collect newly discovered class names
|
|
70
|
+
*/
|
|
71
|
+
private processElement;
|
|
72
|
+
/**
|
|
73
|
+
* Stops observing DOM changes and cleans up resources
|
|
74
|
+
*
|
|
75
|
+
* This method disconnects the MutationObserver and clears the
|
|
76
|
+
* observer reference to prevent memory leaks and allow the
|
|
77
|
+
* ChangeDetector to be properly garbage collected.
|
|
78
|
+
*/
|
|
79
|
+
disconnect(): void;
|
|
80
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.es.js
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
import { parseResultCache as d, createContext as y, IncrementalParser as g, astCache as f } from "@barocss/kit";
|
|
2
|
+
class p {
|
|
3
|
+
constructor(t, e = 50, s = "barocss-style-partition-") {
|
|
4
|
+
this.partitions = [], this.categoryPartitions = /* @__PURE__ */ new Map(), this.partitionCounter = 0, this.maxRulesPerPartition = 50, this.classToPartitionMap = /* @__PURE__ */ new Map(), this.classToCategoryPartitionMap = /* @__PURE__ */ new Map(), this.styleIdPrefix = "barocss-style-partition-", this.insertionPoint = t, this.maxRulesPerPartition = e, this.styleIdPrefix = s, this.initializeDefaultPartition();
|
|
5
|
+
}
|
|
6
|
+
initializeDefaultPartition() {
|
|
7
|
+
this.createNewPartition();
|
|
8
|
+
}
|
|
9
|
+
createNewCategoryPartition(t) {
|
|
10
|
+
const e = {
|
|
11
|
+
id: this.styleIdPrefix + `-${t}`,
|
|
12
|
+
styles: [],
|
|
13
|
+
styleElement: document.createElement("style")
|
|
14
|
+
};
|
|
15
|
+
return e.styleElement.id = e.id, e.styleElement.setAttribute("data-barocss", "partition"), e.styleElement.setAttribute("data-category", t), this.insertionPoint.appendChild(e.styleElement), this.categoryPartitions.set(t, e), e;
|
|
16
|
+
}
|
|
17
|
+
createNewPartition() {
|
|
18
|
+
const t = {
|
|
19
|
+
id: this.styleIdPrefix + `-${this.partitionCounter++}`,
|
|
20
|
+
styles: [],
|
|
21
|
+
styleElement: document.createElement("style")
|
|
22
|
+
};
|
|
23
|
+
return this.partitions.push(t), t.styleElement.id = t.id, t.styleElement.setAttribute("data-barocss", "partition"), t.styleElement.setAttribute(
|
|
24
|
+
"data-partition-index",
|
|
25
|
+
this.partitionCounter.toString()
|
|
26
|
+
), this.insertionPoint.appendChild(t.styleElement), t;
|
|
27
|
+
}
|
|
28
|
+
hasRule(t) {
|
|
29
|
+
return this.classToPartitionMap.has(t);
|
|
30
|
+
}
|
|
31
|
+
hasCategoryRule(t, e) {
|
|
32
|
+
return this.classToCategoryPartitionMap.get(t) === e;
|
|
33
|
+
}
|
|
34
|
+
setRuleCache(t, e) {
|
|
35
|
+
this.classToPartitionMap.set(t, e);
|
|
36
|
+
}
|
|
37
|
+
setCategoryRuleCache(t, e) {
|
|
38
|
+
this.classToCategoryPartitionMap.set(t, e);
|
|
39
|
+
}
|
|
40
|
+
get currentPartition() {
|
|
41
|
+
return this.partitions[this.partitions.length - 1];
|
|
42
|
+
}
|
|
43
|
+
getCategoryPartition(t) {
|
|
44
|
+
return this.categoryPartitions.get(t);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Escape CSS rule text
|
|
48
|
+
* - Properly escape special characters
|
|
49
|
+
* - Prevent CSS syntax errors
|
|
50
|
+
*/
|
|
51
|
+
escapeCssRule(t) {
|
|
52
|
+
return t.replace(/\\\//g, "\\/");
|
|
53
|
+
}
|
|
54
|
+
addRule(t) {
|
|
55
|
+
if (this.hasRule(t))
|
|
56
|
+
return !1;
|
|
57
|
+
this.currentPartition.styles.length >= this.maxRulesPerPartition && this.createNewPartition();
|
|
58
|
+
const e = this.currentPartition, s = this.partitions.length - 1;
|
|
59
|
+
try {
|
|
60
|
+
const i = e.styleElement.sheet;
|
|
61
|
+
return i ? i.insertRule(this.escapeCssRule(t), i.cssRules.length) : e.styleElement.textContent += t + `
|
|
62
|
+
`, this.setRuleCache(t, s), e.styles.push(t), !0;
|
|
63
|
+
} catch (i) {
|
|
64
|
+
return console.warn(
|
|
65
|
+
`[StylePartitionManager] Failed to insert rule: ${t}`,
|
|
66
|
+
i
|
|
67
|
+
), !1;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
addCategoryRule(t, e) {
|
|
71
|
+
if (this.hasCategoryRule(t, e))
|
|
72
|
+
return !1;
|
|
73
|
+
let s = this.getCategoryPartition(e);
|
|
74
|
+
s || (s = this.createNewCategoryPartition(e));
|
|
75
|
+
try {
|
|
76
|
+
const i = s.styleElement.sheet;
|
|
77
|
+
i ? i.insertRule(this.escapeCssRule(t), i.cssRules.length) : s.styleElement.textContent += t + `
|
|
78
|
+
`;
|
|
79
|
+
} catch (i) {
|
|
80
|
+
return console.warn(
|
|
81
|
+
`[StylePartitionManager] Failed to insert rule in category: ${e} ${t}`,
|
|
82
|
+
i
|
|
83
|
+
), !1;
|
|
84
|
+
}
|
|
85
|
+
return this.setCategoryRuleCache(t, e), s.styles.push(t), !0;
|
|
86
|
+
}
|
|
87
|
+
addRootRules(t) {
|
|
88
|
+
let e = this.getCategoryPartition("root");
|
|
89
|
+
e || (e = this.createNewCategoryPartition("root"));
|
|
90
|
+
try {
|
|
91
|
+
const s = e.styleElement.sheet;
|
|
92
|
+
for (const i of t)
|
|
93
|
+
s ? s.insertRule(this.escapeCssRule(i), s.cssRules.length) : e.styleElement.textContent += i + `
|
|
94
|
+
`;
|
|
95
|
+
} catch (s) {
|
|
96
|
+
return console.warn(
|
|
97
|
+
`[StylePartitionManager] Failed to insert rule in category: root ${t.join(`
|
|
98
|
+
`)}`,
|
|
99
|
+
s
|
|
100
|
+
), { success: 0, failed: t.length };
|
|
101
|
+
}
|
|
102
|
+
return { success: t.length, failed: 0 };
|
|
103
|
+
}
|
|
104
|
+
addRules(t) {
|
|
105
|
+
let e = 0, s = 0;
|
|
106
|
+
for (const i of t) {
|
|
107
|
+
const r = d.get(i.cls)?.utility?.category;
|
|
108
|
+
if (r)
|
|
109
|
+
for (const o of i.cssList)
|
|
110
|
+
this.addCategoryRule(o, r);
|
|
111
|
+
else
|
|
112
|
+
for (const o of i.cssList)
|
|
113
|
+
this.addRule(o) ? e++ : s++;
|
|
114
|
+
}
|
|
115
|
+
return { success: e, failed: s };
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* 특정 규칙이 어느 파티션에 있는지 찾기
|
|
119
|
+
*/
|
|
120
|
+
findRulePartition(t) {
|
|
121
|
+
const e = this.classToPartitionMap.get(t);
|
|
122
|
+
if (e !== void 0 && this.partitions[e])
|
|
123
|
+
return this.partitions[e];
|
|
124
|
+
const s = this.classToCategoryPartitionMap.get(t);
|
|
125
|
+
return s !== void 0 && this.categoryPartitions.get(s) || null;
|
|
126
|
+
}
|
|
127
|
+
updateRuleContent(t, e) {
|
|
128
|
+
const s = this.getCategoryPartition(t);
|
|
129
|
+
if (s)
|
|
130
|
+
s.styleElement.textContent = e;
|
|
131
|
+
else {
|
|
132
|
+
const i = this.createNewCategoryPartition(t);
|
|
133
|
+
console.log(`[StylePartitionManager] Created new partition for category: ${t}`), i.styleElement.textContent = e;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* 모든 파티션 정리
|
|
138
|
+
*/
|
|
139
|
+
cleanup() {
|
|
140
|
+
this.partitions.forEach((t) => {
|
|
141
|
+
t.styleElement.parentNode && t.styleElement.parentNode.removeChild(t.styleElement);
|
|
142
|
+
}), this.categoryPartitions.forEach((t) => {
|
|
143
|
+
t.styleElement.parentNode && t.styleElement.parentNode.removeChild(t.styleElement);
|
|
144
|
+
}), this.partitions = [], this.partitionCounter = 0, this.classToPartitionMap.clear(), this.classToCategoryPartitionMap.clear();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function C(a) {
|
|
148
|
+
return a ? a instanceof SVGAnimatedString ? a.baseVal.toString() : a.toString() : "";
|
|
149
|
+
}
|
|
150
|
+
function c(a) {
|
|
151
|
+
return a ? C(a).split(/\s+/).filter(Boolean) : [];
|
|
152
|
+
}
|
|
153
|
+
class P {
|
|
154
|
+
/**
|
|
155
|
+
* Create a new ChangeDetector instance
|
|
156
|
+
*
|
|
157
|
+
* @param incrementalParser - IncrementalParser instance for class processing
|
|
158
|
+
* @param BrowserRuntime - Optional BrowserRuntime instance for CSS injection
|
|
159
|
+
*/
|
|
160
|
+
constructor(t, e) {
|
|
161
|
+
this.observer = null, this.processedElements = /* @__PURE__ */ new WeakSet(), this.incrementalParser = t, this.BrowserRuntime = e;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Starts observing DOM changes for new CSS classes
|
|
165
|
+
*
|
|
166
|
+
* This method sets up a MutationObserver that monitors:
|
|
167
|
+
* - Attribute changes (specifically class attribute modifications)
|
|
168
|
+
* - Child list changes (new elements being added)
|
|
169
|
+
* - Subtree changes (changes in descendant elements)
|
|
170
|
+
*
|
|
171
|
+
* The observer automatically processes any new classes it discovers
|
|
172
|
+
* by adding them to the IncrementalParser's pending queue.
|
|
173
|
+
*
|
|
174
|
+
* @param root - The root element to observe (defaults to document.body)
|
|
175
|
+
* @param options - Configuration options including initial scan and onReady callback
|
|
176
|
+
* @returns The MutationObserver instance for external control
|
|
177
|
+
*/
|
|
178
|
+
observe(t = document.body, e) {
|
|
179
|
+
return typeof window > "u" ? new MutationObserver(() => {
|
|
180
|
+
}) : (this.observer && this.observer.disconnect(), this.observer = new MutationObserver((s) => {
|
|
181
|
+
const i = /* @__PURE__ */ new Set();
|
|
182
|
+
if (s.forEach((n) => {
|
|
183
|
+
if (n.type === "attributes" && n.attributeName === "class") {
|
|
184
|
+
const r = n.target;
|
|
185
|
+
r.className && c(r.className).forEach((l) => {
|
|
186
|
+
this.incrementalParser.isProcessed(l) || i.add(l);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
n.type === "childList" && n.addedNodes.forEach((r) => {
|
|
190
|
+
r instanceof HTMLElement && (this.processElement(r, i), r.querySelectorAll("[class]").forEach((o) => {
|
|
191
|
+
this.processElement(o, i);
|
|
192
|
+
}));
|
|
193
|
+
});
|
|
194
|
+
}), i.size > 0) {
|
|
195
|
+
const n = Array.from(i), r = this.incrementalParser.processClasses(n);
|
|
196
|
+
this.BrowserRuntime?.applyParseResults(r);
|
|
197
|
+
}
|
|
198
|
+
}), this.observer.observe(t, {
|
|
199
|
+
attributes: !0,
|
|
200
|
+
subtree: !0,
|
|
201
|
+
attributeFilter: ["class"],
|
|
202
|
+
childList: !0
|
|
203
|
+
}), e?.scan && this.scanExistingClasses(t, e), this.observer);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Scan existing classes in the DOM and process them
|
|
207
|
+
*/
|
|
208
|
+
scanExistingClasses(t, e) {
|
|
209
|
+
const s = /* @__PURE__ */ new Set();
|
|
210
|
+
t.className && c(t.className).forEach((r) => {
|
|
211
|
+
this.incrementalParser.isProcessed(r) || s.add(r);
|
|
212
|
+
});
|
|
213
|
+
const i = t.querySelectorAll("[class]");
|
|
214
|
+
for (const n of i)
|
|
215
|
+
if (n.className) {
|
|
216
|
+
const r = c(n.className);
|
|
217
|
+
for (const o of r)
|
|
218
|
+
this.incrementalParser.isProcessed(o) || s.add(o);
|
|
219
|
+
}
|
|
220
|
+
if (s.size > 0) {
|
|
221
|
+
const n = Array.from(s), r = this.incrementalParser.processClasses(n), o = r.filter((h) => d.get(h.cls)?.utility?.category === "layout"), l = r.filter((h) => d.get(h.cls)?.utility?.category !== "layout");
|
|
222
|
+
this.BrowserRuntime?.applyParseResults(o), e?.onReady?.(), this.BrowserRuntime?.applyParseResults(l);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Processes an individual element and extracts new classes
|
|
227
|
+
*
|
|
228
|
+
* This method is called for each element discovered during DOM mutations.
|
|
229
|
+
* It:
|
|
230
|
+
* - Checks if the element has already been processed
|
|
231
|
+
* - Extracts all class names from the element's className
|
|
232
|
+
* - Filters out already processed classes
|
|
233
|
+
* - Adds new classes to the collection for batch processing
|
|
234
|
+
* - Marks the element as processed to avoid duplicates
|
|
235
|
+
*
|
|
236
|
+
* @param element - The HTML element to process
|
|
237
|
+
* @param newClasses - Set to collect newly discovered class names
|
|
238
|
+
*/
|
|
239
|
+
processElement(t, e) {
|
|
240
|
+
this.processedElements.has(t) || (t.className && c(t.className).forEach((i) => {
|
|
241
|
+
this.incrementalParser.isProcessed(i) || e.add(i);
|
|
242
|
+
}), this.processedElements.add(t));
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Stops observing DOM changes and cleans up resources
|
|
246
|
+
*
|
|
247
|
+
* This method disconnects the MutationObserver and clears the
|
|
248
|
+
* observer reference to prevent memory leaks and allow the
|
|
249
|
+
* ChangeDetector to be properly garbage collected.
|
|
250
|
+
*/
|
|
251
|
+
disconnect() {
|
|
252
|
+
this.observer && (this.observer.disconnect(), this.observer = null);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
class m {
|
|
256
|
+
constructor(t = {}) {
|
|
257
|
+
this.cache = /* @__PURE__ */ new Map(), this.rootCache = /* @__PURE__ */ new Set(), this.isDestroyed = !1;
|
|
258
|
+
const e = {};
|
|
259
|
+
this.options = {
|
|
260
|
+
config: t.config || e,
|
|
261
|
+
styleId: t.styleId || "barocss-runtime",
|
|
262
|
+
insertionPoint: t.insertionPoint || "head",
|
|
263
|
+
maxRulesPerPartition: t.maxRulesPerPartition || 50
|
|
264
|
+
}, this.context = y(this.options.config), this.incrementalParser = new g(this.context), this.changeDetector = new P(this.incrementalParser, this), this.stylePartitionManager = new p(this.getInsertionPoint(), this.options.maxRulesPerPartition, `${this.options.styleId}-partition`), this.init();
|
|
265
|
+
}
|
|
266
|
+
// Debugging and logging helpers
|
|
267
|
+
/**
|
|
268
|
+
* Add debug logs (by level)
|
|
269
|
+
*/
|
|
270
|
+
debugLog(t, e, s) {
|
|
271
|
+
(console[t] || console.log)(`[BrowserRuntime:${t.toUpperCase()}] ${e}`, s || "");
|
|
272
|
+
}
|
|
273
|
+
init() {
|
|
274
|
+
console.log("[BrowserRuntime] init"), this.injectPreflightCSS(), this.ensureCssVars();
|
|
275
|
+
}
|
|
276
|
+
injectPreflightCSS() {
|
|
277
|
+
if (this.options.config.preflight) {
|
|
278
|
+
const t = this.context.getPreflightCSS(this.options.config.preflight);
|
|
279
|
+
this.stylePartitionManager.updateRuleContent("preflight", t);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
ensureCssVars() {
|
|
283
|
+
if (this.isDestroyed) return;
|
|
284
|
+
const t = this.context.themeToCssVars ? this.context.themeToCssVars() : ":root { /* CSS Variables will be generated here */ }";
|
|
285
|
+
this.stylePartitionManager.updateRuleContent("css-vars", t);
|
|
286
|
+
}
|
|
287
|
+
getInsertionPoint() {
|
|
288
|
+
if (this.options.insertionPoint instanceof HTMLElement)
|
|
289
|
+
return this.options.insertionPoint;
|
|
290
|
+
switch (this.options.insertionPoint) {
|
|
291
|
+
case "body":
|
|
292
|
+
return document.body || document.head;
|
|
293
|
+
case "head":
|
|
294
|
+
default:
|
|
295
|
+
return document.head;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Dynamically add one or more class names and generate/insert CSS
|
|
300
|
+
*/
|
|
301
|
+
addClass(t) {
|
|
302
|
+
if (this.isDestroyed) return;
|
|
303
|
+
const e = this.normalizeClasses(t);
|
|
304
|
+
this.processClasses(e);
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Process classes
|
|
308
|
+
*/
|
|
309
|
+
processClasses(t) {
|
|
310
|
+
this.processClassesIncremental(t);
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Process classes using incremental parsing
|
|
314
|
+
*/
|
|
315
|
+
processClassesIncremental(t) {
|
|
316
|
+
const e = typeof window < "u", s = this.incrementalParser.processClasses(t);
|
|
317
|
+
console.log("[BrowserRuntime] results", s, ...t), this.applyParseResults(s, { isBrowser: e });
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Public method to apply parser results, update internal caches, and inject CSS
|
|
321
|
+
*/
|
|
322
|
+
applyParseResults(t, e) {
|
|
323
|
+
const s = [], i = [];
|
|
324
|
+
for (const n of t)
|
|
325
|
+
if (n.css && Array.isArray(n.cssList) && s.push(n), n.rootCss && Array.isArray(n.rootCssList))
|
|
326
|
+
for (const r of n.rootCssList)
|
|
327
|
+
this.rootCache.has(r) || (this.rootCache.add(r), i.push(r));
|
|
328
|
+
i.length > 0 && this.stylePartitionManager.addRootRules(i.filter(Boolean)), s.length > 0 && this.stylePartitionManager.addRules(s), this.debugLog("info", `Applied ${t.length} parser results`, {
|
|
329
|
+
cssRuleCount: s.length,
|
|
330
|
+
rootCssCount: i.length
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* MutationObserver instance method to automatically call addClass when class attributes change in DOM
|
|
335
|
+
*/
|
|
336
|
+
observe(t = document.body, e) {
|
|
337
|
+
return this.changeDetector.observe(t, e);
|
|
338
|
+
}
|
|
339
|
+
normalizeClasses(t) {
|
|
340
|
+
return Array.isArray(t) ? t.flatMap((e) => e.split(/\s+/)) : t.split(/\s+/);
|
|
341
|
+
}
|
|
342
|
+
has(t) {
|
|
343
|
+
return this.cache.has(t);
|
|
344
|
+
}
|
|
345
|
+
getCss(t) {
|
|
346
|
+
return this.cache.get(t)?.cssList.join(`
|
|
347
|
+
`);
|
|
348
|
+
}
|
|
349
|
+
getAllCss() {
|
|
350
|
+
return Array.from(this.cache.values()).flatMap((e) => e.cssList).join(`
|
|
351
|
+
`);
|
|
352
|
+
}
|
|
353
|
+
getClasses() {
|
|
354
|
+
return Array.from(this.cache.keys());
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Get comprehensive cache statistics
|
|
358
|
+
*/
|
|
359
|
+
getCacheStats() {
|
|
360
|
+
return {
|
|
361
|
+
runtime: {
|
|
362
|
+
cachedClasses: this.cache.size,
|
|
363
|
+
rootCacheSize: this.rootCache.size
|
|
364
|
+
},
|
|
365
|
+
ast: f.getStats(),
|
|
366
|
+
incremental: this.incrementalParser.getStats()
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Clear all caches (useful for debugging or memory management)
|
|
371
|
+
*/
|
|
372
|
+
clearCaches() {
|
|
373
|
+
this.cache.clear(), this.rootCache.clear(), f.clear(), this.incrementalParser.clearProcessed(), this.stylePartitionManager.cleanup();
|
|
374
|
+
}
|
|
375
|
+
reset() {
|
|
376
|
+
this.cache.clear(), this.rootCache.clear(), this.stylePartitionManager.cleanup();
|
|
377
|
+
}
|
|
378
|
+
updateConfig(t) {
|
|
379
|
+
this.options.config = t, this.context = y(t);
|
|
380
|
+
const e = Array.from(this.cache.keys());
|
|
381
|
+
this.reset(), e.length > 0 && this.addClass(e);
|
|
382
|
+
}
|
|
383
|
+
removeClass(t) {
|
|
384
|
+
const e = this.normalizeClasses(t);
|
|
385
|
+
for (const s of e)
|
|
386
|
+
this.cache.delete(s);
|
|
387
|
+
}
|
|
388
|
+
destroy() {
|
|
389
|
+
this.stylePartitionManager.cleanup(), this.cache.clear(), this.rootCache.clear(), this.isDestroyed = !0;
|
|
390
|
+
}
|
|
391
|
+
getStats() {
|
|
392
|
+
return {
|
|
393
|
+
cachedClasses: this.cache.size,
|
|
394
|
+
styleElementId: this.options.styleId,
|
|
395
|
+
isDestroyed: this.isDestroyed,
|
|
396
|
+
config: this.options.config,
|
|
397
|
+
cacheStats: this.getCacheStats()
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
let u = null;
|
|
402
|
+
function R(a) {
|
|
403
|
+
return u || (u = new m(a)), u;
|
|
404
|
+
}
|
|
405
|
+
function b({ loadingClassName: a = "baro-boot", ...t } = {}) {
|
|
406
|
+
const e = `${a}-doing`, s = `${a}-done`;
|
|
407
|
+
try {
|
|
408
|
+
document.body.classList.add(e), R(t).observe(document.body, { scan: !0, onReady: () => {
|
|
409
|
+
document.body.classList.remove(e), document.body.classList.add(s);
|
|
410
|
+
} });
|
|
411
|
+
} catch (i) {
|
|
412
|
+
console.error("BaroCSS boot failed:", i);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
const w = b;
|
|
416
|
+
export {
|
|
417
|
+
m as BrowserRuntime,
|
|
418
|
+
P as ChangeDetector,
|
|
419
|
+
p as StylePartitionManager,
|
|
420
|
+
b as baroBoot,
|
|
421
|
+
w as baroStart,
|
|
422
|
+
R as getRuntime,
|
|
423
|
+
C as normalizeClassName,
|
|
424
|
+
c as normalizeClassNameList
|
|
425
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
(function(o,l){typeof exports=="object"&&typeof module<"u"?l(exports,require("@barocss/kit")):typeof define=="function"&&define.amd?define(["exports","@barocss/kit"],l):(o=typeof globalThis<"u"?globalThis:o||self,l(o.BaroCSSBrowser={},o.BaroCSSKit))})(this,(function(o,l){"use strict";class y{constructor(t,e=50,s="barocss-style-partition-"){this.partitions=[],this.categoryPartitions=new Map,this.partitionCounter=0,this.maxRulesPerPartition=50,this.classToPartitionMap=new Map,this.classToCategoryPartitionMap=new Map,this.styleIdPrefix="barocss-style-partition-",this.insertionPoint=t,this.maxRulesPerPartition=e,this.styleIdPrefix=s,this.initializeDefaultPartition()}initializeDefaultPartition(){this.createNewPartition()}createNewCategoryPartition(t){const e={id:this.styleIdPrefix+`-${t}`,styles:[],styleElement:document.createElement("style")};return e.styleElement.id=e.id,e.styleElement.setAttribute("data-barocss","partition"),e.styleElement.setAttribute("data-category",t),this.insertionPoint.appendChild(e.styleElement),this.categoryPartitions.set(t,e),e}createNewPartition(){const t={id:this.styleIdPrefix+`-${this.partitionCounter++}`,styles:[],styleElement:document.createElement("style")};return this.partitions.push(t),t.styleElement.id=t.id,t.styleElement.setAttribute("data-barocss","partition"),t.styleElement.setAttribute("data-partition-index",this.partitionCounter.toString()),this.insertionPoint.appendChild(t.styleElement),t}hasRule(t){return this.classToPartitionMap.has(t)}hasCategoryRule(t,e){return this.classToCategoryPartitionMap.get(t)===e}setRuleCache(t,e){this.classToPartitionMap.set(t,e)}setCategoryRuleCache(t,e){this.classToCategoryPartitionMap.set(t,e)}get currentPartition(){return this.partitions[this.partitions.length-1]}getCategoryPartition(t){return this.categoryPartitions.get(t)}escapeCssRule(t){return t.replace(/\\\//g,"\\/")}addRule(t){if(this.hasRule(t))return!1;this.currentPartition.styles.length>=this.maxRulesPerPartition&&this.createNewPartition();const e=this.currentPartition,s=this.partitions.length-1;try{const i=e.styleElement.sheet;return i?i.insertRule(this.escapeCssRule(t),i.cssRules.length):e.styleElement.textContent+=t+`
|
|
2
|
+
`,this.setRuleCache(t,s),e.styles.push(t),!0}catch(i){return console.warn(`[StylePartitionManager] Failed to insert rule: ${t}`,i),!1}}addCategoryRule(t,e){if(this.hasCategoryRule(t,e))return!1;let s=this.getCategoryPartition(e);s||(s=this.createNewCategoryPartition(e));try{const i=s.styleElement.sheet;i?i.insertRule(this.escapeCssRule(t),i.cssRules.length):s.styleElement.textContent+=t+`
|
|
3
|
+
`}catch(i){return console.warn(`[StylePartitionManager] Failed to insert rule in category: ${e} ${t}`,i),!1}return this.setCategoryRuleCache(t,e),s.styles.push(t),!0}addRootRules(t){let e=this.getCategoryPartition("root");e||(e=this.createNewCategoryPartition("root"));try{const s=e.styleElement.sheet;for(const i of t)s?s.insertRule(this.escapeCssRule(i),s.cssRules.length):e.styleElement.textContent+=i+`
|
|
4
|
+
`}catch(s){return console.warn(`[StylePartitionManager] Failed to insert rule in category: root ${t.join(`
|
|
5
|
+
`)}`,s),{success:0,failed:t.length}}return{success:t.length,failed:0}}addRules(t){let e=0,s=0;for(const i of t){const r=l.parseResultCache.get(i.cls)?.utility?.category;if(r)for(const c of i.cssList)this.addCategoryRule(c,r);else for(const c of i.cssList)this.addRule(c)?e++:s++}return{success:e,failed:s}}findRulePartition(t){const e=this.classToPartitionMap.get(t);if(e!==void 0&&this.partitions[e])return this.partitions[e];const s=this.classToCategoryPartitionMap.get(t);return s!==void 0&&this.categoryPartitions.get(s)||null}updateRuleContent(t,e){const s=this.getCategoryPartition(t);if(s)s.styleElement.textContent=e;else{const i=this.createNewCategoryPartition(t);console.log(`[StylePartitionManager] Created new partition for category: ${t}`),i.styleElement.textContent=e}}cleanup(){this.partitions.forEach(t=>{t.styleElement.parentNode&&t.styleElement.parentNode.removeChild(t.styleElement)}),this.categoryPartitions.forEach(t=>{t.styleElement.parentNode&&t.styleElement.parentNode.removeChild(t.styleElement)}),this.partitions=[],this.partitionCounter=0,this.classToPartitionMap.clear(),this.classToCategoryPartitionMap.clear()}}function g(a){return a?a instanceof SVGAnimatedString?a.baseVal.toString():a.toString():""}function h(a){return a?g(a).split(/\s+/).filter(Boolean):[]}class p{constructor(t,e){this.observer=null,this.processedElements=new WeakSet,this.incrementalParser=t,this.BrowserRuntime=e}observe(t=document.body,e){return typeof window>"u"?new MutationObserver(()=>{}):(this.observer&&this.observer.disconnect(),this.observer=new MutationObserver(s=>{const i=new Set;if(s.forEach(n=>{if(n.type==="attributes"&&n.attributeName==="class"){const r=n.target;r.className&&h(r.className).forEach(u=>{this.incrementalParser.isProcessed(u)||i.add(u)})}n.type==="childList"&&n.addedNodes.forEach(r=>{r instanceof HTMLElement&&(this.processElement(r,i),r.querySelectorAll("[class]").forEach(c=>{this.processElement(c,i)}))})}),i.size>0){const n=Array.from(i),r=this.incrementalParser.processClasses(n);this.BrowserRuntime?.applyParseResults(r)}}),this.observer.observe(t,{attributes:!0,subtree:!0,attributeFilter:["class"],childList:!0}),e?.scan&&this.scanExistingClasses(t,e),this.observer)}scanExistingClasses(t,e){const s=new Set;t.className&&h(t.className).forEach(r=>{this.incrementalParser.isProcessed(r)||s.add(r)});const i=t.querySelectorAll("[class]");for(const n of i)if(n.className){const r=h(n.className);for(const c of r)this.incrementalParser.isProcessed(c)||s.add(c)}if(s.size>0){const n=Array.from(s),r=this.incrementalParser.processClasses(n),c=r.filter(f=>l.parseResultCache.get(f.cls)?.utility?.category==="layout"),u=r.filter(f=>l.parseResultCache.get(f.cls)?.utility?.category!=="layout");this.BrowserRuntime?.applyParseResults(c),e?.onReady?.(),this.BrowserRuntime?.applyParseResults(u)}}processElement(t,e){this.processedElements.has(t)||(t.className&&h(t.className).forEach(i=>{this.incrementalParser.isProcessed(i)||e.add(i)}),this.processedElements.add(t))}disconnect(){this.observer&&(this.observer.disconnect(),this.observer=null)}}class C{constructor(t={}){this.cache=new Map,this.rootCache=new Set,this.isDestroyed=!1;const e={};this.options={config:t.config||e,styleId:t.styleId||"barocss-runtime",insertionPoint:t.insertionPoint||"head",maxRulesPerPartition:t.maxRulesPerPartition||50},this.context=l.createContext(this.options.config),this.incrementalParser=new l.IncrementalParser(this.context),this.changeDetector=new p(this.incrementalParser,this),this.stylePartitionManager=new y(this.getInsertionPoint(),this.options.maxRulesPerPartition,`${this.options.styleId}-partition`),this.init()}debugLog(t,e,s){(console[t]||console.log)(`[BrowserRuntime:${t.toUpperCase()}] ${e}`,s||"")}init(){console.log("[BrowserRuntime] init"),this.injectPreflightCSS(),this.ensureCssVars()}injectPreflightCSS(){if(this.options.config.preflight){const t=this.context.getPreflightCSS(this.options.config.preflight);this.stylePartitionManager.updateRuleContent("preflight",t)}}ensureCssVars(){if(this.isDestroyed)return;const t=this.context.themeToCssVars?this.context.themeToCssVars():":root { /* CSS Variables will be generated here */ }";this.stylePartitionManager.updateRuleContent("css-vars",t)}getInsertionPoint(){if(this.options.insertionPoint instanceof HTMLElement)return this.options.insertionPoint;switch(this.options.insertionPoint){case"body":return document.body||document.head;case"head":default:return document.head}}addClass(t){if(this.isDestroyed)return;const e=this.normalizeClasses(t);this.processClasses(e)}processClasses(t){this.processClassesIncremental(t)}processClassesIncremental(t){const e=typeof window<"u",s=this.incrementalParser.processClasses(t);console.log("[BrowserRuntime] results",s,...t),this.applyParseResults(s,{isBrowser:e})}applyParseResults(t,e){const s=[],i=[];for(const n of t)if(n.css&&Array.isArray(n.cssList)&&s.push(n),n.rootCss&&Array.isArray(n.rootCssList))for(const r of n.rootCssList)this.rootCache.has(r)||(this.rootCache.add(r),i.push(r));i.length>0&&this.stylePartitionManager.addRootRules(i.filter(Boolean)),s.length>0&&this.stylePartitionManager.addRules(s),this.debugLog("info",`Applied ${t.length} parser results`,{cssRuleCount:s.length,rootCssCount:i.length})}observe(t=document.body,e){return this.changeDetector.observe(t,e)}normalizeClasses(t){return Array.isArray(t)?t.flatMap(e=>e.split(/\s+/)):t.split(/\s+/)}has(t){return this.cache.has(t)}getCss(t){return this.cache.get(t)?.cssList.join(`
|
|
6
|
+
`)}getAllCss(){return Array.from(this.cache.values()).flatMap(e=>e.cssList).join(`
|
|
7
|
+
`)}getClasses(){return Array.from(this.cache.keys())}getCacheStats(){return{runtime:{cachedClasses:this.cache.size,rootCacheSize:this.rootCache.size},ast:l.astCache.getStats(),incremental:this.incrementalParser.getStats()}}clearCaches(){this.cache.clear(),this.rootCache.clear(),l.astCache.clear(),this.incrementalParser.clearProcessed(),this.stylePartitionManager.cleanup()}reset(){this.cache.clear(),this.rootCache.clear(),this.stylePartitionManager.cleanup()}updateConfig(t){this.options.config=t,this.context=l.createContext(t);const e=Array.from(this.cache.keys());this.reset(),e.length>0&&this.addClass(e)}removeClass(t){const e=this.normalizeClasses(t);for(const s of e)this.cache.delete(s)}destroy(){this.stylePartitionManager.cleanup(),this.cache.clear(),this.rootCache.clear(),this.isDestroyed=!0}getStats(){return{cachedClasses:this.cache.size,styleElementId:this.options.styleId,isDestroyed:this.isDestroyed,config:this.options.config,cacheStats:this.getCacheStats()}}}let d=null;function m(a){return d||(d=new C(a)),d}function P({loadingClassName:a="baro-boot",...t}={}){const e=`${a}-doing`,s=`${a}-done`;try{document.body.classList.add(e),m(t).observe(document.body,{scan:!0,onReady:()=>{document.body.classList.remove(e),document.body.classList.add(s)}})}catch(i){console.error("BaroCSS boot failed:",i)}}const R=P;o.BrowserRuntime=C,o.ChangeDetector=p,o.StylePartitionManager=y,o.baroBoot=P,o.baroStart=R,o.getRuntime=m,o.normalizeClassName=g,o.normalizeClassNameList=h,Object.defineProperty(o,Symbol.toStringTag,{value:"Module"})}));
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { GenerateCssRulesResult } from '@barocss/kit';
|
|
2
|
+
export interface StylePartition {
|
|
3
|
+
id: string;
|
|
4
|
+
styles: string[];
|
|
5
|
+
styleElement: HTMLStyleElement;
|
|
6
|
+
}
|
|
7
|
+
export declare class StylePartitionManager {
|
|
8
|
+
private partitions;
|
|
9
|
+
private categoryPartitions;
|
|
10
|
+
private partitionCounter;
|
|
11
|
+
private maxRulesPerPartition;
|
|
12
|
+
private insertionPoint;
|
|
13
|
+
private classToPartitionMap;
|
|
14
|
+
private classToCategoryPartitionMap;
|
|
15
|
+
private styleIdPrefix;
|
|
16
|
+
constructor(insertionPoint: HTMLElement, maxRulesPerPartition?: number, styleIdPrefix?: string);
|
|
17
|
+
private initializeDefaultPartition;
|
|
18
|
+
private createNewCategoryPartition;
|
|
19
|
+
private createNewPartition;
|
|
20
|
+
hasRule(rule: string): boolean;
|
|
21
|
+
hasCategoryRule(rule: string, category: string): boolean;
|
|
22
|
+
setRuleCache(rule: string, partitionIndex: number): void;
|
|
23
|
+
setCategoryRuleCache(rule: string, category: string): void;
|
|
24
|
+
get currentPartition(): StylePartition;
|
|
25
|
+
getCategoryPartition(category: string): StylePartition | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Escape CSS rule text
|
|
28
|
+
* - Properly escape special characters
|
|
29
|
+
* - Prevent CSS syntax errors
|
|
30
|
+
*/
|
|
31
|
+
private escapeCssRule;
|
|
32
|
+
addRule(rule: string): boolean;
|
|
33
|
+
addCategoryRule(rule: string, category: string): boolean;
|
|
34
|
+
addRootRules(rules: string[]): {
|
|
35
|
+
success: number;
|
|
36
|
+
failed: number;
|
|
37
|
+
};
|
|
38
|
+
addRules(rules: GenerateCssRulesResult[]): {
|
|
39
|
+
success: number;
|
|
40
|
+
failed: number;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* 특정 규칙이 어느 파티션에 있는지 찾기
|
|
44
|
+
*/
|
|
45
|
+
findRulePartition(rule: string): StylePartition | null;
|
|
46
|
+
updateRuleContent(category: string, ruleContent: string): void;
|
|
47
|
+
/**
|
|
48
|
+
* 모든 파티션 정리
|
|
49
|
+
*/
|
|
50
|
+
cleanup(): void;
|
|
51
|
+
}
|
package/dist/utils.d.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@barocss/browser",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.umd.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.es.js",
|
|
11
|
+
"require": "./dist/index.umd.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist/**/*",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@barocss/kit": "0.0.2"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"jsdom": "^26.1.0",
|
|
27
|
+
"vite": "^7.1.3",
|
|
28
|
+
"vite-plugin-dts": "^4.5.4",
|
|
29
|
+
"vitest": "^3.2.4"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "vite build",
|
|
33
|
+
"build:cdn": "vite build --config vite.cdn.config.ts",
|
|
34
|
+
"build:library": "pnpm run build && pnpm run build:cdn",
|
|
35
|
+
"test:watch": "vitest",
|
|
36
|
+
"//test": "vitest run",
|
|
37
|
+
"type-check": "tsc --noEmit",
|
|
38
|
+
"lint": "eslint src/**/*.ts"
|
|
39
|
+
},
|
|
40
|
+
"module": "./dist/index.es.js"
|
|
41
|
+
}
|