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