Files
mattermost/docs/site
Eva SarafianouandCursor b9c2bd733f docs: restore generated plugin SDK reference pages (#37788)
* docs: restore generated plugin SDK reference pages

Reimplements the Hugo-era plugingodocs/pluginjsdocs/pluginmanifestdocs
shortcode pipeline natively in Docusaurus, so the server plugin SDK,
web app plugin SDK, and manifest reference pages render full generated
content again instead of "Generated content (migrating)" placeholders.

Two new Go generators (gen-plugin-godocs, gen-plugin-manifest-docs) and
one Node generator (gen-plugin-jsdocs.mjs) read server/public/plugin,
server/public/model, and webapp/channels/src/plugins/registry.ts
directly from this monorepo and emit gitignored JSON consumed by new
PluginGoDocs/PluginGoExample/PluginJsDocs/PluginManifestDocs React
components, wired into prestart/prebuild alongside the existing
sidebar/OpenAPI generators.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: remove migration plan doc from this PR

Was a handoff/status doc for the implementing agent, not meant to ship
as part of the change itself.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: make plugin-godocs/manifest-docs builds atomic

build:plugin-godocs and build:plugin-manifest-docs redirected straight
into data/plugin-*.json, which doesn't exist on a clean checkout (the
redirection itself would fail before the generator ever ran) and, on a
subsequent failed run, would truncate a previously-good JSON file
before failing. Create data/ up front, write to a .tmp file, and only
mv it into place once the generator exits successfully.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: fix review findings in plugin doc generators

gen-plugin-manifest-docs:
- jsonFieldName now excludes unexported fields (matching encoding/json)
  and falls back to the Go field name when there's no tag or the tag's
  name component is empty (e.g. json:",omitempty"), instead of
  dropping the field entirely.
- exprTypeDocs's SelectorExpr case now resolves a qualified type only
  when its package qualifier actually points at the model package
  (checked against the declaring file's imports), instead of matching
  any x.Sel identifier by name alone regardless of which package it
  qualifies.

gen-plugin-godocs:
- The example-code loop now falls back to example.Code when
  example.Play is nil (go/doc leaves Play nil when it can't synthesize
  a whole runnable program), and propagates printer.Fprint errors
  instead of discarding them.

No output change for the current server/public/plugin or
server/public/model content — verified via a full regen.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: drop legacy-repo comment references, align jsdocs generator parser

Remove "port of the old mattermost-developer-documentation Hugo
shortcode" comments from the plugin doc generators/components now that
they're fully native to this monorepo, and switch gen-plugin-jsdocs.mjs
from the TypeScript compiler API to @typescript-eslint/typescript-estree
to match the old script's parser/shape, while keeping the more robust
reArg-declared-parameter-name and node-scoped comment-attachment logic.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: bump plugin doc generators' go.mod to go 1.26

go 1.23 was arbitrarily low; align with a current Go version. go run
auto-downloads a matching toolchain on older local installs, so this
doesn't reintroduce a dependency on server/public/go.mod's version.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: handle RestElement in gen-plugin-jsdocs parameter extraction

paramNamesFromPattern silently dropped rest parameters/destructured rest
members (e.g. (a, ...rest) or {a, ...rest}), since RestElement matched
none of its type checks. No effect on today's registry.ts output (no
rest patterns currently used there), but keeps the extraction correct if
one is ever introduced.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: simplify plugin doc generators README note

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: check stdout write errors in plugin doc generators

os.Stdout.Write's return errors were discarded, so a failed/partial
write (e.g. broken pipe, disk full) would still exit 0. Combined with
the npm scripts' > file.tmp && mv pattern, a truncated write could be
treated as a successful generation. Fatal on either write failing.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: flatten Server/Webapp plugin SDK reference sidebar nesting

server/server-reference.md and webapp/webapp-reference.md were the only
file in their respective folders, so the sidebar generator (which only
collapses a folder into a single entry when it has an index.md) rendered
them as a "Server" > "Server plugin SDK reference" category with one
child instead of one flat entry, unlike every sibling reference folder
(rest-api/index.md, bot-accounts/index.md, etc.).

Rename both to index.md to match that convention, and update the ~20
cross-referencing links (many with #anchor fragments) that pointed at
the old /reference/server/server-reference and /reference/webapp/webapp-
reference paths.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-21 15:21:21 +03:00
..

Mattermost Documentation Site

Docusaurus workspace for docs.mattermost.com, living in the mattermost/mattermost monorepo at docs/site/.

Content layout

Directory Route Description
docs/main/ / User and admin documentation
docs/develop/ /developers Developer documentation
docs/api/ /api API reference intro + generated OpenAPI pages

Paths are relative to the repository root. The Docusaurus site reads them via the relative paths ../main, ../develop, ../api (from docs/site/).

Prerequisites

  • Node.js ≥ 20 — use nvm use inside docs/site/ to pick up .nvmrc
  • Go and make (required for the OpenAPI prebuild step, and for two of the three plugin SDK reference generators — see below)
  • Vale ≥ 3 (for content linting)

Local development

cd docs/site
npm ci
npm start          # dev server at http://localhost:3000

Sidebar generation

The documentation and developers sidebars are generated from the content directories (sidebars/documentation.generated.json / developers.generated.json, both gitignored) by npm run build:sidebars. Docusaurus imports these files directly, so they must exist before docusaurus start or docusaurus build runs — on a fresh checkout there's no other source for them. This is wired automatically via the prestart and prebuild npm lifecycle hooks, so plain npm start / npm run build just work.

(sidebars/active-redirects.json, by contrast, is committed — it's regenerated and checked in manually via node scripts/gen-active-redirects.mjs when the legacy redirect map changes, not on every build.)

Manual grouping overrides

Most top-level sections build their sidebar straight from the filesystem: each subdirectory becomes a category, each file a doc, sorted by sidebar_position frontmatter then filename. But a few sections are flat piles of 15-40 files (or split across an inconsistent filesystem nesting that doesn't reflect any real grouping) that read badly as-is, so gen-documentation-sidebar.mjs layers a manual grouping override on top of the auto-generated tree for those sections only: Overview (OVERVIEW_GROUPS/OVERVIEW_ROOT_ORDER), Deployment Guide (DEPLOYMENT_GROUPS/DEPLOYMENT_ROOT_ORDER), Administration Guide → Configure (ADMIN_CONFIGURE_GROUPS/ADMIN_CONFIGURE_ORDER), Administration Guide → Manage (ADMIN_MANAGE_GROUPS/ADMIN_MANAGE_ORDER), Administration Guide → Onboard (ADMIN_ONBOARD_GROUPS/ADMIN_ONBOARD_ORDER), Administration Guide → Scale (ADMIN_SCALE_GROUPS/ADMIN_SCALE_ORDER), End User Guide → Collaborate (COLLABORATE_GROUPS/COLLABORATE_ORDER), and Integrations Guide (INTEGRATIONS_GROUPS/INTEGRATIONS_ROOT_ORDER).

Usually the override only changes how the sidebar renders — files stay flat on disk at their existing paths, so URLs don't move. The one exception is Administration Guide → Scale: 21 of its 28 files (capacity planning, HA/architecture, search infrastructure, caching) were physically moved to deployment-guide/reference-architecture/scale/ and are grouped via DEPLOYMENT_GROUPS.scaling, matching where Sphinx's live nav actually lists that content (under Deployment Guide → Reference Architecture, as a sibling of Application Architecture). Only the 7 monitoring/observability pages stayed under ADMIN_SCALE_GROUPS. Since that move changes URLs, the Sphinx→Docusaurus redirect targets for those 21 pages in sidebars/active-redirects.json were updated to point at the new paths, and internal .mdx links across the docs were rewritten accordingly.

Each override is a pair of constants near the top of the script:

  • A *_GROUPS map of group key → {label, landing?, items}, where items are doc basenames (relative to that section's directory) or nested inline group objects. A group's items can itself contain a nested {label, items} sub-group, which adds a third category level — a four-level path of Guide → Group → Sub-group → page — for a section large enough to need it — see OVERVIEW_GROUPS.subscription's "Cloud" sub-group, or ADMIN_MANAGE_GROUPS.userAccess's "Attribute-Based Access Control" sub-group, for existing examples.
  • A *_ROOT_ORDER/*_ORDER array listing the top-level order: plain strings for standalone docs, {group: 'key'} for a group from the map above.

There's no single shared "grouping engine" — each overridden section gets its own small buildXItem/regroupX (or buildXSidebar) pair that mirrors the shape of the others (see buildCollaborateItem/regroupCollaborate for the End User Guide → Collaborate one). Adding an override for a new section means copying that shape for the new section, not extending a shared function.

Adding a new file to one of these sections: the script fails loudly if you forget it — it logs a WARN: N file(s) missing from *_ORDER and falls back to appending the orphaned file(s) at the root of that section, so a forgotten file surfaces as a warning during npm run build:sidebars rather than silently disappearing. Add the new file's basename to the relevant group's items (or to the root order array, if it's a standalone/uncategorized page) to place it deliberately instead of leaving it at the root.

Adding a whole new manually-grouped section: copy the pattern of an existing one (Integrations Guide is the simplest example), then wire it into main() alongside the existing dir === '...' checks.

Nesting a doc from one section under a page in another section: most group items are plain basenames relative to that section's own directory, but buildAdminConfigureItem also accepts {doc: '<full id>'} for cross-directory references (their label is read directly from the target file's frontmatter via docLabelById, since it won't be in that section's leafLabels map). This is how ADMIN_CONFIGURE_GROUPS.agents nests the vendored Agents plugin pages (main/agents/docs/, staged by stage-agents-docs.mjs — not one of the TOP_LEVEL sections, so it has no top-level nav entry of its own) under administration-guide/configure/agents-admin-guide. The same page is also nested for End User Guide's end-user-guide/agents doc, but since that section has no manual grouping override at all, it uses the smaller standalone promoteDocToCategory helper instead of a full *_GROUPS override — copy that pattern for other one-off single-doc nestings rather than building a whole grouping override for a section that's otherwise fine auto-generated.

Inlining another doc's content onto a page (rather than just linking or nesting it): Docusaurus's built-in Markdown partials feature — any .md/.mdx file with a leading underscore in its name is excluded from the docs plugin's routing/sidebars and can be imported into another MDX file and rendered as <Component />. stage-agents-docs.mjs uses this to reproduce Sphinx's .. include:: /agents/docs/admin_guide.md behavior: it stages admin_guide.md/user_guide.md as normal (but unlisted: true) docs for direct-link parity, and as _admin_guide_partial.mdx/_user_guide_partial.mdx partials that administration-guide/configure/agents-admin-guide.mdx and end-user-guide/agents.mdx import and render inline — so those two pages show the full vendored guide content directly, with zero extra clicks, instead of just linking out to a separate page.

Plugin SDK reference generators

Three developer pages render content generated at build time from the plugin SDK's own source rather than hand-written prose, each backed by a gitignored JSON file under data/:

Page Generator Reads
Server plugin SDK reference scripts/gen-plugin-godocs (Go) server/public/plugin
Web app plugin SDK reference scripts/gen-plugin-jsdocs.mjs (Node) webapp/channels/src/plugins/registry.ts
Manifest reference scripts/gen-plugin-manifest-docs (Go) server/public/model's Manifest struct

They're consumed by the <PluginGoDocs />, <PluginGoExample />, <PluginJsDocs />, and <PluginManifestDocs /> components (registered globally in src/theme/MDXComponents.tsx), and run via npm run build:plugin-docs, wired into prestart/prebuild like everything else in this section. The two Go generators parse their target packages with go/parser + go/doc rather than type-checking them via golang.org/x/tools/go/packages, so they have no dependency on the Go toolchain version declared in server/public/go.mod — only stdlib, no go.sum.

The API reference section (docs/api/reference/, also gitignored) has the same requirement: docusaurus-plugin-openapi-docs needs docusaurus gen-api-docs mattermost run before it has any pages to render. prestart handles this too — using the existing OpenAPI spec if present, only falling back to the slow make -C api build spec rebuild if it's missing.

Full production build

The production build also includes an OpenAPI prebuild step (npm run build:openapi, wired to run automatically before npm run build via the same prebuild hook) that invokes make -C api build. This requires Go and takes ~2 minutes.

cd docs/site
npm ci
npm run build      # runs build:sidebars + build:openapi (via prebuild), then Docusaurus build

To skip the OpenAPI rebuild during iterative content work:

cd docs/site
npm run build -- --no-minify   # still runs build:sidebars + build:openapi first

If you need to bypass the prebuild step entirely (e.g., all generated artifacts already exist and are current), run:

cd docs/site
npm run docusaurus build       # calls docusaurus directly, skips prebuild

There's a single Algolia DocSearch app for docs.mattermost.com — credentials aren't distributed to individual developers. They're set as repository variables (vars.ALGOLIA_APP_ID, vars.ALGOLIA_SEARCH_API_KEY) and injected only in CI/CD. Local builds simply run without them: the site builds cleanly and the search bar is omitted (see the conditional in docusaurus.config.ts).

If you need to test search locally, export the same two variables in your shell before running npm start/npm run build.

Scripts

Command Description
npm start Dev server with hot reload (runs build:sidebars + build:openapi:docs first via prestart)
npm run build Production build to build/ (runs build:sidebars + build:openapi first via prebuild)
npm run build:sidebars Regenerate the documentation + developer sidebar JSON
npm run build:openapi:spec Regenerate the OpenAPI spec only (slow — invokes make -C api build)
npm run build:openapi:docs Regenerate the API reference MDX pages from the existing spec (fast)
npm run build:openapi Full OpenAPI pipeline: spec then docs
npm run build:plugin-docs Regenerate all three plugin SDK reference data files (see above)
npm run serve Serve the build/ output locally
npm run typecheck TypeScript type check
node scripts/gen-active-redirects.mjs Regenerate legacy redirect map (committed to git; run manually when it changes)

Content linting

vale main develop api