postcss-selector-parser

A CSS selector parser and AST toolkit for building PostCSS plugins that inspect, transform, and rewrite selectors.

Library
npm
v7.1.6
215stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
78/100Good
Development Activity84
Maintenance72
Community76
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
82/100Excellent
Architecture82
Code Quality85
Innovation85
Learning Curve75

postcss-selector-parser converts CSS selector strings into a structured, mutable abstract syntax tree, and serializes that tree back into valid CSS — losslessly preserving whitespace and comments by default. It ships as the selector-parsing engine behind many of the PostCSS ecosystem’s plugins, handling the full selector grammar: combinators, pseudo-classes and pseudo-elements, attribute selectors, id/class/tag selectors, nesting (&), comments, and non-standard syntax like /deep/.

Consumers walk the resulting tree with typed visitor methods (walkClasses, walkAttributes, walkPseudos, etc.), mutate nodes in place, and call toString() or processSync() to get back a selector string — either the original formatting or a normalized, whitespace-collapsed form. A guarded maximum nesting depth (default 256, added after CVE-2026-9358) protects the recursive parse/stringify routines from stack-overflow denial-of-service on adversarial input.

What You Get

  • A full CSS selector parser producing an inspectable node tree (Root, Selector, Tag, ClassName, Id, Attribute, Pseudo, Combinator, Comment, Nesting, String, Universal)
  • Typed walk*/each* traversal methods for every node kind, plus a generic walk() with early-exit support
  • Synchronous and Promise-based processing APIs (processSync/transformSync/astSync vs process/transform/ast)
  • Lossless round-tripping that preserves original whitespace, comments and raw values, with an opt-in lossy/normalized output mode
  • Node constructors (parser.attribute(), parser.className(), parser.pseudo(), etc.) for programmatically building new selector fragments
  • A configurable maxNestingDepth guard against pathological or adversarial selector nesting

Common Use Cases

  • Writing a PostCSS plugin that needs to inspect or rewrite selectors (adding vendor prefixes, scoping classes for CSS Modules, rewriting :global()/:local())
  • Linting or auditing CSS selectors for disallowed patterns (universal selectors, deep combinators, specific pseudo-classes)
  • Build tooling that needs to safely parse untrusted or third-party CSS selectors without risking stack overflow on maliciously nested input
  • Generating or transforming selectors programmatically (renaming classes, namespacing ids, stripping comments) for CSS-in-JS or CSS Modules pipelines
  • Static analysis tools that need an accurate selector AST rather than regex-based selector parsing

Under The Hood

Architecture The library is layered as tokenizer → parser → AST → processor. src/tokenize.js turns a selector string into a character-code token stream (src/tokenTypes.js), src/parser.js consumes that stream to build a tree of node classes defined in src/selectors/ (Root, Selector, Tag, ClassName, Attribute, Pseudo, Combinator, Comment, Nesting, String, Universal), and a shared Container base class implements the walk*/each* traversal methods every composite node inherits. src/processor.js is the public orchestration layer: it wraps _root/_run/_runSync around the parser and the tree’s own toString(), exposing sync and Promise-based variants (processSync/process, astSync/ast, transformSync/transform) that optionally write the transformed selector back onto a postcss.Rule. This separation means the tokenizer and grammar can evolve independently of the public processing API, and the recursive walk/serialize path is the one piece bounded by maxNestingDepth to prevent stack overflow on adversarial input.

Tech Stack The source is authored in TypeScript/modern JS (src/index.ts plus .js implementation files) and compiled to dist/ via tsc, with postcss-selector-parser.d.ts hand-maintained as the public type surface. Runtime dependencies are deliberately minimal: cssesc for CSS-safe escaping and util-deprecate for deprecation warnings. Linting and formatting run on oxlint/oxfmt (Rust-based tooling) rather than ESLint/Prettier, tests run on Node’s built-in node:test runner against the compiled dist/, and CI (GitHub Actions) runs the full suite across an ubuntu/windows × lts/latest Node matrix.

Code Quality The src/__tests__/ directory holds close to 30 .mjs test files, each scoped to one selector construct (attributes, combinators, pseudos, escapes, namespaces, nesting, recursion, lossy round-tripping, sourceIndex, exceptions), giving comprehensive grammar coverage. The coverage script enforces hard thresholds (94% lines, 94% branches, 96% functions) via --experimental-test-coverage, and typecheck runs tsc --strict against the public .d.ts and its accompanying .test.ts to guard the type surface. Public Processor methods carry JSDoc, and API.md explicitly narrows the supported surface, discouraging reliance on undocumented internals.

API Design The public surface is intentionally small: one factory function (parser(transform, options)) returning a Processor with three symmetric sync/async pairs (process/processSync, transform/transformSync, ast/astSync). Losslessness is the default behavior rather than an opt-in, so a naive processSync(str) round-trips input unchanged — a design choice that avoids surprising output for the common case. Every node kind gets both a dedicated walkX traversal method and a matching parser.x() constructor, keeping the mental model uniform across the whole node hierarchy, and getting started requires no configuration beyond the transform callback.

Used by 6 apps in this directory

TypeScript
87%
Other

AFFiNE

Productivity · Project Management · Note Taking

72,238

Write, draw, and plan in one infinite canvas — the open-source alternative to Notion and Miro that keeps your data yours.

View details
91
Repo Health
87
Technical
67
Dependency
Built with
TypeScript87%
Updated yesterday
JavaScript
90%
Other

CodeSandbox

Code Editors · Developer Tools

13,640

Instantly ready browser-based IDE that runs full npm dependency resolution and transpilation entirely client-side, with no server needed.

View details
69
Repo Health
76
Technical
62
Dependency
Built with
JavaScript90%
Updated 1 weeks ago
TypeScript
92%
AGPL 3.0

Documenso

Digital Signiture

14,913

Self-hosted, open-source DocuSign alternative with legally binding PDF signatures, multi-party workflows, and a full REST and tRPC API.

View details
93
Repo Health
79
Technical
71
Dependency
Built with
TypeScript92%
Updated today
TypeScript
90%
Apache 2.0

HyperFrames

AI Development · AI Design Tools

44,430

Turn plain HTML and CSS into deterministic, pixel-perfect MP4 videos — authored by humans or AI agents, rendered by headless Chrome and FFmpeg.

View details
82
Repo Health
83
Technical
65
Dependency
Built with
TypeScript90%
Updated today
Python
64%
Other

Keep

Devops · Automation · Monitoring

12,290

The open-source AIOps and alert management platform that unifies 130+ monitoring tools into a single pane of glass with AI-powered correlation, deduplication, and workflow automation.

View details
91
Repo Health
79
Technical
66
Dependency
Built with
Python64%
TypeScript36%
Updated yesterday
Vue
47%
AGPL 3.0

Wiki.js

Knowledge Management · Collaboration

28,848

A modern, self-hosted wiki platform built on Node.js with a rich plugin ecosystem for authentication, search, storage, and rendering that adapts to any team's infrastructure.

View details
72
Repo Health
67
Technical
60
Dependency
Built with
Vue47%
JavaScript42%
Updated today

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search