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