UNPKG

33 kBJavaScriptView Raw
1import { parse as convertToCSSWhatSelector } from 'css-what';
2import '../../globals';
3import { isCssVariable } from '../core/properties';
4import { isNullOrUndefined } from '../../utils/types';
5import { checkIfMediaQueryMatches } from '../../media-query-list';
6export const MEDIA_QUERY_SEPARATOR = '&&';
7var Combinator;
8(function (Combinator) {
9 Combinator["descendant"] = " ";
10 Combinator["child"] = ">";
11 Combinator["adjacent"] = "+";
12 Combinator["sibling"] = "~";
13 // Not supported
14 Combinator["parent"] = "<";
15 Combinator["column-combinator"] = "||";
16})(Combinator || (Combinator = {}));
17var AttributeSelectorOperator;
18(function (AttributeSelectorOperator) {
19 AttributeSelectorOperator["exists"] = "";
20 AttributeSelectorOperator["equals"] = "=";
21 AttributeSelectorOperator["start"] = "^=";
22 AttributeSelectorOperator["end"] = "$=";
23 AttributeSelectorOperator["any"] = "*=";
24 AttributeSelectorOperator["element"] = "~=";
25 AttributeSelectorOperator["hyphen"] = "|=";
26})(AttributeSelectorOperator || (AttributeSelectorOperator = {}));
27var Match;
28(function (Match) {
29 /**
30 * Depends on attributes or pseudoclasses state;
31 */
32 Match.Dynamic = true;
33 /**
34 * Depends only on the tree structure.
35 */
36 Match.Static = false;
37})(Match || (Match = {}));
38function eachNodePreviousGeneralSibling(node, callback) {
39 if (!node.parent || !node.parent.getChildIndex || !node.parent.getChildAt || !node.parent.getChildrenCount) {
40 return;
41 }
42 const nodeIndex = node.parent.getChildIndex(node);
43 if (nodeIndex === 0) {
44 return;
45 }
46 const count = node.parent.getChildrenCount();
47 let retVal = true;
48 for (let i = nodeIndex - 1; i >= 0 && retVal; i--) {
49 const sibling = node.parent.getChildAt(i);
50 retVal = callback(sibling);
51 }
52}
53function getNodePreviousDirectSibling(node) {
54 if (!node.parent || !node.parent.getChildIndex || !node.parent.getChildAt) {
55 return null;
56 }
57 const nodeIndex = node.parent.getChildIndex(node);
58 if (nodeIndex === 0) {
59 return null;
60 }
61 return node.parent.getChildAt(nodeIndex - 1);
62}
63function SelectorProperties(specificity, rarity, dynamic = false) {
64 return (cls) => {
65 cls.prototype.specificity = specificity;
66 cls.prototype.rarity = rarity;
67 cls.prototype.combinator = undefined;
68 cls.prototype.dynamic = dynamic;
69 return cls;
70 };
71}
72function FunctionalPseudoClassProperties(specificity, rarity, pseudoSelectorListType) {
73 return (cls) => {
74 cls.prototype.specificity = specificity;
75 cls.prototype.rarity = rarity;
76 cls.prototype.combinator = undefined;
77 cls.prototype.dynamic = false;
78 cls.prototype.pseudoSelectorListType = pseudoSelectorListType;
79 return cls;
80 };
81}
82export class SelectorBase {
83}
84let SelectorCore = class SelectorCore extends SelectorBase {
85 lookupSort(sorter, base) {
86 sorter.sortAsUniversal(base || this);
87 }
88};
89SelectorCore = __decorate([
90 SelectorProperties(0 /* Specificity.Universal */, 0 /* Rarity.Universal */, Match.Static)
91], SelectorCore);
92export { SelectorCore };
93export class SimpleSelector extends SelectorCore {
94 accumulateChanges(node, map) {
95 if (!this.dynamic) {
96 return this.match(node);
97 }
98 else if (this.mayMatch(node)) {
99 this.trackChanges(node, map);
100 return true;
101 }
102 return false;
103 }
104 mayMatch(node) {
105 return this.match(node);
106 }
107 trackChanges(node, map) {
108 // No-op, silence the tslint 'block is empty'.
109 // Some derived classes (dynamic) will actually fill the map with stuff here, some (static) won't do anything.
110 }
111}
112function wrap(text) {
113 return text ? ` ${text} ` : '';
114}
115let InvalidSelector = class InvalidSelector extends SimpleSelector {
116 constructor(e) {
117 super();
118 this.e = e;
119 }
120 toString() {
121 return `<${this.e}>`;
122 }
123 match(node) {
124 return false;
125 }
126 lookupSort(sorter, base) {
127 // No-op, silence the tslint 'block is empty'.
128 // It feels like tslint has problems with simple polymorphism...
129 // This selector is invalid and will never match so we won't bother sorting it to further appear in queries.
130 }
131};
132InvalidSelector = __decorate([
133 SelectorProperties(0 /* Specificity.Invalid */, 4 /* Rarity.Invalid */, Match.Static),
134 __metadata("design:paramtypes", [Error])
135], InvalidSelector);
136export { InvalidSelector };
137let UniversalSelector = class UniversalSelector extends SimpleSelector {
138 toString() {
139 return `*${wrap(this.combinator)}`;
140 }
141 match(node) {
142 return true;
143 }
144};
145UniversalSelector = __decorate([
146 SelectorProperties(0 /* Specificity.Universal */, 0 /* Rarity.Universal */, Match.Static)
147], UniversalSelector);
148export { UniversalSelector };
149let IdSelector = class IdSelector extends SimpleSelector {
150 constructor(id) {
151 super();
152 this.id = id;
153 }
154 toString() {
155 return `#${this.id}${wrap(this.combinator)}`;
156 }
157 match(node) {
158 return node.id === this.id;
159 }
160 lookupSort(sorter, base) {
161 sorter.sortById(this.id, base || this);
162 }
163};
164IdSelector = __decorate([
165 SelectorProperties(100 /* Specificity.Id */, 3 /* Rarity.Id */, Match.Static),
166 __metadata("design:paramtypes", [String])
167], IdSelector);
168export { IdSelector };
169let TypeSelector = class TypeSelector extends SimpleSelector {
170 constructor(cssType) {
171 super();
172 this.cssType = cssType;
173 }
174 toString() {
175 return `${this.cssType}${wrap(this.combinator)}`;
176 }
177 match(node) {
178 return node.cssType === this.cssType;
179 }
180 lookupSort(sorter, base) {
181 sorter.sortByType(this.cssType, base || this);
182 }
183};
184TypeSelector = __decorate([
185 SelectorProperties(1 /* Specificity.Type */, 1 /* Rarity.Type */, Match.Static),
186 __metadata("design:paramtypes", [String])
187], TypeSelector);
188export { TypeSelector };
189let ClassSelector = class ClassSelector extends SimpleSelector {
190 constructor(cssClass) {
191 super();
192 this.cssClass = cssClass;
193 }
194 toString() {
195 return `.${this.cssClass}${wrap(this.combinator)}`;
196 }
197 match(node) {
198 return node.cssClasses && node.cssClasses.has(this.cssClass);
199 }
200 lookupSort(sorter, base) {
201 sorter.sortByClass(this.cssClass, base || this);
202 }
203};
204ClassSelector = __decorate([
205 SelectorProperties(10 /* Specificity.Class */, 2 /* Rarity.Class */, Match.Static),
206 __metadata("design:paramtypes", [String])
207], ClassSelector);
208export { ClassSelector };
209let AttributeSelector = class AttributeSelector extends SimpleSelector {
210 constructor(attribute, test, value, ignoreCase) {
211 super();
212 this.attribute = attribute;
213 this.test = test;
214 this.value = value;
215 this.ignoreCase = ignoreCase;
216 }
217 toString() {
218 return `[${this.attribute}${wrap(AttributeSelectorOperator[this.test] ?? this.test)}${this.value || ''}]${wrap(this.combinator)}`;
219 }
220 match(node) {
221 let attr = node[this.attribute];
222 if (this.test === 'exists') {
223 return !isNullOrUndefined(attr);
224 }
225 if (!this.value) {
226 return false;
227 }
228 // Now, convert value to string
229 attr += '';
230 if (this.ignoreCase) {
231 attr = attr.toLowerCase();
232 this.value = this.value.toLowerCase();
233 }
234 // =
235 if (this.test === 'equals') {
236 return attr === this.value;
237 }
238 // ^=
239 if (this.test === 'start') {
240 return attr.startsWith(this.value);
241 }
242 // $=
243 if (this.test === 'end') {
244 return attr.endsWith(this.value);
245 }
246 // *=
247 if (this.test === 'any') {
248 return attr.indexOf(this.value) !== -1;
249 }
250 // ~=
251 if (this.test === 'element') {
252 const words = attr.split(' ');
253 return words && words.indexOf(this.value) !== -1;
254 }
255 // |=
256 if (this.test === 'hyphen') {
257 return attr === this.value || attr.startsWith(this.value + '-');
258 }
259 return false;
260 }
261 mayMatch(node) {
262 return true;
263 }
264 trackChanges(node, map) {
265 map.addAttribute(node, this.attribute);
266 }
267};
268AttributeSelector = __decorate([
269 SelectorProperties(10 /* Specificity.Attribute */, 0 /* Rarity.Attribute */, Match.Dynamic),
270 __metadata("design:paramtypes", [String, String, String, Boolean])
271], AttributeSelector);
272export { AttributeSelector };
273let PseudoClassSelector = class PseudoClassSelector extends SimpleSelector {
274 constructor(cssPseudoClass) {
275 super();
276 this.cssPseudoClass = cssPseudoClass;
277 }
278 toString() {
279 return `:${this.cssPseudoClass}${wrap(this.combinator)}`;
280 }
281 match(node) {
282 return node.cssPseudoClasses && node.cssPseudoClasses.has(this.cssPseudoClass);
283 }
284 mayMatch(node) {
285 return true;
286 }
287 trackChanges(node, map) {
288 map.addPseudoClass(node, this.cssPseudoClass);
289 }
290};
291PseudoClassSelector = __decorate([
292 SelectorProperties(10 /* Specificity.PseudoClass */, 0 /* Rarity.PseudoClass */, Match.Dynamic),
293 __metadata("design:paramtypes", [String])
294], PseudoClassSelector);
295export { PseudoClassSelector };
296export class FunctionalPseudoClassSelector extends PseudoClassSelector {
297 constructor(cssPseudoClass, dataType) {
298 super(cssPseudoClass);
299 const selectors = [];
300 const needsHighestSpecificity = this.specificity === -1 /* Specificity.SelectorListHighest */;
301 let specificity = 0;
302 if (Array.isArray(dataType)) {
303 for (const asts of dataType) {
304 const selector = createSelectorFromAst(asts);
305 if (selector instanceof InvalidSelector) {
306 // Only forgiving selector list can ignore invalid selectors
307 if (this.selectorListType !== 1 /* PseudoClassSelectorList.Forgiving */) {
308 selectors.splice(0);
309 specificity = 0;
310 break;
311 }
312 continue;
313 }
314 // The specificity of some pseudo-classes is replaced by the specificity of the most specific selector in its comma-separated argument of selectors
315 if (needsHighestSpecificity && selector.specificity > specificity) {
316 specificity = selector.specificity;
317 }
318 selectors.push(selector);
319 }
320 }
321 this.selectors = selectors;
322 this.specificity = specificity;
323 // Functional pseudo-classes become dynamic based on selectors in selector list
324 this.dynamic = this.selectors.some((sel) => sel.dynamic);
325 }
326 toString() {
327 return `:${this.cssPseudoClass}(${this.selectors.join(', ')})${wrap(this.combinator)}`;
328 }
329 match(node) {
330 return false;
331 }
332 mayMatch(node) {
333 return true;
334 }
335 trackChanges(node, map) {
336 this.selectors.forEach((sel) => sel.trackChanges(node, map));
337 }
338}
339let NotFunctionalPseudoClassSelector = class NotFunctionalPseudoClassSelector extends FunctionalPseudoClassSelector {
340 match(node) {
341 return !this.selectors.some((sel) => sel.match(node));
342 }
343};
344NotFunctionalPseudoClassSelector = __decorate([
345 FunctionalPseudoClassProperties(-1 /* Specificity.SelectorListHighest */, 0 /* Rarity.PseudoClass */, 0 /* PseudoClassSelectorList.Regular */)
346], NotFunctionalPseudoClassSelector);
347export { NotFunctionalPseudoClassSelector };
348let IsFunctionalPseudoClassSelector = class IsFunctionalPseudoClassSelector extends FunctionalPseudoClassSelector {
349 match(node) {
350 return this.selectors.some((sel) => sel.match(node));
351 }
352 lookupSort(sorter, base) {
353 // A faster lookup can be performed when selector list contains just a single selector
354 if (this.selectors.length === 1) {
355 this.selectors[0].lookupSort(sorter, base || this);
356 }
357 else {
358 super.lookupSort(sorter, base || this);
359 }
360 }
361};
362IsFunctionalPseudoClassSelector = __decorate([
363 FunctionalPseudoClassProperties(-1 /* Specificity.SelectorListHighest */, 0 /* Rarity.PseudoClass */, 1 /* PseudoClassSelectorList.Forgiving */)
364], IsFunctionalPseudoClassSelector);
365export { IsFunctionalPseudoClassSelector };
366let WhereFunctionalPseudoClassSelector = class WhereFunctionalPseudoClassSelector extends FunctionalPseudoClassSelector {
367 match(node) {
368 return this.selectors.some((sel) => sel.match(node));
369 }
370 lookupSort(sorter, base) {
371 // A faster lookup can be performed when selector list contains just a single selector
372 if (this.selectors.length === 1) {
373 this.selectors[0].lookupSort(sorter, base || this);
374 }
375 else {
376 super.lookupSort(sorter, base || this);
377 }
378 }
379};
380WhereFunctionalPseudoClassSelector = __decorate([
381 FunctionalPseudoClassProperties(0 /* Specificity.Zero */, 0 /* Rarity.PseudoClass */, 1 /* PseudoClassSelectorList.Forgiving */)
382], WhereFunctionalPseudoClassSelector);
383export { WhereFunctionalPseudoClassSelector };
384export class SimpleSelectorSequence extends SimpleSelector {
385 constructor(selectors) {
386 super();
387 this.selectors = selectors;
388 this.specificity = selectors.reduce((sum, sel) => sel.specificity + sum, 0);
389 this.head = selectors.reduce((prev, curr) => (!prev || curr.rarity > prev.rarity ? curr : prev), null);
390 this.dynamic = selectors.some((sel) => sel.dynamic);
391 }
392 toString() {
393 return `${this.selectors.join('')}${wrap(this.combinator)}`;
394 }
395 match(node) {
396 return this.selectors.every((sel) => sel.match(node));
397 }
398 mayMatch(node) {
399 return this.selectors.every((sel) => sel.mayMatch(node));
400 }
401 trackChanges(node, map) {
402 this.selectors.forEach((sel) => sel.trackChanges(node, map));
403 }
404 lookupSort(sorter, base) {
405 this.head.lookupSort(sorter, base || this);
406 }
407}
408export class ComplexSelector extends SelectorCore {
409 constructor(selectors) {
410 super();
411 this.selectors = selectors;
412 let siblingsToGroup;
413 let currentGroup;
414 const groups = [];
415 this.specificity = 0;
416 this.dynamic = false;
417 for (let i = selectors.length - 1; i >= 0; i--) {
418 const sel = selectors[i];
419 switch (sel.combinator) {
420 case undefined:
421 case Combinator.descendant:
422 siblingsToGroup = [];
423 currentGroup = [siblingsToGroup];
424 groups.push(currentGroup);
425 break;
426 case Combinator.child:
427 siblingsToGroup = [];
428 currentGroup.push(siblingsToGroup);
429 break;
430 case Combinator.adjacent:
431 case Combinator.sibling:
432 break;
433 default:
434 throw new Error(`Unsupported combinator "${sel.combinator}" for selector ${sel}.`);
435 }
436 this.specificity += sel.specificity;
437 if (sel.dynamic) {
438 this.dynamic = true;
439 }
440 siblingsToGroup.push(sel);
441 }
442 this.groups = groups.map((g) => new Selector.ChildGroup(g.map((selectors) => (selectors.length > 1 ? new Selector.SiblingGroup(selectors) : selectors[0]))));
443 this.last = selectors[selectors.length - 1];
444 }
445 toString() {
446 return this.selectors.join('');
447 }
448 match(node) {
449 return this.groups.every((group, i) => {
450 if (i === 0) {
451 node = group.getMatchingNode(node, true);
452 return !!node;
453 }
454 else {
455 let ancestor = node;
456 while ((ancestor = ancestor.parent ?? ancestor._modalParent)) {
457 if ((node = group.getMatchingNode(ancestor, true))) {
458 return true;
459 }
460 }
461 return false;
462 }
463 });
464 }
465 mayMatch(node) {
466 return false;
467 }
468 trackChanges(node, map) {
469 this.selectors.forEach((sel) => sel.trackChanges(node, map));
470 }
471 lookupSort(sorter, base) {
472 this.last.lookupSort(sorter, base || this);
473 }
474 accumulateChanges(node, map) {
475 if (!this.dynamic) {
476 return this.match(node);
477 }
478 const bounds = [];
479 const mayMatch = this.groups.every((group, i) => {
480 if (i === 0) {
481 const nextNode = group.getMatchingNode(node, false);
482 bounds.push({ left: node, right: node });
483 node = nextNode;
484 return !!node;
485 }
486 else {
487 let ancestor = node;
488 while ((ancestor = ancestor.parent)) {
489 const nextNode = group.getMatchingNode(ancestor, false);
490 if (nextNode) {
491 bounds.push({ left: ancestor, right: null });
492 node = nextNode;
493 return true;
494 }
495 }
496 return false;
497 }
498 });
499 // Calculating the right bounds for each selectors won't save much
500 if (!mayMatch) {
501 return false;
502 }
503 if (!map) {
504 return mayMatch;
505 }
506 for (let i = 0; i < this.groups.length; i++) {
507 const group = this.groups[i];
508 if (!group.dynamic) {
509 continue;
510 }
511 const bound = bounds[i];
512 let node = bound.left;
513 do {
514 if (group.mayMatch(node)) {
515 group.trackChanges(node, map);
516 }
517 } while (node !== bound.right && (node = node.parent));
518 }
519 return mayMatch;
520 }
521}
522export var Selector;
523(function (Selector) {
524 // Non-spec. Selector sequences are grouped by ancestor then by child combinators for easier backtracking.
525 class ChildGroup extends SelectorBase {
526 constructor(selectors) {
527 super();
528 this.selectors = selectors;
529 this.dynamic = selectors.some((sel) => sel.dynamic);
530 }
531 getMatchingNode(node, strict) {
532 const funcName = strict ? 'match' : 'mayMatch';
533 return this.selectors.every((sel, i) => (node = i === 0 ? node : node.parent) && sel[funcName](node)) ? node : null;
534 }
535 match(node) {
536 return this.getMatchingNode(node, true) != null;
537 }
538 mayMatch(node) {
539 return this.getMatchingNode(node, false) != null;
540 }
541 trackChanges(node, map) {
542 this.selectors.forEach((sel, i) => {
543 if (i === 0) {
544 node && sel.trackChanges(node, map);
545 }
546 else {
547 node = node.parent;
548 if (node && sel.mayMatch(node)) {
549 sel.trackChanges(node, map);
550 }
551 }
552 });
553 }
554 }
555 Selector.ChildGroup = ChildGroup;
556 class SiblingGroup extends SelectorBase {
557 constructor(selectors) {
558 super();
559 this.selectors = selectors;
560 this.dynamic = selectors.some((sel) => sel.dynamic);
561 }
562 match(node) {
563 return this.selectors.every((sel, i) => {
564 if (i === 0) {
565 return node && sel.match(node);
566 }
567 if (sel.combinator === Combinator.adjacent) {
568 node = getNodePreviousDirectSibling(node);
569 return node && sel.match(node);
570 }
571 // Sibling combinator
572 let isMatching = false;
573 eachNodePreviousGeneralSibling(node, (sibling) => {
574 isMatching = sel.match(sibling);
575 return !isMatching;
576 });
577 return isMatching;
578 });
579 }
580 mayMatch(node) {
581 return this.selectors.every((sel, i) => {
582 if (i === 0) {
583 return node && sel.mayMatch(node);
584 }
585 if (sel.combinator === Combinator.adjacent) {
586 node = getNodePreviousDirectSibling(node);
587 return node && sel.mayMatch(node);
588 }
589 // Sibling combinator
590 let isMatching = false;
591 eachNodePreviousGeneralSibling(node, (sibling) => {
592 isMatching = sel.mayMatch(sibling);
593 return !isMatching;
594 });
595 return isMatching;
596 });
597 }
598 trackChanges(node, map) {
599 this.selectors.forEach((sel, i) => {
600 if (i === 0) {
601 if (node) {
602 sel.trackChanges(node, map);
603 }
604 }
605 else {
606 if (sel.combinator === Combinator.adjacent) {
607 node = getNodePreviousDirectSibling(node);
608 if (node && sel.mayMatch(node)) {
609 sel.trackChanges(node, map);
610 }
611 }
612 else {
613 // Sibling combinator
614 let matchingSibling;
615 eachNodePreviousGeneralSibling(node, (sibling) => {
616 const isMatching = sel.mayMatch(sibling);
617 if (isMatching) {
618 matchingSibling = sibling;
619 }
620 return !isMatching;
621 });
622 if (matchingSibling) {
623 sel.trackChanges(matchingSibling, map);
624 }
625 }
626 }
627 });
628 }
629 }
630 Selector.SiblingGroup = SiblingGroup;
631})(Selector || (Selector = {}));
632export class RuleSet {
633 constructor(selectors, declarations) {
634 this.selectors = selectors;
635 this.declarations = declarations;
636 this.selectors.forEach((sel) => (sel.ruleset = this));
637 }
638 toString() {
639 let desc = `${this.selectors.join(', ')} {${this.declarations.map((d, i) => `${i === 0 ? ' ' : ''}${d.property}: ${d.value}`).join('; ')} }`;
640 if (this.mediaQueryString) {
641 desc = `@media ${this.mediaQueryString} { ${desc} }`;
642 }
643 return desc;
644 }
645 lookupSort(sorter) {
646 this.selectors.forEach((sel) => sel.lookupSort(sorter));
647 }
648}
649export function fromAstNode(astRule) {
650 const declarations = astRule.declarations.filter(isDeclaration).map(createDeclaration);
651 const selectors = astRule.selectors.map(createSelector);
652 return new RuleSet(selectors, declarations);
653}
654function createDeclaration(decl) {
655 return { property: isCssVariable(decl.property) ? decl.property : decl.property.toLowerCase(), value: decl.value };
656}
657function createSimpleSelectorFromAst(ast) {
658 if (ast.type === 'attribute') {
659 if (ast.name === 'class') {
660 return new ClassSelector(ast.value);
661 }
662 if (ast.name === 'id') {
663 return new IdSelector(ast.value);
664 }
665 return new AttributeSelector(ast.name, ast.action, ast.value, !!ast.ignoreCase);
666 }
667 if (ast.type === 'tag') {
668 return new TypeSelector(ast.name.replace('-', '').toLowerCase());
669 }
670 if (ast.type === 'pseudo') {
671 if (ast.name === 'is') {
672 return new IsFunctionalPseudoClassSelector(ast.name, ast.data);
673 }
674 if (ast.name === 'where') {
675 return new WhereFunctionalPseudoClassSelector(ast.name, ast.data);
676 }
677 if (ast.name === 'not') {
678 return new NotFunctionalPseudoClassSelector(ast.name, ast.data);
679 }
680 return new PseudoClassSelector(ast.name);
681 }
682 if (ast.type === 'universal') {
683 return new UniversalSelector();
684 }
685 return new InvalidSelector(new Error(ast.type));
686}
687function createSimpleSelectorSequenceFromAst(asts) {
688 if (asts.length === 0) {
689 return new InvalidSelector(new Error('Empty simple selector sequence.'));
690 }
691 if (asts.length === 1) {
692 return createSimpleSelectorFromAst(asts[0]);
693 }
694 const sequenceSelectors = [];
695 for (const ast of asts) {
696 const selector = createSimpleSelectorFromAst(ast);
697 if (selector instanceof InvalidSelector) {
698 return selector;
699 }
700 sequenceSelectors.push(selector);
701 }
702 return new SimpleSelectorSequence(sequenceSelectors);
703}
704function createSelectorFromAst(asts) {
705 let result;
706 if (asts.length === 0) {
707 return new InvalidSelector(new Error('Empty selector.'));
708 }
709 if (asts.length === 1) {
710 return createSimpleSelectorFromAst(asts[0]);
711 }
712 const simpleSelectorSequences = [];
713 let sequenceAsts = [];
714 let combinatorCount = 0;
715 for (const ast of asts) {
716 const combinator = Combinator[ast.type];
717 // Combinator means the end of a sequence
718 if (combinator != null) {
719 const selector = createSimpleSelectorSequenceFromAst(sequenceAsts);
720 if (selector instanceof InvalidSelector) {
721 return selector;
722 }
723 selector.combinator = combinator;
724 simpleSelectorSequences.push(selector);
725 combinatorCount++;
726 // Cleanup stored selectors for the new sequence to take place
727 sequenceAsts = [];
728 }
729 else {
730 sequenceAsts.push(ast);
731 }
732 }
733 if (combinatorCount > 0) {
734 // Create a sequence using the remaining selectors after the last combinator
735 if (sequenceAsts.length) {
736 const selector = createSimpleSelectorSequenceFromAst(sequenceAsts);
737 if (selector instanceof InvalidSelector) {
738 return selector;
739 }
740 simpleSelectorSequences.push(selector);
741 }
742 return new ComplexSelector(simpleSelectorSequences);
743 }
744 return createSimpleSelectorSequenceFromAst(sequenceAsts);
745}
746export function createSelector(sel) {
747 try {
748 const result = convertToCSSWhatSelector(sel);
749 if (!result?.length) {
750 return new InvalidSelector(new Error('Empty selector'));
751 }
752 return createSelectorFromAst(result[0]);
753 }
754 catch (e) {
755 return new InvalidSelector(e);
756 }
757}
758function isDeclaration(node) {
759 return node.type === 'declaration';
760}
761export function matchMediaQueryString(mediaQueryString, cachedQueries) {
762 // It can be a single or multiple queries in case of nested media queries
763 const mediaQueryStrings = mediaQueryString.split(MEDIA_QUERY_SEPARATOR);
764 return mediaQueryStrings.every((mq) => {
765 let isMatching;
766 // Query has already been validated
767 if (cachedQueries.includes(mq)) {
768 isMatching = true;
769 }
770 else {
771 isMatching = checkIfMediaQueryMatches(mq);
772 if (isMatching) {
773 cachedQueries.push(mq);
774 }
775 }
776 return isMatching;
777 });
778}
779export class SelectorScope {
780 constructor() {
781 this.id = {};
782 this.class = {};
783 this.type = {};
784 this.universal = [];
785 this.position = 0;
786 }
787 getSelectorCandidates(node) {
788 const { cssClasses, id, cssType } = node;
789 const selectorClasses = [this.universal, this.id[id], this.type[cssType]];
790 if (cssClasses && cssClasses.size) {
791 cssClasses.forEach((c) => selectorClasses.push(this.class[c]));
792 }
793 return selectorClasses.reduce((cur, next) => cur.concat(next || []), []);
794 }
795 sortById(id, sel) {
796 this.addToMap(this.id, id, sel);
797 }
798 sortByClass(cssClass, sel) {
799 this.addToMap(this.class, cssClass, sel);
800 }
801 sortByType(cssType, sel) {
802 this.addToMap(this.type, cssType, sel);
803 }
804 sortAsUniversal(sel) {
805 this.universal.push(this.makeDocSelector(sel));
806 }
807 addToMap(map, head, sel) {
808 if (!map[head]) {
809 map[head] = [];
810 }
811 map[head].push(this.makeDocSelector(sel));
812 }
813 makeDocSelector(sel) {
814 sel.pos = this.position++;
815 return sel;
816 }
817}
818export class MediaQuerySelectorScope extends SelectorScope {
819 constructor(mediaQueryString) {
820 super();
821 this._mediaQueryString = mediaQueryString;
822 }
823 get mediaQueryString() {
824 return this._mediaQueryString;
825 }
826}
827export class StyleSheetSelectorScope extends SelectorScope {
828 constructor(rulesets) {
829 super();
830 this.lookupRulesets(rulesets);
831 }
832 createMediaQuerySelectorScope(mediaQueryString) {
833 const selectorScope = new MediaQuerySelectorScope(mediaQueryString);
834 selectorScope.position = this.position;
835 if (this.mediaQuerySelectorScopes) {
836 this.mediaQuerySelectorScopes.push(selectorScope);
837 }
838 else {
839 this.mediaQuerySelectorScopes = [selectorScope];
840 }
841 return selectorScope;
842 }
843 lookupRulesets(rulesets) {
844 let lastMediaSelectorScope;
845 for (let i = 0, length = rulesets.length; i < length; i++) {
846 const ruleset = rulesets[i];
847 if (lastMediaSelectorScope && lastMediaSelectorScope.mediaQueryString !== ruleset.mediaQueryString) {
848 // Once done with current media query scope, update stylesheet scope position
849 this.position = lastMediaSelectorScope.position;
850 lastMediaSelectorScope = null;
851 }
852 if (ruleset.mediaQueryString) {
853 // Create media query selector scope and register selector lookups there
854 if (!lastMediaSelectorScope) {
855 lastMediaSelectorScope = this.createMediaQuerySelectorScope(ruleset.mediaQueryString);
856 }
857 ruleset.lookupSort(lastMediaSelectorScope);
858 }
859 else {
860 ruleset.lookupSort(this);
861 }
862 }
863 // If reference of last media selector scope is still kept, update stylesheet scope position
864 if (lastMediaSelectorScope) {
865 this.position = lastMediaSelectorScope.position;
866 lastMediaSelectorScope = null;
867 }
868 }
869 query(node) {
870 const selectorsMatch = new SelectorsMatch();
871 const selectors = this.getSelectorCandidates(node);
872 // Validate media queries and include their selectors if needed
873 if (this.mediaQuerySelectorScopes) {
874 // Cache media query results to avoid validations of other identical queries
875 const validatedMediaQueries = [];
876 for (let i = 0, length = this.mediaQuerySelectorScopes.length; i < length; i++) {
877 const selectorScope = this.mediaQuerySelectorScopes[i];
878 const isMatchingAllQueries = matchMediaQueryString(selectorScope.mediaQueryString, validatedMediaQueries);
879 if (isMatchingAllQueries) {
880 const mediaQuerySelectors = selectorScope.getSelectorCandidates(node);
881 selectors.push(...mediaQuerySelectors);
882 }
883 }
884 }
885 selectorsMatch.selectors = selectors.filter((sel) => sel.accumulateChanges(node, selectorsMatch)).sort((a, b) => a.specificity - b.specificity || a.pos - b.pos);
886 return selectorsMatch;
887 }
888}
889export class SelectorsMatch {
890 constructor() {
891 this.changeMap = new Map();
892 }
893 addAttribute(node, attribute) {
894 const deps = this.properties(node);
895 if (!deps.attributes) {
896 deps.attributes = new Set();
897 }
898 deps.attributes.add(attribute);
899 }
900 addPseudoClass(node, pseudoClass) {
901 const deps = this.properties(node);
902 if (!deps.pseudoClasses) {
903 deps.pseudoClasses = new Set();
904 }
905 deps.pseudoClasses.add(pseudoClass);
906 }
907 properties(node) {
908 let set = this.changeMap.get(node);
909 if (!set) {
910 this.changeMap.set(node, (set = {}));
911 }
912 return set;
913 }
914}
915export const CSSHelper = {
916 createSelector,
917 SelectorCore,
918 SimpleSelector,
919 InvalidSelector,
920 UniversalSelector,
921 TypeSelector,
922 ClassSelector,
923 AttributeSelector,
924 PseudoClassSelector,
925 SimpleSelectorSequence,
926 Selector,
927 RuleSet,
928 SelectorScope,
929 MediaQuerySelectorScope,
930 StyleSheetSelectorScope,
931 fromAstNode,
932 SelectorsMatch,
933};
934//# sourceMappingURL=css-selector.js.map
\No newline at end of file