Skip to content

Clear what nothing asks for

$ grim prune --dry-run

grim.json was edited by hand, or a merge dropped a line from it, and the module it named is still sitting in src/. It may still be enabled in the app, though nothing asks for it any more. grim prune finds those leftovers and deletes them.

You rarely need to call it yourself. grim remove ends with the same cleanup, and grim install offers it when it notices orphans.

Usage

$ grim prune --dry-run    # list the orphans, delete nothing
$ grim prune              # list them, then ask
$ grim prune --force      # no prompt, local changes discarded

Run it from the project root, next to grim.json. It needs no network and no login: everything it reads is on disk.

Arguments and options

Remove installed modules that are no longer required by grim.json

Usage

grim prune [--dry-run] [-f|--force] [--no-autoload-dump]

Options

Option Description
--dry-run List orphan modules without deleting anything
-f, --force Skip confirmation prompt and dirty-module warning; list committed local modules in grim.json "local" without asking
--no-autoload-dump Skip composer dump-autoload after removal

What it actually does

  1. Reads what is installed. The module.json of every directory under src/. Modules that belong to core are left out, so they can never be listed.
  2. Walks the requirements. From the requires and the local of grim.json, through the requires of each installed module. Whatever the walk does not reach is an orphan, but only when a grim.lock once named it: the current one or any committed one. A module that was never in a lock was never a unit, and prune does not delete it.
  3. Lists the orphans with their size on disk, and marks those with uncommitted changes. With none it prints No orphan modules — every installed module is reachable from grim.json. and stops.
  4. Asks. Delete 2 orphan module(s)? [y/N], with no as the default.
  5. Deletes. Each orphan's directory under src/, and its line in storage/config/modules-enabled.php.
  6. Runs composer dump-autoload -o in the app container, unless --no-autoload-dump.
  7. Runs pnpm install on your machine when a deleted module carried its own package.json: it leaves the pnpm workspace. It says ✓ pnpm install — commit pnpm-lock.yaml with grim.json, unchanged, nothing to commit, or what to run when pnpm is missing or the install failed. A project that keeps no pnpm lock is left alone. The cleanup that grim install and grim update offer skips this step: when it deletes a module with a package.json, run pnpm install yourself.

A local module, one made with grim make:module --no-repo, is never an orphan: grim.json lists it under local. A module no lock ever named and local does not list is never deleted either. Prune names it in one of two ways:

  • ℹ Local module not listed in grim.json "local": committed in the instance repository, module.json of type module, no .github/workflows/validate.yml. Most likely a local module from before local existed. In a terminal prune asks Add them to grim.json "local"? [Y/n]; --force adds without asking; --dry-run, -n and CI only name it. Adding also writes config/modules-available.php again. Commit both files.
  • ℹ Not a unit and not listed in grim.json "local": untracked, outside git, or a module repository in the making (grim make:module without --no-repo, before grim require). Commit and list it, require it, or delete the directory yourself. A local module made by an older grim make:module --no-repo still carries that .github/: delete it, commit, and the next prune offers the module for local.

Themes are not looked at. A theme taken out of grim.json stays in resources/themes/ until you delete the directory.

Pitfalls

Modules with local changes are skipped

⚠ 1 module has uncommitted changes that would be lost. git status of the project shows changes under that module's directory, and those exist nowhere else. Prune deletes the clean orphans and leaves the edited one. Commit or discard the changes, then run it again; a committed module stays in the project's history after the delete.

--force skips both the prompt and the check for uncommitted changes. Work inside src/<Module>/ that was never pushed is deleted with the directory.

The lock and the database stay as they were

Prune does not rewrite grim.lock, and it does not roll back the migrations of what it deletes. The lock is written again by the next grim install or grim update.

After it finishes

Run the suite: a module that was used without being required shows up here as a missing class. If that happens, grim require brings it back, this time with an entry in grim.json.

$ grim test

Recipes that use it