--- name: mdx-pdf-format description: Use whenever creating, renaming, or editing any file under docs/ in this repo, or when asked to compile/build the book PDF. Enforces the exact .mdx frontmatter, ID scheme, and heading-depth rules required by the go-pretty-pdf tool (https://github.com/sazardev/go-pretty-pdf) so the whole book keeps compiling. --- # MDX format for go-pretty-pdf This repo's book (`docs/`) is built into a PDF with `go-pretty-pdf` (`go install github.com/sazardev/go-pretty-pdf/cmd/pretty-pdf@latest`). It only picks up `.mdx` files (recursive walk of the `source` directory in `go-pretty-pdf.yml`, currently `docs`) — `.md` files (like the `README.md` indexes) are ignored by the build and can stay plain Markdown. Every chapter file that should appear in the PDF **must** be `.mdx` and follow the rules below. Breaking any of these makes `pretty-pdf check`/`build` fail. ## Required frontmatter ```yaml --- id: "[X.Y.Z]" title: "Chapter Title" --- ``` - `id` is mandatory and must match the regex `^\[\d+\.\d+\.\d+\]$` exactly — brackets included, three dot-separated integers, quoted as a YAML string. Documents are ordered in the PDF **by this ID**, not by filename. - `title` is mandatory, quoted, and is what renders in the TOC/header. - Optional fields (`subtitle`, `tags`, `difficulty`, `status`, `completeness`, `depends_on`) may be added but are not required — this repo intentionally omits them to keep frontmatter minimal; don't add them unless the user asks. - No two files anywhere under `docs/` may share the same `id`. ### This repo's ID scheme `X` is fixed per top-level folder, `Y` is the file's existing two-digit numeric prefix, `Z` is reserved (always `0` today): | Folder | X | |---|---| | `docs/front-matter/` | 0 | | `docs/part1/` | 1 | | `docs/go-fundamentals/` | 2 | | `docs/part2/` | 3 | | `docs/advanced/` | 4 | | `docs/part3/` | 5 | | `docs/part-apis/` | 6 | | `docs/back-matter/` | 7 | Example: `docs/part2/05-tcp-in-depth-...mdx` → `id: "[3.5.0]"`. `docs/front-matter/` (X=0) and `docs/back-matter/` (X=7) hold non-chapter content — About the Author, closing remarks — that sorts before Part 1 and after Part APIs respectively, purely by virtue of their `id` being lower or higher than every numbered chapter. They aren't part of the numbered 1-N chapter sequence in `README.md`; list them there as unnumbered entries just before/after that numbered list instead. `docs/go-fundamentals/` (X=2) sits between Part 1 and Part 2 in reading order and covers the Go language itself (installation through testing, including a dedicated goroutines/channels chapter) — it does not follow Part 1's "theory only, no code" rule; it's the opposite, all hands-on Go. When adding a **new** chapter, keep this scheme: pick the `X` for its folder, and a `Y` that doesn't collide with an existing file in that folder (typically the next free number, matching the file's numeric filename prefix). ## Heading depth `go-pretty-pdf`'s linter caps heading depth at **h3** (`max_heading_depth: 3` in `go-pretty-pdf.yml`). Never use `####`/`#####` in chapter content — if a sub-point needs its own label, use a bold line (`**Request:**`) instead of a new heading level, or fold it into the surrounding `###` section. ## Components available in MDX body Three custom tags are transpiled to styled HTML — use them instead of plain blockquotes when the intent matches: - `...` — blue info panel - `...` — orange warning panel - `...` — green italic pull-quote `title` is optional on `DeepDive`/`Warning`. Only simple inline Markdown (code spans, `**bold**`) is rewritten inside these tags — don't nest complex Markdown (tables, nested lists, headings) inside them. **Hard rule, learned the hard way twice in this repo: write the content of every `DeepDive`/`Warning`/`Axiom` tag as ONE physical line in the source file**, no matter how long it looks in your editor — e.g. `All of this stays on one line, however long, until the closing tag.` written as a single line, not soft-wrapped like a normal paragraph. The renderer's HTML transpiler converts every literal newline inside these tags into a forced `
`, so wrapping the text across multiple source lines (as if it were ordinary prose) produces ugly, choppy, mid-sentence line breaks in the printed PDF. This has nothing to do with Markdown's own soft-wrap rules for regular paragraphs, which are unaffected — it only applies to text between these specific opening and closing tags. ## Code block line length Fenced code blocks render in a fixed-width box roughly 90 monospace characters wide (9pt font on the default theme). A line longer than that gets visually clipped at the right margin in the printed PDF instead of wrapping — this applies to Go, shell, YAML, or any other fenced language. Keep every line inside a code fence under about 85 characters after expanding tabs to 8 spaces (`str.expandtabs(8)`), wrapping long struct literals, function signatures, or calls across multiple lines, gofmt-style, rather than one long line. This is easy to miss since the source file looks fine in an editor — when in doubt, count. ## Variable substitution `{{var}}` in any `.mdx` file is replaced from the `vars:` map in `go-pretty-pdf.yml` before parsing. Only use this for values that are genuinely config-driven (versions, product name); don't introduce new `{{...}}` vars without adding them to `go-pretty-pdf.yml`. ## Keeping the index in sync `README.md` (root) and `docs/part-apis/README.md` link to every chapter. Whenever a chapter file is added, renamed, or removed, update the matching link in whichever of those two index files references it — including the `.mdx` extension in the link path. ## Exercise references must exist and build `docs/part2/*.mdx` chapters routinely link to a runnable file under `exercises/part2/`, e.g. `[Exercise: TCP Client](../../exercises/part2/05-tcp-client/main.go)`. This link is a promise the reader can click through and run — treat a chapter as incomplete, not just its exercise, until that promise holds: - **Before adding an `[Exercise: ...](../../exercises/part2/NN-name/main.go)` link, the target file must already exist.** Never add the link first and the file later — a repo-wide audit once found 18 such links pointing at directories that were never created, exactly because a chapter got written before its exercise did. - Extract the exercise verbatim from the chapter's own code block whenever the `.mdx` already contains a complete `package main` — the exercise should match what the chapter shows, not a rewritten variant. - If the chapter's code block is a fragment (a bare function, a type with no `main`), the exercise file still needs a real, runnable `main()` — write one that exercises the fragment, don't ship a non-compiling snippet just because that's what the prose showed inline. - Client/server pairs get **separate** exercise directories (matching `CLAUDE.md`'s existing convention, e.g. `06-udp-client` / `06-udp-server`) — never combine a client's and a server's own `func main()` into one file. - **Verify every new or edited exercise builds** before considering the work done. This repo's `exercises/part2/` directories have no `go.mod` (`CLAUDE.md`), so `go build`/`go vet` in default module mode fails with "cannot find main module" regardless of whether the code is correct — that error is expected here, not a sign of a broken exercise. Validate with GOPATH mode instead, from inside the exercise's own directory: ```sh cd exercises/part2/NN-name && GO111MODULE=off go vet . && GO111MODULE=off go build -o /tmp/out . ``` Also run `gofmt -l` over any new exercise file and fix anything it lists before treating the file as done. - Exercises under a third-party dependency (currently just the `gorilla/websocket` ones, numbered 12 and 13) keep their own `go.mod` — validate those with a plain `go build ./...` from inside that directory instead of the `GO111MODULE=off` form above. ## Build/validate commands ```sh pretty-pdf check # validate frontmatter, IDs, heading depth pretty-pdf build # render docs/ -> the PDF configured in go-pretty-pdf.yml ``` Run `pretty-pdf check` after adding or editing any `.mdx` file to catch format violations before they reach the build.