@master/css-runtime 2.0.0-rc.8 → 2.0.0-rc.81
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 +99 -8
- package/dist/class-tracker.d.ts +14 -0
- package/dist/core.d.ts +110 -22
- package/dist/css-runtime.d.ts +20 -0
- package/dist/debuggers/class-count.d.ts +6 -0
- package/dist/debuggers/index.d.ts +7 -0
- package/dist/debuggers/lifecycle.d.ts +13 -0
- package/dist/debuggers/log.d.ts +19 -0
- package/dist/default-manifest.json +1 -0
- package/dist/generated-rule.d.ts +30 -0
- package/dist/global.min.d.ts +1 -0
- package/dist/global.min.js +3 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.development.d.ts +6 -0
- package/dist/index.development.js +1403 -0
- package/dist/index.js +1403 -0
- package/dist/layer.d.ts +20 -0
- package/dist/native-declaration.d.ts +6 -0
- package/dist/register-global.d.ts +10 -0
- package/dist/theme-layer.d.ts +33 -0
- package/dist/types/hydrate-result.d.ts +9 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/utility-layer.d.ts +16 -0
- package/dist/utils/find-native-css-rule-index.d.ts +4 -0
- package/package.json +71 -1
- package/dist/iife.bundle.js +0 -1
- package/dist/iife.d.ts +0 -1
- package/dist/index.bundle.js +0 -1
- package/dist/index.bundle.mjs +0 -1
- package/dist/init-css-runtime.d.ts +0 -9
- package/dist/types/global.d.ts +0 -8
|
@@ -0,0 +1,1403 @@
|
|
|
1
|
+
import { AnimationRule, Layer, MasterCSS, ThemeLayer, VariableRule, compareRulePriority } 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";
|
|
4
|
+
//#region src/debuggers/log.ts
|
|
5
|
+
const createLogger = (type) => {
|
|
6
|
+
return (message, ...args) => {
|
|
7
|
+
if (!type) return console.log(message, ...args);
|
|
8
|
+
if (console[type]) console[type](`%c[Master CSS]%c ${message}`, "color: gray;", "color: inherit;", ...args);
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
const loggers = {
|
|
12
|
+
info: createLogger("info"),
|
|
13
|
+
debug: createLogger("debug"),
|
|
14
|
+
warn: console.warn,
|
|
15
|
+
error: console.error,
|
|
16
|
+
trace: console.trace,
|
|
17
|
+
table: console.table,
|
|
18
|
+
group: console.group,
|
|
19
|
+
groupCollapsed: console.group,
|
|
20
|
+
groupEnd: console.groupEnd,
|
|
21
|
+
time: console.time,
|
|
22
|
+
timeEnd: console.timeEnd
|
|
23
|
+
};
|
|
24
|
+
const log = createLogger();
|
|
25
|
+
Object.assign(log, loggers);
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/debuggers/class-count.ts
|
|
28
|
+
function debugRuntimeMutation(records, classCounts, cssRuntime) {
|
|
29
|
+
const actualClassCounts = {};
|
|
30
|
+
let errored = false;
|
|
31
|
+
const resolveClass = (className) => {
|
|
32
|
+
if (Object.prototype.hasOwnProperty.call(actualClassCounts, className)) actualClassCounts[className]++;
|
|
33
|
+
else actualClassCounts[className] = 1;
|
|
34
|
+
};
|
|
35
|
+
(cssRuntime.root.constructor.name === "HTMLDocument" ? cssRuntime.host : cssRuntime.container).querySelectorAll("[class]").forEach((element) => {
|
|
36
|
+
element.classList.forEach(resolveClass);
|
|
37
|
+
});
|
|
38
|
+
cssRuntime.host.classList.forEach(resolveClass);
|
|
39
|
+
for (const className in actualClassCounts) {
|
|
40
|
+
const eachCount = cssRuntime.classCounts.get(className);
|
|
41
|
+
const eachActualCount = actualClassCounts[className];
|
|
42
|
+
if (eachCount !== eachActualCount) {
|
|
43
|
+
log.error(`Class count mismatch for \`${className}\` (expected ${eachActualCount}) (received ${eachCount})`);
|
|
44
|
+
errored = true;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
cssRuntime.classCounts.forEach((eachCount, className) => {
|
|
48
|
+
if (!Object.prototype.hasOwnProperty.call(actualClassCounts, className)) {
|
|
49
|
+
log.error(`Class count mismatch for \`${className}\` (expected 0) (received ${eachCount})`);
|
|
50
|
+
errored = true;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
if (errored) {
|
|
54
|
+
log.debug("Records:", records);
|
|
55
|
+
log.debug("Serialized Records:", serializeMutations(records));
|
|
56
|
+
log.debug("Counts:", classCounts);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function serializeMutations(mutations) {
|
|
60
|
+
return mutations.map((m) => {
|
|
61
|
+
const target = m.target;
|
|
62
|
+
return {
|
|
63
|
+
type: m.type,
|
|
64
|
+
attributeName: m.attributeName,
|
|
65
|
+
oldValue: m.oldValue,
|
|
66
|
+
target: {
|
|
67
|
+
tagName: target.tagName,
|
|
68
|
+
id: target.id,
|
|
69
|
+
classList: Array.from(target.classList)
|
|
70
|
+
},
|
|
71
|
+
added: Array.from(m.addedNodes).filter((n) => n.nodeType === 1).map((el) => ({
|
|
72
|
+
tagName: el.tagName,
|
|
73
|
+
id: el.id,
|
|
74
|
+
classList: Array.from(el.classList)
|
|
75
|
+
})),
|
|
76
|
+
removed: Array.from(m.removedNodes).filter((n) => n.nodeType === 1).map((el) => ({
|
|
77
|
+
tagName: el.tagName,
|
|
78
|
+
id: el.id,
|
|
79
|
+
classList: Array.from(el.classList)
|
|
80
|
+
}))
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/debuggers/lifecycle.ts
|
|
86
|
+
function debugRuntimeCreated(cssRuntime) {
|
|
87
|
+
log.debug("created", cssRuntime);
|
|
88
|
+
}
|
|
89
|
+
function debugRuntimeHydrated(cssRuntime, result) {
|
|
90
|
+
log.debug("hydrated", cssRuntime, result);
|
|
91
|
+
}
|
|
92
|
+
function debugRuntimeObserved(cssRuntime) {
|
|
93
|
+
log.debug("observed", cssRuntime);
|
|
94
|
+
if (cssRuntime.progressive) {
|
|
95
|
+
log.info("Progressive rendering is adopted.");
|
|
96
|
+
log.debug("pre-rendered CSS", cssRuntime.style);
|
|
97
|
+
} else log.info("Runtime rendering is adopted.");
|
|
98
|
+
}
|
|
99
|
+
function debugRuntimeRefreshed(cssRuntime, manifest) {
|
|
100
|
+
log.debug("refreshed", cssRuntime, manifest);
|
|
101
|
+
}
|
|
102
|
+
function debugRuntimeDisconnected(cssRuntime) {
|
|
103
|
+
log.debug("disconnected", cssRuntime);
|
|
104
|
+
}
|
|
105
|
+
function debugRuntimeDestroyed(cssRuntime) {
|
|
106
|
+
log.debug("destroyed", cssRuntime);
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
//#region src/debuggers/index.ts
|
|
110
|
+
function startDebuggers() {
|
|
111
|
+
log.info("Debug mode is enabled by default in development.");
|
|
112
|
+
}
|
|
113
|
+
//#endregion
|
|
114
|
+
//#region src/register-global.ts
|
|
115
|
+
function registerGlobal(CSSRuntime) {
|
|
116
|
+
if (!globalThis.MasterCSSRuntime) globalThis.MasterCSSRuntime = CSSRuntime;
|
|
117
|
+
startDebuggers();
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
120
|
+
//#region src/utils/find-native-css-rule-index.ts
|
|
121
|
+
function findNativeCSSRuleIndex(cssRules, nativeRule) {
|
|
122
|
+
for (let i = 0; i < cssRules.length; i++) if (cssRules[i] === nativeRule) return i;
|
|
123
|
+
return -1;
|
|
124
|
+
}
|
|
125
|
+
//#endregion
|
|
126
|
+
//#region src/layer.ts
|
|
127
|
+
function getRuleNodes(rule) {
|
|
128
|
+
const nodes = rule.nodes;
|
|
129
|
+
return Array.isArray(nodes) ? nodes : void 0;
|
|
130
|
+
}
|
|
131
|
+
var RuntimeLayer = class extends Layer {
|
|
132
|
+
name;
|
|
133
|
+
cssRuntime;
|
|
134
|
+
rules = [];
|
|
135
|
+
tokenCounts = /* @__PURE__ */ new Map();
|
|
136
|
+
native = null;
|
|
137
|
+
constructor(name, cssRuntime) {
|
|
138
|
+
super(name, cssRuntime);
|
|
139
|
+
this.name = name;
|
|
140
|
+
this.cssRuntime = cssRuntime;
|
|
141
|
+
}
|
|
142
|
+
attach() {
|
|
143
|
+
super.attach();
|
|
144
|
+
const nativeSheet = this.cssRuntime.style?.sheet;
|
|
145
|
+
if (nativeSheet && !this.native?.parentStyleSheet) {
|
|
146
|
+
const insertedIndex = nativeSheet.insertRule(this.text, nativeSheet.cssRules.length);
|
|
147
|
+
this.native = nativeSheet.cssRules.item(insertedIndex);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
insert(rule, index = this.rules.length) {
|
|
151
|
+
const hadNative = !!this.native?.parentStyleSheet;
|
|
152
|
+
const wasEmpty = this.rules.length === 0;
|
|
153
|
+
const insertedIndex = super.insert(rule, index);
|
|
154
|
+
if (insertedIndex === void 0 || !this.native) return;
|
|
155
|
+
const nativeInsertIndex = this.getNativeInsertIndex(insertedIndex);
|
|
156
|
+
const wasAttachedWithRule = !hadNative && wasEmpty;
|
|
157
|
+
const insertRuleSafely = (text, position) => {
|
|
158
|
+
if (wasAttachedWithRule) return this.native.cssRules.item(position);
|
|
159
|
+
else try {
|
|
160
|
+
const insertedIndex = this.native.insertRule(text, position);
|
|
161
|
+
return this.native.cssRules.item(insertedIndex);
|
|
162
|
+
} catch (error) {
|
|
163
|
+
console.error(error, rule);
|
|
164
|
+
/**
|
|
165
|
+
* If the rule is invalid, remove it from the rules array.
|
|
166
|
+
* It's important to note that the rule may break the entire CSS runtime,
|
|
167
|
+
*/
|
|
168
|
+
this.rules.splice(insertedIndex, 1);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
const nodes = getRuleNodes(rule);
|
|
173
|
+
if (nodes) {
|
|
174
|
+
let currentIndex = nativeInsertIndex;
|
|
175
|
+
nodes.forEach((node) => {
|
|
176
|
+
node.native = insertRuleSafely(node.text, currentIndex);
|
|
177
|
+
if (node.native) currentIndex++;
|
|
178
|
+
});
|
|
179
|
+
} else rule.native = insertRuleSafely(rule.text, nativeInsertIndex);
|
|
180
|
+
return insertedIndex;
|
|
181
|
+
}
|
|
182
|
+
getNativeInsertIndex(index) {
|
|
183
|
+
let nativeIndex = 0;
|
|
184
|
+
for (let i = 0; i < index; i++) {
|
|
185
|
+
const rule = this.rules[i];
|
|
186
|
+
nativeIndex += getRuleNodes(rule)?.length ?? 1;
|
|
187
|
+
}
|
|
188
|
+
return nativeIndex;
|
|
189
|
+
}
|
|
190
|
+
detach() {
|
|
191
|
+
super.detach();
|
|
192
|
+
const nativeSheet = this.cssRuntime.style?.sheet;
|
|
193
|
+
if (nativeSheet && this.native?.parentStyleSheet) {
|
|
194
|
+
const foundIndex = findNativeCSSRuleIndex(nativeSheet.cssRules, this.native);
|
|
195
|
+
if (foundIndex !== -1) nativeSheet.deleteRule(foundIndex);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
delete(key) {
|
|
199
|
+
const deletedRule = super.delete(key);
|
|
200
|
+
if (!deletedRule || !this.native) return;
|
|
201
|
+
const deleteRuleSafely = (rule) => {
|
|
202
|
+
if (!rule) return;
|
|
203
|
+
const foundIndex = findNativeCSSRuleIndex(this.native.cssRules, rule);
|
|
204
|
+
if (foundIndex !== -1) try {
|
|
205
|
+
this.native.deleteRule(foundIndex);
|
|
206
|
+
} catch (error) {
|
|
207
|
+
console.error(error, rule);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
const nodes = getRuleNodes(deletedRule);
|
|
211
|
+
if (nodes) nodes.forEach((node) => deleteRuleSafely(node.native));
|
|
212
|
+
else deleteRuleSafely(deletedRule.native);
|
|
213
|
+
return deletedRule;
|
|
214
|
+
}
|
|
215
|
+
reset() {
|
|
216
|
+
super.reset();
|
|
217
|
+
const nativeSheet = this.cssRuntime.style?.sheet;
|
|
218
|
+
if (this.native && nativeSheet) {
|
|
219
|
+
const foundIndex = findNativeCSSRuleIndex(nativeSheet.cssRules, this.native);
|
|
220
|
+
if (foundIndex !== -1) nativeSheet.deleteRule(foundIndex);
|
|
221
|
+
this.native = null;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
//#endregion
|
|
226
|
+
//#region src/utility-layer.ts
|
|
227
|
+
function findUtilityInsertIndex(rules, utility) {
|
|
228
|
+
let low = 0;
|
|
229
|
+
let high = rules.length;
|
|
230
|
+
while (low < high) {
|
|
231
|
+
const mid = low + high >> 1;
|
|
232
|
+
const rule = rules[mid];
|
|
233
|
+
if (!("priority" in rule)) return findUtilityInsertIndexLinear(rules, utility);
|
|
234
|
+
if (compareRulePriority(utility, rule) < 0) high = mid;
|
|
235
|
+
else low = mid + 1;
|
|
236
|
+
}
|
|
237
|
+
return low;
|
|
238
|
+
}
|
|
239
|
+
function findUtilityInsertIndexLinear(rules, utility) {
|
|
240
|
+
for (let i = 0; i < rules.length; i++) {
|
|
241
|
+
const rule = rules[i];
|
|
242
|
+
if (!("priority" in rule)) continue;
|
|
243
|
+
if (compareRulePriority(utility, rule) < 0) return i;
|
|
244
|
+
}
|
|
245
|
+
return rules.length;
|
|
246
|
+
}
|
|
247
|
+
var RuntimeUtilityLayer = class extends RuntimeLayer {
|
|
248
|
+
rules = [];
|
|
249
|
+
insert(utility) {
|
|
250
|
+
if (this.get(utility.key)) return;
|
|
251
|
+
if ("valid" in utility && !utility.valid) return;
|
|
252
|
+
let index = this.rules.length;
|
|
253
|
+
if ("priority" in utility) index = findUtilityInsertIndex(this.rules, utility);
|
|
254
|
+
const insertedIndex = super.insert(utility, index);
|
|
255
|
+
if (insertedIndex === void 0) return;
|
|
256
|
+
this.insertVariables(utility);
|
|
257
|
+
this.insertAnimations(utility);
|
|
258
|
+
return insertedIndex;
|
|
259
|
+
}
|
|
260
|
+
delete(key) {
|
|
261
|
+
const utility = super.delete(key);
|
|
262
|
+
if (!utility) return;
|
|
263
|
+
const deleteLayerToken = (layerToken, layer, visited = /* @__PURE__ */ new Set()) => {
|
|
264
|
+
if (visited.has(layerToken)) return;
|
|
265
|
+
visited.add(layerToken);
|
|
266
|
+
const count = layer.tokenCounts.get(layerToken) ?? 0;
|
|
267
|
+
const variable = layer === this.css.themeLayer ? this.css.variables.get(layerToken) : void 0;
|
|
268
|
+
if (count <= 1) {
|
|
269
|
+
const deletedRule = layer.delete(layerToken);
|
|
270
|
+
layer.tokenCounts.delete(layerToken);
|
|
271
|
+
variable?.dependencies?.forEach((dependency) => deleteLayerToken(dependency, layer, visited));
|
|
272
|
+
return deletedRule;
|
|
273
|
+
} else {
|
|
274
|
+
layer.tokenCounts.set(layerToken, count - 1);
|
|
275
|
+
variable?.dependencies?.forEach((dependency) => deleteLayerToken(dependency, layer, visited));
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
if ("variableNames" in utility) utility.variableNames?.forEach((eachVariableName) => {
|
|
279
|
+
deleteLayerToken(eachVariableName, this.css.themeLayer);
|
|
280
|
+
});
|
|
281
|
+
if ("animationNames" in utility) utility.animationNames?.forEach((eachAnimationName) => {
|
|
282
|
+
const deletedAnimationRule = deleteLayerToken(eachAnimationName, this.css.animationsNonLayer);
|
|
283
|
+
const variableNames = deletedAnimationRule && "variableNames" in deletedAnimationRule ? deletedAnimationRule.variableNames : void 0;
|
|
284
|
+
if (variableNames) variableNames.forEach((eachVariableName) => {
|
|
285
|
+
deleteLayerToken(eachVariableName, this.css.themeLayer);
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
return utility;
|
|
289
|
+
}
|
|
290
|
+
insertVariables(utility) {
|
|
291
|
+
if (!("variableNames" in utility)) return;
|
|
292
|
+
const insertVariable = (eachVariableName, visited = /* @__PURE__ */ new Set()) => {
|
|
293
|
+
if (visited.has(eachVariableName)) return;
|
|
294
|
+
visited.add(eachVariableName);
|
|
295
|
+
const variable = this.css.variables.get(eachVariableName);
|
|
296
|
+
if (!variable || variable.inline) return;
|
|
297
|
+
if (this.css.themeLayer.rules.find(({ name }) => name === eachVariableName) || this.css.isEmittedGlobalsVariable(eachVariableName)) {
|
|
298
|
+
const count = this.css.themeLayer.tokenCounts.get(eachVariableName) || 0;
|
|
299
|
+
this.css.themeLayer.tokenCounts.set(eachVariableName, count + 1);
|
|
300
|
+
} else {
|
|
301
|
+
const newRule = new VariableRule(eachVariableName, variable, this.css);
|
|
302
|
+
this.css.themeLayer.insert(newRule);
|
|
303
|
+
this.css.themeLayer.tokenCounts.set(eachVariableName, 1);
|
|
304
|
+
}
|
|
305
|
+
variable.dependencies?.forEach((dependency) => insertVariable(dependency, visited));
|
|
306
|
+
};
|
|
307
|
+
utility.variableNames?.forEach((eachVariableName) => insertVariable(eachVariableName));
|
|
308
|
+
}
|
|
309
|
+
insertAnimations(utility) {
|
|
310
|
+
if (!("animationNames" in utility)) return;
|
|
311
|
+
utility.animationNames?.forEach((eachAnimationName) => {
|
|
312
|
+
const animationRule = this.css.animationsNonLayer.rules.find(({ name }) => name === eachAnimationName);
|
|
313
|
+
if (animationRule || this.css.isEmittedGlobalsAnimation(eachAnimationName)) {
|
|
314
|
+
const count = this.css.animationsNonLayer.tokenCounts.get(eachAnimationName) || 0;
|
|
315
|
+
this.css.animationsNonLayer.tokenCounts.set(eachAnimationName, count + 1);
|
|
316
|
+
if (animationRule) this.insertVariables(animationRule);
|
|
317
|
+
} else {
|
|
318
|
+
const keyframes = this.css.animations.get(eachAnimationName);
|
|
319
|
+
if (!keyframes) return;
|
|
320
|
+
const newRule = new AnimationRule(eachAnimationName, keyframes, this.css);
|
|
321
|
+
this.css.animationsNonLayer.insert(newRule);
|
|
322
|
+
this.css.animationsNonLayer.tokenCounts.set(eachAnimationName, 1);
|
|
323
|
+
this.insertVariables(newRule);
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
//#endregion
|
|
329
|
+
//#region src/theme-layer.ts
|
|
330
|
+
var RuntimeThemeLayer = class extends ThemeLayer {
|
|
331
|
+
name;
|
|
332
|
+
cssRuntime;
|
|
333
|
+
native = null;
|
|
334
|
+
nativeBuckets = /* @__PURE__ */ new Map();
|
|
335
|
+
constructor(name, cssRuntime) {
|
|
336
|
+
super(name, cssRuntime);
|
|
337
|
+
this.name = name;
|
|
338
|
+
this.cssRuntime = cssRuntime;
|
|
339
|
+
}
|
|
340
|
+
attach() {
|
|
341
|
+
super.attach();
|
|
342
|
+
const nativeSheet = this.cssRuntime.style?.sheet;
|
|
343
|
+
if (nativeSheet && !this.native?.parentStyleSheet) {
|
|
344
|
+
const insertedIndex = nativeSheet.insertRule(this.text, nativeSheet.cssRules.length);
|
|
345
|
+
this.native = nativeSheet.cssRules.item(insertedIndex);
|
|
346
|
+
this.syncNativeBuckets();
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
getBucketKey(mediaText, selectorText) {
|
|
350
|
+
const normalizedMediaText = mediaText.replace(/\s*:\s*/g, ":").replace(/\s+/g, " ").trim();
|
|
351
|
+
const normalizedSelectorText = selectorText.replace(/\s*,\s*/g, ",").replace(/\s+/g, " ").trim();
|
|
352
|
+
return normalizedMediaText + "\n" + normalizedSelectorText;
|
|
353
|
+
}
|
|
354
|
+
setBucketSyntheticDeclarations(bucket, mode) {
|
|
355
|
+
for (const { name, value } of this.getBucketSyntheticDeclarations({
|
|
356
|
+
mediaText: bucket.mediaText,
|
|
357
|
+
mode,
|
|
358
|
+
selectorText: bucket.selectorText
|
|
359
|
+
})) bucket.styleRule.style.setProperty(name, value);
|
|
360
|
+
}
|
|
361
|
+
hasVariableDeclarations(styleRule) {
|
|
362
|
+
for (let index = 0; index < styleRule.style.length; index++) if (styleRule.style.item(index).startsWith("--")) return true;
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
getStyleRule(nativeRule) {
|
|
366
|
+
if (nativeRule instanceof CSSStyleRule) return nativeRule;
|
|
367
|
+
if (nativeRule instanceof CSSGroupingRule) return nativeRule.cssRules.item(0);
|
|
368
|
+
}
|
|
369
|
+
syncNativeBuckets() {
|
|
370
|
+
this.nativeBuckets.clear();
|
|
371
|
+
if (!this.native) return;
|
|
372
|
+
for (const nativeRule of this.native.cssRules) {
|
|
373
|
+
const styleRule = this.getStyleRule(nativeRule);
|
|
374
|
+
if (!styleRule) continue;
|
|
375
|
+
const mediaText = nativeRule instanceof CSSMediaRule ? nativeRule.conditionText : "";
|
|
376
|
+
const normalizedMediaText = mediaText ? `@media ${mediaText}` : "";
|
|
377
|
+
const key = this.getBucketKey(normalizedMediaText, styleRule.selectorText);
|
|
378
|
+
this.nativeBuckets.set(key, {
|
|
379
|
+
key,
|
|
380
|
+
mediaText: normalizedMediaText,
|
|
381
|
+
selectorText: styleRule.selectorText,
|
|
382
|
+
nativeRule,
|
|
383
|
+
styleRule
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
ensureNativeBucket(mediaText, selectorText, mode) {
|
|
388
|
+
if (!this.native) return;
|
|
389
|
+
const key = this.getBucketKey(mediaText, selectorText);
|
|
390
|
+
let bucket = this.nativeBuckets.get(key);
|
|
391
|
+
if (bucket) {
|
|
392
|
+
this.setBucketSyntheticDeclarations(bucket, mode);
|
|
393
|
+
return bucket;
|
|
394
|
+
}
|
|
395
|
+
const declarationText = this.getBucketSyntheticDeclarations({
|
|
396
|
+
mediaText,
|
|
397
|
+
mode,
|
|
398
|
+
selectorText
|
|
399
|
+
}).map(({ name, value }) => `${name}:${value}`).join(";");
|
|
400
|
+
const text = mediaText ? `${mediaText}{${selectorText}{${declarationText}}}` : `${selectorText}{${declarationText}}`;
|
|
401
|
+
const insertedIndex = this.native.insertRule(text, this.getNativeBucketInsertIndex(mediaText, selectorText));
|
|
402
|
+
const nativeRule = this.native.cssRules.item(insertedIndex);
|
|
403
|
+
if (!nativeRule) return;
|
|
404
|
+
const styleRule = this.getStyleRule(nativeRule);
|
|
405
|
+
if (!styleRule) return;
|
|
406
|
+
const newBucket = {
|
|
407
|
+
key,
|
|
408
|
+
mediaText,
|
|
409
|
+
mode,
|
|
410
|
+
selectorText,
|
|
411
|
+
nativeRule,
|
|
412
|
+
styleRule
|
|
413
|
+
};
|
|
414
|
+
this.nativeBuckets.set(key, newBucket);
|
|
415
|
+
return newBucket;
|
|
416
|
+
}
|
|
417
|
+
getNativeBucketInsertIndex(mediaText, selectorText) {
|
|
418
|
+
if (!this.native) return 0;
|
|
419
|
+
const bucket = {
|
|
420
|
+
mediaText,
|
|
421
|
+
selectorText
|
|
422
|
+
};
|
|
423
|
+
for (let index = 0; index < this.native.cssRules.length; index++) {
|
|
424
|
+
const nativeRule = this.native.cssRules.item(index);
|
|
425
|
+
if (!nativeRule) continue;
|
|
426
|
+
const styleRule = this.getStyleRule(nativeRule);
|
|
427
|
+
if (!styleRule) continue;
|
|
428
|
+
const nativeMediaText = nativeRule instanceof CSSMediaRule ? `@media ${nativeRule.conditionText}` : "";
|
|
429
|
+
if (this.shouldInsertBucketBefore(bucket, {
|
|
430
|
+
mediaText: nativeMediaText,
|
|
431
|
+
selectorText: styleRule.selectorText
|
|
432
|
+
})) return index;
|
|
433
|
+
}
|
|
434
|
+
return this.native.cssRules.length;
|
|
435
|
+
}
|
|
436
|
+
insert(rule, index = this.rules.length) {
|
|
437
|
+
const wasEmpty = this.rules.length === 0;
|
|
438
|
+
const insertedIndex = super.insert(rule, index);
|
|
439
|
+
if (insertedIndex === void 0 || !this.native || wasEmpty) return insertedIndex;
|
|
440
|
+
for (const node of rule.nodes) {
|
|
441
|
+
const bucket = this.ensureNativeBucket(node.mediaText, node.selectorText, node.mode);
|
|
442
|
+
if (bucket) {
|
|
443
|
+
this.setBucketSyntheticDeclarations(bucket, node.mode);
|
|
444
|
+
bucket.styleRule.style.setProperty(node.declarationName, node.declarationValue);
|
|
445
|
+
node.native = bucket.styleRule;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return insertedIndex;
|
|
449
|
+
}
|
|
450
|
+
delete(key) {
|
|
451
|
+
const rule = this.rules.find((rule) => rule.key === key);
|
|
452
|
+
const deletedRule = super.delete(key);
|
|
453
|
+
if (!rule || !deletedRule || !this.native) return deletedRule;
|
|
454
|
+
for (const node of rule.nodes) {
|
|
455
|
+
const bucketKey = this.getBucketKey(node.mediaText, node.selectorText);
|
|
456
|
+
const bucket = this.nativeBuckets.get(bucketKey);
|
|
457
|
+
if (!bucket) continue;
|
|
458
|
+
bucket.styleRule.style.removeProperty(node.declarationName);
|
|
459
|
+
if (!this.hasVariableDeclarations(bucket.styleRule)) {
|
|
460
|
+
const foundIndex = findNativeCSSRuleIndex(this.native.cssRules, bucket.nativeRule);
|
|
461
|
+
if (foundIndex !== -1) this.native.deleteRule(foundIndex);
|
|
462
|
+
this.nativeBuckets.delete(bucketKey);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return deletedRule;
|
|
466
|
+
}
|
|
467
|
+
detach() {
|
|
468
|
+
super.detach();
|
|
469
|
+
const nativeSheet = this.cssRuntime.style?.sheet;
|
|
470
|
+
if (nativeSheet && this.native?.parentStyleSheet) {
|
|
471
|
+
const foundIndex = findNativeCSSRuleIndex(nativeSheet.cssRules, this.native);
|
|
472
|
+
if (foundIndex !== -1) nativeSheet.deleteRule(foundIndex);
|
|
473
|
+
}
|
|
474
|
+
this.native = null;
|
|
475
|
+
this.nativeBuckets.clear();
|
|
476
|
+
}
|
|
477
|
+
reset() {
|
|
478
|
+
super.reset();
|
|
479
|
+
const nativeSheet = this.cssRuntime.style?.sheet;
|
|
480
|
+
if (this.native && nativeSheet) {
|
|
481
|
+
const foundIndex = findNativeCSSRuleIndex(nativeSheet.cssRules, this.native);
|
|
482
|
+
if (foundIndex !== -1) nativeSheet.deleteRule(foundIndex);
|
|
483
|
+
}
|
|
484
|
+
this.native = null;
|
|
485
|
+
this.nativeBuckets.clear();
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
//#endregion
|
|
489
|
+
//#region src/class-tracker.ts
|
|
490
|
+
function updateClassDelta(deltaCounts, className, delta) {
|
|
491
|
+
const nextDelta = (deltaCounts.get(className) || 0) + delta;
|
|
492
|
+
if (nextDelta) deltaCounts.set(className, nextDelta);
|
|
493
|
+
else deltaCounts.delete(className);
|
|
494
|
+
}
|
|
495
|
+
function traverseElementSubtree(root, visited, callback) {
|
|
496
|
+
const stack = [root];
|
|
497
|
+
while (stack.length) {
|
|
498
|
+
const el = stack.pop();
|
|
499
|
+
if (visited.has(el)) continue;
|
|
500
|
+
visited.add(el);
|
|
501
|
+
callback(el);
|
|
502
|
+
for (let child = el.lastElementChild; child; child = child.previousElementSibling) stack.push(child);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
var RuntimeClassTracker = class {
|
|
506
|
+
snapshots = /* @__PURE__ */ new WeakMap();
|
|
507
|
+
reset() {
|
|
508
|
+
this.snapshots = /* @__PURE__ */ new WeakMap();
|
|
509
|
+
}
|
|
510
|
+
collectConnected(root, classCounts) {
|
|
511
|
+
this.reset();
|
|
512
|
+
const connectedNames = /* @__PURE__ */ new Set();
|
|
513
|
+
for (const el of root.querySelectorAll("[class]")) {
|
|
514
|
+
const classNames = this.snapshotElement(el);
|
|
515
|
+
if (!classNames) continue;
|
|
516
|
+
for (const className of classNames) {
|
|
517
|
+
const count = classCounts.get(className) || 0;
|
|
518
|
+
if (!count) connectedNames.add(className);
|
|
519
|
+
classCounts.set(className, count + 1);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
return connectedNames;
|
|
523
|
+
}
|
|
524
|
+
collectMutations(records) {
|
|
525
|
+
const deltaCounts = /* @__PURE__ */ new Map();
|
|
526
|
+
const nodeCounts = /* @__PURE__ */ new Map();
|
|
527
|
+
const attrRecords = /* @__PURE__ */ new Set();
|
|
528
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
529
|
+
const updateNodeCount = (el, delta) => {
|
|
530
|
+
const nextCount = (nodeCounts.get(el) || 0) + delta;
|
|
531
|
+
if (nextCount) nodeCounts.set(el, nextCount);
|
|
532
|
+
else nodeCounts.delete(el);
|
|
533
|
+
};
|
|
534
|
+
for (const record of records) if (record.type === "childList") {
|
|
535
|
+
for (const node of record.addedNodes) if (node instanceof Element && node.isConnected) updateNodeCount(node, 1);
|
|
536
|
+
for (const node of record.removedNodes) if (node instanceof Element && !node.isConnected) updateNodeCount(node, -1);
|
|
537
|
+
} else if (record.type === "attributes" && record.attributeName === "class") {
|
|
538
|
+
const target = record.target;
|
|
539
|
+
if (target instanceof Element) attrRecords.add(target);
|
|
540
|
+
}
|
|
541
|
+
for (const [node, count] of nodeCounts) if (count > 0) traverseElementSubtree(node, visited, (el) => this.diffElement(el, deltaCounts));
|
|
542
|
+
else if (count < 0 && !node.isConnected) traverseElementSubtree(node, visited, (el) => this.removeElement(el, deltaCounts));
|
|
543
|
+
for (const el of attrRecords) if (!visited.has(el)) this.diffElement(el, deltaCounts);
|
|
544
|
+
return deltaCounts;
|
|
545
|
+
}
|
|
546
|
+
snapshotElement(el) {
|
|
547
|
+
const classList = el.classList;
|
|
548
|
+
if (!classList.length) {
|
|
549
|
+
this.snapshots.delete(el);
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
const classNames = /* @__PURE__ */ new Set();
|
|
553
|
+
for (let i = 0; i < classList.length; i++) classNames.add(classList.item(i));
|
|
554
|
+
this.snapshots.set(el, classNames);
|
|
555
|
+
return classNames;
|
|
556
|
+
}
|
|
557
|
+
diffElement(el, deltaCounts) {
|
|
558
|
+
const previousClassNames = this.snapshots.get(el);
|
|
559
|
+
const classList = el.classList;
|
|
560
|
+
let nextClassNames;
|
|
561
|
+
for (let i = 0; i < classList.length; i++) {
|
|
562
|
+
const className = classList.item(i);
|
|
563
|
+
if (!previousClassNames?.has(className)) updateClassDelta(deltaCounts, className, 1);
|
|
564
|
+
(nextClassNames ??= /* @__PURE__ */ new Set()).add(className);
|
|
565
|
+
}
|
|
566
|
+
if (previousClassNames) {
|
|
567
|
+
for (const className of previousClassNames) if (!nextClassNames?.has(className)) updateClassDelta(deltaCounts, className, -1);
|
|
568
|
+
}
|
|
569
|
+
if (nextClassNames) this.snapshots.set(el, nextClassNames);
|
|
570
|
+
else this.snapshots.delete(el);
|
|
571
|
+
}
|
|
572
|
+
removeElement(el, deltaCounts) {
|
|
573
|
+
const previousClassNames = this.snapshots.get(el);
|
|
574
|
+
if (!previousClassNames) return;
|
|
575
|
+
for (const className of previousClassNames) updateClassDelta(deltaCounts, className, -1);
|
|
576
|
+
this.snapshots.delete(el);
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
//#endregion
|
|
580
|
+
//#region src/generated-rule.ts
|
|
581
|
+
function collectVariableNames(text) {
|
|
582
|
+
const variableNames = /* @__PURE__ */ new Set();
|
|
583
|
+
for (const match of text.matchAll(/var\(\s*--([_a-zA-Z0-9-]+)/g)) variableNames.add(match[1]);
|
|
584
|
+
return variableNames;
|
|
585
|
+
}
|
|
586
|
+
var HydratedGeneratedRuleNode = class {
|
|
587
|
+
text;
|
|
588
|
+
native;
|
|
589
|
+
constructor(text) {
|
|
590
|
+
this.text = text;
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
var HydratedGeneratedRule = class {
|
|
594
|
+
ir;
|
|
595
|
+
layer;
|
|
596
|
+
native;
|
|
597
|
+
name;
|
|
598
|
+
key;
|
|
599
|
+
layerName;
|
|
600
|
+
type;
|
|
601
|
+
sortTier;
|
|
602
|
+
priority;
|
|
603
|
+
text;
|
|
604
|
+
valid = true;
|
|
605
|
+
fixedClass;
|
|
606
|
+
nodes;
|
|
607
|
+
variableNames;
|
|
608
|
+
animationNames;
|
|
609
|
+
selectorText;
|
|
610
|
+
constructor(ir, layer) {
|
|
611
|
+
this.ir = ir;
|
|
612
|
+
this.layer = layer;
|
|
613
|
+
this.name = ir.className;
|
|
614
|
+
this.key = ir.key;
|
|
615
|
+
this.layerName = ir.layer;
|
|
616
|
+
this.type = ir.type;
|
|
617
|
+
this.sortTier = ir.sortTier;
|
|
618
|
+
this.priority = ir.priority;
|
|
619
|
+
this.text = ir.text;
|
|
620
|
+
this.selectorText = ir.selectorText;
|
|
621
|
+
if (ir.nodes?.length) this.nodes = ir.nodes.map((node) => new HydratedGeneratedRuleNode(node.text));
|
|
622
|
+
const variableNames = collectVariableNames(ir.text);
|
|
623
|
+
ir.nodes?.forEach((node) => {
|
|
624
|
+
collectVariableNames(node.text).forEach((variableName) => variableNames.add(variableName));
|
|
625
|
+
});
|
|
626
|
+
ir.variableNames?.forEach((variableName) => variableNames.add(variableName));
|
|
627
|
+
if (variableNames.size) this.variableNames = variableNames;
|
|
628
|
+
if (ir.animationNames?.length) this.animationNames = new Set(ir.animationNames);
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
//#endregion
|
|
632
|
+
//#region src/native-declaration.ts
|
|
633
|
+
const supportsCache = /* @__PURE__ */ new Map();
|
|
634
|
+
const browserNativeDeclarationMatcher = ({ property, value }) => {
|
|
635
|
+
if (property.startsWith("--")) return true;
|
|
636
|
+
const cacheKey = property + "\0" + value;
|
|
637
|
+
const cached = supportsCache.get(cacheKey);
|
|
638
|
+
if (cached !== void 0) return cached;
|
|
639
|
+
const supported = Boolean(globalThis.CSS?.supports(property, value));
|
|
640
|
+
supportsCache.set(cacheKey, supported);
|
|
641
|
+
return supported;
|
|
642
|
+
};
|
|
643
|
+
//#endregion
|
|
644
|
+
//#region src/core.ts
|
|
645
|
+
const MASTER_CSS_RUNTIME_STYLE_SELECTOR = `style#${MASTER_CSS_RUNTIME_STYLE_ID}`;
|
|
646
|
+
const RETAINED_CLASS_RULE_MIN_AGE_MS = 1e3;
|
|
647
|
+
const RETAINED_CLASS_RULE_IDLE_TIMEOUT_MS = 5e3;
|
|
648
|
+
const RETAINED_CLASS_RULE_CLEANUP_BATCH_SIZE = 64;
|
|
649
|
+
const RETAINED_CLASS_RULE_SOFT_TARGET = 128;
|
|
650
|
+
const RETAINED_CLASS_RULE_HARD_LIMIT = 512;
|
|
651
|
+
const RETAINED_CLASS_RULE_HARD_RAW_BYTES = 256 * 1024;
|
|
652
|
+
function isDocumentRoot(root) {
|
|
653
|
+
const rootConstructorName = root?.constructor.name;
|
|
654
|
+
return rootConstructorName === "HTMLDocument" || rootConstructorName === "Document";
|
|
655
|
+
}
|
|
656
|
+
function findElementById(root, id) {
|
|
657
|
+
return isDocumentRoot(root) ? root.getElementById(id) : "querySelector" in root ? root.querySelector(`#${id}`) : void 0;
|
|
658
|
+
}
|
|
659
|
+
function validateHydrationManifest(hydrationManifest) {
|
|
660
|
+
return hydrationManifest?.version === 1 && Array.isArray(hydrationManifest?.rules) ? hydrationManifest : void 0;
|
|
661
|
+
}
|
|
662
|
+
function parseHydrationManifest(source) {
|
|
663
|
+
try {
|
|
664
|
+
return validateHydrationManifest(JSON.parse(source));
|
|
665
|
+
} catch (error) {
|
|
666
|
+
console.debug("Cannot parse Master CSS hydration manifest.", error);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
function readInlineHydrationManifest(root) {
|
|
670
|
+
const source = findElementById(root, MASTER_CSS_HYDRATION_MANIFEST_SCRIPT_ID)?.textContent?.trim();
|
|
671
|
+
if (!source) return;
|
|
672
|
+
return parseHydrationManifest(source);
|
|
673
|
+
}
|
|
674
|
+
function readExternalHydrationManifestSource(root) {
|
|
675
|
+
const styleElement = findElementById(root, MASTER_CSS_RUNTIME_STYLE_ID);
|
|
676
|
+
const HTMLStyleElementConstructor = globalThis.HTMLStyleElement;
|
|
677
|
+
return HTMLStyleElementConstructor && styleElement instanceof HTMLStyleElementConstructor ? styleElement.getAttribute(MASTER_CSS_HYDRATION_MANIFEST_ATTR) : void 0;
|
|
678
|
+
}
|
|
679
|
+
function resolveExternalHydrationManifestURL(root, source) {
|
|
680
|
+
const ownerDocument = isDocumentRoot(root) ? root : root.ownerDocument;
|
|
681
|
+
return new URL(source, ownerDocument.baseURI).href;
|
|
682
|
+
}
|
|
683
|
+
async function importHydrationManifest(url) {
|
|
684
|
+
return validateHydrationManifest((await globalThis.Function("specifier", "return import(specifier, { with: { type: 'json' } })")(url)).default);
|
|
685
|
+
}
|
|
686
|
+
var CSSRuntime = class CSSRuntime extends MasterCSS {
|
|
687
|
+
root;
|
|
688
|
+
hydrationManifest;
|
|
689
|
+
static instances = /* @__PURE__ */ new WeakMap();
|
|
690
|
+
host;
|
|
691
|
+
container;
|
|
692
|
+
baseLayer = new RuntimeUtilityLayer("base", this);
|
|
693
|
+
themeLayer = new RuntimeThemeLayer("theme", this);
|
|
694
|
+
defaultsLayer = new RuntimeUtilityLayer("defaults", this);
|
|
695
|
+
componentsLayer = new RuntimeUtilityLayer("components", this);
|
|
696
|
+
utilitiesLayer = new RuntimeUtilityLayer("utilities", this);
|
|
697
|
+
classCounts = /* @__PURE__ */ new Map();
|
|
698
|
+
retainedClassNames = /* @__PURE__ */ new Set();
|
|
699
|
+
classTracker = new RuntimeClassTracker();
|
|
700
|
+
pendingAddedClassNames = /* @__PURE__ */ new Set();
|
|
701
|
+
pendingRemovedClassNames = /* @__PURE__ */ new Set();
|
|
702
|
+
retainedClassRules = /* @__PURE__ */ new Map();
|
|
703
|
+
pendingAdditionFrame;
|
|
704
|
+
pendingRemovalFrame;
|
|
705
|
+
pendingRemovalFlushFrame;
|
|
706
|
+
retainedCleanupIdleHandle;
|
|
707
|
+
retainedCleanupTimeoutHandle;
|
|
708
|
+
hydrationFailureReason;
|
|
709
|
+
observer;
|
|
710
|
+
progressive = false;
|
|
711
|
+
observing = false;
|
|
712
|
+
static create(options) {
|
|
713
|
+
const { manifest, root = document, emittedGlobals, hydrationManifest } = options;
|
|
714
|
+
const resolvedHydrationManifest = hydrationManifest === void 0 ? readInlineHydrationManifest(root) : hydrationManifest;
|
|
715
|
+
const current = globalThis.MasterCSSRuntime.instances.get(root);
|
|
716
|
+
if (current) {
|
|
717
|
+
current.registerEmittedGlobals(emittedGlobals);
|
|
718
|
+
if (resolvedHydrationManifest !== void 0) current.setHydrationManifest(resolvedHydrationManifest);
|
|
719
|
+
return current;
|
|
720
|
+
}
|
|
721
|
+
return new CSSRuntime(root, manifest, emittedGlobals, resolvedHydrationManifest).register();
|
|
722
|
+
}
|
|
723
|
+
constructor(root = document, manifest, emittedGlobals, hydrationManifest) {
|
|
724
|
+
super(manifest, emittedGlobals, { nativeDeclarationMatcher: browserNativeDeclarationMatcher });
|
|
725
|
+
this.root = root;
|
|
726
|
+
this.hydrationManifest = hydrationManifest;
|
|
727
|
+
if (isDocumentRoot(root)) {
|
|
728
|
+
this.container = root.head;
|
|
729
|
+
this.host = root.documentElement;
|
|
730
|
+
} else {
|
|
731
|
+
this.container = this.root;
|
|
732
|
+
this.host = this.root.host;
|
|
733
|
+
}
|
|
734
|
+
this.applyEmittedGlobalsCounts({ variables: this.emittedGlobals.variables });
|
|
735
|
+
}
|
|
736
|
+
register() {
|
|
737
|
+
const registered = globalThis.MasterCSSRuntime.instances.get(this.root) === this;
|
|
738
|
+
globalThis.MasterCSSRuntime.instances.set(this.root, this);
|
|
739
|
+
if (isDocumentRoot(this.root)) this.root.defaultView.globalThis.masterCSSRuntime = this;
|
|
740
|
+
if (!registered && true) debugRuntimeCreated(this);
|
|
741
|
+
return this;
|
|
742
|
+
}
|
|
743
|
+
unregister() {
|
|
744
|
+
globalThis.MasterCSSRuntime.instances.delete(this.root);
|
|
745
|
+
if (isDocumentRoot(this.root) && this.root.defaultView.globalThis.masterCSSRuntime === this) this.root.defaultView.globalThis.masterCSSRuntime = void 0;
|
|
746
|
+
return this;
|
|
747
|
+
}
|
|
748
|
+
setHydrationManifest(hydrationManifest) {
|
|
749
|
+
this.hydrationManifest = hydrationManifest;
|
|
750
|
+
return this;
|
|
751
|
+
}
|
|
752
|
+
needsHydrationManifest() {
|
|
753
|
+
return this.hydrationManifest === void 0 && Boolean(readExternalHydrationManifestSource(this.root));
|
|
754
|
+
}
|
|
755
|
+
async loadHydrationManifest() {
|
|
756
|
+
const inlineHydrationManifest = readInlineHydrationManifest(this.root);
|
|
757
|
+
if (inlineHydrationManifest) {
|
|
758
|
+
this.setHydrationManifest(inlineHydrationManifest);
|
|
759
|
+
return this;
|
|
760
|
+
}
|
|
761
|
+
const source = readExternalHydrationManifestSource(this.root);
|
|
762
|
+
if (!source) return this;
|
|
763
|
+
try {
|
|
764
|
+
this.setHydrationManifest(await importHydrationManifest(resolveExternalHydrationManifestURL(this.root, source)));
|
|
765
|
+
} catch (error) {
|
|
766
|
+
console.debug("Cannot load Master CSS hydration manifest.", error);
|
|
767
|
+
}
|
|
768
|
+
return this;
|
|
769
|
+
}
|
|
770
|
+
createRuntimeStyle() {
|
|
771
|
+
const ownerDocument = "createElement" in this.root ? this.root : this.root.ownerDocument;
|
|
772
|
+
this.style = ownerDocument.createElement("style");
|
|
773
|
+
this.style.id = MASTER_CSS_RUNTIME_STYLE_ID;
|
|
774
|
+
this.style.setAttribute("blocking", "render");
|
|
775
|
+
this.container.append(this.style);
|
|
776
|
+
}
|
|
777
|
+
warnHydrationFallback(reason) {
|
|
778
|
+
console.warn(`Master CSS progressive hydration requires a matching hydration manifest. ${reason} Rebuilding ${MASTER_CSS_RUNTIME_STYLE_SELECTOR} with the runtime.`);
|
|
779
|
+
}
|
|
780
|
+
useRuntimeStyle(connectedNames, reason) {
|
|
781
|
+
if (reason) this.warnHydrationFallback(reason);
|
|
782
|
+
this.style?.remove();
|
|
783
|
+
this.style = null;
|
|
784
|
+
this.progressive = false;
|
|
785
|
+
this.createRuntimeStyle();
|
|
786
|
+
this.insertStaticResources();
|
|
787
|
+
connectedNames.forEach((cls) => this.ensureClassRules(cls));
|
|
788
|
+
}
|
|
789
|
+
detectRuntimeStyle() {
|
|
790
|
+
if (this.root.styleSheets) for (const sheet of this.root.styleSheets) {
|
|
791
|
+
const { ownerNode } = sheet;
|
|
792
|
+
if (ownerNode instanceof HTMLStyleElement && ownerNode.id === MASTER_CSS_RUNTIME_STYLE_ID) {
|
|
793
|
+
this.style = ownerNode;
|
|
794
|
+
this.progressive = true;
|
|
795
|
+
break;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
collectConnectedClasses() {
|
|
800
|
+
return this.classTracker.collectConnected(this.root, this.classCounts);
|
|
801
|
+
}
|
|
802
|
+
hydrateRuntimeStyle(connectedNames) {
|
|
803
|
+
const hydrateResult = this.style?.sheet && this.hydrate(this.style.sheet.cssRules);
|
|
804
|
+
if (hydrateResult) {
|
|
805
|
+
const hydratedClassNames = new Set(hydrateResult.allUtilities.map(({ fixedClass, name }) => fixedClass || name));
|
|
806
|
+
for (const cls of connectedNames) if (!hydratedClassNames.has(cls)) {
|
|
807
|
+
this.ensureClassRules(cls);
|
|
808
|
+
console.debug(`Missing prerendered rule for class \`${cls}\``);
|
|
809
|
+
}
|
|
810
|
+
} else this.useRuntimeStyle(connectedNames, this.hydrationFailureReason || `Cannot read ${MASTER_CSS_RUNTIME_STYLE_SELECTOR} CSS rules.`);
|
|
811
|
+
}
|
|
812
|
+
renderRuntimeStyle(connectedNames) {
|
|
813
|
+
this.createRuntimeStyle();
|
|
814
|
+
this.insertStaticResources();
|
|
815
|
+
connectedNames.forEach((cls) => this.ensureClassRules(cls));
|
|
816
|
+
}
|
|
817
|
+
getAnimationFrameWindow() {
|
|
818
|
+
return (isDocumentRoot(this.root) ? this.root : this.root.ownerDocument)?.defaultView || globalThis;
|
|
819
|
+
}
|
|
820
|
+
cancelPendingRemovalFrames() {
|
|
821
|
+
const view = this.getAnimationFrameWindow();
|
|
822
|
+
if (this.pendingRemovalFrame !== void 0) {
|
|
823
|
+
view.cancelAnimationFrame(this.pendingRemovalFrame);
|
|
824
|
+
this.pendingRemovalFrame = void 0;
|
|
825
|
+
}
|
|
826
|
+
if (this.pendingRemovalFlushFrame !== void 0) {
|
|
827
|
+
view.cancelAnimationFrame(this.pendingRemovalFlushFrame);
|
|
828
|
+
this.pendingRemovalFlushFrame = void 0;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
cancelPendingAdditionFrame() {
|
|
832
|
+
if (this.pendingAdditionFrame === void 0) return;
|
|
833
|
+
this.getAnimationFrameWindow().cancelAnimationFrame(this.pendingAdditionFrame);
|
|
834
|
+
this.pendingAdditionFrame = void 0;
|
|
835
|
+
}
|
|
836
|
+
clearPendingAddedClassNames() {
|
|
837
|
+
this.pendingAddedClassNames.clear();
|
|
838
|
+
this.cancelPendingAdditionFrame();
|
|
839
|
+
}
|
|
840
|
+
clearPendingRemovedClassNames() {
|
|
841
|
+
this.pendingRemovedClassNames.clear();
|
|
842
|
+
this.cancelPendingRemovalFrames();
|
|
843
|
+
}
|
|
844
|
+
getCleanupWindow() {
|
|
845
|
+
return this.getAnimationFrameWindow();
|
|
846
|
+
}
|
|
847
|
+
cancelRetainedClassRuleCleanup() {
|
|
848
|
+
const view = this.getCleanupWindow();
|
|
849
|
+
if (this.retainedCleanupIdleHandle !== void 0) {
|
|
850
|
+
view.cancelIdleCallback?.(this.retainedCleanupIdleHandle);
|
|
851
|
+
this.retainedCleanupIdleHandle = void 0;
|
|
852
|
+
}
|
|
853
|
+
if (this.retainedCleanupTimeoutHandle !== void 0) {
|
|
854
|
+
view.clearTimeout(this.retainedCleanupTimeoutHandle);
|
|
855
|
+
this.retainedCleanupTimeoutHandle = void 0;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
clearRetainedClassRules() {
|
|
859
|
+
this.retainedClassNames.clear();
|
|
860
|
+
this.retainedClassRules.clear();
|
|
861
|
+
this.cancelRetainedClassRuleCleanup();
|
|
862
|
+
}
|
|
863
|
+
cancelPendingRemovedClassNames(classNames) {
|
|
864
|
+
if (!this.pendingRemovedClassNames.size) return;
|
|
865
|
+
for (const className of classNames) this.pendingRemovedClassNames.delete(className);
|
|
866
|
+
if (!this.pendingRemovedClassNames.size) this.cancelPendingRemovalFrames();
|
|
867
|
+
}
|
|
868
|
+
cancelPendingAddedClassNames(classNames) {
|
|
869
|
+
if (!this.pendingAddedClassNames.size) return;
|
|
870
|
+
for (const className of classNames) this.pendingAddedClassNames.delete(className);
|
|
871
|
+
if (!this.pendingAddedClassNames.size) this.cancelPendingAdditionFrame();
|
|
872
|
+
}
|
|
873
|
+
cancelRetainedClassNames(classNames) {
|
|
874
|
+
if (!this.retainedClassNames.size) return;
|
|
875
|
+
for (const className of classNames) {
|
|
876
|
+
this.retainedClassNames.delete(className);
|
|
877
|
+
this.retainedClassRules.delete(className);
|
|
878
|
+
}
|
|
879
|
+
if (!this.retainedClassNames.size) this.cancelRetainedClassRuleCleanup();
|
|
880
|
+
}
|
|
881
|
+
schedulePendingRemovalFlush() {
|
|
882
|
+
if (!this.pendingRemovedClassNames.size) return;
|
|
883
|
+
if (this.pendingRemovalFrame !== void 0 || this.pendingRemovalFlushFrame !== void 0) return;
|
|
884
|
+
const view = this.getAnimationFrameWindow();
|
|
885
|
+
this.pendingRemovalFrame = view.requestAnimationFrame(() => {
|
|
886
|
+
this.pendingRemovalFrame = void 0;
|
|
887
|
+
this.pendingRemovalFlushFrame = view.requestAnimationFrame(() => {
|
|
888
|
+
this.pendingRemovalFlushFrame = void 0;
|
|
889
|
+
this.flushPendingRemovedClassNames();
|
|
890
|
+
});
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
queueRemovedClassNames(classNames) {
|
|
894
|
+
for (const className of classNames) if (!this.classCounts.has(className)) this.pendingRemovedClassNames.add(className);
|
|
895
|
+
this.schedulePendingRemovalFlush();
|
|
896
|
+
}
|
|
897
|
+
schedulePendingAdditionFlush() {
|
|
898
|
+
if (!this.pendingAddedClassNames.size) return;
|
|
899
|
+
if (this.pendingAdditionFrame !== void 0) return;
|
|
900
|
+
const view = this.getAnimationFrameWindow();
|
|
901
|
+
this.pendingAdditionFrame = view.requestAnimationFrame(() => {
|
|
902
|
+
this.pendingAdditionFrame = void 0;
|
|
903
|
+
this.flushPendingAddedClassNames();
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
queueAddedClassNames(classNames) {
|
|
907
|
+
for (const className of classNames) if (this.classCounts.has(className)) this.pendingAddedClassNames.add(className);
|
|
908
|
+
this.schedulePendingAdditionFlush();
|
|
909
|
+
}
|
|
910
|
+
estimateRetainedClassRule(className) {
|
|
911
|
+
const rules = this.classUtilities.get(className);
|
|
912
|
+
if (!rules?.length) return;
|
|
913
|
+
let rawBytes = 0;
|
|
914
|
+
let ruleCount = 0;
|
|
915
|
+
for (const rule of rules) {
|
|
916
|
+
const nodes = rule.nodes;
|
|
917
|
+
if (nodes?.length) for (const node of nodes) {
|
|
918
|
+
rawBytes += node.text.length;
|
|
919
|
+
ruleCount++;
|
|
920
|
+
}
|
|
921
|
+
else {
|
|
922
|
+
rawBytes += rule.text.length;
|
|
923
|
+
ruleCount++;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
return {
|
|
927
|
+
retainedAt: Date.now(),
|
|
928
|
+
rawBytes,
|
|
929
|
+
ruleCount
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
getRetainedClassRuleRawBytes() {
|
|
933
|
+
let rawBytes = 0;
|
|
934
|
+
for (const retainedRule of this.retainedClassRules.values()) rawBytes += retainedRule.rawBytes;
|
|
935
|
+
return rawBytes;
|
|
936
|
+
}
|
|
937
|
+
exceedsRetainedClassRuleHardLimits() {
|
|
938
|
+
return this.retainedClassNames.size > RETAINED_CLASS_RULE_HARD_LIMIT || this.getRetainedClassRuleRawBytes() > RETAINED_CLASS_RULE_HARD_RAW_BYTES;
|
|
939
|
+
}
|
|
940
|
+
scheduleRetainedClassRuleCleanup() {
|
|
941
|
+
if (!this.retainedClassNames.size) return;
|
|
942
|
+
if (this.retainedCleanupIdleHandle !== void 0 || this.retainedCleanupTimeoutHandle !== void 0) return;
|
|
943
|
+
const view = this.getCleanupWindow();
|
|
944
|
+
const hardLimitExceeded = this.exceedsRetainedClassRuleHardLimits();
|
|
945
|
+
const cleanup = () => {
|
|
946
|
+
this.retainedCleanupIdleHandle = void 0;
|
|
947
|
+
this.retainedCleanupTimeoutHandle = void 0;
|
|
948
|
+
this.cleanupRetainedClassRules();
|
|
949
|
+
};
|
|
950
|
+
const timeout = hardLimitExceeded ? 0 : RETAINED_CLASS_RULE_IDLE_TIMEOUT_MS;
|
|
951
|
+
if (view.requestIdleCallback) this.retainedCleanupIdleHandle = view.requestIdleCallback(cleanup, { timeout });
|
|
952
|
+
else this.retainedCleanupTimeoutHandle = view.setTimeout(cleanup, timeout);
|
|
953
|
+
}
|
|
954
|
+
retainRemovedClassRules(classNames) {
|
|
955
|
+
for (const className of classNames) {
|
|
956
|
+
if (this.classCounts.has(className)) continue;
|
|
957
|
+
const retainedRule = this.retainedClassRules.get(className) || this.estimateRetainedClassRule(className);
|
|
958
|
+
if (!retainedRule) continue;
|
|
959
|
+
this.retainedClassNames.add(className);
|
|
960
|
+
this.retainedClassRules.set(className, retainedRule);
|
|
961
|
+
}
|
|
962
|
+
this.scheduleRetainedClassRuleCleanup();
|
|
963
|
+
}
|
|
964
|
+
getRetainedClassRuleCleanupCandidates(force = false) {
|
|
965
|
+
const now = Date.now();
|
|
966
|
+
const hardLimitExceeded = this.exceedsRetainedClassRuleHardLimits();
|
|
967
|
+
const entries = [];
|
|
968
|
+
for (const className of this.retainedClassNames) {
|
|
969
|
+
if (this.classCounts.has(className)) {
|
|
970
|
+
this.retainedClassNames.delete(className);
|
|
971
|
+
this.retainedClassRules.delete(className);
|
|
972
|
+
continue;
|
|
973
|
+
}
|
|
974
|
+
const retainedRule = this.retainedClassRules.get(className);
|
|
975
|
+
if (!retainedRule) {
|
|
976
|
+
this.retainedClassNames.delete(className);
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
const oldEnough = now - retainedRule.retainedAt >= RETAINED_CLASS_RULE_MIN_AGE_MS;
|
|
980
|
+
if (force || hardLimitExceeded || oldEnough && this.retainedClassNames.size > RETAINED_CLASS_RULE_SOFT_TARGET) entries.push([className, retainedRule]);
|
|
981
|
+
}
|
|
982
|
+
entries.sort(([, a], [, b]) => a.retainedAt - b.retainedAt);
|
|
983
|
+
const cleanupCount = hardLimitExceeded ? Math.max(RETAINED_CLASS_RULE_CLEANUP_BATCH_SIZE, this.retainedClassNames.size - RETAINED_CLASS_RULE_SOFT_TARGET) : RETAINED_CLASS_RULE_CLEANUP_BATCH_SIZE;
|
|
984
|
+
return entries.slice(0, force ? entries.length : cleanupCount).map(([className]) => className);
|
|
985
|
+
}
|
|
986
|
+
removeRetainedClassRules(classNames) {
|
|
987
|
+
const removedClassNames = [];
|
|
988
|
+
for (const className of classNames) {
|
|
989
|
+
if (!this.retainedClassNames.has(className)) continue;
|
|
990
|
+
this.retainedClassNames.delete(className);
|
|
991
|
+
this.retainedClassRules.delete(className);
|
|
992
|
+
if (!this.classCounts.has(className)) removedClassNames.push(className);
|
|
993
|
+
}
|
|
994
|
+
if (removedClassNames.length) super.deleteClassRules(...removedClassNames);
|
|
995
|
+
return removedClassNames.length;
|
|
996
|
+
}
|
|
997
|
+
cleanupRetainedClassRules(force = false) {
|
|
998
|
+
const removedCount = this.removeRetainedClassRules(this.getRetainedClassRuleCleanupCandidates(force));
|
|
999
|
+
if (this.retainedClassNames.size && (force || this.retainedClassNames.size > RETAINED_CLASS_RULE_SOFT_TARGET || this.exceedsRetainedClassRuleHardLimits())) this.scheduleRetainedClassRuleCleanup();
|
|
1000
|
+
return removedCount;
|
|
1001
|
+
}
|
|
1002
|
+
flushRetainedClassRules() {
|
|
1003
|
+
this.cancelRetainedClassRuleCleanup();
|
|
1004
|
+
const removedCount = this.cleanupRetainedClassRules(true);
|
|
1005
|
+
if (this.retainedClassNames.size) this.scheduleRetainedClassRuleCleanup();
|
|
1006
|
+
return removedCount;
|
|
1007
|
+
}
|
|
1008
|
+
flushPendingRemovedClassNames() {
|
|
1009
|
+
if (!this.pendingRemovedClassNames.size) return;
|
|
1010
|
+
const classNames = [];
|
|
1011
|
+
for (const className of this.pendingRemovedClassNames) if (!this.classCounts.has(className)) classNames.push(className);
|
|
1012
|
+
this.pendingRemovedClassNames.clear();
|
|
1013
|
+
if (classNames.length) this.retainRemovedClassRules(classNames);
|
|
1014
|
+
}
|
|
1015
|
+
flushPendingAddedClassNames() {
|
|
1016
|
+
if (!this.pendingAddedClassNames.size) return;
|
|
1017
|
+
const classNames = [];
|
|
1018
|
+
for (const className of this.pendingAddedClassNames) if (this.classCounts.has(className)) classNames.push(className);
|
|
1019
|
+
this.pendingAddedClassNames.clear();
|
|
1020
|
+
if (classNames.length) this.ensureClassRules(...classNames);
|
|
1021
|
+
}
|
|
1022
|
+
handleMutationRecords(records) {
|
|
1023
|
+
const deltaCounts = this.classTracker.collectMutations(records);
|
|
1024
|
+
const warmClassNames = [];
|
|
1025
|
+
const queuedClassNames = [];
|
|
1026
|
+
const removedClassNames = [];
|
|
1027
|
+
for (const [cls, change] of deltaCounts) {
|
|
1028
|
+
const current = this.classCounts.get(cls) || 0;
|
|
1029
|
+
const next = current + change;
|
|
1030
|
+
if (next > 0) {
|
|
1031
|
+
this.classCounts.set(cls, next);
|
|
1032
|
+
if (current === 0) if (this.classUtilities.has(cls) || this.retainedClassNames.has(cls)) warmClassNames.push(cls);
|
|
1033
|
+
else queuedClassNames.push(cls);
|
|
1034
|
+
} else {
|
|
1035
|
+
this.classCounts.delete(cls);
|
|
1036
|
+
removedClassNames.push(cls);
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
if (warmClassNames.length) this.ensureClassRules(...warmClassNames);
|
|
1040
|
+
if (queuedClassNames.length) this.queueAddedClassNames(queuedClassNames);
|
|
1041
|
+
if (removedClassNames.length) {
|
|
1042
|
+
this.cancelPendingAddedClassNames(removedClassNames);
|
|
1043
|
+
this.queueRemovedClassNames(removedClassNames);
|
|
1044
|
+
}
|
|
1045
|
+
debugRuntimeMutation(records, deltaCounts, this);
|
|
1046
|
+
}
|
|
1047
|
+
ensureClassRules(...classNames) {
|
|
1048
|
+
this.cancelPendingAddedClassNames(classNames);
|
|
1049
|
+
this.cancelPendingRemovedClassNames(classNames);
|
|
1050
|
+
this.cancelRetainedClassNames(classNames);
|
|
1051
|
+
return super.ensureClassRules(...classNames);
|
|
1052
|
+
}
|
|
1053
|
+
deleteClassRules(...classNames) {
|
|
1054
|
+
this.cancelPendingAddedClassNames(classNames);
|
|
1055
|
+
this.cancelPendingRemovedClassNames(classNames);
|
|
1056
|
+
this.cancelRetainedClassNames(classNames);
|
|
1057
|
+
super.deleteClassRules(...classNames);
|
|
1058
|
+
}
|
|
1059
|
+
startMutationObserver() {
|
|
1060
|
+
this.observer = new MutationObserver((records) => this.handleMutationRecords(records));
|
|
1061
|
+
this.observer.observe(this.root, {
|
|
1062
|
+
childList: true,
|
|
1063
|
+
attributes: true,
|
|
1064
|
+
attributeFilter: ["class"],
|
|
1065
|
+
subtree: true
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
revealHostIfNeeded() {
|
|
1069
|
+
if (!this.progressive) this.host.removeAttribute("hidden");
|
|
1070
|
+
}
|
|
1071
|
+
/**
|
|
1072
|
+
* Observe the DOM for changes and update the running stylesheet. (browser only)
|
|
1073
|
+
* @param options mutation observer options
|
|
1074
|
+
* @returns this
|
|
1075
|
+
*/
|
|
1076
|
+
observe() {
|
|
1077
|
+
if (this.observing) return this;
|
|
1078
|
+
this.detectRuntimeStyle();
|
|
1079
|
+
const connectedNames = this.collectConnectedClasses();
|
|
1080
|
+
if (this.progressive) this.hydrateRuntimeStyle(connectedNames);
|
|
1081
|
+
else this.renderRuntimeStyle(connectedNames);
|
|
1082
|
+
this.startMutationObserver();
|
|
1083
|
+
this.revealHostIfNeeded();
|
|
1084
|
+
this.observing = true;
|
|
1085
|
+
debugRuntimeObserved(this);
|
|
1086
|
+
return this;
|
|
1087
|
+
}
|
|
1088
|
+
failHydration(reason) {
|
|
1089
|
+
this.hydrationFailureReason = reason;
|
|
1090
|
+
}
|
|
1091
|
+
getHydrationManifestLayerRules() {
|
|
1092
|
+
const layers = /* @__PURE__ */ new Map();
|
|
1093
|
+
if (!this.hydrationManifest?.rules?.length) return layers;
|
|
1094
|
+
for (const rule of this.hydrationManifest.rules) {
|
|
1095
|
+
const layerRules = layers.get(rule.layer);
|
|
1096
|
+
if (layerRules) layerRules.push(rule);
|
|
1097
|
+
else layers.set(rule.layer, [rule]);
|
|
1098
|
+
}
|
|
1099
|
+
return layers;
|
|
1100
|
+
}
|
|
1101
|
+
registerHydratedClassRule(className, rule) {
|
|
1102
|
+
const classUtilities = this.classUtilities;
|
|
1103
|
+
const rules = classUtilities.get(className);
|
|
1104
|
+
if (rules) rules.push(rule);
|
|
1105
|
+
else classUtilities.set(className, [rule]);
|
|
1106
|
+
}
|
|
1107
|
+
getUtilityLayerByName(name) {
|
|
1108
|
+
switch (name) {
|
|
1109
|
+
case "base": return this.baseLayer;
|
|
1110
|
+
case "defaults": return this.defaultsLayer;
|
|
1111
|
+
case "components": return this.componentsLayer;
|
|
1112
|
+
case "utilities": return this.utilitiesLayer;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
collectHydrationManifestVariableNames() {
|
|
1116
|
+
const variableNames = /* @__PURE__ */ new Set();
|
|
1117
|
+
const collectVariableReferences = (text) => {
|
|
1118
|
+
for (const match of text.matchAll(/var\(\s*--([_a-zA-Z0-9-]+)/g)) collectVariable(match[1]);
|
|
1119
|
+
};
|
|
1120
|
+
const collectVariable = (variableName, visited = /* @__PURE__ */ new Set()) => {
|
|
1121
|
+
if (visited.has(variableName)) return;
|
|
1122
|
+
visited.add(variableName);
|
|
1123
|
+
const variable = this.variables.get(variableName);
|
|
1124
|
+
if (!variable) return;
|
|
1125
|
+
variableNames.add(variableName);
|
|
1126
|
+
variable.dependencies?.forEach((dependency) => collectVariable(dependency, visited));
|
|
1127
|
+
};
|
|
1128
|
+
for (const rule of this.hydrationManifest?.rules || []) {
|
|
1129
|
+
rule.variableNames?.forEach((variableName) => collectVariable(variableName));
|
|
1130
|
+
collectVariableReferences(rule.text);
|
|
1131
|
+
rule.nodes?.forEach((node) => collectVariableReferences(node.text));
|
|
1132
|
+
rule.animationNames?.forEach((animationName) => {
|
|
1133
|
+
const keyframes = this.animations.get(animationName);
|
|
1134
|
+
if (!keyframes) return;
|
|
1135
|
+
new AnimationRule(animationName, keyframes, this).variableNames?.forEach((variableName) => collectVariable(variableName));
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
for (const [variableName, variable] of this.variables) if (variable.static) collectVariable(variableName);
|
|
1139
|
+
return variableNames;
|
|
1140
|
+
}
|
|
1141
|
+
collectHydrationManifestAnimationNames() {
|
|
1142
|
+
const animationNames = /* @__PURE__ */ new Set();
|
|
1143
|
+
for (const rule of this.hydrationManifest?.rules || []) rule.animationNames?.forEach((animationName) => animationNames.add(animationName));
|
|
1144
|
+
for (const animationName of this.animations.keys()) if (this.manifest.animationOptions?.[animationName]?.static) animationNames.add(animationName);
|
|
1145
|
+
return animationNames;
|
|
1146
|
+
}
|
|
1147
|
+
nativeThemeLayerHasOnlyEmittedGlobalsVariables(nativeThemeLayer) {
|
|
1148
|
+
if (!nativeThemeLayer) return false;
|
|
1149
|
+
let found = false;
|
|
1150
|
+
for (const nativeRule of nativeThemeLayer.cssRules) {
|
|
1151
|
+
const styleRule = this.themeLayer.getStyleRule(nativeRule);
|
|
1152
|
+
if (!styleRule) return false;
|
|
1153
|
+
for (let index = 0; index < styleRule.style.length; index++) {
|
|
1154
|
+
const propertyName = styleRule.style.item(index);
|
|
1155
|
+
if (this.isSyntheticColorSchemeDeclaration(nativeRule, styleRule, propertyName)) continue;
|
|
1156
|
+
if (!propertyName.startsWith("--")) return false;
|
|
1157
|
+
found = true;
|
|
1158
|
+
if (!this.isEmittedGlobalsVariable(propertyName.slice(2))) return false;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
return found;
|
|
1162
|
+
}
|
|
1163
|
+
getVariableRuleBuckets(variableRules) {
|
|
1164
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
1165
|
+
for (const rule of variableRules) for (const node of rule.nodes) {
|
|
1166
|
+
const key = this.themeLayer.getBucketKey(node.mediaText, node.selectorText);
|
|
1167
|
+
let bucket = buckets.get(key);
|
|
1168
|
+
if (!bucket) {
|
|
1169
|
+
bucket = {
|
|
1170
|
+
mediaText: node.mediaText,
|
|
1171
|
+
mode: node.mode,
|
|
1172
|
+
selectorText: node.selectorText,
|
|
1173
|
+
nodes: []
|
|
1174
|
+
};
|
|
1175
|
+
buckets.set(key, bucket);
|
|
1176
|
+
}
|
|
1177
|
+
bucket.nodes.push(node);
|
|
1178
|
+
}
|
|
1179
|
+
return Array.from(buckets.values());
|
|
1180
|
+
}
|
|
1181
|
+
isSyntheticColorSchemeDeclaration(nativeRule, styleRule, propertyName) {
|
|
1182
|
+
if (propertyName !== "color-scheme") return false;
|
|
1183
|
+
const colorScheme = styleRule.style.getPropertyValue(propertyName).trim();
|
|
1184
|
+
return colorScheme === this.themeLayer.getBucketColorScheme({
|
|
1185
|
+
mediaText: nativeRule instanceof CSSMediaRule ? `@media ${nativeRule.conditionText}` : "",
|
|
1186
|
+
mode: colorScheme,
|
|
1187
|
+
selectorText: styleRule.selectorText
|
|
1188
|
+
});
|
|
1189
|
+
}
|
|
1190
|
+
getNativeThemeRuleBucketKey(nativeRule) {
|
|
1191
|
+
const styleRule = this.themeLayer.getStyleRule(nativeRule);
|
|
1192
|
+
if (!styleRule) return;
|
|
1193
|
+
const mediaText = nativeRule instanceof CSSMediaRule ? `@media ${nativeRule.conditionText}` : "";
|
|
1194
|
+
return {
|
|
1195
|
+
key: this.themeLayer.getBucketKey(mediaText, styleRule.selectorText),
|
|
1196
|
+
styleRule
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
hydrateHydrationManifestVariables(nativeThemeLayer) {
|
|
1200
|
+
const variableRules = [...this.collectHydrationManifestVariableNames()].filter((variableName) => !this.isEmittedGlobalsVariable(variableName)).map((variableName) => {
|
|
1201
|
+
const variable = this.variables.get(variableName);
|
|
1202
|
+
return variable && !variable.inline ? new VariableRule(variableName, variable, this) : void 0;
|
|
1203
|
+
}).filter((rule) => Boolean(rule));
|
|
1204
|
+
const expectedBuckets = this.getVariableRuleBuckets(variableRules);
|
|
1205
|
+
const nativeRuleCount = nativeThemeLayer?.cssRules.length || 0;
|
|
1206
|
+
if (expectedBuckets.length !== nativeRuleCount) return !variableRules.length && this.nativeThemeLayerHasOnlyEmittedGlobalsVariables(nativeThemeLayer);
|
|
1207
|
+
if (!variableRules.length) return true;
|
|
1208
|
+
if (!nativeThemeLayer) return false;
|
|
1209
|
+
this.themeLayer.native = nativeThemeLayer;
|
|
1210
|
+
const nativeBuckets = /* @__PURE__ */ new Map();
|
|
1211
|
+
for (const nativeRule of nativeThemeLayer.cssRules) {
|
|
1212
|
+
const nativeBucket = this.getNativeThemeRuleBucketKey(nativeRule);
|
|
1213
|
+
if (!nativeBucket || nativeBuckets.has(nativeBucket.key)) return false;
|
|
1214
|
+
nativeBuckets.set(nativeBucket.key, nativeBucket.styleRule);
|
|
1215
|
+
}
|
|
1216
|
+
for (const bucket of expectedBuckets) {
|
|
1217
|
+
const nativeStyleRule = nativeBuckets.get(this.themeLayer.getBucketKey(bucket.mediaText, bucket.selectorText));
|
|
1218
|
+
if (!nativeStyleRule) return false;
|
|
1219
|
+
for (const node of bucket.nodes) node.native = nativeStyleRule;
|
|
1220
|
+
}
|
|
1221
|
+
this.themeLayer.rules.push(...variableRules);
|
|
1222
|
+
this.themeLayer.syncNativeBuckets();
|
|
1223
|
+
if (this.themeLayer.rules.length && !this.rules.includes(this.themeLayer)) this.rules.push(this.themeLayer);
|
|
1224
|
+
return true;
|
|
1225
|
+
}
|
|
1226
|
+
hydrateHydrationManifestAnimations(nativeKeyframesRules) {
|
|
1227
|
+
const animationNames = this.collectHydrationManifestAnimationNames();
|
|
1228
|
+
const hydratedAnimationNames = /* @__PURE__ */ new Set();
|
|
1229
|
+
for (const animationName of animationNames) {
|
|
1230
|
+
if (this.isEmittedGlobalsAnimation(animationName)) {
|
|
1231
|
+
if (nativeKeyframesRules.has(animationName)) hydratedAnimationNames.add(animationName);
|
|
1232
|
+
continue;
|
|
1233
|
+
}
|
|
1234
|
+
const keyframes = this.animations.get(animationName);
|
|
1235
|
+
if (!keyframes) continue;
|
|
1236
|
+
const nativeRule = nativeKeyframesRules.get(animationName);
|
|
1237
|
+
if (!nativeRule) return false;
|
|
1238
|
+
const animationRule = new AnimationRule(animationName, keyframes, this);
|
|
1239
|
+
animationRule.native = nativeRule;
|
|
1240
|
+
this.animationsNonLayer.rules.push(animationRule);
|
|
1241
|
+
this.rules.push(animationRule);
|
|
1242
|
+
hydratedAnimationNames.add(animationName);
|
|
1243
|
+
}
|
|
1244
|
+
for (const animationName of nativeKeyframesRules.keys()) if (!hydratedAnimationNames.has(animationName)) return false;
|
|
1245
|
+
return true;
|
|
1246
|
+
}
|
|
1247
|
+
hydrateHydrationManifestLayer(layer, nativeLayerRule, manifestRules, result) {
|
|
1248
|
+
if (manifestRules.reduce((count, rule) => count + (rule.nodes?.length || 1), 0) !== nativeLayerRule.cssRules.length) return false;
|
|
1249
|
+
layer.native = nativeLayerRule;
|
|
1250
|
+
let nativeIndex = 0;
|
|
1251
|
+
for (const manifestRule of manifestRules) {
|
|
1252
|
+
const hydratedRule = new HydratedGeneratedRule(manifestRule, layer);
|
|
1253
|
+
const nodes = hydratedRule.nodes;
|
|
1254
|
+
if (nodes?.length) for (const node of nodes) node.native = nativeLayerRule.cssRules.item(nativeIndex++) || void 0;
|
|
1255
|
+
else hydratedRule.native = nativeLayerRule.cssRules.item(nativeIndex++) || void 0;
|
|
1256
|
+
layer.rules.push(hydratedRule);
|
|
1257
|
+
layer.insertVariables(hydratedRule);
|
|
1258
|
+
layer.insertAnimations(hydratedRule);
|
|
1259
|
+
this.registerHydratedClassRule(manifestRule.className, hydratedRule);
|
|
1260
|
+
result.allUtilities.push(hydratedRule);
|
|
1261
|
+
}
|
|
1262
|
+
if (layer.rules.length && !this.rules.includes(layer)) this.rules.push(layer);
|
|
1263
|
+
return true;
|
|
1264
|
+
}
|
|
1265
|
+
hydrate(nativeLayerRules) {
|
|
1266
|
+
this.hydrationFailureReason = void 0;
|
|
1267
|
+
if (this.hydrationManifest?.version !== 1 || !Array.isArray(this.hydrationManifest.rules)) return this.failHydration("Missing or invalid hydration manifest.");
|
|
1268
|
+
if (!this.hydrationManifest.rules.length) return this.failHydration(`Hydration manifest has no generated rules for ${MASTER_CSS_RUNTIME_STYLE_SELECTOR}.`);
|
|
1269
|
+
const result = { allUtilities: [] };
|
|
1270
|
+
const manifestLayerRules = this.getHydrationManifestLayerRules();
|
|
1271
|
+
const nativeUtilityLayerRules = /* @__PURE__ */ new Map();
|
|
1272
|
+
const nativeKeyframesRules = /* @__PURE__ */ new Map();
|
|
1273
|
+
let nativeThemeLayer;
|
|
1274
|
+
for (let i = 0; i < nativeLayerRules.length; i++) {
|
|
1275
|
+
const eachNativeCSSRule = nativeLayerRules[i];
|
|
1276
|
+
if (eachNativeCSSRule.constructor.name === "CSSLayerBlockRule") {
|
|
1277
|
+
const eachCSSLayerRule = eachNativeCSSRule;
|
|
1278
|
+
if (eachCSSLayerRule.name === "theme") {
|
|
1279
|
+
if (nativeThemeLayer) return this.failHydration(`Duplicate theme layer in ${MASTER_CSS_RUNTIME_STYLE_SELECTOR}.`);
|
|
1280
|
+
nativeThemeLayer = eachCSSLayerRule;
|
|
1281
|
+
continue;
|
|
1282
|
+
}
|
|
1283
|
+
const layer = this.getUtilityLayerByName(eachCSSLayerRule.name);
|
|
1284
|
+
if (!layer) return this.failHydration(`Unknown layer \`${eachCSSLayerRule.name}\` in ${MASTER_CSS_RUNTIME_STYLE_SELECTOR}.`);
|
|
1285
|
+
if (nativeUtilityLayerRules.has(layer.name)) return this.failHydration(`Duplicate layer \`${eachCSSLayerRule.name}\` in ${MASTER_CSS_RUNTIME_STYLE_SELECTOR}.`);
|
|
1286
|
+
nativeUtilityLayerRules.set(layer.name, eachCSSLayerRule);
|
|
1287
|
+
} else if (eachNativeCSSRule.constructor.name === "CSSKeyframesRule") {
|
|
1288
|
+
const nativeKeyframesRule = eachNativeCSSRule;
|
|
1289
|
+
nativeKeyframesRules.set(nativeKeyframesRule.name, nativeKeyframesRule);
|
|
1290
|
+
} else return this.failHydration(`Unknown top-level rule \`${eachNativeCSSRule.cssText}\` in ${MASTER_CSS_RUNTIME_STYLE_SELECTOR}.`);
|
|
1291
|
+
}
|
|
1292
|
+
for (const [layerName, manifestRules] of manifestLayerRules) {
|
|
1293
|
+
const nativeLayerRule = nativeUtilityLayerRules.get(layerName);
|
|
1294
|
+
if (!nativeLayerRule) return this.failHydration(`Missing layer \`${layerName}\` in ${MASTER_CSS_RUNTIME_STYLE_SELECTOR}.`);
|
|
1295
|
+
if (manifestRules.reduce((count, rule) => count + (rule.nodes?.length || 1), 0) !== nativeLayerRule.cssRules.length) return this.failHydration(`Layer \`${layerName}\` does not match the hydration manifest.`);
|
|
1296
|
+
}
|
|
1297
|
+
for (const [layerName, nativeLayerRule] of nativeUtilityLayerRules) if (!manifestLayerRules.has(layerName) && nativeLayerRule.cssRules.length) return this.failHydration(`Layer \`${layerName}\` has no hydration manifest rules.`);
|
|
1298
|
+
if (!this.hydrateHydrationManifestVariables(nativeThemeLayer)) return this.failHydration("Theme layer does not match the hydration manifest.");
|
|
1299
|
+
if (!this.hydrateHydrationManifestAnimations(nativeKeyframesRules)) return this.failHydration("Keyframes do not match the hydration manifest.");
|
|
1300
|
+
for (const [layerName, nativeLayerRule] of nativeUtilityLayerRules) {
|
|
1301
|
+
const manifestRules = manifestLayerRules.get(layerName);
|
|
1302
|
+
if (!manifestRules?.length) continue;
|
|
1303
|
+
const layer = this.getUtilityLayerByName(layerName);
|
|
1304
|
+
this.hydrateHydrationManifestLayer(layer, nativeLayerRule, manifestRules, result);
|
|
1305
|
+
}
|
|
1306
|
+
debugRuntimeHydrated(this, result);
|
|
1307
|
+
return result;
|
|
1308
|
+
}
|
|
1309
|
+
disconnect() {
|
|
1310
|
+
this.clearPendingAddedClassNames();
|
|
1311
|
+
this.clearPendingRemovedClassNames();
|
|
1312
|
+
this.clearRetainedClassRules();
|
|
1313
|
+
if (!this.observing) return;
|
|
1314
|
+
if (this.observer) {
|
|
1315
|
+
this.observer.disconnect();
|
|
1316
|
+
this.observer = void 0;
|
|
1317
|
+
}
|
|
1318
|
+
this.observing = false;
|
|
1319
|
+
this.reset();
|
|
1320
|
+
this.loadManifest(this.manifest);
|
|
1321
|
+
this.classCounts.clear();
|
|
1322
|
+
this.classTracker.reset();
|
|
1323
|
+
if (!this.progressive) {
|
|
1324
|
+
this.style?.remove();
|
|
1325
|
+
this.style = null;
|
|
1326
|
+
}
|
|
1327
|
+
debugRuntimeDisconnected(this);
|
|
1328
|
+
return this;
|
|
1329
|
+
}
|
|
1330
|
+
refresh(manifest = this.manifest) {
|
|
1331
|
+
this.clearPendingAddedClassNames();
|
|
1332
|
+
this.clearPendingRemovedClassNames();
|
|
1333
|
+
this.clearRetainedClassRules();
|
|
1334
|
+
if (!this.observing || !this.style.sheet) return this;
|
|
1335
|
+
const cssRules = this.style.sheet.cssRules;
|
|
1336
|
+
for (let i = cssRules.length - 1; i >= 0; i--) this.style.sheet.deleteRule(i);
|
|
1337
|
+
super.refresh(manifest);
|
|
1338
|
+
/**
|
|
1339
|
+
* Recreate rules from the current class names against the latest manifest.
|
|
1340
|
+
* 所以 refresh 過後 rules 可能會變多也可能會變少
|
|
1341
|
+
*/
|
|
1342
|
+
this.classCounts.forEach((_, className) => {
|
|
1343
|
+
this.ensureClassRules(className);
|
|
1344
|
+
});
|
|
1345
|
+
debugRuntimeRefreshed(this, manifest);
|
|
1346
|
+
return this;
|
|
1347
|
+
}
|
|
1348
|
+
destroy() {
|
|
1349
|
+
this.disconnect();
|
|
1350
|
+
this.unregister();
|
|
1351
|
+
debugRuntimeDestroyed(this);
|
|
1352
|
+
return this;
|
|
1353
|
+
}
|
|
1354
|
+
};
|
|
1355
|
+
(function(CSSRuntime) {
|
|
1356
|
+
registerGlobal(CSSRuntime);
|
|
1357
|
+
})(CSSRuntime);
|
|
1358
|
+
//#endregion
|
|
1359
|
+
//#region src/css-runtime.ts
|
|
1360
|
+
function resolveManifest(host, manifest) {
|
|
1361
|
+
return typeof manifest === "function" ? manifest(host) : manifest;
|
|
1362
|
+
}
|
|
1363
|
+
function resolveRoot(host, root) {
|
|
1364
|
+
return typeof root === "function" ? root(host) : root || host.shadowRoot;
|
|
1365
|
+
}
|
|
1366
|
+
function resolveEmittedGlobals(host, emittedGlobals) {
|
|
1367
|
+
return typeof emittedGlobals === "function" ? emittedGlobals(host) : emittedGlobals;
|
|
1368
|
+
}
|
|
1369
|
+
function resolveHydrationManifest(host, hydrationManifest) {
|
|
1370
|
+
return typeof hydrationManifest === "function" ? hydrationManifest(host) : hydrationManifest;
|
|
1371
|
+
}
|
|
1372
|
+
function cssRuntime(options) {
|
|
1373
|
+
return function(target) {
|
|
1374
|
+
const connectedCallback = target.prototype.connectedCallback;
|
|
1375
|
+
const disconnectedCallback = target.prototype.disconnectedCallback;
|
|
1376
|
+
return class extends target {
|
|
1377
|
+
cssRuntime;
|
|
1378
|
+
connectedCallback() {
|
|
1379
|
+
connectedCallback?.call(this);
|
|
1380
|
+
const root = resolveRoot(this, options.root);
|
|
1381
|
+
if (!root) throw new Error("`@cssRuntime()` requires a shadow root. Provide `options.root` or create a shadow root before `connectedCallback()` finishes.");
|
|
1382
|
+
const cssRuntime = CSSRuntime.create({
|
|
1383
|
+
manifest: resolveManifest(this, options.manifest),
|
|
1384
|
+
root,
|
|
1385
|
+
emittedGlobals: resolveEmittedGlobals(this, options.emittedGlobals),
|
|
1386
|
+
hydrationManifest: resolveHydrationManifest(this, options.hydrationManifest)
|
|
1387
|
+
});
|
|
1388
|
+
this.cssRuntime = cssRuntime;
|
|
1389
|
+
if (cssRuntime.needsHydrationManifest()) cssRuntime.loadHydrationManifest().then(() => {
|
|
1390
|
+
if (this.cssRuntime === cssRuntime) cssRuntime.observe();
|
|
1391
|
+
});
|
|
1392
|
+
else cssRuntime.observe();
|
|
1393
|
+
}
|
|
1394
|
+
disconnectedCallback() {
|
|
1395
|
+
disconnectedCallback?.call(this);
|
|
1396
|
+
this.cssRuntime?.destroy();
|
|
1397
|
+
this.cssRuntime = void 0;
|
|
1398
|
+
}
|
|
1399
|
+
};
|
|
1400
|
+
};
|
|
1401
|
+
}
|
|
1402
|
+
//#endregion
|
|
1403
|
+
export { RuntimeUtilityLayer, cssRuntime, CSSRuntime as default };
|