Skip to content

feat: CSS/SCSS/LESS parsing with cross-language STYLES edges and conflict detection#61

Open
CodeBlackwell wants to merge 2 commits into
tirth8205:mainfrom
CodeBlackwell:pr/css-scss-support
Open

feat: CSS/SCSS/LESS parsing with cross-language STYLES edges and conflict detection#61
CodeBlackwell wants to merge 2 commits into
tirth8205:mainfrom
CodeBlackwell:pr/css-scss-support

Conversation

@CodeBlackwell

Copy link
Copy Markdown

Summary

  • CSS/SCSS parsing with Tree-sitter: selector extraction, specificity computation [a,b,c], single-file override detection (specificity, !important, BEM, source order), custom properties, @media/@keyframes/@import, SCSS $variables/@mixin/@include/& nesting, Vue <style> block delegation
  • Cross-language STYLES edges: extract className from JSX/TSX and class from Vue <template>, post-build linking pass connects components to CSS selectors, CSS Modules resolution (styles.foo.foo with camelCase-to-kebab)
  • Cross-file conflict detection: heuristic flagging of same-name selectors across files via POTENTIAL_CONFLICT edges with specificity comparison and confidence metadata
  • LESS support: regex-based parser (no tree-sitter grammar available) for selectors, @variables, .mixins(), @import
  • 3 new MCP query patterns: styles_of, styled_by, conflicts_of
  • Visualization: STYLES (blue) and POTENTIAL_CONFLICT (red) edge styling with legend entries

New Edge Types

Edge Source Target Purpose
OVERRIDES Winning CSS selector Losing CSS selector Single-file specificity/cascade override
STYLES JSX component / Vue file CSS selector Cross-language class reference
POTENTIAL_CONFLICT CSS selector CSS selector Cross-file same-name selector conflict

Architecture

Two-phase approach keeps the parser stateless:

  1. Per-file: Extract class references during parsing, store in node extra metadata
  2. Post-build: link_css_styles() and detect_cross_file_conflicts() query the graph and create cross-file edges

Both phases run automatically in full_build() and incremental_update().

Test Plan

  • 229 tests passing (47 new)
  • ruff check clean
  • mypy clean
  • TestJSXClassExtraction — static className, CSS Module refs, dynamic skip, function attachment
  • TestVueTemplateClassExtraction — static class, dynamic :class skip, style+template coexistence
  • TestLESSParsing — selectors, @variables, @import, .mixins, language tagging
  • TestCSSStylesLinking — cross-file STYLES edges, Vue same-file linking, missing selector no-op
  • TestCrossFileConflicts — same-selector conflict, unique selector no-conflict, metadata validation
  • TestCamelToKebab — camelCase conversion correctness
  • Full graph rebuild exercises linking pass end-to-end

@tirth8205

Copy link
Copy Markdown
Owner

Closing — CSS/SCSS support scope is too broad for the current architecture. Tree-sitter CSS parsing doesn't produce meaningful function/class nodes for the knowledge graph.

@tirth8205

Copy link
Copy Markdown
Owner

CSS/SCSS/LESS parsing with cross-language STYLES edges is not in main — parser.py has no CSS/SCSS/LESS support, no STYLES edge type, and no styles_of/styled_by/conflicts_of query patterns. This is a substantial PR (2135 additions) with well-defined scope. Worth reviving — it would be valuable for frontend-heavy codebases. The two-phase approach (per-file extraction + post-build linking) is architecturally sound.

@tirth8205 tirth8205 reopened this Apr 11, 2026
dpesch added a commit to 11com7/code-review-graph that referenced this pull request May 3, 2026
…ES edges

Adds CSS (.css), SCSS (.scss), LESS (.less/.sass) parsing via
tree-sitter. Emits STYLES edges from CSS selectors to the HTML/JSX/Vue
components they target. Adds cross-file conflict detection for
overlapping CSS selectors. Includes bug fixes found during merge:
- Propagate jsx_class_collector through parser helper methods
- Fix confidence value type in detect_cross_file_conflicts (str→float)
CodeBlackwell and others added 2 commits July 1, 2026 07:10
…lict detection

Revives PR tirth8205#61 (originally against v1.8.4 / f9f01ae) by manually reapplying
its feature diff onto current upstream/main (392 commits of unrelated drift,
including the parser/tools.py decompositions). Adds Tree-sitter-based
CSS/SCSS parsing and a regex-based LESS parser, a STYLES edge type linking
JSX/Vue class references to CSS selectors, an OVERRIDES edge type for
same-file CSS specificity/!important/BEM override detection, and a
POTENTIAL_CONFLICT edge type for cross-file selector collisions. Exposes
five new query_graph patterns (styles_of, styled_by, conflicts_of,
overrides_of, overridden_by) and CSS-aware review guidance.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hw3m91NAzZmXw9MHrvNnDB
…exing

Address correctness issues in the CSS/STYLES linking:

- Scope STYLES edges by imports, not global class-name match. A component
  now links to a selector only when the selector is in the same file or in
  a stylesheet the component actually imports. Record every imported
  stylesheet (module + plain side-effect `import './x.css'`) on the File
  node as `css_import_files`, resolved in the same path convention as node
  file_paths (normpath, no symlink canonicalization) so comparisons hold on
  symlinked repo roots. Applies to the static, Vue, and CSS-Module paths.

- Index selectors by every bare class token via a single regex, so
  compound (`.btn.active`), descendant (`.card .btn`), and pseudo
  (`.btn:hover`, `.btn::before`) selectors all match a plain className.

- Hoist the per-file CSS-module import map out of the module-node loop.

- Correct the `_jsx_class_collector` docstring to describe its actual
  reset/consume behavior on the JS/TS path vs the early-returning Vue/Svelte
  paths.

Tests cover import-scoping (positive + unimported-negative), same-file Vue
scoping, and compound/descendant/pseudo indexing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@CodeBlackwell CodeBlackwell force-pushed the pr/css-scss-support branch from 72558de to 6d8868d Compare July 1, 2026 15:53
@CodeBlackwell

Copy link
Copy Markdown
Author

Revived and rebased onto current main (the tools.py split + parser/graph refactors). Beyond resolving conflicts, this tightens the STYLES linking based on a review pass:

  • Import-scoped edges — components now link only to stylesheets they actually import (same-file or via css_import_files recorded on the File node), replacing the previous global class-name match that produced false edges across unrelated CSS files.
  • Robust selector indexing — compound (.btn.active), descendant (.card .btn), and pseudo (.btn:hover) selectors now match a plain className via a single token regex.
  • Hoisted a per-file import map out of an inner loop; corrected a stale docstring.

Full suite green (1437 passed), ruff + mypy clean on touched files. Happy to adjust the scoping heuristic if you'd prefer a different policy for global (non-module) CSS.

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.

2 participants