Skip to content

feat: CSS/SCSS/LESS support — stylesheets join the code graph#614

Open
CodeBlackwell wants to merge 4 commits into
tirth8205:mainfrom
CodeBlackwell:feat/css-scss-upstream
Open

feat: CSS/SCSS/LESS support — stylesheets join the code graph#614
CodeBlackwell wants to merge 4 commits into
tirth8205:mainfrom
CodeBlackwell:feat/css-scss-upstream

Conversation

@CodeBlackwell

Copy link
Copy Markdown

Hi! This PR teaches the graph to understand stylesheets, so reviews can see the relationship between components and the CSS that styles them.

What's new

Parsing

  • 🧩 .css and .scss files parse via tree-sitter: selectors, custom properties, @media, @keyframes, mixins, and SCSS variables all become graph nodes
  • 🍃 .less support via a regex fallback (no tree-sitter grammar exists for LESS)
  • 🖼️ Vue and Svelte <style> / <style lang="scss"> blocks are parsed in place, with correct line offsets back into the SFC
  • 📦 @import chains resolve to real files, including SCSS partials (_variables.scss)

Cross-language edges

  • 🔗 STYLES — JSX className, Vue/Svelte static template classes, and CSS Modules references (styles.btnPrimary.btn-primary, camelCase→kebab handled) link components to the selectors that style them
  • ⚔️ OVERRIDES — same-file specificity conflicts, source-order overrides, and !important wins are tracked per property
  • 🚨 POTENTIAL_CONFLICT — selectors in different files targeting the same class get flagged with both specificities

Tooling

  • 🔍 Five new query_graph patterns: styles_of, styled_by, overrides_of, overridden_by, conflicts_of
  • 📝 Review guidance now warns when a change touches CSS override relationships
  • 🎨 Visualization renders the new edge kinds with their own colors, dashes, and legend entries; CSS nodes get a dashed ring

Design notes

  • Cross-file linking runs as a best-effort post-pass alongside the existing resolvers (ReScript/Spring/Temporal), so it can never fail a build and is skipped on incremental updates when no style-relevant file changed
  • Query patterns are table-driven — a future edge kind is one dict entry, not a new branch
  • Docs updated across README, USAGE, FEATURES, COMMANDS, and the edge-type schema

Testing

  • ✅ 68 new tests (parser fixtures for CSS/SCSS/LESS/TSX/Vue/Svelte + end-to-end linking through full_build)
  • ✅ Full suite green on top of current main: 1437 passed, 1 skipped
  • ✅ ruff clean

Happy to adjust anything — naming, edge semantics, or scope. Thanks for the great project!

Extend the merged CSS feature to Svelte SFCs, which upstream added
after the CSS branch was written:

- Shared _extract_style_blocks helper (refactored out of _parse_vue)
  now also runs in _parse_svelte, so selectors, custom properties,
  and OVERRIDES edges inside Svelte <style>/<style lang="scss">
  blocks land in the graph with correct line offsets.
- Static class attributes in Svelte markup are collected onto the
  File node as css_classes, so link_css_styles creates STYLES edges
  with no changes; class:foo directives are ignored as dynamic.
- .less added to JS and stylesheet import resolution extensions.
- First test coverage for _parse_svelte (script + style + linking).
- parser.py: shared _attr_name_value/_tag_attrs helpers replace four
  copies of the nested SFC attribute-decoding loop (Vue script, Svelte
  script, style blocks, template class walker). Vue template classes
  now use the same css_classes extra key as Svelte and JSX.
- incremental.py: link_css_styles/detect_cross_file_conflicts moved
  into the best-effort resolver layer (_run_css_resolver), matching
  the rescript/spring/temporal contract: run after commit, swallow
  failures, and skip on incremental updates when no style-relevant
  file changed. Selector scan now runs once and is shared by both
  passes; loop-invariant CSS-module File query hoisted out of the
  per-node loop; duplicate vue_template_classes STYLES loop deleted.
- tools/query.py: five hand-rolled CSS query branches collapsed into
  one _EDGE_WALK_PATTERNS (kind, directions) table walk.
- visualization.py: legend class ternary ladder replaced with a
  LEGEND_CLASS lookup map alongside EDGE_COLOR/EDGE_CFG.

Net -35 lines, no behavior change; full suite 1442 green.
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.

1 participant