@master/css-runtime 2.0.0-rc.54 → 2.0.0-rc.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core.d.ts +2 -4
- package/dist/debug.d.ts +1 -0
- package/dist/global.min.js +1 -1
- package/dist/index.cjs +54 -82
- package/dist/index.d.ts +2 -2
- package/dist/index.development.cjs +443 -0
- package/dist/index.development.d.ts +1 -0
- package/dist/index.development.mjs +440 -0
- package/dist/index.mjs +53 -81
- package/dist/init.d.ts +10 -0
- package/dist/register-global.d.ts +9 -0
- package/package.json +1 -1
- package/dist/init-css-runtime.d.ts +0 -9
- package/dist/types/global.d.ts +0 -9
package/dist/index.cjs
CHANGED
|
@@ -3,45 +3,40 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var css = require('@master/css');
|
|
6
|
+
var cssDevtoolsHook = require('@master/css-devtools-hook');
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
function registerGlobal(CSSRuntime) {
|
|
9
|
+
if (!globalThis.CSSRuntime) globalThis.CSSRuntime = CSSRuntime;
|
|
10
|
+
cssDevtoolsHook.installHook();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
class CSSRuntime extends css.MasterCSS {
|
|
8
14
|
root;
|
|
9
15
|
customConfig;
|
|
10
|
-
|
|
16
|
+
static instances = new WeakMap();
|
|
11
17
|
host;
|
|
12
18
|
observing;
|
|
13
19
|
progressive;
|
|
14
|
-
// @ts-expect-error
|
|
15
20
|
container;
|
|
16
21
|
observer;
|
|
17
22
|
constructor(root = document, customConfig = css.config){
|
|
18
23
|
super(customConfig), this.root = root, this.customConfig = customConfig, this.observing = false, this.progressive = false;
|
|
19
|
-
|
|
20
|
-
this.supportVendors = new Set();
|
|
21
|
-
const styleDeclaration = document.documentElement.style;
|
|
22
|
-
if ('webkitTransform' in styleDeclaration) this.supportVendors.add('webkit');
|
|
23
|
-
if ('MozTransform' in styleDeclaration) this.supportVendors.add('moz');
|
|
24
|
-
if ('msTransform' in styleDeclaration) this.supportVendors.add('ms');
|
|
25
|
-
if ('OTransform' in styleDeclaration) this.supportVendors.add('o');
|
|
26
|
-
}
|
|
27
|
-
init() {
|
|
28
|
-
const existingRuntimeCSS = globalThis.runtimeCSSs.find((eachCSS)=>eachCSS.root === this.root);
|
|
29
|
-
if (existingRuntimeCSS) throw new Error('Cannot create multiple RuntimeCSS instances for the same root element.');
|
|
30
|
-
const rootConstructorName = this.root?.constructor.name;
|
|
24
|
+
const rootConstructorName = root?.constructor.name;
|
|
31
25
|
if (rootConstructorName === 'HTMLDocument' || rootConstructorName === 'Document') {
|
|
32
|
-
|
|
33
|
-
this.root.defaultView.globalThis.runtimeCSS = this;
|
|
34
|
-
// @ts-ignore readonly
|
|
26
|
+
this.root.defaultView.globalThis.cssRuntime = this;
|
|
35
27
|
this.container = this.root.head;
|
|
36
|
-
// @ts-ignore readonly
|
|
37
28
|
this.host = this.root.documentElement;
|
|
38
29
|
} else {
|
|
39
|
-
// @ts-ignore readonly
|
|
40
30
|
this.container = this.root;
|
|
41
|
-
// @ts-ignore readonly
|
|
42
31
|
this.host = this.root.host;
|
|
43
32
|
}
|
|
44
|
-
|
|
33
|
+
this.supportVendors = new Set();
|
|
34
|
+
const styleDeclaration = document.documentElement.style;
|
|
35
|
+
if ('webkitTransform' in styleDeclaration) this.supportVendors.add('webkit');
|
|
36
|
+
if ('MozTransform' in styleDeclaration) this.supportVendors.add('moz');
|
|
37
|
+
if ('msTransform' in styleDeclaration) this.supportVendors.add('ms');
|
|
38
|
+
if ('OTransform' in styleDeclaration) this.supportVendors.add('o');
|
|
39
|
+
globalThis.CSSRuntime.instances.set(this.root, this);
|
|
45
40
|
}
|
|
46
41
|
/**
|
|
47
42
|
* Observe the DOM for changes and update the running stylesheet. (browser only)
|
|
@@ -70,10 +65,10 @@ class RuntimeCSS extends css.MasterCSS {
|
|
|
70
65
|
const firstConnectedElementsClasses = new Map();
|
|
71
66
|
const addClasses = (element)=>{
|
|
72
67
|
element.classList.forEach((className)=>{
|
|
73
|
-
if (this.
|
|
74
|
-
this.
|
|
68
|
+
if (this.classCounts.has(className)) {
|
|
69
|
+
this.classCounts.set(className, this.classCounts.get(className) + 1);
|
|
75
70
|
} else {
|
|
76
|
-
this.
|
|
71
|
+
this.classCounts.set(className, 1);
|
|
77
72
|
this.add(className);
|
|
78
73
|
}
|
|
79
74
|
const classes = firstConnectedElementsClasses.get(element);
|
|
@@ -98,14 +93,14 @@ class RuntimeCSS extends css.MasterCSS {
|
|
|
98
93
|
// const test = ''
|
|
99
94
|
// if (test) {
|
|
100
95
|
// console.log('')
|
|
101
|
-
// console.log(`${test}: ${this.
|
|
96
|
+
// console.log(`${test}: ${this.classCounts.get(test)}`)
|
|
102
97
|
// }
|
|
103
|
-
const
|
|
98
|
+
const eachClassCounts = new Map();
|
|
104
99
|
const targetFirstAttrMutationRecord = new Map();
|
|
105
|
-
const
|
|
100
|
+
const updateClassCount = (classes, isAdding = false)=>{
|
|
106
101
|
const usage = isAdding ? 1 : -1;
|
|
107
102
|
classes.forEach((className)=>{
|
|
108
|
-
|
|
103
|
+
eachClassCounts.set(className, (eachClassCounts.get(className) || 0) + usage);
|
|
109
104
|
});
|
|
110
105
|
};
|
|
111
106
|
const connectedStatusMap = new Map();
|
|
@@ -148,12 +143,12 @@ class RuntimeCSS extends css.MasterCSS {
|
|
|
148
143
|
targetFirstAttrMutationRecord.delete(target);
|
|
149
144
|
}
|
|
150
145
|
if (adding) {
|
|
151
|
-
|
|
146
|
+
updateClassCount(target.classList, adding);
|
|
152
147
|
} else {
|
|
153
148
|
if (firstAttrMutationRecord) {
|
|
154
|
-
|
|
149
|
+
updateClassCount(firstAttrMutationRecord.oldValue ? firstAttrMutationRecord.oldValue.split(/\s+/) : [], adding);
|
|
155
150
|
} else {
|
|
156
|
-
|
|
151
|
+
updateClassCount(target.classList, adding);
|
|
157
152
|
}
|
|
158
153
|
disconnectedStatusMap.forEach((disconnectedTargetStatus, disconnectedTarget)=>{
|
|
159
154
|
if (disconnectedTargetStatus.mutationRecord.target === target && disconnectedTargetStatus.change !== 0) {
|
|
@@ -178,59 +173,33 @@ class RuntimeCSS extends css.MasterCSS {
|
|
|
178
173
|
const removedClasses = oldClassList.filter((c)=>!newClassList.contains(c));
|
|
179
174
|
if (addedClasses.length) {
|
|
180
175
|
// console.log('[attribute]', '[add] ', addedClasses, target)
|
|
181
|
-
|
|
176
|
+
updateClassCount(addedClasses, true);
|
|
182
177
|
}
|
|
183
178
|
if (removedClasses.length) {
|
|
184
179
|
// console.log('[attribute]', '[remove]', removedClasses, target)
|
|
185
|
-
|
|
180
|
+
updateClassCount(removedClasses, false);
|
|
186
181
|
}
|
|
187
182
|
});
|
|
188
183
|
/**
|
|
189
184
|
* Merge the class usage changes into the current class usage map.
|
|
190
|
-
*/
|
|
191
|
-
let currentCount = this.
|
|
185
|
+
*/ eachClassCounts.forEach((countChange, className)=>{
|
|
186
|
+
let currentCount = this.classCounts.get(className) || 0;
|
|
192
187
|
let newCount = currentCount + countChange;
|
|
193
188
|
if (newCount > 0) {
|
|
194
|
-
this.
|
|
189
|
+
this.classCounts.set(className, newCount);
|
|
195
190
|
if (currentCount === 0) {
|
|
196
191
|
this.add(className);
|
|
197
192
|
}
|
|
198
193
|
} else {
|
|
199
|
-
this.
|
|
194
|
+
this.classCounts.delete(className);
|
|
200
195
|
this.remove(className);
|
|
201
196
|
}
|
|
202
197
|
});
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
// element.classList.forEach((className) => {
|
|
209
|
-
// if (Object.prototype.hasOwnProperty.call(safeClassUsages, className)) {
|
|
210
|
-
// safeClassUsages[className]++
|
|
211
|
-
// } else {
|
|
212
|
-
// safeClassUsages[className] = 1
|
|
213
|
-
// }
|
|
214
|
-
// })
|
|
215
|
-
// })
|
|
216
|
-
// this.host.classList.forEach((className) => {
|
|
217
|
-
// if (Object.prototype.hasOwnProperty.call(safeClassUsages, className)) {
|
|
218
|
-
// safeClassUsages[className]++
|
|
219
|
-
// } else {
|
|
220
|
-
// safeClassUsages[className] = 1
|
|
221
|
-
// }
|
|
222
|
-
// })
|
|
223
|
-
// for (const className in safeClassUsages) {
|
|
224
|
-
// if (this.classUsages.get(className) !== safeClassUsages[className]) {
|
|
225
|
-
// throw new Error(`[css] ${className} ${this.classUsages.get(className)} (correct: ${safeClassUsages[className]})`)
|
|
226
|
-
// }
|
|
227
|
-
// }
|
|
228
|
-
// this.classUsages.forEach((count, className) => {
|
|
229
|
-
// if (!Object.prototype.hasOwnProperty.call(safeClassUsages, className)) {
|
|
230
|
-
// throw new Error(`[css] ${className} ${count} (correct: 0)`)
|
|
231
|
-
// }
|
|
232
|
-
// })
|
|
233
|
-
// end: debug
|
|
198
|
+
globalThis.__MASTER_CSS_DEVTOOLS_HOOK__?.emit('mutated', {
|
|
199
|
+
records: mutationRecords,
|
|
200
|
+
classCounts: eachClassCounts,
|
|
201
|
+
cssRuntime: this
|
|
202
|
+
});
|
|
234
203
|
});
|
|
235
204
|
this.observer.observe(this.root, {
|
|
236
205
|
attributes: true,
|
|
@@ -361,7 +330,7 @@ class RuntimeCSS extends css.MasterCSS {
|
|
|
361
330
|
// @ts-ignore
|
|
362
331
|
this.observing = false;
|
|
363
332
|
this.reset();
|
|
364
|
-
this.
|
|
333
|
+
this.classCounts.clear();
|
|
365
334
|
if (!this.progressive) {
|
|
366
335
|
this.style?.remove();
|
|
367
336
|
this.style = null;
|
|
@@ -378,25 +347,28 @@ class RuntimeCSS extends css.MasterCSS {
|
|
|
378
347
|
}
|
|
379
348
|
destroy() {
|
|
380
349
|
this.disconnect();
|
|
381
|
-
|
|
350
|
+
globalThis.CSSRuntime.instances.delete(this.root);
|
|
382
351
|
return this;
|
|
383
352
|
}
|
|
384
353
|
}
|
|
385
|
-
|
|
386
|
-
(
|
|
387
|
-
|
|
388
|
-
globalThis.runtimeCSSs = runtimeCSSs;
|
|
389
|
-
})();
|
|
354
|
+
(function(CSSRuntime) {
|
|
355
|
+
registerGlobal(CSSRuntime);
|
|
356
|
+
})(CSSRuntime);
|
|
390
357
|
|
|
391
358
|
/**
|
|
392
|
-
* Initialize a new
|
|
359
|
+
* Initialize a new CSSRuntime instance and observe the target root
|
|
393
360
|
* @param config master css config
|
|
394
|
-
* @param
|
|
361
|
+
* @param root target root to observe
|
|
362
|
+
* @param autoObserve auto observe the target root
|
|
395
363
|
* @returns master css instance
|
|
396
|
-
*/ function initCSSRuntime(config) {
|
|
397
|
-
|
|
364
|
+
*/ function initCSSRuntime(config, root = document, autoObserve = true) {
|
|
365
|
+
let cssRuntime = globalThis.CSSRuntime.instances.get(root);
|
|
366
|
+
if (cssRuntime) return cssRuntime;
|
|
367
|
+
cssRuntime = new CSSRuntime(document, config);
|
|
368
|
+
if (autoObserve) cssRuntime.observe();
|
|
369
|
+
return cssRuntime;
|
|
398
370
|
}
|
|
399
371
|
|
|
400
|
-
exports.
|
|
401
|
-
exports.default =
|
|
372
|
+
exports.CSSRuntime = CSSRuntime;
|
|
373
|
+
exports.default = CSSRuntime;
|
|
402
374
|
exports.initCSSRuntime = initCSSRuntime;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export { default as initCSSRuntime } from './init
|
|
1
|
+
export { default as CSSRuntime, default } from './core';
|
|
2
|
+
export { default as initCSSRuntime } from './init';
|
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var css = require('@master/css');
|
|
4
|
+
var cssDevtoolsHook = require('@master/css-devtools-hook');
|
|
5
|
+
|
|
6
|
+
const createLogger = (type)=>{
|
|
7
|
+
return (message, ...args)=>{
|
|
8
|
+
if (console[type]) {
|
|
9
|
+
console[type](`%cMaster CSS%c ${message}`, 'color: gray;', 'color: inherit;', ...args);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
const loggers = {
|
|
14
|
+
debug: createLogger('debug'),
|
|
15
|
+
info: createLogger('info'),
|
|
16
|
+
warn: createLogger('warn'),
|
|
17
|
+
error: createLogger('error'),
|
|
18
|
+
trace: createLogger('trace'),
|
|
19
|
+
table: console.table,
|
|
20
|
+
group: console.group,
|
|
21
|
+
groupCollapsed: console.group,
|
|
22
|
+
groupEnd: console.groupEnd,
|
|
23
|
+
time: console.time,
|
|
24
|
+
timeEnd: console.timeEnd
|
|
25
|
+
};
|
|
26
|
+
const log = createLogger('log');
|
|
27
|
+
Object.assign(log, loggers);
|
|
28
|
+
|
|
29
|
+
function startDebug() {
|
|
30
|
+
log('debug mode is enabled by default in development.');
|
|
31
|
+
const hook = cssDevtoolsHook.installHook();
|
|
32
|
+
hook.on('mutated', ({ records, classCounts, cssRuntime })=>{
|
|
33
|
+
const actualClassCounts = {};
|
|
34
|
+
let errored = false;
|
|
35
|
+
const resolveClass = (className)=>{
|
|
36
|
+
if (Object.prototype.hasOwnProperty.call(actualClassCounts, className)) {
|
|
37
|
+
actualClassCounts[className]++;
|
|
38
|
+
} else {
|
|
39
|
+
actualClassCounts[className] = 1;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
(cssRuntime.root.constructor.name === 'HTMLDocument' ? cssRuntime.host : cssRuntime.container).querySelectorAll('[class]').forEach((element)=>{
|
|
43
|
+
element.classList.forEach(resolveClass);
|
|
44
|
+
});
|
|
45
|
+
cssRuntime.host.classList.forEach(resolveClass);
|
|
46
|
+
for(const className in actualClassCounts){
|
|
47
|
+
const eachCount = cssRuntime.classCounts.get(className);
|
|
48
|
+
const eachActualCount = actualClassCounts[className];
|
|
49
|
+
if (eachCount !== eachActualCount) {
|
|
50
|
+
log.error(`Count mismatch for`, {
|
|
51
|
+
class: className,
|
|
52
|
+
expected: eachActualCount,
|
|
53
|
+
received: eachCount
|
|
54
|
+
});
|
|
55
|
+
errored = true;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
cssRuntime.classCounts.forEach((eachCount, className)=>{
|
|
59
|
+
if (!Object.prototype.hasOwnProperty.call(actualClassCounts, className)) {
|
|
60
|
+
log.error(`Count mismatch for`, {
|
|
61
|
+
class: className,
|
|
62
|
+
expected: 0,
|
|
63
|
+
received: eachCount
|
|
64
|
+
});
|
|
65
|
+
errored = true;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
if (errored) {
|
|
69
|
+
console.debug('Records:', records);
|
|
70
|
+
console.debug('Counts:', classCounts);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function registerGlobal(CSSRuntime) {
|
|
76
|
+
if (!globalThis.CSSRuntime) globalThis.CSSRuntime = CSSRuntime;
|
|
77
|
+
cssDevtoolsHook.installHook();
|
|
78
|
+
{
|
|
79
|
+
startDebug();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
class CSSRuntime extends css.MasterCSS {
|
|
84
|
+
root;
|
|
85
|
+
customConfig;
|
|
86
|
+
static instances = new WeakMap();
|
|
87
|
+
host;
|
|
88
|
+
observing;
|
|
89
|
+
progressive;
|
|
90
|
+
container;
|
|
91
|
+
observer;
|
|
92
|
+
constructor(root = document, customConfig = css.config){
|
|
93
|
+
super(customConfig), this.root = root, this.customConfig = customConfig, this.observing = false, this.progressive = false;
|
|
94
|
+
const rootConstructorName = root?.constructor.name;
|
|
95
|
+
if (rootConstructorName === 'HTMLDocument' || rootConstructorName === 'Document') {
|
|
96
|
+
this.root.defaultView.globalThis.cssRuntime = this;
|
|
97
|
+
this.container = this.root.head;
|
|
98
|
+
this.host = this.root.documentElement;
|
|
99
|
+
} else {
|
|
100
|
+
this.container = this.root;
|
|
101
|
+
this.host = this.root.host;
|
|
102
|
+
}
|
|
103
|
+
this.supportVendors = new Set();
|
|
104
|
+
const styleDeclaration = document.documentElement.style;
|
|
105
|
+
if ('webkitTransform' in styleDeclaration) this.supportVendors.add('webkit');
|
|
106
|
+
if ('MozTransform' in styleDeclaration) this.supportVendors.add('moz');
|
|
107
|
+
if ('msTransform' in styleDeclaration) this.supportVendors.add('ms');
|
|
108
|
+
if ('OTransform' in styleDeclaration) this.supportVendors.add('o');
|
|
109
|
+
globalThis.CSSRuntime.instances.set(this.root, this);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Observe the DOM for changes and update the running stylesheet. (browser only)
|
|
113
|
+
* @param options mutation observer options
|
|
114
|
+
* @returns this
|
|
115
|
+
*/ observe() {
|
|
116
|
+
if (this.observing) return this;
|
|
117
|
+
if (this.root.styleSheets) for (const sheet of this.root.styleSheets){
|
|
118
|
+
const { ownerNode } = sheet;
|
|
119
|
+
if (ownerNode && ownerNode.id === 'master') {
|
|
120
|
+
this.style = ownerNode;
|
|
121
|
+
// @ts-ignore
|
|
122
|
+
this.progressive = true;
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (this.progressive) {
|
|
127
|
+
this.hydrate(this.style.sheet.cssRules);
|
|
128
|
+
} else {
|
|
129
|
+
this.style = document.createElement('style');
|
|
130
|
+
this.style.id = 'master';
|
|
131
|
+
this.container.append(this.style);
|
|
132
|
+
this.style.sheet.insertRule(this.layerStatementRule.text);
|
|
133
|
+
this.layerStatementRule.nodes[0].native = this.style.sheet.cssRules.item(0);
|
|
134
|
+
}
|
|
135
|
+
const firstConnectedElementsClasses = new Map();
|
|
136
|
+
const addClasses = (element)=>{
|
|
137
|
+
element.classList.forEach((className)=>{
|
|
138
|
+
if (this.classCounts.has(className)) {
|
|
139
|
+
this.classCounts.set(className, this.classCounts.get(className) + 1);
|
|
140
|
+
} else {
|
|
141
|
+
this.classCounts.set(className, 1);
|
|
142
|
+
this.add(className);
|
|
143
|
+
}
|
|
144
|
+
const classes = firstConnectedElementsClasses.get(element);
|
|
145
|
+
if (classes) {
|
|
146
|
+
classes.push(className);
|
|
147
|
+
} else {
|
|
148
|
+
firstConnectedElementsClasses.set(element, [
|
|
149
|
+
className
|
|
150
|
+
]);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
addClasses(this.host);
|
|
155
|
+
/**
|
|
156
|
+
* 待所有 DOM 結構完成解析後,開始繪製 Rule 樣式
|
|
157
|
+
*/ (this.root.constructor.name === 'HTMLDocument' ? this.host : this.container).querySelectorAll('[class]').forEach((element)=>{
|
|
158
|
+
addClasses(element);
|
|
159
|
+
});
|
|
160
|
+
// @ts-expect-error readonly
|
|
161
|
+
this.observer = new MutationObserver((mutationRecords)=>{
|
|
162
|
+
// console.clear()
|
|
163
|
+
// const test = ''
|
|
164
|
+
// if (test) {
|
|
165
|
+
// console.log('')
|
|
166
|
+
// console.log(`${test}: ${this.classCounts.get(test)}`)
|
|
167
|
+
// }
|
|
168
|
+
const eachClassCounts = new Map();
|
|
169
|
+
const targetFirstAttrMutationRecord = new Map();
|
|
170
|
+
const updateClassCount = (classes, isAdding = false)=>{
|
|
171
|
+
const usage = isAdding ? 1 : -1;
|
|
172
|
+
classes.forEach((className)=>{
|
|
173
|
+
eachClassCounts.set(className, (eachClassCounts.get(className) || 0) + usage);
|
|
174
|
+
});
|
|
175
|
+
};
|
|
176
|
+
const connectedStatusMap = new Map();
|
|
177
|
+
const disconnectedStatusMap = new Map();
|
|
178
|
+
const recordStatus = (target, mutationRecord, map, adding)=>{
|
|
179
|
+
const status = map.get(target);
|
|
180
|
+
if (status) {
|
|
181
|
+
status.change += adding ? 1 : -1;
|
|
182
|
+
status.mutationRecord = mutationRecord;
|
|
183
|
+
} else {
|
|
184
|
+
map.set(target, {
|
|
185
|
+
change: adding ? 1 : -1,
|
|
186
|
+
mutationRecord
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
mutationRecords.forEach((mutationRecord)=>{
|
|
191
|
+
const target = mutationRecord.target;
|
|
192
|
+
switch(mutationRecord.type){
|
|
193
|
+
case 'attributes':
|
|
194
|
+
if (!targetFirstAttrMutationRecord.has(target)) {
|
|
195
|
+
targetFirstAttrMutationRecord.set(target, mutationRecord);
|
|
196
|
+
}
|
|
197
|
+
break;
|
|
198
|
+
case 'childList':
|
|
199
|
+
const targetStatusMap = target.isConnected ? connectedStatusMap : disconnectedStatusMap;
|
|
200
|
+
mutationRecord.addedNodes.forEach((node)=>'classList' in node && recordStatus(node, mutationRecord, targetStatusMap, true));
|
|
201
|
+
mutationRecord.removedNodes.forEach((node)=>'classList' in node && recordStatus(node, mutationRecord, targetStatusMap, false));
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
const updatedTargetChangeMap = new Map();
|
|
206
|
+
const updateTarget = (target, adding)=>{
|
|
207
|
+
const change = updatedTargetChangeMap.get(target) || 0;
|
|
208
|
+
const newChange = change + (adding ? 1 : -1);
|
|
209
|
+
if (newChange >= -1 && newChange <= 1) {
|
|
210
|
+
updatedTargetChangeMap.set(target, newChange);
|
|
211
|
+
const firstAttrMutationRecord = targetFirstAttrMutationRecord.get(target);
|
|
212
|
+
if (firstAttrMutationRecord) {
|
|
213
|
+
targetFirstAttrMutationRecord.delete(target);
|
|
214
|
+
}
|
|
215
|
+
if (adding) {
|
|
216
|
+
updateClassCount(target.classList, adding);
|
|
217
|
+
} else {
|
|
218
|
+
if (firstAttrMutationRecord) {
|
|
219
|
+
updateClassCount(firstAttrMutationRecord.oldValue ? firstAttrMutationRecord.oldValue.split(/\s+/) : [], adding);
|
|
220
|
+
} else {
|
|
221
|
+
updateClassCount(target.classList, adding);
|
|
222
|
+
}
|
|
223
|
+
disconnectedStatusMap.forEach((disconnectedTargetStatus, disconnectedTarget)=>{
|
|
224
|
+
if (disconnectedTargetStatus.mutationRecord.target === target && disconnectedTargetStatus.change !== 0) {
|
|
225
|
+
updateTarget(disconnectedTarget, disconnectedTargetStatus.change > 0);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
for (const child of target.children){
|
|
230
|
+
updateTarget(child, adding);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
connectedStatusMap.forEach(({ change }, target)=>change !== 0 && updateTarget(target, change > 0));
|
|
235
|
+
targetFirstAttrMutationRecord.forEach((mutation, target)=>{
|
|
236
|
+
if (!target.isConnected) return;
|
|
237
|
+
const oldClassList = mutation.oldValue ? mutation.oldValue.split(/\s+/) : [];
|
|
238
|
+
const newClassList = target.classList;
|
|
239
|
+
const addedClasses = [];
|
|
240
|
+
newClassList.forEach((c)=>{
|
|
241
|
+
if (!oldClassList.includes(c)) addedClasses.push(c);
|
|
242
|
+
});
|
|
243
|
+
const removedClasses = oldClassList.filter((c)=>!newClassList.contains(c));
|
|
244
|
+
if (addedClasses.length) {
|
|
245
|
+
// console.log('[attribute]', '[add] ', addedClasses, target)
|
|
246
|
+
updateClassCount(addedClasses, true);
|
|
247
|
+
}
|
|
248
|
+
if (removedClasses.length) {
|
|
249
|
+
// console.log('[attribute]', '[remove]', removedClasses, target)
|
|
250
|
+
updateClassCount(removedClasses, false);
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
/**
|
|
254
|
+
* Merge the class usage changes into the current class usage map.
|
|
255
|
+
*/ eachClassCounts.forEach((countChange, className)=>{
|
|
256
|
+
let currentCount = this.classCounts.get(className) || 0;
|
|
257
|
+
let newCount = currentCount + countChange;
|
|
258
|
+
if (newCount > 0) {
|
|
259
|
+
this.classCounts.set(className, newCount);
|
|
260
|
+
if (currentCount === 0) {
|
|
261
|
+
this.add(className);
|
|
262
|
+
}
|
|
263
|
+
} else {
|
|
264
|
+
this.classCounts.delete(className);
|
|
265
|
+
this.remove(className);
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
globalThis.__MASTER_CSS_DEVTOOLS_HOOK__?.emit('mutated', {
|
|
269
|
+
records: mutationRecords,
|
|
270
|
+
classCounts: eachClassCounts,
|
|
271
|
+
cssRuntime: this
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
this.observer.observe(this.root, {
|
|
275
|
+
attributes: true,
|
|
276
|
+
attributeOldValue: true,
|
|
277
|
+
attributeFilter: [
|
|
278
|
+
'class'
|
|
279
|
+
],
|
|
280
|
+
childList: true,
|
|
281
|
+
subtree: true
|
|
282
|
+
});
|
|
283
|
+
if (!this.progressive) {
|
|
284
|
+
this.host.style.removeProperty('display');
|
|
285
|
+
}
|
|
286
|
+
// @ts-ignore
|
|
287
|
+
this.observing = true;
|
|
288
|
+
return this;
|
|
289
|
+
}
|
|
290
|
+
hydrate(nativeLayerRules) {
|
|
291
|
+
const cssLayerRules = [];
|
|
292
|
+
const checkSheet = new CSSStyleSheet();
|
|
293
|
+
for(let i = 0; i < nativeLayerRules.length; i++){
|
|
294
|
+
const eachNativeCSSRule = nativeLayerRules[i];
|
|
295
|
+
if (eachNativeCSSRule.constructor.name === 'CSSLayerBlockRule') {
|
|
296
|
+
const eachCSSLayerRule = eachNativeCSSRule;
|
|
297
|
+
if (eachNativeCSSRule.name === 'theme') {
|
|
298
|
+
this.themeLayer.native = eachCSSLayerRule;
|
|
299
|
+
let variableRule;
|
|
300
|
+
let lastVariableName;
|
|
301
|
+
for(let j = 0; j < eachCSSLayerRule.cssRules.length; j++){
|
|
302
|
+
const cssRule = eachCSSLayerRule.cssRules[j];
|
|
303
|
+
const variableCSSRule = cssRule.constructor.name === 'CSSMediaRule' ? cssRule.cssRules[0] : cssRule;
|
|
304
|
+
const variableName = variableCSSRule.style[0].slice(2);
|
|
305
|
+
if (variableName !== lastVariableName) {
|
|
306
|
+
lastVariableName = variableName;
|
|
307
|
+
variableRule = new css.Rule(variableName);
|
|
308
|
+
this.themeLayer.rules.push(variableRule);
|
|
309
|
+
this.themeLayer.usages[variableRule.name] = 0;
|
|
310
|
+
}
|
|
311
|
+
variableRule?.nodes.push({
|
|
312
|
+
native: cssRule,
|
|
313
|
+
text: cssRule.cssText
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
if (this.themeLayer.rules.length) this.rules.push(this.themeLayer);
|
|
317
|
+
} else {
|
|
318
|
+
cssLayerRules.push(eachCSSLayerRule);
|
|
319
|
+
}
|
|
320
|
+
} else if (eachNativeCSSRule.constructor.name === 'CSSLayerStatementRule') {
|
|
321
|
+
this.layerStatementRule.nodes[0].native = eachNativeCSSRule;
|
|
322
|
+
} else if (eachNativeCSSRule.constructor.name === 'CSSKeyframesRule') {
|
|
323
|
+
const keyframsRule = eachNativeCSSRule;
|
|
324
|
+
const animationRule = new css.Rule(keyframsRule.name, [
|
|
325
|
+
{
|
|
326
|
+
native: keyframsRule,
|
|
327
|
+
text: keyframsRule.cssText
|
|
328
|
+
}
|
|
329
|
+
]);
|
|
330
|
+
this.animationsNonLayer.rules.push(animationRule);
|
|
331
|
+
this.rules.push(animationRule);
|
|
332
|
+
this.animationsNonLayer.usages[animationRule.name] = 0;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
for (const eachCSSLayerRule of cssLayerRules){
|
|
336
|
+
const nativeLayerRules = Array.from(eachCSSLayerRule.cssRules);
|
|
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
|
+
default:
|
|
349
|
+
layer = this.generalLayer;
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
layer.native = eachCSSLayerRule;
|
|
353
|
+
for (const eachNativeRule of nativeLayerRules){
|
|
354
|
+
const selectorText = this.getSelectorText(eachNativeRule);
|
|
355
|
+
if (!selectorText) {
|
|
356
|
+
console.error(`Cannot get the selector text from \`${eachNativeRule.cssText}\`. (${layer.name}) (https://rc.css.master.co/messages/hydration-errors)`);
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
const createdRules = this.createFromSelectorText(selectorText);
|
|
360
|
+
if (createdRules) {
|
|
361
|
+
for (const createdRule of createdRules){
|
|
362
|
+
layer.rules.push(createdRule);
|
|
363
|
+
layer.insertVariables(createdRule);
|
|
364
|
+
layer.insertAnimations(createdRule);
|
|
365
|
+
createdRule.nodes.forEach((node)=>{
|
|
366
|
+
const createdNodeNativeRule = checkSheet.cssRules.item(checkSheet.insertRule(node.text));
|
|
367
|
+
if (createdNodeNativeRule) {
|
|
368
|
+
for (const eachFindingNativeRule of nativeLayerRules){
|
|
369
|
+
if (createdNodeNativeRule.cssText === eachFindingNativeRule.cssText) {
|
|
370
|
+
node.native = eachFindingNativeRule;
|
|
371
|
+
nativeLayerRules.splice(nativeLayerRules.indexOf(eachFindingNativeRule), 1);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
console.error(`Cannot retrieve CSS rule for \`${node.selectorText}\`. (${layer.name}) (https://rc.css.master.co/messages/hydration-errors)`);
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
} else {
|
|
380
|
+
console.error(`Cannot recognize \`${eachNativeRule.cssText}\`. (${layer.name}) (https://rc.css.master.co/messages/hydration-errors)`);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (layer.rules.length) this.rules.push(layer);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
getSelectorText(cssRule) {
|
|
387
|
+
if ('selectorText' in cssRule) {
|
|
388
|
+
return cssRule.selectorText;
|
|
389
|
+
} else if ('cssRules' in cssRule) {
|
|
390
|
+
return this.getSelectorText(cssRule.cssRules.item(0));
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
disconnect() {
|
|
394
|
+
if (!this.observing) return;
|
|
395
|
+
if (this.observer) {
|
|
396
|
+
this.observer.disconnect();
|
|
397
|
+
// @ts-expect-error readonly
|
|
398
|
+
this.observer = undefined;
|
|
399
|
+
}
|
|
400
|
+
// @ts-ignore
|
|
401
|
+
this.observing = false;
|
|
402
|
+
this.reset();
|
|
403
|
+
this.classCounts.clear();
|
|
404
|
+
if (!this.progressive) {
|
|
405
|
+
this.style?.remove();
|
|
406
|
+
this.style = null;
|
|
407
|
+
}
|
|
408
|
+
return this;
|
|
409
|
+
}
|
|
410
|
+
refresh(customConfig = this.customConfig) {
|
|
411
|
+
if (!this.observing || !this.style.sheet) return this;
|
|
412
|
+
for(let i = 1; i <= this.style.sheet.cssRules.length - 1; i++){
|
|
413
|
+
this.style.sheet.deleteRule(i);
|
|
414
|
+
}
|
|
415
|
+
super.refresh(customConfig);
|
|
416
|
+
return this;
|
|
417
|
+
}
|
|
418
|
+
destroy() {
|
|
419
|
+
this.disconnect();
|
|
420
|
+
globalThis.CSSRuntime.instances.delete(this.root);
|
|
421
|
+
return this;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
(function(CSSRuntime) {
|
|
425
|
+
registerGlobal(CSSRuntime);
|
|
426
|
+
})(CSSRuntime);
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Initialize a new CSSRuntime instance and observe the target root
|
|
430
|
+
* @param config master css config
|
|
431
|
+
* @param root target root to observe
|
|
432
|
+
* @param autoObserve auto observe the target root
|
|
433
|
+
* @returns master css instance
|
|
434
|
+
*/ function initCSSRuntime(config, root = document, autoObserve = true) {
|
|
435
|
+
let cssRuntime = globalThis.CSSRuntime.instances.get(root);
|
|
436
|
+
if (cssRuntime) return cssRuntime;
|
|
437
|
+
cssRuntime = new CSSRuntime(document, config);
|
|
438
|
+
if (autoObserve) cssRuntime.observe();
|
|
439
|
+
return cssRuntime;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
exports.CSSRuntime = CSSRuntime;
|
|
443
|
+
exports.initCSSRuntime = initCSSRuntime;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './index';
|