From b11676a0bc3549e5a81c94314d9e15d27efa4612 Mon Sep 17 00:00:00 2001 From: Felix Boehm <188768+fb55@users.noreply.github.com> Date: Sat, 3 Oct 2026 23:14:32 +0100 Subject: [PATCH] fix(pseudos): avoid overlapping whitespace in nth parsing Co-Authored-By: Claude Fable 5.1 --- src/pseudo-selectors/filters.ts | 2 +- test/pseudo-classes.ts | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/pseudo-selectors/filters.ts b/src/pseudo-selectors/filters.ts index 83f0a69d..ea98cd98 100644 --- a/src/pseudo-selectors/filters.ts +++ b/src/pseudo-selectors/filters.ts @@ -32,7 +32,7 @@ function extendedFilter(tag: string[], range: string[]): boolean { } /** @see {@link https://www.w3.org/TR/selectors-4/#the-nth-child-pseudo} */ -const nthOfRegex = /^(.+?)\s+of\s+(.+)$/is; +const nthOfRegex = /^(.+?\s)of\s+(.+)$/is; /** A pre-compiled pseudo filter. */ export type Filter = ( diff --git a/test/pseudo-classes.ts b/test/pseudo-classes.ts index 92a58545..1feeb8ce 100644 --- a/test/pseudo-classes.ts +++ b/test/pseudo-classes.ts @@ -9,6 +9,13 @@ const dom = parseDOM( "

In the end, it doesn't really Matter.

Indeed-that's a delicate matter.
", ) as Element[]; +// The previous pattern backtracked on long whitespace, taking minutes here. +it("should reject a long whitespace nth argument without backtracking", () => { + expect(() => + CSSselect.compile(`:nth-child(${" ".repeat(350_000)})`), + ).toThrow("n-th rule couldn't be parsed"); +}, 2000); + describe(":icontains", () => { describe("ignore case", () => { it("should match full string", () => {