# `MishkaCmsCore.Runtime.ComponentPrefetch`
[🔗](https://github.com/mishka-group/mishka_cms_core/blob/v0.0.1/lib/mishka_cms_core/runtime/component_prefetch.ex#L5)

Builds the components a page or a layout is about to draw, several at once, before it draws them.

A component that is not precompiled is built by the first render that names it
(`MishkaCmsCore.Runtime.LiveViewHelpers.component/1`), and a render meets its components one
after another, a child only once its parent is drawn. So the first visitor of a cold page waited
for every one of them in a row. Compiling is CPU-bound, and the node has more than one core.

A page module and a layout module built by `MishkaCmsCore.Runtime.Compilers.LiveViewCompiler` and
`MishkaCmsCore.Runtime.Compilers.LayoutCompiler` call `prefetch/2` at the top of their `render/1`.

## A built page does nothing

The module carries the components its own template names in a tag that says which copy it draws,
as module names. When every one of them is loaded — or its last compile failed, or it is another
site's — `prefetch/2` answers `:ok` having read nothing but the loaded-code table: no file, no
database, no message.

## A cold one

When one is not, the module's own template is walked from what it keeps
(`MishkaCmsCore.Runtime.Compilers.CssClosure.reached_components/2`, `:references`), through the
component templates the boot writes, to every component it reaches, each in the copy the render
draws. Those not loaded, not known to fail, and global or of the site this process renders for
are built through `MishkaCmsCore.Runtime.LiveViewHelpers.ensure_component_loaded/2` — the path
the render takes, so the registry, the worker and `MishkaCmsCore.Runtime.Handlers.CompileGate`
are the same ones, nothing is compiled twice, and a render or another request asking for one of
them meanwhile waits for that compile. `render/1` goes on once they are all done.

## Its share of the node

One render builds at most `slots/0` at once: `config :mishka_cms_core, :component_prefetch_share`
of the gate's limit, rounded down and at least one — half when unset. The rest of the gate stays
for every other request and site. `0` or `false` turns this off, and a render builds its
components one after another, as it did.

## What it never does

It takes no lock but a gate slot per compile, and reads no row itself: each compile reads its own
in its worker, as a render's would. A component that does not compile fails as it does in a
render — its failure is kept in `MishkaCmsCore.Runtime.States.CompileFailureState`, and the render
draws the box it always draws in its place. A compile that crashes ends its task, not the page.

## Events

  * `runtime:compile:before_page_prefetch` — before anything is built, with
    `%{module: module, site: site_name, site_id: site_id, components: [%{name: name, site: site}]}`,
    `site` the site's name for a component of its own and `"Global"` for a global one. A plugin
    answers the map with other `components` to build more or fewer; only those not built yet, of
    the site rendered for or global, are built.
  * `runtime:compile:after_page_prefetch` — once they are built, with
    `%{module: module, site: site_name, site_id: site_id, built: [component],
    failed: [%{name: name, site: site, reason: reason}]}`.

## Telemetry

  * `[:mishka_cms, :runtime, :component_prefetch, :start | :stop | :exception]` — a span around a
    cold page's walk and build, with `%{module: module, site: site_name, site_id: site_id}`, and on
    `:stop` `asked`, `built`, `failed` and `slots` too. A built page emits nothing.

# `component`

```elixir
@type component() :: %{name: String.t(), site: String.t()}
```

A component, and the site a `<.component>` tag names to draw it.

# `prefetch`

```elixir
@spec prefetch(module(), [{module(), String.t()}]) :: :ok
```

Builds what `owner` is about to draw, unless every component in `named` is already built.

`owner` is a page or layout module; `named` is the components its template names, as
`{module, site}`, which the compiler writes into the call. Always `:ok`.

# `slots`

```elixir
@spec slots() :: non_neg_integer()
```

How many components one render builds at once: `:component_prefetch_share` of
`MishkaCmsCore.Runtime.Handlers.CompileGate.limit/0`, at least one, and `0` when it is off.

---

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