@master/css-runtime 2.0.0-rc.46 → 2.0.0-rc.48

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
@@ -14,7 +14,8 @@ class RuntimeCSS extends css.MasterCSS {
14
14
  observer;
15
15
  constructor(root = document, customConfig = css.config){
16
16
  super(customConfig), this.root = root, this.customConfig = customConfig, this.observing = false, this.progressive = false;
17
- if (!root) this.root = document;
17
+ const existingRuntimeCSS = globalThis.runtimeCSSs.find((eachCSS)=>eachCSS.root === this.root);
18
+ if (existingRuntimeCSS) throw new Error('Cannot create multiple RuntimeCSS instances for the same root element.');
18
19
  const rootConstructorName = this.root?.constructor.name;
19
20
  if (rootConstructorName === 'HTMLDocument' || rootConstructorName === 'Document') {
20
21
  // @ts-ignore
@@ -35,138 +36,24 @@ class RuntimeCSS extends css.MasterCSS {
35
36
  subtree: true,
36
37
  childList: true
37
38
  }) {
38
- if (this.observing || !this.root) return this;
39
- if (runtimeCSSs.find((eachRuntimeCSS)=>eachRuntimeCSS !== this && eachRuntimeCSS.root === this.root)) {
40
- console.warn('Cannot observe the same root element repeatedly.');
41
- return this;
42
- }
43
- if (this.root?.styleSheets) for (const sheet of this.root.styleSheets){
39
+ if (this.observing) return this;
40
+ if (this.root.styleSheets) for (const sheet of this.root.styleSheets){
44
41
  const { ownerNode } = sheet;
45
42
  if (ownerNode && ownerNode.id === 'master') {
46
- // @ts-ignore
47
43
  this.style = ownerNode;
48
44
  // @ts-ignore
49
45
  this.progressive = true;
50
46
  break;
51
47
  }
52
48
  }
53
- if (this.style?.sheet) {
54
- let index = 0;
55
- for(; index < this.style.sheet.cssRules.length; index++){
56
- const eachCSSRule = this.style.sheet.cssRules[index];
57
- switch(eachCSSRule.constructor.name){
58
- case 'CSSKeyframesRule':
59
- continue;
60
- case 'CSSMediaRule':
61
- const result = /\(prefers-color-scheme: ?(.*?)\)/.exec(eachCSSRule.conditionText);
62
- if (result) {
63
- const firstCSSRule = eachCSSRule.cssRules[0];
64
- if (firstCSSRule?.constructor.name === 'CSSStyleRule' && firstCSSRule.selectorText === ':root') {
65
- this.pushVariableNativeRule(result[1], firstCSSRule);
66
- continue;
67
- }
68
- }
69
- break;
70
- case 'CSSStyleRule':
71
- const selectorText = eachCSSRule.selectorText;
72
- if (eachCSSRule.style.length) {
73
- let isVariablesRule = true;
74
- // eslint-disable-next-line @typescript-eslint/prefer-for-of
75
- for(let i = 0; i < eachCSSRule.style.length; i++){
76
- if (!eachCSSRule.style[i]?.startsWith('--')) {
77
- isVariablesRule = false;
78
- break;
79
- }
80
- }
81
- if (isVariablesRule) {
82
- if (selectorText === ':root') {
83
- this.pushVariableNativeRule('', eachCSSRule);
84
- continue;
85
- } else {
86
- const result = /:host(.*?)/.exec(selectorText);
87
- if (result) {
88
- this.pushVariableNativeRule(result[1], eachCSSRule);
89
- continue;
90
- } else if (!selectorText.startsWith('.\\$')) {
91
- const [mode] = selectorText.split(',');
92
- this.pushVariableNativeRule(mode.slice(1), eachCSSRule);
93
- continue;
94
- }
95
- }
96
- }
97
- }
98
- break;
99
- }
100
- break;
101
- }
102
- for(; index < this.style.sheet.cssRules.length; index++){
103
- const getRule = (cssRule)=>{
104
- if (cssRule.selectorText) {
105
- const selectorTexts = cssRule.selectorText.split(', ');
106
- const escapedClassNames = selectorTexts[0].split(' ');
107
- for (const eachSelectorText of escapedClassNames){
108
- if (eachSelectorText[0] === '.') {
109
- const escapedClassName = eachSelectorText.slice(1);
110
- let className = '';
111
- for(let j = 0; j < escapedClassName.length; j++){
112
- const char = escapedClassName[j];
113
- const nextChar = escapedClassName[j + 1];
114
- if (char === '\\') {
115
- j++;
116
- if (nextChar !== '\\') {
117
- className += nextChar;
118
- continue;
119
- }
120
- } else if ([
121
- ',',
122
- '.',
123
- '#',
124
- '[',
125
- '!',
126
- '*',
127
- '>',
128
- '+',
129
- '~',
130
- ':',
131
- '@'
132
- ].includes(char)) {
133
- break;
134
- }
135
- className += char;
136
- }
137
- if (!Object.prototype.hasOwnProperty.call(this.ruleBy, className) && !Object.prototype.hasOwnProperty.call(this.styles, className)) {
138
- const currentRule = this.generate(className)[0];
139
- if (currentRule) return currentRule;
140
- }
141
- }
142
- }
143
- } else if (cssRule.cssRules) {
144
- for (const rule of cssRule.cssRules){
145
- const currentRule = getRule(rule);
146
- if (currentRule) return currentRule;
147
- }
148
- }
149
- };
150
- const rule = getRule(this.style.sheet.cssRules[index]);
151
- if (rule) {
152
- this.syntaxes.push(rule);
153
- this.ruleBy[rule.className] = rule;
154
- for(let i = 0; i < rule.natives.length; i++){
155
- rule.natives[i].cssRule = this.style.sheet.cssRules[index + i];
156
- }
157
- index += rule.natives.length - 1;
158
- // variables
159
- this.handleRuleWithVariableNames(rule, true);
160
- // animations
161
- this.handleRuleWithAnimationNames(rule, true);
162
- rule.definition.insert?.call(rule);
163
- }
164
- }
49
+ if (this.progressive) {
50
+ this.hydrate(this.style.sheet.cssRules);
165
51
  } else {
166
- // @ts-ignore
167
52
  this.style = document.createElement('style');
168
53
  this.style.id = 'master';
169
54
  this.container.append(this.style);
55
+ this.style.sheet.insertRule(this.layerStatementRule.text);
56
+ this.layerStatementRule.nodes[0].native = this.style.sheet.cssRules.item(0);
170
57
  }
171
58
  const handleClassList = (classList)=>{
172
59
  classList.forEach((className)=>{
@@ -304,7 +191,7 @@ class RuntimeCSS extends css.MasterCSS {
304
191
  /**
305
192
  * class name 從 DOM tree 中被移除,
306
193
  * 匹配並刪除對應的 rule
307
- */ this.delete(className);
194
+ */ this.remove(className);
308
195
  } else {
309
196
  if (!Object.prototype.hasOwnProperty.call(this.classesUsage, className)) {
310
197
  // add
@@ -330,34 +217,154 @@ class RuntimeCSS extends css.MasterCSS {
330
217
  this.observing = true;
331
218
  return this;
332
219
  }
333
- refresh(customConfig = this.customConfig) {
334
- // remove all CSS rules
335
- // todo: e2e test
336
- const sheet = this.style?.sheet;
337
- if (sheet?.cssRules) {
338
- for(let i = sheet.cssRules.length - 1; i >= 0; i--){
339
- sheet.deleteRule(i);
220
+ hydrate(nativeCSSRules) {
221
+ const cssSyntaxLayerBlockRules = [];
222
+ for(let i = 0; i < nativeCSSRules.length; i++){
223
+ const eachNativeCSSRule = nativeCSSRules[i];
224
+ if (eachNativeCSSRule.constructor.name === 'CSSLayerBlockRule') {
225
+ const cssLayerBlockRule = eachNativeCSSRule;
226
+ if (eachNativeCSSRule.name === 'theme') {
227
+ this.themeLayer.native = cssLayerBlockRule;
228
+ let variableRule;
229
+ let lastVariableName;
230
+ for(let j = 0; j < cssLayerBlockRule.cssRules.length; j++){
231
+ const cssRule = cssLayerBlockRule.cssRules[j];
232
+ const variableCSSRule = cssRule.constructor.name === 'CSSMediaRule' ? cssRule.cssRules[0] : cssRule;
233
+ const variableName = variableCSSRule.style[0].slice(2);
234
+ if (variableName !== lastVariableName) {
235
+ lastVariableName = variableName;
236
+ variableRule = new css.Rule(variableName);
237
+ this.themeLayer.rules.push(variableRule);
238
+ this.themeLayer.usages[variableRule.name] = 0;
239
+ }
240
+ variableRule?.nodes.push({
241
+ native: cssRule,
242
+ text: cssRule.cssText
243
+ });
244
+ }
245
+ if (this.themeLayer.rules.length) this.rules.push(this.themeLayer);
246
+ } else {
247
+ cssSyntaxLayerBlockRules.push(cssLayerBlockRule);
248
+ }
249
+ } else if (eachNativeCSSRule.constructor.name === 'CSSLayerStatementRule') {
250
+ this.layerStatementRule.nodes[0].native = eachNativeCSSRule;
251
+ } else if (eachNativeCSSRule.constructor.name === 'CSSKeyframesRule') {
252
+ const keyframsRule = eachNativeCSSRule;
253
+ const animationRule = new css.Rule(keyframsRule.name, [
254
+ {
255
+ native: keyframsRule,
256
+ text: keyframsRule.cssText
257
+ }
258
+ ]);
259
+ this.animationsNonLayer.rules.push(animationRule);
260
+ this.rules.push(animationRule);
261
+ this.animationsNonLayer.usages[animationRule.name] = 0;
340
262
  }
341
263
  }
342
- super.refresh(customConfig);
343
- return this;
344
- }
345
- reset() {
346
- super.reset();
347
- const sheet = this.style?.sheet;
348
- if (sheet?.cssRules) {
349
- for(let i = sheet.cssRules.length - 1; i >= 0; i--){
350
- sheet.deleteRule(i);
264
+ for (const cssLayerBlockRule of cssSyntaxLayerBlockRules){
265
+ const handleSyntaxLayer = (layer)=>{
266
+ layer.native = cssLayerBlockRule;
267
+ for(let j = 0; j < cssLayerBlockRule.cssRules.length; j++){
268
+ const cssRule = cssLayerBlockRule.cssRules[j];
269
+ const createSyntaxRule = (cssRule)=>{
270
+ if (cssRule.selectorText) {
271
+ const syntaxRule = this.createFromSelectorText(cssRule.selectorText)[0];
272
+ if (syntaxRule) return syntaxRule;
273
+ } else if (cssRule.cssRules) {
274
+ for (const eachCSSRule of cssRule.cssRules){
275
+ const syntaxRule = createSyntaxRule(eachCSSRule);
276
+ if (syntaxRule) return syntaxRule;
277
+ }
278
+ }
279
+ };
280
+ const syntaxRule = createSyntaxRule(cssRule);
281
+ if (syntaxRule) {
282
+ layer.rules.push(syntaxRule);
283
+ layer.insertVariables(syntaxRule);
284
+ layer.insertAnimations(syntaxRule);
285
+ for (const eachNode of syntaxRule.nodes){
286
+ if (!eachNode.native) eachNode.native = cssRule;
287
+ }
288
+ } else {
289
+ cssLayerBlockRule.deleteRule?.(j--);
290
+ console.error(`Cannot recognize the CSS rule in the ${layer.name} layer. \`${cssRule.cssText}\` (https://rc.css.master.co/messages/hydration-errors)`);
291
+ }
292
+ }
293
+ for (const eachRule of layer.rules){
294
+ for(let k = eachRule.nodes.length - 1; k >= 0; k--){
295
+ if (!eachRule.nodes[k].native) {
296
+ eachRule.nodes.splice(k, 1);
297
+ }
298
+ }
299
+ }
300
+ if (layer.rules.length) this.rules.push(layer);
301
+ };
302
+ switch(cssLayerBlockRule.name){
303
+ case 'base':
304
+ handleSyntaxLayer(this.baseLayer);
305
+ break;
306
+ case 'preset':
307
+ handleSyntaxLayer(this.presetLayer);
308
+ break;
309
+ case 'components':
310
+ this.stylesLayer.native = cssLayerBlockRule;
311
+ let stylePreText;
312
+ const createSyntaxRules = (cssRule)=>{
313
+ if (cssRule.selectorText) {
314
+ const syntaxRules = this.createFromSelectorText(cssRule.selectorText);
315
+ if (syntaxRules.length) {
316
+ stylePreText = cssRule.selectorText + '{';
317
+ return syntaxRules;
318
+ }
319
+ } else if (cssRule.cssRules) {
320
+ for (const eachCSSRule of cssRule.cssRules){
321
+ const syntaxRules = this.createFromSelectorText(eachCSSRule.selectorText);
322
+ if (syntaxRules) return syntaxRules;
323
+ }
324
+ }
325
+ };
326
+ for(let j = 0; j < cssLayerBlockRule.cssRules.length; j++){
327
+ const cssRule = cssLayerBlockRule.cssRules[j];
328
+ const syntaxRules = createSyntaxRules(cssRule);
329
+ if (syntaxRules) {
330
+ let matched = false;
331
+ for (const eachSyntaxRule of syntaxRules){
332
+ for (const node of eachSyntaxRule.nodes){
333
+ if (!node.native && node.text.includes(stylePreText)) {
334
+ node.native = cssRule;
335
+ if (!this.stylesLayer.rules.includes(eachSyntaxRule)) {
336
+ this.stylesLayer.rules.push(eachSyntaxRule);
337
+ this.stylesLayer.insertVariables(eachSyntaxRule);
338
+ this.stylesLayer.insertAnimations(eachSyntaxRule);
339
+ }
340
+ matched = true;
341
+ break;
342
+ }
343
+ }
344
+ if (matched) break;
345
+ }
346
+ } else {
347
+ cssLayerBlockRule?.deleteRule?.(j--);
348
+ console.error(`Cannot recognize the CSS rule in the components layer. \`${cssRule.cssText}\` (https://rc.css.master.co/messages/hydration-errors)`);
349
+ }
350
+ }
351
+ for (const eachRule of this.stylesLayer.rules){
352
+ for(let k = eachRule.nodes.length - 1; k >= 0; k--){
353
+ if (!eachRule.nodes[k].native) {
354
+ eachRule.nodes.splice(k, 1);
355
+ }
356
+ }
357
+ }
358
+ if (this.stylesLayer.rules.length) this.rules.push(this.stylesLayer);
359
+ break;
360
+ case 'general':
361
+ handleSyntaxLayer(this.generalLayer);
362
+ break;
351
363
  }
352
364
  }
353
- if (!this.progressive) {
354
- this.style?.remove();
355
- // @ts-ignore
356
- this.style = null;
357
- }
358
- return this;
359
365
  }
360
366
  disconnect() {
367
+ if (!this.observing) return;
361
368
  if (this.observer) {
362
369
  this.observer.disconnect();
363
370
  this.observer = null;
@@ -365,6 +372,20 @@ class RuntimeCSS extends css.MasterCSS {
365
372
  // @ts-ignore
366
373
  this.observing = false;
367
374
  this.reset();
375
+ // @ts-expect-error
376
+ this.classesUsage = {};
377
+ if (!this.progressive) {
378
+ this.style?.remove();
379
+ this.style = null;
380
+ }
381
+ return this;
382
+ }
383
+ refresh(customConfig = this.customConfig) {
384
+ if (!this.observing || !this.style.sheet) return this;
385
+ for(let i = 1; i <= this.style.sheet.cssRules.length - 1; i++){
386
+ this.style.sheet.deleteRule(i);
387
+ }
388
+ super.refresh(customConfig);
368
389
  return this;
369
390
  }
370
391
  destroy() {