@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.
@@ -1,5 +1,4 @@
1
1
  import {
2
- WhiteSpace,
3
2
  Delim,
4
3
  Ident,
5
4
  Function as FunctionToken,
@@ -20,17 +19,20 @@ import {
20
19
  const PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+)
21
20
  const HYPHENMINUS = 0x002D; // U+002D HYPHEN-MINUS (-)
22
21
 
22
+ // code:
23
+ // 0xxxxxxx x0000000 - char code (0x80 for non-ASCII) or delim value
24
+ // 00000000 0xxxxxx0 - token type (0 for delim, 1 for token)
25
+ // 00000000 0000000x - reserved for carriage emit flag (0 for no space, 1 for space)
23
26
  const code = (type, value) => {
24
27
  if (type === Delim) {
25
28
  type = value;
26
29
  }
27
30
 
28
31
  if (typeof type === 'string') {
29
- const charCode = type.charCodeAt(0);
30
- return charCode > 0x7F ? 0x8000 : charCode << 8;
32
+ type = Math.min(type.charCodeAt(0), 0x80) << 6; // replace non-ASCII with 0x80
31
33
  }
32
34
 
33
- return type;
35
+ return type << 1;
34
36
  };
35
37
 
36
38
  // https://www.w3.org/TR/css-syntax-3/#serialization
@@ -167,14 +169,10 @@ function createMap(pairs) {
167
169
  type !== FunctionToken &&
168
170
  type !== CDC) ||
169
171
  (nextCharCode === PLUSSIGN)
170
- ? isWhiteSpaceRequired.has(prevCode << 16 | nextCharCode << 8)
171
- : isWhiteSpaceRequired.has(prevCode << 16 | nextCode);
172
+ ? isWhiteSpaceRequired.has((prevCode & 0xFFFE) << 16 | nextCharCode << 7)
173
+ : isWhiteSpaceRequired.has((prevCode & 0xFFFE) << 16 | nextCode);
172
174
 
173
- if (emitWs) {
174
- this.emit(' ', WhiteSpace, true);
175
- }
176
-
177
- return nextCode;
175
+ return nextCode | emitWs;
178
176
  };
179
177
  }
180
178
 
package/lib/index.d.ts CHANGED
@@ -2921,14 +2921,64 @@ interface SyntaxMatchGraph {
2921
2921
  source: DSNode;
2922
2922
  }
2923
2923
 
2924
+ /**
2925
+ * Kinds of syntax descriptors created by the lexer.
2926
+ */
2927
+ type SyntaxDescriptorType = "AtruleDescriptor" | "AtrulePrelude" | "Property" | "Type";
2928
+
2929
+ /**
2930
+ * The built-in unit groups stored on a `Lexer` instance.
2931
+ */
2932
+ interface LexerUnits {
2933
+ /**
2934
+ * Length units such as `px`, `em`, and `vh`.
2935
+ */
2936
+ length: string[];
2937
+
2938
+ /**
2939
+ * Angle units such as `deg` and `rad`.
2940
+ */
2941
+ angle: string[];
2942
+
2943
+ /**
2944
+ * Time units such as `s` and `ms`.
2945
+ */
2946
+ time: string[];
2947
+
2948
+ /**
2949
+ * Frequency units such as `hz` and `khz`.
2950
+ */
2951
+ frequency: string[];
2952
+
2953
+ /**
2954
+ * Resolution units such as `dpi` and `dppx`.
2955
+ */
2956
+ resolution: string[];
2957
+
2958
+ /**
2959
+ * Flex units such as `fr`.
2960
+ */
2961
+ flex: string[];
2962
+
2963
+ /**
2964
+ * Volume-related decibel units.
2965
+ */
2966
+ decibel: string[];
2967
+
2968
+ /**
2969
+ * Pitch-related semitone units.
2970
+ */
2971
+ semitones: string[];
2972
+ }
2973
+
2924
2974
  /**
2925
2975
  * Describes a syntax rule or definition.
2926
2976
  */
2927
- type SyntaxDescriptor = {
2977
+ type SyntaxDescriptor<Kind extends SyntaxDescriptorType = SyntaxDescriptorType> = {
2928
2978
  /**
2929
2979
  * The type of the syntax descriptor (e.g., "Type", "Property").
2930
2980
  */
2931
- type: string;
2981
+ type: Kind;
2932
2982
 
2933
2983
  /**
2934
2984
  * The name of the syntax descriptor.
@@ -2946,9 +2996,9 @@ type SyntaxDescriptor = {
2946
2996
  serializable: boolean;
2947
2997
 
2948
2998
  /**
2949
- * The syntax object associated with the descriptor.
2999
+ * The parsed definition-syntax AST, or `null` for function-backed descriptors.
2950
3000
  */
2951
- syntax: Syntax;
3001
+ syntax: DSNode | null;
2952
3002
 
2953
3003
  /**
2954
3004
  * The graph of syntax matches for this descriptor, or `null` if none exists.
@@ -2958,9 +3008,34 @@ type SyntaxDescriptor = {
2958
3008
  /**
2959
3009
  * A reference to a graph of syntax matches, or `null` if none exists.
2960
3010
  */
2961
- matchRef?: SyntaxMatchGraph | null;
3011
+ matchRef: SyntaxMatchGraph | null;
2962
3012
  };
2963
3013
 
3014
+ /**
3015
+ * An at-rule definition exposed through `Lexer#atrules`.
3016
+ */
3017
+ interface LexerAtruleDefinition {
3018
+ /**
3019
+ * Discriminator for at-rule entries.
3020
+ */
3021
+ type: "Atrule";
3022
+
3023
+ /**
3024
+ * The at-rule name.
3025
+ */
3026
+ name: string;
3027
+
3028
+ /**
3029
+ * The descriptor for the at-rule prelude, if one exists.
3030
+ */
3031
+ prelude: SyntaxDescriptor<"AtrulePrelude"> | null;
3032
+
3033
+ /**
3034
+ * At-rule descriptors keyed by descriptor name, if any.
3035
+ */
3036
+ descriptors: Record<string, SyntaxDescriptor<"AtruleDescriptor"> | undefined> | null;
3037
+ }
3038
+
2964
3039
  /**
2965
3040
  * Represents a fragment match, including its parent list and nodes.
2966
3041
  */
@@ -2996,6 +3071,46 @@ type LexerValidationResult = {
2996
3071
  * It provides utilities for handling at-rules, properties, types, and general syntax.
2997
3072
  */
2998
3073
  export class Lexer {
3074
+ /**
3075
+ * CSS-wide keywords accepted by the lexer.
3076
+ */
3077
+ cssWideKeywords: string[];
3078
+
3079
+ /**
3080
+ * The associated `Syntax` instance, if the lexer was created from one.
3081
+ */
3082
+ syntax: Syntax | undefined;
3083
+
3084
+ /**
3085
+ * Whether generic types are enabled for this lexer.
3086
+ */
3087
+ generic: boolean;
3088
+
3089
+ /**
3090
+ * Known CSS units grouped by unit family.
3091
+ */
3092
+ units: LexerUnits;
3093
+
3094
+ /**
3095
+ * Known at-rule definitions keyed by at-rule name.
3096
+ */
3097
+ atrules: Record<string, LexerAtruleDefinition | undefined>;
3098
+
3099
+ /**
3100
+ * Known property definitions keyed by property name.
3101
+ */
3102
+ properties: Record<string, SyntaxDescriptor<"Property"> | undefined>;
3103
+
3104
+ /**
3105
+ * Known type definitions keyed by type name.
3106
+ */
3107
+ types: Record<string, SyntaxDescriptor<"Type"> | undefined>;
3108
+
3109
+ /**
3110
+ * Node structure validators used by `checkStructure()`.
3111
+ */
3112
+ structure: Structure;
3113
+
2999
3114
  /**
3000
3115
  * Creates a new `Lexer` instance.
3001
3116
  *
@@ -3147,13 +3262,13 @@ export class Lexer {
3147
3262
  findAllFragments(ast: AnyCssNode, type: string, name: string): FragmentMatch[];
3148
3263
 
3149
3264
  /**
3150
- * Retrieves the syntax descriptor for an at-rule.
3265
+ * Retrieves the definition for an at-rule.
3151
3266
  *
3152
3267
  * @param atruleName - The name of the at-rule.
3153
3268
  * @param fallbackBasename - (Optional) Whether to use a fallback basename.
3154
- * @returns The syntax descriptor or `null` if not found.
3269
+ * @returns The at-rule definition or `null` if not found.
3155
3270
  */
3156
- getAtrule(atruleName: string, fallbackBasename?: boolean): SyntaxDescriptor | null;
3271
+ getAtrule(atruleName: string, fallbackBasename?: boolean): LexerAtruleDefinition | null;
3157
3272
 
3158
3273
  /**
3159
3274
  * Retrieves the prelude descriptor for an at-rule.
@@ -3162,7 +3277,7 @@ export class Lexer {
3162
3277
  * @param fallbackBasename - (Optional) Whether to use a fallback basename.
3163
3278
  * @returns The syntax descriptor or `null` if not found.
3164
3279
  */
3165
- getAtrulePrelude(atruleName: string, fallbackBasename?: boolean): SyntaxDescriptor | null;
3280
+ getAtrulePrelude(atruleName: string, fallbackBasename?: boolean): SyntaxDescriptor<"AtrulePrelude"> | null;
3166
3281
 
3167
3282
  /**
3168
3283
  * Retrieves the descriptor for an at-rule's property.
@@ -3171,7 +3286,7 @@ export class Lexer {
3171
3286
  * @param name - The property name.
3172
3287
  * @returns The syntax descriptor or `null` if not found.
3173
3288
  */
3174
- getAtruleDescriptor(atruleName: string, name: string): SyntaxDescriptor | null;
3289
+ getAtruleDescriptor(atruleName: string, name: string): SyntaxDescriptor<"AtruleDescriptor"> | null;
3175
3290
 
3176
3291
  /**
3177
3292
  * Retrieves the syntax descriptor for a property.
@@ -3180,7 +3295,7 @@ export class Lexer {
3180
3295
  * @param fallbackBasename - (Optional) Whether to use a fallback basename.
3181
3296
  * @returns The syntax descriptor or `null` if not found.
3182
3297
  */
3183
- getProperty(propertyName: string, fallbackBasename?: boolean): SyntaxDescriptor | null;
3298
+ getProperty(propertyName: string, fallbackBasename?: boolean): SyntaxDescriptor<"Property"> | null;
3184
3299
 
3185
3300
  /**
3186
3301
  * Retrieves the syntax descriptor for a type.
@@ -3188,7 +3303,7 @@ export class Lexer {
3188
3303
  * @param name - The name of the type.
3189
3304
  * @returns The syntax descriptor or `null` if not found.
3190
3305
  */
3191
- getType(name: string): SyntaxDescriptor | null;
3306
+ getType(name: string): SyntaxDescriptor<"Type"> | null;
3192
3307
 
3193
3308
  /**
3194
3309
  * Validates the syntax rules and properties.
@@ -439,9 +439,13 @@ export class Lexer {
439
439
  return atrule && atrule.prelude || null;
440
440
  }
441
441
  getAtruleDescriptor(atruleName, name) {
442
- return this.atrules.hasOwnProperty(atruleName) && this.atrules.declarators
443
- ? this.atrules[atruleName].declarators[name] || null
442
+ const atrule = this.getAtrule(atruleName);
443
+ const descriptor = names.keyword(name);
444
+ const descriptorEntry = atrule && atrule.descriptors
445
+ ? atrule.descriptors[descriptor.name] || atrule.descriptors[descriptor.basename]
444
446
  : null;
447
+
448
+ return descriptorEntry || null;
445
449
  }
446
450
  getProperty(propertyName, fallbackBasename = true) {
447
451
  const property = names.property(propertyName);
@@ -698,10 +698,41 @@ export function createDemensionTypes(units) {
698
698
  };
699
699
  }
700
700
 
701
+ // The <attr-unit> production matches any identifier that is an ASCII case-insensitive
702
+ // match for the name of a CSS dimension unit, such as px, or the <delim-token> %.
703
+ function createAttrUnit(units) {
704
+ const unitSet = new Set();
705
+
706
+ for (const group of unitGroups) {
707
+ if (Array.isArray(units[group])) {
708
+ for (const unit of units[group]) {
709
+ unitSet.add(unit.toLowerCase());
710
+ }
711
+ }
712
+ }
713
+
714
+ return function attrUnit(token) {
715
+ if (token === null) {
716
+ return 0;
717
+ }
718
+
719
+ if (token.type === Delim && token.value === '%') {
720
+ return 1;
721
+ }
722
+
723
+ if (token.type === Ident && unitSet.has(token.value.toLowerCase())) {
724
+ return 1;
725
+ }
726
+
727
+ return 0;
728
+ };
729
+ }
730
+
701
731
  export function createGenericTypes(units) {
702
732
  return {
703
733
  ...tokenTypes,
704
734
  ...productionTypes,
705
- ...createDemensionTypes(units)
735
+ ...createDemensionTypes(units),
736
+ 'attr-unit': createAttrUnit(units)
706
737
  };
707
738
  };
@@ -29,6 +29,36 @@ const SEMICOLON = 0x003B; // U+003B SEMICOLON (;)
29
29
  const LEFTCURLYBRACKET = 0x007B; // U+007B LEFT CURLY BRACKET ({)
30
30
  const NULL = 0;
31
31
 
32
+ const arrayMethods = {
33
+ createList() {
34
+ return [];
35
+ },
36
+ createSingleNodeList(node) {
37
+ return [node];
38
+ },
39
+ getFirstListNode(list) {
40
+ return list && list[0] || null;
41
+ },
42
+ getLastListNode(list) {
43
+ return list && list.length > 0 ? list[list.length - 1] : null;
44
+ }
45
+ };
46
+
47
+ const listMethods = {
48
+ createList() {
49
+ return new List();
50
+ },
51
+ createSingleNodeList(node) {
52
+ return new List().appendData(node);
53
+ },
54
+ getFirstListNode(list) {
55
+ return list && list.first;
56
+ },
57
+ getLastListNode(list) {
58
+ return list && list.last;
59
+ }
60
+ };
61
+
32
62
  function createParseContext(name) {
33
63
  return function() {
34
64
  return this[name]();
@@ -110,18 +140,10 @@ export function createParser(config) {
110
140
  return code === SEMICOLON ? 2 : 0;
111
141
  },
112
142
 
113
- createList() {
114
- return new List();
115
- },
116
- createSingleNodeList(node) {
117
- return new List().appendData(node);
118
- },
119
- getFirstListNode(list) {
120
- return list && list.first;
121
- },
122
- getLastListNode(list) {
123
- return list && list.last;
124
- },
143
+ createList: NOOP,
144
+ createSingleNodeList: NOOP,
145
+ getFirstListNode: NOOP,
146
+ getLastListNode: NOOP,
125
147
 
126
148
  parseWithFallback(consumer, fallback) {
127
149
  const startIndex = this.tokenIndex;
@@ -346,12 +368,14 @@ export function createParser(config) {
346
368
  parser.parseValue = 'parseValue' in options ? Boolean(options.parseValue) : true;
347
369
  parser.parseCustomProperty = 'parseCustomProperty' in options ? Boolean(options.parseCustomProperty) : false;
348
370
 
349
- const { context = 'default', onComment, onToken } = options;
371
+ const { context = 'default', list = true, onComment, onToken } = options;
350
372
 
351
373
  if (context in parser.context === false) {
352
374
  throw new Error('Unknown context `' + context + '`');
353
375
  }
354
376
 
377
+ Object.assign(parser, list ? listMethods : arrayMethods);
378
+
355
379
  if (Array.isArray(onToken)) {
356
380
  parser.forEachToken((type, start, end) => {
357
381
  onToken.push({ type, start, end });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eslint/css-tree",
3
- "version": "3.6.9",
3
+ "version": "4.0.1",
4
4
  "description": "A tool set for CSS: fast detailed parser (CSS → AST), walker (AST traversal), generator (AST → CSS) and lexer (validation and matching) based on specs and browser implementations",
5
5
  "author": "Roman Dvornov <rdvornov@gmail.com> (https://github.com/lahmatiy)",
6
6
  "license": "MIT",
@@ -107,21 +107,21 @@
107
107
  "test:types": "tsc -p tests/types/tsconfig.json"
108
108
  },
109
109
  "dependencies": {
110
- "mdn-data": "2.23.0",
111
- "source-map-js": "^1.0.1"
110
+ "mdn-data": "2.27.1",
111
+ "source-map-js": "^1.2.1"
112
112
  },
113
113
  "devDependencies": {
114
- "c8": "^7.12.0",
114
+ "c8": "^11.0.0",
115
115
  "clap": "^2.0.1",
116
- "esbuild": "^0.24.0",
117
- "eslint": "^8.4.1",
116
+ "esbuild": "^0.27.3",
117
+ "eslint": "^8.50.0",
118
118
  "json-to-ast": "^2.1.0",
119
119
  "mocha": "^9.2.2",
120
- "rollup": "^2.79.2",
120
+ "rollup": "^2.80.0",
121
121
  "typescript": "^5.7.3"
122
122
  },
123
123
  "engines": {
124
- "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
124
+ "node": "^20.19.0 || ^22.13.0 || >=24"
125
125
  },
126
126
  "files": [
127
127
  "data",