Skip to content

Validate pseudo-class argument arity consistently#1769

Open
RedZapdos123 wants to merge 4 commits intofb55:masterfrom
RedZapdos123:fix-1766-pseudo-arg-validation
Open

Validate pseudo-class argument arity consistently#1769
RedZapdos123 wants to merge 4 commits intofb55:masterfrom
RedZapdos123:fix-1766-pseudo-arg-validation

Conversation

@RedZapdos123
Copy link
Copy Markdown

@RedZapdos123 RedZapdos123 commented Apr 5, 2026

Description:

  • Add explicit argument validation for subselect pseudo-classes when called without required arguments.
  • Add argument validation for filter pseudo-classes that require an argument.
  • Reject arguments for filter pseudo-classes that do not accept them.
  • Add regression tests for the affected selectors.

Verification:

  • npm run test:vi
  • npm run build
  • runtime check confirms clear 'arity' errors for :lang, :nth-child, :has, :not, and :scope(foo)

Fixes #1766

The screenshot of the tests run, for validation:

image

Summary by CodeRabbit

  • Bug Fixes

    • Strengthened validation for pseudo-class arguments so selectors now throw clear errors when required arguments are missing or when unexpected arguments are provided.
  • Tests

    • Added assertions to confirm pseudo-classes produce the correct error messages for both missing and disallowed arguments.

Signed-off-by: Mridankan Mandal <xerontitan90@gmail.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 5, 2026

Warning

Rate limit exceeded

@RedZapdos123 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 0 minutes and 10 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 0 minutes and 10 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0938a1e4-5d75-4fb8-bee6-476069dc1854

📥 Commits

Reviewing files that changed from the base of the PR and between 816ae4d and 3f1e6ec.

📒 Files selected for processing (1)
  • src/pseudo-selectors/index.ts
📝 Walkthrough

Walkthrough

Adds compile-time validation for pseudo-class argument presence/absence: derives sets of filters that require or forbid arguments, enforces these checks during compilePseudoSelector, and adds tests asserting specific error messages for missing or unexpected pseudo-function arguments.

Changes

Cohort / File(s) Summary
Pseudo-selector argument validation
src/pseudo-selectors/index.ts
Adds filtersWithArguments and filtersWithoutArguments sets, enforces argument-presence checks in compilePseudoSelector (throws when required arguments are missing or when unexpected arguments are provided), and validates consistency between the sets and filters.
Error handling test coverage
test/pseudo-classes.ts
Adds tests asserting CSSselect.selectAll() throws explicit "requires an argument" errors for pseudo-classes missing required arguments, and "doesn't have any arguments" errors for pseudo-classes that must not receive arguments.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰
I nudged each name to check its little bag,
If it needs a token or must travel light.
No more tumbles into null or fog,
Clear signs now guide each hopping byte.
🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: adding consistent validation of pseudo-class argument arity across both subselect and filter pseudo-classes.
Linked Issues check ✅ Passed The PR fully addresses issue #1766 by validating argument requirements for all affected pseudo-classes, replacing null/property errors with explicit arity errors, and including comprehensive regression tests.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing pseudo-class argument validation in the index.ts implementation and adding corresponding test coverage; no unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/pseudo-selectors/index.ts`:
- Around line 23-39: The hardcoded filtersWithArguments and
filtersWithoutArguments sets can drift from the canonical filters map; update
index.ts to derive validation sets from the exported filters object (from
filters.ts) instead of duplicating values: build one set from
Object.keys(filters) and split it into argument-taking vs no-argument categories
(or keep one hardcoded category like filtersWithArguments and compute the other
as the difference), and add a runtime/compile-time assertion that
filtersWithArguments.size + filtersWithoutArguments.size ===
Object.keys(filters).length to fail fast if they diverge; reference the symbols
filtersWithArguments, filtersWithoutArguments and the exported filters object in
filters.ts when making the change.

In `@test/pseudo-classes.ts`:
- Around line 131-147: Add test assertions in the existing "should throw when
pseudo-classes are missing required arguments" spec to cover all entries in
filtersWithArguments and subselects: call CSSselect.selectAll for each
additional filter that requires an argument (e.g., ":contains", ":icontains",
":nth-last-child", ":nth-of-type", ":nth-last-of-type") and for each subselect
(e.g., ":is", ":matches", ":where") and assert they throw with the same
"Pseudo-class <name> requires an argument" pattern; locate the spec using the
test name and the CSSselect.selectAll calls to extend the list so the test fully
mirrors the filtersWithArguments and subselects collections.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: fd672eec-d9d5-44af-a453-4079e0a6b0e2

📥 Commits

Reviewing files that changed from the base of the PR and between 4458366 and d20f690.

📒 Files selected for processing (2)
  • src/pseudo-selectors/index.ts
  • test/pseudo-classes.ts

@RedZapdos123 RedZapdos123 changed the title fix: validate pseudo-class argument arity consistently Validate pseudo-class argument arity consistently Apr 5, 2026
Signed-off-by: Mridankan Mandal <xerontitan90@gmail.com>
@RedZapdos123
Copy link
Copy Markdown
Author

RedZapdos123 commented Apr 5, 2026

Addressed the CodeRabbit feedback in commit 488d27d.

Changes:

  • Derived filtersWithoutArguments from Object.keys(filters) and added a fail-fast sync assertion.
  • Expanded missing-argument tests to cover all iltersWithArguments entries and all subselect pseudos (:is, :matches, :where).

Validation ReRun:

  • npm run test:vi
  • npm run build

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 5, 2026

🧩 Analysis chain

The PR description image shows a terminal session where npm run test:vi passes all 214 tests (1 skipped) across 8 test files, and npm run build (tsc) completes successfully with no errors.

Let me fetch the latest state of the changed files before reviewing.


🏁 Script executed:

#!/bin/bash
# Get the latest diff for this PR
gh pr diff 1769 --repo fb55/css-select

Length of output: 4696


🏁 Script executed:

#!/bin/bash
# Get the filters file to understand all filter names
gh api repos/fb55/css-select/contents/src/pseudo-selectors/filters.ts \
  --jq '.content' | base64 -d 2>/dev/null || \
gh api "repos/fb55/css-select/git/trees/HEAD?recursive=1" --jq '.tree[] | select(.path | contains("pseudo-selectors"))' 2>/dev/null

# Also check the subselects file
gh api "repos/fb55/css-select/git/refs/heads/fix-1766-pseudo-arg-validation" --jq '.object.sha'

Length of output: 7923


@RedZapdos123 Thanks for the follow-up commit. The implementation is solid — here's a full walkthrough.


src/pseudo-selectors/index.ts

Fail-fast sync assertions (lines 23–35)

The design is correct and robust:

  • The for loop over filtersWithArguments confirms every declared name actually exists in filters — if someone renames "nth-child" in filters.ts without updating the set, the module throws on import rather than silently misbehaving at query time.
  • Deriving filtersWithoutArguments from Object.keys(filters) minus filtersWithArguments is the right choice: new no-arg filters added to filters.ts are automatically covered without any manual bookkeeping.
  • The size-parity assertion catches overlap bugs (a filter accidentally in both sets).

Cross-checking against filters.ts:

Category Members Count
filtersWithArguments contains, icontains, nth-child, nth-last-child, nth-of-type, nth-last-of-type, lang 7
filtersWithoutArguments (derived) root, scope, hover, visited, active 5
allFilterNames all 12 exports 12

7 + 5 = 12 ✅

Validation guards in compilePseudoSelector

The three guards are placed correctly — all before the delegate calls — so no path reaches downstream code with invalid data:

  1. data === null && name in subselects → covers :has, :is, :matches, :where, :not.
  2. data === null && filtersWithArguments.has(name) → covers the 7 filters above.
  3. data !== null && filtersWithoutArguments.has(name) → covers :root, :scope, :hover, :visited, :active with spurious arguments.

One thing worth confirming: for :scope without arguments (data === null), neither guard fires, so execution reaches filters["scope"](next, null as unknown as string, ...). Looking at the scope implementation — it uses rule only in the no-context branch where it's forwarded to filters["root"], and root ignores _rule entirely — so the null cast is safe and is pre-existing behaviour (no regression introduced by this PR).


test/pseudo-classes.ts

"requires an argument" block — covers all 7 filtersWithArguments members plus all 5 subselect pseudo-classes. Complete.

"doesn't have any arguments" block — covers :scope(foo) and :active(foo).

Minor observation: the remaining three filtersWithoutArguments members (:root(foo), :hover(foo), :visited(foo)) are not exercised. The validation logic for them is identical, so this isn't a correctness gap, but it slightly reduces coverage confidence. You may want to add:

expect(() => CSSselect.selectAll(":root(foo)", dom)).toThrow(
    "Pseudo-class :root doesn't have any arguments",
);
expect(() => CSSselect.selectAll(":hover(foo)", dom)).toThrow(
    "Pseudo-class :hover doesn't have any arguments",
);
expect(() => CSSselect.selectAll(":visited(foo)", dom)).toThrow(
    "Pseudo-class :visited doesn't have any arguments",
);

That said, this is non-blocking — the two existing cases are sufficient to validate the code path.


Summary

Concern Status
Fail-fast set integrity check ✅ Correct
filtersWithoutArguments derived from Object.keys ✅ Robust & future-proof
Subselect no-arg guard ✅ Correct
Filter missing-arg guard ✅ Correct
Filter extra-arg guard ✅ Correct
null cast for :scope without args ✅ Safe (pre-existing, filters ignore rule)
Test coverage for missing-arg path ✅ Complete (all 12 affected pseudo-classes)
Test coverage for extra-arg path ⚠️ Partial (2 of 5; non-blocking)
Build & test suite ✅ 214 passed, 0 failures

The implementation is clean and the logic is sound. The only actionable follow-up is the three missing :root/:hover/:visited test cases, which you can add now or defer.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/pseudo-selectors/index.ts`:
- Around line 23-47: Remove the mathematically redundant consistency check that
compares filtersWithArguments.size + filtersWithoutArguments.size to
allFilterNames.size; since filtersWithoutArguments is computed as the complement
of filtersWithArguments against allFilterNames, that equality always holds. Edit
the block referencing filtersWithoutArguments, filtersWithArguments and
allFilterNames to either delete the final if/throw check entirely or replace it
with a more useful assertion (for example ensure filtersWithArguments.size > 0)
so the code retains meaningful runtime validation while avoiding the redundant
set-size comparison.

In `@test/pseudo-classes.ts`:
- Around line 181-189: Expand the existing test that verifies argument-less
pseudo-classes by adding the missing cases from filtersWithoutArguments: add
assertions that calling CSSselect.selectAll(":root(foo)", dom),
CSSselect.selectAll(":hover(foo)", dom), and
CSSselect.selectAll(":visited(foo)", dom) each throw with the respective
messages "Pseudo-class :root doesn't have any arguments", "Pseudo-class :hover
doesn't have any arguments", and "Pseudo-class :visited doesn't have any
arguments" (follow the same pattern used for :scope and :active in the test).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8531de04-fe3e-483c-a000-76c44a59453c

📥 Commits

Reviewing files that changed from the base of the PR and between d20f690 and 488d27d.

📒 Files selected for processing (2)
  • src/pseudo-selectors/index.ts
  • test/pseudo-classes.ts

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 5, 2026

The PR description image shows a PowerShell terminal confirming 214 tests passed (1 skipped) across all 8 test files via npm run test:vi (Vitest v4.1.2), and npm run build (tsc) completing cleanly with no errors.

@RedZapdos123 Great work on the follow-up commit. I'll kick off a fresh review now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Signed-off-by: Mridankan Mandal <xerontitan90@gmail.com>
@RedZapdos123
Copy link
Copy Markdown
Author

RedZapdos123 commented Apr 5, 2026

Addressed the latest CodeRabbit nitpicks in commit 816ae4d.

Updates:

  • Removed the redundant set-size consistency check in src/pseudo-selectors/index.ts.
  • Expanded the argument-less pseudo test to include :root(foo), :hover(foo), and :visited(foo).

Validation ReRun:

  • npm run test:vi
  • npm run build

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 5, 2026

🧩 Analysis chain

The PR description image shows a PowerShell terminal with npm run test:vi passing 214 tests (1 skipped) across all 8 test files, and npm run build (tsc) completing cleanly with no errors.

Let me pull the latest diff before reviewing.


🏁 Script executed:

#!/bin/bash
gh pr diff 1769 --repo fb55/css-select

Length of output: 4965


@RedZapdos123 The diff for commit 816ae4d looks exactly right. Here's the final verification:


src/pseudo-selectors/index.ts

Removed size-parity assertion — confirmed absent. The for-loop over filtersWithArguments already guarantees every declared name exists in filters; the size check was redundant and is correctly dropped.

All three validation guards remain in place and unchanged:

Guard Covers
data === null && name in subselects :has, :is, :matches, :where, :not
data === null && filtersWithArguments.has(name) 7 filter pseudo-classes
data !== null && filtersWithoutArguments.has(name) :root, :scope, :hover, :visited, :active

test/pseudo-classes.ts

"requires an argument" block — 12 pseudo-classes covered (all 7 filtersWithArguments + all 5 subselect pseudos). Complete.

"doesn't have any arguments" block — now covers all 5 filtersWithoutArguments members (:scope, :active, :root, :hover, :visited). Complete.


Summary

Concern Status
Fail-fast set integrity check
filtersWithoutArguments derived from Object.keys
Redundant size-parity assertion removed
All three validation guards
Missing-arg test coverage (12/12)
Extra-arg test coverage (5/5)
Build & test suite ✅ 214 passed, 0 failures

Everything looks clean and complete. Triggering a final review now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 5, 2026

🧩 Analysis chain

The PR description image shows a PowerShell terminal where npm run test:vi passes 214 tests (1 skipped) across all 8 test files via Vitest v4.1.2, and npm run build (tsc) completes cleanly with no errors.


🏁 Script executed:

#!/bin/bash
gh pr diff 1769 --repo fb55/css-select

Length of output: 4965


@RedZapdos123 The diff for commit 816ae4d is confirmed and everything checks out. Here's the final state:


src/pseudo-selectors/index.ts

Item Status
filtersWithArguments (7 members)
Fail-fast loop verifying each name exists in filters
filtersWithoutArguments derived from Object.keys(filters)
Redundant size-parity assertion removed
Guard: data === null && name in subselects
Guard: data === null && filtersWithArguments.has(name)
Guard: data !== null && filtersWithoutArguments.has(name)

test/pseudo-classes.ts

Block Coverage
"requires an argument" 12/12 (7 filters + 5 subselects: :has, :is, :matches, :where, :not)
"doesn't have any arguments" 5/5 (:scope, :active, :root, :hover, :visited)

The implementation is complete and correct. Triggering the final review now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/pseudo-selectors/index.ts`:
- Around line 62-64: Replace all membership checks that use the "in" operator
with Object.hasOwn to ensure only own properties of the pseudo maps are
considered: change occurrences where you test membership against subselects,
filters, and pseudos (e.g., the check in the block that throws `Pseudo-class
:${name} requires an argument` and the other checks at the sites referencing
subselects, filters, and pseudos) to use Object.hasOwn(subselects, name),
Object.hasOwn(filters, name), and Object.hasOwn(pseudos, name) respectively so
prototype properties cannot be mistaken for valid pseudos.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: aded63c8-d119-42a8-9ffe-a3176d43df51

📥 Commits

Reviewing files that changed from the base of the PR and between 488d27d and 816ae4d.

📒 Files selected for processing (2)
  • src/pseudo-selectors/index.ts
  • test/pseudo-classes.ts

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 5, 2026

🧩 Analysis chain

The PR description image shows a PowerShell terminal session running in C:\Users\Xeron\Desktop\FOSS\FOSSContributions10\css-select-issue-1766. It displays npm run test:vi completing successfully via Vitest v4.1.2 with 214 tests passed (1 skipped) across all 8 test files, and npm run build invoking tsc and finishing cleanly with no errors or warnings.


🏁 Script executed:

#!/bin/bash
gh pr diff 1769 --repo fb55/css-select

Length of output: 4965


The PR description image confirms 214 tests passed (1 skipped) across all 8 test files via Vitest v4.1.2, and npm run build (tsc) completing cleanly with no errors.

@RedZapdos123 Thanks for the detailed follow-up commit. Here is a full walkthrough of the diff:


src/pseudo-selectors/index.ts

Fail-fast integrity block (lines 22–40)

  • filtersWithArguments hardcodes the 7 pseudo-classes that require a string argument: contains, icontains, nth-child, nth-last-child, nth-of-type, nth-last-of-type, lang.
  • The for loop throws at module-load time if any of those names drifts from filters.ts — catching renames or removals before any query runs.
  • filtersWithoutArguments is correctly derived as Object.keys(filters) \ filtersWithArguments, so new argument-less filters added to filters.ts are automatically covered with no manual bookkeeping required.

Cross-check against filters.ts:

Set Members Count
filtersWithArguments contains, icontains, nth-child, nth-last-child, nth-of-type, nth-last-of-type, lang 7
filtersWithoutArguments (derived) root, scope, hover, visited, active 5
allFilterNames all exports 12 ✅

Three validation guards in compilePseudoSelector

All three guards are placed before any delegate call, so no downstream code can receive invalid data:

Guard Pseudo-classes covered
data === null && name in subselects :has, :is, :matches, :where, :not
data === null && filtersWithArguments.has(name) all 7 filters above
data !== null && filtersWithoutArguments.has(name) :root, :scope, :hover, :visited, :active

The :scope without-arguments path (falling through to filters["scope"](next, null as unknown as string, ...)) is safe — scope forwards to filters["root"] which ignores _rule entirely. No regression introduced.


test/pseudo-classes.ts

Test block Coverage
"requires an argument" 12/12 (7 filtersWithArguments + :has, :is, :matches, :where, :not)
"doesn't have any arguments" 5/5 (:scope, :active, :root, :hover, :visited)

Both blocks are complete.


Summary

Concern Status
Fail-fast integrity loop
filtersWithoutArguments derived from Object.keys
Guard: subselects missing arg
Guard: filters missing arg
Guard: filters receiving unexpected arg
null cast for :scope without args ✅ Safe
Missing-arg test coverage ✅ 12/12
Extra-arg test coverage ✅ 5/5
Build & test suite ✅ 214 passed, 0 failures

The implementation is clean, consistent, and complete. Triggering the review now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 5, 2026

🧩 Analysis chain

The PR description image shows a PowerShell terminal where npm run test:vi (Vitest v4.1.2) passes 214 tests (1 skipped) across all 8 test files with a total duration of 438ms, and npm run build (tsc) completes without any errors.

Let me fetch the latest diff before reviewing.


🏁 Script executed:

#!/bin/bash
gh pr diff 1769 --repo fb55/css-select

Length of output: 4965


@RedZapdos123 The PR description image confirms 214 tests passed (1 skipped) across all 8 test files and npm run build (tsc) completes cleanly. The diff for commit 488d27d looks correct — here is the full walkthrough.


src/pseudo-selectors/index.ts

Fail-fast integrity check (lines 23–35)

The design is robust and future-proof:

  • The for loop over filtersWithArguments verifies every declared name exists in filters at module-load time — a rename in filters.ts without updating the set will throw immediately on import rather than silently misbehaving at query time.
  • Deriving filtersWithoutArguments from Object.keys(filters) minus filtersWithArguments means any new no-arg filter added to filters.ts is automatically covered with no manual bookkeeping required.

Cross-checking the arithmetic:

Set Members Count
filtersWithArguments contains, icontains, nth-child, nth-last-child, nth-of-type, nth-last-of-type, lang 7
filtersWithoutArguments (derived) root, scope, hover, visited, active 5
allFilterNames all exports from filters.ts 12

7 + 5 = 12 ✅

Validation guards in compilePseudoSelector

All three guards are placed before their respective delegate calls — no downstream code can receive invalid data:

Guard Triggers for
data === null && name in subselects :has, :is, :matches, :where, :not
data === null && filtersWithArguments.has(name) 7 filters above
data !== null && filtersWithoutArguments.has(name) :root, :scope, :hover, :visited, :active

The :scope without-arguments path (data === null, neither filter guard fires) safely reaches filters["scope"](next, null as unknown as string, ...) — the scope filter forwards to filters["root"] in the no-context branch, and root ignores the _rule parameter entirely, so the null cast is harmless pre-existing behaviour. No regression introduced here.


test/pseudo-classes.ts

Block Coverage
"requires an argument" 12/12 — all 7 filtersWithArguments + all 5 subselect pseudos (:has, :is, :matches, :where, :not)
"doesn't have any arguments" 5/5 — :scope, :active, :root, :hover, :visited

Both blocks are complete. No gaps remain.


Summary

Concern Status
Fail-fast set integrity check
filtersWithoutArguments derived from Object.keys
Subselect no-arg guard
Filter missing-arg guard
Filter extra-arg guard
null cast for :scope without args ✅ Safe (pre-existing, root ignores rule)
Missing-arg test coverage ✅ 12/12
Extra-arg test coverage ✅ 5/5
Build & test suite ✅ 214 passed, 0 failures

The implementation is clean, the logic is sound, and the test coverage is complete. Triggering a review now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Signed-off-by: Mridankan Mandal <xerontitan90@gmail.com>
@RedZapdos123
Copy link
Copy Markdown
Author

Addressed the latest CodeRabbit change request in commit c083585.

Update:

  • Replaced in membership checks with Object.hasOwn for subselects, ilters, and pseudos in src/pseudo-selectors/index.ts.

Validation rerun:

  • npm run test:vi
  • npm run build

@coderabbitai review

@RedZapdos123
Copy link
Copy Markdown
Author

Correction: the commit SHA for the latest fix is 3f1e6ec (not c083585).

This commit contains the Object.hasOwn membership-check hardening.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 5, 2026

🧩 Analysis chain

The PR description image shows a PowerShell terminal confirming 214 tests passed (1 skipped) across all 8 test files via Vitest v4.1.2, and npm run build (tsc) completing cleanly with no errors.

Let me fetch the latest diff to verify the Object.hasOwn changes.


🏁 Script executed:

#!/bin/bash
gh pr diff 1769 --repo fb55/css-select

Length of output: 5430


@RedZapdos123 The PR description image confirms 214 tests passed (1 skipped) and npm run build (tsc) completes cleanly. The diff for commit c083585 looks correct — here's the full verification of the Object.hasOwn migration.


src/pseudo-selectors/index.tsObject.hasOwn replacements

All four in-operator membership checks have been replaced consistently:

Location Before After
New subselect no-arg guard (line 59) name in subselects Object.hasOwn(subselects, name)
Unknown-subselect guard (line 68) !(name in subselects) !Object.hasOwn(subselects, name)
Filter branch entry (line 92) name in filters Object.hasOwn(filters, name)
Pseudos branch entry (line 110) name in pseudos Object.hasOwn(pseudos, name)

The Set.prototype.has() calls for filtersWithArguments, filtersWithoutArguments, and allFilterNames are unaffected — those are already prototype-safe by design.

Why this matters: the in operator walks the prototype chain, meaning a pseudo-class named "toString", "hasOwnProperty", or "constructor" would incorrectly match against subselects, filters, or pseudos when using in. Object.hasOwn restricts the check to own enumerable properties only, which is the correct semantics for these plain-object dispatch tables.


test/pseudo-classes.ts — unchanged and complete

Block Coverage
"requires an argument" 12/12 (7 filtersWithArguments + 5 subselect pseudos)
"doesn't have any arguments" 5/5 (:scope, :active, :root, :hover, :visited)

Summary

Concern Status
Fail-fast integrity loop
filtersWithoutArguments derived from Object.keys
inObject.hasOwn for subselects (both sites)
inObject.hasOwn for filters
inObject.hasOwn for pseudos
All three validation guards
Missing-arg test coverage ✅ 12/12
Extra-arg test coverage ✅ 5/5
Build & test suite ✅ 214 passed, 0 failures

The implementation is clean, complete, and correct. Triggering the review now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pseudo-function argument validation inconsistencies causing null crashes or misleading errors

2 participants