GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — bug(parser): CSS selectors are indexed as Function nodes and HTML class/id attributes as Class/Variable nodes — every selector is a find_dead_code row · Issue #1857 · CodeGraphContext/CodeGraphContext

bug(parser): CSS selectors are indexed as Function nodes and HTML class/id attributes as Class/Variable nodes — every selector is a find_dead_code row #1857

New issue
Copy link
New issue
Copy link
Open
Open
bug(parser): CSS selectors are indexed as Function nodes and HTML class/id attributes as Class/Variable nodes — every selector is a find_dead_code row#1857
Copy link

Description

@mollyjester
mollyjester
opened on Oct 22, 2026
Issue body actions

Problem

The CSS parser (tools/languages/css.py) returns every selector, whether class, id or tag, in "functions". The writer then stores each one as a Function node. A selector repeated in a stylesheet becomes one Function per occurrence. The HTML parser (tools/languages/html.py) stores class="…" values as Class nodes and id="…" values as Variable nodes.

Selectors and attribute values are not callable, and they don't declare classes or variables. Nothing ever CALLS them, so every stylesheet selector becomes a find_dead_code row. They also show up in find_code as functions and classes.

Repro (upstream/main 1b595a6, no code changes; real Kùzu 0.11.3 and LadybugDB 0.19.1, each in its own process)

A 3-file repo:

/* style.css */
:root { --brand: #336699; }
body { margin: 0; }
.navbar { display: flex; }
.navbar a:hover, .navbar .active { color: var(--brand); }
#main > p { line-height: 1.5; }
@media (min-width: 640px) { .navbar { gap: 1rem; } }
<!-- index.html -->
<!doctype html>
<html><head><link rel="stylesheet" href="style.css"></head>
<body><nav class="navbar"><a class="active" href="#">Home</a></nav>
<div id="main"><p class="lead">Hello</p></div>
<script src="app.js"></script></body></html>
// app.js
export function render() { return document.querySelector('.navbar'); }
render();

Indexed with GraphBuilder.build_graph_from_path_async, then queried with MATCH (f:File)-[:CONTAINS]->(n:<Label>) and the find_dead_code / find_code handlers:

Function nodes (10): app.js render; style.css body, .navbar(3), .active(4), .navbar(4), .navbar(4), a(4), #main(5), p(5), .navbar(6)
Class nodes (3):     index.html active, navbar, lead
Variable nodes (2):  index.html main; style.css --brand
find_dead_code rows: 9   -> every one is a style.css selector (body, a, p, .navbar x4, .active, #main)
find_code '.navbar': functions_by_name = 4 x ('.navbar', style.css)
find_code 'navbar':  classes_by_name   = ('navbar', index.html)
find_code 'main':    variables_by_name = ('main', index.html)

The node and dead-code sets are identical on Kùzu and LadybugDB; only the order of the two tied rows on line 4 differs. Of the 10 Function nodes, 9 are selectors. .navbar gets 4 Function nodes, two of them on line 4 because it appears twice in one selector list. The only real function, render, is the one that isn't dead code. Every reported dead-code row is a false positive.

Impact

Measured on real repositories:

  • A mid-size web application: 727 selector Function nodes. Every one is a find_dead_code row and find_code noise.
  • A large Odoo 19 codebase (core + addons): 6,812 selector Function nodes and 486 HTML-derived Class/Variable nodes.

Every selector Function is a false find_dead_code row and an extra find_code hit. find_code for .x returns one "function" per occurrence of the selector, and a bare x also gets one content match per occurrence. Tools that list Class nodes per file report HTML attribute values as classes.

Proposed fix

Treat selectors as facts about the file, not as graph symbols:

  • css.py emits no functions. It collects class and id selector names into two File list properties, for example css_classes and css_ids. Each list is deduplicated, sorted, and null when empty, so a rewritten file drops a stale list. CSS identifier escapes are unescaped (.sm\:flex becomes sm:flex). Tag selectors (body, a, p) are dropped. CSS custom properties (--brand) stay Variable nodes, because they are declarations with values that var() and scripts read.
  • html.py writes class / id attribute values to the same two File lists instead of creating Class/Variable nodes.
  • The embedded DDL gains css_classes STRING[] and css_ids STRING[] on File, with an ALTER for existing databases. Neo4j needs no index for them.
  • find_code treats a file as an exact match when the query names one of its classes or ids (.x for a class, #x for an id, or bare x for either), so a lookup like "where is .navbar styled" still works without selector nodes.
  • Existing Neo4j graphs keep the old selector nodes until a re-index. An upgrade note with a one-off cleanup query would cover that.

This is a data-model change: selector names stop being nodes. That's why this is an issue rather than a PR. A downstream fork implements exactly this model, with tests and Kùzu/LadybugDB bundle round-trip parity. Its implementation depends on that fork's own schema and search infrastructure, though, so it can't be submitted to upstream as-is.

Related

  • fix(css): extract quoted and unquoted url imports #1806 (open, fix(css): url imports) and fix(html): index unquoted attributes and self-closing tags #1807 (open, fix(html): unquoted attributes / self-closing tags) edit the same two parsers. Their new audit tests lock in the current model (('has', 'functions', '.one'), ('has', 'classes', 'one'), ('has', 'variables', 'main')), so either change will need to adjust the other's assertions.
  • CSS parser: url() imports are omitted #1801 (CSS url() imports omitted) and HTML parser: unquoted attributes and self-closing tags are omitted #1800 (HTML unquoted attributes / self-closing tags omitted) are the issues behind those PRs. They are related only: neither addresses selectors stored as Function nodes.

Activity

  1. github-project-automation
    added this to CodeGraphContext Roadmapand CGC Progress Boardon Oct 22, 2026
  2. github-project-automation
    moved this to Backlog tasks in CGC Progress Boardon Oct 22, 2026
  3. github-actions commented on Oct 22, 2026

    @github-actions
    github-actionsBot
    on Oct 22, 2026 – with GitHub Actions
    Contributor
    More actions

    Hi! 👋 Join our CodeGraphContext Discord channel to collaborate: https://discord.gg/dR4QY32uYQ

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    • CGC Progress Board
      Status
      Backlog tasks
      Show more project fields
    • CodeGraphContext Roadmap
      Status
      No status
      Show more project fields

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    You are visitor 01402520 — GeezerVison does not store the pages you look up.
    Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.