# `PhoenixMountable.Assets`
[🔗](https://github.com/hattoriai/phoenix_mountable/blob/v0.2.1/lib/phoenix_mountable/assets.ex#L2)

How a mountable library's CSS and JavaScript reach the browser.

A library picks a strategy with the `:assets` option of `use PhoenixMountable`:

    use PhoenixMountable,
      otp_app: :my_lib,
      macro: :my_lib,
      assets: {PhoenixMountable.Assets.Embedded, css: "priv/static/app.css", js: "priv/static/app.js"}

Strategies shipped with PhoenixMountable:

  * `PhoenixMountable.Assets.Embedded` — bundles read and gzipped at compile
    time and served from memory under content-hashed URLs, the approach of
    Phoenix LiveDashboard, Oban Web and Lotus Web.
  * `PhoenixMountable.Assets.Static` — a directory of files (a Vite, esbuild
    or React build, with an optional manifest) served under the mount.
  * `PhoenixMountable.Assets.Host` — the host's own asset pipeline, for
    libraries styled by the host's Tailwind.

A strategy is any module implementing this behaviour, so a library can bring
its own.

## Entries

Each strategy describes the files a page needs as *entries*: a name, a URL
and a kind (`:css`, `:js` or `:module`). `PhoenixMountable.Layout` renders
them as tags with the request's CSP nonces and `phx-track-static`, and
`url/2` returns one entry's URL.

## Stale assets

After a deploy, a browser can keep running the previous bundle. When a
library LiveView connects, PhoenixMountable asks the strategy whether any of
the URLs the page tracks (`phx-track-static`) belongs to a previous build,
and reloads the page if so. Phoenix's own `static_changed?/1` cannot see this
because it only knows the host's static manifest.

# `entry`

```elixir
@type entry() :: %{name: atom(), url: String.t(), kind: :css | :js | :module}
```

An asset of a page.

# `route`

```elixir
@type route() :: %{
  :verb =&gt; atom(),
  :path =&gt; String.t(),
  :plug =&gt; module(),
  :plug_opts =&gt; term(),
  optional(:opts) =&gt; keyword()
}
```

A route a strategy adds under the mount path.

# `entries`

```elixir
@callback entries(
  state :: term(),
  PhoenixMountable.Mount.t(),
  path_fun :: (String.t() -&gt; String.t())
) :: [
  entry()
]
```

The entries of the strategy for a mount.

`path_fun` turns a path relative to the mount into a full path, honouring
the endpoint's script name and the mount's `:asset_host`.

# `init`

```elixir
@callback init(opts :: keyword(), context :: %{module: module(), otp_app: atom()}) ::
  term()
```

Validates the strategy options and computes its state.

Runs at compile time in the definition module. `context` has the
definition `:module` and the library's `:otp_app`. The state is stored in
every mount, so keep it small: file contents belong in `c:inject/2`.

# `inject`
*optional* 

```elixir
@callback inject(state :: term(), definition :: module()) :: Macro.t()
```

Code to inject into the definition module, such as embedded file contents.

# `routes`

```elixir
@callback routes(state :: term(), definition :: module()) :: [route()]
```

Routes to add under the mount path.

# `stale?`

```elixir
@callback stale?(state :: term(), PhoenixMountable.Mount.t(), tracked :: [String.t()]) ::
  boolean()
```

Whether any of the tracked URLs was produced by a previous build.

# `entries`

```elixir
@spec entries(PhoenixMountable.Mount.t()) :: [entry()]
```

The asset entries of a mount, in order.

# `stale?`

```elixir
@spec stale?(PhoenixMountable.Mount.t(), [String.t()]) :: boolean()
```

Whether the page that tracks `tracked` URLs runs assets of a previous build.

# `url`

```elixir
@spec url(PhoenixMountable.Mount.t(), atom()) :: String.t()
```

The URL of the asset entry `name`.

    <script src={PhoenixMountable.Assets.url(@mount, :js)} defer></script>

---

*Consult [api-reference.md](api-reference.md) for complete listing*
