@tbela99/css-parser 0.0.1-rc7 → 0.1.0

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,58 +1,33 @@
1
- import { isIdentStart, isWhiteSpace, isIdent, isFunction } from '../parser/utils/syntax.js';
2
- import { PropertyList } from '../parser/declaration/list.js';
3
- import { eq } from '../parser/utils/eq.js';
4
- import { render, renderToken } from '../renderer/render.js';
5
1
  import { parseString } from '../parser/parse.js';
6
- import { replaceCompound } from './expand.js';
2
+ import { isIdentStart, isWhiteSpace, isIdent, isFunction } from '../parser/utils/syntax.js';
3
+ import { EnumToken } from './types.js';
7
4
  import { walkValues } from './walk.js';
5
+ import { replaceCompound } from './expand.js';
6
+ import { eq } from '../parser/utils/eq.js';
7
+ import { renderToken, doRender } from '../renderer/render.js';
8
+ import * as index from './features/index.js';
8
9
 
9
10
  const combinators = ['+', '>', '~'];
10
11
  const notEndingWith = ['(', '['].concat(combinators);
11
12
  const definedPropertySettings = { configurable: true, enumerable: false, writable: true };
12
- function minify(ast, options = {}, recursive = false, errors, nestingContent) {
13
- function wrapNodes(previous, node, match, ast, i, nodeIndex) {
14
- // @ts-ignore
15
- let pSel = match.selector1.reduce(reducer, []).join(',');
16
- // @ts-ignore
17
- let nSel = match.selector2.reduce(reducer, []).join(',');
13
+ // @ts-ignore
14
+ const features = Object.values(index).sort((a, b) => a.ordering - b.ordering);
15
+ function minify(ast, options = {}, recursive = false, errors, nestingContent, context = {}) {
16
+ if (!('nodes' in context)) {
17
+ context.nodes = new WeakSet;
18
+ }
19
+ if (context.nodes.has(ast)) {
20
+ // console.error('skipped', ast.typ);
21
+ return ast;
22
+ }
23
+ context.nodes.add(ast);
24
+ if (!('features' in options)) {
18
25
  // @ts-ignore
19
- const wrapper = { ...previous, chi: [], sel: match.match.reduce(reducer, []).join(',') };
26
+ options = { removeDuplicateDeclarations: true, computeShorthand: true, computeCalcExpression: true, features: [], ...options };
20
27
  // @ts-ignore
21
- Object.defineProperty(wrapper, 'raw', { ...definedPropertySettings,
22
- // @ts-ignore
23
- value: match.match.map(t => t.slice())
24
- });
25
- if (pSel == '&' || pSel === '') {
26
- // @ts-ignore
27
- wrapper.chi.push(...previous.chi);
28
- // @ts-ignore
29
- if ((nSel == '&' || nSel === '')) {
30
- // @ts-ignore
31
- wrapper.chi.push(...node.chi);
32
- }
33
- else {
34
- // @ts-ignore
35
- wrapper.chi.push(node);
36
- }
28
+ for (const feature of features) {
29
+ feature.register(options);
37
30
  }
38
- else {
39
- // @ts-ignore
40
- wrapper.chi.push(previous, node);
41
- }
42
- // @ts-ignore
43
- ast.chi.splice(i, 1, wrapper);
44
- // @ts-ignore
45
- ast.chi.splice(nodeIndex, 1);
46
- // @ts-ignore
47
- previous.sel = pSel;
48
- // @ts-ignore
49
- previous.raw = match.selector1;
50
- // @ts-ignore
51
- node.sel = nSel;
52
- // @ts-ignore
53
- node.raw = match.selector2;
54
- reduceRuleSelector(wrapper);
55
- return wrapper;
56
31
  }
57
32
  function reducer(acc, curr, index, array) {
58
33
  // trim :is()
@@ -67,258 +42,16 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent) {
67
42
  curr.splice(0, 1);
68
43
  }
69
44
  }
70
- else if (ast.typ == 'Rule' && (isIdent(curr[0]) || isFunction(curr[0]))) {
45
+ else if (ast.typ == 4 /* NodeType.RuleNodeType */ && (isIdent(curr[0]) || isFunction(curr[0]))) {
71
46
  curr.unshift('&', ' ');
72
47
  }
73
48
  acc.push(curr.join(''));
74
49
  return acc;
75
50
  }
76
- function diff(n1, n2, options = {}) {
77
- let node1 = n1;
78
- let node2 = n2;
79
- let exchanged = false;
80
- if (node1.chi.length > node2.chi.length) {
81
- const t = node1;
82
- node1 = node2;
83
- node2 = t;
84
- exchanged = true;
85
- }
86
- let i = node1.chi.length;
87
- let j = node2.chi.length;
88
- if (i == 0 || j == 0) {
89
- // @ts-ignore
90
- return null;
91
- }
92
- // @ts-ignore
93
- const raw1 = node1.raw;
94
- // @ts-ignore
95
- const raw2 = node2.raw;
96
- // @ts-ignore
97
- node1 = { ...node1, chi: node1.chi.slice() };
98
- node2 = { ...node2, chi: node2.chi.slice() };
99
- if (raw1 != null) {
100
- Object.defineProperty(node1, 'raw', { ...definedPropertySettings, value: raw1 });
101
- }
102
- if (raw2 != null) {
103
- Object.defineProperty(node2, 'raw', { ...definedPropertySettings, value: raw2 });
104
- }
105
- const intersect = [];
106
- while (i--) {
107
- if (node1.chi[i].typ == 'Comment') {
108
- continue;
109
- }
110
- j = node2.chi.length;
111
- if (j == 0) {
112
- break;
113
- }
114
- while (j--) {
115
- if (node2.chi[j].typ == 'Comment') {
116
- continue;
117
- }
118
- if (node1.chi[i].nam == node2.chi[j].nam) {
119
- if (eq(node1.chi[i], node2.chi[j])) {
120
- intersect.push(node1.chi[i]);
121
- node1.chi.splice(i, 1);
122
- node2.chi.splice(j, 1);
123
- break;
124
- }
125
- }
126
- }
127
- }
128
- // @ts-ignore
129
- const result = (intersect.length == 0 ? null : {
130
- ...node1,
131
- // @ts-ignore
132
- sel: [...new Set([...(n1?.raw?.reduce(reducer, []) || splitRule(n1.sel)).concat(n2?.raw?.reduce(reducer, []) || splitRule(n2.sel))])].join(','),
133
- chi: intersect.reverse()
134
- });
135
- 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)) {
136
- // @ts-ignore
137
- return null;
138
- }
139
- return { result, node1: exchanged ? node2 : node1, node2: exchanged ? node1 : node2 };
140
- }
141
- function matchSelectors(selector1, selector2, parentType) {
142
- let match = [[]];
143
- 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));
144
- let i = 0;
145
- let k;
146
- let l;
147
- let token;
148
- let matching = true;
149
- let matchFunction = 0;
150
- let inAttr = 0;
151
- for (; i < j; i++) {
152
- k = 0;
153
- token = selector1[0][i];
154
- for (; k < selector1.length; k++) {
155
- if (selector1[k][i] != token) {
156
- matching = false;
157
- break;
158
- }
159
- }
160
- if (matching) {
161
- l = 0;
162
- for (; l < selector2.length; l++) {
163
- if (selector2[l][i] != token) {
164
- matching = false;
165
- break;
166
- }
167
- }
168
- }
169
- if (!matching) {
170
- break;
171
- }
172
- if (token == ',') {
173
- match.push([]);
174
- }
175
- else {
176
- if (token.endsWith('(')) {
177
- matchFunction++;
178
- }
179
- if (token.endsWith('[')) {
180
- inAttr++;
181
- }
182
- else if (token == ')') {
183
- matchFunction--;
184
- }
185
- else if (token == ']') {
186
- inAttr--;
187
- }
188
- match.at(-1).push(token);
189
- }
190
- }
191
- // invalid function
192
- if (matchFunction != 0 || inAttr != 0) {
193
- return null;
194
- }
195
- if (parentType != 'Rule') {
196
- for (const part of match) {
197
- if (part.length > 0 && combinators.includes(part[0].charAt(0))) {
198
- return null;
199
- }
200
- }
201
- }
202
- if (match.length > 1) {
203
- errors?.push({ action: 'ignore', message: `minify: unsupported multilevel matching\n${JSON.stringify({ match, selector1, selector2 }, null, 1)}` });
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
- };
300
- }
301
- function fixSelector(node) {
302
- // @ts-ignore
303
- if (node.sel.includes('&')) {
304
- const attributes = parseString(node.sel);
305
- for (const attr of walkValues(attributes)) {
306
- if (attr.value.typ == 'Pseudo-class-func' && attr.value.val == ':is') {
307
- let i = attr.value.chi.length;
308
- while (i--) {
309
- if (attr.value.chi[i].typ == 'Literal' && attr.value.chi[i].val == '&') {
310
- attr.value.chi.splice(i, 1);
311
- }
312
- }
313
- }
314
- }
315
- node.sel = attributes.reduce((acc, curr) => acc + renderToken(curr), '');
316
- }
317
- }
318
51
  // @ts-ignore
319
- if (('chi' in ast) && ast.chi?.length > 0) {
52
+ if ('chi' in ast && ast.chi.length > 0) {
320
53
  if (!nestingContent) {
321
- nestingContent = options.nestingRules && ast.typ == 'Rule';
54
+ nestingContent = options.nestingRules && ast.typ == 4 /* NodeType.RuleNodeType */;
322
55
  }
323
56
  let i = 0;
324
57
  let previous;
@@ -327,7 +60,7 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent) {
327
60
  // @ts-ignore
328
61
  for (; i < ast.chi.length; i++) {
329
62
  // @ts-ignore
330
- if (ast.chi[i].typ == 'Comment') {
63
+ if (ast.chi[i].typ == 0 /* NodeType.CommentNodeType */) {
331
64
  continue;
332
65
  }
333
66
  // @ts-ignore
@@ -339,10 +72,10 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent) {
339
72
  i--;
340
73
  continue;
341
74
  }
342
- if (node.typ == 'AtRule' && node.nam == 'font-face') {
75
+ if (node.typ == 3 /* NodeType.AtRuleNodeType */ && node.nam == 'font-face') {
343
76
  continue;
344
77
  }
345
- if (node.typ == 'AtRule') {
78
+ if (node.typ == 3 /* NodeType.AtRuleNodeType */) {
346
79
  if (node.nam == 'media' && node.val == 'all') {
347
80
  // @ts-ignore
348
81
  ast.chi?.splice(i, 1, ...node.chi);
@@ -350,47 +83,41 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent) {
350
83
  continue;
351
84
  }
352
85
  // @ts-ignore
353
- if (previous?.typ == 'AtRule' &&
86
+ if (previous?.typ == 3 /* NodeType.AtRuleNodeType */ &&
354
87
  previous.nam == node.nam &&
355
88
  previous.val == node.val) {
356
89
  if ('chi' in node) {
357
90
  // @ts-ignore
358
91
  previous.chi.push(...node.chi);
359
92
  }
360
- // else {
361
93
  ast?.chi?.splice(i--, 1);
362
94
  continue;
363
- // }
364
95
  }
365
96
  // @ts-ignore
366
- if (hasDeclaration(node)) {
367
- // @ts-ignore
368
- minifyRule(node);
369
- }
370
- else {
371
- minify(node, options, recursive, errors, nestingContent);
97
+ if (!hasDeclaration(node)) {
98
+ minify(node, options, recursive, errors, nestingContent, context);
372
99
  }
373
100
  previous = node;
374
101
  nodeIndex = i;
375
102
  continue;
376
103
  }
377
104
  // @ts-ignore
378
- if (node.typ == 'Rule') {
105
+ if (node.typ == 4 /* NodeType.RuleNodeType */) {
379
106
  reduceRuleSelector(node);
380
107
  let wrapper;
381
108
  let match;
382
109
  // @ts-ignore
383
110
  if (options.nestingRules) {
384
111
  // @ts-ignore
385
- if (previous?.typ == 'Rule') {
112
+ if (previous?.typ == 4 /* NodeType.RuleNodeType */) {
386
113
  // @ts-ignore
387
114
  reduceRuleSelector(previous);
388
115
  // @ts-ignore
389
- match = matchSelectors(previous.raw, node.raw, ast.typ);
116
+ match = matchSelectors(previous.raw, node.raw, ast.typ, errors);
390
117
  // @ts-ignore
391
118
  if (match != null) {
392
119
  // @ts-ignore
393
- wrapper = wrapNodes(previous, node, match, ast, i, nodeIndex);
120
+ wrapper = wrapNodes(previous, node, match, ast, reducer, i, nodeIndex);
394
121
  nodeIndex = i - 1;
395
122
  // @ts-ignore
396
123
  previous = ast.chi[nodeIndex];
@@ -403,26 +130,23 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent) {
403
130
  // @ts-ignore
404
131
  const nextNode = ast.chi[i];
405
132
  // @ts-ignore
406
- if (nextNode.typ != 'Rule') {
407
- // i--;
408
- // previous = wrapper;
409
- // nodeIndex = i;
133
+ if (nextNode.typ != 4 /* NodeType.RuleNodeType */) {
410
134
  break;
411
135
  }
412
136
  reduceRuleSelector(nextNode);
413
137
  // @ts-ignore
414
- match = matchSelectors(wrapper.raw, nextNode.raw, ast.typ);
138
+ match = matchSelectors(wrapper.raw, nextNode.raw, ast.typ, errors);
415
139
  // @ts-ignore
416
140
  if (match == null) {
417
141
  break;
418
142
  }
419
143
  // @ts-ignore
420
- wrapper = wrapNodes(wrapper, nextNode, match, ast, i, nodeIndex);
144
+ wrapper = wrapNodes(wrapper, nextNode, match, ast, reducer, i, nodeIndex);
421
145
  }
422
146
  nodeIndex = --i;
423
147
  // @ts-ignore
424
148
  previous = ast.chi[nodeIndex];
425
- minify(wrapper, options, recursive, errors, nestingContent);
149
+ minify(wrapper, options, recursive, errors, nestingContent, context);
426
150
  continue;
427
151
  }
428
152
  // @ts-ignore
@@ -460,7 +184,7 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent) {
460
184
  curr.splice(0, 2);
461
185
  }
462
186
  else {
463
- if (ast.typ != 'Rule' && combinators.includes(curr[1])) {
187
+ if (ast.typ != 4 /* NodeType.RuleNodeType */ && combinators.includes(curr[1])) {
464
188
  wrap = false;
465
189
  }
466
190
  else {
@@ -477,7 +201,7 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent) {
477
201
  return acc;
478
202
  }, []);
479
203
  if (!wrap) {
480
- wrap = selector.some(s => s[0] != '&');
204
+ wrap = selector.some((s) => s[0] != '&');
481
205
  }
482
206
  let rule = selector.map(s => {
483
207
  if (s[0] == '&') {
@@ -508,37 +232,36 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent) {
508
232
  let k = previous.chi.length;
509
233
  while (k-- > 0) {
510
234
  // @ts-ignore
511
- if (previous.chi[k].typ == 'Comment') {
235
+ if (previous.chi[k].typ == 0 /* NodeType.CommentNodeType */) {
512
236
  continue;
513
237
  }
514
238
  // @ts-ignore
515
- shouldMerge = previous.chi[k].typ == 'Declaration';
239
+ shouldMerge = previous.chi[k].typ == 5 /* NodeType.DeclarationNodeType */;
516
240
  break;
517
241
  }
518
242
  if (shouldMerge) {
519
243
  // @ts-ignore
520
- if ((node.typ == 'Rule' && node.sel == previous.sel) ||
244
+ if ((node.typ == 4 /* NodeType.RuleNodeType */ && node.sel == previous.sel) ||
521
245
  // @ts-ignore
522
- (node.typ == 'AtRule') && node.val != 'font-face' && node.val == previous.val) {
246
+ (node.typ == 3 /* NodeType.AtRuleNodeType */) && node.val != 'font-face' && node.val == previous.val) {
523
247
  // @ts-ignore
524
248
  node.chi.unshift(...previous.chi);
525
249
  // @ts-ignore
526
250
  ast.chi.splice(nodeIndex, 1);
527
251
  // @ts-ignore
528
- if (hasDeclaration(node)) {
252
+ if (!hasDeclaration(node)) {
529
253
  // @ts-ignore
530
- minifyRule(node);
531
- }
532
- else {
533
- minify(node, options, recursive, errors, nestingContent);
254
+ // minifyRule(node, <MinifyOptions>options, ast, context);
255
+ // } else {
256
+ minify(node, options, recursive, errors, nestingContent, context);
534
257
  }
535
258
  i--;
536
259
  previous = node;
537
260
  nodeIndex = i;
538
261
  continue;
539
262
  }
540
- else if (node.typ == 'Rule' && previous?.typ == 'Rule') {
541
- const intersect = diff(previous, node, options);
263
+ else if (node.typ == 4 /* NodeType.RuleNodeType */ && previous?.typ == 4 /* NodeType.RuleNodeType */) {
264
+ const intersect = diff(previous, node, reducer, options);
542
265
  if (intersect != null) {
543
266
  if (intersect.node1.chi.length == 0) {
544
267
  // @ts-ignore
@@ -570,24 +293,22 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent) {
570
293
  // @ts-ignore
571
294
  if (recursive && previous != node) {
572
295
  // @ts-ignore
573
- if (hasDeclaration(previous)) {
296
+ if (!hasDeclaration(previous)) {
574
297
  // @ts-ignore
575
- minifyRule(previous);
576
- }
577
- else {
578
- minify(previous, options, recursive, errors, nestingContent);
298
+ // minifyRule(previous, <MinifyOptions>options, ast, context);
299
+ // } else {
300
+ minify(previous, options, recursive, errors, nestingContent, context);
579
301
  }
580
302
  }
581
303
  }
582
304
  else {
583
305
  if ('chi' in previous) {
584
306
  // @ts-ignore
585
- if (hasDeclaration(previous)) {
307
+ if (!hasDeclaration(previous)) {
586
308
  // @ts-ignore
587
- minifyRule(previous);
588
- }
589
- else {
590
- minify(previous, options, recursive, errors, nestingContent);
309
+ // minifyRule(previous, <MinifyOptions>options, ast, context);
310
+ // } else {
311
+ minify(previous, options, recursive, errors, nestingContent, context);
591
312
  }
592
313
  }
593
314
  }
@@ -596,7 +317,7 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent) {
596
317
  // @ts-ignore
597
318
  previous != null &&
598
319
  // previous.optimized != null &&
599
- previous.typ == 'Rule' &&
320
+ previous.typ == 4 /* NodeType.RuleNodeType */ &&
600
321
  previous.sel.includes('&')) {
601
322
  fixSelector(previous);
602
323
  }
@@ -606,13 +327,10 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent) {
606
327
  // @ts-ignore
607
328
  if (recursive && node != null && ('chi' in node)) {
608
329
  // @ts-ignore
609
- if (node.chi.some(n => n.typ == 'Declaration')) {
610
- minifyRule(node);
611
- }
612
- else {
330
+ if (!node.chi.some(n => n.typ == 5 /* NodeType.DeclarationNodeType */)) {
613
331
  // @ts-ignore
614
- if (!(node.typ == 'AtRule' && node.nam != 'font-face')) {
615
- minify(node, options, recursive, errors, nestingContent);
332
+ if (!(node.typ == 3 /* NodeType.AtRuleNodeType */ && node.nam != 'font-face')) {
333
+ minify(node, options, recursive, errors, nestingContent, context);
616
334
  }
617
335
  }
618
336
  }
@@ -620,11 +338,46 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent) {
620
338
  // @ts-ignore
621
339
  node != null &&
622
340
  // previous.optimized != null &&
623
- node.typ == 'Rule' &&
341
+ node.typ == 4 /* NodeType.RuleNodeType */ &&
624
342
  node.sel.includes('&')) {
625
343
  fixSelector(node);
626
344
  }
627
345
  }
346
+ if (ast.typ == 2 /* NodeType.StyleSheetNodeType */) {
347
+ let parent;
348
+ let parents = [ast];
349
+ while (parents.length > 0) {
350
+ parent = parents.shift();
351
+ // @ts-ignore
352
+ for (let k = 0; k < parent.chi.length; k++) {
353
+ // @ts-ignore
354
+ const node = parent.chi[k];
355
+ if (!('chi' in node) || node.typ == 2 /* NodeType.StyleSheetNodeType */ || (node.typ == 3 /* NodeType.AtRuleNodeType */ && node.nam == 'font-face')) {
356
+ continue;
357
+ }
358
+ // @ts-ignore
359
+ if (node.chi.length > 0) {
360
+ parents.push(node);
361
+ Object.defineProperty(node, 'parent', { ...definedPropertySettings, value: parent });
362
+ for (const feature of options.features) {
363
+ feature.run(node, options, parent, context);
364
+ }
365
+ }
366
+ // @ts-ignore
367
+ if (options.removeEmpty && node.chi.length == 0) {
368
+ // @ts-ignore
369
+ parent.chi.splice(k, 1);
370
+ k--;
371
+ }
372
+ }
373
+ }
374
+ for (const feature of options.features) {
375
+ if ('cleanup' in feature) {
376
+ // @ts-ignore
377
+ feature.cleanup(ast, options, context);
378
+ }
379
+ }
380
+ }
628
381
  return ast;
629
382
  }
630
383
  function reduceSelector(selector) {
@@ -710,37 +463,14 @@ function hasDeclaration(node) {
710
463
  // @ts-ignore
711
464
  for (let i = 0; i < node.chi?.length; i++) {
712
465
  // @ts-ignore
713
- if (node.chi[i].typ == 'Comment') {
466
+ if (node.chi[i].typ == 0 /* NodeType.CommentNodeType */) {
714
467
  continue;
715
468
  }
716
469
  // @ts-ignore
717
- return node.chi[i].typ == 'Declaration';
470
+ return node.chi[i].typ == 5 /* NodeType.DeclarationNodeType */;
718
471
  }
719
472
  return true;
720
473
  }
721
- function minifyRule(ast) {
722
- // @ts-ignore
723
- if (!('chi' in ast) || ast.chi?.length <= 1) {
724
- return ast;
725
- }
726
- // @ts-ignore
727
- const j = ast.chi.length;
728
- let k = 0;
729
- let properties = new PropertyList();
730
- // @ts-ignore
731
- for (; k < j; k++) {
732
- // @ts-ignore
733
- const node = ast.chi[k];
734
- if (node.typ == 'Comment' || node.typ == 'Declaration') {
735
- properties.add(node);
736
- continue;
737
- }
738
- break;
739
- }
740
- // @ts-ignore
741
- ast.chi = [...properties].concat(ast.chi.slice(k));
742
- return ast;
743
- }
744
474
  function splitRule(buffer) {
745
475
  const result = [[]];
746
476
  let str = '';
@@ -829,6 +559,300 @@ function splitRule(buffer) {
829
559
  }
830
560
  return result;
831
561
  }
562
+ function matchSelectors(selector1, selector2, parentType, errors) {
563
+ let match = [[]];
564
+ 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));
565
+ let i = 0;
566
+ let k;
567
+ let l;
568
+ let token;
569
+ let matching = true;
570
+ let matchFunction = 0;
571
+ let inAttr = 0;
572
+ for (; i < j; i++) {
573
+ k = 0;
574
+ token = selector1[0][i];
575
+ for (; k < selector1.length; k++) {
576
+ if (selector1[k][i] != token) {
577
+ matching = false;
578
+ break;
579
+ }
580
+ }
581
+ if (matching) {
582
+ l = 0;
583
+ for (; l < selector2.length; l++) {
584
+ if (selector2[l][i] != token) {
585
+ matching = false;
586
+ break;
587
+ }
588
+ }
589
+ }
590
+ if (!matching) {
591
+ break;
592
+ }
593
+ if (token == ',') {
594
+ match.push([]);
595
+ }
596
+ else {
597
+ if (token.endsWith('(')) {
598
+ matchFunction++;
599
+ }
600
+ if (token.endsWith('[')) {
601
+ inAttr++;
602
+ }
603
+ else if (token == ')') {
604
+ matchFunction--;
605
+ }
606
+ else if (token == ']') {
607
+ inAttr--;
608
+ }
609
+ match.at(-1).push(token);
610
+ }
611
+ }
612
+ // invalid function
613
+ if (matchFunction != 0 || inAttr != 0) {
614
+ return null;
615
+ }
616
+ if (parentType != 4 /* NodeType.RuleNodeType */) {
617
+ for (const part of match) {
618
+ if (part.length > 0 && combinators.includes(part[0].charAt(0))) {
619
+ return null;
620
+ }
621
+ }
622
+ }
623
+ if (match.length > 1) {
624
+ errors?.push({
625
+ action: 'ignore',
626
+ message: `minify: unsupported multilevel matching\n${JSON.stringify({
627
+ match,
628
+ selector1,
629
+ selector2
630
+ }, null, 1)}`
631
+ });
632
+ return null;
633
+ }
634
+ for (const part of match) {
635
+ while (part.length > 0) {
636
+ const token = part.at(-1);
637
+ if (token == ' ' || combinators.includes(token) || notEndingWith.includes(token.at(-1))) {
638
+ part.pop();
639
+ continue;
640
+ }
641
+ break;
642
+ }
643
+ }
644
+ if (match.every(t => t.length == 0)) {
645
+ return null;
646
+ }
647
+ if (eq([['&']], match)) {
648
+ return null;
649
+ }
650
+ function reduce(acc, curr) {
651
+ if (acc === null) {
652
+ return null;
653
+ }
654
+ let hasCompoundSelector = true;
655
+ curr = curr.slice(match[0].length);
656
+ while (curr.length > 0) {
657
+ if (curr[0] == ' ') {
658
+ hasCompoundSelector = false;
659
+ curr.unshift('&');
660
+ continue;
661
+ }
662
+ break;
663
+ }
664
+ // invalid function match
665
+ if (curr.length > 0 && curr[0].endsWith('(') && curr.at(-1) != ')') {
666
+ return null;
667
+ }
668
+ if (curr.length == 1 && combinators.includes(curr[0].charAt(0))) {
669
+ return null;
670
+ }
671
+ if (hasCompoundSelector && curr.length > 0) {
672
+ hasCompoundSelector = !['&'].concat(combinators).includes(curr[0].charAt(0));
673
+ }
674
+ if (curr[0] == ':is(') {
675
+ let inFunction = 0;
676
+ let canReduce = true;
677
+ const isCompound = curr.reduce((acc, token, index) => {
678
+ if (index == 0) {
679
+ inFunction++;
680
+ canReduce = curr[1] == '&';
681
+ }
682
+ else if (token.endsWith('(')) {
683
+ if (inFunction == 0) {
684
+ canReduce = false;
685
+ }
686
+ inFunction++;
687
+ }
688
+ else if (token == ')') {
689
+ inFunction--;
690
+ }
691
+ else if (token == ',') {
692
+ if (!canReduce) {
693
+ canReduce = curr[index + 1] == '&';
694
+ }
695
+ acc.push([]);
696
+ }
697
+ else
698
+ acc.at(-1)?.push(token);
699
+ return acc;
700
+ }, [[]]);
701
+ if (inFunction > 0) {
702
+ canReduce = false;
703
+ }
704
+ if (canReduce) {
705
+ curr = isCompound.reduce((acc, curr) => {
706
+ if (acc.length > 0) {
707
+ acc.push(',');
708
+ }
709
+ acc.push(...curr);
710
+ return acc;
711
+ }, []);
712
+ }
713
+ }
714
+ // @todo: check hasCompoundSelector && curr[0] == '&' && curr[1] == ' '
715
+ acc.push(match.length == 0 ? ['&'] : (hasCompoundSelector && curr[0] != '&' && (curr.length == 0 || !combinators.includes(curr[0].charAt(0))) ? ['&'].concat(curr) : curr));
716
+ return acc;
717
+ }
718
+ // @ts-ignore
719
+ selector1 = selector1.reduce(reduce, []);
720
+ // @ts-ignore
721
+ selector2 = selector2.reduce(reduce, []);
722
+ return selector1 == null || selector2 == null ? null : {
723
+ eq: eq(selector1, selector2),
724
+ match,
725
+ selector1,
726
+ selector2
727
+ };
728
+ }
729
+ function fixSelector(node) {
730
+ // @ts-ignore
731
+ if (node.sel.includes('&')) {
732
+ const attributes = parseString(node.sel);
733
+ for (const attr of walkValues(attributes)) {
734
+ if (attr.value.typ == EnumToken.PseudoClassFuncTokenType && attr.value.val == ':is') {
735
+ let i = attr.value.chi.length;
736
+ while (i--) {
737
+ if (attr.value.chi[i].typ == EnumToken.LiteralTokenType && attr.value.chi[i].val == '&') {
738
+ attr.value.chi.splice(i, 1);
739
+ }
740
+ }
741
+ }
742
+ }
743
+ node.sel = attributes.reduce((acc, curr) => acc + renderToken(curr), '');
744
+ }
745
+ }
746
+ function wrapNodes(previous, node, match, ast, reducer, i, nodeIndex) {
747
+ // @ts-ignore
748
+ let pSel = match.selector1.reduce(reducer, []).join(',');
749
+ // @ts-ignore
750
+ let nSel = match.selector2.reduce(reducer, []).join(',');
751
+ // @ts-ignore
752
+ const wrapper = { ...previous, chi: [], sel: match.match.reduce(reducer, []).join(',') };
753
+ // @ts-ignore
754
+ Object.defineProperty(wrapper, 'raw', {
755
+ ...definedPropertySettings,
756
+ // @ts-ignore
757
+ value: match.match.map(t => t.slice())
758
+ });
759
+ if (pSel == '&' || pSel === '') {
760
+ // @ts-ignore
761
+ wrapper.chi.push(...previous.chi);
762
+ // @ts-ignore
763
+ if ((nSel == '&' || nSel === '')) {
764
+ // @ts-ignore
765
+ wrapper.chi.push(...node.chi);
766
+ }
767
+ else {
768
+ // @ts-ignore
769
+ wrapper.chi.push(node);
770
+ }
771
+ }
772
+ else {
773
+ // @ts-ignore
774
+ wrapper.chi.push(previous, node);
775
+ }
776
+ // @ts-ignore
777
+ ast.chi.splice(i, 1, wrapper);
778
+ // @ts-ignore
779
+ ast.chi.splice(nodeIndex, 1);
780
+ // @ts-ignore
781
+ previous.sel = pSel;
782
+ // @ts-ignore
783
+ previous.raw = match.selector1;
784
+ // @ts-ignore
785
+ node.sel = nSel;
786
+ // @ts-ignore
787
+ node.raw = match.selector2;
788
+ reduceRuleSelector(wrapper);
789
+ return wrapper;
790
+ }
791
+ function diff(n1, n2, reducer, options = {}) {
792
+ let node1 = n1;
793
+ let node2 = n2;
794
+ let exchanged = false;
795
+ if (node1.chi.length > node2.chi.length) {
796
+ const t = node1;
797
+ node1 = node2;
798
+ node2 = t;
799
+ exchanged = true;
800
+ }
801
+ let i = node1.chi.length;
802
+ let j = node2.chi.length;
803
+ if (i == 0 || j == 0) {
804
+ // @ts-ignore
805
+ return null;
806
+ }
807
+ // @ts-ignore
808
+ const raw1 = node1.raw;
809
+ // @ts-ignore
810
+ const raw2 = node2.raw;
811
+ // @ts-ignore
812
+ node1 = { ...node1, chi: node1.chi.slice() };
813
+ node2 = { ...node2, chi: node2.chi.slice() };
814
+ if (raw1 != null) {
815
+ Object.defineProperty(node1, 'raw', { ...definedPropertySettings, value: raw1 });
816
+ }
817
+ if (raw2 != null) {
818
+ Object.defineProperty(node2, 'raw', { ...definedPropertySettings, value: raw2 });
819
+ }
820
+ const intersect = [];
821
+ while (i--) {
822
+ if (node1.chi[i].typ == 0 /* NodeType.CommentNodeType */) {
823
+ continue;
824
+ }
825
+ j = node2.chi.length;
826
+ if (j == 0) {
827
+ break;
828
+ }
829
+ while (j--) {
830
+ if (node2.chi[j].typ == 0 /* NodeType.CommentNodeType */) {
831
+ continue;
832
+ }
833
+ if (node1.chi[i].nam == node2.chi[j].nam) {
834
+ if (eq(node1.chi[i], node2.chi[j])) {
835
+ intersect.push(node1.chi[i]);
836
+ node1.chi.splice(i, 1);
837
+ node2.chi.splice(j, 1);
838
+ break;
839
+ }
840
+ }
841
+ }
842
+ }
843
+ // @ts-ignore
844
+ const result = (intersect.length == 0 ? null : {
845
+ ...node1,
846
+ // @ts-ignore
847
+ sel: [...new Set([...(n1?.raw?.reduce(reducer, []) || splitRule(n1.sel)).concat(n2?.raw?.reduce(reducer, []) || splitRule(n2.sel))])].join(','),
848
+ chi: intersect.reverse()
849
+ });
850
+ if (result == null || [n1, n2].reduce((acc, curr) => curr.chi.length == 0 ? acc : acc + doRender(curr, options).code.length, 0) <= [node1, node2, result].reduce((acc, curr) => curr.chi.length == 0 ? acc : acc + doRender(curr, options).code.length, 0)) {
851
+ // @ts-ignore
852
+ return null;
853
+ }
854
+ return { result, node1: exchanged ? node2 : node1, node2: exchanged ? node1 : node2 };
855
+ }
832
856
  function reduceRuleSelector(node) {
833
857
  if (node.raw == null) {
834
858
  Object.defineProperty(node, 'raw', { ...definedPropertySettings, value: splitRule(node.sel) });
@@ -864,4 +888,4 @@ function reduceRuleSelector(node) {
864
888
  }
865
889
  }
866
890
 
867
- export { combinators, hasDeclaration, minify, minifyRule, reduceSelector, splitRule };
891
+ export { combinators, hasDeclaration, minify, reduceSelector, splitRule };