DocShell.Presentation.StaticGenerator (DocShell v0.3.0)

Copy Markdown View Source

Builds navigation, search, and content indexes from extracted entries.

This is the default DocShell.Presentation.Source — the one DocShell.Build uses unless a host substitutes its own. It takes the flat list of entries the extractors produced and derives the three indexes a documentation site needs:

  • navigation — one DocShell.Presentation.NavigationItem per entry, sorted by kind then title, so modules, guides, notebooks, and release notes group together and each group reads alphabetically.
  • search — one DocShell.Presentation.SearchEntry per entry, with the document flattened to plain text and optional precomputed tokens.
  • content — a map from entry id to its AST nodes, so a renderer can load one page without parsing the whole set.

Deliberately flat

Every navigation item comes back with no children. That is not an omission: DocShell has no way to know whether your guides should nest under a section, whether modules should group by namespace, or whether the tree should follow the file layout at all. Those are product decisions, and a package that guessed at them would be wrong for most hosts and hard to override for the rest.

A host that wants structure has two options: reshape the flat list after DocShell.Build.run/1 returns it, or implement DocShell.Presentation.GraphProjector and own categorization outright.

Paths

Entry paths default to /docs/{kind}/{id}, which is a placeholder more than a recommendation. Hosts routing documentation anywhere else pass a :path_builder function:

StaticGenerator.project(
  entries: entries,
  path_builder: fn entry -> "/handbook/" <> entry["id"] end
)

The same function builds both navigation and search paths, so a search result and a nav link can never disagree about where a document lives.

Empty entries

Entries with no content are dropped. DocShell.Generate.ExDoc returns every module it can read, including ones marked @moduledoc false, because coverage reporting needs them — but a navigation tree listing every internal module as a blank page helps nobody. skip_empty: false keeps them.

Search text

Search content preserves inline text adjacency and separates block elements, which means code blocks, table cells, and link text are all searchable and no markup leaks into the index.

tokens defaults to []. With search_tokens: true, the same text is downcased and split on non-alphanumeric runs. Enable this when the host's search backend needs precomputed tokens.

Options

  • :entries — the extracted entries to project; defaults to []
  • :path_builder — a function from entry to path; defaults to default_path/1
  • :skip_empty — drop entries with no content; defaults to true
  • :search_tokens — populate SearchEntry.tokens; defaults to false

Summary

Functions

Returns the fallback path for an entry: /docs/{kind}/{id}.

Functions

default_path(entry)

@spec default_path(map()) :: String.t()

Returns the fallback path for an entry: /docs/{kind}/{id}.

Public so a host writing its own :path_builder can fall back to it for kinds it does not handle specially.

Examples

iex> DocShell.Presentation.StaticGenerator.default_path(%{"kind" => "guide", "id" => "intro"})
"/docs/guide/intro"