State & Tooling

Control module state, compile manifests, and check compatibility.

Category: Modules

Enabled state (storage/modules.json) #

  • Created automatically the first time you run velvet module:enable foo or module:disable foo.
  • Structure:
{
  "enabled": [
    "docs",
    "acme-blog"
  ]
}
  • Only modules listed here participate in compilation. Disable a module to keep the code around without loading it.

Compilation artifacts #

File Purpose
storage/modules-compiled.json Canonical manifest with load order, entry class, version, requirements. Read at runtime by ModuleManager::load().
storage/modules-autoload.php Auto-generated PSR-4 mapping used by a single spl_autoload_register for all modules. Keeps bootstrap O(1) even with large module counts.

velvet module:compile regenerates both files, validates dependencies, and ensures each entry class autoloads.

CLI flow #

  1. module:list – Discover everything (config paths, globbed directories, vendor/* packages of type velvetcms-module) and print metadata.
  2. module:enable foo – Adds foo to modules.json. module:disable foo removes it.
  3. module:compile – Validates enabled modules and writes the compiled manifest/autoloader.
  4. Deploy or reload the app—the bootstrap loads modules via the compiled manifest.

Runtime lifecycle #

  1. ModuleManager::load() reads modules-compiled.json, registers the shared autoloader, instantiates each entry class, and stores them in $this->modules.
  2. register() phase: modules bind services/config.
  3. boot() phase: modules register routes, commands, template paths, event listeners.
  4. VersionRegistry merges compiled module metadata into config/version.php, making versions accessible to CLI + HTTP.

Checking compatibility #

VelvetCMS\Core\VersionRegistry exposes helpers:

$registry = VersionRegistry::instance();
$registry->getModules();
$registry->isCompatible('docs', 'core');
$registry->checkModuleRequirements('acme-blog');

The registry reads module requirements from the compiled manifest, so you always compare against the actual enabled set.

Troubleshooting #

  • Missing entry classmodule:compile fails with unable to autoload entry class.... Verify namespaces in your module’s composer.json.
  • Unsatisfied constraints → upgrade core or dependent modules until requires strings (Composer-style semver constraints) are satisfied.
  • Modules not loading → ensure modules-autoload.php exists and includes the relevant namespace. If it’s missing, rerun module:compile.
  • Hot reload → after changing module PHP files you usually don’t need to recompile (since autoload mapping is unchanged). Recompile when you add/remove modules or change manifest metadata.

With the state + tooling pieces in place, you can safely enable/disable features per environment and verify compatibility before deploys.