@master/css-runtime 2.0.0-rc.57 → 2.0.0-rc.59

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/index.cjs CHANGED
@@ -10,6 +10,110 @@ function registerGlobal(CSSRuntime) {
10
10
  cssDevtoolsHook.installHook();
11
11
  }
12
12
 
13
+ function findNativeCSSRuleIndex(cssRules, nativeRule) {
14
+ for(let i = 0; i < cssRules.length; i++){
15
+ if (cssRules[i] === nativeRule) {
16
+ return i;
17
+ }
18
+ }
19
+ return -1;
20
+ }
21
+
22
+ class RuntimeLayer extends css.Layer {
23
+ name;
24
+ cssRuntime;
25
+ rules;
26
+ tokenCounts;
27
+ native;
28
+ constructor(name, cssRuntime){
29
+ super(name, cssRuntime), this.name = name, this.cssRuntime = cssRuntime, this.rules = [], this.tokenCounts = new Map(), this.native = null;
30
+ }
31
+ attach() {
32
+ super.attach();
33
+ const nativeSheet = this.cssRuntime.style?.sheet;
34
+ if (nativeSheet && !this.native?.parentStyleSheet) {
35
+ const insertedIndex = nativeSheet.insertRule(this.text, nativeSheet.cssRules.length);
36
+ this.native = nativeSheet.cssRules.item(insertedIndex);
37
+ }
38
+ }
39
+ insert(rule, index = this.rules.length) {
40
+ const insertedIndex = super.insert(rule, index);
41
+ if (insertedIndex === undefined) return;
42
+ if (this.native) {
43
+ let cssRuleIndex = 0;
44
+ const lastCssRule = function getLastCssRule(layer, index) {
45
+ let lastCssRule;
46
+ const previouRule = layer.rules[index];
47
+ if (previouRule && 'nodes' in previouRule) {
48
+ if (!previouRule.nodes.length) return getLastCssRule(layer, index - 1);
49
+ const lastNativeRule = previouRule.nodes[previouRule.nodes.length - 1];
50
+ lastCssRule = lastNativeRule.native;
51
+ }
52
+ return lastCssRule;
53
+ }(this, insertedIndex - 1);
54
+ if (lastCssRule) {
55
+ for(let i = 0; i < this.native.cssRules.length; i++){
56
+ if (this.native.cssRules[i] === lastCssRule) {
57
+ cssRuleIndex = i + 1;
58
+ break;
59
+ }
60
+ }
61
+ }
62
+ for(let i = 0; i < rule.nodes.length;){
63
+ try {
64
+ const node = rule.nodes[i];
65
+ const insertedIndex = this.native.insertRule(node.text, cssRuleIndex);
66
+ node.native = this.native.cssRules.item(insertedIndex);
67
+ cssRuleIndex++;
68
+ i++;
69
+ } catch (error) {
70
+ console.error(error, rule.nodes[i]);
71
+ rule.nodes.splice(i, 1);
72
+ }
73
+ }
74
+ }
75
+ return insertedIndex;
76
+ }
77
+ detach() {
78
+ const nativeSheet = this.cssRuntime.style?.sheet;
79
+ if (nativeSheet && this.native?.parentStyleSheet) {
80
+ const foundIndex = findNativeCSSRuleIndex(nativeSheet.cssRules, this.native);
81
+ if (foundIndex !== -1) {
82
+ nativeSheet.deleteRule(foundIndex);
83
+ }
84
+ }
85
+ }
86
+ delete(key) {
87
+ const deletedRule = super.delete(key);
88
+ if (!deletedRule) return;
89
+ if (this.native?.cssRules && 'nodes' in deletedRule) {
90
+ for (const node of deletedRule.nodes){
91
+ if (node.native) {
92
+ const foundIndex = findNativeCSSRuleIndex(this.native.cssRules, node.native);
93
+ if (foundIndex !== -1) {
94
+ // todo: Firefox throw "Uncaught NS_ERROR_FAILURE". Reproduce: Add '@fade|1s @fade|2s' and remove '@fade|1s @fade|2s'
95
+ this.native.deleteRule(foundIndex);
96
+ }
97
+ }
98
+ }
99
+ }
100
+ return deletedRule;
101
+ }
102
+ reset() {
103
+ super.reset();
104
+ const nativeSheet = this.cssRuntime.style?.sheet;
105
+ if (this.native && nativeSheet) {
106
+ const foundIndex = findNativeCSSRuleIndex(nativeSheet.cssRules, this.native);
107
+ if (foundIndex !== -1) {
108
+ nativeSheet.deleteRule(foundIndex);
109
+ }
110
+ this.native = null;
111
+ }
112
+ }
113
+ }
114
+
115
+ const RuntimeSyntaxLayer = css.withSyntaxLayer(RuntimeLayer);
116
+
13
117
  class CSSRuntime extends css.MasterCSS {
14
118
  root;
15
119
  customConfig;
@@ -19,8 +123,14 @@ class CSSRuntime extends css.MasterCSS {
19
123
  progressive;
20
124
  container;
21
125
  observer;
126
+ baseLayer;
127
+ themeLayer;
128
+ presetLayer;
129
+ componentsLayer;
130
+ generalLayer;
131
+ classCounts;
22
132
  constructor(root = document, customConfig = css.config){
23
- super(customConfig), this.root = root, this.customConfig = customConfig, this.observing = false, this.progressive = false;
133
+ super(customConfig), this.root = root, this.customConfig = customConfig, this.observing = false, this.progressive = false, this.baseLayer = new RuntimeSyntaxLayer('base', this), this.themeLayer = new RuntimeLayer('theme', this), this.presetLayer = new RuntimeSyntaxLayer('preset', this), this.componentsLayer = new RuntimeSyntaxLayer('components', this), this.generalLayer = new RuntimeSyntaxLayer('general', this), this.classCounts = new Map();
24
134
  const rootConstructorName = root?.constructor.name;
25
135
  if (rootConstructorName === 'HTMLDocument' || rootConstructorName === 'Document') {
26
136
  this.root.defaultView.globalThis.cssRuntime = this;
@@ -47,6 +157,7 @@ class CSSRuntime extends css.MasterCSS {
47
157
  * @returns this
48
158
  */ observe() {
49
159
  if (this.observing) return this;
160
+ // 1. Check if the stylesheet is progressive.
50
161
  if (this.root.styleSheets) for (const sheet of this.root.styleSheets){
51
162
  const { ownerNode } = sheet;
52
163
  if (ownerNode && ownerNode.id === 'master') {
@@ -56,31 +167,35 @@ class CSSRuntime extends css.MasterCSS {
56
167
  break;
57
168
  }
58
169
  }
170
+ // 2. Count the classes in the host and its children.
171
+ const connectedNames = new Set();
172
+ const increaseClassCount = (className)=>{
173
+ const count = this.classCounts.get(className) || 0;
174
+ if (!count) {
175
+ connectedNames.add(className);
176
+ }
177
+ this.classCounts.set(className, count + 1);
178
+ };
179
+ this.host.classList.forEach(increaseClassCount);
180
+ (this.root.constructor.name === 'HTMLDocument' ? this.host : this.container).querySelectorAll('[class]').forEach((element)=>element.classList.forEach(increaseClassCount));
181
+ // 3. Hydrate the existing CSS rules or create a new one.
59
182
  if (this.progressive) {
60
- this.hydrate(this.style.sheet.cssRules);
183
+ const hydrateResult = this.hydrate(this.style.sheet.cssRules);
184
+ // Add the connected class names that are not hydrated yet.
185
+ for (const eachConnectedName of connectedNames){
186
+ if (hydrateResult.allSyntaxRules.find((rule)=>(rule.fixedClass || rule.name) === eachConnectedName)) continue;
187
+ this.add(eachConnectedName);
188
+ }
61
189
  } else {
62
190
  this.style = document.createElement('style');
63
191
  this.style.id = 'master';
64
192
  this.container.append(this.style);
65
193
  this.style.sheet.insertRule(this.layerStatementRule.text);
66
194
  this.layerStatementRule.nodes[0].native = this.style.sheet.cssRules.item(0);
195
+ for (const eachConnectedName of connectedNames){
196
+ this.add(eachConnectedName);
197
+ }
67
198
  }
68
- const addClasses = (element)=>{
69
- element.classList.forEach((className)=>{
70
- if (this.classCounts.has(className)) {
71
- this.classCounts.set(className, this.classCounts.get(className) + 1);
72
- } else {
73
- this.classCounts.set(className, 1);
74
- this.add(className);
75
- }
76
- });
77
- };
78
- addClasses(this.host);
79
- /**
80
- * 待所有 DOM 結構完成解析後,開始繪製 Rule 樣式
81
- */ (this.root.constructor.name === 'HTMLDocument' ? this.host : this.container).querySelectorAll('[class]').forEach((element)=>{
82
- addClasses(element);
83
- });
84
199
  // @ts-expect-error readonly
85
200
  this.observer = new MutationObserver((mutationRecords)=>{
86
201
  // console.clear()
@@ -217,6 +332,9 @@ class CSSRuntime extends css.MasterCSS {
217
332
  hydrate(nativeLayerRules) {
218
333
  const cssLayerRules = [];
219
334
  const checkSheet = new CSSStyleSheet();
335
+ const result = {
336
+ allSyntaxRules: []
337
+ };
220
338
  for(let i = 0; i < nativeLayerRules.length; i++){
221
339
  const eachNativeCSSRule = nativeLayerRules[i];
222
340
  if (eachNativeCSSRule.constructor.name === 'CSSLayerBlockRule') {
@@ -233,7 +351,7 @@ class CSSRuntime extends css.MasterCSS {
233
351
  lastVariableName = variableName;
234
352
  variableRule = new css.Rule(variableName);
235
353
  this.themeLayer.rules.push(variableRule);
236
- this.themeLayer.usages[variableRule.name] = 0;
354
+ this.themeLayer.tokenCounts.set(variableRule.name, 0);
237
355
  }
238
356
  variableRule?.nodes.push({
239
357
  native: cssRule,
@@ -256,11 +374,10 @@ class CSSRuntime extends css.MasterCSS {
256
374
  ]);
257
375
  this.animationsNonLayer.rules.push(animationRule);
258
376
  this.rules.push(animationRule);
259
- this.animationsNonLayer.usages[animationRule.name] = 0;
377
+ this.animationsNonLayer.tokenCounts.set(animationRule.name, 0);
260
378
  }
261
379
  }
262
380
  for (const eachCSSLayerRule of cssLayerRules){
263
- const nativeLayerRules = Array.from(eachCSSLayerRule.cssRules);
264
381
  let layer;
265
382
  switch(eachCSSLayerRule.name){
266
383
  case 'base':
@@ -272,15 +389,23 @@ class CSSRuntime extends css.MasterCSS {
272
389
  case 'components':
273
390
  layer = this.componentsLayer;
274
391
  break;
275
- default:
392
+ case 'general':
276
393
  layer = this.generalLayer;
277
394
  break;
395
+ default:
396
+ console.error(`Cannot recognize the layer \`${eachCSSLayerRule.name}\`. (https://rc.css.master.co/messages/hydration-errors)`);
397
+ continue;
278
398
  }
279
399
  layer.native = eachCSSLayerRule;
280
- for (const eachNativeRule of nativeLayerRules){
281
- const selectorText = this.getSelectorText(eachNativeRule);
400
+ const unresolvedCSSRules = new Map();
401
+ for (const rule of eachCSSLayerRule.cssRules){
402
+ unresolvedCSSRules.set(rule.cssText, rule);
403
+ }
404
+ for (const eachNativeLayerRule of eachCSSLayerRule.cssRules){
405
+ if (!unresolvedCSSRules.has(eachNativeLayerRule.cssText)) continue;
406
+ const selectorText = this.getSelectorText(eachNativeLayerRule);
282
407
  if (!selectorText) {
283
- console.error(`Cannot get the selector text from \`${eachNativeRule.cssText}\`. (${layer.name}) (https://rc.css.master.co/messages/hydration-errors)`);
408
+ console.error(`Cannot get the selector text from \`${eachNativeLayerRule.cssText}\`. (${layer.name}) (https://rc.css.master.co/messages/hydration-errors)`);
284
409
  continue;
285
410
  }
286
411
  const createdRules = this.createFromSelectorText(selectorText);
@@ -289,34 +414,36 @@ class CSSRuntime extends css.MasterCSS {
289
414
  layer.rules.push(createdRule);
290
415
  layer.insertVariables(createdRule);
291
416
  layer.insertAnimations(createdRule);
417
+ result.allSyntaxRules.push(createdRule);
292
418
  createdRule.nodes.forEach((node)=>{
293
419
  const createdNodeNativeRule = checkSheet.cssRules.item(checkSheet.insertRule(node.text));
294
420
  if (createdNodeNativeRule) {
295
- for (const eachFindingNativeRule of nativeLayerRules){
296
- if (createdNodeNativeRule.cssText === eachFindingNativeRule.cssText) {
297
- node.native = eachFindingNativeRule;
298
- nativeLayerRules.splice(nativeLayerRules.indexOf(eachFindingNativeRule), 1);
299
- return;
300
- }
421
+ const match = unresolvedCSSRules.get(createdNodeNativeRule.cssText);
422
+ if (match) {
423
+ node.native = match;
424
+ unresolvedCSSRules.delete(createdNodeNativeRule.cssText);
425
+ return;
301
426
  }
302
427
  }
303
428
  console.error(`Cannot retrieve CSS rule for \`${node.selectorText}\`. (${layer.name}) (https://rc.css.master.co/messages/hydration-errors)`);
304
429
  });
305
430
  }
306
431
  } else {
307
- console.error(`Cannot recognize \`${eachNativeRule.cssText}\`. (${layer.name}) (https://rc.css.master.co/messages/hydration-errors)`);
432
+ console.error(`Cannot recognize \`${eachNativeLayerRule.cssText}\`. (${layer.name}) (https://rc.css.master.co/messages/hydration-errors)`);
308
433
  }
309
434
  }
310
435
  if (layer.rules.length) this.rules.push(layer);
311
436
  }
312
437
  globalThis.__MASTER_CSS_DEVTOOLS_HOOK__?.emit('runtime:hydrated', {
313
- cssRuntime: this
438
+ cssRuntime: this,
439
+ result
314
440
  });
441
+ return result;
315
442
  }
316
443
  getSelectorText(cssRule) {
317
- if ('selectorText' in cssRule) {
444
+ if (cssRule instanceof CSSStyleRule) {
318
445
  return cssRule.selectorText;
319
- } else if ('cssRules' in cssRule) {
446
+ } else if (cssRule instanceof CSSGroupingRule) {
320
447
  return this.getSelectorText(cssRule.cssRules.item(0));
321
448
  }
322
449
  }
@@ -346,6 +473,12 @@ class CSSRuntime extends css.MasterCSS {
346
473
  this.style.sheet.deleteRule(i);
347
474
  }
348
475
  super.refresh(customConfig);
476
+ /**
477
+ * 拿當前所有的 classNames 按照最新的 colors, config.rules 匹配並生成新的 style
478
+ * 所以 refresh 過後 rules 可能會變多也可能會變少
479
+ */ this.classCounts.forEach((_, className)=>{
480
+ this.add(className);
481
+ });
349
482
  globalThis.__MASTER_CSS_DEVTOOLS_HOOK__?.emit('runtime:refreshed', {
350
483
  cssRuntime: this,
351
484
  customConfig
package/dist/index.d.ts CHANGED
@@ -1,2 +1,4 @@
1
1
  export { default as CSSRuntime, default } from './core';
2
2
  export { default as initCSSRuntime } from './init';
3
+ export type * from './types';
4
+ export type * from './syntax-layer';
@@ -100,6 +100,110 @@ function registerGlobal(CSSRuntime) {
100
100
  }
101
101
  }
102
102
 
103
+ function findNativeCSSRuleIndex(cssRules, nativeRule) {
104
+ for(let i = 0; i < cssRules.length; i++){
105
+ if (cssRules[i] === nativeRule) {
106
+ return i;
107
+ }
108
+ }
109
+ return -1;
110
+ }
111
+
112
+ class RuntimeLayer extends css.Layer {
113
+ name;
114
+ cssRuntime;
115
+ rules;
116
+ tokenCounts;
117
+ native;
118
+ constructor(name, cssRuntime){
119
+ super(name, cssRuntime), this.name = name, this.cssRuntime = cssRuntime, this.rules = [], this.tokenCounts = new Map(), this.native = null;
120
+ }
121
+ attach() {
122
+ super.attach();
123
+ const nativeSheet = this.cssRuntime.style?.sheet;
124
+ if (nativeSheet && !this.native?.parentStyleSheet) {
125
+ const insertedIndex = nativeSheet.insertRule(this.text, nativeSheet.cssRules.length);
126
+ this.native = nativeSheet.cssRules.item(insertedIndex);
127
+ }
128
+ }
129
+ insert(rule, index = this.rules.length) {
130
+ const insertedIndex = super.insert(rule, index);
131
+ if (insertedIndex === undefined) return;
132
+ if (this.native) {
133
+ let cssRuleIndex = 0;
134
+ const lastCssRule = function getLastCssRule(layer, index) {
135
+ let lastCssRule;
136
+ const previouRule = layer.rules[index];
137
+ if (previouRule && 'nodes' in previouRule) {
138
+ if (!previouRule.nodes.length) return getLastCssRule(layer, index - 1);
139
+ const lastNativeRule = previouRule.nodes[previouRule.nodes.length - 1];
140
+ lastCssRule = lastNativeRule.native;
141
+ }
142
+ return lastCssRule;
143
+ }(this, insertedIndex - 1);
144
+ if (lastCssRule) {
145
+ for(let i = 0; i < this.native.cssRules.length; i++){
146
+ if (this.native.cssRules[i] === lastCssRule) {
147
+ cssRuleIndex = i + 1;
148
+ break;
149
+ }
150
+ }
151
+ }
152
+ for(let i = 0; i < rule.nodes.length;){
153
+ try {
154
+ const node = rule.nodes[i];
155
+ const insertedIndex = this.native.insertRule(node.text, cssRuleIndex);
156
+ node.native = this.native.cssRules.item(insertedIndex);
157
+ cssRuleIndex++;
158
+ i++;
159
+ } catch (error) {
160
+ console.error(error, rule.nodes[i]);
161
+ rule.nodes.splice(i, 1);
162
+ }
163
+ }
164
+ }
165
+ return insertedIndex;
166
+ }
167
+ detach() {
168
+ const nativeSheet = this.cssRuntime.style?.sheet;
169
+ if (nativeSheet && this.native?.parentStyleSheet) {
170
+ const foundIndex = findNativeCSSRuleIndex(nativeSheet.cssRules, this.native);
171
+ if (foundIndex !== -1) {
172
+ nativeSheet.deleteRule(foundIndex);
173
+ }
174
+ }
175
+ }
176
+ delete(key) {
177
+ const deletedRule = super.delete(key);
178
+ if (!deletedRule) return;
179
+ if (this.native?.cssRules && 'nodes' in deletedRule) {
180
+ for (const node of deletedRule.nodes){
181
+ if (node.native) {
182
+ const foundIndex = findNativeCSSRuleIndex(this.native.cssRules, node.native);
183
+ if (foundIndex !== -1) {
184
+ // todo: Firefox throw "Uncaught NS_ERROR_FAILURE". Reproduce: Add '@fade|1s @fade|2s' and remove '@fade|1s @fade|2s'
185
+ this.native.deleteRule(foundIndex);
186
+ }
187
+ }
188
+ }
189
+ }
190
+ return deletedRule;
191
+ }
192
+ reset() {
193
+ super.reset();
194
+ const nativeSheet = this.cssRuntime.style?.sheet;
195
+ if (this.native && nativeSheet) {
196
+ const foundIndex = findNativeCSSRuleIndex(nativeSheet.cssRules, this.native);
197
+ if (foundIndex !== -1) {
198
+ nativeSheet.deleteRule(foundIndex);
199
+ }
200
+ this.native = null;
201
+ }
202
+ }
203
+ }
204
+
205
+ const RuntimeSyntaxLayer = css.withSyntaxLayer(RuntimeLayer);
206
+
103
207
  class CSSRuntime extends css.MasterCSS {
104
208
  root;
105
209
  customConfig;
@@ -109,8 +213,14 @@ class CSSRuntime extends css.MasterCSS {
109
213
  progressive;
110
214
  container;
111
215
  observer;
216
+ baseLayer;
217
+ themeLayer;
218
+ presetLayer;
219
+ componentsLayer;
220
+ generalLayer;
221
+ classCounts;
112
222
  constructor(root = document, customConfig = css.config){
113
- super(customConfig), this.root = root, this.customConfig = customConfig, this.observing = false, this.progressive = false;
223
+ super(customConfig), this.root = root, this.customConfig = customConfig, this.observing = false, this.progressive = false, this.baseLayer = new RuntimeSyntaxLayer('base', this), this.themeLayer = new RuntimeLayer('theme', this), this.presetLayer = new RuntimeSyntaxLayer('preset', this), this.componentsLayer = new RuntimeSyntaxLayer('components', this), this.generalLayer = new RuntimeSyntaxLayer('general', this), this.classCounts = new Map();
114
224
  const rootConstructorName = root?.constructor.name;
115
225
  if (rootConstructorName === 'HTMLDocument' || rootConstructorName === 'Document') {
116
226
  this.root.defaultView.globalThis.cssRuntime = this;
@@ -137,6 +247,7 @@ class CSSRuntime extends css.MasterCSS {
137
247
  * @returns this
138
248
  */ observe() {
139
249
  if (this.observing) return this;
250
+ // 1. Check if the stylesheet is progressive.
140
251
  if (this.root.styleSheets) for (const sheet of this.root.styleSheets){
141
252
  const { ownerNode } = sheet;
142
253
  if (ownerNode && ownerNode.id === 'master') {
@@ -146,31 +257,39 @@ class CSSRuntime extends css.MasterCSS {
146
257
  break;
147
258
  }
148
259
  }
260
+ // 2. Count the classes in the host and its children.
261
+ const connectedNames = new Set();
262
+ const increaseClassCount = (className)=>{
263
+ const count = this.classCounts.get(className) || 0;
264
+ if (!count) {
265
+ connectedNames.add(className);
266
+ }
267
+ this.classCounts.set(className, count + 1);
268
+ };
269
+ this.host.classList.forEach(increaseClassCount);
270
+ (this.root.constructor.name === 'HTMLDocument' ? this.host : this.container).querySelectorAll('[class]').forEach((element)=>element.classList.forEach(increaseClassCount));
271
+ // 3. Hydrate the existing CSS rules or create a new one.
149
272
  if (this.progressive) {
150
- this.hydrate(this.style.sheet.cssRules);
273
+ const hydrateResult = this.hydrate(this.style.sheet.cssRules);
274
+ // Add the connected class names that are not hydrated yet.
275
+ for (const eachConnectedName of connectedNames){
276
+ if (hydrateResult.allSyntaxRules.find((rule)=>(rule.fixedClass || rule.name) === eachConnectedName)) continue;
277
+ this.add(eachConnectedName);
278
+ {
279
+ // Basically, the style#master should have all the prerendered CSS rules that are connected in the DOM.
280
+ console.debug(`The class \`${eachConnectedName}\` was added via script before calling hydrate, or the corresponding CSS rule were not properly pre-rendered.`);
281
+ }
282
+ }
151
283
  } else {
152
284
  this.style = document.createElement('style');
153
285
  this.style.id = 'master';
154
286
  this.container.append(this.style);
155
287
  this.style.sheet.insertRule(this.layerStatementRule.text);
156
288
  this.layerStatementRule.nodes[0].native = this.style.sheet.cssRules.item(0);
289
+ for (const eachConnectedName of connectedNames){
290
+ this.add(eachConnectedName);
291
+ }
157
292
  }
158
- const addClasses = (element)=>{
159
- element.classList.forEach((className)=>{
160
- if (this.classCounts.has(className)) {
161
- this.classCounts.set(className, this.classCounts.get(className) + 1);
162
- } else {
163
- this.classCounts.set(className, 1);
164
- this.add(className);
165
- }
166
- });
167
- };
168
- addClasses(this.host);
169
- /**
170
- * 待所有 DOM 結構完成解析後,開始繪製 Rule 樣式
171
- */ (this.root.constructor.name === 'HTMLDocument' ? this.host : this.container).querySelectorAll('[class]').forEach((element)=>{
172
- addClasses(element);
173
- });
174
293
  // @ts-expect-error readonly
175
294
  this.observer = new MutationObserver((mutationRecords)=>{
176
295
  // console.clear()
@@ -307,6 +426,9 @@ class CSSRuntime extends css.MasterCSS {
307
426
  hydrate(nativeLayerRules) {
308
427
  const cssLayerRules = [];
309
428
  const checkSheet = new CSSStyleSheet();
429
+ const result = {
430
+ allSyntaxRules: []
431
+ };
310
432
  for(let i = 0; i < nativeLayerRules.length; i++){
311
433
  const eachNativeCSSRule = nativeLayerRules[i];
312
434
  if (eachNativeCSSRule.constructor.name === 'CSSLayerBlockRule') {
@@ -323,7 +445,7 @@ class CSSRuntime extends css.MasterCSS {
323
445
  lastVariableName = variableName;
324
446
  variableRule = new css.Rule(variableName);
325
447
  this.themeLayer.rules.push(variableRule);
326
- this.themeLayer.usages[variableRule.name] = 0;
448
+ this.themeLayer.tokenCounts.set(variableRule.name, 0);
327
449
  }
328
450
  variableRule?.nodes.push({
329
451
  native: cssRule,
@@ -346,11 +468,10 @@ class CSSRuntime extends css.MasterCSS {
346
468
  ]);
347
469
  this.animationsNonLayer.rules.push(animationRule);
348
470
  this.rules.push(animationRule);
349
- this.animationsNonLayer.usages[animationRule.name] = 0;
471
+ this.animationsNonLayer.tokenCounts.set(animationRule.name, 0);
350
472
  }
351
473
  }
352
474
  for (const eachCSSLayerRule of cssLayerRules){
353
- const nativeLayerRules = Array.from(eachCSSLayerRule.cssRules);
354
475
  let layer;
355
476
  switch(eachCSSLayerRule.name){
356
477
  case 'base':
@@ -362,15 +483,23 @@ class CSSRuntime extends css.MasterCSS {
362
483
  case 'components':
363
484
  layer = this.componentsLayer;
364
485
  break;
365
- default:
486
+ case 'general':
366
487
  layer = this.generalLayer;
367
488
  break;
489
+ default:
490
+ console.error(`Cannot recognize the layer \`${eachCSSLayerRule.name}\`. (https://rc.css.master.co/messages/hydration-errors)`);
491
+ continue;
368
492
  }
369
493
  layer.native = eachCSSLayerRule;
370
- for (const eachNativeRule of nativeLayerRules){
371
- const selectorText = this.getSelectorText(eachNativeRule);
494
+ const unresolvedCSSRules = new Map();
495
+ for (const rule of eachCSSLayerRule.cssRules){
496
+ unresolvedCSSRules.set(rule.cssText, rule);
497
+ }
498
+ for (const eachNativeLayerRule of eachCSSLayerRule.cssRules){
499
+ if (!unresolvedCSSRules.has(eachNativeLayerRule.cssText)) continue;
500
+ const selectorText = this.getSelectorText(eachNativeLayerRule);
372
501
  if (!selectorText) {
373
- console.error(`Cannot get the selector text from \`${eachNativeRule.cssText}\`. (${layer.name}) (https://rc.css.master.co/messages/hydration-errors)`);
502
+ console.error(`Cannot get the selector text from \`${eachNativeLayerRule.cssText}\`. (${layer.name}) (https://rc.css.master.co/messages/hydration-errors)`);
374
503
  continue;
375
504
  }
376
505
  const createdRules = this.createFromSelectorText(selectorText);
@@ -379,34 +508,36 @@ class CSSRuntime extends css.MasterCSS {
379
508
  layer.rules.push(createdRule);
380
509
  layer.insertVariables(createdRule);
381
510
  layer.insertAnimations(createdRule);
511
+ result.allSyntaxRules.push(createdRule);
382
512
  createdRule.nodes.forEach((node)=>{
383
513
  const createdNodeNativeRule = checkSheet.cssRules.item(checkSheet.insertRule(node.text));
384
514
  if (createdNodeNativeRule) {
385
- for (const eachFindingNativeRule of nativeLayerRules){
386
- if (createdNodeNativeRule.cssText === eachFindingNativeRule.cssText) {
387
- node.native = eachFindingNativeRule;
388
- nativeLayerRules.splice(nativeLayerRules.indexOf(eachFindingNativeRule), 1);
389
- return;
390
- }
515
+ const match = unresolvedCSSRules.get(createdNodeNativeRule.cssText);
516
+ if (match) {
517
+ node.native = match;
518
+ unresolvedCSSRules.delete(createdNodeNativeRule.cssText);
519
+ return;
391
520
  }
392
521
  }
393
522
  console.error(`Cannot retrieve CSS rule for \`${node.selectorText}\`. (${layer.name}) (https://rc.css.master.co/messages/hydration-errors)`);
394
523
  });
395
524
  }
396
525
  } else {
397
- console.error(`Cannot recognize \`${eachNativeRule.cssText}\`. (${layer.name}) (https://rc.css.master.co/messages/hydration-errors)`);
526
+ console.error(`Cannot recognize \`${eachNativeLayerRule.cssText}\`. (${layer.name}) (https://rc.css.master.co/messages/hydration-errors)`);
398
527
  }
399
528
  }
400
529
  if (layer.rules.length) this.rules.push(layer);
401
530
  }
402
531
  globalThis.__MASTER_CSS_DEVTOOLS_HOOK__?.emit('runtime:hydrated', {
403
- cssRuntime: this
532
+ cssRuntime: this,
533
+ result
404
534
  });
535
+ return result;
405
536
  }
406
537
  getSelectorText(cssRule) {
407
- if ('selectorText' in cssRule) {
538
+ if (cssRule instanceof CSSStyleRule) {
408
539
  return cssRule.selectorText;
409
- } else if ('cssRules' in cssRule) {
540
+ } else if (cssRule instanceof CSSGroupingRule) {
410
541
  return this.getSelectorText(cssRule.cssRules.item(0));
411
542
  }
412
543
  }
@@ -436,6 +567,12 @@ class CSSRuntime extends css.MasterCSS {
436
567
  this.style.sheet.deleteRule(i);
437
568
  }
438
569
  super.refresh(customConfig);
570
+ /**
571
+ * 拿當前所有的 classNames 按照最新的 colors, config.rules 匹配並生成新的 style
572
+ * 所以 refresh 過後 rules 可能會變多也可能會變少
573
+ */ this.classCounts.forEach((_, className)=>{
574
+ this.add(className);
575
+ });
439
576
  globalThis.__MASTER_CSS_DEVTOOLS_HOOK__?.emit('runtime:refreshed', {
440
577
  cssRuntime: this,
441
578
  customConfig