@tbela99/css-parser 0.0.1-alpha5 → 0.0.1-rc2

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,59 +1,303 @@
1
- import { isIdentStart, isWhiteSpace } from './utils/syntax.js';
2
- import { getConfig } from './utils/config.js';
3
- import { PropertyList } from './declaration/list.js';
4
- import { eq } from './utils/eq.js';
1
+ import { isIdentStart, isIdent, isFunction, isWhiteSpace } from '../parser/utils/syntax.js';
2
+ import { PropertyList } from '../parser/declaration/list.js';
3
+ import { eq } from '../parser/utils/eq.js';
5
4
  import { render } from '../renderer/render.js';
5
+ import '../renderer/utils/color.js';
6
6
 
7
- const configuration = getConfig();
8
7
  const combinators = ['+', '>', '~'];
9
8
  const notEndingWith = ['(', '['].concat(combinators);
10
- function wrapNodes(previous, node, match, ast, i, nodeIndex) {
11
- // @ts-ignore
12
- let pSel = match.selector1.reduce(reducer, []).join(',');
13
- // @ts-ignore
14
- let nSel = match.selector2.reduce(reducer, []).join(',');
15
- // @ts-ignore
16
- const wrapper = { ...previous, chi: [], sel: match.match.reduce(reducer, []).join(',') };
17
- // @ts-ignore
18
- Object.defineProperty(wrapper, 'raw', {
19
- enumerable: false,
20
- writable: true,
9
+ function minify(ast, options = {}, recursive = false) {
10
+ function wrapNodes(previous, node, match, ast, i, nodeIndex) {
21
11
  // @ts-ignore
22
- value: match.match.map(t => t.slice())
23
- });
24
- if (pSel == '&' || pSel === '') {
12
+ let pSel = match.selector1.reduce(reducer, []).join(',');
25
13
  // @ts-ignore
26
- wrapper.chi.push(...previous.chi);
14
+ let nSel = match.selector2.reduce(reducer, []).join(',');
27
15
  // @ts-ignore
28
- if ((nSel == '&' || nSel === '') && hasOnlyDeclarations(previous)) {
16
+ const wrapper = { ...previous, chi: [], sel: match.match.reduce(reducer, []).join(',') };
17
+ // @ts-ignore
18
+ Object.defineProperty(wrapper, 'raw', {
19
+ enumerable: false,
20
+ writable: true,
21
+ // @ts-ignore
22
+ value: match.match.map(t => t.slice())
23
+ });
24
+ if (pSel == '&' || pSel === '') {
25
+ // @ts-ignore
26
+ wrapper.chi.push(...previous.chi);
29
27
  // @ts-ignore
30
- wrapper.chi.push(...node.chi);
28
+ if ((nSel == '&' || nSel === '') && hasOnlyDeclarations(previous)) {
29
+ // @ts-ignore
30
+ wrapper.chi.push(...node.chi);
31
+ }
32
+ else {
33
+ // @ts-ignore
34
+ wrapper.chi.push(node);
35
+ }
31
36
  }
32
37
  else {
33
38
  // @ts-ignore
34
- wrapper.chi.push(node);
39
+ wrapper.chi.push(previous, node);
40
+ }
41
+ // @ts-ignore
42
+ ast.chi.splice(i, 1, wrapper);
43
+ // @ts-ignore
44
+ ast.chi.splice(nodeIndex, 1);
45
+ // @ts-ignore
46
+ previous.sel = pSel;
47
+ // @ts-ignore
48
+ previous.raw = match.selector1;
49
+ // @ts-ignore
50
+ node.sel = nSel;
51
+ // @ts-ignore
52
+ node.raw = match.selector2;
53
+ reduceRuleSelector(wrapper);
54
+ return wrapper;
55
+ }
56
+ function reducer(acc, curr, index, array) {
57
+ // trim :is()
58
+ if (array.length == 1 && array[0][0] == ':is(' && array[0].at(-1) == ')') {
59
+ curr = curr.slice(1, -1);
35
60
  }
61
+ if (curr[0] == '&') {
62
+ if (curr[1] == ' ' && !isIdent(curr[2]) && !isFunction(curr[2])) {
63
+ curr.splice(0, 2);
64
+ }
65
+ else if (combinators.includes(curr[1])) {
66
+ curr.splice(0, 1);
67
+ }
68
+ }
69
+ else if (ast.typ == 'Rule' && (isIdent(curr[0]) || isFunction(curr[0]))) {
70
+ curr.unshift('&', ' ');
71
+ }
72
+ acc.push(curr.join(''));
73
+ return acc;
36
74
  }
37
- else {
75
+ function diff(n1, n2, options = {}) {
76
+ let node1 = n1;
77
+ let node2 = n2;
78
+ let exchanged = false;
79
+ if (node1.chi.length > node2.chi.length) {
80
+ const t = node1;
81
+ node1 = node2;
82
+ node2 = t;
83
+ exchanged = true;
84
+ }
85
+ let i = node1.chi.length;
86
+ let j = node2.chi.length;
87
+ if (i == 0 || j == 0) {
88
+ // @ts-ignore
89
+ return null;
90
+ }
91
+ // @ts-ignore
92
+ const raw1 = node1.raw;
93
+ // @ts-ignore
94
+ const raw2 = node2.raw;
95
+ // @ts-ignore
96
+ node1 = { ...node1, chi: node1.chi.slice() };
97
+ node2 = { ...node2, chi: node2.chi.slice() };
98
+ if (raw1 != null) {
99
+ Object.defineProperty(node1, 'raw', { enumerable: false, writable: true, value: raw1 });
100
+ }
101
+ if (raw2 != null) {
102
+ Object.defineProperty(node2, 'raw', { enumerable: false, writable: true, value: raw2 });
103
+ }
104
+ const intersect = [];
105
+ while (i--) {
106
+ if (node1.chi[i].typ == 'Comment') {
107
+ continue;
108
+ }
109
+ j = node2.chi.length;
110
+ if (j == 0) {
111
+ break;
112
+ }
113
+ while (j--) {
114
+ if (node2.chi[j].typ == 'Comment') {
115
+ continue;
116
+ }
117
+ if (node1.chi[i].nam == node2.chi[j].nam) {
118
+ if (eq(node1.chi[i], node2.chi[j])) {
119
+ intersect.push(node1.chi[i]);
120
+ node1.chi.splice(i, 1);
121
+ node2.chi.splice(j, 1);
122
+ break;
123
+ }
124
+ }
125
+ }
126
+ }
38
127
  // @ts-ignore
39
- wrapper.chi.push(previous, node);
128
+ const result = (intersect.length == 0 ? null : {
129
+ ...node1,
130
+ // @ts-ignore
131
+ sel: [...new Set([...(n1?.raw?.reduce(reducer, []) || splitRule(n1.sel)).concat(n2?.raw?.reduce(reducer, []) || splitRule(n2.sel))])].join(','),
132
+ chi: intersect.reverse()
133
+ });
134
+ if (result == null || [n1, n2].reduce((acc, curr) => curr.chi.length == 0 ? acc : acc + render(curr, options).code.length, 0) <= [node1, node2, result].reduce((acc, curr) => curr.chi.length == 0 ? acc : acc + render(curr, options).code.length, 0)) {
135
+ // @ts-ignore
136
+ return null;
137
+ }
138
+ return { result, node1: exchanged ? node2 : node1, node2: exchanged ? node1 : node2 };
139
+ }
140
+ function matchSelectors(selector1, selector2, parentType) {
141
+ let match = [[]];
142
+ const j = Math.min(selector1.reduce((acc, curr) => Math.min(acc, curr.length), selector1.length > 0 ? selector1[0].length : 0), selector2.reduce((acc, curr) => Math.min(acc, curr.length), selector2.length > 0 ? selector2[0].length : 0));
143
+ let i = 0;
144
+ let k;
145
+ let l;
146
+ let token;
147
+ let matching = true;
148
+ let matchFunction = 0;
149
+ let inAttr = 0;
150
+ for (; i < j; i++) {
151
+ k = 0;
152
+ token = selector1[0][i];
153
+ for (; k < selector1.length; k++) {
154
+ if (selector1[k][i] != token) {
155
+ matching = false;
156
+ break;
157
+ }
158
+ }
159
+ if (matching) {
160
+ l = 0;
161
+ for (; l < selector2.length; l++) {
162
+ if (selector2[l][i] != token) {
163
+ matching = false;
164
+ break;
165
+ }
166
+ }
167
+ }
168
+ if (!matching) {
169
+ break;
170
+ }
171
+ if (token == ',') {
172
+ match.push([]);
173
+ }
174
+ else {
175
+ if (token.endsWith('(')) {
176
+ matchFunction++;
177
+ }
178
+ if (token.endsWith('[')) {
179
+ inAttr++;
180
+ }
181
+ else if (token == ')') {
182
+ matchFunction--;
183
+ }
184
+ else if (token == ']') {
185
+ inAttr--;
186
+ }
187
+ match.at(-1).push(token);
188
+ }
189
+ }
190
+ // invalid function
191
+ if (matchFunction != 0 || inAttr != 0) {
192
+ return null;
193
+ }
194
+ if (parentType != 'Rule') {
195
+ for (const part of match) {
196
+ if (part.length > 0 && combinators.includes(part[0].charAt(0))) {
197
+ return null;
198
+ }
199
+ }
200
+ }
201
+ if (match.length > 1) {
202
+ console.error(`unsupported multilevel matching`);
203
+ console.error({ match, selector1, selector2 });
204
+ return null;
205
+ }
206
+ for (const part of match) {
207
+ while (part.length > 0) {
208
+ const token = part.at(-1);
209
+ if (token == ' ' || combinators.includes(token) || notEndingWith.includes(token.at(-1))) {
210
+ part.pop();
211
+ continue;
212
+ }
213
+ break;
214
+ }
215
+ }
216
+ if (match.every(t => t.length == 0)) {
217
+ return null;
218
+ }
219
+ if (eq([['&']], match)) {
220
+ return null;
221
+ }
222
+ function reduce(acc, curr) {
223
+ if (acc === null) {
224
+ return null;
225
+ }
226
+ let hasCompoundSelector = true;
227
+ curr = curr.slice(match[0].length);
228
+ while (curr.length > 0) {
229
+ if (curr[0] == ' ') {
230
+ hasCompoundSelector = false;
231
+ curr.unshift('&');
232
+ continue;
233
+ }
234
+ break;
235
+ }
236
+ // invalid function match
237
+ if (curr.length > 0 && curr[0].endsWith('(') && curr.at(-1) != ')') {
238
+ return null;
239
+ }
240
+ if (curr.length == 1 && combinators.includes(curr[0].charAt(0))) {
241
+ return null;
242
+ }
243
+ if (hasCompoundSelector && curr.length > 0) {
244
+ hasCompoundSelector = !['&'].concat(combinators).includes(curr[0].charAt(0));
245
+ }
246
+ if (curr[0] == ':is(') {
247
+ let inFunction = 0;
248
+ let canReduce = true;
249
+ const isCompound = curr.reduce((acc, token, index) => {
250
+ if (index == 0) {
251
+ inFunction++;
252
+ canReduce = curr[1] == '&';
253
+ }
254
+ else if (token.endsWith('(')) {
255
+ if (inFunction == 0) {
256
+ canReduce = false;
257
+ }
258
+ inFunction++;
259
+ }
260
+ else if (token == ')') {
261
+ inFunction--;
262
+ }
263
+ else if (token == ',') {
264
+ if (!canReduce) {
265
+ canReduce = curr[index + 1] == '&';
266
+ }
267
+ acc.push([]);
268
+ }
269
+ else
270
+ acc.at(-1)?.push(token);
271
+ return acc;
272
+ }, [[]]);
273
+ if (inFunction > 0) {
274
+ canReduce = false;
275
+ }
276
+ if (canReduce) {
277
+ curr = isCompound.reduce((acc, curr) => {
278
+ if (acc.length > 0) {
279
+ acc.push(',');
280
+ }
281
+ acc.push(...curr);
282
+ return acc;
283
+ }, []);
284
+ }
285
+ }
286
+ // @todo: check hasCompoundSelector && curr[0] == '&' && curr[1] == ' '
287
+ acc.push(match.length == 0 ? ['&'] : (hasCompoundSelector && curr[0] != '&' && (curr.length == 0 || !combinators.includes(curr[0].charAt(0))) ? ['&'].concat(curr) : curr));
288
+ return acc;
289
+ }
290
+ // @ts-ignore
291
+ selector1 = selector1.reduce(reduce, []);
292
+ // @ts-ignore
293
+ selector2 = selector2.reduce(reduce, []);
294
+ return selector1 == null || selector2 == null ? null : {
295
+ eq: eq(selector1, selector2),
296
+ match,
297
+ selector1,
298
+ selector2
299
+ };
40
300
  }
41
- // @ts-ignore
42
- ast.chi.splice(i, 1, wrapper);
43
- // @ts-ignore
44
- ast.chi.splice(nodeIndex, 1);
45
- // @ts-ignore
46
- previous.sel = pSel;
47
- // @ts-ignore
48
- previous.raw = match.selector1;
49
- // @ts-ignore
50
- node.sel = nSel;
51
- // @ts-ignore
52
- node.raw = match.selector2;
53
- reduceRuleSelector(wrapper);
54
- return wrapper;
55
- }
56
- function deduplicate(ast, options = {}, recursive = false) {
57
301
  // @ts-ignore
58
302
  if (('chi' in ast) && ast.chi?.length > 0) {
59
303
  let i = 0;
@@ -79,11 +323,27 @@ function deduplicate(ast, options = {}, recursive = false) {
79
323
  if (node.typ == 'AtRule' && node.nam == 'font-face') {
80
324
  continue;
81
325
  }
82
- if (node.typ == 'AtRule' && node.val == 'all') {
326
+ if (node.typ == 'AtRule') {
327
+ if (node.nam == 'media' && node.val == 'all') {
328
+ // @ts-ignore
329
+ ast.chi?.splice(i, 1, ...node.chi);
330
+ i--;
331
+ continue;
332
+ }
333
+ // console.debug({previous, node});
83
334
  // @ts-ignore
84
- ast.chi?.splice(i, 1, ...node.chi);
85
- i--;
86
- continue;
335
+ if (previous?.typ == 'AtRule' &&
336
+ previous.nam == node.nam &&
337
+ previous.val == node.val) {
338
+ if ('chi' in node) {
339
+ // @ts-ignore
340
+ previous.chi.push(...node.chi);
341
+ }
342
+ // else {
343
+ ast?.chi?.splice(i--, 1);
344
+ continue;
345
+ // }
346
+ }
87
347
  }
88
348
  // @ts-ignore
89
349
  if (node.typ == 'Rule') {
@@ -93,7 +353,8 @@ function deduplicate(ast, options = {}, recursive = false) {
93
353
  // @ts-ignore
94
354
  if (options.nestingRules) {
95
355
  // @ts-ignore
96
- if (previous != null && previous.typ == 'Rule') {
356
+ if (previous?.typ == 'Rule') {
357
+ // @ts-ignore
97
358
  reduceRuleSelector(previous);
98
359
  // @ts-ignore
99
360
  match = matchSelectors(previous.raw, node.raw, ast.typ);
@@ -132,7 +393,7 @@ function deduplicate(ast, options = {}, recursive = false) {
132
393
  nodeIndex = --i;
133
394
  // @ts-ignore
134
395
  previous = ast.chi[nodeIndex];
135
- deduplicate(wrapper, options, recursive);
396
+ minify(wrapper, options, recursive);
136
397
  continue;
137
398
  }
138
399
  // @ts-ignore
@@ -181,13 +442,24 @@ function deduplicate(ast, options = {}, recursive = false) {
181
442
  }
182
443
  else if (combinators.includes(curr[0])) {
183
444
  curr.unshift('&');
445
+ wrap = false;
184
446
  }
185
447
  // @ts-ignore
186
- acc.push(curr.map(t => t.replaceAll('&', node.optimized.optimized[0])).join(''));
448
+ acc.push(curr);
187
449
  return acc;
188
450
  }, []);
451
+ if (!wrap) {
452
+ wrap = selector.some(s => s[0] != '&');
453
+ }
454
+ const rule = selector.map(s => {
455
+ if (s[0] == '&') {
456
+ // @ts-ignore
457
+ s[0] = node.optimized.optimized[0];
458
+ }
459
+ return s.join('');
460
+ }).join(',');
189
461
  // @ts-ignore
190
- node.sel = (wrap ? node.optimized.optimized[0] : '') + `:is(${selector.join(',')})`;
462
+ node.sel = wrap ? node.optimized.optimized[0] + `:is(${rule})` : rule;
191
463
  }
192
464
  }
193
465
  // @ts-ignore
@@ -218,10 +490,10 @@ function deduplicate(ast, options = {}, recursive = false) {
218
490
  // @ts-ignore
219
491
  if (hasDeclaration(node)) {
220
492
  // @ts-ignore
221
- deduplicateRule(node);
493
+ minifyRule(node);
222
494
  }
223
495
  else {
224
- deduplicate(node, options, recursive);
496
+ minify(node, options, recursive);
225
497
  }
226
498
  i--;
227
499
  previous = node;
@@ -263,10 +535,10 @@ function deduplicate(ast, options = {}, recursive = false) {
263
535
  // @ts-ignore
264
536
  if (hasDeclaration(previous)) {
265
537
  // @ts-ignore
266
- deduplicateRule(previous);
538
+ minifyRule(previous);
267
539
  }
268
540
  else {
269
- deduplicate(previous, options, recursive);
541
+ minify(previous, options, recursive);
270
542
  }
271
543
  }
272
544
  }
@@ -277,18 +549,97 @@ function deduplicate(ast, options = {}, recursive = false) {
277
549
  if (recursive && node != null && ('chi' in node)) {
278
550
  // @ts-ignore
279
551
  if (node.chi.some(n => n.typ == 'Declaration')) {
280
- deduplicateRule(node);
552
+ minifyRule(node);
281
553
  }
282
554
  else {
283
555
  // @ts-ignore
284
556
  if (!(node.typ == 'AtRule' && node.nam != 'font-face')) {
285
- deduplicate(node, options, recursive);
557
+ minify(node, options, recursive);
286
558
  }
287
559
  }
288
560
  }
289
561
  }
290
562
  return ast;
291
563
  }
564
+ function reduceSelector(selector) {
565
+ if (selector.length == 0) {
566
+ return null;
567
+ }
568
+ const optimized = [];
569
+ const k = selector.reduce((acc, curr) => acc == 0 ? curr.length : (curr.length == 0 ? acc : Math.min(acc, curr.length)), 0);
570
+ let i = 0;
571
+ let j;
572
+ let match;
573
+ for (; i < k; i++) {
574
+ const item = selector[0][i];
575
+ match = true;
576
+ for (j = 1; j < selector.length; j++) {
577
+ if (item != selector[j][i]) {
578
+ match = false;
579
+ break;
580
+ }
581
+ }
582
+ if (!match) {
583
+ break;
584
+ }
585
+ optimized.push(item);
586
+ }
587
+ while (optimized.length > 0) {
588
+ const last = optimized.at(-1);
589
+ if ((last == ' ' || combinators.includes(last))) {
590
+ optimized.pop();
591
+ continue;
592
+ }
593
+ break;
594
+ }
595
+ selector.forEach((selector) => selector.splice(0, optimized.length));
596
+ // combinator
597
+ if (combinators.includes(optimized.at(-1))) {
598
+ const combinator = optimized.pop();
599
+ selector.forEach(selector => selector.unshift(combinator));
600
+ }
601
+ let reducible = optimized.length == 1;
602
+ if (optimized[0] == '&' && optimized[1] == ' ') {
603
+ optimized.splice(0, 2);
604
+ }
605
+ if (optimized.length == 0 ||
606
+ (optimized[0].charAt(0) == '&' ||
607
+ selector.length == 1)) {
608
+ return {
609
+ match: false,
610
+ optimized,
611
+ selector: selector.map(selector => selector[0] == '&' && selector[1] == ' ' ? selector.slice(2) : selector),
612
+ reducible: selector.length > 1 && selector.every((selector) => !combinators.includes(selector[0]))
613
+ };
614
+ }
615
+ return {
616
+ match: true,
617
+ optimized,
618
+ selector: selector.reduce((acc, curr) => {
619
+ let hasCompound = true;
620
+ if (hasCompound && curr.length > 0) {
621
+ hasCompound = !['&'].concat(combinators).includes(curr[0].charAt(0));
622
+ }
623
+ // @ts-ignore
624
+ if (hasCompound && curr[0] == ' ') {
625
+ hasCompound = false;
626
+ curr.unshift('&');
627
+ }
628
+ if (curr.length == 0) {
629
+ curr.push('&');
630
+ hasCompound = false;
631
+ }
632
+ if (reducible) {
633
+ const chr = curr[0].charAt(0);
634
+ // @ts-ignore
635
+ reducible = chr == '.' || chr == ':' || isIdentStart(chr.codePointAt(0));
636
+ }
637
+ acc.push(hasCompound ? ['&'].concat(curr) : curr);
638
+ return acc;
639
+ }, []),
640
+ reducible: selector.every((selector) => !['>', '+', '~', '&'].includes(selector[0]))
641
+ };
642
+ }
292
643
  function hasOnlyDeclarations(node) {
293
644
  let k = node.chi.length;
294
645
  while (k--) {
@@ -311,7 +662,7 @@ function hasDeclaration(node) {
311
662
  }
312
663
  return true;
313
664
  }
314
- function deduplicateRule(ast) {
665
+ function minifyRule(ast) {
315
666
  // @ts-ignore
316
667
  if (!('chi' in ast) || ast.chi?.length <= 1) {
317
668
  return ast;
@@ -319,45 +670,19 @@ function deduplicateRule(ast) {
319
670
  // @ts-ignore
320
671
  const j = ast.chi.length;
321
672
  let k = 0;
322
- let map = new Map;
673
+ let properties = new PropertyList();
323
674
  // @ts-ignore
324
675
  for (; k < j; k++) {
325
676
  // @ts-ignore
326
677
  const node = ast.chi[k];
327
- if (node.typ == 'Comment') {
328
- // @ts-ignore
329
- map.set(node, node);
678
+ if (node.typ == 'Comment' || node.typ == 'Declaration') {
679
+ properties.add(node);
330
680
  continue;
331
681
  }
332
- else if (node.typ != 'Declaration') {
333
- break;
334
- }
335
- if (node.nam in configuration.map ||
336
- node.nam in configuration.properties) {
337
- // @ts-ignore
338
- const shorthand = node.nam in configuration.map ? configuration.map[node.nam].shorthand : configuration.properties[node.nam].shorthand;
339
- if (!map.has(shorthand)) {
340
- map.set(shorthand, new PropertyList());
341
- }
342
- map.get(shorthand).add(node);
343
- }
344
- else {
345
- map.set(node.nam, node);
346
- }
347
- }
348
- const children = [];
349
- for (let child of map.values()) {
350
- if (child instanceof PropertyList) {
351
- // @ts-ignore
352
- children.push(...child);
353
- }
354
- else {
355
- // @ts-ignore
356
- children.push(child);
357
- }
682
+ break;
358
683
  }
359
684
  // @ts-ignore
360
- ast.chi = children.concat(ast.chi?.slice(k));
685
+ ast.chi = [...properties].concat(ast.chi.slice(k));
361
686
  return ast;
362
687
  }
363
688
  function splitRule(buffer) {
@@ -483,335 +808,5 @@ function reduceRuleSelector(node) {
483
808
  }
484
809
  // }
485
810
  }
486
- function diff(n1, n2, options = {}) {
487
- let node1 = n1;
488
- let node2 = n2;
489
- let exchanged = false;
490
- if (node1.chi.length > node2.chi.length) {
491
- const t = node1;
492
- node1 = node2;
493
- node2 = t;
494
- exchanged = true;
495
- }
496
- let i = node1.chi.length;
497
- let j = node2.chi.length;
498
- if (i == 0 || j == 0) {
499
- // @ts-ignore
500
- return null;
501
- }
502
- // @ts-ignore
503
- const raw1 = node1.raw;
504
- // @ts-ignore
505
- // const optimized1 = node1.optimized;
506
- // @ts-ignore
507
- const raw2 = node2.raw;
508
- // @ts-ignore
509
- // const optimized2 = node2.optimized;
510
- node1 = { ...node1, chi: node1.chi.slice() };
511
- node2 = { ...node2, chi: node2.chi.slice() };
512
- if (raw1 != null) {
513
- Object.defineProperty(node1, 'raw', { enumerable: false, writable: true, value: raw1 });
514
- }
515
- // if (optimized1 != null) {
516
- // Object.defineProperty(node1, 'optimized', {enumerable: false, writable: true, value: optimized1});
517
- // }
518
- if (raw2 != null) {
519
- Object.defineProperty(node2, 'raw', { enumerable: false, writable: true, value: raw2 });
520
- }
521
- // if (optimized2 != null) {
522
- // Object.defineProperty(node2, 'optimized', {enumerable: false, writable: true, value: optimized2});
523
- // }
524
- const intersect = [];
525
- while (i--) {
526
- if (node1.chi[i].typ == 'Comment') {
527
- continue;
528
- }
529
- j = node2.chi.length;
530
- if (j == 0) {
531
- break;
532
- }
533
- while (j--) {
534
- if (node2.chi[j].typ == 'Comment') {
535
- continue;
536
- }
537
- if (node1.chi[i].nam == node2.chi[j].nam) {
538
- if (eq(node1.chi[i], node2.chi[j])) {
539
- intersect.push(node1.chi[i]);
540
- node1.chi.splice(i, 1);
541
- node2.chi.splice(j, 1);
542
- break;
543
- }
544
- }
545
- }
546
- }
547
- // @ts-ignore
548
- const result = (intersect.length == 0 ? null : {
549
- ...node1,
550
- // @ts-ignore
551
- sel: [...new Set([...(n1?.raw?.reduce(reducer, []) || splitRule(n1.sel)).concat(n2?.raw?.reduce(reducer, []) || splitRule(n2.sel))])].join(','),
552
- chi: intersect.reverse()
553
- });
554
- if (result == null || [n1, n2].reduce((acc, curr) => curr.chi.length == 0 ? acc : acc + render(curr, options).code.length, 0) <= [node1, node2, result].reduce((acc, curr) => curr.chi.length == 0 ? acc : acc + render(curr, options).code.length, 0)) {
555
- // @ts-ignore
556
- return null;
557
- }
558
- return { result, node1: exchanged ? node2 : node1, node2: exchanged ? node2 : node2 };
559
- }
560
- function matchSelectors(selector1, selector2, parentType) {
561
- let match = [[]];
562
- const j = Math.min(selector1.reduce((acc, curr) => Math.min(acc, curr.length), selector1.length > 0 ? selector1[0].length : 0), selector2.reduce((acc, curr) => Math.min(acc, curr.length), selector2.length > 0 ? selector2[0].length : 0));
563
- let i = 0;
564
- let k;
565
- let l;
566
- let token;
567
- let matching = true;
568
- let matchFunction = 0;
569
- let inAttr = 0;
570
- for (; i < j; i++) {
571
- k = 0;
572
- token = selector1[0][i];
573
- for (; k < selector1.length; k++) {
574
- if (selector1[k][i] != token) {
575
- matching = false;
576
- break;
577
- }
578
- }
579
- if (matching) {
580
- l = 0;
581
- for (; l < selector2.length; l++) {
582
- if (selector2[l][i] != token) {
583
- matching = false;
584
- break;
585
- }
586
- }
587
- }
588
- if (!matching) {
589
- break;
590
- }
591
- if (token == ',') {
592
- match.push([]);
593
- }
594
- else {
595
- if (token.endsWith('(')) {
596
- matchFunction++;
597
- }
598
- if (token.endsWith('[')) {
599
- inAttr++;
600
- }
601
- else if (token == ')') {
602
- matchFunction--;
603
- }
604
- else if (token == ']') {
605
- inAttr--;
606
- }
607
- match.at(-1).push(token);
608
- }
609
- }
610
- // invalid function
611
- if (matchFunction != 0 || inAttr != 0) {
612
- return null;
613
- }
614
- if (parentType != 'Rule') {
615
- for (const part of match) {
616
- if (part.length > 0 && combinators.includes(part[0].charAt(0))) {
617
- return null;
618
- }
619
- }
620
- }
621
- if (match.length > 1) {
622
- console.error(`unsupported multilevel matching`);
623
- console.error({ match, selector1, selector2 });
624
- return null;
625
- }
626
- for (const part of match) {
627
- while (part.length > 0) {
628
- const token = part.at(-1);
629
- if (token == ' ' || combinators.includes(token) || notEndingWith.includes(token.at(-1))) {
630
- part.pop();
631
- continue;
632
- }
633
- break;
634
- }
635
- }
636
- if (match.every(t => t.length == 0)) {
637
- return null;
638
- }
639
- if (eq([['&']], match)) {
640
- return null;
641
- }
642
- function reduce(acc, curr) {
643
- if (acc === null) {
644
- return null;
645
- }
646
- let hasCompoundSelector = true;
647
- curr = curr.slice(match[0].length);
648
- while (curr.length > 0) {
649
- if (curr[0] == ' ') {
650
- hasCompoundSelector = false;
651
- curr.unshift('&');
652
- continue;
653
- }
654
- break;
655
- }
656
- // invalid function match
657
- if (curr.length > 0 && curr[0].endsWith('(') && curr.at(-1) != ')') {
658
- return null;
659
- }
660
- if (curr.length == 1 && combinators.includes(curr[0].charAt(0))) {
661
- return null;
662
- }
663
- if (hasCompoundSelector && curr.length > 0) {
664
- hasCompoundSelector = !['&'].concat(combinators).includes(curr[0].charAt(0));
665
- }
666
- if (curr[0] == ':is(') {
667
- let inFunction = 0;
668
- let canReduce = true;
669
- const isCompound = curr.reduce((acc, token, index) => {
670
- if (index == 0) {
671
- inFunction++;
672
- canReduce = curr[1] == '&';
673
- }
674
- else if (token.endsWith('(')) {
675
- if (inFunction == 0) {
676
- canReduce = false;
677
- }
678
- inFunction++;
679
- }
680
- else if (token == ')') {
681
- inFunction--;
682
- }
683
- else if (token == ',') {
684
- if (!canReduce) {
685
- canReduce = curr[index + 1] == '&';
686
- }
687
- acc.push([]);
688
- }
689
- else
690
- acc.at(-1)?.push(token);
691
- return acc;
692
- }, [[]]);
693
- if (inFunction > 0) {
694
- canReduce = false;
695
- }
696
- if (canReduce) {
697
- curr = isCompound.reduce((acc, curr) => {
698
- if (acc.length > 0) {
699
- acc.push(',');
700
- }
701
- acc.push(...curr);
702
- return acc;
703
- }, []);
704
- }
705
- }
706
- // @todo: check hasCompoundSelector && curr[0] == '&' && curr[1] == ' '
707
- acc.push(match.length == 0 ? ['&'] : (hasCompoundSelector && curr[0] != '&' && (curr.length == 0 || !combinators.includes(curr[0].charAt(0))) ? ['&'].concat(curr) : curr));
708
- return acc;
709
- }
710
- // @ts-ignore
711
- selector1 = selector1.reduce(reduce, []);
712
- // @ts-ignore
713
- selector2 = selector2.reduce(reduce, []);
714
- return selector1 == null || selector2 == null ? null : {
715
- eq: eq(selector1, selector2),
716
- match,
717
- selector1,
718
- selector2
719
- };
720
- }
721
- function reduceSelector(selector) {
722
- if (selector.length == 0) {
723
- return null;
724
- }
725
- const optimized = [];
726
- const k = selector.reduce((acc, curr) => acc == 0 ? curr.length : (curr.length == 0 ? acc : Math.min(acc, curr.length)), 0);
727
- let i = 0;
728
- let j;
729
- let match;
730
- for (; i < k; i++) {
731
- const item = selector[0][i];
732
- match = true;
733
- for (j = 1; j < selector.length; j++) {
734
- if (item != selector[j][i]) {
735
- match = false;
736
- break;
737
- }
738
- }
739
- if (!match) {
740
- break;
741
- }
742
- optimized.push(item);
743
- }
744
- while (optimized.length > 0) {
745
- const last = optimized.at(-1);
746
- if ((last == ' ' || combinators.includes(last))) {
747
- optimized.pop();
748
- continue;
749
- }
750
- break;
751
- }
752
- selector.forEach((selector) => selector.splice(0, optimized.length));
753
- // combinator
754
- if (combinators.includes(optimized.at(-1))) {
755
- const combinator = optimized.pop();
756
- selector.forEach(selector => selector.unshift(combinator));
757
- }
758
- let reducible = optimized.length == 1;
759
- if (optimized[0] == '&' && optimized[1] == ' ') {
760
- optimized.splice(0, 2);
761
- }
762
- if (optimized.length == 0 ||
763
- (optimized[0].charAt(0) == '&' ||
764
- selector.length == 1)) {
765
- return {
766
- match: false,
767
- optimized,
768
- selector: selector.map(selector => selector[0] == '&' && selector[1] == ' ' ? selector.slice(2) : selector),
769
- reducible: selector.length > 1 && selector.every((selector) => !combinators.includes(selector[0]))
770
- };
771
- }
772
- return {
773
- match: true,
774
- optimized,
775
- selector: selector.reduce((acc, curr) => {
776
- let hasCompound = true;
777
- if (hasCompound && curr.length > 0) {
778
- hasCompound = !['&'].concat(combinators).includes(curr[0].charAt(0));
779
- }
780
- // @ts-ignore
781
- if (hasCompound && curr[0] == ' ') {
782
- hasCompound = false;
783
- curr.unshift('&');
784
- }
785
- if (curr.length == 0) {
786
- curr.push('&');
787
- hasCompound = false;
788
- }
789
- if (reducible) {
790
- const chr = curr[0].charAt(0);
791
- // @ts-ignore
792
- reducible = chr == '.' || chr == ':' || isIdentStart(chr.codePointAt(0));
793
- }
794
- acc.push(hasCompound ? ['&'].concat(curr) : curr);
795
- return acc;
796
- }, []),
797
- reducible: selector.every((selector) => !['>', '+', '~', '&'].includes(selector[0]))
798
- };
799
- }
800
- function reducer(acc, curr, index, array) {
801
- // trim :is()
802
- if (array.length == 1 && array[0][0] == ':is(' && array[0].at(-1) == ')') {
803
- curr = curr.slice(1, -1);
804
- }
805
- if (curr[0] == '&') {
806
- if (curr[1] == ' ') {
807
- curr.splice(0, 2);
808
- }
809
- else if (combinators.includes(curr[1])) {
810
- curr.splice(0, 1);
811
- }
812
- }
813
- acc.push(curr.join(''));
814
- return acc;
815
- }
816
811
 
817
- export { deduplicate, deduplicateRule, hasDeclaration, reduceSelector };
812
+ export { combinators, hasDeclaration, minify, minifyRule, reduceSelector };