Pure helpers for the mix mob.adopt Elixir-source patches and
content generators: the LiveView bridge patches (assets/js/app.js
MobHook + root.html.heex bridge div), the mix.exs dep injection,
and the generated mob_screen.ex / mob_app.ex / mob.exs /
src/<app>.erl contents.
Duplicated from MobNew.LiveViewPatcher (mob_new, the project
generator archive). Only the transitive closure mix mob.adopt
actually exercises was copied — mob.new's notes-app generators
(repo_content, note_content, the LiveView starter screens, …)
stay in mob_new. Phase 5 of build_system_migration.md reunifies the
two copies behind a single Igniter-based path; until then both repos
carry their own copy (mob_new can't depend on mob_dev — it's a
self-contained Mix archive; see ArchiveSelfContainedTest).
Compile-time regex
Like the mob_new original, this module compiles regexes at runtime via
Regex.compile!/1 rather than ~r// literals — the ~r// form bakes
a bytecode pattern that calls :re.import/1, removed in OTP 28.0 (the
version Mob's bundled iOS/Android tarballs ship). See mob AGENTS.md
rule #9.
Summary
Functions
Generates the Erlang bootstrap for a LiveView project.
Injects mob / mob_dev dependencies into the deps/0 function in mix.exs content.
Adds {:ecto_sqlite3, "~> 0.18"} to the deps list in mix.exs content
if not already present. The generated mob_app.ex (LiveView flavour)
calls Application.ensure_all_started(:ecto_sqlite3) and runs
Ecto.Migrator on-device, so the dep is required whenever that
template is emitted.
Injects the hidden bridge <div> immediately after the opening <body> tag.
Injects the MobHook definition and registration into the given app.js content.
Generates a thin-client <App>.MobApp for projects where the BEAM on
device does NOT host Phoenix/Hologram/game state — instead the WebView
points at a deployed Phoenix server and the device's BEAM is just the
native interop layer.
Returns the hidden bridge element string (for test assertions).
Generates mob.exs config content for a LiveView project.
Returns the MobHook JS string (for tests and warning messages).
Generates the mob_app.ex entry point for a LiveView project.
Generates MobScreen content for mix mob.adopt.
Functions
Generates the Erlang bootstrap for a LiveView project.
Calls ModuleName.MobApp.start() instead of ModuleName.App.start().
Injects mob / mob_dev dependencies into the deps/0 function in mix.exs content.
mob_dep and mob_dev_dep are dependency tuple strings (already formatted —
e.g. ~s({:mob, "~> 0.5"}) or ~s({:mob, path: "/path"})). They are parsed
back to AST and inserted at the end of the user's deps list.
Idempotent: no-op if :mob is already declared in the user's deps list,
regardless of indentation or trailing-comma shape.
Implementation note
Deps are injected by an AST walk (robust against Phoenix-version / formatter
variation), then serialized with stdlib only (Macro.to_string +
Code.format_string!). The mob_new original is reachable from mix mob.new
running as a Mix archive, and archives don't bundle runtime deps — so it
must not call any non-stdlib module (Sourceror). Preserved here verbatim.
Adds {:ecto_sqlite3, "~> 0.18"} to the deps list in mix.exs content
if not already present. The generated mob_app.ex (LiveView flavour)
calls Application.ensure_all_started(:ecto_sqlite3) and runs
Ecto.Migrator on-device, so the dep is required whenever that
template is emitted.
Idempotent — no-op when ecto_sqlite3 is already in the deps string.
Injects the hidden bridge <div> immediately after the opening <body> tag.
Idempotent: returns unchanged content if mob-bridge is already present.
Injects the MobHook definition and registration into the given app.js content.
Idempotent: returns unchanged content if MobHook is already present.
Generates a thin-client <App>.MobApp for projects where the BEAM on
device does NOT host Phoenix/Hologram/game state — instead the WebView
points at a deployed Phoenix server and the device's BEAM is just the
native interop layer.
Produced when mix mob.adopt --no-live-view is invoked. The thin
variant uses use Mob.App with navigation/1 + on_start/0
callbacks (the same shape mix mob.new generates for native mode),
rather than the LV-flavored def start do ... end that boots the
host Phoenix endpoint on-device.
@spec mob_bridge_element() :: String.t()
Returns the hidden bridge element string (for test assertions).
Generates mob.exs config content for a LiveView project.
@spec mob_hook_js() :: String.t()
Returns the MobHook JS string (for tests and warning messages).
Generates the mob_app.ex entry point for a LiveView project.
This module is called from the Erlang bootstrap (src/app_name.erl) instead
of a native Mob.App module. It starts the Phoenix OTP application (which
boots the endpoint) and then starts MobScreen to open the WebView.
Unlike native Mob apps, this does NOT use Mob.App — Phoenix owns the
supervision tree. Mob is wired in at the BEAM entry level only.
secret_key_base and signing_salt are embedded directly because Mix config
files (config/*.exs) are not loaded on-device — Application.put_env/3 is
the only way to configure the endpoint before ensure_all_started/1 runs.
The on-device port defaults to a per-app hash (4200..4999) — see
default_liveview_port/0 for the collision rationale.
Generates MobScreen content for mix mob.adopt.
The generated module reads the WebView URL from application config:
config :mob, host_url: "https://your-app.example.com/"Default if unset is http://127.0.0.1:4000/, suitable for on-device
BEAM hitting a local Phoenix endpoint. mix mob.adopt --host-url <URL> writes the config entry so the user doesn't need to edit
config/config.exs by hand.