@master/css-runtime 2.0.0-rc.69 → 2.0.0-rc.70
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/README.md +89 -7
- package/dist/class-tracker.d.ts +12 -0
- package/dist/core.d.ts +51 -66
- package/dist/css-runtime.d.ts +17 -0
- package/dist/debuggers/log.d.ts +17 -0
- package/dist/default-manifest.json +1 -0
- package/dist/generated-rule.d.ts +26 -0
- package/dist/global.min.js +1 -1
- package/dist/index.d.ts +8 -3
- package/dist/index.development.mjs +980 -268
- package/dist/index.mjs +972 -263
- package/dist/layer.d.ts +3 -2
- package/dist/native-declaration.d.ts +2 -0
- package/dist/register-global.d.ts +2 -5
- package/dist/theme-layer.d.ts +27 -0
- package/dist/types/hydrate-result.d.ts +3 -2
- package/dist/utility-layer.d.ts +13 -0
- package/dist/utils/find-native-css-rule-index.d.ts +1 -0
- package/package.json +1 -1
- package/dist/index.cjs +0 -481
- package/dist/index.development.cjs +0 -608
- package/dist/init.d.ts +0 -10
- package/dist/syntax-layer.d.ts +0 -19
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { Layer,
|
|
1
|
+
import { Layer, AnimationRule, VariableRule, compareRulePriority, ThemeLayer, MasterCSS } from '@master/css-engine';
|
|
2
|
+
import { MASTER_CSS_HYDRATION_MANIFEST_ATTR, MASTER_CSS_HYDRATION_MANIFEST_SCRIPT_ID } from '@master/css-schema/hydration-manifest';
|
|
3
|
+
import { MASTER_CSS_RUNTIME_STYLE_ID } from '@master/css-schema/runtime-style';
|
|
2
4
|
import { installHook } from '@master/css-devtools-hook';
|
|
3
5
|
|
|
4
6
|
function registerGlobal(CSSRuntime) {
|
|
5
|
-
if (!globalThis.
|
|
7
|
+
if (!globalThis.MasterCSSRuntime) globalThis.MasterCSSRuntime = CSSRuntime;
|
|
6
8
|
installHook();
|
|
7
9
|
}
|
|
8
10
|
|
|
@@ -15,6 +17,10 @@ function findNativeCSSRuleIndex(cssRules, nativeRule) {
|
|
|
15
17
|
return -1;
|
|
16
18
|
}
|
|
17
19
|
|
|
20
|
+
function getRuleNodes(rule) {
|
|
21
|
+
const nodes = rule.nodes;
|
|
22
|
+
return Array.isArray(nodes) ? nodes : undefined;
|
|
23
|
+
}
|
|
18
24
|
class RuntimeLayer extends Layer {
|
|
19
25
|
name;
|
|
20
26
|
cssRuntime;
|
|
@@ -33,11 +39,15 @@ class RuntimeLayer extends Layer {
|
|
|
33
39
|
}
|
|
34
40
|
}
|
|
35
41
|
insert(rule, index = this.rules.length) {
|
|
42
|
+
const hadNative = !!this.native?.parentStyleSheet;
|
|
43
|
+
const wasEmpty = this.rules.length === 0;
|
|
36
44
|
const insertedIndex = super.insert(rule, index);
|
|
37
45
|
if (insertedIndex === undefined || !this.native) return;
|
|
46
|
+
const nativeInsertIndex = this.getNativeInsertIndex(insertedIndex);
|
|
47
|
+
const wasAttachedWithRule = !hadNative && wasEmpty;
|
|
38
48
|
const insertRuleSafely = (text, position)=>{
|
|
39
49
|
// Checks if the rule is inserted in a native CSS rule with this.attach()
|
|
40
|
-
if (
|
|
50
|
+
if (wasAttachedWithRule) {
|
|
41
51
|
return this.native.cssRules.item(position);
|
|
42
52
|
} else {
|
|
43
53
|
try {
|
|
@@ -53,17 +63,26 @@ class RuntimeLayer extends Layer {
|
|
|
53
63
|
}
|
|
54
64
|
}
|
|
55
65
|
};
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
66
|
+
const nodes = getRuleNodes(rule);
|
|
67
|
+
if (nodes) {
|
|
68
|
+
let currentIndex = nativeInsertIndex;
|
|
69
|
+
nodes.forEach((node)=>{
|
|
59
70
|
node.native = insertRuleSafely(node.text, currentIndex);
|
|
60
71
|
if (node.native) currentIndex++;
|
|
61
72
|
});
|
|
62
73
|
} else {
|
|
63
|
-
rule.native = insertRuleSafely(rule.text,
|
|
74
|
+
rule.native = insertRuleSafely(rule.text, nativeInsertIndex);
|
|
64
75
|
}
|
|
65
76
|
return insertedIndex;
|
|
66
77
|
}
|
|
78
|
+
getNativeInsertIndex(index) {
|
|
79
|
+
let nativeIndex = 0;
|
|
80
|
+
for(let i = 0; i < index; i++){
|
|
81
|
+
const rule = this.rules[i];
|
|
82
|
+
nativeIndex += getRuleNodes(rule)?.length ?? 1;
|
|
83
|
+
}
|
|
84
|
+
return nativeIndex;
|
|
85
|
+
}
|
|
67
86
|
detach() {
|
|
68
87
|
super.detach();
|
|
69
88
|
const nativeSheet = this.cssRuntime.style?.sheet;
|
|
@@ -88,8 +107,9 @@ class RuntimeLayer extends Layer {
|
|
|
88
107
|
}
|
|
89
108
|
}
|
|
90
109
|
};
|
|
91
|
-
|
|
92
|
-
|
|
110
|
+
const nodes = getRuleNodes(deletedRule);
|
|
111
|
+
if (nodes) {
|
|
112
|
+
nodes.forEach((node)=>deleteRuleSafely(node.native));
|
|
93
113
|
} else {
|
|
94
114
|
deleteRuleSafely(deletedRule.native);
|
|
95
115
|
}
|
|
@@ -108,291 +128,950 @@ class RuntimeLayer extends Layer {
|
|
|
108
128
|
}
|
|
109
129
|
}
|
|
110
130
|
|
|
111
|
-
|
|
131
|
+
function findUtilityInsertIndex(rules, utility) {
|
|
132
|
+
let low = 0;
|
|
133
|
+
let high = rules.length;
|
|
134
|
+
while(low < high){
|
|
135
|
+
const mid = low + high >> 1;
|
|
136
|
+
const rule = rules[mid];
|
|
137
|
+
if (!('priority' in rule)) {
|
|
138
|
+
return findUtilityInsertIndexLinear(rules, utility);
|
|
139
|
+
}
|
|
140
|
+
if (compareRulePriority(utility, rule) < 0) {
|
|
141
|
+
high = mid;
|
|
142
|
+
} else {
|
|
143
|
+
low = mid + 1;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return low;
|
|
147
|
+
}
|
|
148
|
+
function findUtilityInsertIndexLinear(rules, utility) {
|
|
149
|
+
for(let i = 0; i < rules.length; i++){
|
|
150
|
+
const rule = rules[i];
|
|
151
|
+
if (!('priority' in rule)) continue;
|
|
152
|
+
if (compareRulePriority(utility, rule) < 0) {
|
|
153
|
+
return i;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return rules.length;
|
|
157
|
+
}
|
|
158
|
+
class RuntimeUtilityLayer extends RuntimeLayer {
|
|
159
|
+
rules = [];
|
|
160
|
+
insert(utility) {
|
|
161
|
+
if (this.get(utility.key)) return;
|
|
162
|
+
if ('valid' in utility && !utility.valid) return;
|
|
163
|
+
let index = this.rules.length;
|
|
164
|
+
if ('priority' in utility) {
|
|
165
|
+
index = findUtilityInsertIndex(this.rules, utility);
|
|
166
|
+
}
|
|
167
|
+
const insertedIndex = super.insert(utility, index);
|
|
168
|
+
if (insertedIndex === undefined) return;
|
|
169
|
+
this.insertVariables(utility);
|
|
170
|
+
this.insertAnimations(utility);
|
|
171
|
+
return insertedIndex;
|
|
172
|
+
}
|
|
173
|
+
delete(key) {
|
|
174
|
+
const utility = super.delete(key);
|
|
175
|
+
if (!utility) return;
|
|
176
|
+
const deleteLayerToken = (layerToken, layer, visited = new Set())=>{
|
|
177
|
+
if (visited.has(layerToken)) return;
|
|
178
|
+
visited.add(layerToken);
|
|
179
|
+
const count = layer.tokenCounts.get(layerToken) ?? 0;
|
|
180
|
+
const variable = layer === this.css.themeLayer ? this.css.variables.get(layerToken) : undefined;
|
|
181
|
+
if (count <= 1) {
|
|
182
|
+
const deletedRule = layer.delete(layerToken);
|
|
183
|
+
layer.tokenCounts.delete(layerToken);
|
|
184
|
+
variable?.dependencies?.forEach((dependency)=>deleteLayerToken(dependency, layer, visited));
|
|
185
|
+
return deletedRule;
|
|
186
|
+
} else {
|
|
187
|
+
layer.tokenCounts.set(layerToken, count - 1);
|
|
188
|
+
variable?.dependencies?.forEach((dependency)=>deleteLayerToken(dependency, layer, visited));
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
if ('variableNames' in utility) utility.variableNames?.forEach((eachVariableName)=>{
|
|
192
|
+
deleteLayerToken(eachVariableName, this.css.themeLayer);
|
|
193
|
+
});
|
|
194
|
+
if ('animationNames' in utility) utility.animationNames?.forEach((eachAnimationName)=>{
|
|
195
|
+
const deletedAnimationRule = deleteLayerToken(eachAnimationName, this.css.animationsNonLayer);
|
|
196
|
+
const variableNames = deletedAnimationRule && 'variableNames' in deletedAnimationRule ? deletedAnimationRule.variableNames : undefined;
|
|
197
|
+
if (variableNames) {
|
|
198
|
+
variableNames.forEach((eachVariableName)=>{
|
|
199
|
+
deleteLayerToken(eachVariableName, this.css.themeLayer);
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
return utility;
|
|
204
|
+
}
|
|
205
|
+
insertVariables(utility) {
|
|
206
|
+
if (!('variableNames' in utility)) return;
|
|
207
|
+
const insertVariable = (eachVariableName, visited = new Set())=>{
|
|
208
|
+
if (visited.has(eachVariableName)) return;
|
|
209
|
+
visited.add(eachVariableName);
|
|
210
|
+
const variable = this.css.variables.get(eachVariableName);
|
|
211
|
+
if (!variable || variable.inline) return;
|
|
212
|
+
if (this.css.themeLayer.rules.find(({ name })=>name === eachVariableName) || this.css.isEmittedGlobalsVariable(eachVariableName)) {
|
|
213
|
+
const count = this.css.themeLayer.tokenCounts.get(eachVariableName) || 0;
|
|
214
|
+
this.css.themeLayer.tokenCounts.set(eachVariableName, count + 1);
|
|
215
|
+
} else {
|
|
216
|
+
const newRule = new VariableRule(eachVariableName, variable, this.css);
|
|
217
|
+
this.css.themeLayer.insert(newRule);
|
|
218
|
+
this.css.themeLayer.tokenCounts.set(eachVariableName, 1);
|
|
219
|
+
}
|
|
220
|
+
variable.dependencies?.forEach((dependency)=>insertVariable(dependency, visited));
|
|
221
|
+
};
|
|
222
|
+
utility.variableNames?.forEach((eachVariableName)=>insertVariable(eachVariableName));
|
|
223
|
+
}
|
|
224
|
+
insertAnimations(utility) {
|
|
225
|
+
if (!('animationNames' in utility)) return;
|
|
226
|
+
utility.animationNames?.forEach((eachAnimationName)=>{
|
|
227
|
+
const animationRule = this.css.animationsNonLayer.rules.find(({ name })=>name === eachAnimationName);
|
|
228
|
+
if (animationRule || this.css.isEmittedGlobalsAnimation(eachAnimationName)) {
|
|
229
|
+
const count = this.css.animationsNonLayer.tokenCounts.get(eachAnimationName) || 0;
|
|
230
|
+
this.css.animationsNonLayer.tokenCounts.set(eachAnimationName, count + 1);
|
|
231
|
+
if (animationRule) this.insertVariables(animationRule);
|
|
232
|
+
} else {
|
|
233
|
+
const keyframes = this.css.animations.get(eachAnimationName);
|
|
234
|
+
if (!keyframes) return;
|
|
235
|
+
const newRule = new AnimationRule(eachAnimationName, keyframes, this.css);
|
|
236
|
+
this.css.animationsNonLayer.insert(newRule);
|
|
237
|
+
this.css.animationsNonLayer.tokenCounts.set(eachAnimationName, 1);
|
|
238
|
+
this.insertVariables(newRule);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
class RuntimeThemeLayer extends ThemeLayer {
|
|
245
|
+
name;
|
|
246
|
+
cssRuntime;
|
|
247
|
+
native = null;
|
|
248
|
+
nativeBuckets = new Map();
|
|
249
|
+
constructor(name, cssRuntime){
|
|
250
|
+
super(name, cssRuntime), this.name = name, this.cssRuntime = cssRuntime;
|
|
251
|
+
}
|
|
252
|
+
attach() {
|
|
253
|
+
super.attach();
|
|
254
|
+
const nativeSheet = this.cssRuntime.style?.sheet;
|
|
255
|
+
if (nativeSheet && !this.native?.parentStyleSheet) {
|
|
256
|
+
const insertedIndex = nativeSheet.insertRule(this.text, nativeSheet.cssRules.length);
|
|
257
|
+
this.native = nativeSheet.cssRules.item(insertedIndex);
|
|
258
|
+
this.syncNativeBuckets();
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
getBucketKey(mediaText, selectorText) {
|
|
262
|
+
const normalizedMediaText = mediaText.replace(/\s*:\s*/g, ':').replace(/\s+/g, ' ').trim();
|
|
263
|
+
const normalizedSelectorText = selectorText.replace(/\s*,\s*/g, ',').replace(/\s+/g, ' ').trim();
|
|
264
|
+
return normalizedMediaText + '\n' + normalizedSelectorText;
|
|
265
|
+
}
|
|
266
|
+
getStyleRule(nativeRule) {
|
|
267
|
+
if (nativeRule instanceof CSSStyleRule) return nativeRule;
|
|
268
|
+
if (nativeRule instanceof CSSGroupingRule) {
|
|
269
|
+
return nativeRule.cssRules.item(0);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
syncNativeBuckets() {
|
|
273
|
+
this.nativeBuckets.clear();
|
|
274
|
+
if (!this.native) return;
|
|
275
|
+
for (const nativeRule of this.native.cssRules){
|
|
276
|
+
const styleRule = this.getStyleRule(nativeRule);
|
|
277
|
+
if (!styleRule) continue;
|
|
278
|
+
const mediaText = nativeRule instanceof CSSMediaRule ? nativeRule.conditionText : '';
|
|
279
|
+
const normalizedMediaText = mediaText ? `@media ${mediaText}` : '';
|
|
280
|
+
const key = this.getBucketKey(normalizedMediaText, styleRule.selectorText);
|
|
281
|
+
this.nativeBuckets.set(key, {
|
|
282
|
+
key,
|
|
283
|
+
mediaText: normalizedMediaText,
|
|
284
|
+
selectorText: styleRule.selectorText,
|
|
285
|
+
nativeRule,
|
|
286
|
+
styleRule
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
ensureNativeBucket(mediaText, selectorText) {
|
|
291
|
+
if (!this.native) return;
|
|
292
|
+
const key = this.getBucketKey(mediaText, selectorText);
|
|
293
|
+
let bucket = this.nativeBuckets.get(key);
|
|
294
|
+
if (bucket) return bucket;
|
|
295
|
+
const text = mediaText ? `${mediaText}{${selectorText}{}}` : `${selectorText}{}`;
|
|
296
|
+
const insertedIndex = this.native.insertRule(text, this.getNativeBucketInsertIndex(mediaText, selectorText));
|
|
297
|
+
const nativeRule = this.native.cssRules.item(insertedIndex);
|
|
298
|
+
if (!nativeRule) return;
|
|
299
|
+
const styleRule = this.getStyleRule(nativeRule);
|
|
300
|
+
if (!styleRule) return;
|
|
301
|
+
const newBucket = {
|
|
302
|
+
key,
|
|
303
|
+
mediaText,
|
|
304
|
+
selectorText,
|
|
305
|
+
nativeRule,
|
|
306
|
+
styleRule
|
|
307
|
+
};
|
|
308
|
+
this.nativeBuckets.set(key, newBucket);
|
|
309
|
+
return newBucket;
|
|
310
|
+
}
|
|
311
|
+
getNativeBucketInsertIndex(mediaText, selectorText) {
|
|
312
|
+
if (!this.native) return 0;
|
|
313
|
+
const bucket = {
|
|
314
|
+
mediaText,
|
|
315
|
+
selectorText
|
|
316
|
+
};
|
|
317
|
+
for(let index = 0; index < this.native.cssRules.length; index++){
|
|
318
|
+
const nativeRule = this.native.cssRules.item(index);
|
|
319
|
+
if (!nativeRule) continue;
|
|
320
|
+
const styleRule = this.getStyleRule(nativeRule);
|
|
321
|
+
if (!styleRule) continue;
|
|
322
|
+
const nativeMediaText = nativeRule instanceof CSSMediaRule ? `@media ${nativeRule.conditionText}` : '';
|
|
323
|
+
if (this.shouldInsertBucketBefore(bucket, {
|
|
324
|
+
mediaText: nativeMediaText,
|
|
325
|
+
selectorText: styleRule.selectorText
|
|
326
|
+
})) {
|
|
327
|
+
return index;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return this.native.cssRules.length;
|
|
331
|
+
}
|
|
332
|
+
insert(rule, index = this.rules.length) {
|
|
333
|
+
const wasEmpty = this.rules.length === 0;
|
|
334
|
+
const insertedIndex = super.insert(rule, index);
|
|
335
|
+
if (insertedIndex === undefined || !this.native || wasEmpty) return insertedIndex;
|
|
336
|
+
for (const node of rule.nodes){
|
|
337
|
+
const bucket = this.ensureNativeBucket(node.mediaText, node.selectorText);
|
|
338
|
+
bucket?.styleRule.style.setProperty(node.declarationName, node.declarationValue);
|
|
339
|
+
node.native = bucket?.styleRule;
|
|
340
|
+
}
|
|
341
|
+
return insertedIndex;
|
|
342
|
+
}
|
|
343
|
+
delete(key) {
|
|
344
|
+
const rule = this.rules.find((rule)=>rule.key === key);
|
|
345
|
+
const deletedRule = super.delete(key);
|
|
346
|
+
if (!rule || !deletedRule || !this.native) return deletedRule;
|
|
347
|
+
for (const node of rule.nodes){
|
|
348
|
+
const bucketKey = this.getBucketKey(node.mediaText, node.selectorText);
|
|
349
|
+
const bucket = this.nativeBuckets.get(bucketKey);
|
|
350
|
+
if (!bucket) continue;
|
|
351
|
+
bucket.styleRule.style.removeProperty(node.declarationName);
|
|
352
|
+
if (bucket.styleRule.style.length === 0) {
|
|
353
|
+
const foundIndex = findNativeCSSRuleIndex(this.native.cssRules, bucket.nativeRule);
|
|
354
|
+
if (foundIndex !== -1) {
|
|
355
|
+
this.native.deleteRule(foundIndex);
|
|
356
|
+
}
|
|
357
|
+
this.nativeBuckets.delete(bucketKey);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return deletedRule;
|
|
361
|
+
}
|
|
362
|
+
detach() {
|
|
363
|
+
super.detach();
|
|
364
|
+
const nativeSheet = this.cssRuntime.style?.sheet;
|
|
365
|
+
if (nativeSheet && this.native?.parentStyleSheet) {
|
|
366
|
+
const foundIndex = findNativeCSSRuleIndex(nativeSheet.cssRules, this.native);
|
|
367
|
+
if (foundIndex !== -1) {
|
|
368
|
+
nativeSheet.deleteRule(foundIndex);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
this.native = null;
|
|
372
|
+
this.nativeBuckets.clear();
|
|
373
|
+
}
|
|
374
|
+
reset() {
|
|
375
|
+
super.reset();
|
|
376
|
+
const nativeSheet = this.cssRuntime.style?.sheet;
|
|
377
|
+
if (this.native && nativeSheet) {
|
|
378
|
+
const foundIndex = findNativeCSSRuleIndex(nativeSheet.cssRules, this.native);
|
|
379
|
+
if (foundIndex !== -1) {
|
|
380
|
+
nativeSheet.deleteRule(foundIndex);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
this.native = null;
|
|
384
|
+
this.nativeBuckets.clear();
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function updateClassDelta(deltaCounts, className, delta) {
|
|
389
|
+
const nextDelta = (deltaCounts.get(className) || 0) + delta;
|
|
390
|
+
if (nextDelta) {
|
|
391
|
+
deltaCounts.set(className, nextDelta);
|
|
392
|
+
} else {
|
|
393
|
+
deltaCounts.delete(className);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
function traverseElementSubtree(root, visited, callback) {
|
|
397
|
+
const stack = [
|
|
398
|
+
root
|
|
399
|
+
];
|
|
400
|
+
while(stack.length){
|
|
401
|
+
const el = stack.pop();
|
|
402
|
+
if (visited.has(el)) continue;
|
|
403
|
+
visited.add(el);
|
|
404
|
+
callback(el);
|
|
405
|
+
for(let child = el.lastElementChild; child; child = child.previousElementSibling){
|
|
406
|
+
stack.push(child);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
class RuntimeClassTracker {
|
|
411
|
+
snapshots = new WeakMap();
|
|
412
|
+
reset() {
|
|
413
|
+
this.snapshots = new WeakMap();
|
|
414
|
+
}
|
|
415
|
+
collectConnected(root, classCounts) {
|
|
416
|
+
this.reset();
|
|
417
|
+
const connectedNames = new Set();
|
|
418
|
+
for (const el of root.querySelectorAll('[class]')){
|
|
419
|
+
const classNames = this.snapshotElement(el);
|
|
420
|
+
if (!classNames) continue;
|
|
421
|
+
for (const className of classNames){
|
|
422
|
+
const count = classCounts.get(className) || 0;
|
|
423
|
+
if (!count) connectedNames.add(className);
|
|
424
|
+
classCounts.set(className, count + 1);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return connectedNames;
|
|
428
|
+
}
|
|
429
|
+
collectMutations(records) {
|
|
430
|
+
const deltaCounts = new Map();
|
|
431
|
+
const nodeCounts = new Map();
|
|
432
|
+
const attrRecords = new Set();
|
|
433
|
+
const visited = new WeakSet();
|
|
434
|
+
const updateNodeCount = (el, delta)=>{
|
|
435
|
+
const nextCount = (nodeCounts.get(el) || 0) + delta;
|
|
436
|
+
if (nextCount) {
|
|
437
|
+
nodeCounts.set(el, nextCount);
|
|
438
|
+
} else {
|
|
439
|
+
nodeCounts.delete(el);
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
for (const record of records){
|
|
443
|
+
if (record.type === 'childList') {
|
|
444
|
+
for (const node of record.addedNodes){
|
|
445
|
+
if (node instanceof Element && node.isConnected) updateNodeCount(node, 1);
|
|
446
|
+
}
|
|
447
|
+
for (const node of record.removedNodes){
|
|
448
|
+
if (node instanceof Element && !node.isConnected) updateNodeCount(node, -1);
|
|
449
|
+
}
|
|
450
|
+
} else if (record.type === 'attributes' && record.attributeName === 'class') {
|
|
451
|
+
const target = record.target;
|
|
452
|
+
if (target instanceof Element) attrRecords.add(target);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
for (const [node, count] of nodeCounts){
|
|
456
|
+
if (count > 0) {
|
|
457
|
+
traverseElementSubtree(node, visited, (el)=>this.diffElement(el, deltaCounts));
|
|
458
|
+
} else if (count < 0 && !node.isConnected) {
|
|
459
|
+
traverseElementSubtree(node, visited, (el)=>this.removeElement(el, deltaCounts));
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
for (const el of attrRecords){
|
|
463
|
+
if (!visited.has(el)) this.diffElement(el, deltaCounts);
|
|
464
|
+
}
|
|
465
|
+
return deltaCounts;
|
|
466
|
+
}
|
|
467
|
+
snapshotElement(el) {
|
|
468
|
+
const classList = el.classList;
|
|
469
|
+
if (!classList.length) {
|
|
470
|
+
this.snapshots.delete(el);
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
const classNames = new Set();
|
|
474
|
+
for(let i = 0; i < classList.length; i++){
|
|
475
|
+
classNames.add(classList.item(i));
|
|
476
|
+
}
|
|
477
|
+
this.snapshots.set(el, classNames);
|
|
478
|
+
return classNames;
|
|
479
|
+
}
|
|
480
|
+
diffElement(el, deltaCounts) {
|
|
481
|
+
const previousClassNames = this.snapshots.get(el);
|
|
482
|
+
const classList = el.classList;
|
|
483
|
+
let nextClassNames;
|
|
484
|
+
for(let i = 0; i < classList.length; i++){
|
|
485
|
+
const className = classList.item(i);
|
|
486
|
+
if (!previousClassNames?.has(className)) updateClassDelta(deltaCounts, className, 1);
|
|
487
|
+
(nextClassNames ??= new Set()).add(className);
|
|
488
|
+
}
|
|
489
|
+
if (previousClassNames) {
|
|
490
|
+
for (const className of previousClassNames){
|
|
491
|
+
if (!nextClassNames?.has(className)) updateClassDelta(deltaCounts, className, -1);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
if (nextClassNames) {
|
|
495
|
+
this.snapshots.set(el, nextClassNames);
|
|
496
|
+
} else {
|
|
497
|
+
this.snapshots.delete(el);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
removeElement(el, deltaCounts) {
|
|
501
|
+
const previousClassNames = this.snapshots.get(el);
|
|
502
|
+
if (!previousClassNames) return;
|
|
503
|
+
for (const className of previousClassNames){
|
|
504
|
+
updateClassDelta(deltaCounts, className, -1);
|
|
505
|
+
}
|
|
506
|
+
this.snapshots.delete(el);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function collectVariableNames(text) {
|
|
511
|
+
const variableNames = new Set();
|
|
512
|
+
for (const match of text.matchAll(/var\(\s*--([_a-zA-Z0-9-]+)/g)){
|
|
513
|
+
variableNames.add(match[1]);
|
|
514
|
+
}
|
|
515
|
+
return variableNames;
|
|
516
|
+
}
|
|
517
|
+
class HydratedGeneratedRuleNode {
|
|
518
|
+
text;
|
|
519
|
+
native;
|
|
520
|
+
constructor(text){
|
|
521
|
+
this.text = text;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
class HydratedGeneratedRule {
|
|
525
|
+
ir;
|
|
526
|
+
layer;
|
|
527
|
+
native;
|
|
528
|
+
name;
|
|
529
|
+
key;
|
|
530
|
+
layerName;
|
|
531
|
+
type;
|
|
532
|
+
sortTier;
|
|
533
|
+
priority;
|
|
534
|
+
text;
|
|
535
|
+
valid = true;
|
|
536
|
+
fixedClass;
|
|
537
|
+
nodes;
|
|
538
|
+
variableNames;
|
|
539
|
+
animationNames;
|
|
540
|
+
selectorText;
|
|
541
|
+
constructor(ir, layer){
|
|
542
|
+
this.ir = ir;
|
|
543
|
+
this.layer = layer;
|
|
544
|
+
this.name = ir.className;
|
|
545
|
+
this.key = ir.key;
|
|
546
|
+
this.layerName = ir.layer;
|
|
547
|
+
this.type = ir.type;
|
|
548
|
+
this.sortTier = ir.sortTier;
|
|
549
|
+
this.priority = ir.priority;
|
|
550
|
+
this.text = ir.text;
|
|
551
|
+
this.selectorText = ir.selectorText;
|
|
552
|
+
if (ir.nodes?.length) {
|
|
553
|
+
this.nodes = ir.nodes.map((node)=>new HydratedGeneratedRuleNode(node.text));
|
|
554
|
+
}
|
|
555
|
+
const variableNames = collectVariableNames(ir.text);
|
|
556
|
+
ir.nodes?.forEach((node)=>{
|
|
557
|
+
collectVariableNames(node.text).forEach((variableName)=>variableNames.add(variableName));
|
|
558
|
+
});
|
|
559
|
+
ir.variableNames?.forEach((variableName)=>variableNames.add(variableName));
|
|
560
|
+
if (variableNames.size) {
|
|
561
|
+
this.variableNames = variableNames;
|
|
562
|
+
}
|
|
563
|
+
if (ir.animationNames?.length) {
|
|
564
|
+
this.animationNames = new Set(ir.animationNames);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
112
568
|
|
|
569
|
+
const supportsCache = new Map();
|
|
570
|
+
const browserNativeDeclarationMatcher = ({ property, value })=>{
|
|
571
|
+
if (property.startsWith('--')) return true;
|
|
572
|
+
const cacheKey = property + '\0' + value;
|
|
573
|
+
const cached = supportsCache.get(cacheKey);
|
|
574
|
+
if (cached !== undefined) return cached;
|
|
575
|
+
const supported = Boolean(globalThis.CSS?.supports(property, value));
|
|
576
|
+
supportsCache.set(cacheKey, supported);
|
|
577
|
+
return supported;
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
const MASTER_CSS_RUNTIME_STYLE_SELECTOR = `style#${MASTER_CSS_RUNTIME_STYLE_ID}`;
|
|
581
|
+
function isDocumentRoot(root) {
|
|
582
|
+
const rootConstructorName = root?.constructor.name;
|
|
583
|
+
return rootConstructorName === 'HTMLDocument' || rootConstructorName === 'Document';
|
|
584
|
+
}
|
|
585
|
+
function findElementById(root, id) {
|
|
586
|
+
return isDocumentRoot(root) ? root.getElementById(id) : 'querySelector' in root ? root.querySelector(`#${id}`) : undefined;
|
|
587
|
+
}
|
|
588
|
+
function parseHydrationManifest(source) {
|
|
589
|
+
try {
|
|
590
|
+
const hydrationManifest = JSON.parse(source);
|
|
591
|
+
return hydrationManifest?.version === 1 && Array.isArray(hydrationManifest.rules) ? hydrationManifest : undefined;
|
|
592
|
+
} catch (error) {
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
function readInlineHydrationManifest(root) {
|
|
596
|
+
const element = findElementById(root, MASTER_CSS_HYDRATION_MANIFEST_SCRIPT_ID);
|
|
597
|
+
const source = element?.textContent?.trim();
|
|
598
|
+
if (!source) return;
|
|
599
|
+
return parseHydrationManifest(source);
|
|
600
|
+
}
|
|
601
|
+
function readExternalHydrationManifestSource(root) {
|
|
602
|
+
const styleElement = findElementById(root, MASTER_CSS_RUNTIME_STYLE_ID);
|
|
603
|
+
const HTMLStyleElementConstructor = globalThis.HTMLStyleElement;
|
|
604
|
+
return HTMLStyleElementConstructor && styleElement instanceof HTMLStyleElementConstructor ? styleElement.getAttribute(MASTER_CSS_HYDRATION_MANIFEST_ATTR) : undefined;
|
|
605
|
+
}
|
|
113
606
|
class CSSRuntime extends MasterCSS {
|
|
114
607
|
root;
|
|
115
|
-
|
|
116
|
-
baseConfig;
|
|
608
|
+
hydrationManifest;
|
|
117
609
|
static instances = new WeakMap();
|
|
118
610
|
host;
|
|
119
611
|
container;
|
|
120
|
-
baseLayer = new
|
|
121
|
-
themeLayer = new
|
|
122
|
-
|
|
123
|
-
componentsLayer = new
|
|
124
|
-
|
|
612
|
+
baseLayer = new RuntimeUtilityLayer('base', this);
|
|
613
|
+
themeLayer = new RuntimeThemeLayer('theme', this);
|
|
614
|
+
defaultsLayer = new RuntimeUtilityLayer('defaults', this);
|
|
615
|
+
componentsLayer = new RuntimeUtilityLayer('components', this);
|
|
616
|
+
utilitiesLayer = new RuntimeUtilityLayer('utilities', this);
|
|
125
617
|
classCounts = new Map();
|
|
618
|
+
classTracker = new RuntimeClassTracker();
|
|
619
|
+
hydrationFailureReason;
|
|
126
620
|
observer;
|
|
127
621
|
progressive = false;
|
|
128
622
|
observing = false;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const
|
|
133
|
-
if (
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
623
|
+
static create(options) {
|
|
624
|
+
const { manifest, root = document, emittedGlobals, hydrationManifest } = options;
|
|
625
|
+
const resolvedHydrationManifest = hydrationManifest === undefined ? readInlineHydrationManifest(root) : hydrationManifest;
|
|
626
|
+
const current = globalThis.MasterCSSRuntime.instances.get(root);
|
|
627
|
+
if (current) {
|
|
628
|
+
current.registerEmittedGlobals(emittedGlobals);
|
|
629
|
+
if (resolvedHydrationManifest !== undefined) current.setHydrationManifest(resolvedHydrationManifest);
|
|
630
|
+
return current;
|
|
631
|
+
}
|
|
632
|
+
return new CSSRuntime(root, manifest, emittedGlobals, resolvedHydrationManifest).register();
|
|
633
|
+
}
|
|
634
|
+
constructor(root = document, manifest, emittedGlobals, hydrationManifest){
|
|
635
|
+
super(manifest, emittedGlobals, {
|
|
636
|
+
nativeDeclarationMatcher: browserNativeDeclarationMatcher
|
|
637
|
+
}), this.root = root, this.hydrationManifest = hydrationManifest;
|
|
638
|
+
if (isDocumentRoot(root)) {
|
|
639
|
+
this.container = root.head;
|
|
640
|
+
this.host = root.documentElement;
|
|
137
641
|
} else {
|
|
138
642
|
this.container = this.root;
|
|
139
643
|
this.host = this.root.host;
|
|
140
644
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
cssRuntime: this
|
|
645
|
+
this.applyEmittedGlobalsCounts({
|
|
646
|
+
variables: this.emittedGlobals.variables
|
|
144
647
|
});
|
|
145
648
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
649
|
+
register() {
|
|
650
|
+
const registered = globalThis.MasterCSSRuntime.instances.get(this.root) === this;
|
|
651
|
+
globalThis.MasterCSSRuntime.instances.set(this.root, this);
|
|
652
|
+
if (isDocumentRoot(this.root)) {
|
|
653
|
+
this.root.defaultView.globalThis.masterCSSRuntime = this;
|
|
654
|
+
}
|
|
655
|
+
if (!registered) {
|
|
656
|
+
__MASTER_CSS_DEVTOOLS_HOOK__?.emit('runtime:created', {
|
|
657
|
+
cssRuntime: this
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
return this;
|
|
661
|
+
}
|
|
662
|
+
unregister() {
|
|
663
|
+
globalThis.MasterCSSRuntime.instances.delete(this.root);
|
|
664
|
+
if (isDocumentRoot(this.root) && this.root.defaultView.globalThis.masterCSSRuntime === this) {
|
|
665
|
+
this.root.defaultView.globalThis.masterCSSRuntime = undefined;
|
|
666
|
+
}
|
|
667
|
+
return this;
|
|
668
|
+
}
|
|
669
|
+
setHydrationManifest(hydrationManifest) {
|
|
670
|
+
this.hydrationManifest = hydrationManifest;
|
|
671
|
+
return this;
|
|
672
|
+
}
|
|
673
|
+
needsHydrationManifest() {
|
|
674
|
+
return this.hydrationManifest === undefined && Boolean(readExternalHydrationManifestSource(this.root));
|
|
675
|
+
}
|
|
676
|
+
async loadHydrationManifest() {
|
|
677
|
+
const inlineHydrationManifest = readInlineHydrationManifest(this.root);
|
|
678
|
+
if (inlineHydrationManifest) {
|
|
679
|
+
this.setHydrationManifest(inlineHydrationManifest);
|
|
680
|
+
return this;
|
|
681
|
+
}
|
|
682
|
+
const source = readExternalHydrationManifestSource(this.root);
|
|
683
|
+
if (!source) return this;
|
|
684
|
+
try {
|
|
685
|
+
const response = await fetch(source, {
|
|
686
|
+
credentials: 'same-origin'
|
|
687
|
+
});
|
|
688
|
+
if (!response.ok) return this;
|
|
689
|
+
this.setHydrationManifest(parseHydrationManifest(await response.text()));
|
|
690
|
+
} catch (error) {
|
|
691
|
+
}
|
|
692
|
+
return this;
|
|
693
|
+
}
|
|
694
|
+
createRuntimeStyle() {
|
|
695
|
+
const ownerDocument = 'createElement' in this.root ? this.root : this.root.ownerDocument;
|
|
696
|
+
this.style = ownerDocument.createElement('style');
|
|
697
|
+
this.style.id = MASTER_CSS_RUNTIME_STYLE_ID;
|
|
698
|
+
this.style.setAttribute('blocking', 'render');
|
|
699
|
+
this.container.append(this.style);
|
|
700
|
+
}
|
|
701
|
+
warnHydrationFallback(reason) {
|
|
702
|
+
console.warn(`Master CSS progressive hydration requires a matching hydration manifest. ${reason} Rebuilding ${MASTER_CSS_RUNTIME_STYLE_SELECTOR} with the runtime.`);
|
|
703
|
+
}
|
|
704
|
+
useRuntimeStyle(connectedNames, reason) {
|
|
705
|
+
if (reason) this.warnHydrationFallback(reason);
|
|
706
|
+
this.style?.remove();
|
|
707
|
+
this.style = null;
|
|
708
|
+
this.progressive = false;
|
|
709
|
+
this.createRuntimeStyle();
|
|
710
|
+
this.insertStaticResources();
|
|
711
|
+
connectedNames.forEach((cls)=>this.add(cls));
|
|
712
|
+
}
|
|
713
|
+
detectRuntimeStyle() {
|
|
153
714
|
if (this.root.styleSheets) {
|
|
154
715
|
for (const sheet of this.root.styleSheets){
|
|
155
716
|
const { ownerNode } = sheet;
|
|
156
|
-
if (ownerNode instanceof HTMLStyleElement && ownerNode.id ===
|
|
717
|
+
if (ownerNode instanceof HTMLStyleElement && ownerNode.id === MASTER_CSS_RUNTIME_STYLE_ID) {
|
|
157
718
|
this.style = ownerNode;
|
|
158
|
-
|
|
159
|
-
this.progressive = true;
|
|
160
|
-
}
|
|
719
|
+
this.progressive = true;
|
|
161
720
|
break;
|
|
162
721
|
}
|
|
163
722
|
}
|
|
164
723
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
};
|
|
174
|
-
const elementsWithClass = this.root.querySelectorAll('[class]');
|
|
175
|
-
elementsWithClass.forEach((el)=>{
|
|
176
|
-
const clsList = el.classList;
|
|
177
|
-
if (clsList) {
|
|
178
|
-
el.classList.forEach(increaseClassCount);
|
|
179
|
-
}
|
|
180
|
-
elementClasses.set(el, new Set(clsList));
|
|
181
|
-
});
|
|
182
|
-
// Hydration or style creation
|
|
183
|
-
if (this.progressive) {
|
|
184
|
-
const hydrateResult = this.hydrate(this.style.sheet.cssRules);
|
|
724
|
+
}
|
|
725
|
+
collectConnectedClasses() {
|
|
726
|
+
return this.classTracker.collectConnected(this.root, this.classCounts);
|
|
727
|
+
}
|
|
728
|
+
hydrateRuntimeStyle(connectedNames) {
|
|
729
|
+
const hydrateResult = this.style?.sheet && this.hydrate(this.style.sheet.cssRules);
|
|
730
|
+
if (hydrateResult) {
|
|
731
|
+
const hydratedClassNames = new Set(hydrateResult.allUtilities.map(({ fixedClass, name })=>fixedClass || name));
|
|
185
732
|
for (const cls of connectedNames){
|
|
186
|
-
if (!
|
|
733
|
+
if (!hydratedClassNames.has(cls)) {
|
|
187
734
|
this.add(cls);
|
|
188
735
|
}
|
|
189
736
|
}
|
|
190
737
|
} else {
|
|
191
|
-
this.
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const removeSnapshot = (el)=>{
|
|
213
|
-
for (const c of elementClasses.get(el) || [])updateDelta(c, -1);
|
|
214
|
-
elementClasses.delete(el);
|
|
215
|
-
};
|
|
216
|
-
for (const record of records){
|
|
217
|
-
if (record.type === 'childList') {
|
|
218
|
-
for (const node of record.addedNodes)if (node instanceof Element && node.isConnected) nodeMap.set(node, (nodeMap.get(node) || 0) + 1);
|
|
219
|
-
for (const node of record.removedNodes)if (node instanceof Element && !node.isConnected) nodeMap.set(node, (nodeMap.get(node) || 0) - 1);
|
|
220
|
-
} else if (record.type === 'attributes' && record.attributeName === 'class') {
|
|
221
|
-
attrRecords.add(record.target);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
const traverseIncludingSelf = (el, fn, visited)=>{
|
|
225
|
-
if (!visited.has(el)) {
|
|
226
|
-
visited.add(el);
|
|
227
|
-
fn(el);
|
|
228
|
-
for (const child of el.children){
|
|
229
|
-
traverseIncludingSelf(child, fn, visited);
|
|
230
|
-
}
|
|
231
|
-
} else {
|
|
232
|
-
nodeMap.delete(el);
|
|
233
|
-
}
|
|
234
|
-
};
|
|
235
|
-
for (const [node, count] of nodeMap){
|
|
236
|
-
if (count > 0) {
|
|
237
|
-
traverseIncludingSelf(node, diffAndSnapshot, visited);
|
|
238
|
-
} else if (count < 0 && !node.isConnected) {
|
|
239
|
-
traverseIncludingSelf(node, removeSnapshot, visited);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
for (const el of attrRecords){
|
|
243
|
-
if (!visited.has(el)) diffAndSnapshot(el);
|
|
244
|
-
}
|
|
245
|
-
for (const [cls, change] of deltaCounts){
|
|
246
|
-
const current = this.classCounts.get(cls) || 0;
|
|
247
|
-
const next = current + change;
|
|
248
|
-
if (next > 0) {
|
|
249
|
-
this.classCounts.set(cls, next);
|
|
250
|
-
if (current === 0) this.add(cls);
|
|
251
|
-
} else {
|
|
252
|
-
this.classCounts.delete(cls);
|
|
253
|
-
this.remove(cls);
|
|
254
|
-
}
|
|
738
|
+
this.useRuntimeStyle(connectedNames, this.hydrationFailureReason || `Cannot read ${MASTER_CSS_RUNTIME_STYLE_SELECTOR} CSS rules.`);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
renderRuntimeStyle(connectedNames) {
|
|
742
|
+
this.createRuntimeStyle();
|
|
743
|
+
this.insertStaticResources();
|
|
744
|
+
connectedNames.forEach((cls)=>this.add(cls));
|
|
745
|
+
}
|
|
746
|
+
handleMutationRecords(records) {
|
|
747
|
+
const deltaCounts = this.classTracker.collectMutations(records);
|
|
748
|
+
const addedClassNames = [];
|
|
749
|
+
const removedClassNames = [];
|
|
750
|
+
for (const [cls, change] of deltaCounts){
|
|
751
|
+
const current = this.classCounts.get(cls) || 0;
|
|
752
|
+
const next = current + change;
|
|
753
|
+
if (next > 0) {
|
|
754
|
+
this.classCounts.set(cls, next);
|
|
755
|
+
if (current === 0) addedClassNames.push(cls);
|
|
756
|
+
} else {
|
|
757
|
+
this.classCounts.delete(cls);
|
|
758
|
+
removedClassNames.push(cls);
|
|
255
759
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
760
|
+
}
|
|
761
|
+
if (addedClassNames.length) this.add(...addedClassNames);
|
|
762
|
+
if (removedClassNames.length) this.remove(...removedClassNames);
|
|
763
|
+
globalThis.__MASTER_CSS_DEVTOOLS_HOOK__?.emit('runtime:mutated', {
|
|
764
|
+
records,
|
|
765
|
+
classCounts: deltaCounts,
|
|
766
|
+
cssRuntime: this
|
|
261
767
|
});
|
|
768
|
+
}
|
|
769
|
+
startMutationObserver() {
|
|
770
|
+
this.observer = new MutationObserver((records)=>this.handleMutationRecords(records));
|
|
262
771
|
this.observer.observe(this.root, {
|
|
263
772
|
childList: true,
|
|
264
773
|
attributes: true,
|
|
265
774
|
attributeFilter: [
|
|
266
775
|
'class'
|
|
267
776
|
],
|
|
268
|
-
attributeOldValue: true,
|
|
269
777
|
subtree: true
|
|
270
778
|
});
|
|
779
|
+
}
|
|
780
|
+
revealHostIfNeeded() {
|
|
271
781
|
if (!this.progressive) this.host.removeAttribute('hidden');
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Observe the DOM for changes and update the running stylesheet. (browser only)
|
|
785
|
+
* @param options mutation observer options
|
|
786
|
+
* @returns this
|
|
787
|
+
*/ observe() {
|
|
788
|
+
if (this.observing) return this;
|
|
789
|
+
this.detectRuntimeStyle();
|
|
790
|
+
const connectedNames = this.collectConnectedClasses();
|
|
791
|
+
if (this.progressive) {
|
|
792
|
+
this.hydrateRuntimeStyle(connectedNames);
|
|
793
|
+
} else {
|
|
794
|
+
this.renderRuntimeStyle(connectedNames);
|
|
795
|
+
}
|
|
796
|
+
this.startMutationObserver();
|
|
797
|
+
this.revealHostIfNeeded();
|
|
272
798
|
this.observing = true;
|
|
273
799
|
globalThis.__MASTER_CSS_DEVTOOLS_HOOK__?.emit('runtime:observed', {
|
|
274
800
|
cssRuntime: this
|
|
275
801
|
});
|
|
276
802
|
return this;
|
|
277
803
|
}
|
|
804
|
+
failHydration(reason) {
|
|
805
|
+
this.hydrationFailureReason = reason;
|
|
806
|
+
return undefined;
|
|
807
|
+
}
|
|
808
|
+
getHydrationManifestLayerRules() {
|
|
809
|
+
const layers = new Map();
|
|
810
|
+
if (!this.hydrationManifest?.rules?.length) return layers;
|
|
811
|
+
for (const rule of this.hydrationManifest.rules){
|
|
812
|
+
const layerRules = layers.get(rule.layer);
|
|
813
|
+
if (layerRules) {
|
|
814
|
+
layerRules.push(rule);
|
|
815
|
+
} else {
|
|
816
|
+
layers.set(rule.layer, [
|
|
817
|
+
rule
|
|
818
|
+
]);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
return layers;
|
|
822
|
+
}
|
|
823
|
+
registerHydratedClassRule(className, rule) {
|
|
824
|
+
const classUtilities = this.classUtilities;
|
|
825
|
+
const rules = classUtilities.get(className);
|
|
826
|
+
if (rules) {
|
|
827
|
+
rules.push(rule);
|
|
828
|
+
} else {
|
|
829
|
+
classUtilities.set(className, [
|
|
830
|
+
rule
|
|
831
|
+
]);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
getUtilityLayerByName(name) {
|
|
835
|
+
switch(name){
|
|
836
|
+
case 'base':
|
|
837
|
+
return this.baseLayer;
|
|
838
|
+
case 'defaults':
|
|
839
|
+
return this.defaultsLayer;
|
|
840
|
+
case 'components':
|
|
841
|
+
return this.componentsLayer;
|
|
842
|
+
case 'utilities':
|
|
843
|
+
return this.utilitiesLayer;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
collectHydrationManifestVariableNames() {
|
|
847
|
+
const variableNames = new Set();
|
|
848
|
+
const collectVariableReferences = (text)=>{
|
|
849
|
+
for (const match of text.matchAll(/var\(\s*--([_a-zA-Z0-9-]+)/g)){
|
|
850
|
+
collectVariable(match[1]);
|
|
851
|
+
}
|
|
852
|
+
};
|
|
853
|
+
const collectVariable = (variableName, visited = new Set())=>{
|
|
854
|
+
if (visited.has(variableName)) return;
|
|
855
|
+
visited.add(variableName);
|
|
856
|
+
const variable = this.variables.get(variableName);
|
|
857
|
+
if (!variable) return;
|
|
858
|
+
variableNames.add(variableName);
|
|
859
|
+
variable.dependencies?.forEach((dependency)=>collectVariable(dependency, visited));
|
|
860
|
+
};
|
|
861
|
+
for (const rule of this.hydrationManifest?.rules || []){
|
|
862
|
+
rule.variableNames?.forEach((variableName)=>collectVariable(variableName));
|
|
863
|
+
collectVariableReferences(rule.text);
|
|
864
|
+
rule.nodes?.forEach((node)=>collectVariableReferences(node.text));
|
|
865
|
+
rule.animationNames?.forEach((animationName)=>{
|
|
866
|
+
const keyframes = this.animations.get(animationName);
|
|
867
|
+
if (!keyframes) return;
|
|
868
|
+
const animationRule = new AnimationRule(animationName, keyframes, this);
|
|
869
|
+
animationRule.variableNames?.forEach((variableName)=>collectVariable(variableName));
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
for (const [variableName, variable] of this.variables){
|
|
873
|
+
if (variable.static) collectVariable(variableName);
|
|
874
|
+
}
|
|
875
|
+
return variableNames;
|
|
876
|
+
}
|
|
877
|
+
collectHydrationManifestAnimationNames() {
|
|
878
|
+
const animationNames = new Set();
|
|
879
|
+
for (const rule of this.hydrationManifest?.rules || []){
|
|
880
|
+
rule.animationNames?.forEach((animationName)=>animationNames.add(animationName));
|
|
881
|
+
}
|
|
882
|
+
for (const animationName of this.animations.keys()){
|
|
883
|
+
if (this.manifest.animationOptions?.[animationName]?.static) animationNames.add(animationName);
|
|
884
|
+
}
|
|
885
|
+
return animationNames;
|
|
886
|
+
}
|
|
887
|
+
nativeThemeLayerHasOnlyEmittedGlobalsVariables(nativeThemeLayer) {
|
|
888
|
+
if (!nativeThemeLayer) return false;
|
|
889
|
+
let found = false;
|
|
890
|
+
for (const nativeRule of nativeThemeLayer.cssRules){
|
|
891
|
+
const styleRule = this.themeLayer.getStyleRule(nativeRule);
|
|
892
|
+
if (!styleRule) return false;
|
|
893
|
+
for(let index = 0; index < styleRule.style.length; index++){
|
|
894
|
+
const propertyName = styleRule.style.item(index);
|
|
895
|
+
if (!propertyName.startsWith('--')) return false;
|
|
896
|
+
found = true;
|
|
897
|
+
if (!this.isEmittedGlobalsVariable(propertyName.slice(2))) return false;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
return found;
|
|
901
|
+
}
|
|
902
|
+
getVariableRuleBuckets(variableRules) {
|
|
903
|
+
const buckets = new Map();
|
|
904
|
+
for (const rule of variableRules){
|
|
905
|
+
for (const node of rule.nodes){
|
|
906
|
+
const key = this.themeLayer.getBucketKey(node.mediaText, node.selectorText);
|
|
907
|
+
let bucket = buckets.get(key);
|
|
908
|
+
if (!bucket) {
|
|
909
|
+
bucket = {
|
|
910
|
+
mediaText: node.mediaText,
|
|
911
|
+
selectorText: node.selectorText,
|
|
912
|
+
nodes: []
|
|
913
|
+
};
|
|
914
|
+
buckets.set(key, bucket);
|
|
915
|
+
}
|
|
916
|
+
bucket.nodes.push(node);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
return Array.from(buckets.values());
|
|
920
|
+
}
|
|
921
|
+
getNativeThemeRuleBucketKey(nativeRule) {
|
|
922
|
+
const styleRule = this.themeLayer.getStyleRule(nativeRule);
|
|
923
|
+
if (!styleRule) return;
|
|
924
|
+
const mediaText = nativeRule instanceof CSSMediaRule ? `@media ${nativeRule.conditionText}` : '';
|
|
925
|
+
return {
|
|
926
|
+
key: this.themeLayer.getBucketKey(mediaText, styleRule.selectorText),
|
|
927
|
+
styleRule
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
hydrateHydrationManifestVariables(nativeThemeLayer) {
|
|
931
|
+
const variableRules = [
|
|
932
|
+
...this.collectHydrationManifestVariableNames()
|
|
933
|
+
].filter((variableName)=>!this.isEmittedGlobalsVariable(variableName)).map((variableName)=>{
|
|
934
|
+
const variable = this.variables.get(variableName);
|
|
935
|
+
return variable && !variable.inline ? new VariableRule(variableName, variable, this) : undefined;
|
|
936
|
+
}).filter((rule)=>Boolean(rule));
|
|
937
|
+
const expectedBuckets = this.getVariableRuleBuckets(variableRules);
|
|
938
|
+
const nativeRuleCount = nativeThemeLayer?.cssRules.length || 0;
|
|
939
|
+
if (expectedBuckets.length !== nativeRuleCount) {
|
|
940
|
+
return !variableRules.length && this.nativeThemeLayerHasOnlyEmittedGlobalsVariables(nativeThemeLayer);
|
|
941
|
+
}
|
|
942
|
+
if (!variableRules.length) return true;
|
|
943
|
+
if (!nativeThemeLayer) return false;
|
|
944
|
+
this.themeLayer.native = nativeThemeLayer;
|
|
945
|
+
const nativeBuckets = new Map();
|
|
946
|
+
for (const nativeRule of nativeThemeLayer.cssRules){
|
|
947
|
+
const nativeBucket = this.getNativeThemeRuleBucketKey(nativeRule);
|
|
948
|
+
if (!nativeBucket || nativeBuckets.has(nativeBucket.key)) return false;
|
|
949
|
+
nativeBuckets.set(nativeBucket.key, nativeBucket.styleRule);
|
|
950
|
+
}
|
|
951
|
+
for (const bucket of expectedBuckets){
|
|
952
|
+
const nativeStyleRule = nativeBuckets.get(this.themeLayer.getBucketKey(bucket.mediaText, bucket.selectorText));
|
|
953
|
+
if (!nativeStyleRule) return false;
|
|
954
|
+
for (const node of bucket.nodes){
|
|
955
|
+
node.native = nativeStyleRule;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
this.themeLayer.rules.push(...variableRules);
|
|
959
|
+
this.themeLayer.syncNativeBuckets();
|
|
960
|
+
if (this.themeLayer.rules.length && !this.rules.includes(this.themeLayer)) {
|
|
961
|
+
this.rules.push(this.themeLayer);
|
|
962
|
+
}
|
|
963
|
+
return true;
|
|
964
|
+
}
|
|
965
|
+
hydrateHydrationManifestAnimations(nativeKeyframesRules) {
|
|
966
|
+
const animationNames = this.collectHydrationManifestAnimationNames();
|
|
967
|
+
const hydratedAnimationNames = new Set();
|
|
968
|
+
for (const animationName of animationNames){
|
|
969
|
+
if (this.isEmittedGlobalsAnimation(animationName)) {
|
|
970
|
+
if (nativeKeyframesRules.has(animationName)) hydratedAnimationNames.add(animationName);
|
|
971
|
+
continue;
|
|
972
|
+
}
|
|
973
|
+
const keyframes = this.animations.get(animationName);
|
|
974
|
+
if (!keyframes) continue;
|
|
975
|
+
const nativeRule = nativeKeyframesRules.get(animationName);
|
|
976
|
+
if (!nativeRule) return false;
|
|
977
|
+
const animationRule = new AnimationRule(animationName, keyframes, this);
|
|
978
|
+
animationRule.native = nativeRule;
|
|
979
|
+
this.animationsNonLayer.rules.push(animationRule);
|
|
980
|
+
this.rules.push(animationRule);
|
|
981
|
+
hydratedAnimationNames.add(animationName);
|
|
982
|
+
}
|
|
983
|
+
for (const animationName of nativeKeyframesRules.keys()){
|
|
984
|
+
if (!hydratedAnimationNames.has(animationName)) return false;
|
|
985
|
+
}
|
|
986
|
+
return true;
|
|
987
|
+
}
|
|
988
|
+
hydrateHydrationManifestLayer(layer, nativeLayerRule, manifestRules, result) {
|
|
989
|
+
const expectedRuleCount = manifestRules.reduce((count, rule)=>count + (rule.nodes?.length || 1), 0);
|
|
990
|
+
if (expectedRuleCount !== nativeLayerRule.cssRules.length) return false;
|
|
991
|
+
layer.native = nativeLayerRule;
|
|
992
|
+
let nativeIndex = 0;
|
|
993
|
+
for (const manifestRule of manifestRules){
|
|
994
|
+
const hydratedRule = new HydratedGeneratedRule(manifestRule, layer);
|
|
995
|
+
const nodes = hydratedRule.nodes;
|
|
996
|
+
if (nodes?.length) {
|
|
997
|
+
for (const node of nodes){
|
|
998
|
+
node.native = nativeLayerRule.cssRules.item(nativeIndex++) || undefined;
|
|
999
|
+
}
|
|
1000
|
+
} else {
|
|
1001
|
+
hydratedRule.native = nativeLayerRule.cssRules.item(nativeIndex++) || undefined;
|
|
1002
|
+
}
|
|
1003
|
+
layer.rules.push(hydratedRule);
|
|
1004
|
+
layer.insertVariables(hydratedRule);
|
|
1005
|
+
layer.insertAnimations(hydratedRule);
|
|
1006
|
+
this.registerHydratedClassRule(manifestRule.className, hydratedRule);
|
|
1007
|
+
result.allUtilities.push(hydratedRule);
|
|
1008
|
+
}
|
|
1009
|
+
if (layer.rules.length && !this.rules.includes(layer)) {
|
|
1010
|
+
this.rules.push(layer);
|
|
1011
|
+
}
|
|
1012
|
+
return true;
|
|
1013
|
+
}
|
|
278
1014
|
hydrate(nativeLayerRules) {
|
|
279
|
-
|
|
280
|
-
|
|
1015
|
+
this.hydrationFailureReason = undefined;
|
|
1016
|
+
if (this.hydrationManifest?.version !== 1 || !Array.isArray(this.hydrationManifest.rules)) {
|
|
1017
|
+
return this.failHydration('Missing or invalid hydration manifest.');
|
|
1018
|
+
}
|
|
1019
|
+
if (!this.hydrationManifest.rules.length) {
|
|
1020
|
+
return this.failHydration(`Hydration manifest has no generated rules for ${MASTER_CSS_RUNTIME_STYLE_SELECTOR}.`);
|
|
1021
|
+
}
|
|
281
1022
|
const result = {
|
|
282
|
-
|
|
1023
|
+
allUtilities: []
|
|
283
1024
|
};
|
|
1025
|
+
const manifestLayerRules = this.getHydrationManifestLayerRules();
|
|
1026
|
+
const nativeUtilityLayerRules = new Map();
|
|
1027
|
+
const nativeKeyframesRules = new Map();
|
|
1028
|
+
let nativeThemeLayer;
|
|
284
1029
|
for(let i = 0; i < nativeLayerRules.length; i++){
|
|
285
1030
|
const eachNativeCSSRule = nativeLayerRules[i];
|
|
286
1031
|
if (eachNativeCSSRule.constructor.name === 'CSSLayerBlockRule') {
|
|
287
1032
|
const eachCSSLayerRule = eachNativeCSSRule;
|
|
288
|
-
if (
|
|
289
|
-
this.
|
|
290
|
-
|
|
291
|
-
const unresolvedCSSRules = new Map();
|
|
292
|
-
for (const rule of eachCSSLayerRule.cssRules){
|
|
293
|
-
// trim() for fix the firefox bug that the cssText ends with \n\n
|
|
294
|
-
unresolvedCSSRules.set(rule.cssText.trim(), rule);
|
|
295
|
-
}
|
|
296
|
-
for (const cssRule of eachCSSLayerRule.cssRules){
|
|
297
|
-
if (!unresolvedCSSRules.has(cssRule.cssText)) continue;
|
|
298
|
-
const variableCSSRule = cssRule.constructor.name === 'CSSMediaRule' ? cssRule.cssRules[0] : cssRule;
|
|
299
|
-
const variableName = variableCSSRule.style[0].slice(2);
|
|
300
|
-
const variable = this.variables.get(variableName);
|
|
301
|
-
if (!variable) continue;
|
|
302
|
-
variableRule = new VariableRule(variableName, variable, this);
|
|
303
|
-
this.themeLayer.rules.push(variableRule);
|
|
304
|
-
this.themeLayer.tokenCounts.set(variableRule.name, 0);
|
|
305
|
-
variableRule.nodes.forEach((node)=>{
|
|
306
|
-
const checkRuleIndex = checkSheet.insertRule(node.text);
|
|
307
|
-
const checkNodeNativeRule = checkSheet.cssRules.item(checkRuleIndex);
|
|
308
|
-
if (checkNodeNativeRule) {
|
|
309
|
-
const checkNodeNativeRuleText = checkNodeNativeRule.cssText.trim();
|
|
310
|
-
const match = unresolvedCSSRules.get(checkNodeNativeRuleText);
|
|
311
|
-
if (match) {
|
|
312
|
-
node.native = match;
|
|
313
|
-
unresolvedCSSRules.delete(checkNodeNativeRuleText);
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
if (this.themeLayer.rules.length) this.rules.push(this.themeLayer);
|
|
320
|
-
} else {
|
|
321
|
-
cssLayerRules.push(eachCSSLayerRule);
|
|
322
|
-
}
|
|
323
|
-
} else if (eachNativeCSSRule.constructor.name === 'CSSLayerStatementRule') {
|
|
324
|
-
this.layerStatementRule.native = eachNativeCSSRule;
|
|
325
|
-
} else if (eachNativeCSSRule.constructor.name === 'CSSKeyframesRule') {
|
|
326
|
-
const nativeKeyframsRule = eachNativeCSSRule;
|
|
327
|
-
const keyframes = this.animations.get(nativeKeyframsRule.name);
|
|
328
|
-
if (!keyframes) continue;
|
|
329
|
-
const animationRule = new AnimationRule(nativeKeyframsRule.name, keyframes, this);
|
|
330
|
-
animationRule.native = nativeKeyframsRule;
|
|
331
|
-
this.animationsNonLayer.rules.push(animationRule);
|
|
332
|
-
this.rules.push(animationRule);
|
|
333
|
-
this.animationsNonLayer.tokenCounts.set(animationRule.name, 0);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
for (const eachCSSLayerRule of cssLayerRules){
|
|
337
|
-
let layer;
|
|
338
|
-
switch(eachCSSLayerRule.name){
|
|
339
|
-
case 'base':
|
|
340
|
-
layer = this.baseLayer;
|
|
341
|
-
break;
|
|
342
|
-
case 'preset':
|
|
343
|
-
layer = this.presetLayer;
|
|
344
|
-
break;
|
|
345
|
-
case 'components':
|
|
346
|
-
layer = this.componentsLayer;
|
|
347
|
-
break;
|
|
348
|
-
case 'general':
|
|
349
|
-
layer = this.generalLayer;
|
|
350
|
-
break;
|
|
351
|
-
default:
|
|
352
|
-
console.error(`Cannot recognize the layer \`${eachCSSLayerRule.name}\`. (https://rc.css.master.co/messages/hydration-errors)`);
|
|
353
|
-
continue;
|
|
354
|
-
}
|
|
355
|
-
layer.native = eachCSSLayerRule;
|
|
356
|
-
const unresolvedCSSRules = new Map();
|
|
357
|
-
for (const rule of eachCSSLayerRule.cssRules){
|
|
358
|
-
// trim() for fix the firefox bug that the cssText ends with \n\n
|
|
359
|
-
unresolvedCSSRules.set(rule.cssText.trim(), rule);
|
|
360
|
-
}
|
|
361
|
-
for (const eachNativeLayerRule of eachCSSLayerRule.cssRules){
|
|
362
|
-
if (!unresolvedCSSRules.has(eachNativeLayerRule.cssText)) continue;
|
|
363
|
-
const selectorText = this.getSelectorText(eachNativeLayerRule);
|
|
364
|
-
if (!selectorText) {
|
|
365
|
-
console.error(`Cannot get the selector text from \`${eachNativeLayerRule.cssText}\`. (${layer.name}) (https://rc.css.master.co/messages/hydration-errors)`);
|
|
1033
|
+
if (eachCSSLayerRule.name === 'theme') {
|
|
1034
|
+
if (nativeThemeLayer) return this.failHydration(`Duplicate theme layer in ${MASTER_CSS_RUNTIME_STYLE_SELECTOR}.`);
|
|
1035
|
+
nativeThemeLayer = eachCSSLayerRule;
|
|
366
1036
|
continue;
|
|
367
1037
|
}
|
|
368
|
-
const
|
|
369
|
-
if (
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
layer.insertVariables(createdRule);
|
|
373
|
-
layer.insertAnimations(createdRule);
|
|
374
|
-
result.allSyntaxRules.push(createdRule);
|
|
375
|
-
try {
|
|
376
|
-
const checkRuleIndex = checkSheet.insertRule(createdRule.text);
|
|
377
|
-
const checkNodeNativeRule = checkSheet.cssRules.item(checkRuleIndex);
|
|
378
|
-
if (checkNodeNativeRule) {
|
|
379
|
-
const checkNodeNativeRuleText = checkNodeNativeRule.cssText.trim();
|
|
380
|
-
const match = unresolvedCSSRules.get(checkNodeNativeRuleText);
|
|
381
|
-
if (match) {
|
|
382
|
-
createdRule.native = match;
|
|
383
|
-
unresolvedCSSRules.delete(checkNodeNativeRuleText);
|
|
384
|
-
continue;
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
console.error(`Cannot retrieve CSS rule for \`${createdRule.text}\`. (${layer.name}) (https://rc.css.master.co/messages/hydration-errors)`);
|
|
388
|
-
} catch (error) {
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
} else {
|
|
392
|
-
console.error(`Cannot recognize \`${eachNativeLayerRule.cssText}\`. (${layer.name}) (https://rc.css.master.co/messages/hydration-errors)`);
|
|
1038
|
+
const layer = this.getUtilityLayerByName(eachCSSLayerRule.name);
|
|
1039
|
+
if (!layer) return this.failHydration(`Unknown layer \`${eachCSSLayerRule.name}\` in ${MASTER_CSS_RUNTIME_STYLE_SELECTOR}.`);
|
|
1040
|
+
if (nativeUtilityLayerRules.has(layer.name)) {
|
|
1041
|
+
return this.failHydration(`Duplicate layer \`${eachCSSLayerRule.name}\` in ${MASTER_CSS_RUNTIME_STYLE_SELECTOR}.`);
|
|
393
1042
|
}
|
|
1043
|
+
nativeUtilityLayerRules.set(layer.name, eachCSSLayerRule);
|
|
1044
|
+
} else if (eachNativeCSSRule.constructor.name === 'CSSKeyframesRule') {
|
|
1045
|
+
const nativeKeyframesRule = eachNativeCSSRule;
|
|
1046
|
+
nativeKeyframesRules.set(nativeKeyframesRule.name, nativeKeyframesRule);
|
|
1047
|
+
} else {
|
|
1048
|
+
return this.failHydration(`Unknown top-level rule \`${eachNativeCSSRule.cssText}\` in ${MASTER_CSS_RUNTIME_STYLE_SELECTOR}.`);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
for (const [layerName, manifestRules] of manifestLayerRules){
|
|
1052
|
+
const nativeLayerRule = nativeUtilityLayerRules.get(layerName);
|
|
1053
|
+
if (!nativeLayerRule) return this.failHydration(`Missing layer \`${layerName}\` in ${MASTER_CSS_RUNTIME_STYLE_SELECTOR}.`);
|
|
1054
|
+
const expectedRuleCount = manifestRules.reduce((count, rule)=>count + (rule.nodes?.length || 1), 0);
|
|
1055
|
+
if (expectedRuleCount !== nativeLayerRule.cssRules.length) {
|
|
1056
|
+
return this.failHydration(`Layer \`${layerName}\` does not match the hydration manifest.`);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
for (const [layerName, nativeLayerRule] of nativeUtilityLayerRules){
|
|
1060
|
+
if (!manifestLayerRules.has(layerName) && nativeLayerRule.cssRules.length) {
|
|
1061
|
+
return this.failHydration(`Layer \`${layerName}\` has no hydration manifest rules.`);
|
|
394
1062
|
}
|
|
395
|
-
|
|
1063
|
+
}
|
|
1064
|
+
if (!this.hydrateHydrationManifestVariables(nativeThemeLayer)) {
|
|
1065
|
+
return this.failHydration('Theme layer does not match the hydration manifest.');
|
|
1066
|
+
}
|
|
1067
|
+
if (!this.hydrateHydrationManifestAnimations(nativeKeyframesRules)) {
|
|
1068
|
+
return this.failHydration('Keyframes do not match the hydration manifest.');
|
|
1069
|
+
}
|
|
1070
|
+
for (const [layerName, nativeLayerRule] of nativeUtilityLayerRules){
|
|
1071
|
+
const manifestRules = manifestLayerRules.get(layerName);
|
|
1072
|
+
if (!manifestRules?.length) continue;
|
|
1073
|
+
const layer = this.getUtilityLayerByName(layerName);
|
|
1074
|
+
this.hydrateHydrationManifestLayer(layer, nativeLayerRule, manifestRules, result);
|
|
396
1075
|
}
|
|
397
1076
|
globalThis.__MASTER_CSS_DEVTOOLS_HOOK__?.emit('runtime:hydrated', {
|
|
398
1077
|
cssRuntime: this,
|
|
@@ -400,13 +1079,6 @@ class CSSRuntime extends MasterCSS {
|
|
|
400
1079
|
});
|
|
401
1080
|
return result;
|
|
402
1081
|
}
|
|
403
|
-
getSelectorText(cssRule) {
|
|
404
|
-
if (cssRule instanceof CSSStyleRule) {
|
|
405
|
-
return cssRule.selectorText;
|
|
406
|
-
} else if (cssRule instanceof CSSGroupingRule) {
|
|
407
|
-
return this.getSelectorText(cssRule.cssRules.item(0));
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
1082
|
disconnect() {
|
|
411
1083
|
if (!this.observing) return;
|
|
412
1084
|
if (this.observer) {
|
|
@@ -416,7 +1088,9 @@ class CSSRuntime extends MasterCSS {
|
|
|
416
1088
|
// @ts-ignore
|
|
417
1089
|
this.observing = false;
|
|
418
1090
|
this.reset();
|
|
1091
|
+
this.loadManifest(this.manifest);
|
|
419
1092
|
this.classCounts.clear();
|
|
1093
|
+
this.classTracker.reset();
|
|
420
1094
|
if (!this.progressive) {
|
|
421
1095
|
this.style?.remove();
|
|
422
1096
|
this.style = null;
|
|
@@ -426,27 +1100,28 @@ class CSSRuntime extends MasterCSS {
|
|
|
426
1100
|
});
|
|
427
1101
|
return this;
|
|
428
1102
|
}
|
|
429
|
-
refresh(
|
|
1103
|
+
refresh(manifest = this.manifest) {
|
|
430
1104
|
if (!this.observing || !this.style.sheet) return this;
|
|
431
|
-
|
|
1105
|
+
const cssRules = this.style.sheet.cssRules;
|
|
1106
|
+
for(let i = cssRules.length - 1; i >= 0; i--){
|
|
432
1107
|
this.style.sheet.deleteRule(i);
|
|
433
1108
|
}
|
|
434
|
-
super.refresh(
|
|
1109
|
+
super.refresh(manifest);
|
|
435
1110
|
/**
|
|
436
|
-
*
|
|
1111
|
+
* Recreate rules from the current class names against the latest manifest.
|
|
437
1112
|
* 所以 refresh 過後 rules 可能會變多也可能會變少
|
|
438
1113
|
*/ this.classCounts.forEach((_, className)=>{
|
|
439
1114
|
this.add(className);
|
|
440
1115
|
});
|
|
441
1116
|
globalThis.__MASTER_CSS_DEVTOOLS_HOOK__?.emit('runtime:refreshed', {
|
|
442
1117
|
cssRuntime: this,
|
|
443
|
-
|
|
1118
|
+
manifest
|
|
444
1119
|
});
|
|
445
1120
|
return this;
|
|
446
1121
|
}
|
|
447
1122
|
destroy() {
|
|
448
1123
|
this.disconnect();
|
|
449
|
-
|
|
1124
|
+
this.unregister();
|
|
450
1125
|
globalThis.__MASTER_CSS_DEVTOOLS_HOOK__?.emit('runtime:destroyed', {
|
|
451
1126
|
cssRuntime: this
|
|
452
1127
|
});
|
|
@@ -457,18 +1132,52 @@ class CSSRuntime extends MasterCSS {
|
|
|
457
1132
|
registerGlobal(CSSRuntime);
|
|
458
1133
|
})(CSSRuntime);
|
|
459
1134
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
1135
|
+
function resolveManifest(host, manifest) {
|
|
1136
|
+
return typeof manifest === 'function' ? manifest(host) : manifest;
|
|
1137
|
+
}
|
|
1138
|
+
function resolveRoot(host, root) {
|
|
1139
|
+
return typeof root === 'function' ? root(host) : root || host.shadowRoot;
|
|
1140
|
+
}
|
|
1141
|
+
function resolveEmittedGlobals(host, emittedGlobals) {
|
|
1142
|
+
return typeof emittedGlobals === 'function' ? emittedGlobals(host) : emittedGlobals;
|
|
1143
|
+
}
|
|
1144
|
+
function resolveHydrationManifest(host, hydrationManifest) {
|
|
1145
|
+
return typeof hydrationManifest === 'function' ? hydrationManifest(host) : hydrationManifest;
|
|
1146
|
+
}
|
|
1147
|
+
function cssRuntime(options) {
|
|
1148
|
+
return function(target) {
|
|
1149
|
+
const connectedCallback = target.prototype.connectedCallback;
|
|
1150
|
+
const disconnectedCallback = target.prototype.disconnectedCallback;
|
|
1151
|
+
return class extends target {
|
|
1152
|
+
cssRuntime;
|
|
1153
|
+
connectedCallback() {
|
|
1154
|
+
connectedCallback?.call(this);
|
|
1155
|
+
const root = resolveRoot(this, options.root);
|
|
1156
|
+
if (!root) {
|
|
1157
|
+
throw new Error('`@cssRuntime()` requires a shadow root. Provide `options.root` or create a shadow root before `connectedCallback()` finishes.');
|
|
1158
|
+
}
|
|
1159
|
+
const cssRuntime = CSSRuntime.create({
|
|
1160
|
+
manifest: resolveManifest(this, options.manifest),
|
|
1161
|
+
root,
|
|
1162
|
+
emittedGlobals: resolveEmittedGlobals(this, options.emittedGlobals),
|
|
1163
|
+
hydrationManifest: resolveHydrationManifest(this, options.hydrationManifest)
|
|
1164
|
+
});
|
|
1165
|
+
this.cssRuntime = cssRuntime;
|
|
1166
|
+
if (cssRuntime.needsHydrationManifest()) {
|
|
1167
|
+
void cssRuntime.loadHydrationManifest().then(()=>{
|
|
1168
|
+
if (this.cssRuntime === cssRuntime) cssRuntime.observe();
|
|
1169
|
+
});
|
|
1170
|
+
} else {
|
|
1171
|
+
cssRuntime.observe();
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
disconnectedCallback() {
|
|
1175
|
+
disconnectedCallback?.call(this);
|
|
1176
|
+
this.cssRuntime?.destroy();
|
|
1177
|
+
this.cssRuntime = undefined;
|
|
1178
|
+
}
|
|
1179
|
+
};
|
|
1180
|
+
};
|
|
472
1181
|
}
|
|
473
1182
|
|
|
474
|
-
export { CSSRuntime,
|
|
1183
|
+
export { CSSRuntime, RuntimeUtilityLayer, cssRuntime, CSSRuntime as default };
|