Mix compiler that vendors PhoenixKit's JS hooks into the host app.
This compiler keeps two vendored files current on every mix compile:
priv/static/assets/vendor/phoenix_kit.js— a straight copy of PhoenixKit's own core hooks file. Previously this was only copied once, bymix phoenix_kit.install/mix phoenix_kit.update(File.cp/2inPhoenixKit.Install.JsIntegration) — a deploy that doesrm -rf priv/static+ rebuild without re-runningphoenix_kit.update(i.e. most CI/CD pipelines) shipped a 404 on this file, silently breaking every PhoenixKit JS hook. Vendoring it from a compiler makes it self-healing: it comes back on the nextmix compile, with zero dependency onphoenix_kit.updateever having run again after install.priv/static/assets/vendor/phoenix_kit_modules.js— external modules declare prebuilt hook bundles viajs_sources/0(seePhoenixKit.Module); this compiler discovers them viaPhoenixKit.ModuleDiscovery, resolves each bundle's absolute path via:code.priv_dir/1(works for Hex installs and path deps alike), and concatenates them (each wrapped in an IIFE so their top-level scopes can't collide) into this file, followed by a merge that folds each bundle'swindow.<Global>intowindow.PhoenixKitHooks(which the host spreads intoLiveSocket).
Both writes are diffed against the existing file first, so a mix compile
that changes nothing doesn't touch either file (avoids live-reload thrash).
The two <script> tags are stable: adding or removing a JS-bearing
module only changes phoenix_kit_modules.js's content on the next compile —
never the host's layout — so it stays zero-config like css_sources/0.
Setup (one-time, by mix phoenix_kit.install)
# mix.exs
compilers: [:phoenix_kit_js_sources, :phoenix_live_view] ++ Mix.compilers()
# root.html.heex, before app.js
<script src={~p"/assets/vendor/phoenix_kit.js"}></script>
<script src={~p"/assets/vendor/phoenix_kit_modules.js"}></script>Failures are loud: a missing core JS file (corrupted/incomplete dependency fetch) or a declared module bundle that can't be resolved raises a compile error rather than silently shipping "unknown hook" console errors.
This is a fast path, not the only way
It used to be the only way, on the reasoning that a hook had to be in the
host's single LiveSocket at construction time because a nested LiveView
could not register one at runtime. That stopped being true in LiveView 1.1:
getHookDefinition/1 resolves a hook name when its element mounts, and falls
back to a script[data-phx-runtime-hook="Name"] in the document whose
window.phx_hook_<Name>() returns the callbacks. LiveView re-creates such a
script when a patch adds it, so it works on a page delivered over the socket
as well as in the first HTML.
A module can therefore ship its hooks itself and ask nothing of the host —
phoenix_kit_boards does, after two releases where js_sources/0 alone
silently delivered nothing to hosts that bundle dependency JS their own way.
The host's own registration still wins (getHookDefinition checks
liveSocket.hooks first), so the two never collide and this compiler stays
the cheaper path where it is set up.
What it must not be is a silent requirement. PhoenixKitWeb.Integration
warns at compile time when installed modules declare js_sources/0 and this
compiler is absent from the host's :compilers — the state where those
modules' hooks are never delivered, no error appears anywhere, and only the
feature is missing.