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

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,16 +1,22 @@
1
- import { isWhiteSpace, isDigit, isPseudo, isAtKeyword, isFunction, isNumber, isDimension, parseDimension, isPercentage, isIdent, isHash, isNewLine, isIdentStart, isHexColor } from './utils/syntax.js';
1
+ import { isPseudo, isAtKeyword, isFunction, isNumber, isDimension, parseDimension, isPercentage, isIdent, isHexColor, isHash, isIdentStart } from './utils/syntax.js';
2
2
  import { renderToken } from '../renderer/render.js';
3
3
  import { COLORS_NAMES } from '../renderer/utils/color.js';
4
- import { deduplicate } from './deduplicate.js';
4
+ import { minify, combinators } from '../ast/minify.js';
5
+ import { tokenize } from './tokenize.js';
5
6
 
6
7
  const urlTokenMatcher = /^(["']?)[a-zA-Z0-9_/.-][a-zA-Z0-9_/:.#?-]+(\1)$/;
7
8
  const funcLike = ['Start-parens', 'Func', 'UrlFunc', 'Pseudo-class-func'];
9
+ /**
10
+ *
11
+ * @param iterator
12
+ * @param opt
13
+ */
8
14
  async function parse(iterator, opt = {}) {
9
15
  const errors = [];
10
16
  const options = {
11
17
  src: '',
12
18
  sourcemap: false,
13
- compress: false,
19
+ minify: true,
14
20
  nestingRules: false,
15
21
  resolveImport: false,
16
22
  resolveUrls: false,
@@ -20,172 +26,28 @@ async function parse(iterator, opt = {}) {
20
26
  if (options.resolveImport) {
21
27
  options.resolveUrls = true;
22
28
  }
23
- let ind = -1;
24
- let lin = 1;
25
- let col = 0;
26
- const tokens = [];
27
29
  const src = options.src;
28
30
  const stack = [];
29
31
  const ast = {
30
32
  typ: "StyleSheet",
31
33
  chi: []
32
34
  };
33
- const position = {
34
- ind: Math.max(ind, 0),
35
- lin: lin,
36
- col: Math.max(col, 1)
37
- };
38
- let value;
39
- let buffer = '';
40
- let total = iterator.length;
41
- let bytesIn = total;
35
+ let tokens = [];
42
36
  let map = new Map;
37
+ let bytesIn = 0;
43
38
  let context = ast;
44
39
  if (options.sourcemap) {
45
40
  ast.loc = {
46
41
  sta: {
47
- ind: ind,
48
- lin: lin,
49
- col: col
42
+ ind: 0,
43
+ lin: 1,
44
+ col: 1
50
45
  },
51
46
  src: ''
52
47
  };
53
48
  }
54
- function getType(val) {
55
- if (val === '') {
56
- throw new Error('empty string?');
57
- }
58
- if (val == ':') {
59
- return { typ: 'Colon' };
60
- }
61
- if (val == ')') {
62
- return { typ: 'End-parens' };
63
- }
64
- if (val == '(') {
65
- return { typ: 'Start-parens' };
66
- }
67
- if (val == '=') {
68
- return { typ: 'Delim', val };
69
- }
70
- if (val == ';') {
71
- return { typ: 'Semi-colon' };
72
- }
73
- if (val == ',') {
74
- return { typ: 'Comma' };
75
- }
76
- if (val == '<') {
77
- return { typ: 'Lt' };
78
- }
79
- if (val == '>') {
80
- return { typ: 'Gt' };
81
- }
82
- if (isPseudo(val)) {
83
- return val.endsWith('(') ? {
84
- typ: 'Pseudo-class-func',
85
- val: val.slice(0, -1),
86
- chi: []
87
- }
88
- : {
89
- typ: 'Pseudo-class',
90
- val
91
- };
92
- }
93
- if (isAtKeyword(val)) {
94
- return {
95
- typ: 'At-rule',
96
- val: val.slice(1)
97
- };
98
- }
99
- if (isFunction(val)) {
100
- val = val.slice(0, -1);
101
- return {
102
- typ: val == 'url' ? 'UrlFunc' : 'Func',
103
- val,
104
- chi: []
105
- };
106
- }
107
- if (isNumber(val)) {
108
- return {
109
- typ: 'Number',
110
- val
111
- };
112
- }
113
- if (isDimension(val)) {
114
- return parseDimension(val);
115
- }
116
- if (isPercentage(val)) {
117
- return {
118
- typ: 'Perc',
119
- val: val.slice(0, -1)
120
- };
121
- }
122
- if (val == 'currentColor') {
123
- return {
124
- typ: 'Color',
125
- val,
126
- kin: 'lit'
127
- };
128
- }
129
- if (isIdent(val)) {
130
- return {
131
- typ: 'Iden',
132
- val
133
- };
134
- }
135
- if (val.charAt(0) == '#' && isHash(val)) {
136
- return {
137
- typ: 'Hash',
138
- val
139
- };
140
- }
141
- if ('"\''.includes(val.charAt(0))) {
142
- return {
143
- typ: 'Unclosed-string',
144
- val
145
- };
146
- }
147
- return {
148
- typ: 'Literal',
149
- val
150
- };
151
- }
152
- // consume and throw away
153
- function consume(open, close) {
154
- let count = 1;
155
- let chr;
156
- while (true) {
157
- chr = next();
158
- if (chr == '\\') {
159
- if (peek() === '') {
160
- break;
161
- }
162
- continue;
163
- }
164
- else if (chr == '/' && peek() == '*') {
165
- next();
166
- while (true) {
167
- chr = next();
168
- if (chr === '') {
169
- break;
170
- }
171
- if (chr == '*' && peek() == '/') {
172
- next();
173
- break;
174
- }
175
- }
176
- }
177
- else if (chr == close) {
178
- count--;
179
- }
180
- else if (chr == open) {
181
- count++;
182
- }
183
- if (chr === '' || count == 0) {
184
- break;
185
- }
186
- }
187
- }
188
- async function parseNode(tokens) {
49
+ async function parseNode(results) {
50
+ let tokens = results.map(mapToken);
189
51
  let i;
190
52
  let loc;
191
53
  for (i = 0; i < tokens.length; i++) {
@@ -283,7 +145,7 @@ async function parse(iterator, opt = {}) {
283
145
  // @ts-ignore
284
146
  const root = await options.load(url, options.src).then((src) => {
285
147
  return parse(src, Object.assign({}, options, {
286
- compress: false,
148
+ minify: false,
287
149
  // @ts-ignore
288
150
  src: options.resolve(url, options.src).absolute
289
151
  }));
@@ -334,20 +196,24 @@ async function parse(iterator, opt = {}) {
334
196
  // rule
335
197
  if (delim.typ == 'Block-start') {
336
198
  const position = map.get(tokens[0]);
337
- if (context.typ == 'Rule') {
338
- if (tokens[0]?.typ == 'Iden') {
339
- errors.push({ action: 'drop', message: 'invalid nesting rule', location: { src, ...position } });
340
- return null;
341
- }
342
- }
199
+ // if (context.typ == 'Rule') {
200
+ //
201
+ // if (tokens[0]?.typ == 'Iden') {
202
+ // errors.push({action: 'drop', message: 'invalid nesting rule', location: {src, ...position}});
203
+ // return null;
204
+ // }
205
+ // }
343
206
  const uniq = new Map;
344
- parseTokens(tokens, 'Rule', { compress: options.compress }).reduce((acc, curr, index, array) => {
207
+ parseTokens(tokens, { minify: options.minify }).reduce((acc, curr, index, array) => {
345
208
  if (curr.typ == 'Whitespace') {
346
- if (array[index - 1]?.val == '+' || array[index + 1]?.val == '+') {
209
+ if (array[index - 1]?.typ == 'Gt' ||
210
+ array[index + 1]?.typ == 'Gt' ||
211
+ combinators.includes(array[index - 1]?.val) ||
212
+ combinators.includes(array[index + 1]?.val)) {
347
213
  return acc;
348
214
  }
349
215
  }
350
- let t = renderToken(curr, { compress: true });
216
+ let t = renderToken(curr, { minify: true });
351
217
  if (t == ',') {
352
218
  acc.push([]);
353
219
  }
@@ -390,7 +256,7 @@ async function parse(iterator, opt = {}) {
390
256
  }
391
257
  if (tokens[i].typ == 'Colon') {
392
258
  name = tokens.slice(0, i);
393
- value = parseTokens(tokens.slice(i + 1), 'Declaration', {
259
+ value = parseTokens(tokens.slice(i + 1), {
394
260
  parseColor: true,
395
261
  src: options.src,
396
262
  resolveUrls: options.resolveUrls,
@@ -416,11 +282,19 @@ async function parse(iterator, opt = {}) {
416
282
  }
417
283
  }
418
284
  if (value == null) {
419
- errors.push({ action: 'drop', message: 'invalid declaration', location: { src, ...position } });
285
+ errors.push({
286
+ action: 'drop',
287
+ message: 'invalid declaration',
288
+ location: { src, ...position }
289
+ });
420
290
  return null;
421
291
  }
422
292
  if (value.length == 0) {
423
- errors.push({ action: 'drop', message: 'invalid declaration', location: { src, ...position } });
293
+ errors.push({
294
+ action: 'drop',
295
+ message: 'invalid declaration',
296
+ location: { src, ...position }
297
+ });
424
298
  return null;
425
299
  }
426
300
  const node = {
@@ -434,7 +308,11 @@ async function parse(iterator, opt = {}) {
434
308
  node.val.shift();
435
309
  }
436
310
  if (node.val.length == 0) {
437
- errors.push({ action: 'drop', message: 'invalid declaration', location: { src, ...position } });
311
+ errors.push({
312
+ action: 'drop',
313
+ message: 'invalid declaration',
314
+ location: { src, ...position }
315
+ });
438
316
  return null;
439
317
  }
440
318
  // @ts-ignore
@@ -443,517 +321,211 @@ async function parse(iterator, opt = {}) {
443
321
  }
444
322
  }
445
323
  }
446
- function peek(count = 1) {
447
- if (count == 1) {
448
- return iterator.charAt(ind + 1);
449
- }
450
- return iterator.slice(ind + 1, ind + count + 1);
451
- }
452
- function prev(count = 1) {
453
- if (count == 1) {
454
- return ind == 0 ? '' : iterator.charAt(ind - 1);
455
- }
456
- return iterator.slice(ind - 1 - count, ind - 1);
324
+ function mapToken(token) {
325
+ const node = getTokenType(token.token, token.hint);
326
+ map.set(node, token.position);
327
+ return node;
457
328
  }
458
- function next(count = 1) {
459
- let char = '';
460
- while (count-- > 0 && ind < total) {
461
- const codepoint = iterator.charCodeAt(++ind);
462
- if (isNaN(codepoint)) {
463
- return char;
464
- }
465
- char += iterator.charAt(ind);
466
- if (isNewLine(codepoint)) {
467
- lin++;
468
- col = 0;
469
- }
470
- else {
471
- col++;
472
- }
473
- }
474
- return char;
475
- }
476
- function pushToken(token) {
477
- tokens.push(token);
478
- map.set(token, { ...position });
479
- position.ind = ind;
480
- position.lin = lin;
481
- position.col = col == 0 ? 1 : col;
482
- }
483
- function consumeWhiteSpace() {
484
- let count = 0;
485
- while (isWhiteSpace(iterator.charAt(count + ind + 1).charCodeAt(0))) {
486
- count++;
487
- }
488
- next(count);
489
- return count;
490
- }
491
- function consumeString(quoteStr) {
492
- const quote = quoteStr;
493
- let value;
494
- let hasNewLine = false;
495
- if (buffer.length > 0) {
496
- pushToken(getType(buffer));
497
- buffer = '';
329
+ const iter = tokenize(iterator);
330
+ let item;
331
+ while (true) {
332
+ item = iter.next().value;
333
+ if (item == null) {
334
+ break;
498
335
  }
499
- buffer += quoteStr;
500
- while (ind < total) {
501
- value = peek();
502
- if (ind >= total) {
503
- pushToken({ typ: hasNewLine ? 'Bad-string' : 'Unclosed-string', val: buffer });
504
- break;
505
- }
506
- if (value == '\\') {
507
- const sequence = peek(6);
508
- let escapeSequence = '';
509
- let codepoint;
510
- let i;
511
- for (i = 1; i < sequence.length; i++) {
512
- codepoint = sequence.charCodeAt(i);
513
- if (codepoint == 0x20 ||
514
- (codepoint >= 0x61 && codepoint <= 0x66) ||
515
- (codepoint >= 0x41 && codepoint <= 0x46) ||
516
- (codepoint >= 0x30 && codepoint <= 0x39)) {
517
- escapeSequence += sequence[i];
518
- if (codepoint == 0x20) {
519
- break;
520
- }
521
- continue;
522
- }
523
- break;
524
- }
525
- // not hex or new line
336
+ tokens.push(item);
337
+ bytesIn = item.bytesIn;
338
+ if (item.token == ';' || item.token == '{') {
339
+ let node = await parseNode(tokens);
340
+ if (node != null) {
341
+ stack.push(node);
526
342
  // @ts-ignore
527
- if (i == 1 && !isNewLine(codepoint)) {
528
- buffer += sequence[i];
529
- next(2);
530
- continue;
531
- }
532
- if (escapeSequence.trimEnd().length > 0) {
533
- const codepoint = Number(`0x${escapeSequence.trimEnd()}`);
534
- if (codepoint == 0 ||
535
- // leading surrogate
536
- (0xD800 <= codepoint && codepoint <= 0xDBFF) ||
537
- // trailing surrogate
538
- (0xDC00 <= codepoint && codepoint <= 0xDFFF)) {
539
- buffer += String.fromCodePoint(0xFFFD);
343
+ context = node;
344
+ }
345
+ else if (item.token == '{') {
346
+ // node == null
347
+ // consume and throw away until the closing '}' or EOF
348
+ let inBlock = 1;
349
+ do {
350
+ item = iter.next().value;
351
+ if (item == null) {
352
+ break;
540
353
  }
541
- else {
542
- buffer += String.fromCodePoint(codepoint);
354
+ if (item.token == '{') {
355
+ inBlock++;
543
356
  }
544
- next(escapeSequence.length + 1);
545
- continue;
546
- }
547
- // buffer += value;
548
- if (ind >= total) {
549
- // drop '\\' at the end
550
- pushToken(getType(buffer));
551
- break;
552
- }
553
- buffer += next(2);
554
- continue;
555
- }
556
- if (value == quote) {
557
- buffer += value;
558
- pushToken({ typ: hasNewLine ? 'Bad-string' : 'String', val: buffer });
559
- next();
560
- // i += value.length;
561
- buffer = '';
562
- break;
563
- }
564
- if (isNewLine(value.charCodeAt(0))) {
565
- hasNewLine = true;
566
- }
567
- if (hasNewLine && value == ';') {
568
- pushToken({ typ: 'Bad-string', val: buffer });
569
- buffer = '';
570
- break;
571
- }
572
- buffer += value;
573
- // i += value.length;
574
- next();
575
- }
576
- }
577
- while (ind < total) {
578
- value = next();
579
- if (ind >= total) {
580
- if (buffer.length > 0) {
581
- pushToken(getType(buffer));
582
- buffer = '';
357
+ else if (item.token == '}') {
358
+ inBlock--;
359
+ }
360
+ } while (inBlock != 0);
583
361
  }
584
- break;
362
+ tokens = [];
363
+ map = new Map;
585
364
  }
586
- if (isWhiteSpace(value.charCodeAt(0))) {
587
- if (buffer.length > 0) {
588
- pushToken(getType(buffer));
589
- buffer = '';
590
- }
591
- while (ind < total) {
592
- value = next();
593
- if (ind >= total) {
594
- break;
595
- }
596
- if (!isWhiteSpace(value.charCodeAt(0))) {
597
- break;
598
- }
599
- }
600
- pushToken({ typ: 'Whitespace' });
601
- buffer = '';
602
- if (ind >= total) {
603
- break;
365
+ else if (item.token == '}') {
366
+ await parseNode(tokens);
367
+ const previousNode = stack.pop();
368
+ // @ts-ignore
369
+ context = stack[stack.length - 1] || ast;
370
+ // @ts-ignore
371
+ if (options.removeEmpty && previousNode != null && previousNode.chi.length == 0 && context.chi[context.chi.length - 1] == previousNode) {
372
+ context.chi.pop();
604
373
  }
605
- }
606
- switch (value) {
607
- case '/':
608
- if (buffer.length > 0 && tokens.at(-1)?.typ == 'Whitespace') {
609
- pushToken(getType(buffer));
610
- buffer = '';
611
- if (peek() != '*') {
612
- pushToken(getType(value));
613
- break;
614
- }
615
- }
616
- buffer += value;
617
- if (peek() == '*') {
618
- buffer += '*';
619
- // i++;
620
- next();
621
- while (ind < total) {
622
- value = next();
623
- if (ind >= total) {
624
- pushToken({
625
- typ: 'Bad-comment', val: buffer
626
- });
627
- break;
628
- }
629
- if (value == '\\') {
630
- buffer += value;
631
- value = next();
632
- if (ind >= total) {
633
- pushToken({
634
- typ: 'Bad-comment',
635
- val: buffer
636
- });
637
- break;
638
- }
639
- buffer += value;
640
- continue;
641
- }
642
- if (value == '*') {
643
- buffer += value;
644
- value = next();
645
- if (ind >= total) {
646
- pushToken({
647
- typ: 'Bad-comment', val: buffer
648
- });
649
- break;
650
- }
651
- buffer += value;
652
- if (value == '/') {
653
- pushToken({ typ: 'Comment', val: buffer });
654
- buffer = '';
655
- break;
656
- }
657
- }
658
- else {
659
- buffer += value;
660
- }
661
- }
662
- }
663
- break;
664
- case '<':
665
- if (buffer.length > 0) {
666
- pushToken(getType(buffer));
667
- buffer = '';
668
- }
669
- buffer += value;
670
- value = next();
671
- if (ind >= total) {
672
- break;
673
- }
674
- if (peek(3) == '!--') {
675
- while (ind < total) {
676
- value = next();
677
- if (ind >= total) {
678
- break;
679
- }
680
- buffer += value;
681
- if (value == '>' && prev(2) == '--') {
682
- pushToken({
683
- typ: 'CDOCOMM',
684
- val: buffer
685
- });
686
- buffer = '';
687
- break;
688
- }
689
- }
690
- }
691
- if (ind >= total) {
692
- pushToken({ typ: 'BADCDO', val: buffer });
693
- buffer = '';
694
- }
695
- break;
696
- case '\\':
697
- value = next();
698
- // EOF
699
- if (ind + 1 >= total) {
700
- // end of stream ignore \\
701
- pushToken(getType(buffer));
702
- buffer = '';
703
- break;
704
- }
705
- buffer += value;
706
- break;
707
- case '"':
708
- case "'":
709
- consumeString(value);
710
- break;
711
- case '~':
712
- case '|':
713
- if (tokens.at(-1)?.typ == 'Whitespace') {
714
- tokens.pop();
715
- }
716
- if (buffer.length > 0) {
717
- pushToken(getType(buffer));
718
- buffer = '';
719
- }
720
- buffer += value;
721
- value = next();
722
- if (ind >= total) {
723
- pushToken(getType(buffer));
724
- buffer = '';
725
- break;
726
- }
727
- if (value == '=') {
728
- buffer += value;
729
- pushToken({
730
- typ: buffer[0] == '~' ? 'Includes' : 'Dash-matches',
731
- val: buffer
732
- });
733
- buffer = '';
734
- break;
735
- }
736
- pushToken(getType(buffer));
737
- while (isWhiteSpace(value.charCodeAt(0))) {
738
- value = next();
739
- }
740
- buffer = value;
741
- break;
742
- case '>':
743
- if (buffer !== '') {
744
- pushToken(getType(buffer));
745
- buffer = '';
746
- }
747
- if (tokens[tokens.length - 1]?.typ == 'Whitespace') {
748
- tokens.pop();
749
- }
750
- pushToken({ typ: 'Gt' });
751
- consumeWhiteSpace();
752
- break;
753
- case '.':
754
- const codepoint = peek().charCodeAt(0);
755
- if (!isDigit(codepoint) && buffer !== '') {
756
- pushToken(getType(buffer));
757
- buffer = value;
758
- break;
759
- }
760
- buffer += value;
761
- break;
762
- case '+':
763
- case ':':
764
- case ',':
765
- case '=':
766
- if (buffer.length > 0) {
767
- pushToken(getType(buffer));
768
- buffer = '';
769
- }
770
- if (value == ':' && ':' == peek()) {
771
- buffer += value + next();
772
- break;
773
- }
774
- pushToken(getType(value));
775
- buffer = '';
776
- if (value == '+' && isWhiteSpace(peek().charCodeAt(0))) {
777
- pushToken(getType(next()));
778
- }
779
- while (isWhiteSpace(peek().charCodeAt(0))) {
780
- next();
781
- }
782
- break;
783
- case ')':
784
- if (buffer.length > 0) {
785
- pushToken(getType(buffer));
786
- buffer = '';
787
- }
788
- pushToken({ typ: 'End-parens' });
789
- break;
790
- case '(':
791
- if (buffer.length == 0) {
792
- pushToken({ typ: 'Start-parens' });
793
- }
794
- else {
795
- buffer += value;
796
- pushToken(getType(buffer));
797
- buffer = '';
798
- const token = tokens[tokens.length - 1];
799
- if (token.typ == 'UrlFunc') {
800
- // consume either string or url token
801
- let whitespace = '';
802
- value = peek();
803
- while (isWhiteSpace(value.charCodeAt(0))) {
804
- whitespace += value;
805
- }
806
- if (whitespace.length > 0) {
807
- next(whitespace.length);
808
- }
809
- value = peek();
810
- if (value == '"' || value == "'") {
811
- consumeString(next());
812
- let token = tokens[tokens.length - 1];
813
- if (['String', 'Literal'].includes(token.typ) && urlTokenMatcher.test(token.val)) {
814
- if (token.val.slice(1, 6) != 'data:') {
815
- if (token.typ == 'String') {
816
- token.val = token.val.slice(1, -1);
817
- }
818
- // @ts-ignore
819
- token.typ = 'Url-token';
820
- }
821
- }
822
- break;
823
- }
824
- else {
825
- buffer = '';
826
- do {
827
- let cp = value.charCodeAt(0);
828
- // EOF -
829
- if (cp == null) {
830
- pushToken({ typ: 'Bad-url-token', val: buffer });
831
- break;
832
- }
833
- // ')'
834
- if (cp == 0x29 || cp == null) {
835
- if (buffer.length == 0) {
836
- pushToken({ typ: 'Bad-url-token', val: '' });
837
- }
838
- else {
839
- pushToken({ typ: 'Url-token', val: buffer });
840
- }
841
- if (cp != null) {
842
- pushToken(getType(next()));
843
- }
844
- break;
845
- }
846
- if (isWhiteSpace(cp)) {
847
- whitespace = next();
848
- while (true) {
849
- value = peek();
850
- cp = value.charCodeAt(0);
851
- if (isWhiteSpace(cp)) {
852
- whitespace += value;
853
- continue;
854
- }
855
- break;
856
- }
857
- if (cp == null || cp == 0x29) {
858
- continue;
859
- }
860
- // bad url token
861
- buffer += next(whitespace.length);
862
- do {
863
- value = peek();
864
- cp = value.charCodeAt(0);
865
- if (cp == null || cp == 0x29) {
866
- break;
867
- }
868
- buffer += next();
869
- } while (true);
870
- pushToken({ typ: 'Bad-url-token', val: buffer });
871
- continue;
872
- }
873
- buffer += next();
874
- value = peek();
875
- } while (true);
876
- buffer = '';
877
- }
878
- }
879
- }
880
- break;
881
- case '[':
882
- case ']':
883
- case '{':
884
- case '}':
885
- case ';':
886
- if (buffer.length > 0) {
887
- pushToken(getType(buffer));
888
- buffer = '';
889
- }
890
- pushToken(getBlockType(value));
891
- let node = null;
892
- if (value == '{' || value == ';') {
893
- node = await parseNode(tokens);
894
- if (node != null) {
895
- stack.push(node);
896
- // @ts-ignore
897
- context = node;
898
- }
899
- else if (value == '{') {
900
- // node == null
901
- // consume and throw away until the closing '}' or EOF
902
- consume('{', '}');
903
- }
904
- tokens.length = 0;
905
- map.clear();
906
- }
907
- else if (value == '}') {
908
- await parseNode(tokens);
909
- const previousNode = stack.pop();
910
- // @ts-ignore
911
- context = stack[stack.length - 1] || ast;
912
- // @ts-ignore
913
- if (options.removeEmpty && previousNode != null && previousNode.chi.length == 0 && context.chi[context.chi.length - 1] == previousNode) {
914
- context.chi.pop();
915
- }
916
- tokens.length = 0;
917
- map.clear();
918
- buffer = '';
919
- }
920
- break;
921
- case '!':
922
- if (buffer.length > 0) {
923
- pushToken(getType(buffer));
924
- buffer = '';
925
- }
926
- const important = peek(9);
927
- if (important == 'important') {
928
- if (tokens[tokens.length - 1]?.typ == 'Whitespace') {
929
- tokens.pop();
930
- }
931
- pushToken({ typ: 'Important' });
932
- next(9);
933
- buffer = '';
934
- break;
935
- }
936
- buffer = '!';
937
- break;
938
- default:
939
- buffer += value;
940
- break;
374
+ tokens = [];
375
+ map = new Map;
941
376
  }
942
377
  }
943
- if (buffer.length > 0) {
944
- pushToken(getType(buffer));
945
- }
946
378
  if (tokens.length > 0) {
947
379
  await parseNode(tokens);
948
380
  }
949
- if (options.compress) {
381
+ if (options.minify) {
950
382
  if (ast.chi.length > 0) {
951
- deduplicate(ast, options, true);
383
+ minify(ast, options, true);
952
384
  }
953
385
  }
954
386
  return { ast, errors, bytesIn };
955
387
  }
956
- function parseTokens(tokens, nodeType, options = {}) {
388
+ function parseString(src, options = { location: false }) {
389
+ return [...tokenize(src)].map(t => {
390
+ const token = getTokenType(t.token, t.hint);
391
+ if (options.location) {
392
+ Object.assign(token, { loc: t.position });
393
+ }
394
+ return token;
395
+ });
396
+ }
397
+ function getTokenType(val, hint) {
398
+ if (val === '' && hint == null) {
399
+ throw new Error('empty string?');
400
+ }
401
+ if (hint != null) {
402
+ return ([
403
+ 'Whitespace', 'Semi-colon', 'Colon', 'Block-start',
404
+ 'Block-start', 'Attr-start', 'Attr-end', 'Start-parens', 'End-parens',
405
+ 'Comma', 'Gt', 'Lt'
406
+ ].includes(hint) ? { typ: hint } : { typ: hint, val });
407
+ }
408
+ if (val == ' ') {
409
+ return { typ: 'Whitespace' };
410
+ }
411
+ if (val == ';') {
412
+ return { typ: 'Semi-colon' };
413
+ }
414
+ if (val == '{') {
415
+ return { typ: 'Block-start' };
416
+ }
417
+ if (val == '}') {
418
+ return { typ: 'Block-end' };
419
+ }
420
+ if (val == '[') {
421
+ return { typ: 'Attr-start' };
422
+ }
423
+ if (val == ']') {
424
+ return { typ: 'Attr-end' };
425
+ }
426
+ if (val == ':') {
427
+ return { typ: 'Colon' };
428
+ }
429
+ if (val == ')') {
430
+ return { typ: 'End-parens' };
431
+ }
432
+ if (val == '(') {
433
+ return { typ: 'Start-parens' };
434
+ }
435
+ if (val == '=') {
436
+ return { typ: 'Delim', val };
437
+ }
438
+ if (val == ';') {
439
+ return { typ: 'Semi-colon' };
440
+ }
441
+ if (val == ',') {
442
+ return { typ: 'Comma' };
443
+ }
444
+ if (val == '<') {
445
+ return { typ: 'Lt' };
446
+ }
447
+ if (val == '>') {
448
+ return { typ: 'Gt' };
449
+ }
450
+ if (isPseudo(val)) {
451
+ return val.endsWith('(') ? {
452
+ typ: 'Pseudo-class-func',
453
+ val: val.slice(0, -1),
454
+ chi: []
455
+ }
456
+ : {
457
+ typ: 'Pseudo-class',
458
+ val
459
+ };
460
+ }
461
+ if (isAtKeyword(val)) {
462
+ return {
463
+ typ: 'At-rule',
464
+ val: val.slice(1)
465
+ };
466
+ }
467
+ if (isFunction(val)) {
468
+ val = val.slice(0, -1);
469
+ return {
470
+ typ: val == 'url' ? 'UrlFunc' : 'Func',
471
+ val,
472
+ chi: []
473
+ };
474
+ }
475
+ if (isNumber(val)) {
476
+ return {
477
+ typ: 'Number',
478
+ val
479
+ };
480
+ }
481
+ if (isDimension(val)) {
482
+ return parseDimension(val);
483
+ }
484
+ if (isPercentage(val)) {
485
+ return {
486
+ typ: 'Perc',
487
+ val: val.slice(0, -1)
488
+ };
489
+ }
490
+ const v = val.toLowerCase();
491
+ if (v == 'currentcolor' || val == 'transparent' || v in COLORS_NAMES) {
492
+ return {
493
+ typ: 'Color',
494
+ val,
495
+ kin: 'lit'
496
+ };
497
+ }
498
+ if (isIdent(val)) {
499
+ return {
500
+ typ: 'Iden',
501
+ val
502
+ };
503
+ }
504
+ if (val.charAt(0) == '#' && isHexColor(val)) {
505
+ return {
506
+ typ: 'Color',
507
+ val,
508
+ kin: 'hex'
509
+ };
510
+ }
511
+ if (val.charAt(0) == '#' && isHash(val)) {
512
+ return {
513
+ typ: 'Hash',
514
+ val
515
+ };
516
+ }
517
+ if ('"\''.includes(val.charAt(0))) {
518
+ return {
519
+ typ: 'Unclosed-string',
520
+ val
521
+ };
522
+ }
523
+ return {
524
+ typ: 'Literal',
525
+ val
526
+ };
527
+ }
528
+ function parseTokens(tokens, options = {}) {
957
529
  for (let i = 0; i < tokens.length; i++) {
958
530
  const t = tokens[i];
959
531
  if (t.typ == 'Whitespace' && ((i == 0 ||
@@ -1007,7 +579,7 @@ function parseTokens(tokens, nodeType, options = {}) {
1007
579
  if (t.chi.length > 1) {
1008
580
  /*(<AttrToken>t).chi =*/
1009
581
  // @ts-ignore
1010
- parseTokens(t.chi, t.typ, options);
582
+ parseTokens(t.chi, t.typ);
1011
583
  }
1012
584
  // @ts-ignore
1013
585
  t.chi.forEach(val => {
@@ -1119,8 +691,8 @@ function parseTokens(tokens, nodeType, options = {}) {
1119
691
  // @ts-ignore
1120
692
  if (t.chi.length > 0) {
1121
693
  // @ts-ignore
1122
- parseTokens(t.chi, t.typ, options);
1123
- if (t.typ == 'Pseudo-class-func' && t.val == ':is' && options.compress) {
694
+ parseTokens(t.chi, t.typ);
695
+ if (t.typ == 'Pseudo-class-func' && t.val == ':is' && options.minify) {
1124
696
  //
1125
697
  const count = t.chi.filter(t => t.typ != 'Comment').length;
1126
698
  if (count == 1 ||
@@ -1158,23 +730,5 @@ function parseTokens(tokens, nodeType, options = {}) {
1158
730
  }
1159
731
  return tokens;
1160
732
  }
1161
- function getBlockType(chr) {
1162
- if (chr == ';') {
1163
- return { typ: 'Semi-colon' };
1164
- }
1165
- if (chr == '{') {
1166
- return { typ: 'Block-start' };
1167
- }
1168
- if (chr == '}') {
1169
- return { typ: 'Block-end' };
1170
- }
1171
- if (chr == '[') {
1172
- return { typ: 'Attr-start' };
1173
- }
1174
- if (chr == ']') {
1175
- return { typ: 'Attr-end' };
1176
- }
1177
- throw new Error(`unhandled token: '${chr}'`);
1178
- }
1179
733
 
1180
- export { parse };
734
+ export { parse, parseString, urlTokenMatcher };