@stacksjs/ts-css 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.
@@ -0,0 +1,41 @@
1
+ /**
2
+ * css-select adapter / option types. Drop-in compatible with css-select v5.
3
+ *
4
+ * The library is generic over the consumer's tree (`Node`/`ElementNode`),
5
+ * communicated via the `Adapter` interface — exactly the shape SVGO etc.
6
+ * already implement, so callers don't need to change a thing.
7
+ */
8
+ export declare interface Adapter<Node, ElementNode extends Node> {
9
+ isTag: (node: Node) => node is ElementNode
10
+ existsOne: (test: (e: ElementNode) => boolean, elems: Node[]) => boolean
11
+ getAttributeValue: (elem: ElementNode, name: string) => string | undefined
12
+ getChildren: (node: Node) => Node[]
13
+ getName: (elem: ElementNode) => string
14
+ getParent: (elem: ElementNode | Node) => ElementNode | null
15
+ getSiblings: (elem: Node) => Node[]
16
+ getText: (node: Node) => string
17
+ hasAttrib: (elem: ElementNode, name: string) => boolean
18
+ removeSubsets: (nodes: Node[]) => Node[]
19
+ findAll?: (test: (e: ElementNode) => boolean, elems: Node[]) => ElementNode[]
20
+ findOne?: (test: (e: ElementNode) => boolean, elems: Node[]) => ElementNode | null
21
+ equals?: (a: Node, b: Node) => boolean
22
+ isActive?: (elem: ElementNode) => boolean
23
+ isVisited?: (elem: ElementNode) => boolean
24
+ isHovered?: (elem: ElementNode) => boolean
25
+ }
26
+ export declare interface Options<Node, ElementNode extends Node> {
27
+ xmlMode?: boolean
28
+ lowerCaseAttributeNames?: boolean
29
+ lowerCaseTags?: boolean
30
+ cacheResults?: boolean
31
+ adapter: Adapter<Node, ElementNode>
32
+ context?: Node | Node[]
33
+ pseudos?: Record<string, string | ((elem: ElementNode, value?: string) => boolean)>
34
+ rootFunc?: (elem: ElementNode) => boolean
35
+ relativeSelector?: boolean
36
+ quirksMode?: boolean
37
+ }
38
+ // eslint-disable-next-line pickier/no-unused-vars
39
+ export type CompiledQuery<ElementNode> = (node: ElementNode) => boolean;
40
+ // eslint-disable-next-line pickier/no-unused-vars
41
+ export type Predicate<Value> = (v: Value) => boolean;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Public configuration shape for ts-css. Loaded by `bunfig` from
3
+ * `css.config.ts` (or `.json` / `.toml`) when present.
4
+ */
5
+ export declare interface CSSConfig {
6
+ floatPrecision: number
7
+ verbose: boolean
8
+ }
9
+ export type CSSOptions = Partial<CSSConfig>;
@@ -0,0 +1,20 @@
1
+ export type {
2
+ AttributeAction,
3
+ AttributeSelectorNode,
4
+ IgnoreCase,
5
+ ParseOptions,
6
+ PseudoElementNode,
7
+ PseudoSelectorNode,
8
+ Selector,
9
+ SelectorType,
10
+ TagSelectorNode,
11
+ TraversalNode,
12
+ UniversalSelectorNode,
13
+ } from './types';
14
+ /**
15
+ * Public surface — drop-in compatible with css-what v6.
16
+ */
17
+ export { parse } from './parse';
18
+ export { stringify } from './stringify';
19
+ export { isTraversal } from './traversal';
20
+ export { IgnoreCaseMode } from './types';
@@ -0,0 +1,514 @@
1
+ // @bun
2
+ var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, {
10
+ get: all[name],
11
+ enumerable: true,
12
+ configurable: true,
13
+ set: __exportSetter.bind(all, name)
14
+ });
15
+ };
16
+ var __require = import.meta.require;
17
+
18
+ // src/what/index.ts
19
+ var exports_what = {};
20
+ __export(exports_what, {
21
+ stringify: () => stringify,
22
+ parse: () => parse,
23
+ isTraversal: () => isTraversal,
24
+ IgnoreCaseMode: () => IgnoreCaseMode
25
+ });
26
+
27
+ // src/what/parse.ts
28
+ var RE_NAME_STICKY = /(?:\\(?:[\dA-Fa-f]{1,6} ?|[^])|[\w\-\u00B0-\uFFFF])+/y;
29
+ var RE_ESCAPE = /\\([\dA-Fa-f]{1,6} ?|[^])/g;
30
+ function unescape(name) {
31
+ return name.replace(RE_ESCAPE, (_m, escape) => {
32
+ if (escape.length > 1 && /^[\dA-Fa-f]/.test(escape)) {
33
+ const code = Number.parseInt(escape, 16);
34
+ if (code >= 55296 && code <= 57343)
35
+ return "\uFFFD";
36
+ return String.fromCodePoint(code);
37
+ }
38
+ return escape;
39
+ });
40
+ }
41
+ function unescapeIfNeeded(name) {
42
+ return name.indexOf("\\") < 0 ? name : unescape(name);
43
+ }
44
+ var ATTRIBUTES_QUIRKS = new Set([
45
+ "accept",
46
+ "accept-charset",
47
+ "align",
48
+ "alink",
49
+ "axis",
50
+ "bgcolor",
51
+ "charset",
52
+ "checked",
53
+ "clear",
54
+ "codetype",
55
+ "color",
56
+ "compact",
57
+ "declare",
58
+ "defer",
59
+ "dir",
60
+ "direction",
61
+ "disabled",
62
+ "enctype",
63
+ "face",
64
+ "frame",
65
+ "hreflang",
66
+ "http-equiv",
67
+ "lang",
68
+ "language",
69
+ "link",
70
+ "media",
71
+ "method",
72
+ "multiple",
73
+ "nohref",
74
+ "noresize",
75
+ "noshade",
76
+ "nowrap",
77
+ "readonly",
78
+ "rel",
79
+ "rev",
80
+ "rules",
81
+ "scope",
82
+ "scrolling",
83
+ "selected",
84
+ "shape",
85
+ "target",
86
+ "text",
87
+ "type",
88
+ "valign",
89
+ "valuetype",
90
+ "vlink"
91
+ ]);
92
+ function actionFromChar(ch) {
93
+ switch (ch) {
94
+ case 126:
95
+ return "element";
96
+ case 94:
97
+ return "start";
98
+ case 36:
99
+ return "end";
100
+ case 42:
101
+ return "any";
102
+ case 33:
103
+ return "not";
104
+ case 124:
105
+ return "hyphen";
106
+ default:
107
+ return null;
108
+ }
109
+ }
110
+ function isWsCode(c) {
111
+ return c === 32 || c === 9 || c === 10 || c === 13 || c === 12;
112
+ }
113
+ function parse(selector, options = {}) {
114
+ const subselects = [];
115
+ const endIndex = parseSelectorImpl(subselects, selector, options, 0);
116
+ if (endIndex < selector.length)
117
+ throw new Error(`Unmatched selector: ${selector.slice(endIndex)}`);
118
+ return subselects;
119
+ }
120
+ function readName(selector, from) {
121
+ RE_NAME_STICKY.lastIndex = from;
122
+ const m = RE_NAME_STICKY.exec(selector);
123
+ if (!m)
124
+ throw new Error(`Expected name, found ${selector.slice(from)}`);
125
+ return { value: unescapeIfNeeded(m[0]), end: from + m[0].length };
126
+ }
127
+ function stripWS(selector, from) {
128
+ while (from < selector.length && isWsCode(selector.charCodeAt(from)))
129
+ from++;
130
+ return from;
131
+ }
132
+ function parseSelectorImpl(subselects, selector, options, startIndex) {
133
+ let tokens = [];
134
+ let i = stripWS(selector, startIndex);
135
+ const len = selector.length;
136
+ const xmlMode = options.xmlMode === true;
137
+ const lowerCaseAttrs = options.lowerCaseAttributeNames !== false && !xmlMode;
138
+ const lowerCaseTagsFlag = options.lowerCaseTags !== false;
139
+ while (i < len) {
140
+ const code = selector.charCodeAt(i);
141
+ if (isWsCode(code)) {
142
+ let trimmed = i + 1;
143
+ while (trimmed < len && isWsCode(selector.charCodeAt(trimmed)))
144
+ trimmed++;
145
+ if (tokens.length === 0)
146
+ return trimmed;
147
+ i = trimmed;
148
+ addTraversal(tokens, "descendant");
149
+ continue;
150
+ }
151
+ if (code === 62 || code === 60 || code === 126 || code === 43 || code === 124) {
152
+ let j = i + 1;
153
+ while (j < len && isWsCode(selector.charCodeAt(j)))
154
+ j++;
155
+ i = j;
156
+ switch (code) {
157
+ case 62:
158
+ addTraversal(tokens, "child");
159
+ break;
160
+ case 60:
161
+ addTraversal(tokens, "parent");
162
+ break;
163
+ case 126:
164
+ addTraversal(tokens, "sibling");
165
+ break;
166
+ case 43:
167
+ addTraversal(tokens, "adjacent");
168
+ break;
169
+ case 124:
170
+ if (i < len && selector.charCodeAt(i) === 124) {
171
+ i++;
172
+ i = stripWS(selector, i);
173
+ addTraversal(tokens, "column-combinator");
174
+ } else {
175
+ tokens.push({ type: "tag", name: "", namespace: "" });
176
+ }
177
+ break;
178
+ }
179
+ continue;
180
+ }
181
+ if (code === 44) {
182
+ if (tokens.length === 0)
183
+ throw new Error("Empty sub-selector");
184
+ subselects.push(tokens);
185
+ tokens = [];
186
+ i = stripWS(selector, i + 1);
187
+ continue;
188
+ }
189
+ if (code === 47 && selector.charCodeAt(i + 1) === 42) {
190
+ const end = selector.indexOf("*/", i + 2);
191
+ if (end < 0)
192
+ throw new Error("Unmatched comment");
193
+ i = stripWS(selector, end + 2);
194
+ continue;
195
+ }
196
+ if (code === 42) {
197
+ i++;
198
+ tokens.push({ type: "universal", namespace: null });
199
+ continue;
200
+ }
201
+ if (code === 35) {
202
+ const r = readName(selector, i + 1);
203
+ i = r.end;
204
+ tokens.push({
205
+ type: "attribute",
206
+ name: "id",
207
+ action: "equals",
208
+ value: r.value,
209
+ namespace: null,
210
+ ignoreCase: false
211
+ });
212
+ continue;
213
+ }
214
+ if (code === 46) {
215
+ const r = readName(selector, i + 1);
216
+ i = r.end;
217
+ tokens.push({
218
+ type: "attribute",
219
+ name: "class",
220
+ action: "element",
221
+ value: r.value,
222
+ namespace: null,
223
+ ignoreCase: false
224
+ });
225
+ continue;
226
+ }
227
+ if (code === 91) {
228
+ i = parseAttribute(selector, i, tokens, options, xmlMode, lowerCaseAttrs);
229
+ continue;
230
+ }
231
+ if (code === 58) {
232
+ i = parsePseudo(selector, i, tokens, options);
233
+ continue;
234
+ }
235
+ if (code === 124) {
236
+ i++;
237
+ const r = readName(selector, i);
238
+ i = r.end;
239
+ tokens.push({ type: "tag", name: lowerCaseTagsFlag ? r.value.toLowerCase() : r.value, namespace: "" });
240
+ continue;
241
+ }
242
+ {
243
+ const r1 = readName(selector, i);
244
+ i = r1.end;
245
+ if (i < len && selector.charCodeAt(i) === 124 && selector.charCodeAt(i + 1) !== 61) {
246
+ i++;
247
+ const r2 = readName(selector, i);
248
+ i = r2.end;
249
+ tokens.push({ type: "tag", name: lowerCaseTagsFlag ? r2.value.toLowerCase() : r2.value, namespace: r1.value });
250
+ } else {
251
+ tokens.push({ type: "tag", name: lowerCaseTagsFlag ? r1.value.toLowerCase() : r1.value, namespace: null });
252
+ }
253
+ }
254
+ }
255
+ if (tokens.length > 0)
256
+ subselects.push(tokens);
257
+ return i;
258
+ }
259
+ function parseAttribute(selector, idx, tokens, options, xmlMode, lowerCaseAttrs) {
260
+ let i = idx + 1;
261
+ const len = selector.length;
262
+ let attribute;
263
+ if (selector.charCodeAt(i) === 124)
264
+ throw new Error("Empty namespace not supported");
265
+ if (selector.charCodeAt(i) === 42 && selector.charCodeAt(i + 1) === 124) {
266
+ i += 2;
267
+ const r = readName(selector, i);
268
+ i = r.end;
269
+ attribute = r.value;
270
+ } else {
271
+ const r = readName(selector, i);
272
+ i = r.end;
273
+ attribute = r.value;
274
+ if (selector.charCodeAt(i) === 124 && selector.charCodeAt(i + 1) !== 61) {
275
+ i++;
276
+ const r2 = readName(selector, i);
277
+ i = r2.end;
278
+ attribute = r2.value;
279
+ }
280
+ }
281
+ i = stripWS(selector, i);
282
+ let action = "exists";
283
+ let value = "";
284
+ let ignoreCase = null;
285
+ const opCode = selector.charCodeAt(i);
286
+ if (opCode === 61) {
287
+ action = "equals";
288
+ i++;
289
+ } else if (opCode === 33 && selector.charCodeAt(i + 1) === 61) {
290
+ action = "not";
291
+ i += 2;
292
+ } else {
293
+ const a = actionFromChar(opCode);
294
+ if (a !== null && selector.charCodeAt(i + 1) === 61) {
295
+ action = a;
296
+ i += 2;
297
+ }
298
+ }
299
+ if (action !== "exists") {
300
+ i = stripWS(selector, i);
301
+ const q = selector.charCodeAt(i);
302
+ if (q === 34 || q === 39) {
303
+ const end = findEndOfString(selector, i + 1, q);
304
+ value = unescapeIfNeeded(selector.slice(i + 1, end));
305
+ i = end + 1;
306
+ } else {
307
+ const r = readName(selector, i);
308
+ value = r.value;
309
+ i = r.end;
310
+ }
311
+ i = stripWS(selector, i);
312
+ const flag = selector.charCodeAt(i);
313
+ if (flag === 105 || flag === 73) {
314
+ ignoreCase = true;
315
+ i++;
316
+ } else if (flag === 115 || flag === 83) {
317
+ ignoreCase = false;
318
+ i++;
319
+ }
320
+ }
321
+ if (selector.charCodeAt(i) !== 93)
322
+ throw new Error("Expected ]");
323
+ i++;
324
+ if (ignoreCase === null && !xmlMode && ATTRIBUTES_QUIRKS.has(attribute.toLowerCase()))
325
+ ignoreCase = "quirks";
326
+ tokens.push({
327
+ type: "attribute",
328
+ name: lowerCaseAttrs ? attribute.toLowerCase() : attribute,
329
+ action,
330
+ value,
331
+ namespace: null,
332
+ ignoreCase
333
+ });
334
+ return i;
335
+ }
336
+ function parsePseudo(selector, idx, tokens, options) {
337
+ if (selector.charCodeAt(idx + 1) === 58) {
338
+ let i2 = idx + 2;
339
+ const r2 = readName(selector, i2);
340
+ i2 = r2.end;
341
+ const name2 = r2.value.toLowerCase();
342
+ let data = null;
343
+ if (selector.charCodeAt(i2) === 40) {
344
+ const end = findClose(selector, i2);
345
+ data = selector.slice(i2 + 1, end).trim();
346
+ i2 = end + 1;
347
+ }
348
+ tokens.push({ type: "pseudo-element", name: name2, data });
349
+ return i2;
350
+ }
351
+ let i = idx + 1;
352
+ const r = readName(selector, i);
353
+ i = r.end;
354
+ const name = r.value.toLowerCase();
355
+ if (selector.charCodeAt(i) === 40) {
356
+ const end = findClose(selector, i);
357
+ const inner = selector.slice(i + 1, end);
358
+ i = end + 1;
359
+ if (name === "is" || name === "not" || name === "where" || name === "has" || name === "matches" || name === "-moz-any" || name === "-webkit-any") {
360
+ const sub = [];
361
+ parseSelectorImpl(sub, inner.trim(), options, 0);
362
+ tokens.push({ type: "pseudo", name, data: sub });
363
+ } else {
364
+ tokens.push({ type: "pseudo", name, data: inner.trim() });
365
+ }
366
+ } else {
367
+ tokens.push({ type: "pseudo", name, data: null });
368
+ }
369
+ return i;
370
+ }
371
+ function addTraversal(tokens, type) {
372
+ if (tokens.length > 0 && tokens[tokens.length - 1].type === "descendant" && type !== "descendant")
373
+ tokens.pop();
374
+ if (tokens.length > 0 && tokens[tokens.length - 1].type === type)
375
+ return;
376
+ tokens.push({ type });
377
+ }
378
+ function findEndOfString(selector, start, qCode) {
379
+ let i = start;
380
+ const len = selector.length;
381
+ while (i < len) {
382
+ const c = selector.charCodeAt(i);
383
+ if (c === 92) {
384
+ i += 2;
385
+ continue;
386
+ }
387
+ if (c === qCode)
388
+ return i;
389
+ i++;
390
+ }
391
+ throw new Error("Unterminated string");
392
+ }
393
+ function findClose(selector, openParen) {
394
+ let depth = 1;
395
+ let i = openParen + 1;
396
+ const len = selector.length;
397
+ while (i < len) {
398
+ const c = selector.charCodeAt(i);
399
+ if (c === 92) {
400
+ i += 2;
401
+ continue;
402
+ }
403
+ if (c === 34 || c === 39) {
404
+ i = findEndOfString(selector, i + 1, c) + 1;
405
+ continue;
406
+ }
407
+ if (c === 40)
408
+ depth++;
409
+ else if (c === 41) {
410
+ depth--;
411
+ if (depth === 0)
412
+ return i;
413
+ }
414
+ i++;
415
+ }
416
+ throw new Error("Unterminated parenthesis");
417
+ }
418
+ // src/what/stringify.ts
419
+ var COMBINATORS = {
420
+ child: " > ",
421
+ parent: " < ",
422
+ sibling: " ~ ",
423
+ adjacent: " + ",
424
+ descendant: " ",
425
+ "column-combinator": " || "
426
+ };
427
+ function stringify(selector) {
428
+ return selector.map(stringifySegments).join(", ");
429
+ }
430
+ function stringifySegments(tokens) {
431
+ return tokens.map((t, i) => stringifyOne(t, tokens[i - 1])).join("");
432
+ }
433
+ function stringifyOne(token, _prev) {
434
+ switch (token.type) {
435
+ case "tag":
436
+ return `${nsPrefix(token.namespace)}${escapeIdent(token.name)}`;
437
+ case "universal":
438
+ return `${nsPrefix(token.namespace)}*`;
439
+ case "attribute": {
440
+ if (token.name === "id" && token.action === "equals" && !token.ignoreCase && !token.namespace)
441
+ return `#${escapeIdent(token.value)}`;
442
+ if (token.name === "class" && token.action === "element" && !token.ignoreCase && !token.namespace)
443
+ return `.${escapeIdent(token.value)}`;
444
+ let out = `[${nsPrefix(token.namespace)}${escapeIdent(token.name)}`;
445
+ if (token.action !== "exists") {
446
+ const op = ACTION_OP[token.action] ?? "=";
447
+ out += op;
448
+ out += `"${token.value.replace(/"/g, "\\\"")}"`;
449
+ if (token.ignoreCase === true)
450
+ out += " i";
451
+ else if (token.ignoreCase === false)
452
+ out += " s";
453
+ }
454
+ out += "]";
455
+ return out;
456
+ }
457
+ case "pseudo":
458
+ if (token.data === null)
459
+ return `:${token.name}`;
460
+ if (typeof token.data === "string")
461
+ return `:${token.name}(${token.data})`;
462
+ return `:${token.name}(${stringify(token.data)})`;
463
+ case "pseudo-element":
464
+ return token.data === null ? `::${token.name}` : `::${token.name}(${token.data})`;
465
+ case "descendant":
466
+ case "child":
467
+ case "parent":
468
+ case "sibling":
469
+ case "adjacent":
470
+ case "column-combinator":
471
+ return COMBINATORS[token.type] ?? " ";
472
+ }
473
+ return "";
474
+ }
475
+ var ACTION_OP = {
476
+ equals: "=",
477
+ element: "~=",
478
+ start: "^=",
479
+ end: "$=",
480
+ any: "*=",
481
+ not: "!=",
482
+ hyphen: "|="
483
+ };
484
+ function nsPrefix(ns) {
485
+ if (ns === null)
486
+ return "";
487
+ if (ns === "")
488
+ return "|";
489
+ return `${escapeIdent(ns)}|`;
490
+ }
491
+ var RE_INVALID_ID_CHAR = /[^\w\u00B0-\uFFFF-]/g;
492
+ function escapeIdent(name) {
493
+ if (name === "")
494
+ return "";
495
+ return name.replace(RE_INVALID_ID_CHAR, (m) => `\\${m}`);
496
+ }
497
+ // src/what/traversal.ts
498
+ var TRAVERSAL_TYPES = new Set(["adjacent", "child", "descendant", "parent", "sibling", "column-combinator"]);
499
+ function isTraversal(token) {
500
+ return TRAVERSAL_TYPES.has(token.type);
501
+ }
502
+ // src/what/types.ts
503
+ var IgnoreCaseMode = {
504
+ Unknown: null,
505
+ QuirksMode: "quirks",
506
+ IgnoreCase: true,
507
+ CaseSensitive: false
508
+ };
509
+ export {
510
+ stringify,
511
+ parse,
512
+ isTraversal,
513
+ IgnoreCaseMode
514
+ };
@@ -0,0 +1,2 @@
1
+ import type { ParseOptions, Selector } from './types';
2
+ export declare function parse(selector: string, options?: ParseOptions): Selector[][];
@@ -0,0 +1,2 @@
1
+ import type { Selector } from './types';
2
+ export declare function stringify(selector: Selector[][]): string;
@@ -0,0 +1,3 @@
1
+ import type { Selector } from './types';
2
+ /** True when `token` is a traversal/combinator segment (vs. simple selector). */
3
+ export declare function isTraversal(token: Selector): boolean;
@@ -0,0 +1,66 @@
1
+ export declare const IgnoreCaseMode: Readonly<Record<string, true | false | 'quirks' | null>>;
2
+ export declare interface AttributeSelectorNode {
3
+ type: 'attribute'
4
+ name: string
5
+ action: AttributeAction
6
+ value: string
7
+ namespace: string | null
8
+ ignoreCase: IgnoreCase
9
+ }
10
+ export declare interface TagSelectorNode {
11
+ type: 'tag'
12
+ name: string
13
+ namespace: string | null
14
+ }
15
+ export declare interface UniversalSelectorNode {
16
+ type: 'universal'
17
+ namespace: string | null
18
+ }
19
+ export declare interface PseudoSelectorNode {
20
+ type: 'pseudo'
21
+ name: string
22
+ data: string | Selector[][] | null
23
+ }
24
+ export declare interface PseudoElementNode {
25
+ type: 'pseudo-element'
26
+ name: string
27
+ data: string | null
28
+ }
29
+ export declare interface TraversalNode {
30
+ type: 'adjacent' | 'child' | 'descendant' | 'parent' | 'sibling' | 'column-combinator'
31
+ }
32
+ export declare interface ParseOptions {
33
+ xmlMode?: boolean
34
+ lowerCaseAttributeNames?: boolean
35
+ lowerCaseTags?: boolean
36
+ }
37
+ /**
38
+ * Selector AST mirroring css-what v6 — the segment-list-of-lists shape
39
+ * `parse(selector)` returns. Compatible with css-select consumers.
40
+ */
41
+ export type SelectorType = | 'attribute'
42
+ | 'pseudo'
43
+ | 'pseudo-element'
44
+ | 'tag'
45
+ | 'universal'
46
+ | 'adjacent'
47
+ | 'child'
48
+ | 'descendant'
49
+ | 'parent'
50
+ | 'sibling'
51
+ | 'column-combinator';
52
+ export type AttributeAction = | 'any'
53
+ | 'element'
54
+ | 'end'
55
+ | 'equals'
56
+ | 'exists'
57
+ | 'hyphen'
58
+ | 'not'
59
+ | 'start';
60
+ export type IgnoreCase = boolean | 'quirks' | null;
61
+ export type Selector = | AttributeSelectorNode
62
+ | TagSelectorNode
63
+ | UniversalSelectorNode
64
+ | PseudoSelectorNode
65
+ | PseudoElementNode
66
+ | TraversalNode;