Skip to content

Documentation toolchain โ€‹

How the documentation under docs/ is produced, and which parts of it you may edit by hand.

This covers the documentation toolchain only. For the general development loop โ€” lint, static analysis, tests, and what to run before a pull request โ€” see CONTRIBUTING.md.

Generated pages โ€” do not edit โ€‹

These pages are written by composer docs:gen. Editing them directly is wasted work: the next docs:gen overwrites your changes, and so does composer docs:build, which runs docs:gen first.

PageBuilt from
reference/annotations.mdsrc/Annotations/ docblocks + snippets/preamble_annotations.md
reference/attributes.mdsrc/Attributes/ docblocks + snippets/preamble_attributes.md
reference/spec-attributes.mdsrc/Spec/ docblocks + snippets/preamble_spec-attributes.md
reference/processors.mdsrc/Processors/ docblocks + snippets/preamble_processors.md + prose in ProcessorGenerator
reference/augmenters.mdsrc/Augmenter/ docblocks + snippets/preamble_augmenters.md + prose in AugmenterGenerator
guide/examples.mdexample sources + the per-example docs/examples/specs/*/Readme.md

To change one of these, change its source and re-run composer docs:gen.

Note that some prose lives inside the generators rather than in any markdown file โ€” the -c and -D explanations in the "Configuration" sections are string literals in tools/src/Docs/Reference/{Augmenter,Processor}Generator.php.

Every generator renders through tools/src/Docs/Sections/; Renderer builds only the page frame โ€” preamble, headers, and the example blocks. Parameters render as a definition list: a description is prose with its own paragraph breaks and a type arrives HTML-escaped, so both need markup that reaches the page without the markdown parser reading it again.

Everything else under docs/ is hand-written โ€” including the top-level docs/examples/Readme.md, since only the per-example ones feed the generated page.

docs:gen is a drift check โ€‹

The generators are deterministic, and committed output is expected to match its source. So:

shell
composer docs:gen && git status --porcelain docs/

Any output means the committed pages have drifted, and the regenerated version is the correct one. Worth running before opening a pull request.

Run it with an otherwise-clean tree, or scope the git status to the generated pages โ€” docs/reference/ holds hand-written ones too, so a directory-wide check reports your own edits as drift.

Commands โ€‹

CommandNotes
composer docs:genregenerate the pages listed above
composer docs:buildruns docs:gen, then builds the static site
composer docs:devlocal preview โ€” long-running, prints its URL on startup and does not return

What counts as a documented config setting โ€‹

reference/augmenters.md and reference/processors.md list the options accepted by -c name.option=value. A setting qualifies when it is a constructor parameter that is not object typed:

  • constructor parameters are the public configuration contract, by convention
  • object typed parameters โ€” factories, resolvers, the generator โ€” are collaborators, not settings, and cannot be expressed as a CLI value

This is implemented once, in DocGenerator::configurableParameters(), and mirrors what Utils\Pipeline::getConfig() reports at runtime. The two are aligned by hand rather than by construction; if you change one, check the other. The check is that ./bin/openapi --mode spec -D src lists exactly the settings the reference page documents.

CLI behavior worth knowing โ€‹

  • -D / --defaults prints the resolved default config, but still requires the paths argument: ./bin/openapi --mode spec -D src, not ./bin/openapi --mode spec -D.
  • --version sets the target OpenAPI version, not the tool version.
  • Help output is standard Symfony Console format, from ./bin/openapi -h.
  • Unknown -c keys are reported as warnings in spec mode, via Pipeline::configure(). In classic mode they are silently ignored, because configuration is routed through Generator::setConfig() instead. A typo in a classic -c key fails invisibly.

Known rough edge โ€‹

AugmenterGenerator::renderConfigSection() and ProcessorGenerator::renderConfigSection() are the same method twice, differing in the noun, the mode flag, and the programmatic example. A fix to one usually needs applying to the other.