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.NavigationItemper entry, sorted by kind, title, then ID, so modules, guides, notebooks, and release notes group together and each group reads alphabetically. - search — one
DocShell.Presentation.SearchEntryper 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.
search_members: true adds module member names/arities, signatures and parsed
documentation to the containing page's search text. It does not create member
routes or change content ASTs, and does not override skip_empty. Malformed
member records/Markdown return an error tagged with the module ID. The default
stays false so existing search payloads and extraction cost are unchanged.
Options
:entries— the extracted entries to project; defaults to[]:path_builder— a function from entry to path; defaults todefault_path/1:skip_empty— drop entries with no content; defaults totrue:search_tokens— populateSearchEntry.tokens; defaults tofalse:search_members— include module member text; defaults tofalse
Summary
Functions
Returns the fallback path for an entry: /docs/{kind}/{id}.