@eslint/css-tree 3.6.9 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (C) 2016-2024 by Roman Dvornov
1
+ Copyright (C) 2016-2026 by Roman Dvornov
2
2
  Copyright OpenJS Foundation and other contributors, <www.openjsf.org>
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
package/README.md CHANGED
@@ -202,9 +202,9 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
202
202
 
203
203
  <h3>Platinum Sponsors</h3>
204
204
  <p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="128"></a></p><h3>Gold Sponsors</h3>
205
- <p><a href="https://qlty.sh/"><img src="https://images.opencollective.com/qltysh/33d157d/logo.png" alt="Qlty Software" height="96"></a> <a href="https://shopify.engineering/"><img src="https://avatars.githubusercontent.com/u/8085" alt="Shopify" height="96"></a></p><h3>Silver Sponsors</h3>
205
+ <p><a href="https://qlty.sh/"><img src="https://images.opencollective.com/qltysh/33d157d/logo.png" alt="Qlty Software" height="96"></a></p><h3>Silver Sponsors</h3>
206
206
  <p><a href="https://vite.dev/"><img src="https://images.opencollective.com/vite/d472863/logo.png" alt="Vite" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/2d6c3b6/logo.png" alt="Liftoff" height="64"></a> <a href="https://stackblitz.com"><img src="https://avatars.githubusercontent.com/u/28635252" alt="StackBlitz" height="64"></a></p><h3>Bronze Sponsors</h3>
207
- <p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://opensource.sap.com"><img src="https://avatars.githubusercontent.com/u/2531208" alt="SAP" height="32"></a> <a href="https://www.crawljobs.com/"><img src="https://images.opencollective.com/crawljobs-poland/fa43a17/logo.png" alt="CrawlJobs" height="32"></a> <a href="https://depot.dev"><img src="https://images.opencollective.com/depot/39125a1/logo.png" alt="Depot" height="32"></a> <a href="https://www.n-ix.com/"><img src="https://images.opencollective.com/n-ix-ltd/575a7a5/logo.png" alt="N-iX Ltd" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a> <a href="https://www.lambdatest.com"><img src="https://avatars.githubusercontent.com/u/171592363" alt="TestMu AI Open Source Office (Formerly LambdaTest)" height="32"></a></p>
207
+ <p><a href="https://syntax.fm"><img src="https://github.com/syntaxfm.png" alt="Syntax" height="32"></a> <a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://opensource.sap.com"><img src="https://avatars.githubusercontent.com/u/2531208" alt="SAP" height="32"></a> <a href="https://www.crawljobs.com/"><img src="https://images.opencollective.com/crawljobs-poland/fa43a17/logo.png" alt="CrawlJobs" height="32"></a> <a href="https://depot.dev"><img src="https://images.opencollective.com/depot/39125a1/logo.png" alt="Depot" height="32"></a> <a href="https://www.n-ix.com/"><img src="https://images.opencollective.com/n-ix-ltd/575a7a5/logo.png" alt="N-iX Ltd" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a> <a href="https://www.lambdatest.com"><img src="https://avatars.githubusercontent.com/u/171592363" alt="TestMu AI Open Source Office (Formerly LambdaTest)" height="32"></a></p>
208
208
  <h3>Technology Sponsors</h3>
209
209
  Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.
210
210
  <p><a href="https://netlify.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/netlify-icon.svg" alt="Netlify" height="32"></a> <a href="https://algolia.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/algolia-icon.svg" alt="Algolia" height="32"></a> <a href="https://1password.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/1password-icon.svg" alt="1Password" height="32"></a></p>
@@ -162,6 +162,16 @@ function maybeMultiplied(scanner, node) {
162
162
  return maybeMultiplied(scanner, multiplier);
163
163
  }
164
164
 
165
+ // https://www.w3.org/TR/css-values-4/#component-multipliers
166
+ // > the # and ? multipliers, {A} and ? multipliers, and {A,B} and ? multipliers
167
+ // > may be stacked as #?, {A}?, and {A,B}?, respectively
168
+ // Represent "{}?" as nested multipliers as well as "+#".
169
+ // The "#?" case is already handled above, in maybeMultiplied()
170
+ if (scanner.charCode() === QUESTIONMARK &&
171
+ scanner.charCodeAt(scanner.pos - 1) === RIGHTCURLYBRACKET) {
172
+ return maybeMultiplied(scanner, multiplier);
173
+ }
174
+
165
175
  return multiplier;
166
176
  }
167
177
 
@@ -378,35 +388,65 @@ function regroupTerms(terms, combinators) {
378
388
  return combinator;
379
389
  }
380
390
 
381
- function readImplicitGroup(scanner, stopCharCode) {
391
+ function readImplicitGroup(scanner, stopCharCode = -1) {
382
392
  const combinators = Object.create(null);
383
393
  const terms = [];
384
- let token;
385
394
  let prevToken = null;
386
395
  let prevTokenPos = scanner.pos;
396
+ let prevTokenIsFunction = false;
397
+
398
+ while (scanner.charCode() !== stopCharCode) {
399
+ let token = prevTokenIsFunction
400
+ ? readImplicitGroup(scanner, RIGHTPARENTHESIS)
401
+ : peek(scanner);
402
+
403
+ if (!token) {
404
+ break;
405
+ }
406
+
407
+ if (token.type === 'Spaces') {
408
+ continue;
409
+ }
410
+
411
+ if (prevTokenIsFunction) {
412
+ if (token.terms.length === 0) {
413
+ prevTokenIsFunction = false;
414
+ continue;
415
+ }
387
416
 
388
- while (scanner.charCode() !== stopCharCode && (token = peek(scanner, stopCharCode))) {
389
- if (token.type !== 'Spaces') {
390
- if (token.type === 'Combinator') {
391
- // check for combinator in group beginning and double combinator sequence
392
- if (prevToken === null || prevToken.type === 'Combinator') {
393
- scanner.pos = prevTokenPos;
394
- scanner.error('Unexpected combinator');
417
+ if (token.combinator === ' ') {
418
+ while (token.terms.length > 1) {
419
+ combinators[' '] = true; // a b
420
+ terms.push({
421
+ type: 'Combinator',
422
+ value: ' '
423
+ }, token.terms.shift());
395
424
  }
396
425
 
397
- combinators[token.value] = true;
398
- } else if (prevToken !== null && prevToken.type !== 'Combinator') {
399
- combinators[' '] = true; // a b
400
- terms.push({
401
- type: 'Combinator',
402
- value: ' '
403
- });
426
+ token = token.terms[0];
404
427
  }
428
+ }
405
429
 
406
- terms.push(token);
407
- prevToken = token;
408
- prevTokenPos = scanner.pos;
430
+ if (token.type === 'Combinator') {
431
+ // check for combinator in group beginning and double combinator sequence
432
+ if (prevToken === null || prevToken.type === 'Combinator') {
433
+ scanner.pos = prevTokenPos;
434
+ scanner.error('Unexpected combinator');
435
+ }
436
+
437
+ combinators[token.value] = true;
438
+ } else if (prevToken !== null && prevToken.type !== 'Combinator') {
439
+ combinators[' '] = true; // a b
440
+ terms.push({
441
+ type: 'Combinator',
442
+ value: ' '
443
+ });
409
444
  }
445
+
446
+ terms.push(token);
447
+ prevToken = token;
448
+ prevTokenPos = scanner.pos;
449
+ prevTokenIsFunction = token.type === 'Function';
410
450
  }
411
451
 
412
452
  // check for combinator in group ending
@@ -424,11 +464,11 @@ function readImplicitGroup(scanner, stopCharCode) {
424
464
  };
425
465
  }
426
466
 
427
- function readGroup(scanner, stopCharCode) {
467
+ function readGroup(scanner) {
428
468
  let result;
429
469
 
430
470
  scanner.eat(LEFTSQUAREBRACKET);
431
- result = readImplicitGroup(scanner, stopCharCode);
471
+ result = readImplicitGroup(scanner, RIGHTSQUAREBRACKET);
432
472
  scanner.eat(RIGHTSQUAREBRACKET);
433
473
 
434
474
  result.explicit = true;
@@ -441,7 +481,7 @@ function readGroup(scanner, stopCharCode) {
441
481
  return result;
442
482
  }
443
483
 
444
- function peek(scanner, stopCharCode) {
484
+ function peek(scanner) {
445
485
  let code = scanner.charCode();
446
486
 
447
487
  switch (code) {
@@ -450,7 +490,7 @@ function peek(scanner, stopCharCode) {
450
490
  break;
451
491
 
452
492
  case LEFTSQUAREBRACKET:
453
- return maybeMultiplied(scanner, readGroup(scanner, stopCharCode));
493
+ return maybeMultiplied(scanner, readGroup(scanner));
454
494
 
455
495
  case LESSTHANSIGN:
456
496
  return scanner.nextCharCode() === APOSTROPHE
@@ -49,9 +49,13 @@ function createGenerator(config) {
49
49
  }
50
50
  },
51
51
  tokenBefore: tokenBefore.safe,
52
- token(type, value) {
52
+ token(type, value, suppressAutoWhiteSpace) {
53
53
  prevCode = this.tokenBefore(prevCode, type, value);
54
54
 
55
+ if (!suppressAutoWhiteSpace && prevCode & 1) {
56
+ this.emit(' ', types.WhiteSpace, true);
57
+ }
58
+
55
59
  this.emit(value, type, false);
56
60
 
57
61
  if (type === types.Delim && value.charCodeAt(0) === REVERSESOLIDUS) {
@@ -88,7 +92,11 @@ function createGenerator(config) {
88
92
  const tokenize = getTokenizer.getTokenizer(config);
89
93
 
90
94
  return tokenize(chunk, (type, start, end) => {
91
- this.token(type, chunk.slice(start, end));
95
+ handlers.token(
96
+ type,
97
+ chunk.slice(start, end),
98
+ start !== 0 // suppress auto whitespace for internal value tokens
99
+ );
92
100
  });
93
101
  }
94
102
  };
@@ -5,17 +5,20 @@ const types = require('../tokenizer/types.cjs');
5
5
  const PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+)
6
6
  const HYPHENMINUS = 0x002D; // U+002D HYPHEN-MINUS (-)
7
7
 
8
+ // code:
9
+ // 0xxxxxxx x0000000 - char code (0x80 for non-ASCII) or delim value
10
+ // 00000000 0xxxxxx0 - token type (0 for delim, 1 for token)
11
+ // 00000000 0000000x - reserved for carriage emit flag (0 for no space, 1 for space)
8
12
  const code = (type, value) => {
9
13
  if (type === types.Delim) {
10
14
  type = value;
11
15
  }
12
16
 
13
17
  if (typeof type === 'string') {
14
- const charCode = type.charCodeAt(0);
15
- return charCode > 0x7F ? 0x8000 : charCode << 8;
18
+ type = Math.min(type.charCodeAt(0), 0x80) << 6; // replace non-ASCII with 0x80
16
19
  }
17
20
 
18
- return type;
21
+ return type << 1;
19
22
  };
20
23
 
21
24
  // https://www.w3.org/TR/css-syntax-3/#serialization
@@ -152,14 +155,10 @@ function createMap(pairs) {
152
155
  type !== types.Function &&
153
156
  type !== types.CDC) ||
154
157
  (nextCharCode === PLUSSIGN)
155
- ? isWhiteSpaceRequired.has(prevCode << 16 | nextCharCode << 8)
156
- : isWhiteSpaceRequired.has(prevCode << 16 | nextCode);
158
+ ? isWhiteSpaceRequired.has((prevCode & 0xFFFE) << 16 | nextCharCode << 7)
159
+ : isWhiteSpaceRequired.has((prevCode & 0xFFFE) << 16 | nextCode);
157
160
 
158
- if (emitWs) {
159
- this.emit(' ', types.WhiteSpace, true);
160
- }
161
-
162
- return nextCode;
161
+ return nextCode | emitWs;
163
162
  };
164
163
  }
165
164
 
@@ -443,9 +443,13 @@ class Lexer {
443
443
  return atrule && atrule.prelude || null;
444
444
  }
445
445
  getAtruleDescriptor(atruleName, name) {
446
- return this.atrules.hasOwnProperty(atruleName) && this.atrules.declarators
447
- ? this.atrules[atruleName].declarators[name] || null
446
+ const atrule = this.getAtrule(atruleName);
447
+ const descriptor = names.keyword(name);
448
+ const descriptorEntry = atrule && atrule.descriptors
449
+ ? atrule.descriptors[descriptor.name] || atrule.descriptors[descriptor.basename]
448
450
  : null;
451
+
452
+ return descriptorEntry || null;
449
453
  }
450
454
  getProperty(propertyName, fallbackBasename = true) {
451
455
  const property = names.property(propertyName);
@@ -634,6 +634,17 @@ const productionTypes = {
634
634
  'any-value': anyValue
635
635
  };
636
636
 
637
+ const unitGroups = [
638
+ 'length',
639
+ 'angle',
640
+ 'time',
641
+ 'frequency',
642
+ 'resolution',
643
+ 'flex',
644
+ 'decibel',
645
+ 'semitones'
646
+ ];
647
+
637
648
  // dimensions types depend on units set
638
649
  function createDemensionTypes(units) {
639
650
  const {
@@ -660,11 +671,42 @@ function createDemensionTypes(units) {
660
671
  };
661
672
  }
662
673
 
674
+ // The <attr-unit> production matches any identifier that is an ASCII case-insensitive
675
+ // match for the name of a CSS dimension unit, such as px, or the <delim-token> %.
676
+ function createAttrUnit(units) {
677
+ const unitSet = new Set();
678
+
679
+ for (const group of unitGroups) {
680
+ if (Array.isArray(units[group])) {
681
+ for (const unit of units[group]) {
682
+ unitSet.add(unit.toLowerCase());
683
+ }
684
+ }
685
+ }
686
+
687
+ return function attrUnit(token) {
688
+ if (token === null) {
689
+ return 0;
690
+ }
691
+
692
+ if (token.type === types.Delim && token.value === '%') {
693
+ return 1;
694
+ }
695
+
696
+ if (token.type === types.Ident && unitSet.has(token.value.toLowerCase())) {
697
+ return 1;
698
+ }
699
+
700
+ return 0;
701
+ };
702
+ }
703
+
663
704
  function createGenericTypes(units) {
664
705
  return {
665
706
  ...tokenTypes,
666
707
  ...productionTypes,
667
- ...createDemensionTypes(units)
708
+ ...createDemensionTypes(units),
709
+ 'attr-unit': createAttrUnit(units)
668
710
  };
669
711
  }
670
712
 
@@ -672,3 +714,4 @@ exports.createDemensionTypes = createDemensionTypes;
672
714
  exports.createGenericTypes = createGenericTypes;
673
715
  exports.productionTypes = productionTypes;
674
716
  exports.tokenTypes = tokenTypes;
717
+ exports.unitGroups = unitGroups;
@@ -17,6 +17,36 @@ const SEMICOLON = 0x003B; // U+003B SEMICOLON (;)
17
17
  const LEFTCURLYBRACKET = 0x007B; // U+007B LEFT CURLY BRACKET ({)
18
18
  const NULL = 0;
19
19
 
20
+ const arrayMethods = {
21
+ createList() {
22
+ return [];
23
+ },
24
+ createSingleNodeList(node) {
25
+ return [node];
26
+ },
27
+ getFirstListNode(list) {
28
+ return list && list[0] || null;
29
+ },
30
+ getLastListNode(list) {
31
+ return list && list.length > 0 ? list[list.length - 1] : null;
32
+ }
33
+ };
34
+
35
+ const listMethods = {
36
+ createList() {
37
+ return new List.List();
38
+ },
39
+ createSingleNodeList(node) {
40
+ return new List.List().appendData(node);
41
+ },
42
+ getFirstListNode(list) {
43
+ return list && list.first;
44
+ },
45
+ getLastListNode(list) {
46
+ return list && list.last;
47
+ }
48
+ };
49
+
20
50
  function createParseContext(name) {
21
51
  return function() {
22
52
  return this[name]();
@@ -98,18 +128,10 @@ function createParser(config) {
98
128
  return code === SEMICOLON ? 2 : 0;
99
129
  },
100
130
 
101
- createList() {
102
- return new List.List();
103
- },
104
- createSingleNodeList(node) {
105
- return new List.List().appendData(node);
106
- },
107
- getFirstListNode(list) {
108
- return list && list.first;
109
- },
110
- getLastListNode(list) {
111
- return list && list.last;
112
- },
131
+ createList: NOOP,
132
+ createSingleNodeList: NOOP,
133
+ getFirstListNode: NOOP,
134
+ getLastListNode: NOOP,
113
135
 
114
136
  parseWithFallback(consumer, fallback) {
115
137
  const startIndex = this.tokenIndex;
@@ -334,12 +356,14 @@ function createParser(config) {
334
356
  parser.parseValue = 'parseValue' in options ? Boolean(options.parseValue) : true;
335
357
  parser.parseCustomProperty = 'parseCustomProperty' in options ? Boolean(options.parseCustomProperty) : false;
336
358
 
337
- const { context = 'default', onComment, onToken } = options;
359
+ const { context = 'default', list = true, onComment, onToken } = options;
338
360
 
339
361
  if (context in parser.context === false) {
340
362
  throw new Error('Unknown context `' + context + '`');
341
363
  }
342
364
 
365
+ Object.assign(parser, list ? listMethods : arrayMethods);
366
+
343
367
  if (Array.isArray(onToken)) {
344
368
  parser.forEachToken((type, start, end) => {
345
369
  onToken.push({ type, start, end });
package/data/patch.json CHANGED
@@ -264,9 +264,10 @@
264
264
  "fill-opacity": {
265
265
  "comment": "added SVG property",
266
266
  "references": [
267
+ "https://developer.mozilla.org/en-US/docs/Web/CSS/fill-opacity",
267
268
  "https://www.w3.org/TR/SVG/painting.html#FillProperty"
268
269
  ],
269
- "syntax": "<number-zero-one>"
270
+ "syntax": "<number-zero-one> | <percentage>"
270
271
  },
271
272
  "filter": {
272
273
  "comment": "extend with IE legacy syntaxes",
@@ -346,6 +347,14 @@
346
347
  "comment": "extend by vendor keywords https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-y",
347
348
  "syntax": "| <-non-standard-overflow>"
348
349
  },
350
+ "overflow-block": {
351
+ "comment": "extend by vendor keywords https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-y",
352
+ "syntax": "| <-non-standard-overflow>"
353
+ },
354
+ "overflow-inline": {
355
+ "comment": "extend by vendor keywords https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-x",
356
+ "syntax": "| <-non-standard-overflow>"
357
+ },
349
358
  "pause": {
350
359
  "comment": "https://www.w3.org/TR/css3-speech/#property-index",
351
360
  "syntax": "<'pause-before'> <'pause-after'>?"
@@ -572,6 +581,9 @@
572
581
  "attr-fallback": {
573
582
  "syntax": "<any-value>"
574
583
  },
584
+ "autospace": {
585
+ "syntax": "no-autospace | [ ideograph-alpha || ideograph-numeric || punctuation ] || [ insert | replace ]"
586
+ },
575
587
  "bottom": {
576
588
  "comment": "missed; not sure we should add it, but no others except `shape` is using it so it's ok for now; https://drafts.fxtf.org/css-masking-1/#funcdef-clip-rect",
577
589
  "syntax": "<length> | auto"
@@ -917,6 +929,27 @@
917
929
  "references": [
918
930
  "https://drafts.csswg.org/css-anchor-position-1/#typedef-position-area"
919
931
  ]
932
+ },
933
+ "syntax": {
934
+ "syntax": "'*' | <syntax-component> [ <syntax-combinator> <syntax-component> ]* | <syntax-string>"
935
+ },
936
+ "syntax-component": {
937
+ "syntax": "<syntax-single-component> <syntax-multiplier>? | '<' transform-list '>'"
938
+ },
939
+ "syntax-single-component": {
940
+ "syntax": "'<' <syntax-type-name> '>' | <ident>"
941
+ },
942
+ "syntax-type-name": {
943
+ "syntax": "angle | color | custom-ident | image | integer | length | length-percentage | number | percentage | resolution | string | time | url | transform-function"
944
+ },
945
+ "syntax-combinator": {
946
+ "syntax": "'|'"
947
+ },
948
+ "syntax-multiplier": {
949
+ "syntax": "'#' | '+'"
950
+ },
951
+ "syntax-string": {
952
+ "syntax": "<string>"
920
953
  }
921
954
  }
922
955
  }