Skip to content
internal/resolver
On this page

Dispatching one directive to whatever can answer it: the content directives first, then the project's own custom scripts, then the language extractors.

#internal/resolver

#internal/resolver

Package resolver dispatches one directive to whatever can answer it.

A resolver is built for a project and called once per directive. It tries the content directives first (they are language-agnostic), then the project's own custom directives, and then the language extractors -- which for a multi-language project means deciding which language owns the path the directive names.

#Custom directives run out of process

The directives config key maps a directive name to a script, and the contract that key has always had is Python's: the script defines resolve(attrs, config, body) and returns Markdown. That contract is kept, so the script is still loaded and called by Python: an embedded driver (driver.py) is handed to python3, the script's path as its argument and one JSON object on standard input, and the Markdown it prints is what replaces the directive.

The driver runs as a declared read through the effects handle, so a --dry-run resolves directives like any other run.

A script that cannot be loaded, one with no callable resolve, one that raises, and a machine with no python3 are each a hard error naming the directive and the script. The Python this replaces swallowed all four into an inline note on the page; a page that says "custom directive 'api' failed" where its API reference belongs is not a page anybody wanted published, and the note was as easy to miss as any other paragraph.

#Directives compiled into the binary

The same config key also accepts a [BuiltinDirective]: a function this binary carries, registered by the caller that owns it and resolved in process at the point a script would have been. The registrars are the site-level directives an assembled site's home project carries, which render from the assembly's manifests -- state a build is handed and no config document can hold. They arrived as shipped Python shim scripts before there was one binary to compile them into.

#Resolver

Go go
type Resolver struct

Resolver resolves directives for one project.

It is not safe for concurrent use: LastSourceEntry is per-call state that a second caller would overwrite.

#BuiltinDirective

Go go
type BuiltinDirective func(attrs map[string]string, body []string) (string, error)

BuiltinDirective resolves one directive in process, from whatever state the registering caller captured.

It is the second value the "directives" config key accepts. A string names a script, which the Python driver loads and calls; a BuiltinDirective is a directive compiled into this binary and handed to [MakeResolver] by the caller that owns it, at the one point in the dispatch order the scripts occupy. The site-level directives of an assembled site's home project are the registrars: they render from the assembly's manifests, which the home project's build receives and no config document can hold.

Both values live under one key because a directive name is either known to the catalog or declared there, and the name set the config declares is what decides which markers a page may carry. A separate key would leave a registered directive unknown to that check and refused before it ever reached a resolver.

#MakeResolver

Go go
func MakeResolver(config map[string]any, baseDir string, handle *effects.Handle) (*Resolver, error)

MakeResolver builds the resolver for a project.

config is the loaded selfdoc.json, baseDir the project root every relative path is resolved against, and handle the effects handle the custom-directive driver runs under.

#Resolver.Resolve

Go go
func (r *Resolver) Resolve(name string, attrs map[string]string, body []string) (string, error)

Resolve resolves one directive into the Markdown that replaces it.

A directive that cannot be answered from the project's own files renders an error marker and no error: one bad directive degrades one region of one page instead of failing the build. An error is the hard-error family -- a directive that reads source code in a project that declares none, an ambiguous path, a custom directive that failed -- where a marker would hide the fact that the page lost the content it asked for.

Search