Detects Phoenix runtime configuration patterns and determines appropriate config strategy.
This module analyzes Phoenix project configuration files to determine:
- Whether the project uses runtime.exs patterns
- The appropriate configuration file to modify
- The correct insertion location for PhoenixKit configuration
A stock Phoenix 1.7+ app has both a simple config/dev.exs and a
config/runtime.exs that calls System.get_env. The Local mailer adapter
belongs in dev.exs on that layout. Preferring runtime.exs just because
it exists is how 2.13.6 wrote config :phoenix_kit, PhoenixKit.Mailer
above import Config and crashed the next boot with
undefined function config/3.
Summary
Functions
Detects if the project uses runtime configuration patterns.
Same as detect_config_pattern/0 but takes file contents so it can be
tested without the process cwd. Pass nil for a missing file.
Checks if dev.exs file exists.
Moves any config calls that appear before import Config to just
after it.
Finds the appropriate insertion point for PhoenixKit configuration.
Finds the appropriate location within runtime.exs for development configuration.
Same as find_runtime_insertion_point/0 for a content string.
Checks if runtime.exs contains runtime configuration patterns.
Inserts snippet into a config file immediately after import Config.
Checks if runtime.exs file exists in the project.
Checks if dev.exs file exists and is simple enough to modify.
Wraps the unguarded 2.13.6 Local-adapter snippet in
if config_env() == :dev. No-op when already wrapped or absent.
Functions
Detects if the project uses runtime configuration patterns.
Prefers a simple config/dev.exs even when config/runtime.exs exists
(every mix phx.new tree). runtime.exs is only chosen when there is
no simple dev.exs to write to.
Returns
:dev_exs- Simpleconfig/dev.exs(stock Phoenix):runtime- Uses runtime.exs with no simpledev.exs(e.g. Dotenvy):config_exs- Fall back toconfig/config.exs
Examples
iex> RuntimeDetector.detect_config_pattern()
:dev_exs
@spec detect_config_pattern(String.t() | nil, String.t() | nil) :: :runtime | :dev_exs | :config_exs
Same as detect_config_pattern/0 but takes file contents so it can be
tested without the process cwd. Pass nil for a missing file.
Checks if dev.exs file exists.
Returns
true if dev.exs exists, false otherwise.
Moves any config calls that appear before import Config to just
after it.
A stock Phoenix runtime.exs starts with import Config; writing
above that line is a CompileError (undefined function config/3).
Comments and blank lines above the import are left alone. If
import Config is missing and the file already has config calls,
the import is prepended.
Idempotent.
Finds the appropriate insertion point for PhoenixKit configuration.
Returns
{:runtime, line_number}- Insert at specific line in runtime.exs{:dev_exs, line_number}- Insert at end of dev.exs{:config_exs, line_number}- Insert in config.exs with env check
Examples
iex> RuntimeDetector.find_insertion_point()
{:dev_exs, 40}
Finds the appropriate location within runtime.exs for development configuration.
Returns
line_number (1-based) where PhoenixKit config should be inserted —
always strictly after import Config when that line is present.
@spec find_runtime_insertion_point_in(String.t()) :: pos_integer()
Same as find_runtime_insertion_point/0 for a content string.
The returned 1-based line number is the line to insert AT (existing
content at that line is pushed down). import Config on line 1
therefore returns 2, not 1 — inserting at line 1 is the 2.13.6
CompileError (undefined function config/3).
Checks if runtime.exs contains runtime configuration patterns.
Returns
true if runtime patterns are detected, false otherwise.
Inserts snippet into a config file immediately after import Config.
If import Config is missing, it is added at the top. Never places a
config/3 call above import Config — that is a CompileError.
snippet is trimmed; a blank line is kept on either side.
Checks if runtime.exs file exists in the project.
Returns
true if runtime.exs exists, false otherwise.
Checks if dev.exs file exists and is simple enough to modify.
Returns
true if simple dev.exs exists, false otherwise.
Wraps the unguarded 2.13.6 Local-adapter snippet in
if config_env() == :dev. No-op when already wrapped or absent.
Idempotent.