@eslint/css-tree 3.2.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.
Files changed (279) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +194 -0
  3. package/cjs/convertor/create.cjs +32 -0
  4. package/cjs/convertor/index.cjs +8 -0
  5. package/cjs/data-patch.cjs +7 -0
  6. package/cjs/data.cjs +120 -0
  7. package/cjs/definition-syntax/SyntaxError.cjs +16 -0
  8. package/cjs/definition-syntax/generate.cjs +139 -0
  9. package/cjs/definition-syntax/index.cjs +13 -0
  10. package/cjs/definition-syntax/parse.cjs +556 -0
  11. package/cjs/definition-syntax/scanner.cjs +113 -0
  12. package/cjs/definition-syntax/walk.cjs +57 -0
  13. package/cjs/generator/create.cjs +102 -0
  14. package/cjs/generator/index.cjs +8 -0
  15. package/cjs/generator/sourceMap.cjs +96 -0
  16. package/cjs/generator/token-before.cjs +170 -0
  17. package/cjs/index.cjs +65 -0
  18. package/cjs/lexer/Lexer.cjs +517 -0
  19. package/cjs/lexer/error.cjs +128 -0
  20. package/cjs/lexer/generic-an-plus-b.cjs +235 -0
  21. package/cjs/lexer/generic-const.cjs +12 -0
  22. package/cjs/lexer/generic-urange.cjs +149 -0
  23. package/cjs/lexer/generic.cjs +589 -0
  24. package/cjs/lexer/index.cjs +7 -0
  25. package/cjs/lexer/match-graph.cjs +530 -0
  26. package/cjs/lexer/match.cjs +632 -0
  27. package/cjs/lexer/prepare-tokens.cjs +54 -0
  28. package/cjs/lexer/search.cjs +65 -0
  29. package/cjs/lexer/structure.cjs +173 -0
  30. package/cjs/lexer/trace.cjs +73 -0
  31. package/cjs/lexer/units.cjs +38 -0
  32. package/cjs/parser/SyntaxError.cjs +74 -0
  33. package/cjs/parser/create.cjs +378 -0
  34. package/cjs/parser/index.cjs +8 -0
  35. package/cjs/parser/parse-selector.cjs +8 -0
  36. package/cjs/parser/sequence.cjs +47 -0
  37. package/cjs/syntax/atrule/container.cjs +32 -0
  38. package/cjs/syntax/atrule/font-face.cjs +12 -0
  39. package/cjs/syntax/atrule/import.cjs +101 -0
  40. package/cjs/syntax/atrule/index.cjs +27 -0
  41. package/cjs/syntax/atrule/layer.cjs +16 -0
  42. package/cjs/syntax/atrule/media.cjs +16 -0
  43. package/cjs/syntax/atrule/nest.cjs +16 -0
  44. package/cjs/syntax/atrule/page.cjs +16 -0
  45. package/cjs/syntax/atrule/scope.cjs +16 -0
  46. package/cjs/syntax/atrule/starting-style.cjs +12 -0
  47. package/cjs/syntax/atrule/supports.cjs +16 -0
  48. package/cjs/syntax/config/generator.cjs +9 -0
  49. package/cjs/syntax/config/lexer.cjs +14 -0
  50. package/cjs/syntax/config/mix.cjs +126 -0
  51. package/cjs/syntax/config/parser-selector.cjs +19 -0
  52. package/cjs/syntax/config/parser.cjs +49 -0
  53. package/cjs/syntax/config/walker.cjs +9 -0
  54. package/cjs/syntax/create.cjs +58 -0
  55. package/cjs/syntax/function/expression.cjs +11 -0
  56. package/cjs/syntax/function/var.cjs +43 -0
  57. package/cjs/syntax/index.cjs +14 -0
  58. package/cjs/syntax/node/AnPlusB.cjs +293 -0
  59. package/cjs/syntax/node/Atrule.cjs +103 -0
  60. package/cjs/syntax/node/AtrulePrelude.cjs +52 -0
  61. package/cjs/syntax/node/AttributeSelector.cjs +148 -0
  62. package/cjs/syntax/node/Block.cjs +96 -0
  63. package/cjs/syntax/node/Brackets.cjs +38 -0
  64. package/cjs/syntax/node/CDC.cjs +26 -0
  65. package/cjs/syntax/node/CDO.cjs +26 -0
  66. package/cjs/syntax/node/ClassSelector.cjs +31 -0
  67. package/cjs/syntax/node/Combinator.cjs +61 -0
  68. package/cjs/syntax/node/Comment.cjs +40 -0
  69. package/cjs/syntax/node/Condition.cjs +120 -0
  70. package/cjs/syntax/node/Declaration.cjs +166 -0
  71. package/cjs/syntax/node/DeclarationList.cjs +62 -0
  72. package/cjs/syntax/node/Dimension.cjs +30 -0
  73. package/cjs/syntax/node/Feature.cjs +101 -0
  74. package/cjs/syntax/node/FeatureFunction.cjs +67 -0
  75. package/cjs/syntax/node/FeatureRange.cjs +133 -0
  76. package/cjs/syntax/node/Function.cjs +45 -0
  77. package/cjs/syntax/node/GeneralEnclosed.cjs +68 -0
  78. package/cjs/syntax/node/Hash.cjs +30 -0
  79. package/cjs/syntax/node/IdSelector.cjs +33 -0
  80. package/cjs/syntax/node/Identifier.cjs +25 -0
  81. package/cjs/syntax/node/Layer.cjs +35 -0
  82. package/cjs/syntax/node/LayerList.cjs +43 -0
  83. package/cjs/syntax/node/MediaQuery.cjs +100 -0
  84. package/cjs/syntax/node/MediaQueryList.cjs +41 -0
  85. package/cjs/syntax/node/NestingSelector.cjs +29 -0
  86. package/cjs/syntax/node/Nth.cjs +54 -0
  87. package/cjs/syntax/node/Number.cjs +25 -0
  88. package/cjs/syntax/node/Operator.cjs +28 -0
  89. package/cjs/syntax/node/Parentheses.cjs +38 -0
  90. package/cjs/syntax/node/Percentage.cjs +25 -0
  91. package/cjs/syntax/node/PseudoClassSelector.cjs +67 -0
  92. package/cjs/syntax/node/PseudoElementSelector.cjs +69 -0
  93. package/cjs/syntax/node/Ratio.cjs +71 -0
  94. package/cjs/syntax/node/Raw.cjs +48 -0
  95. package/cjs/syntax/node/Rule.cjs +58 -0
  96. package/cjs/syntax/node/Scope.cjs +69 -0
  97. package/cjs/syntax/node/Selector.cjs +38 -0
  98. package/cjs/syntax/node/SelectorList.cjs +43 -0
  99. package/cjs/syntax/node/String.cjs +26 -0
  100. package/cjs/syntax/node/StyleSheet.cjs +83 -0
  101. package/cjs/syntax/node/SupportsDeclaration.cjs +38 -0
  102. package/cjs/syntax/node/TypeSelector.cjs +59 -0
  103. package/cjs/syntax/node/UnicodeRange.cjs +158 -0
  104. package/cjs/syntax/node/Url.cjs +54 -0
  105. package/cjs/syntax/node/Value.cjs +26 -0
  106. package/cjs/syntax/node/WhiteSpace.cjs +34 -0
  107. package/cjs/syntax/node/index-generate.cjs +103 -0
  108. package/cjs/syntax/node/index-parse-selector.cjs +39 -0
  109. package/cjs/syntax/node/index-parse.cjs +103 -0
  110. package/cjs/syntax/node/index.cjs +103 -0
  111. package/cjs/syntax/pseudo/index.cjs +60 -0
  112. package/cjs/syntax/pseudo/lang.cjs +37 -0
  113. package/cjs/syntax/scope/atrulePrelude.cjs +9 -0
  114. package/cjs/syntax/scope/default.cjs +76 -0
  115. package/cjs/syntax/scope/index.cjs +11 -0
  116. package/cjs/syntax/scope/selector.cjs +88 -0
  117. package/cjs/syntax/scope/value.cjs +29 -0
  118. package/cjs/tokenizer/OffsetToLocation.cjs +91 -0
  119. package/cjs/tokenizer/TokenStream.cjs +358 -0
  120. package/cjs/tokenizer/adopt-buffer.cjs +13 -0
  121. package/cjs/tokenizer/char-code-definitions.cjs +236 -0
  122. package/cjs/tokenizer/index.cjs +554 -0
  123. package/cjs/tokenizer/names.cjs +32 -0
  124. package/cjs/tokenizer/types.cjs +57 -0
  125. package/cjs/tokenizer/utils.cjs +261 -0
  126. package/cjs/utils/List.cjs +473 -0
  127. package/cjs/utils/clone.cjs +25 -0
  128. package/cjs/utils/create-custom-error.cjs +18 -0
  129. package/cjs/utils/ident.cjs +102 -0
  130. package/cjs/utils/index.cjs +20 -0
  131. package/cjs/utils/names.cjs +113 -0
  132. package/cjs/utils/string.cjs +99 -0
  133. package/cjs/utils/url.cjs +108 -0
  134. package/cjs/version.cjs +5 -0
  135. package/cjs/walker/create.cjs +291 -0
  136. package/cjs/walker/index.cjs +8 -0
  137. package/data/patch.json +874 -0
  138. package/dist/csstree.esm.js +12 -0
  139. package/dist/csstree.js +12 -0
  140. package/dist/data.cjs +1341 -0
  141. package/dist/data.js +1341 -0
  142. package/dist/version.cjs +1 -0
  143. package/dist/version.js +1 -0
  144. package/lib/convertor/create.js +28 -0
  145. package/lib/convertor/index.js +4 -0
  146. package/lib/data-patch.js +6 -0
  147. package/lib/data.js +118 -0
  148. package/lib/definition-syntax/SyntaxError.js +12 -0
  149. package/lib/definition-syntax/generate.js +135 -0
  150. package/lib/definition-syntax/index.js +4 -0
  151. package/lib/definition-syntax/parse.js +552 -0
  152. package/lib/definition-syntax/scanner.js +109 -0
  153. package/lib/definition-syntax/walk.js +53 -0
  154. package/lib/generator/create.js +97 -0
  155. package/lib/generator/index.js +4 -0
  156. package/lib/generator/sourceMap.js +92 -0
  157. package/lib/generator/token-before.js +182 -0
  158. package/lib/index.d.ts +3016 -0
  159. package/lib/index.js +30 -0
  160. package/lib/lexer/Lexer.js +511 -0
  161. package/lib/lexer/error.js +123 -0
  162. package/lib/lexer/generic-an-plus-b.js +238 -0
  163. package/lib/lexer/generic-const.js +8 -0
  164. package/lib/lexer/generic-urange.js +151 -0
  165. package/lib/lexer/generic.js +622 -0
  166. package/lib/lexer/index.js +1 -0
  167. package/lib/lexer/match-graph.js +527 -0
  168. package/lib/lexer/match.js +630 -0
  169. package/lib/lexer/prepare-tokens.js +50 -0
  170. package/lib/lexer/search.js +61 -0
  171. package/lib/lexer/structure.js +169 -0
  172. package/lib/lexer/trace.js +66 -0
  173. package/lib/lexer/units.js +27 -0
  174. package/lib/parser/SyntaxError.js +70 -0
  175. package/lib/parser/create.js +388 -0
  176. package/lib/parser/index.js +4 -0
  177. package/lib/parser/parse-selector.js +4 -0
  178. package/lib/parser/sequence.js +43 -0
  179. package/lib/syntax/atrule/container.js +28 -0
  180. package/lib/syntax/atrule/font-face.js +8 -0
  181. package/lib/syntax/atrule/import.js +104 -0
  182. package/lib/syntax/atrule/index.js +23 -0
  183. package/lib/syntax/atrule/layer.js +12 -0
  184. package/lib/syntax/atrule/media.js +12 -0
  185. package/lib/syntax/atrule/nest.js +12 -0
  186. package/lib/syntax/atrule/page.js +12 -0
  187. package/lib/syntax/atrule/scope.js +12 -0
  188. package/lib/syntax/atrule/starting-style.js +8 -0
  189. package/lib/syntax/atrule/supports.js +12 -0
  190. package/lib/syntax/config/generator.js +5 -0
  191. package/lib/syntax/config/lexer.js +10 -0
  192. package/lib/syntax/config/mix.js +122 -0
  193. package/lib/syntax/config/parser-selector.js +15 -0
  194. package/lib/syntax/config/parser.js +45 -0
  195. package/lib/syntax/config/walker.js +5 -0
  196. package/lib/syntax/create.js +55 -0
  197. package/lib/syntax/function/expression.js +7 -0
  198. package/lib/syntax/function/var.js +39 -0
  199. package/lib/syntax/index.js +10 -0
  200. package/lib/syntax/node/AnPlusB.js +292 -0
  201. package/lib/syntax/node/Atrule.js +100 -0
  202. package/lib/syntax/node/AtrulePrelude.js +47 -0
  203. package/lib/syntax/node/AttributeSelector.js +147 -0
  204. package/lib/syntax/node/Block.js +95 -0
  205. package/lib/syntax/node/Brackets.js +35 -0
  206. package/lib/syntax/node/CDC.js +19 -0
  207. package/lib/syntax/node/CDO.js +19 -0
  208. package/lib/syntax/node/ClassSelector.js +24 -0
  209. package/lib/syntax/node/Combinator.js +54 -0
  210. package/lib/syntax/node/Comment.js +33 -0
  211. package/lib/syntax/node/Condition.js +123 -0
  212. package/lib/syntax/node/Declaration.js +165 -0
  213. package/lib/syntax/node/DeclarationList.js +62 -0
  214. package/lib/syntax/node/Dimension.js +23 -0
  215. package/lib/syntax/node/Feature.js +103 -0
  216. package/lib/syntax/node/FeatureFunction.js +63 -0
  217. package/lib/syntax/node/FeatureRange.js +133 -0
  218. package/lib/syntax/node/Function.js +41 -0
  219. package/lib/syntax/node/GeneralEnclosed.js +66 -0
  220. package/lib/syntax/node/Hash.js +23 -0
  221. package/lib/syntax/node/IdSelector.js +26 -0
  222. package/lib/syntax/node/Identifier.js +18 -0
  223. package/lib/syntax/node/Layer.js +28 -0
  224. package/lib/syntax/node/LayerList.js +36 -0
  225. package/lib/syntax/node/MediaQuery.js +102 -0
  226. package/lib/syntax/node/MediaQueryList.js +34 -0
  227. package/lib/syntax/node/NestingSelector.js +22 -0
  228. package/lib/syntax/node/Nth.js +47 -0
  229. package/lib/syntax/node/Number.js +18 -0
  230. package/lib/syntax/node/Operator.js +21 -0
  231. package/lib/syntax/node/Parentheses.js +34 -0
  232. package/lib/syntax/node/Percentage.js +18 -0
  233. package/lib/syntax/node/PseudoClassSelector.js +65 -0
  234. package/lib/syntax/node/PseudoElementSelector.js +66 -0
  235. package/lib/syntax/node/Ratio.js +68 -0
  236. package/lib/syntax/node/Raw.js +41 -0
  237. package/lib/syntax/node/Rule.js +51 -0
  238. package/lib/syntax/node/Scope.js +66 -0
  239. package/lib/syntax/node/Selector.js +31 -0
  240. package/lib/syntax/node/SelectorList.js +35 -0
  241. package/lib/syntax/node/String.js +19 -0
  242. package/lib/syntax/node/StyleSheet.js +82 -0
  243. package/lib/syntax/node/SupportsDeclaration.js +34 -0
  244. package/lib/syntax/node/TypeSelector.js +52 -0
  245. package/lib/syntax/node/UnicodeRange.js +156 -0
  246. package/lib/syntax/node/Url.js +52 -0
  247. package/lib/syntax/node/Value.js +19 -0
  248. package/lib/syntax/node/WhiteSpace.js +27 -0
  249. package/lib/syntax/node/index-generate.js +49 -0
  250. package/lib/syntax/node/index-parse-selector.js +17 -0
  251. package/lib/syntax/node/index-parse.js +49 -0
  252. package/lib/syntax/node/index.js +49 -0
  253. package/lib/syntax/pseudo/index.js +56 -0
  254. package/lib/syntax/pseudo/lang.js +33 -0
  255. package/lib/syntax/scope/atrulePrelude.js +5 -0
  256. package/lib/syntax/scope/default.js +85 -0
  257. package/lib/syntax/scope/index.js +3 -0
  258. package/lib/syntax/scope/selector.js +94 -0
  259. package/lib/syntax/scope/value.js +25 -0
  260. package/lib/tokenizer/OffsetToLocation.js +87 -0
  261. package/lib/tokenizer/TokenStream.js +366 -0
  262. package/lib/tokenizer/adopt-buffer.js +9 -0
  263. package/lib/tokenizer/char-code-definitions.js +212 -0
  264. package/lib/tokenizer/index.js +513 -0
  265. package/lib/tokenizer/names.js +28 -0
  266. package/lib/tokenizer/types.js +28 -0
  267. package/lib/tokenizer/utils.js +254 -0
  268. package/lib/utils/List.js +469 -0
  269. package/lib/utils/clone.js +21 -0
  270. package/lib/utils/create-custom-error.js +14 -0
  271. package/lib/utils/ident.js +101 -0
  272. package/lib/utils/index.js +6 -0
  273. package/lib/utils/names.js +106 -0
  274. package/lib/utils/string.js +99 -0
  275. package/lib/utils/url.js +108 -0
  276. package/lib/version.js +5 -0
  277. package/lib/walker/create.js +287 -0
  278. package/lib/walker/index.js +4 -0
  279. package/package.json +133 -0
@@ -0,0 +1,527 @@
1
+ import { parse } from '../definition-syntax/parse.js';
2
+
3
+ export const MATCH = { type: 'Match' };
4
+ export const MISMATCH = { type: 'Mismatch' };
5
+ export const DISALLOW_EMPTY = { type: 'DisallowEmpty' };
6
+
7
+ const LEFTPARENTHESIS = 40; // (
8
+ const RIGHTPARENTHESIS = 41; // )
9
+
10
+ function createCondition(match, thenBranch, elseBranch) {
11
+ // reduce node count
12
+ if (thenBranch === MATCH && elseBranch === MISMATCH) {
13
+ return match;
14
+ }
15
+
16
+ if (match === MATCH && thenBranch === MATCH && elseBranch === MATCH) {
17
+ return match;
18
+ }
19
+
20
+ if (match.type === 'If' && match.else === MISMATCH && thenBranch === MATCH) {
21
+ thenBranch = match.then;
22
+ match = match.match;
23
+ }
24
+
25
+ return {
26
+ type: 'If',
27
+ match,
28
+ then: thenBranch,
29
+ else: elseBranch
30
+ };
31
+ }
32
+
33
+ function isFunctionType(name) {
34
+ return (
35
+ name.length > 2 &&
36
+ name.charCodeAt(name.length - 2) === LEFTPARENTHESIS &&
37
+ name.charCodeAt(name.length - 1) === RIGHTPARENTHESIS
38
+ );
39
+ }
40
+
41
+ function isEnumCapatible(term) {
42
+ return (
43
+ term.type === 'Keyword' ||
44
+ term.type === 'AtKeyword' ||
45
+ term.type === 'Function' ||
46
+ term.type === 'Type' && isFunctionType(term.name)
47
+ );
48
+ }
49
+
50
+ function groupNode(terms, combinator = ' ', explicit = false) {
51
+ return {
52
+ type: 'Group',
53
+ terms,
54
+ combinator,
55
+ disallowEmpty: false,
56
+ explicit
57
+ };
58
+ }
59
+
60
+ function replaceTypeInGraph(node, replacements, visited = new Set()) {
61
+ if (!visited.has(node)) {
62
+ visited.add(node);
63
+
64
+ switch (node.type) {
65
+ case 'If':
66
+ node.match = replaceTypeInGraph(node.match, replacements, visited);
67
+ node.then = replaceTypeInGraph(node.then, replacements, visited);
68
+ node.else = replaceTypeInGraph(node.else, replacements, visited);
69
+ break;
70
+
71
+ case 'Type':
72
+ return replacements[node.name] || node;
73
+ }
74
+ }
75
+
76
+ return node;
77
+ }
78
+
79
+ function buildGroupMatchGraph(combinator, terms, atLeastOneTermMatched) {
80
+ switch (combinator) {
81
+ case ' ': {
82
+ // Juxtaposing components means that all of them must occur, in the given order.
83
+ //
84
+ // a b c
85
+ // =
86
+ // match a
87
+ // then match b
88
+ // then match c
89
+ // then MATCH
90
+ // else MISMATCH
91
+ // else MISMATCH
92
+ // else MISMATCH
93
+ let result = MATCH;
94
+
95
+ for (let i = terms.length - 1; i >= 0; i--) {
96
+ const term = terms[i];
97
+
98
+ result = createCondition(
99
+ term,
100
+ result,
101
+ MISMATCH
102
+ );
103
+ };
104
+
105
+ return result;
106
+ }
107
+
108
+ case '|': {
109
+ // A bar (|) separates two or more alternatives: exactly one of them must occur.
110
+ //
111
+ // a | b | c
112
+ // =
113
+ // match a
114
+ // then MATCH
115
+ // else match b
116
+ // then MATCH
117
+ // else match c
118
+ // then MATCH
119
+ // else MISMATCH
120
+
121
+ let result = MISMATCH;
122
+ let map = null;
123
+
124
+ for (let i = terms.length - 1; i >= 0; i--) {
125
+ let term = terms[i];
126
+
127
+ // reduce sequence of keywords into a Enum
128
+ if (isEnumCapatible(term)) {
129
+ if (map === null && i > 0 && isEnumCapatible(terms[i - 1])) {
130
+ map = Object.create(null);
131
+ result = createCondition(
132
+ {
133
+ type: 'Enum',
134
+ map
135
+ },
136
+ MATCH,
137
+ result
138
+ );
139
+ }
140
+
141
+ if (map !== null) {
142
+ const key = (isFunctionType(term.name) ? term.name.slice(0, -1) : term.name).toLowerCase();
143
+ if (key in map === false) {
144
+ map[key] = term;
145
+ continue;
146
+ }
147
+ }
148
+ }
149
+
150
+ map = null;
151
+
152
+ // create a new conditonal node
153
+ result = createCondition(
154
+ term,
155
+ MATCH,
156
+ result
157
+ );
158
+ };
159
+
160
+ return result;
161
+ }
162
+
163
+ case '&&': {
164
+ // A double ampersand (&&) separates two or more components,
165
+ // all of which must occur, in any order.
166
+
167
+ // Use MatchOnce for groups with a large number of terms,
168
+ // since &&-groups produces at least N!-node trees
169
+ if (terms.length > 5) {
170
+ return {
171
+ type: 'MatchOnce',
172
+ terms,
173
+ all: true
174
+ };
175
+ }
176
+
177
+ // Use a combination tree for groups with small number of terms
178
+ //
179
+ // a && b && c
180
+ // =
181
+ // match a
182
+ // then [b && c]
183
+ // else match b
184
+ // then [a && c]
185
+ // else match c
186
+ // then [a && b]
187
+ // else MISMATCH
188
+ //
189
+ // a && b
190
+ // =
191
+ // match a
192
+ // then match b
193
+ // then MATCH
194
+ // else MISMATCH
195
+ // else match b
196
+ // then match a
197
+ // then MATCH
198
+ // else MISMATCH
199
+ // else MISMATCH
200
+ let result = MISMATCH;
201
+
202
+ for (let i = terms.length - 1; i >= 0; i--) {
203
+ const term = terms[i];
204
+ let thenClause;
205
+
206
+ if (terms.length > 1) {
207
+ thenClause = buildGroupMatchGraph(
208
+ combinator,
209
+ terms.filter(function(newGroupTerm) {
210
+ return newGroupTerm !== term;
211
+ }),
212
+ false
213
+ );
214
+ } else {
215
+ thenClause = MATCH;
216
+ }
217
+
218
+ result = createCondition(
219
+ term,
220
+ thenClause,
221
+ result
222
+ );
223
+ };
224
+
225
+ return result;
226
+ }
227
+
228
+ case '||': {
229
+ // A double bar (||) separates two or more options:
230
+ // one or more of them must occur, in any order.
231
+
232
+ // Use MatchOnce for groups with a large number of terms,
233
+ // since ||-groups produces at least N!-node trees
234
+ if (terms.length > 5) {
235
+ return {
236
+ type: 'MatchOnce',
237
+ terms,
238
+ all: false
239
+ };
240
+ }
241
+
242
+ // Use a combination tree for groups with small number of terms
243
+ //
244
+ // a || b || c
245
+ // =
246
+ // match a
247
+ // then [b || c]
248
+ // else match b
249
+ // then [a || c]
250
+ // else match c
251
+ // then [a || b]
252
+ // else MISMATCH
253
+ //
254
+ // a || b
255
+ // =
256
+ // match a
257
+ // then match b
258
+ // then MATCH
259
+ // else MATCH
260
+ // else match b
261
+ // then match a
262
+ // then MATCH
263
+ // else MATCH
264
+ // else MISMATCH
265
+ let result = atLeastOneTermMatched ? MATCH : MISMATCH;
266
+
267
+ for (let i = terms.length - 1; i >= 0; i--) {
268
+ const term = terms[i];
269
+ let thenClause;
270
+
271
+ if (terms.length > 1) {
272
+ thenClause = buildGroupMatchGraph(
273
+ combinator,
274
+ terms.filter(function(newGroupTerm) {
275
+ return newGroupTerm !== term;
276
+ }),
277
+ true
278
+ );
279
+ } else {
280
+ thenClause = MATCH;
281
+ }
282
+
283
+ result = createCondition(
284
+ term,
285
+ thenClause,
286
+ result
287
+ );
288
+ };
289
+
290
+ return result;
291
+ }
292
+ }
293
+ }
294
+
295
+ function buildMultiplierMatchGraph(node) {
296
+ let result = MATCH;
297
+ let matchTerm = buildMatchGraphInternal(node.term);
298
+
299
+ if (node.max === 0) {
300
+ // disable repeating of empty match to prevent infinite loop
301
+ matchTerm = createCondition(
302
+ matchTerm,
303
+ DISALLOW_EMPTY,
304
+ MISMATCH
305
+ );
306
+
307
+ // an occurrence count is not limited, make a cycle;
308
+ // to collect more terms on each following matching mismatch
309
+ result = createCondition(
310
+ matchTerm,
311
+ null, // will be a loop
312
+ MISMATCH
313
+ );
314
+
315
+ result.then = createCondition(
316
+ MATCH,
317
+ MATCH,
318
+ result // make a loop
319
+ );
320
+
321
+ if (node.comma) {
322
+ result.then.else = createCondition(
323
+ { type: 'Comma', syntax: node },
324
+ result,
325
+ MISMATCH
326
+ );
327
+ }
328
+ } else {
329
+ // create a match node chain for [min .. max] interval with optional matches
330
+ for (let i = node.min || 1; i <= node.max; i++) {
331
+ if (node.comma && result !== MATCH) {
332
+ result = createCondition(
333
+ { type: 'Comma', syntax: node },
334
+ result,
335
+ MISMATCH
336
+ );
337
+ }
338
+
339
+ result = createCondition(
340
+ matchTerm,
341
+ createCondition(
342
+ MATCH,
343
+ MATCH,
344
+ result
345
+ ),
346
+ MISMATCH
347
+ );
348
+ }
349
+ }
350
+
351
+ if (node.min === 0) {
352
+ // allow zero match
353
+ result = createCondition(
354
+ MATCH,
355
+ MATCH,
356
+ result
357
+ );
358
+ } else {
359
+ // create a match node chain to collect [0 ... min - 1] required matches
360
+ for (let i = 0; i < node.min - 1; i++) {
361
+ if (node.comma && result !== MATCH) {
362
+ result = createCondition(
363
+ { type: 'Comma', syntax: node },
364
+ result,
365
+ MISMATCH
366
+ );
367
+ }
368
+
369
+ result = createCondition(
370
+ matchTerm,
371
+ result,
372
+ MISMATCH
373
+ );
374
+ }
375
+ }
376
+
377
+ return result;
378
+ }
379
+
380
+ function buildMatchGraphInternal(node) {
381
+ if (typeof node === 'function') {
382
+ return {
383
+ type: 'Generic',
384
+ fn: node
385
+ };
386
+ }
387
+
388
+ switch (node.type) {
389
+ case 'Group': {
390
+ let result = buildGroupMatchGraph(
391
+ node.combinator,
392
+ node.terms.map(buildMatchGraphInternal),
393
+ false
394
+ );
395
+
396
+ if (node.disallowEmpty) {
397
+ result = createCondition(
398
+ result,
399
+ DISALLOW_EMPTY,
400
+ MISMATCH
401
+ );
402
+ }
403
+
404
+ return result;
405
+ }
406
+
407
+ case 'Multiplier':
408
+ return buildMultiplierMatchGraph(node);
409
+
410
+ // https://drafts.csswg.org/css-values-5/#boolean
411
+ case 'Boolean': {
412
+ const term = buildMatchGraphInternal(node.term);
413
+ // <boolean-expr[ <test> ]> = not <boolean-expr-group> | <boolean-expr-group> [ [ and <boolean-expr-group> ]* | [ or <boolean-expr-group> ]* ]
414
+ const matchNode = buildMatchGraphInternal(groupNode([
415
+ groupNode([
416
+ { type: 'Keyword', name: 'not' },
417
+ { type: 'Type', name: '!boolean-group' }
418
+ ]),
419
+ groupNode([
420
+ { type: 'Type', name: '!boolean-group' },
421
+ groupNode([
422
+ { type: 'Multiplier', comma: false, min: 0, max: 0, term: groupNode([
423
+ { type: 'Keyword', name: 'and' },
424
+ { type: 'Type', name: '!boolean-group' }
425
+ ]) },
426
+ { type: 'Multiplier', comma: false, min: 0, max: 0, term: groupNode([
427
+ { type: 'Keyword', name: 'or' },
428
+ { type: 'Type', name: '!boolean-group' }
429
+ ]) }
430
+ ], '|')
431
+ ])
432
+ ], '|'));
433
+ // <boolean-expr-group> = <test> | ( <boolean-expr[ <test> ]> ) | <general-enclosed>
434
+ const booleanGroup = buildMatchGraphInternal(
435
+ groupNode([
436
+ { type: 'Type', name: '!term' },
437
+ groupNode([
438
+ { type: 'Token', value: '(' },
439
+ { type: 'Type', name: '!self' },
440
+ { type: 'Token', value: ')' }
441
+ ]),
442
+ { type: 'Type', name: 'general-enclosed' }
443
+ ], '|')
444
+ );
445
+
446
+ replaceTypeInGraph(booleanGroup, { '!term': term, '!self': matchNode });
447
+ replaceTypeInGraph(matchNode, { '!boolean-group': booleanGroup });
448
+
449
+ return matchNode;
450
+ }
451
+
452
+ case 'Type':
453
+ case 'Property':
454
+ return {
455
+ type: node.type,
456
+ name: node.name,
457
+ syntax: node
458
+ };
459
+
460
+ case 'Keyword':
461
+ return {
462
+ type: node.type,
463
+ name: node.name.toLowerCase(),
464
+ syntax: node
465
+ };
466
+
467
+ case 'AtKeyword':
468
+ return {
469
+ type: node.type,
470
+ name: '@' + node.name.toLowerCase(),
471
+ syntax: node
472
+ };
473
+
474
+ case 'Function':
475
+ return {
476
+ type: node.type,
477
+ name: node.name.toLowerCase() + '(',
478
+ syntax: node
479
+ };
480
+
481
+ case 'String':
482
+ // convert a one char length String to a Token
483
+ if (node.value.length === 3) {
484
+ return {
485
+ type: 'Token',
486
+ value: node.value.charAt(1),
487
+ syntax: node
488
+ };
489
+ }
490
+
491
+ // otherwise use it as is
492
+ return {
493
+ type: node.type,
494
+ value: node.value.substr(1, node.value.length - 2).replace(/\\'/g, '\''),
495
+ syntax: node
496
+ };
497
+
498
+ case 'Token':
499
+ return {
500
+ type: node.type,
501
+ value: node.value,
502
+ syntax: node
503
+ };
504
+
505
+ case 'Comma':
506
+ return {
507
+ type: node.type,
508
+ syntax: node
509
+ };
510
+
511
+ default:
512
+ throw new Error('Unknown node type:', node.type);
513
+ }
514
+ }
515
+
516
+ export function buildMatchGraph(syntaxTree, ref) {
517
+ if (typeof syntaxTree === 'string') {
518
+ syntaxTree = parse(syntaxTree);
519
+ }
520
+
521
+ return {
522
+ type: 'MatchGraph',
523
+ match: buildMatchGraphInternal(syntaxTree),
524
+ syntax: ref || null,
525
+ source: syntaxTree
526
+ };
527
+ }