# `PhoenixKit.ModuleDiscovery`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v1.7.164/lib/phoenix_kit/module_discovery.ex#L1)

Zero-config auto-discovery of external PhoenixKit modules.

Uses the same pattern as Elixir's protocol consolidation: scans `.beam` files
for persisted `@phoenix_kit_module` attributes via `:beam_lib.chunks/2`.
No module loading required — pure file I/O.

## How It Works

1. `use PhoenixKit.Module` persists `@phoenix_kit_module true` in the `.beam` file
2. This module scans only deps that depend on `:phoenix_kit` (fast, targeted)
3. Reads the persisted attribute from each beam file without loading the module
4. Works at both compile time (route generation) and runtime (ModuleRegistry)

## Fallback

Also reads `Application.get_env(:phoenix_kit, :modules, [])` for backwards
compatibility. Both sources are merged and deduplicated.

# `discover_external_modules`

```elixir
@spec discover_external_modules() :: [module()]
```

Discovers external PhoenixKit modules from beam files + config fallback.

Returns a deduplicated list of module atoms that implement `PhoenixKit.Module`.
Excludes internal modules (those in the `PhoenixKit.Modules` namespace that are
bundled with PhoenixKit itself).

# `module_hash`

```elixir
@spec module_hash() :: binary()
```

Returns a deterministic hash of the current set of discovered external modules.

Used by `__mix_recompile__?/0` (injected into the host router) to detect when
modules are added or removed, triggering router recompilation.

# `phoenix_kit_dependent_apps`

```elixir
@spec phoenix_kit_dependent_apps() :: [atom()]
```

Returns the names of dependency apps on disk that declare `:phoenix_kit` in their
`applications` (i.e. via `extra_applications`).

Filesystem-based, independent of load state. Used by the CSS-sources compiler to
warn when discovery yields zero sources even though phoenix_kit-dependent deps
are present.

# `scan_beam_files`

```elixir
@spec scan_beam_files() :: [module()]
```

Scans beam files of phoenix_kit-dependent apps for `@phoenix_kit_module` attribute.

Walks dependency `ebin` directories on disk (pure file I/O) rather than relying
on `:application.loaded_applications/0`, so it is deterministic at compile time —
it returns the same set whether or not the apps happen to be loaded yet. An app
qualifies when its `<app>.app` lists `:phoenix_kit` in `applications`; its beams
are then read with `:beam_lib.chunks/2` to keep the ones carrying
`@phoenix_kit_module true`. No module loading required.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
