mix compile.phoenix_kit_js_sources (phoenix_kit v2.5.0)

Copy Markdown View Source

Mix compiler that vendors PhoenixKit's JS hooks into the host app.

This compiler keeps two vendored files current on every mix compile:

  1. priv/static/assets/vendor/phoenix_kit.js — a straight copy of PhoenixKit's own core hooks file. Previously this was only copied once, by mix phoenix_kit.install/mix phoenix_kit.update (File.cp/2 in PhoenixKit.Install.JsIntegration) — a deploy that does rm -rf priv/static + rebuild without re-running phoenix_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 next mix compile, with zero dependency on phoenix_kit.update ever having run again after install.
  2. priv/static/assets/vendor/phoenix_kit_modules.js — external modules declare prebuilt hook bundles via js_sources/0 (see PhoenixKit.Module); this compiler discovers them via PhoenixKit.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's window.<Global> into window.PhoenixKitHooks (which the host spreads into LiveSocket).

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.