@master/css-runtime 2.0.0-rc.63 → 2.0.0-rc.65
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/core.d.ts +3 -3
- package/dist/global.min.js +1 -1
- package/dist/index.cjs +103 -132
- package/dist/index.development.cjs +131 -135
- package/dist/index.development.mjs +131 -135
- package/dist/index.mjs +103 -132
- package/package.json +1 -1
|
@@ -57,10 +57,36 @@ function registerClassCountDebugger() {
|
|
|
57
57
|
});
|
|
58
58
|
if (errored) {
|
|
59
59
|
log.debug('Records:', records);
|
|
60
|
+
log.debug('Serialized Records:', serializeMutations(records));
|
|
60
61
|
log.debug('Counts:', classCounts);
|
|
61
62
|
}
|
|
62
63
|
});
|
|
63
64
|
}
|
|
65
|
+
function serializeMutations(mutations) {
|
|
66
|
+
return mutations.map((m)=>{
|
|
67
|
+
const target = m.target;
|
|
68
|
+
return {
|
|
69
|
+
type: m.type,
|
|
70
|
+
attributeName: m.attributeName,
|
|
71
|
+
oldValue: m.oldValue,
|
|
72
|
+
target: {
|
|
73
|
+
tagName: target.tagName,
|
|
74
|
+
id: target.id,
|
|
75
|
+
classList: Array.from(target.classList)
|
|
76
|
+
},
|
|
77
|
+
added: Array.from(m.addedNodes).filter((n)=>n.nodeType === 1).map((el)=>({
|
|
78
|
+
tagName: el.tagName,
|
|
79
|
+
id: el.id,
|
|
80
|
+
classList: Array.from(el.classList)
|
|
81
|
+
})),
|
|
82
|
+
removed: Array.from(m.removedNodes).filter((n)=>n.nodeType === 1).map((el)=>({
|
|
83
|
+
tagName: el.tagName,
|
|
84
|
+
id: el.id,
|
|
85
|
+
classList: Array.from(el.classList)
|
|
86
|
+
}))
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
}
|
|
64
90
|
|
|
65
91
|
function registerLifecycleDebugger() {
|
|
66
92
|
const hook = globalThis.__MASTER_CSS_DEVTOOLS_HOOK__;
|
|
@@ -134,12 +160,21 @@ class RuntimeLayer extends Layer {
|
|
|
134
160
|
const insertedIndex = super.insert(rule, index);
|
|
135
161
|
if (insertedIndex === undefined || !this.native) return;
|
|
136
162
|
const insertRuleSafely = (text, position)=>{
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
return this.native.cssRules.item(
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
|
|
163
|
+
// Checks if the rule is inserted in a native CSS rule with this.attach()
|
|
164
|
+
if (this.rules.length === 1) {
|
|
165
|
+
return this.native.cssRules.item(position);
|
|
166
|
+
} else {
|
|
167
|
+
try {
|
|
168
|
+
const insertedIndex = this.native.insertRule(text, position);
|
|
169
|
+
return this.native.cssRules.item(insertedIndex);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error(error, rule);
|
|
172
|
+
/**
|
|
173
|
+
* If the rule is invalid, remove it from the rules array.
|
|
174
|
+
* It's important to note that the rule may break the entire CSS runtime,
|
|
175
|
+
*/ this.rules.splice(insertedIndex, 1);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
143
178
|
}
|
|
144
179
|
};
|
|
145
180
|
if ('nodes' in rule) {
|
|
@@ -149,7 +184,7 @@ class RuntimeLayer extends Layer {
|
|
|
149
184
|
if (node.native) currentIndex++;
|
|
150
185
|
});
|
|
151
186
|
} else {
|
|
152
|
-
rule.native = insertRuleSafely(rule.text,
|
|
187
|
+
rule.native = insertRuleSafely(rule.text, insertedIndex);
|
|
153
188
|
}
|
|
154
189
|
return insertedIndex;
|
|
155
190
|
}
|
|
@@ -204,20 +239,19 @@ class CSSRuntime extends MasterCSS {
|
|
|
204
239
|
customConfig;
|
|
205
240
|
static instances = new WeakMap();
|
|
206
241
|
host;
|
|
207
|
-
observing;
|
|
208
|
-
progressive;
|
|
209
242
|
container;
|
|
210
|
-
observer;
|
|
211
243
|
baseLayer;
|
|
212
244
|
themeLayer;
|
|
213
245
|
presetLayer;
|
|
214
246
|
componentsLayer;
|
|
215
247
|
generalLayer;
|
|
216
248
|
classCounts;
|
|
249
|
+
observer;
|
|
250
|
+
progressive;
|
|
251
|
+
observing;
|
|
217
252
|
constructor(root = document, customConfig = config){
|
|
218
|
-
super(customConfig), this.root = root, this.customConfig = customConfig, this.
|
|
219
|
-
|
|
220
|
-
if (rootConstructorName === 'HTMLDocument' || rootConstructorName === 'Document') {
|
|
253
|
+
super(customConfig), this.root = root, this.customConfig = customConfig, this.baseLayer = new RuntimeSyntaxLayer('base', this), this.themeLayer = new RuntimeLayer('theme', this), this.presetLayer = new RuntimeSyntaxLayer('preset', this), this.componentsLayer = new RuntimeSyntaxLayer('components', this), this.generalLayer = new RuntimeSyntaxLayer('general', this), this.classCounts = new Map(), this.progressive = false, this.observing = false;
|
|
254
|
+
if (this.root instanceof Document || this.root instanceof HTMLDocument) {
|
|
221
255
|
this.root.defaultView.globalThis.cssRuntime = this;
|
|
222
256
|
this.container = this.root.head;
|
|
223
257
|
this.host = this.root.documentElement;
|
|
@@ -236,39 +270,46 @@ class CSSRuntime extends MasterCSS {
|
|
|
236
270
|
* @returns this
|
|
237
271
|
*/ observe() {
|
|
238
272
|
if (this.observing) return this;
|
|
239
|
-
//
|
|
240
|
-
if (this.root.styleSheets)
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
273
|
+
// Detect prerendered stylesheet
|
|
274
|
+
if (this.root.styleSheets) {
|
|
275
|
+
for (const sheet of this.root.styleSheets){
|
|
276
|
+
const { ownerNode } = sheet;
|
|
277
|
+
if (ownerNode instanceof HTMLStyleElement && ownerNode.id === 'master') {
|
|
278
|
+
this.style = ownerNode;
|
|
279
|
+
if (this.style.sheet?.cssRules.length) {
|
|
280
|
+
this.progressive = true;
|
|
281
|
+
}
|
|
282
|
+
break;
|
|
247
283
|
}
|
|
248
|
-
break;
|
|
249
284
|
}
|
|
250
285
|
}
|
|
251
|
-
//
|
|
286
|
+
// Prepare snapshot map
|
|
287
|
+
const elementClasses = new WeakMap();
|
|
288
|
+
// Initial scan and populate counts + snapshot
|
|
252
289
|
const connectedNames = new Set();
|
|
253
290
|
const increaseClassCount = (className)=>{
|
|
254
291
|
const count = this.classCounts.get(className) || 0;
|
|
255
|
-
if (!count)
|
|
256
|
-
connectedNames.add(className);
|
|
257
|
-
}
|
|
292
|
+
if (!count) connectedNames.add(className);
|
|
258
293
|
this.classCounts.set(className, count + 1);
|
|
259
294
|
};
|
|
260
|
-
this.
|
|
261
|
-
|
|
262
|
-
|
|
295
|
+
const rootEl = this.root instanceof Document || this.root instanceof HTMLDocument ? this.root : this.container;
|
|
296
|
+
const elementsWithClass = rootEl.querySelectorAll('[class]');
|
|
297
|
+
elementsWithClass.forEach((el)=>{
|
|
298
|
+
const clsList = el.classList;
|
|
299
|
+
if (clsList) {
|
|
300
|
+
el.classList.forEach(increaseClassCount);
|
|
301
|
+
}
|
|
302
|
+
elementClasses.set(el, new Set(clsList));
|
|
303
|
+
});
|
|
304
|
+
// Hydration or style creation
|
|
263
305
|
if (this.progressive) {
|
|
264
306
|
const hydrateResult = this.hydrate(this.style.sheet.cssRules);
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
console.debug(`The class \`${eachConnectedName}\` was added via script before calling hydrate, or the corresponding CSS rule were not properly pre-rendered.`);
|
|
307
|
+
for (const cls of connectedNames){
|
|
308
|
+
if (!hydrateResult.allSyntaxRules.find((r)=>(r.fixedClass || r.name) === cls)) {
|
|
309
|
+
this.add(cls);
|
|
310
|
+
{
|
|
311
|
+
console.debug(`Missing prerendered rule for class \`${cls}\``);
|
|
312
|
+
}
|
|
272
313
|
}
|
|
273
314
|
}
|
|
274
315
|
} else {
|
|
@@ -277,125 +318,81 @@ class CSSRuntime extends MasterCSS {
|
|
|
277
318
|
this.container.append(this.style);
|
|
278
319
|
this.style.sheet.insertRule(this.layerStatementRule.text);
|
|
279
320
|
this.layerStatementRule.native = this.style.sheet.cssRules.item(0);
|
|
280
|
-
|
|
281
|
-
this.add(eachConnectedName);
|
|
282
|
-
}
|
|
321
|
+
connectedNames.forEach((cls)=>this.add(cls));
|
|
283
322
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const
|
|
287
|
-
const
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
323
|
+
this.observer = new MutationObserver((records)=>{
|
|
324
|
+
const deltaCounts = new Map();
|
|
325
|
+
const nodeMap = new Map();
|
|
326
|
+
const attrRecords = new Set();
|
|
327
|
+
const visited = new WeakSet();
|
|
328
|
+
const updateDelta = (cls, delta)=>deltaCounts.set(cls, (deltaCounts.get(cls) || 0) + delta);
|
|
329
|
+
const diffAndSnapshot = (el)=>{
|
|
330
|
+
const prev = new Set(elementClasses.get(el) || []);
|
|
331
|
+
const next = new Set(el.classList);
|
|
332
|
+
for (const c of next)if (!prev.has(c)) updateDelta(c, 1);
|
|
333
|
+
for (const c of prev)if (!next.has(c)) updateDelta(c, -1);
|
|
334
|
+
elementClasses.set(el, next);
|
|
293
335
|
};
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
const status = map.get(target);
|
|
298
|
-
if (status) {
|
|
299
|
-
status.change += adding ? 1 : -1;
|
|
300
|
-
status.mutationRecord = mutationRecord;
|
|
301
|
-
} else {
|
|
302
|
-
map.set(target, {
|
|
303
|
-
change: adding ? 1 : -1,
|
|
304
|
-
mutationRecord
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
for (const child of target.children){
|
|
308
|
-
if ('classList' in child) {
|
|
309
|
-
recordStatus(child, mutationRecord, map, adding);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
336
|
+
const removeSnapshot = (el)=>{
|
|
337
|
+
for (const c of elementClasses.get(el) || [])updateDelta(c, -1);
|
|
338
|
+
elementClasses.delete(el);
|
|
312
339
|
};
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
}
|
|
320
|
-
break;
|
|
321
|
-
case 'childList':
|
|
322
|
-
const targetStatusMap = target.isConnected ? connectedStatusMap : disconnectedStatusMap;
|
|
323
|
-
mutationRecord.addedNodes.forEach((node)=>'classList' in node && recordStatus(node, mutationRecord, targetStatusMap, true));
|
|
324
|
-
mutationRecord.removedNodes.forEach((node)=>'classList' in node && recordStatus(node, mutationRecord, targetStatusMap, false));
|
|
325
|
-
break;
|
|
340
|
+
for (const record of records){
|
|
341
|
+
if (record.type === 'childList') {
|
|
342
|
+
for (const node of record.addedNodes)if (node instanceof Element && node.isConnected) nodeMap.set(node, (nodeMap.get(node) || 0) + 1);
|
|
343
|
+
for (const node of record.removedNodes)if (node instanceof Element && !node.isConnected) nodeMap.set(node, (nodeMap.get(node) || 0) - 1);
|
|
344
|
+
} else if (record.type === 'attributes' && record.attributeName === 'class') {
|
|
345
|
+
attrRecords.add(record.target);
|
|
326
346
|
}
|
|
327
|
-
}
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
updateClassCount(target.classList, adding);
|
|
335
|
-
} else {
|
|
336
|
-
if (firstAttrMutationRecord) {
|
|
337
|
-
updateClassCount(firstAttrMutationRecord.oldValue ? firstAttrMutationRecord.oldValue.split(/\s+/) : [], adding);
|
|
338
|
-
} else {
|
|
339
|
-
updateClassCount(target.classList, adding);
|
|
347
|
+
}
|
|
348
|
+
const traverseIncludingSelf = (el, fn, visited)=>{
|
|
349
|
+
if (!visited.has(el)) {
|
|
350
|
+
visited.add(el);
|
|
351
|
+
fn(el);
|
|
352
|
+
for (const child of el.children){
|
|
353
|
+
traverseIncludingSelf(child, fn, visited);
|
|
340
354
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
updateTarget(disconnectedTarget, disconnectedTargetStatus.change > 0);
|
|
344
|
-
}
|
|
345
|
-
});
|
|
355
|
+
} else {
|
|
356
|
+
nodeMap.delete(el);
|
|
346
357
|
}
|
|
347
358
|
};
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
const addedClasses = [];
|
|
354
|
-
newClassList.forEach((c)=>{
|
|
355
|
-
if (!oldClassList.includes(c)) addedClasses.push(c);
|
|
356
|
-
});
|
|
357
|
-
const removedClasses = oldClassList.filter((c)=>!newClassList.contains(c));
|
|
358
|
-
if (addedClasses.length) {
|
|
359
|
-
updateClassCount(addedClasses, true);
|
|
359
|
+
for (const [node, count] of nodeMap){
|
|
360
|
+
if (count > 0) {
|
|
361
|
+
traverseIncludingSelf(node, diffAndSnapshot, visited);
|
|
362
|
+
} else if (count < 0 && !node.isConnected) {
|
|
363
|
+
traverseIncludingSelf(node, removeSnapshot, visited);
|
|
360
364
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
this.classCounts.set(className, newCount);
|
|
372
|
-
if (currentCount === 0) {
|
|
373
|
-
this.add(className);
|
|
374
|
-
}
|
|
365
|
+
}
|
|
366
|
+
for (const el of attrRecords){
|
|
367
|
+
if (!visited.has(el)) diffAndSnapshot(el);
|
|
368
|
+
}
|
|
369
|
+
for (const [cls, change] of deltaCounts){
|
|
370
|
+
const current = this.classCounts.get(cls) || 0;
|
|
371
|
+
const next = current + change;
|
|
372
|
+
if (next > 0) {
|
|
373
|
+
this.classCounts.set(cls, next);
|
|
374
|
+
if (current === 0) this.add(cls);
|
|
375
375
|
} else {
|
|
376
|
-
this.classCounts.delete(
|
|
377
|
-
this.remove(
|
|
376
|
+
this.classCounts.delete(cls);
|
|
377
|
+
this.remove(cls);
|
|
378
378
|
}
|
|
379
|
-
}
|
|
379
|
+
}
|
|
380
380
|
globalThis.__MASTER_CSS_DEVTOOLS_HOOK__?.emit('runtime:mutated', {
|
|
381
|
-
records
|
|
382
|
-
classCounts:
|
|
381
|
+
records,
|
|
382
|
+
classCounts: deltaCounts,
|
|
383
383
|
cssRuntime: this
|
|
384
384
|
});
|
|
385
385
|
});
|
|
386
386
|
this.observer.observe(this.root, {
|
|
387
|
+
childList: true,
|
|
387
388
|
attributes: true,
|
|
388
|
-
attributeOldValue: true,
|
|
389
389
|
attributeFilter: [
|
|
390
390
|
'class'
|
|
391
391
|
],
|
|
392
|
-
|
|
392
|
+
attributeOldValue: true,
|
|
393
393
|
subtree: true
|
|
394
394
|
});
|
|
395
|
-
if (!this.progressive)
|
|
396
|
-
this.host.removeAttribute('hidden');
|
|
397
|
-
}
|
|
398
|
-
// @ts-ignore
|
|
395
|
+
if (!this.progressive) this.host.removeAttribute('hidden');
|
|
399
396
|
this.observing = true;
|
|
400
397
|
globalThis.__MASTER_CSS_DEVTOOLS_HOOK__?.emit('runtime:observed', {
|
|
401
398
|
cssRuntime: this
|
|
@@ -541,7 +538,6 @@ class CSSRuntime extends MasterCSS {
|
|
|
541
538
|
if (!this.observing) return;
|
|
542
539
|
if (this.observer) {
|
|
543
540
|
this.observer.disconnect();
|
|
544
|
-
// @ts-expect-error readonly
|
|
545
541
|
this.observer = undefined;
|
|
546
542
|
}
|
|
547
543
|
// @ts-ignore
|