Skip to content

Commit

Permalink
Rework Compound selectors
Browse files Browse the repository at this point in the history
  • Loading branch information
Jym77 committed Nov 22, 2023
1 parent d2a05fc commit 77cb81b
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 296 deletions.
7 changes: 7 additions & 0 deletions .changeset/metal-pandas-end.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@siteimprove/alfa-selector": minor
---

**Breaking:** `Compound` selectors are now built on top of Iterable, rather than re-inventing chained lists.

That is, `Compound#left` and `Compound#right` are no more available, but `Compound.selectors` replaces them.
2 changes: 1 addition & 1 deletion .changeset/twenty-seahorses-try.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@

**Breaking:** `List` selectors are now built on top of Iterable, rather than re-inventing chained lists.

That is, `List#left` and List#right`are no more available, but`List.selectors` replaces them.
That is, `List#left` and `List#right` are no more available, but `List.selectors` replaces them.
19 changes: 10 additions & 9 deletions packages/alfa-cascade/src/selector-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,10 @@ function getKeySelector(selector: Selector): Id | Class | Type | null {
}

if (isCompound(selector)) {
return getKeySelector(selector.left) ?? getKeySelector(selector.right);
return Iterable.find(
Iterable.map(selector.selectors, getKeySelector),
(selector) => selector !== null,
).getOr(null);
}

if (isComplex(selector)) {
Expand Down Expand Up @@ -477,11 +480,7 @@ function getSpecificity(selector: Selector): Specificity {
const queue: Array<Selector> = [selector];

while (queue.length > 0) {
const selector = queue.pop();

if (selector === undefined) {
break;
}
const selector = queue.pop()!;

if (isId(selector)) {
a++;
Expand All @@ -493,8 +492,10 @@ function getSpecificity(selector: Selector): Specificity {
b++;
} else if (isType(selector) || isPseudoElement(selector)) {
c++;
} else if (isCompound(selector) || isComplex(selector)) {
} else if (isComplex(selector)) {
queue.push(selector.left, selector.right);
} else if (isCompound(selector)) {
queue.push(...selector.selectors);
}
}

Expand All @@ -519,8 +520,8 @@ function canReject(selector: Selector, filter: AncestorFilter): boolean {
if (isCompound(selector)) {
// Compound selectors are right-leaning, so recurse to the left first as it
// is likely the shortest branch.
return (
canReject(selector.left, filter) || canReject(selector.right, filter)
return Iterable.some(selector.selectors, (selector) =>
canReject(selector, filter),
);
}

Expand Down
53 changes: 22 additions & 31 deletions packages/alfa-selector/src/selector/compound.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Array } from "@siteimprove/alfa-array";
import { Token } from "@siteimprove/alfa-css";
import type { Element } from "@siteimprove/alfa-dom";
import { Iterable } from "@siteimprove/alfa-iterable";
import { Parser } from "@siteimprove/alfa-parser";
import { Slice } from "@siteimprove/alfa-slice";

import type { Context } from "../context";
import type { Absolute } from "../selector";
import type { Absolute } from "./index";

import { Selector } from "./selector";
import { Simple } from "./simple";
Expand All @@ -18,31 +19,30 @@ const { map, oneOrMore } = Parser;
* @public
*/
export class Compound extends Selector<"compound"> {
public static of(left: Simple, right: Simple | Compound): Compound {
return new Compound(left, right);
public static of(...selectors: Array<Simple>): Compound {
return new Compound(selectors);
}

private readonly _left: Simple;
private readonly _right: Simple | Compound;
private readonly _selectors: Array<Simple>;
private readonly _length: number;

private constructor(left: Simple, right: Simple | Compound) {
private constructor(selectors: Array<Simple>) {
super("compound");
this._left = left;
this._right = right;
this._selectors = selectors;
this._length = selectors.length;
}

public get left(): Simple {
return this._left;
public get selectors(): Iterable<Simple> {
return this._selectors;
}

public get right(): Simple | Compound {
return this._right;
public get length(): number {
return this._length;
}

public matches(element: Element, context?: Context): boolean {
return (
this._left.matches(element, context) &&
this._right.matches(element, context)
return this._selectors.every((selector) =>
selector.matches(element, context),
);
}

Expand All @@ -53,8 +53,7 @@ export class Compound extends Selector<"compound"> {
public equals(value: unknown): boolean {
return (
value instanceof Compound &&
value._left.equals(this._left) &&
value._right.equals(this._right)
Array.equals(value._selectors, this._selectors)
);
}

Expand All @@ -65,13 +64,12 @@ export class Compound extends Selector<"compound"> {
public toJSON(): Compound.JSON {
return {
...super.toJSON(),
left: this._left.toJSON(),
right: this._right.toJSON(),
selectors: this._selectors.map((selector) => selector.toJSON()),
};
}

public toString(): string {
return `${this._left}${this._right}`;
return this._selectors.map((selector) => selector.toString()).join("");
}
}

Expand All @@ -80,8 +78,7 @@ export class Compound extends Selector<"compound"> {
*/
export namespace Compound {
export interface JSON extends Selector.JSON<"compound"> {
left: Simple.JSON;
right: Simple.JSON | JSON;
selectors: Array<Simple.JSON>;
}

export function isCompound(value: unknown): value is Compound {
Expand All @@ -96,13 +93,7 @@ export namespace Compound {
export const parseCompound = (
parseSelector: () => Parser<Slice<Token>, Absolute, string>,
) =>
map(oneOrMore(Simple.parse(parseSelector)), (result) => {
const [left, ...selectors] = Iterable.reverse(result);

return Iterable.reduce(
selectors,
(right, left) => Compound.of(left, right),
left as Simple | Compound,
);
});
map(oneOrMore(Simple.parse(parseSelector)), (result) =>
result.length === 1 ? result[0] : Compound.of(...result),
);
}
Loading

0 comments on commit 77cb81b

Please sign in to comment.