feat(framework-edges): TYPO3 convention-file awareness#114
Open
CybotTM wants to merge 1 commit intorepowise-dev:mainfrom
Open
feat(framework-edges): TYPO3 convention-file awareness#114CybotTM wants to merge 1 commit intorepowise-dev:mainfrom
CybotTM wants to merge 1 commit intorepowise-dev:mainfrom
Conversation
TYPO3 loads a fixed set of convention-named files from each extension at bootstrap (`ext_localconf.php`, `Configuration/TCA/*.php`, etc.). These were never imported via PHP/JS imports, so the static graph reported `in_degree=0` and the dead-code analyzer flagged them as unreachable. Adds: - `_add_typo3_edges()` in `framework_edges.py`. Discovers extensions via `composer.json` `"type": "typo3-cms-extension"` (canonical for v11-v14) with legacy fallback to `ext_emconf.php`. Walks the repo root, monorepo subdirs, and `vendor/<vendor>/<package>` for project-mode installs. Anchors convention files (PHP and YAML) from a synthetic `framework:typo3-core` node. Parses `Configuration/JavaScriptModules.php` and adds edges to the JS modules it registers, so CKEditor plugins and backend modules become reachable precisely (no globbing). - `tech_stack.detect_tech_stack` learns to read composer.json and emit TYPO3 / Symfony / Laravel framework labels. Two related bugs fixed along the way: - The `repowise dead-code` CLI never invoked `add_framework_edges`, so even Django/Laravel/Rails repos showed convention files as unreachable. The CLI now calls `detect_tech_stack` + `add_framework_edges` after `graph_builder.build()`. - The dead-code analyzer skipped all `external:`-prefixed predecessors in zombie-package detection. That's correct for third-party imports but wrong for framework-mediated wiring. New `framework:` prefix is introduced for synthetic anchors and `_is_synthetic_node()` consolidates the skip rules: `framework:` nodes are ignored in unreachable / unused- export passes (like `external:`), but `framework:` predecessors *do* count as cross-package importers in zombie detection, preventing legitimate convention dirs (e.g. `Configuration/`) from showing up as zombie packages. Tests: 19 new (10 TYPO3 framework-edge cases incl. v14-without-legacy, project-mode vendor/ discovery, JavaScriptModules.php parsing, cross- extension JS skip, node_modules skip; 7 tech_stack composer.json cases; 2 analyzer regression tests for the `framework:` prefix). Full suite: 1283 pass, 0 regressions. Validated on 20 real-world TYPO3 extensions (~29k findings total): zero framework-file false positives, zero legitimate Configuration zombie packages. Out of scope (follow-up work): - TYPO3 indirect loading: Fluid templates, XLF translations, TypoScript references, `EXT:` path resolution via `getFileAbsFileName()`. - CKEditor plugin class methods (called via dynamic dispatch by the editor framework). - Per-version convention deltas (v15 deprecations etc.).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ext_localconf.php,Configuration/TCA/*.php, etc.) as unreachable. Mirrors the Laravel/Rails/Django pattern: composer-based discovery + synthetic edges.repowise dead-codenever invokedadd_framework_edges, so even existing Laravel/Rails/Django repos showed convention files as dead. The CLI now runs the same edge-synthesis pipeline as the indexing path.framework:synthetic-node prefix distinct fromexternal:. Framework-mediated wiring (TYPO3 core loading everyConfiguration/*) now counts as a cross-package importer in zombie-package detection, preventing convention dirs likeConfiguration/from showing as zombies.external:(third-party imports) still doesn't.What's new for TYPO3
Discovery (in
framework_edges._find_typo3_extension_roots):composer.jsonwith"type": "typo3-cms-extension"— canonical across v11→v14, present even whenext_emconf.phpis gone in v14.ext_emconf.phpinpath_set(legacy non-composer installs).vendor/<vendor>/<package>/composer.jsonfor project-mode TYPO3 installs. Skipsnode_modules,.git,.bare,Build, hidden dirs.Edges added:
framework:typo3-coreto convention files at the extension root (ext_localconf.php,ext_emconf.php,ext_tables.php(legacy),ext_tables.sql).framework:typo3-coretoConfiguration/*convention files (PHP and YAML):JavaScriptModules.php,ContentSecurityPolicies.php,RequestMiddlewares.php,Icons.php,Services.{php,yaml,yml},TCA/*.php,TCA/Overrides/*.php,Backend/*.php,RTE/*.{yaml,yml}.Configuration/JavaScriptModules.phpto the JS files it registers — parsed via regex over'EXT:<key>/...js'literals, with own-extension filtering so cross-extension references aren't fabricated.tech_stack.detect_tech_stack: learns to readcomposer.jsonand emitTYPO3/Symfony/Laravelframework labels (TYPO3 takes precedence when both match, since TYPO3 ships Symfony).Why these are the right anchor points
composer.jsontypeis canonical and version-stable — degrades gracefully when a v14 extension dropsext_tables.php(the file simply isn't a target; no false negative).framework:predecessors model real framework-mediated dependencies (different semantics from third-partyexternal:imports), so the analyzer can treat them differently in zombie-package detection without overloading the existingexternal:prefix.JavaScriptModules.phpby content is more accurate than a glob — only files actually registered get rescued from "unreachable", so genuinely orphaned JS still surfaces.Test Plan
Tests pass —
pytest tests/unit/→ 1283 passed, 0 regressions, 19 new tests:tests/unit/ingestion/test_typo3_framework_edges.py(composer-type discovery, legacyext_emconf.phpfallback, non-TYPO3 negative case, TCA/Backend/Services edges, v14 layout without legacy files, YAML-onlyConfiguration/,JavaScriptModules.phpparser, cross-extension JS skip, project-modevendor/discovery,node_modules/skip).tests/unit/generation/test_tech_stack.py(TYPO3/Symfony/Laravel composer detection, TYPO3-precedence-over-Symfony, plain-PHP-library negative, malformed JSON robustness).tests/unit/test_dead_code.py(framework:predecessors rescue from zombie status;framework:nodes themselves not flagged dead).Lint passes —
ruff checkclean on changed code (one pre-existing en-dash on line 381 offramework_edges.pyis unrelated upstream code).Mypy clean on changed files.
Real-world validation: ran
repowise dead-codeagainst 20 production TYPO3 extensions (~29,151 total findings):Configuration/zombie packages (was 1 before YAML coverage was added)Resources/zombie findings remaining — confidence 0.5,safe_to_delete=False. These are legitimate signal: TYPO3's indirect loading paths (Fluid templates, XLF lookups, TypoScript references,EXT:resolution viagetFileAbsFileName()) aren't traced. Out of scope for this PR.Repos validated:
lehrerbuero(11.5k findings),nrc_template,t3x-contexts,t3x-cowriter,t3x-nr-image-sitemap,t3x-nr-llm,t3x-nr-passkeys-be,t3x-nr-saml-auth,t3x-nr-textdb,t3x-nr-vault,t3x-nr-xliff-streaming,t3x-rte_ckeditor_image,t3x-scheduler,t3x-sync,t3x-universal-messenger,temporal-cache-fix,ter,ter_fe2,ter_layout,ter_rest.Out of scope (potential follow-ups)
EXT:path resolution. Would address the residualResources/zombie findings.unused_exportsymbol-level findings since CKEditor invokes commands via dynamic dispatch internal to the editor.ext_tables.phpwas removed in v14; many extensions still constrain^11.5 || ^12.4 || ^13.4so the v1 union behavior is correct anyway). The hook is in place — adding constraint parsing later is purely additive.tech_stack.pybut no edge functions yet. Same scaffolding as TYPO3 once the maintainer is happy with the shape here.Checklist
docs/LANGUAGE_SUPPORT.md,docs/CHANGELOG.md)