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