On this page
Generating and reading a project's manifest: the JSON record of its identity, version, its pages with their heading anchors, and its published posts.
#internal/manifest
#internal/manifest
Package manifest generates and reads a project's manifest: the JSON record of what a build published -- the project's identity and version, its pages with their heading anchors, and its posts.
The manifest is what a reader that cannot run the build asks. The assembly composes a site out of one manifest per project, the editor draws its outline and its link targets from the pages' headings, and the slug immutability check reads the committed copy out of git to see what a post was published as.
#The document's bytes are the contract
The manifest is committed, so every byte of it shows up in a diff. Its keys are written in declaration order rather than sorted, two-space indented, with every non-ASCII character escaped -- the shape the Python encoder wrote, reproduced here so the port does not rewrite every project's committed manifest on its first run. A write is skipped entirely when nothing but the generation timestamp would change.
#The reader is tolerant
Compat is the one door every read path goes through. It takes the fields it knows and ignores every other key, so a manifest written by a later selfdoc still reads here; a schema_version above the supported one is the single hard refusal, because that declares a document this reader cannot claim to understand.
#DefaultTheme
const DefaultTheme = "minimal"DefaultTheme is the theme a project's manifest records when its config names none. It is the same default the build applies, and the assembly's chrome reads this field to decide which stylesheet a project's pages reference.
#SchemaVersion
const SchemaVersion = 1SchemaVersion is the manifest format this package writes, and the highest it reads. A document declaring more is refused rather than read on this version's terms.
#DefaultOutputName
const DefaultOutputName = "manifest.json"DefaultOutputName is the filename a project's manifest is written under inside .selfdoc/.
#Heading
type Heading structHeading is one heading on a page, with the element id the built page carries for it.
#Page
type Page structPage is one documentation page as the manifest records it.
#Post
type Post structPost is one blog post as the manifest records it.
#Manifest
type Manifest structManifest is a project's published record.
#Doc
type Doc structDoc is one resolved docs page in the shape this package reads it.
It is the docs resolution's per-page tuple, narrowed to the three members the manifest uses: the frontmatter for the title and the type, the resolved content for the headings -- so a directive-generated heading is recorded -- and the raw content for the title fallback.
#ToKebab
func ToKebab(name string) stringToKebab converts a name to a kebab-case slug: lowercased, with spaces and underscores becoming hyphens, every other non-alphanumeric character dropped, runs of hyphens collapsed and the ends trimmed.
#Generate
func Generate(Generate builds a Manifest from a project's config and its resolved docs, and writes it to .selfdoc/
The write is skipped when everything but the generation timestamp is unchanged, so a gen over untouched content does not dirty the working tree. The returned Manifest is what was built either way.
#Compat
func Compat(data map[string]any, source string) (*Manifest, error)Compat builds a Manifest from a parsed manifest document.
Every read path goes through here: the file reader, the git reader, and the assembly's own raw decode. It is a tolerant reader -- it takes the fields it knows about and ignores every other key, which is the contract that lets a later selfdoc add a field without breaking an older reader.
source names where the document came from, for the error message; pass "" when there is nothing useful to name. A schema_version above SchemaVersion is an error: this reader cannot honestly read a document whose format it does not know.
#Load
func Load(path string) (*Manifest, error)Load reads a manifest file and returns what it records.
It returns a nil Manifest and a nil error when the file does not exist -- "this project has no manifest" is an answer, not a failure.
#LoadFromGit
func LoadFromGit(dirPath string, handle *effects.Handle) (*Manifest, error)LoadFromGit reads .selfdoc/manifest.json out of the repository's HEAD, bypassing the working-tree copy.
This is what the slug immutability check compares against: a post's slug must match the one it was published under, and by the time the check runs gen has already rewritten the on-disk manifest with the new slug.
It returns a nil Manifest and a nil error when the directory is not a repository, when the repository has no commits, or when the manifest has never been committed.