Mutare.Phoenix.LiveView.Hook (mutare_phoenix_live_view v0.1.0)

Copy Markdown View Source

:lv_hook — removes a Phoenix.LiveView lifecycle-hook management call, collapsing it to the socket it would have returned:

attach_hook(socket, :log, :handle_event, &log/3)  ->  socket
detach_hook(socket, :log, :handle_event)          ->  socket
socket |> attach_hook(:log, :handle_event, &log/3)  ->  Elixir.Function.identity()

Both directions of the hook lifecycle are one family with two variant labels: attach ("no test depends on this hook running" — the hook never fires) and detach ("no test depends on this hook stopping" — the hook keeps running). Suppress one kind with # mutare:ignore[lv_hook:attach] / [lv_hook:detach], or the whole family with # mutare:ignore[lv_hook].

Only the attachment is this family's seam. The :cont/:halt decision inside a hook function (and in on_mount/4) belongs to Mutare's built-in :convention family, which swaps those atoms wherever they appear — enabling both families mutates the two axes independently.

Only the real arities fire — attach_hook/4 and detach_hook/3 — so a name-matched call of any other arity (reachable only by an explicit qualifier) is left alone, keeping every metamutant compiling. Piped calls are removed with the Elixir.Function.identity() no-op stage (the :lv_event trick — the :Elixir-led alias is never rewritten by alias resolution, so the no-op always names the real Function.identity/1).

The hook name and stage arguments are structural identifiers, not computed values — perturbing :handle_event raises inside attach_hook (an uninformative crash-kill), and a renamed hook still runs (a near-unkillable equivalent). This family therefore routes both positions :raw (Mutare.CallRouting), so no core family — AtomLiteral, StringLiteral, ConventionAtom, … — mutates them or anything inside them.

Matches direct (Phoenix.LiveView.attach_hook(...)), aliased, and bare-imported (use-injected) calls.