# Building and viewing the docs The Breeze documentation is one [VitePress](https://vitepress.dev) site: hand-written guides plus an API reference that [TypeDoc](https://typedoc.org) generates from the TypeScript source. Both live in `docs/` and share one navigation and one search box. The site is published to GitHub Pages at https://breeze.github.io/breeze-client-v3/, by hand: see [Publishing](#publishing). --- ## TL;DR ```bash npm install # once npm run docs:dev # then open http://localhost:5173/ and click "API" npm run docs:publish # build the site and push it to GitHub Pages ``` --- ## Publishing ```bash npm run docs:publish # build, commit to the gh-pages branch, push npm run docs:publish -- --no-push # build and commit; push later with: git push origin gh-pages npm run docs:publish -- --force # publish again from a commit that is already published ``` The site is whatever was last published, not what is on `master`: nothing publishes it automatically. So publish after pushing a change to the docs, or to the doc comments in `src/`. `scripts/publish-docs.mjs`: 1. **Refuses uncommitted changes**, so the published site always matches a commit. Commit or stash first. 2. **Builds** the site with `npm run docs:build`, setting `DOCS_BASE=/breeze-client-v3/`, because Pages serves this repo from that path rather than from `/`. A dead link or a stale anchor fails the build and stops the publish. 3. **Commits the built site to `gh-pages`**, in a temporary worktree, so the branch you are on and your working copy are never touched. `gh-pages` holds only the built site. Its commits are named after the source commit: `Publish docs from 51a0000`. 4. **Pushes `gh-pages`**, unless you passed `--no-push`. Pages updates a minute or two later. If `gh-pages` was already published from the commit you are on, it does nothing, or only pushes a publish that `--no-push` left behind. It decides that by commit, not by comparing files: VitePress writes its search index in the order pages finish rendering, so two builds of the same source are never byte-identical. ### One-time setup - The repository must be **public**. Pages on a private repository needs a paid plan. - After the first publish, set **Settings → Pages → Source** to *Deploy from a branch*, branch `gh-pages`, folder `/ (root)`. The branch is not in that list until it has been pushed once. ### Moving to GitHub Actions later Keep the build (`DOCS_BASE=/breeze-client-v3/ npm run docs:build`), upload `docs/.vitepress/dist` with `actions/upload-pages-artifact`, deploy it with `actions/deploy-pages`, and set the Pages source to *GitHub Actions*. Check out with `fetch-depth: 0`: the "last updated" dates come from git history. `gh-pages` is then no longer needed. --- ## The commands | command | what it does | where to look | |---|---|---| | `npm run docs:dev` | generates the API reference, then serves the site with hot reload | http://localhost:5173/ | | `npm run docs:build` | generates the API reference, then builds the static site | `docs/.vitepress/dist/` | | `npm run docs:preview` | serves the last `docs:build` output, exactly as it would be published | http://localhost:4173/ | | `npm run docs:api` | regenerates only the API reference markdown | `docs/api/` | | `npm run docs:publish` | builds the site for GitHub Pages and pushes it to `gh-pages` — see [Publishing](#publishing) | https://breeze.github.io/breeze-client-v3/ | The API reference is at `/api/` on either server — for example http://localhost:5173/api/classes/EntityManager. VitePress prints the actual URL when it starts; if the port is taken it picks the next free one. `docs:dev` and `docs:build` always run TypeDoc first, so the reference matches the source you have checked out. `docs:dev` then picks up edits to the **guide pages** as you save them. Edits to **doc comments in `src/`** do not show up until you run `npm run docs:api` again (or restart `docs:dev`). `docs:preview` does not rebuild. Run `docs:build` first, or you are looking at an old build. Before you commit, run `npm run docs:build`: it is the check that the site still builds, with no dead links (see [Checks](#checks)). --- ## How it fits together ``` docs/ .vitepress/config.mts site config: nav, sidebars, search index.md home page guide/ query/ metadata/ server/ hand-written pages api/ GENERATED by TypeDoc - do not edit, not committed typedoc.config.mjs what TypeDoc documents, and how src/breeze.ts the entry point: the API reference is what this exports ``` - **The API reference** is whatever `src/breeze.ts` exports, rendered from the doc comments in `src/`. TypeDoc writes markdown into `docs/api/`, plus `docs/api/typedoc-sidebar.json`, which `config.mts` imports as the API sidebar. That is why the site cannot start until TypeDoc has run once. - **The guides** are plain markdown under `docs/guide`, `docs/query`, `docs/metadata` and `docs/server`. Their sidebars are listed by hand in `docs/.vitepress/config.mts`. - Generated output — `docs/api/`, `docs/.vitepress/dist/` and `docs/.vitepress/cache/` — is gitignored. --- ## Writing doc comments The API pages are only as good as the comments in `src/`. TypeDoc reads standard TSDoc tags (`@param`, `@returns`, `@example`, `@deprecated`, `@see`, `@throws`, `@defaultValue`) and these Breeze ones, declared in `typedoc.config.mjs`: | tag | meaning | |---|---| | `@adapter` | names the adapter interface a class implements | | `@eventArgs` | documents the argument type of a `BreezeEvent` | | `@chainable` | the method returns `this`, so calls can be chained | | `@summary`, `@event`, `@property` | as in TSDoc | Two rules that have bitten before: - **`@param` names must match the real parameter names.** A rest parameter (`...args`) is documented as `args`. TypeDoc warns about every mismatch. - **`@hidden` and `@internal` are not the same thing.** - `@hidden` only keeps a declaration out of the docs. - `@internal` also removes it from the published `.d.ts`, because the build uses `stripInternal`. Never put `@internal` on anything `src/breeze.ts` exports, or on anything an exported signature needs: the package's type declarations then refer to something that is not there, and fail to compile for users. Use `@hidden` for those. Types that public signatures mention but that are deliberately not exported are listed in `intentionallyNotExported` in `typedoc.config.mjs`. Adding a name there, or exporting it from `breeze.ts` instead, is an API decision — not just a way to quieten a warning. --- ## Writing guide pages To add a page, create the markdown file under the right folder and add it to that section's `sidebar` in `docs/.vitepress/config.mts`. Link to other pages by site path, with no `.md`: `/guide/saving-changes`, `/query/predicates`, and to the API as `/api/classes/EntityManager`. VitePress specifics: - **Never write `{{` outside a fenced code block.** VitePress pages are Vue templates, so `{{ ... }}` in prose — or in inline code — is treated as an expression, and the build breaks. Inside fenced code blocks it is fine. - **A link to a page that does not exist fails the build** (`ignoreDeadLinks: false`). A link to an `#anchor` that does not exist does **not** — VitePress does not check those, which is what `npm run docs:check-anchors` is for. It runs as the last step of `docs:build`. - Callouts use `::: tip`, `::: warning` and `::: danger` blocks. Anything a 2.x user would trip over belongs in [Migrating from 2.x](docs/guide/migrating-from-2x.md) and UPGRADE.md, not in a callout on the page: the `::: tip Changed in 3.0` blocks that used to be scattered through the guides were consolidated there. --- ## Checks `npm run docs:build` must exit cleanly. It fails on: - a link to a page that does not exist, - a link to an `#anchor` that does not exist (`scripts/check-doc-anchors.mjs`, which checks every link against the ids the built HTML actually emitted), - a `{{` that Vue could not compile, or - an entry in the API reference with no description (`scripts/typedoc-require-docs.mjs`). Every export, member and constructor needs a doc comment that says what it is for, and so does each field of a return or property type written as an object literal, since the reference gives those a heading too. Its error lists each one with its file and line. This runs in `docs:api`, so `docs:dev` fails on it as well. Close doc comments with `*/`, not `**/`: TypeDoc keeps the extra `*`, which renders as a stray `*` after the text, or as an empty bullet when it is on a line of its own. TypeDoc should also report **0 warnings**. It prints them during `docs:api`, `docs:dev` and `docs:build`; the usual causes are a stale `@param` name, an unknown tag, a public signature that mentions a type which is neither exported nor listed in `intentionallyNotExported`, or a newly exported symbol that `scripts/typedoc-categories.mjs` has no category for. TypeDoc reads **one** entry point, `scripts/docs-entry.ts`, which re-exports `src/breeze.ts` plus the two optional mixins. Its header says why that rather than three entry points; the short version is that several entry points would move every page under `/api/breeze/` and would stop the category plugin working. A new public symbol appears in the reference by itself, and the plugin warns until it has been placed in a category. The categories come in two tiers, listed in `scripts/api-tiers.mjs`. The first is the API itself - what an application constructs or calls. The second, "Supporting types", is what only describes the shape of what the API takes and returns: results, errors, event arguments, config objects, typed paths. The sidebar collapses the second tier into one group at the bottom. When you place a new symbol, ask whether anyone would come to its page other than by clicking it in a signature; if not, it belongs in the second tier. The introduction on the index page is the `@packageDocumentation` comment at the top of `scripts/docs-entry.ts`. Note that TypeScript, and so TypeDoc, reads only the **last** doc comment before a declaration. Two stacked `/** ... */` blocks mean the first one's tags are silently discarded — `@hidden @internal`, which decides whether a member reaches the published `.d.ts` and the reference, and `@deprecated`, which decides whether an editor warns anybody. It reads as two comments about the same thing and has happened more than twenty times here, so `side-effects.spec.ts` now fails on it. A comment that is not about the declaration below it - a note on the design, say - belongs in a plain `/* ... */`. --- ## Adding an optional extension Save queuing, entity graphs and RxJS are opt-in: each is a subpath of its own that `src/breeze.ts` does not re-export, so an application that does not import it pays nothing. A new one touches these, and the tests fail on the first three if any is missed: 1. **`package.json` `exports`** — the subpath. If it needs a package, make it a devDependency here and an *optional* peer dependency (`peerDependenciesMeta`), never a dependency: npm 7 and later install a plain peer dependency for everyone. See `breeze-client/rxjs`. 2. **`docs/guide/extensions.md`** — a row in *At a glance* and a section of its own. A large one can have its own page, linked from its section, as RxJS does. 3. **`docs/guide/configuration.md`** — a row in the table of everything importable. 4. **`docs/.vitepress/config.mts`** — a child of *Optional extensions* under *Advanced*. 5. **`scripts/docs-entry.ts`** — `export *` it, so the API reference covers it. 6. **`scripts/typedoc-categories.mjs`** — its exports under `'Optional extensions'`. The plugin warns until it has been placed. If importing the module changes anything by itself, as the entity-graph mixin does, add it to `sideEffects` in `package.json` too; `side-effects.spec.ts` checks that list. --- ## Troubleshooting **The site fails to start with an error about `typedoc-sidebar.json`** You ran `vitepress dev docs` (or `build`) directly, before TypeDoc had generated the API reference. Use the `npm run docs:*` scripts, or run `npm run docs:api` once first. **My doc-comment change does not show up** `docs:dev` generates the API reference only when it starts. Run `npm run docs:api` in a second terminal, or restart `docs:dev`. **The build fails on a dead link after renaming a page** Fix every link to the old path, and the page's entry in `docs/.vitepress/config.mts`. **An API page is missing a class or function** The reference documents only what `src/breeze.ts` exports. Check it is exported there, and that its declaration is not tagged `@hidden` or `@internal`. **Port 5173 (or 4173) is already in use** VitePress moves to the next free port and prints it. Use the URL it prints. --- ## Not set up yet - **Automatic publishing.** Publishing is by hand, with `npm run docs:publish`; see [Moving to GitHub Actions later](#moving-to-github-actions-later). - **The .NET server docs** are a separate DocFX site in breeze-server-v3 — guides plus the .NET API reference — published at https://breeze.github.io/breeze-server-v3/ by `scripts\publish-docs.ps1` there, the same way this site is published here. Its [DOCS.md](https://github.com/Breeze/breeze-server-v3/blob/master/DOCS.md) covers building and publishing it. The nav's *Server* menu links to it, and to [docs/server/dotnet.md](docs/server/dotnet.md) here, which says what a .NET server gives this client. The two sites are separate: neither build knows about the other, so a link from here to a server page is an ordinary external URL and nothing checks it.