@master/css 2.0.0-rc.61 → 2.0.0-rc.62

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.
Files changed (125) hide show
  1. package/dist/animation-rule.cjs +24 -0
  2. package/dist/animation-rule.d.ts +11 -0
  3. package/dist/animation-rule.mjs +22 -0
  4. package/dist/common.cjs +50 -1
  5. package/dist/common.d.ts +14 -0
  6. package/dist/common.mjs +45 -2
  7. package/dist/config/at.cjs +13 -10
  8. package/dist/config/at.d.ts +4 -1
  9. package/dist/config/at.mjs +13 -10
  10. package/dist/config/index.cjs +3 -0
  11. package/dist/config/index.d.ts +2 -1
  12. package/dist/config/index.mjs +3 -1
  13. package/dist/config/modes.d.ts +4 -2
  14. package/dist/config/rules.cjs +25 -13
  15. package/dist/config/rules.d.ts +4 -2
  16. package/dist/config/rules.mjs +25 -13
  17. package/dist/config/screens.cjs +17 -0
  18. package/dist/config/screens.d.ts +14 -0
  19. package/dist/config/screens.mjs +15 -0
  20. package/dist/config/selectors.cjs +11 -15
  21. package/dist/config/selectors.d.ts +10 -8
  22. package/dist/config/selectors.mjs +11 -15
  23. package/dist/config/utilities.cjs +3 -0
  24. package/dist/config/utilities.d.ts +3 -0
  25. package/dist/config/utilities.mjs +3 -0
  26. package/dist/config/variables.cjs +0 -13
  27. package/dist/config/variables.d.ts +0 -13
  28. package/dist/config/variables.mjs +0 -13
  29. package/dist/core.cjs +438 -476
  30. package/dist/core.d.ts +13 -4
  31. package/dist/core.mjs +438 -476
  32. package/dist/factories/with-syntax-layer.cjs +37 -260
  33. package/dist/factories/with-syntax-layer.mjs +37 -260
  34. package/dist/index.cjs +38 -12
  35. package/dist/index.d.ts +19 -7
  36. package/dist/index.mjs +17 -7
  37. package/dist/layer.cjs +11 -7
  38. package/dist/layer.d.ts +4 -3
  39. package/dist/layer.mjs +11 -7
  40. package/dist/non-layer.cjs +10 -17
  41. package/dist/non-layer.d.ts +1 -1
  42. package/dist/non-layer.mjs +10 -17
  43. package/dist/rule.cjs +4 -6
  44. package/dist/rule.d.ts +3 -10
  45. package/dist/rule.mjs +4 -6
  46. package/dist/syntax-rule-type.cjs +7 -8
  47. package/dist/syntax-rule-type.d.ts +7 -7
  48. package/dist/syntax-rule-type.mjs +7 -8
  49. package/dist/syntax-rule.cjs +116 -328
  50. package/dist/syntax-rule.d.ts +18 -25
  51. package/dist/syntax-rule.mjs +117 -329
  52. package/dist/types/common.d.ts +2 -0
  53. package/dist/types/config.d.ts +12 -7
  54. package/dist/types/syntax.d.ts +5 -24
  55. package/dist/utils/compare-rule-priority.cjs +143 -0
  56. package/dist/utils/compare-rule-priority.d.ts +7 -0
  57. package/dist/utils/compare-rule-priority.mjs +138 -0
  58. package/dist/utils/equal-at-rules.cjs +22 -0
  59. package/dist/utils/equal-at-rules.d.ts +2 -0
  60. package/dist/utils/equal-at-rules.mjs +20 -0
  61. package/dist/utils/equal-declarations.cjs +10 -0
  62. package/dist/utils/equal-declarations.d.ts +2 -0
  63. package/dist/utils/equal-declarations.mjs +8 -0
  64. package/dist/utils/equal-selectors.cjs +70 -0
  65. package/dist/utils/equal-selectors.d.ts +2 -0
  66. package/dist/utils/equal-selectors.mjs +68 -0
  67. package/dist/utils/equal-variants.cjs +10 -0
  68. package/dist/utils/equal-variants.d.ts +2 -0
  69. package/dist/utils/equal-variants.mjs +8 -0
  70. package/dist/utils/extend-config.cjs +0 -1
  71. package/dist/utils/extend-config.mjs +0 -1
  72. package/dist/utils/generate-at.cjs +30 -0
  73. package/dist/utils/generate-at.d.ts +2 -0
  74. package/dist/utils/generate-at.mjs +28 -0
  75. package/dist/utils/generate-selector.cjs +49 -0
  76. package/dist/utils/generate-selector.d.ts +2 -0
  77. package/dist/utils/generate-selector.mjs +47 -0
  78. package/dist/utils/get-single-at-number-rule-node.cjs +9 -0
  79. package/dist/utils/get-single-at-number-rule-node.d.ts +2 -0
  80. package/dist/utils/get-single-at-number-rule-node.mjs +7 -0
  81. package/dist/utils/parse-at.cjs +157 -0
  82. package/dist/utils/parse-at.d.ts +39 -0
  83. package/dist/utils/parse-at.mjs +155 -0
  84. package/dist/utils/parse-pair.cjs +51 -0
  85. package/dist/utils/parse-pair.d.ts +8 -0
  86. package/dist/utils/parse-pair.mjs +49 -0
  87. package/dist/utils/parse-selector.cjs +112 -0
  88. package/dist/utils/parse-selector.d.ts +19 -0
  89. package/dist/utils/parse-selector.mjs +110 -0
  90. package/dist/utils/parse-value.cjs +39 -0
  91. package/dist/utils/parse-value.d.ts +2 -0
  92. package/dist/utils/parse-value.mjs +37 -0
  93. package/dist/utils/replace-char-outside-quotes.cjs +21 -0
  94. package/dist/utils/replace-char-outside-quotes.d.ts +1 -0
  95. package/dist/utils/replace-char-outside-quotes.mjs +19 -0
  96. package/dist/utils/sort-readable-classes.cjs +74 -0
  97. package/dist/utils/{reorder-for-readable-classes.d.ts → sort-readable-classes.d.ts} +1 -1
  98. package/dist/utils/sort-readable-classes.mjs +72 -0
  99. package/dist/utils/split-char-outside-quotes.cjs +29 -0
  100. package/dist/utils/split-char-outside-quotes.d.ts +1 -0
  101. package/dist/utils/split-char-outside-quotes.mjs +27 -0
  102. package/dist/variable-rule.cjs +86 -0
  103. package/dist/variable-rule.d.ts +23 -0
  104. package/dist/variable-rule.mjs +81 -0
  105. package/package.json +1 -1
  106. package/dist/rule-node.cjs +0 -18
  107. package/dist/rule-node.d.ts +0 -14
  108. package/dist/rule-node.mjs +0 -16
  109. package/dist/utils/are-rule-at-equal.cjs +0 -12
  110. package/dist/utils/are-rule-at-equal.d.ts +0 -2
  111. package/dist/utils/are-rule-at-equal.mjs +0 -10
  112. package/dist/utils/are-rule-modes-equal.cjs +0 -7
  113. package/dist/utils/are-rule-modes-equal.d.ts +0 -2
  114. package/dist/utils/are-rule-modes-equal.mjs +0 -5
  115. package/dist/utils/are-rule-selectors-equal.cjs +0 -29
  116. package/dist/utils/are-rule-selectors-equal.d.ts +0 -2
  117. package/dist/utils/are-rule-selectors-equal.mjs +0 -27
  118. package/dist/utils/are-rule-states-equal.cjs +0 -11
  119. package/dist/utils/are-rule-states-equal.d.ts +0 -2
  120. package/dist/utils/are-rule-states-equal.mjs +0 -9
  121. package/dist/utils/are-rules-duplicated.cjs +0 -17
  122. package/dist/utils/are-rules-duplicated.d.ts +0 -2
  123. package/dist/utils/are-rules-duplicated.mjs +0 -15
  124. package/dist/utils/reorder-for-readable-classes.cjs +0 -62
  125. package/dist/utils/reorder-for-readable-classes.mjs +0 -60
package/dist/core.mjs CHANGED
@@ -9,6 +9,9 @@ import Layer from './layer.mjs';
9
9
  import SyntaxLayer from './syntax-layer.mjs';
10
10
  import NonLayer from './non-layer.mjs';
11
11
  import registerGlobal from './register-global.mjs';
12
+ import parseAt from './utils/parse-at.mjs';
13
+ import parseValue from './utils/parse-value.mjs';
14
+ import parseSelector from './utils/parse-selector.mjs';
12
15
 
13
16
  class MasterCSS {
14
17
  customConfig;
@@ -17,6 +20,7 @@ class MasterCSS {
17
20
  config;
18
21
  layerStatementRule;
19
22
  rules;
23
+ classRules;
20
24
  animationsNonLayer;
21
25
  baseLayer;
22
26
  themeLayer;
@@ -26,19 +30,16 @@ class MasterCSS {
26
30
  components;
27
31
  selectors;
28
32
  variables;
29
- at;
33
+ atRules;
30
34
  animations;
31
35
  constructor(customConfig){
32
36
  this.customConfig = customConfig;
33
37
  this.definedRules = [];
34
- this.layerStatementRule = new Rule('layer-statement', [
35
- {
36
- text: '@layer base,theme,preset,components,general;'
37
- }
38
- ]);
38
+ this.layerStatementRule = new Rule('layer-statement', '@layer base,theme,preset,components,general;');
39
39
  this.rules = [
40
40
  this.layerStatementRule
41
41
  ];
42
+ this.classRules = new Map();
42
43
  this.animationsNonLayer = new NonLayer(this);
43
44
  this.baseLayer = new SyntaxLayer('base', this);
44
45
  this.themeLayer = new Layer('theme', this);
@@ -48,10 +49,9 @@ class MasterCSS {
48
49
  this.components = new Map();
49
50
  this.selectors = new Map();
50
51
  this.variables = new Map();
51
- this.at = new Map();
52
+ this.atRules = new Map();
52
53
  this.animations = new Map();
53
- this.config = customConfig?.override ? extendConfig(customConfig) : extendConfig(config, customConfig);
54
- this.resolve();
54
+ this.resolve(customConfig);
55
55
  }
56
56
  get text() {
57
57
  return this.rules.sort((a, b)=>{
@@ -68,224 +68,433 @@ class MasterCSS {
68
68
  return indexA - indexB;
69
69
  }).map(({ text })=>text).join('');
70
70
  }
71
- resolve() {
72
- this.components.clear();
73
- this.selectors.clear();
74
- this.variables.clear();
75
- this.at.clear();
76
- this.animations.clear();
77
- this.definedRules.length = 0;
78
- const colorVariableNames = {
79
- current: undefined,
80
- currentColor: undefined,
81
- transparent: undefined
82
- };
83
- const { components, selectors, variables, utilities, at, rules, animations } = this.config;
71
+ resolve(customConfig) {
72
+ if (customConfig) {
73
+ this.customConfig = customConfig;
74
+ } else {
75
+ customConfig = this.customConfig;
76
+ }
77
+ // @ts-expect-error read-only
78
+ this.config = customConfig?.override ? extendConfig(customConfig) : extendConfig(config, customConfig);
79
+ this.resolveVariables();
80
+ this.resolveAnimations();
81
+ this.resolveSelectors();
82
+ this.resolveAtRules();
83
+ this.resolveRules();
84
+ this.resolveComponents();
85
+ }
86
+ resolveAnimations() {
87
+ const { animations } = this.config;
88
+ if (animations) {
89
+ for(const animationName in animations){
90
+ const eachAnimation = {};
91
+ this.animations.set(animationName, eachAnimation);
92
+ const eachKeyframes = animations[animationName];
93
+ for(const eachKeyframeValue in eachKeyframes){
94
+ const newValueByPropertyName = eachAnimation[eachKeyframeValue] = {};
95
+ const eachKeyframeDeclarations = eachKeyframes[eachKeyframeValue];
96
+ for(const propertyName in eachKeyframeDeclarations){
97
+ newValueByPropertyName[propertyName] = eachKeyframeDeclarations[propertyName];
98
+ }
99
+ }
100
+ }
101
+ }
102
+ }
103
+ resolveRules() {
104
+ const { rules, utilities } = this.config;
84
105
  function escapeString(str) {
85
106
  return str.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
86
107
  }
87
- if (selectors) {
88
- const resolvedSelectors = flattenObject(selectors);
89
- for(const eachSelectorName in resolvedSelectors){
90
- const eachResolvedSelectorText = resolvedSelectors[eachSelectorName];
91
- const regexp = new RegExp(escapeString(eachSelectorName) + '(?![a-z-])');
92
- for (const eachNewSelectorText of Array.isArray(eachResolvedSelectorText) ? eachResolvedSelectorText : [
93
- eachResolvedSelectorText
94
- ]){
95
- const vendor = eachNewSelectorText.match(/^:(?::)?-(webkit|moz|ms|o)-/m)?.[1] ?? '';
96
- let selectorValues = this.selectors.get(vendor);
97
- if (!selectorValues) {
98
- this.selectors.set(vendor, selectorValues = []);
108
+ if (!rules && !utilities) return;
109
+ const rulesEntries = [];
110
+ // Collect utility rules
111
+ if (utilities) {
112
+ for(const utilityName in utilities){
113
+ const declarations = utilities[utilityName];
114
+ rulesEntries.push([
115
+ utilityName,
116
+ {
117
+ declarations,
118
+ type: SyntaxRuleType.Utility
99
119
  }
100
- let currentSelectValue = selectorValues.find(([_valueRegexp])=>_valueRegexp === regexp);
101
- if (!currentSelectValue) {
102
- currentSelectValue = [
103
- regexp,
104
- []
105
- ];
106
- selectorValues.push(currentSelectValue);
120
+ ]);
121
+ }
122
+ }
123
+ // Collect normal rules
124
+ if (rules) {
125
+ rulesEntries.push(...Object.entries(rules));
126
+ }
127
+ const rulesEntriesLength = rulesEntries.length;
128
+ // Prepare color variables
129
+ const colorNames = new Set([
130
+ 'current',
131
+ 'currentColor',
132
+ 'transparent'
133
+ ]);
134
+ this.variables.forEach((v)=>{
135
+ if (v.type === 'color') colorNames.add(v.name);
136
+ });
137
+ const colorPattern = [
138
+ ...colorNames
139
+ ].join('|');
140
+ // Main loop
141
+ rulesEntries.sort((a, b)=>{
142
+ if (a[1].type !== b[1].type) {
143
+ return (b[1].type || 0) - (a[1].type || 0);
144
+ }
145
+ return b[0].localeCompare(a[0], undefined, {
146
+ numeric: true
147
+ });
148
+ }).forEach(([id, def], index)=>{
149
+ const order = rulesEntriesLength - 1 - index;
150
+ const syntax = {
151
+ id,
152
+ keys: [],
153
+ variables: {},
154
+ matchers: {},
155
+ order,
156
+ definition: def
157
+ };
158
+ def.unit ??= '';
159
+ def.separators ??= [
160
+ ','
161
+ ];
162
+ this.definedRules.push(syntax);
163
+ const { matcher, type, subkey, ambiguousKeys, ambiguousValues, sign, key: originalKey, variables: ruleVariableGroups } = def;
164
+ const keys = [];
165
+ let key = originalKey;
166
+ // Helper: resolve variable groups
167
+ const addResolvedVariables = (groupName)=>{
168
+ this.variables.forEach((v)=>{
169
+ if (v.group === groupName) {
170
+ syntax.variables[v.key] = v;
171
+ }
172
+ });
173
+ };
174
+ // Rule-defined variable groups
175
+ if (ruleVariableGroups) {
176
+ ruleVariableGroups.forEach(addResolvedVariables);
177
+ }
178
+ // Auto variable binding
179
+ addResolvedVariables(id);
180
+ // Key derivation logic
181
+ if (type === SyntaxRuleType.NativeShorthand || type === SyntaxRuleType.Native) {
182
+ if (!key) def.key = key = id;
183
+ keys.push(id);
184
+ }
185
+ if (sign) {
186
+ syntax.matchers.arbitrary = new RegExp(`^${sign}[^!*>+~:[@_]+\\|`);
187
+ } else if (!matcher) {
188
+ if (!key && !subkey) {
189
+ keys.push(id);
190
+ } else {
191
+ if (key && !keys.includes(key)) keys.push(key);
192
+ if (subkey) keys.push(subkey);
193
+ if (type === SyntaxRuleType.Shorthand) keys.push(id);
194
+ }
195
+ // Ambiguous keys and values
196
+ if (ambiguousKeys?.length) {
197
+ const keyPattern = ambiguousKeys.length > 1 ? `(?:${ambiguousKeys.join('|')})` : ambiguousKeys[0];
198
+ const variableKeys = Object.keys(syntax.variables);
199
+ if (ambiguousValues?.length) {
200
+ const valuePatterns = ambiguousValues.map((val)=>val instanceof RegExp ? val.source.replace('\\$colors', colorPattern) : `${val}(?:\\b|_)`);
201
+ syntax.matchers.value = new RegExp(`^${keyPattern}:(?:${valuePatterns.join('|')})[^|]*?(?:@|$)`);
202
+ }
203
+ if (variableKeys.length) {
204
+ syntax.matchers.variable = new RegExp(`^${keyPattern}:(?:${variableKeys.join('|')}(?![a-zA-Z0-9-]))[^|]*?(?:@|$)`);
107
205
  }
108
- currentSelectValue[1].push(eachNewSelectorText);
109
206
  }
207
+ } else {
208
+ syntax.matchers.arbitrary = matcher;
110
209
  }
111
- }
112
- if (variables) {
113
- const aliasVariableModeResolvers = new Map;
114
- const resolveVariable = (variableDefinition, name, mode)=>{
115
- if (variableDefinition === undefined || variableDefinition === null) return;
116
- const addVariable = (name, variable, replacedMode, alpha)=>{
117
- if (variable === undefined) return;
118
- const flatName = name.join('-');
119
- const groups = name.slice(0, -1).filter(Boolean);
120
- const key = (name[0] === '' ? '-' : '') + name[name.length - 1];
121
- variable.key = key;
122
- variable.name = flatName;
123
- if (groups.length) variable.group = groups.join('.');
124
- if (variable.type === 'color') {
125
- if (alpha) {
126
- const slashIndex = variable.value.indexOf('/');
127
- variable = {
128
- ...variable,
129
- value: slashIndex === -1 ? variable.value + ' / ' + (alpha.startsWith('0.') ? alpha.slice(1) : alpha) : variable.value.slice(0, slashIndex + 2) + String(+variable.value.slice(slashIndex + 2) * +alpha).slice(1)
130
- };
210
+ // Utility rule matcher
211
+ if (type === SyntaxRuleType.Utility) {
212
+ syntax.id = '.' + id;
213
+ syntax.matchers.arbitrary = new RegExp('^' + escapeString(id) + '(?=!|\\*|>|\\+|~|:|\\[|@|_|\\.|$)', 'm');
214
+ }
215
+ // Key matcher
216
+ if (keys.length) {
217
+ syntax.keys = keys;
218
+ syntax.matchers.key = new RegExp(`^${keys.length > 1 ? `(${keys.join('|')})` : keys[0]}:.`);
219
+ }
220
+ });
221
+ }
222
+ resolveComponents() {
223
+ const { components } = this.config;
224
+ const flatComps = components ? flattenObject(components) : {};
225
+ const names = Object.keys(flatComps);
226
+ const nameSet = new Set(names);
227
+ // First pass: expand class names recursively
228
+ for (const name of names){
229
+ if (this.components.has(name)) continue;
230
+ const currentClass = [];
231
+ this.components.set(name, currentClass);
232
+ const className = flatComps[name];
233
+ if (!className) continue;
234
+ const classNames = className.replace(/(?:\n\s*)+/g, ' ').trim().split(' ');
235
+ const seen = new Set();
236
+ const addClass = (cls)=>{
237
+ if (!seen.has(cls)) {
238
+ seen.add(cls);
239
+ currentClass.push(cls);
240
+ }
241
+ };
242
+ for (const eachClassName of classNames){
243
+ if (nameSet.has(eachClassName)) {
244
+ addClass(eachClassName);
245
+ // Lazy expand dependencies if not processed
246
+ if (!this.components.has(eachClassName)) {
247
+ const componentClass = flatComps[eachClassName];
248
+ if (componentClass) {
249
+ this.components.set(eachClassName, []) // placeholder to prevent infinite loop
250
+ ;
251
+ // Recursively resolve
252
+ const depClassNames = componentClass.replace(/(?:\n\s*)+/g, ' ').trim().split(' ');
253
+ depClassNames.forEach(addClass);
131
254
  }
132
- colorVariableNames[flatName] = undefined;
133
255
  }
134
- /**
135
- * resolve `variables.screen-*` to `at.*`
136
- */ if (variable.name.startsWith('screen-') && variable.type === 'number') {
137
- this.at.set(variable.name.slice(7), variable.value);
256
+ const componentClasses = this.components.get(eachClassName);
257
+ componentClasses?.forEach(addClass);
258
+ } else {
259
+ addClass(eachClassName);
260
+ }
261
+ }
262
+ }
263
+ // Second pass: convert to grouped syntax rule strings
264
+ this.components.forEach((componentClasses, componentName)=>{
265
+ const syntaxRulesByStateToken = {};
266
+ for (const syntax of componentClasses){
267
+ const parsed = this.create(syntax);
268
+ if (parsed?.text) {
269
+ const group = syntaxRulesByStateToken[parsed.stateToken] ||= [];
270
+ group.push(parsed);
271
+ }
272
+ }
273
+ const result = Object.entries(syntaxRulesByStateToken).map(([stateToken, syntaxRules])=>{
274
+ if (syntaxRules.length === 1) return syntaxRules[0].name;
275
+ const namesWithoutToken = syntaxRules.map((s)=>s.name.slice(0, s.name.length - s.stateToken.length));
276
+ return `{${namesWithoutToken.join(';')}}${stateToken}`;
277
+ });
278
+ this.components.set(componentName, result);
279
+ });
280
+ }
281
+ resolveSelectors() {
282
+ const { selectors } = this.config;
283
+ if (selectors) {
284
+ const flatSelectors = flattenObject(selectors);
285
+ for(const token in flatSelectors){
286
+ const value = flatSelectors[token];
287
+ const nodes = parseSelector(value, this, false);
288
+ this.selectors.set(token, nodes);
289
+ }
290
+ }
291
+ }
292
+ resolveAtRules() {
293
+ const { at, screens } = this.config;
294
+ if (screens) {
295
+ for(const key in screens){
296
+ const value = screens[key];
297
+ const node = this.parseValue(value);
298
+ this.atRules.set(key, {
299
+ id: 'media',
300
+ nodes: [
301
+ node
302
+ ]
303
+ });
304
+ }
305
+ }
306
+ if (at) {
307
+ const flatAt = flattenObject(at);
308
+ for(const token in flatAt){
309
+ const value = flatAt[token];
310
+ if (typeof value === 'number') {
311
+ const node = this.parseValue(value);
312
+ this.atRules.set(token, {
313
+ id: 'media',
314
+ nodes: [
315
+ node
316
+ ]
317
+ });
318
+ } else {
319
+ const atRule = parseAt(value, this, false);
320
+ this.atRules.set(token, atRule);
321
+ }
322
+ }
323
+ }
324
+ }
325
+ resolveVariables() {
326
+ const { variables, screens } = this.config;
327
+ const aliasVariableModeResolvers = new Map();
328
+ const resolveVariable = (variableDefinition, name, mode)=>{
329
+ if (variableDefinition === undefined || variableDefinition === null) return;
330
+ const addVariable = (name, variable, replacedMode, alpha)=>{
331
+ if (variable === undefined) return;
332
+ const flatName = name.join('-');
333
+ const groups = name.slice(0, -1).filter(Boolean);
334
+ const key = (name[0] === '' ? '-' : '') + name[name.length - 1];
335
+ variable.key = key;
336
+ variable.name = flatName;
337
+ if (groups.length) variable.group = groups.join('.');
338
+ if (variable.type === 'color') {
339
+ if (alpha) {
340
+ const slashIndex = variable.value.indexOf('/');
341
+ variable = {
342
+ ...variable,
343
+ value: slashIndex === -1 ? variable.value + ' / ' + (alpha.startsWith('0.') ? alpha.slice(1) : alpha) : variable.value.slice(0, slashIndex + 2) + String(+variable.value.slice(slashIndex + 2) * +alpha).slice(1)
344
+ };
138
345
  }
139
- const currentMode = replacedMode ?? mode;
140
- if (currentMode !== undefined) {
141
- const foundVariable = this.variables.get(flatName);
142
- if (foundVariable) {
143
- if (currentMode) {
144
- if (!foundVariable.modes) {
145
- foundVariable.modes = {};
146
- }
147
- foundVariable.modes[currentMode] = variable;
148
- } else {
149
- foundVariable.value = variable.value;
150
- if (variable.type === 'color') {
151
- foundVariable.space = variable.space;
152
- }
346
+ }
347
+ const currentMode = replacedMode ?? mode;
348
+ if (currentMode !== undefined) {
349
+ const foundVariable = this.variables.get(flatName);
350
+ if (foundVariable) {
351
+ if (currentMode) {
352
+ if (!foundVariable.modes) {
353
+ foundVariable.modes = {};
153
354
  }
355
+ foundVariable.modes[currentMode] = variable;
154
356
  } else {
155
- if (currentMode) {
156
- const newVariable = {
157
- key: variable.key,
158
- name: variable.name,
159
- group: variable.group,
160
- type: variable.type,
161
- modes: {
162
- [currentMode]: variable
163
- }
164
- };
165
- if (variable.type === 'color') {
166
- newVariable.space = variable.space;
167
- }
168
- this.variables.set(flatName, newVariable);
169
- } else {
170
- this.variables.set(flatName, variable);
357
+ foundVariable.value = variable.value;
358
+ if (variable.type === 'color') {
359
+ foundVariable.space = variable.space;
171
360
  }
172
361
  }
173
362
  } else {
174
- this.variables.set(flatName, variable);
175
- }
176
- };
177
- if (typeof variableDefinition === 'object') {
178
- if (Array.isArray(variableDefinition)) {
179
- addVariable(name, {
180
- type: 'string',
181
- value: variableDefinition.join(',')
182
- });
183
- } else {
184
- const keys = Object.keys(variableDefinition);
185
- for (const eachKey of keys){
186
- if (eachKey === '' || eachKey.startsWith('@')) {
187
- resolveVariable(variableDefinition[eachKey], name, eachKey || keys.some((eachKey)=>eachKey.startsWith('@')) ? eachKey.slice(1) : undefined);
188
- } else {
189
- resolveVariable(variableDefinition[eachKey], [
190
- ...name,
191
- eachKey
192
- ]);
363
+ if (currentMode) {
364
+ const newVariable = {
365
+ key: variable.key,
366
+ name: variable.name,
367
+ group: variable.group,
368
+ type: variable.type,
369
+ modes: {
370
+ [currentMode]: variable
371
+ }
372
+ };
373
+ if (variable.type === 'color') {
374
+ newVariable.space = variable.space;
193
375
  }
376
+ this.variables.set(flatName, newVariable);
377
+ } else {
378
+ this.variables.set(flatName, variable);
194
379
  }
195
380
  }
196
- } else if (typeof variableDefinition === 'number') {
381
+ } else {
382
+ this.variables.set(flatName, variable);
383
+ }
384
+ };
385
+ if (typeof variableDefinition === 'object') {
386
+ if (Array.isArray(variableDefinition)) {
197
387
  addVariable(name, {
198
- type: 'number',
199
- value: variableDefinition
388
+ type: 'string',
389
+ value: variableDefinition.join(',')
200
390
  });
201
- addVariable([
202
- '',
203
- ...name
204
- ], {
205
- type: 'number',
206
- value: variableDefinition * -1
207
- });
208
- } else if (typeof variableDefinition === 'string') {
209
- const aliasResult = /^\$\((.*?)\)(?: ?\/ ?(.+?))?$/.exec(variableDefinition);
210
- const flatName = name.join('-');
211
- if (aliasResult) {
212
- let aliasVariableModeResolver = aliasVariableModeResolvers.get(flatName);
213
- if (!aliasVariableModeResolver) {
214
- aliasVariableModeResolvers.set(flatName, aliasVariableModeResolver = {});
391
+ } else {
392
+ const keys = Object.keys(variableDefinition);
393
+ for (const eachKey of keys){
394
+ if (eachKey === '' || eachKey.startsWith('@')) {
395
+ resolveVariable(variableDefinition[eachKey], name, eachKey || keys.some((eachKey)=>eachKey.startsWith('@')) ? eachKey.slice(1) : undefined);
396
+ } else {
397
+ resolveVariable(variableDefinition[eachKey], [
398
+ ...name,
399
+ eachKey
400
+ ]);
215
401
  }
216
- aliasVariableModeResolver[mode] = ()=>{
217
- delete aliasVariableModeResolver[mode];
218
- const [alias, aliasMode] = aliasResult[1].split('@');
219
- if (alias) {
220
- const eachAliasModeVariableResolver = aliasVariableModeResolvers.get(alias);
221
- if (eachAliasModeVariableResolver) {
222
- for (const mode of Object.keys(eachAliasModeVariableResolver)){
223
- eachAliasModeVariableResolver[mode]?.();
224
- }
402
+ }
403
+ }
404
+ } else if (typeof variableDefinition === 'number') {
405
+ addVariable(name, {
406
+ type: 'number',
407
+ value: variableDefinition
408
+ });
409
+ addVariable([
410
+ '',
411
+ ...name
412
+ ], {
413
+ type: 'number',
414
+ value: variableDefinition * -1
415
+ });
416
+ } else if (typeof variableDefinition === 'string') {
417
+ const aliasResult = /^\$\((.*?)\)(?: ?\/ ?(.+?))?$/.exec(variableDefinition);
418
+ const flatName = name.join('-');
419
+ if (aliasResult) {
420
+ let aliasVariableModeResolver = aliasVariableModeResolvers.get(flatName);
421
+ if (!aliasVariableModeResolver) {
422
+ aliasVariableModeResolvers.set(flatName, aliasVariableModeResolver = {});
423
+ }
424
+ aliasVariableModeResolver[mode] = ()=>{
425
+ delete aliasVariableModeResolver[mode];
426
+ const [alias, aliasMode] = aliasResult[1].split('@');
427
+ if (alias) {
428
+ const eachAliasModeVariableResolver = aliasVariableModeResolvers.get(alias);
429
+ if (eachAliasModeVariableResolver) {
430
+ for (const mode of Object.keys(eachAliasModeVariableResolver)){
431
+ eachAliasModeVariableResolver[mode]?.();
225
432
  }
226
- const aliasVariable = this.variables.get(alias);
227
- if (aliasVariable) {
228
- if (aliasMode === undefined && aliasVariable.modes) {
229
- addVariable(name, {
230
- type: aliasVariable.type,
231
- value: aliasVariable.value,
232
- space: aliasVariable.space
233
- }, '', aliasResult[2]);
234
- for(const mode in aliasVariable.modes){
235
- addVariable(name, aliasVariable.modes[mode], mode, aliasResult[2]);
236
- }
237
- } else {
238
- const variable = aliasMode !== undefined ? aliasVariable.modes?.[aliasMode] : aliasVariable;
239
- if (variable) {
240
- const newVariable = {
241
- type: variable.type,
242
- value: variable.value
243
- };
244
- if (variable.type === 'color') {
245
- newVariable.space = variable.space;
246
- }
247
- addVariable(name, newVariable, undefined, aliasResult[2]);
433
+ }
434
+ const aliasVariable = this.variables.get(alias);
435
+ if (aliasVariable) {
436
+ if (aliasMode === undefined && aliasVariable.modes) {
437
+ addVariable(name, {
438
+ type: aliasVariable.type,
439
+ value: aliasVariable.value,
440
+ space: aliasVariable.space
441
+ }, '', aliasResult[2]);
442
+ for(const mode in aliasVariable.modes){
443
+ addVariable(name, aliasVariable.modes[mode], mode, aliasResult[2]);
444
+ }
445
+ } else {
446
+ const variable = aliasMode !== undefined ? aliasVariable.modes?.[aliasMode] : aliasVariable;
447
+ if (variable) {
448
+ const newVariable = {
449
+ type: variable.type,
450
+ value: variable.value
451
+ };
452
+ if (variable.type === 'color') {
453
+ newVariable.space = variable.space;
248
454
  }
455
+ addVariable(name, newVariable, undefined, aliasResult[2]);
249
456
  }
250
457
  }
251
458
  }
252
- };
459
+ }
460
+ };
461
+ } else {
462
+ const hexColorResult = /^#([A-Fa-f0-9]{3,4}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$/.exec(variableDefinition);
463
+ if (hexColorResult) {
464
+ const [r, g, b, a] = hexToRgb(hexColorResult[1]);
465
+ addVariable(name, {
466
+ type: 'color',
467
+ value: `${r} ${g} ${b}${a === 1 ? '' : ' / ' + a}`,
468
+ space: 'rgb'
469
+ });
253
470
  } else {
254
- const hexColorResult = /^#([A-Fa-f0-9]{3,4}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$/.exec(variableDefinition);
255
- if (hexColorResult) {
256
- const [r, g, b, a] = hexToRgb(hexColorResult[1]);
471
+ const rgbFunctionResult = /^rgb\( *([0-9]{1,3})(?: *, *| +)([0-9]{1,3})(?: *, *| +)([0-9]{1,3}) *(?:(?:,|\/) *(.*?) *)?\)$/.exec(variableDefinition);
472
+ if (rgbFunctionResult) {
257
473
  addVariable(name, {
258
474
  type: 'color',
259
- value: `${r} ${g} ${b}${a === 1 ? '' : ' / ' + a}`,
475
+ value: rgbFunctionResult[1] + ' ' + rgbFunctionResult[2] + ' ' + rgbFunctionResult[3] + (rgbFunctionResult[4] ? ' / ' + (rgbFunctionResult[4].startsWith('0.') ? rgbFunctionResult[4].slice(1) : rgbFunctionResult[4]) : ''),
260
476
  space: 'rgb'
261
477
  });
262
478
  } else {
263
- const rgbFunctionResult = /^rgb\( *([0-9]{1,3})(?: *, *| +)([0-9]{1,3})(?: *, *| +)([0-9]{1,3}) *(?:(?:,|\/) *(.*?) *)?\)$/.exec(variableDefinition);
264
- if (rgbFunctionResult) {
479
+ const hslFunctionResult = /^hsl\((.*?)\)$/.exec(variableDefinition);
480
+ if (hslFunctionResult) {
265
481
  addVariable(name, {
266
482
  type: 'color',
267
- value: rgbFunctionResult[1] + ' ' + rgbFunctionResult[2] + ' ' + rgbFunctionResult[3] + (rgbFunctionResult[4] ? ' / ' + (rgbFunctionResult[4].startsWith('0.') ? rgbFunctionResult[4].slice(1) : rgbFunctionResult[4]) : ''),
268
- space: 'rgb'
483
+ value: hslFunctionResult[1],
484
+ space: 'hsl'
269
485
  });
270
486
  } else {
271
- const hslFunctionResult = /^hsl\((.*?)\)$/.exec(variableDefinition);
272
- if (hslFunctionResult) {
273
- addVariable(name, {
274
- type: 'color',
275
- value: hslFunctionResult[1],
276
- space: 'hsl'
277
- });
278
- } else {
279
- addVariable(name, {
280
- type: 'string',
281
- value: variableDefinition
282
- });
283
- }
487
+ addVariable(name, {
488
+ type: 'string',
489
+ value: variableDefinition
490
+ });
284
491
  }
285
492
  }
286
493
  }
287
494
  }
288
- };
495
+ }
496
+ };
497
+ if (variables) {
289
498
  for(const parnetKey in variables){
290
499
  resolveVariable(variables[parnetKey], [
291
500
  parnetKey
@@ -298,178 +507,14 @@ class MasterCSS {
298
507
  }
299
508
  });
300
509
  }
301
- if (at) {
302
- const resolvedAt = flattenObject(at);
303
- for(const eachAt in resolvedAt){
304
- this.at.set(eachAt, resolvedAt[eachAt]);
305
- }
510
+ if (screens) {
511
+ resolveVariable(screens, [
512
+ 'screen'
513
+ ]);
306
514
  }
307
- if (animations) {
308
- for(const animationName in animations){
309
- const eachAnimation = {};
310
- this.animations.set(animationName, eachAnimation);
311
- const eachKeyframes = animations[animationName];
312
- for(const eachKeyframeValue in eachKeyframes){
313
- const newValueByPropertyName = eachAnimation[eachKeyframeValue] = {};
314
- const eachKeyframeDeclarations = eachKeyframes[eachKeyframeValue];
315
- for(const propertyName in eachKeyframeDeclarations){
316
- newValueByPropertyName[propertyName] = eachKeyframeDeclarations[propertyName];
317
- }
318
- }
319
- }
320
- }
321
- const flattedStyles = components ? flattenObject(components) : {};
322
- const utilityNames = Object.keys(flattedStyles);
323
- const handleUtilityName = (utilityName)=>{
324
- if (this.components.has(utilityName)) return;
325
- const currentClass = [];
326
- this.components.set(utilityName, currentClass);
327
- const className = flattedStyles[utilityName];
328
- if (!className) return;
329
- const classNames = className.replace(/(?:\n(?:\s*))+/g, ' ').trim().split(' ');
330
- for (const eachClassName of classNames){
331
- const handle = (className)=>{
332
- if (!currentClass.includes(className)) {
333
- currentClass.push(className);
334
- }
335
- };
336
- if (utilityNames.includes(eachClassName)) {
337
- handleUtilityName(eachClassName);
338
- const componentClasses = this.components.get(eachClassName);
339
- componentClasses?.forEach(handle);
340
- } else {
341
- handle(eachClassName);
342
- }
343
- }
344
- };
345
- for (const eachUtilityName of utilityNames){
346
- handleUtilityName(eachUtilityName);
347
- }
348
- if (rules || utilities) {
349
- const rulesEntries = [];
350
- if (utilities) {
351
- for(const utilityName in utilities){
352
- const declarations = utilities[utilityName];
353
- rulesEntries.push([
354
- utilityName,
355
- {
356
- declarations,
357
- type: SyntaxRuleType.Utility
358
- }
359
- ]);
360
- }
361
- }
362
- if (rules) {
363
- rulesEntries.push(...Object.entries(rules));
364
- }
365
- const rulesEntriesLength = rulesEntries.length;
366
- const colorNames = Object.keys(colorVariableNames);
367
- rulesEntries.sort((a, b)=>{
368
- if (a[1].type !== b[1].type) {
369
- return (b[1].type || 0) - (a[1].type || 0);
370
- }
371
- return b[0].localeCompare(a[0]);
372
- }).forEach(([id, eachSyntaxRuleDefinition], index)=>{
373
- const syntax = {
374
- id,
375
- keys: [],
376
- variables: {},
377
- matchers: {},
378
- order: rulesEntriesLength - 1 - index,
379
- definition: eachSyntaxRuleDefinition
380
- };
381
- if (!eachSyntaxRuleDefinition.unit) {
382
- eachSyntaxRuleDefinition.unit = '';
383
- }
384
- if (!eachSyntaxRuleDefinition.separators) {
385
- eachSyntaxRuleDefinition.separators = [
386
- ','
387
- ];
388
- }
389
- this.definedRules.push(syntax);
390
- const { matcher, type, subkey, ambiguousKeys, ambiguousValues, sign } = eachSyntaxRuleDefinition;
391
- if (type === SyntaxRuleType.Utility) {
392
- syntax.id = '.' + id;
393
- syntax.matchers.arbitrary = new RegExp('^' + escapeString(id) + '(?=!|\\*|>|\\+|~|:|\\[|@|_|\\.|$)', 'm');
394
- }
395
- // todo: 不可使用 startsWith 判斷,應改為更精準的從 config.variables 取得目標變數群組,但 config.variables 中的值還沒被 resolve 像是 Array
396
- const addResolvedVariables = (groupName)=>{
397
- this.variables.forEach((eachVariable)=>{
398
- if (eachVariable.group === groupName) {
399
- syntax.variables[eachVariable.key] = eachVariable;
400
- }
401
- });
402
- };
403
- // 1. custom `config.rules[id].variables`
404
- if (eachSyntaxRuleDefinition.variables) {
405
- for (const eachVariableGroup of eachSyntaxRuleDefinition.variables){
406
- addResolvedVariables(eachVariableGroup);
407
- }
408
- }
409
- // 2. custom `config.variables`
410
- addResolvedVariables(id);
411
- const keys = [];
412
- let { key } = eachSyntaxRuleDefinition;
413
- if (type === SyntaxRuleType.NativeShorthand || type === SyntaxRuleType.Native) {
414
- if (!key) eachSyntaxRuleDefinition.key = key = id;
415
- keys.push(id);
416
- }
417
- if (sign) {
418
- syntax.matchers.arbitrary = new RegExp(`^${sign}[^!*>+~:[@_]+\\|`);
419
- } else if (!matcher) {
420
- const colorsPatten = colorNames.join('|');
421
- if (!key && !subkey) {
422
- keys.push(id);
423
- } else {
424
- if (key && !keys.includes(key)) keys.push(key);
425
- if (subkey) keys.push(subkey);
426
- if (type === SyntaxRuleType.Shorthand) {
427
- keys.push(id);
428
- }
429
- }
430
- if (ambiguousKeys?.length) {
431
- const ambiguousKeyPattern = ambiguousKeys.length > 1 ? `(?:${ambiguousKeys.join('|')})` : ambiguousKeys[0];
432
- const variableKeys = Object.keys(syntax.variables);
433
- if (ambiguousValues?.length) {
434
- const ambiguousValuePatterns = [];
435
- for (const eachAmbiguousValue of ambiguousValues){
436
- if (eachAmbiguousValue instanceof RegExp) {
437
- ambiguousValuePatterns.push(eachAmbiguousValue.source.replace('\\$colors', colorsPatten));
438
- } else {
439
- ambiguousValuePatterns.unshift(`${eachAmbiguousValue}(?:\\b|_)`);
440
- }
441
- }
442
- syntax.matchers.value = new RegExp(`^${ambiguousKeyPattern}:(?:${ambiguousValuePatterns.join('|')})[^|]*?(?:@|$)`);
443
- }
444
- if (variableKeys.length) {
445
- syntax.matchers.variable = new RegExp(`^${ambiguousKeyPattern}:(?:${variableKeys.join('|')}(?![a-zA-Z0-9-]))[^|]*?(?:@|$)`);
446
- }
447
- }
448
- } else {
449
- syntax.matchers.arbitrary = matcher;
450
- }
451
- if (keys.length) {
452
- syntax.keys = keys;
453
- syntax.matchers.key = new RegExp(`^${keys.length > 1 ? `(${keys.join('|')})` : keys[0]}:.`);
454
- }
455
- });
456
- }
457
- this.components.forEach((componentClasses, componentClass)=>{
458
- const syntaxRulesByStateToken = componentClasses.map((eachSyntax)=>this.create(eachSyntax)).filter((eachSyntax)=>eachSyntax?.text).reduce((obj, eachSyntaxRule)=>{
459
- if (eachSyntaxRule.stateToken in obj) {
460
- obj[eachSyntaxRule.stateToken].push(eachSyntaxRule);
461
- } else {
462
- obj[eachSyntaxRule.stateToken] = [
463
- eachSyntaxRule
464
- ];
465
- }
466
- return obj;
467
- }, {});
468
- this.components.set(componentClass, Object.keys(syntaxRulesByStateToken).map((stateToken)=>{
469
- const syntaxRules = syntaxRulesByStateToken[stateToken];
470
- return syntaxRules.length === 1 ? syntaxRules[0].name : '{' + syntaxRulesByStateToken[stateToken].map((eachSyntaxRule)=>eachSyntaxRule.name.slice(0, eachSyntaxRule.name.length - eachSyntaxRule.stateToken.length)).join(';') + '}' + stateToken;
471
- }));
472
- });
515
+ }
516
+ parseValue(token, unit = 'rem') {
517
+ return parseValue(token, unit, this.config.rootSize);
473
518
  }
474
519
  /**
475
520
  * Match check if Master CSS syntax
@@ -511,7 +556,9 @@ class MasterCSS {
511
556
  if (componentClasses) {
512
557
  componentClasses.forEach((eachSyntax)=>{
513
558
  const syntaxRule = this.create(eachSyntax, className, mode);
514
- if (syntaxRule?.text) syntaxRules.push(syntaxRule);
559
+ if (syntaxRule && syntaxRule.valid) {
560
+ syntaxRules.push(syntaxRule);
561
+ }
515
562
  });
516
563
  } else {
517
564
  const atIndex = className.indexOf('@');
@@ -522,98 +569,18 @@ class MasterCSS {
522
569
  const atToken = className.slice(atIndex);
523
570
  componentClasses.forEach((eachSyntax)=>{
524
571
  const syntaxRule = this.create(eachSyntax + atToken, className, mode);
525
- if (syntaxRule?.text) syntaxRules.push(syntaxRule);
572
+ if (syntaxRule && syntaxRule.valid) {
573
+ syntaxRules.push(syntaxRule);
574
+ }
526
575
  });
527
576
  }
528
577
  }
529
578
  const syntaxRule = this.create(className, undefined, mode);
530
- if (syntaxRule?.text) syntaxRules.push(syntaxRule);
531
- }
532
- return syntaxRules;
533
- }
534
- generateVendorSelectors(selectorText) {
535
- const vendorSelectors = {};
536
- const transformSelector = (selectorText, selectorValues, selectors, matched)=>{
537
- for (const [regexp, newSelectorTexts] of selectorValues){
538
- if (regexp.test(selectorText)) {
539
- for (const eachNewSelectorText of newSelectorTexts){
540
- transformSelector(selectorText.replace(regexp, eachNewSelectorText), selectorValues, selectors, true);
541
- }
542
- return;
543
- }
544
- }
545
- if (matched) {
546
- selectors.push(selectorText);
547
- }
548
- };
549
- const spacedSelectorToken = (selectorText)=>{
550
- // 1. \'123\'
551
- // 2. [href='http://localhost']
552
- const transformedSelectorText = selectorText.split(/(\\'(?:.*?)[^\\]\\')(?=[*_>~+,)])|(\[[^=]+='(?:.*?)[^\\]'\])/).map((eachToken, i)=>i % 3 ? eachToken : eachToken.replace(/(^|[^_])_(?!_)/g, '$1 ')).join('');
553
- const selectors = [];
554
- let currentSelector = '';
555
- let symbolCount = 0;
556
- for(let i = 0; i < transformedSelectorText.length; i++){
557
- const char = transformedSelectorText[i];
558
- if (char === '\\') {
559
- currentSelector += char + transformedSelectorText[++i];
560
- continue;
561
- }
562
- if (!symbolCount && char === ',') {
563
- selectors.push(currentSelector);
564
- currentSelector = '';
565
- } else {
566
- currentSelector += char;
567
- if (symbolCount && char === ')') {
568
- symbolCount--;
569
- } else if (char === '(') {
570
- symbolCount++;
571
- }
572
- }
573
- }
574
- if (currentSelector) {
575
- selectors.push(currentSelector);
576
- }
577
- return selectors;
578
- };
579
- const transformedSelectors = [];
580
- const selectorValues = this.selectors.get('');
581
- if (selectorValues) {
582
- transformSelector(selectorText, selectorValues, transformedSelectors, true);
583
- } else {
584
- transformedSelectors.push(selectorText);
585
- }
586
- const unspacedVendorSelectors = {};
587
- this.selectors.forEach((selectorValues, vendor)=>{
588
- if (!vendor) return;
589
- const newUnspacedVendorSelectors = [];
590
- for (const eachTransformedSelector of transformedSelectors){
591
- transformSelector(eachTransformedSelector, selectorValues, newUnspacedVendorSelectors, false);
592
- }
593
- if (newUnspacedVendorSelectors.length) {
594
- unspacedVendorSelectors[vendor] = newUnspacedVendorSelectors;
595
- }
596
- });
597
- const insertVendorSelectors = (vendor, selectorTexts)=>{
598
- const groupedSelectorTexts = selectorTexts.reduce((arr, eachSuffixSelector)=>{
599
- arr.push(...spacedSelectorToken(eachSuffixSelector));
600
- return arr;
601
- }, []);
602
- if (vendor in vendorSelectors) {
603
- vendorSelectors[vendor].push(...groupedSelectorTexts);
604
- } else {
605
- vendorSelectors[vendor] = groupedSelectorTexts;
606
- }
607
- };
608
- const vendors = Object.keys(unspacedVendorSelectors);
609
- if (vendors.length) {
610
- for (const eachVendor of vendors){
611
- insertVendorSelectors(eachVendor, unspacedVendorSelectors[eachVendor]);
579
+ if (syntaxRule && syntaxRule.valid) {
580
+ syntaxRules.push(syntaxRule);
612
581
  }
613
- } else {
614
- insertVendorSelectors('', transformedSelectors);
615
582
  }
616
- return vendorSelectors;
583
+ return syntaxRules;
617
584
  }
618
585
  /**
619
586
  * Create syntax rule from given class name
@@ -634,7 +601,7 @@ class MasterCSS {
634
601
  for(let i = 0; i < selectorTextSplits.length; i++){
635
602
  const eachField = selectorTextSplits[i];
636
603
  const modeSelector = this.getModeSelector(eachField);
637
- if (i === 0 && eachField === modeSelector) continue;
604
+ if (i === 0 && eachField === modeSelector || (i === 0 || i === 1) && eachField === this.config.scope) continue;
638
605
  if (eachField.startsWith('.')) {
639
606
  const eachFieldName = eachField.slice(1);
640
607
  let className = '';
@@ -660,17 +627,23 @@ class MasterCSS {
660
627
  * 根據蒐集到的所有 DOM class 重新 create
661
628
  */ refresh(customConfig) {
662
629
  this.reset();
663
- if (customConfig) {
664
- this.customConfig = customConfig;
665
- } else {
666
- customConfig = this.customConfig;
667
- }
668
- // @ts-ignore
669
- this.config = customConfig?.override ? extendConfig(customConfig) : extendConfig(config, customConfig);
670
- this.resolve();
630
+ this.resolve(customConfig);
671
631
  return this;
672
632
  }
673
633
  reset() {
634
+ // @ts-ignore
635
+ this.animations = new Map();
636
+ // @ts-ignore
637
+ this.variables = new Map();
638
+ // @ts-ignore
639
+ this.atRules = new Map();
640
+ // @ts-ignore
641
+ this.selectors = new Map();
642
+ // @ts-ignore
643
+ this.components = new Map();
644
+ // @ts-ignore
645
+ this.classRules = new Map();
646
+ this.definedRules.length = 0;
674
647
  this.baseLayer.reset();
675
648
  this.themeLayer.reset();
676
649
  this.presetLayer.reset();
@@ -685,7 +658,13 @@ class MasterCSS {
685
658
  }
686
659
  add(...classNames) {
687
660
  for (const className of classNames){
688
- this.generate(className).forEach((eachSyntaxRule)=>eachSyntaxRule.layer.insert(eachSyntaxRule));
661
+ const rules = this.classRules.get(className);
662
+ if (rules) continue;
663
+ const newRules = this.generate(className);
664
+ if (newRules.length) {
665
+ newRules.forEach((eachSyntaxRule)=>eachSyntaxRule.layer.insert(eachSyntaxRule));
666
+ this.classRules.set(className, newRules);
667
+ }
689
668
  }
690
669
  return this;
691
670
  }
@@ -694,27 +673,10 @@ class MasterCSS {
694
673
  * class name 從 DOM tree 中被移除,
695
674
  * 匹配並刪除對應的 rule
696
675
  */ for (const className of classNames){
697
- const componentClasses = this.components.get(className);
698
- if (componentClasses) {
699
- componentClasses.forEach((componentClass)=>{
700
- this.componentsLayer.delete(className + ' ' + componentClass);
701
- });
702
- } else {
703
- const atIndex = className.indexOf('@');
704
- if (atIndex !== -1) {
705
- const name = className.slice(0, atIndex);
706
- const componentClasses = this.components.get(name);
707
- if (componentClasses) {
708
- const atToken = className.slice(atIndex);
709
- componentClasses.forEach((componentClass)=>{
710
- this.componentsLayer.delete(componentClass + atToken + ' ' + className);
711
- });
712
- } else {
713
- this.generalLayer.delete(className);
714
- }
715
- } else {
716
- this.generalLayer.delete(className);
717
- }
676
+ const rules = this.classRules.get(className);
677
+ if (rules) {
678
+ rules.forEach((rule)=>rule.layer.delete(rule.key));
679
+ this.classRules.delete(className);
718
680
  }
719
681
  }
720
682
  }