Skip to content

feat(frameworks): add Drupal 8/9/10/11 support#271

Merged
colbymchenry merged 5 commits into
colbymchenry:mainfrom
marcelovani:feat/drupal-framework-support
May 21, 2026
Merged

feat(frameworks): add Drupal 8/9/10/11 support#271
colbymchenry merged 5 commits into
colbymchenry:mainfrom
marcelovani:feat/drupal-framework-support

Conversation

@marcelovani
Copy link
Copy Markdown
Contributor

@marcelovani marcelovani commented May 21, 2026

Summary

Implements a Drupal framework resolver, closing #268.

  • Route extraction: parses *.routing.yml files and emits route nodes linked by references edges to the _controller, _form, or entity-handler class/method. Querying a controller method via codegraph_callers now surfaces the URL route that binds it.
  • Hook detection: scans .module, .install, .theme, and .inc files for hook implementations using two strategies — docblock (@Implements hook_X(), zero false positives) and name-prefix pattern ({moduleName}_{hookSuffix}, fallback). Each implementation emits a references edge to the canonical hook_X name so codegraph_callers("hook_form_alter") returns every implementation across all modules.
  • New file extensions indexed: .module, .install, .theme, .inc (extracted with the existing PHP tree-sitter grammar); *.routing.yml (YAML, route extraction only); *.twig (Twig templates, file-level tracking — no symbol extraction in v1).
  • New languages: yaml and twig added to the language registry; both appear in codegraph_files. yaml also allows framework extractors to run on YAML files.
  • Drupal excludes: web/core/**, web/modules/contrib/**, and web/themes/contrib/** excluded by default to focus indexing on custom code.
  • Detection: via drupal/* dependency in composer.json (require or require-dev).

What's deferred to future iterations

  • Services / DI: *.services.yml extraction (class → service-id edges)
  • Plugin annotations: @Block, @FormElement, @Field, etc.
  • Twig symbol extraction (pending a tree-sitter Twig WASM grammar)
  • Virtual hook_* nodes so codegraph_callers works even when Drupal core is not indexed

Test plan

  • npm test passes (new __tests__/drupal.test.ts covers detection, route extraction, hook detection, and FQCN resolution)
  • Index a Drupal 10 project and verify codegraph_callers("hook_form_alter") returns all implementing functions
  • Verify codegraph_callers on a controller method surfaces its *.routing.yml route node
  • Confirm web/core/** and web/modules/contrib/** are excluded from the index

🤖 Generated with Claude Code

@marcelovani marcelovani changed the title Feat/drupal framework support feat(frameworks): add Drupal 8/9/10/11 support May 21, 2026
Adds Drupal as a supported framework with route extraction from *.routing.yml
files and hook detection. Extends language support with yaml and twig (file-level
tracking only). Registers Drupal-specific file extensions (.module, .install,
.theme, .inc) and excludes core/contrib paths from indexing.
@marcelovani marcelovani force-pushed the feat/drupal-framework-support branch from 4de45e5 to 6c51025 Compare May 21, 2026 15:11
@colbymchenry colbymchenry merged commit 5b71a89 into colbymchenry:main May 21, 2026
nanofatdog added a commit to nanofatdog/codegraph_hermes that referenced this pull request May 22, 2026
Upstream changes (colbymchenry/codegraph v0.9.1):
- Self-contained distribution (bundle Node + node:sqlite, colbymchenry#282)
- Zero-config .gitignore-driven indexing (colbymchenry#283)
- Drupal 8/9/10/11 framework support (colbymchenry#271)
- Security fix: refuse symlinks for /tmp session marker (colbymchenry#280)
- Fix installer: strip stale auto-sync hooks (colbymchenry#278)
- Hermes Agent target added upstream (colbymchenry#274)
- MCP tools: improved codegraph_explore budget
- Better SQLite backend (node:sqlite)

Our additions KEPT:
✅ AGENTS.md — Hermes dev guide
✅ skills/codegraph/SKILL.md — Hermes skill
✅ scripts/hermes-setup.sh — one-command setup
✅ README.md — Hermes-optimized
✅ server-instructions.ts — Hermes delegate_task section

hermes.ts: merged upstream (platform_toolsets.cli + cleaner YAML) +
kept our skill installation + wireProjectSurfaces
andreinknv added a commit to andreinknv/codegraph that referenced this pull request May 27, 2026
…ML language + FrameworkResolver.extract() interface extension

Ships full Drupal 8/9/10/11 support to this fork. Three coupled
additions that together unlock the previously-deferred Drupal port:

  1. **YAML language** (`src/extraction/languages/yaml.ts`).
     tree-sitter-yaml grammar vendored from npm
     `@tree-sitter-grammars/tree-sitter-yaml@0.7.1` (ships a pre-
     built 189KB ABI-14 compatible wasm — no emcc needed). Minimal
     `yamlExtractor` (every type-list empty) so the default tree-
     sitter walk runs but emits nothing -- YAML has no functions /
     classes / etc. to extract universally. The grammar is loaded
     so framework resolvers can walk YAML ASTs directly.

  2. **FrameworkResolver.extract() interface extension** -- backward-
     compatible additive change in `src/resolution/types.ts`.
     Existing resolvers (express, swift/Vapor, etc.) keep their
     `extractNodes(): Node[]` hook unchanged. Resolvers that need
     to emit cross-file references (Drupal's routing.yml -> handler
     class) implement the new `extract(): {nodes, references}`
     hook. Orchestrator dispatches to `extract` first when present;
     the orchestrator does NOT call both for the same file.
     Mirrors the optional-extension precedent set by
     `ResolutionContext.getProjectAliases?()` / `getReExports?()`.

  3. **Drupal framework resolver** (`src/resolution/frameworks/
     drupal.ts`). Adapted from upstream 5b71a89 (PR colbymchenry#271) with
     audit corrections applied at port time:

       - **Tree-sitter-yaml AST walk** for routing.yml parsing
         (upstream used regex on source -- avoided by walking
         `block_mapping_pair` children, dodging the YAML anchors /
         multi-line-value / comment-collision bug class).
       - **Content-hash route node IDs** via `generateNodeId({kind:
         'route', name: 'routeName::path', ordinal: 0})` -- upstream
         used `route:${filePath}:${lineNum}:${path}`, violating this
         fork's G7 stable-node-id contract (line numbers move when
         unrelated content shifts).
       - **Hook strategy B name-pattern fallback** limited to the
         specific module's prefix (upstream scanned every function
         in the file, broadening the false-positive surface).

  4. **PHP_DEF extends to `.module/.install/.theme/.inc`**. Drupal
     ships hook implementations in PHP files with non-standard
     extensions. Registering them on PHP_DEF means the tree-sitter
     PHP grammar parses them and the Drupal resolver's hook
     detection runs on the resulting function nodes.

Drupal feature surface (validated end-to-end via 6 tests):
  - **Detection**: composer.json `drupal/*` dep gate.
  - **routing.yml extraction**: one `route` node per top-level
    mapping pair; `references` from each route to its
    `_controller` / `_form` / `_entity_form` / `_entity_list` /
    `_entity_view` handler FQCN.
  - **Hook detection**: docblock `@Implements hook_X()` (precise)
    + name-pattern `{moduleName}_{hookSuffix}` (best-effort).
  - **Resolution**: `_controller: '\Drupal\\…\Foo::bar'` resolves
    to method `bar` on class `Foo` (or class fallback with reduced
    confidence); `_form: '\Drupal\\…\MyForm'` resolves to class;
    `hook_X` resolves to first matching `*_X` function in any
    hook file.

Cross-pipeline assumption documented in drupal.ts header: hook refs
use `generateNodeId({filePath, kind: 'function', name, ordinal: 0})`
to match the PHP extractor's first-occurrence function node ID. If
`generateNodeId`'s contract ever changes, the hook edges break
silently -- documented inline so a future audit catches it.

v1 scope (deferred to follow-ups):
  - `*.services.yml` service definitions
  - Plugin annotations (`@Block`, `@FormElement`, …)
  - Twig template scanning
  - Virtual `hook_*` nodes when Drupal core isn't indexed

Tests (`__tests__/drupal-extraction.test.ts`, 6 cases):
  - YAML language registration
  - .yml/.yaml/.routing.yml detection
  - Drupal detection: positive (drupal/*) + negative (laravel)
  - End-to-end: routing.yml -> controller method edge
  - End-to-end: .module file with docblock hook implementation

Gates: check 0/0/0, typecheck clean. Drupal suite 6/6 + ObjC
6/6 + extraction 459/459 + resolution 75/75 unchanged. F#60
staleness-banner tests show known parallel-load flakiness (10/10
in isolation; pre-existing pattern from memory).

Dependencies: zero runtime additions. tree-sitter-yaml's pre-built
wasm vendored at `src/extraction/wasm/yaml.wasm`; transient npm
install at port time produced only the artifact, no package.json
or package-lock.json changes. Updates B7 backlog entry's "deferred"
status to shipped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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