Skip to content

Engine: Module Discovery & Manifest Building #8

@ollieread

Description

@ollieread

Responsible for discovering modules from the modules/ Composer project, building ModuleManifest and ModuleRegistrar instances from package metadata and reflection, and generating a cache file that the module registry loads from at runtime.

Discovery

Module discovery is triggered when a module is installed, removed, or updated. Using Composer as a library, the process reads the modules/composer.json and its lock file to enumerate all required packages. For each package, extra.tgp is read alongside the standard composer.json metadata to build the full picture of the module.

The provide block in modules/composer.json is regenerated from the panel's own lock file as part of this process to keep the two in sync.

ModuleManifest

ModuleManifest is a final readonly DTO built from a Composer package. It contains:

  • ident — derived from the package name (the part after the vendor slug)
  • vendor — the vendor slug from the package name
  • version — the package version
  • name — from extra.tgp
  • description, license, keywords, authors, support — from CompletePackageInterface where available
  • definition and icon — from extra.tgp
  • capabilities — from extra.tgp, mapped to Capability enum cases
  • core — whether the module is a core module, not installable, updatable, or removable via the module system
  • registrar — the registrar class string, from extra.tgp

ModuleRegistrar

ModuleRegistrar is a final readonly metadata class built via reflection on the registrar class declared in the manifest. It records:

  • The registrar class string
  • The register method name, identified by the #[Register] attribute
  • The boot method name, identified by the #[Boot] attribute
  • Collector method mappings, identified by the #[Collect] attribute — broken into scoped and unscoped variants, and further indexed by PanelContext where provided
  • No-context collector mappings for collectors that do not target a specific PanelContext

Cache File

After discovery and reflection, a cache file is generated containing:

  • A map of ident -> ModuleManifest for all discovered modules
  • A list of enabled module idents
  • A list of disabled module idents
  • A map of ident -> ModuleRegistrar for enabled modules only

The cache format is a PHP file using new expressions or a similar approach that avoids var_export complexity given the readonly classes involved. The file is regenerated in full on every discovery run.

DI Integration

  • #[Manifest('ident')] resolvable attribute and resolver — injects a ModuleManifest for the given ident
  • #[Registrar('ident')] resolvable attribute and resolver — injects a ModuleRegistrar for the given ident

Tasks

  • Define Capability enum with all capability cases
  • Implement ModuleManifest final readonly DTO
  • Implement ModuleManifestBuilder to construct a ModuleManifest from a Composer package
  • Implement ModuleRegistrar final readonly metadata class
  • Implement reflection-based ModuleRegistrarBuilder to construct a ModuleRegistrar from a registrar class
  • Define #[Register], #[Boot], #[Collect], and #[Unscoped] registrar attributes
  • Implement discovery process using Composer as a library
  • Implement provide block regeneration from the panel lock file
  • Implement cache file generation from discovered manifests and registrars
  • Implement #[Manifest] resolvable attribute and resolver
  • Implement #[Registrar] resolvable attribute and resolver
  • Write tests for ModuleManifestBuilder from a mock package
  • Write tests for ModuleRegistrarBuilder reflection output
  • Write tests for cache file generation and structure
  • Write tests for #[Manifest] and #[Registrar] resolution

Metadata

Metadata

Assignees

Labels

layer: engineBase framework and engine work

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions