|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## What this repository is |
| 6 | + |
| 7 | +This is the source for [docs.github.com](https://docs.github.com) — a Next.js/Express application that renders Markdown content (with Liquid templating and versioning) into GitHub's product documentation site. The vast majority of contributions are content changes (Markdown in `content/`); application code (Node.js/Express/React/Next.js) is a smaller, separate concern. |
| 8 | + |
| 9 | +Note: GitHub's Docs team writes pre-production content in a private repo (`docs-internal`) that syncs with this public repo, so some workflows referenced in docs (e.g. Early Access) assume that private counterpart. |
| 10 | + |
| 11 | +## Commands |
| 12 | + |
| 13 | +```sh |
| 14 | +npm ci # install deps (run once per pulled branch) |
| 15 | +npm ci --include=optional # also installs test-only deps like puppeteer (needed to run full test suite locally) |
| 16 | +npm run build # next build — creates static assets (run once per pulled branch, and again if tests fail complaining about missing production build) |
| 17 | +npm start # dev server at localhost:4000, only 'en' and 'ja' enabled by default (NODE_ENV=development) |
| 18 | +npm run debug # dev server with --inspect for VS Code's Node debugger |
| 19 | + |
| 20 | +npm test # run full jest suite |
| 21 | +npm test -- <TEST_NAME> # run tests matching a filename/partial filename/path |
| 22 | +NODE_OPTIONS=--experimental-vm-modules npx jest tests/unit # run a specific test directory directly |
| 23 | +npm run test-watch # watch mode with coverage |
| 24 | + |
| 25 | +npm run lint # eslint on **/*.{js,mjs,ts,tsx} |
| 26 | +npm run tsc # typescript check (tsc --noEmit) |
| 27 | +npm run prettier # auto-format ts/tsx/js/mjs/scss/yml/yaml |
| 28 | +npm run prettier-check # check formatting without writing |
| 29 | +``` |
| 30 | + |
| 31 | +- Node version is pinned to **16.x** (see `.node-version` / `engines` in `package.json`). |
| 32 | +- To enable more languages locally, temporarily edit `ENABLED_LANGUAGES` in the `start` script in `package.json` (language codes defined in `lib/languages.js`). |
| 33 | +- Visit `localhost:4000/dev-toc` while the dev server is running to see the full site table of contents (internal-only route, not on production). |
| 34 | +- Git LFS is required (`pre-push`/`post-checkout`/`post-merge` husky hooks call into `git-lfs`). |
| 35 | +- `husky` pre-commit hook runs `script/prevent-translation-commits.js` (blocks commits that touch `translations/`, since those are Crowdin-managed) and `lint-staged` (eslint --fix + prettier on staged files). |
| 36 | +- `script/` follows the ["Scripts to Rule Them All"](https://githubengineering.com/scripts-to-rule-them-all/) pattern: `script/bootstrap` = install, `script/server` = start dev server, `script/test` = run tests. Prefer writing new scripts in JavaScript over Bash (repo is developed cross-platform, including Windows). |
| 37 | + |
| 38 | +## Architecture |
| 39 | + |
| 40 | +### Request flow |
| 41 | + |
| 42 | +`server.mjs` boots an Express app (`lib/app.js`) that composes middleware from `middleware/index.js` in a specific, order-sensitive pipeline: language detection → request `context` building → redirects → `find-page` (resolves URL to a content file) → Next.js page rendering. Two middleware subdirectories matter: |
| 43 | +- `middleware/contextualizers/` — populate `req.context` (the object templates/pages render from). |
| 44 | +- `middleware/redirects/` — handle the various redirect mechanisms (external sites, language-code redirects, `redirect_from` frontmatter, etc). |
| 45 | + |
| 46 | +Pages live under `pages/[versionId]/...` using Next.js file-system routing; most product content is served through the catch-all `pages/[versionId]/[productId]/[...restPage].tsx`, with a few products (REST, GraphQL, Admin release notes) having dedicated page files because they need custom rendering. |
| 47 | + |
| 48 | +### Content model (the part contributors touch most) |
| 49 | + |
| 50 | +- **`content/`** — all English Markdown source, organized by product/category. Every Markdown file has YAML frontmatter validated against a schema in `lib/frontmatter.js` (see `contributing/content-markup-reference.md` for the full field reference). Key frontmatter: |
| 51 | + - `versions` (required on every page) — which products/releases (`fpt`, `ghes`, `ghae`, etc., see `lib/all-versions.js`) the page applies to. Drives both routing and in-page Liquid conditionals. |
| 52 | + - `redirect_from` — old URLs that should redirect to this page. |
| 53 | + - `children` (required on `index.md`) — the site only knows about pages listed in some ancestor's `children`; anything not reachable via `children` 404s. `childGroups` is additionally required on the homepage `index.md`. |
| 54 | + - Filenames must be a kebab-case match of `title` (enforced by tests) unless `allowTitleToDifferFromFilename: true` is set. |
| 55 | +- **`data/`** — YAML/Markdown exposed to templates via the `{% data %}` Liquid tag. Subdirectories: `reusables/` (long reusable text blocks, one per Markdown file), `variables/` (short reusable strings), `features/` (feature-based versioning), `glossaries/`, `learning-tracks/`, `graphql/` (schema synced from `github/github`), `webhooks/` (payload JSON), `release-notes/`. Most YAML/Markdown under `data/` and `content/` is translated via Crowdin by default. |
| 56 | +- **`includes/`** — Liquid include templates (`.html`), notably `includes/liquid-tags/` for custom tags. |
| 57 | +- **Rendering pipeline** — `lib/render-content/` turns Markdown + Liquid + context into HTML, built on `remark`/`unified` with custom plugins in `lib/render-content/plugins/` (link rewriting per language/version, asset path rewriting, code block headers, etc). |
| 58 | +- **Versioning happens at two layers**: frontmatter `versions` (page-level availability) and inline Liquid conditionals (`{% ifversion %}` etc., can appear in `content`, `data`, and `includes`) for version-specific content within a single page. See `contributing/liquid-helpers.md` and `contributing/permalinks.md`. |
| 59 | +- **Custom Markdown extensions**: callout tags (`{% note %}`/`{% warning %}`), OS tags (`{% mac %}`/`{% windows %}`/`{% linux %}`), tool tags (`{% webui %}`/`{% cli %}`/`{% desktop %}`/`{% curl %}`/`{% codespaces %}`/`{% vscode %}`/`{% importer_cli %}`/`{% graphql %}`), Octicons (`{% octicon "name" %}`) — see `contributing/content-markup-reference.md`. |
| 60 | +- **Local links** must start with a product ID (e.g. `/actions`); image paths must start with `/assets`. These get rewritten server-side per language/version (`lib/render-content/plugins/rewrite-local-links.js`). To stop a link from being enterprise-ified, use raw HTML with `class="dotcom-only"`. |
| 61 | + |
| 62 | +### Translations |
| 63 | + |
| 64 | +`translations/<lang>/` mirrors `content/` and `data/` per locale (currently `es-ES`, `ja-JP`, `pt-BR`, `zh-CN`), synced via Crowdin. **Never hand-edit files under `translations/`** — the pre-commit hook (`script/prevent-translation-commits.js`) blocks this by default; it's only overridable via `ALLOW_TRANSLATION_COMMITS` for the sync tooling itself. |
| 65 | + |
| 66 | +### REST/GraphQL/webhook docs |
| 67 | + |
| 68 | +Reference docs for REST, GraphQL, and webhooks are largely generated/synced rather than hand-authored: |
| 69 | +- REST: `script/rest/update-files.js` pulls OpenAPI schemas from `github/github`, dereferences, and decorates them; served via `pages/[versionId]/rest/`. |
| 70 | +- GraphQL: `script/graphql/update-files.js` keeps `data/graphql/` in sync with `github/github`'s schema. |
| 71 | +- Webhooks: JSON payloads live in `data/webhooks/`. |
| 72 | +Avoid hand-editing generated output in these areas; change the source script/schema instead. |
| 73 | + |
| 74 | +### Components |
| 75 | + |
| 76 | +`components/` holds React components rendered via Next.js (server or client). `pages/` is the entry point — file-system routing matches a URL to a page component, which composes components from `components/`. This area is explicitly noted as a work in progress in `components/README.md`, so expect some inconsistency. |
| 77 | + |
| 78 | +## Testing conventions |
| 79 | + |
| 80 | +- Tests use Jest and live under `tests/` (`tests/unit`, `tests/content`, `tests/rendering`, `tests/routing`, `tests/graphql`, `tests/javascripts`, `tests/browser`, `tests/linting`, `tests/meta`, `tests/translations`). |
| 81 | +- `tests/linting/lint-files.js` is the content linter — validates frontmatter schema, Markdown structure, filename/title consistency, link/reusable/variable usage, etc. across every content file. This is usually the relevant test when a content change fails CI. |
| 82 | +- Content-focused tests are often slow given the size of `content/`; scope test runs with `npm test -- <TEST_NAME>` rather than always running the full suite. |
| 83 | + |
| 84 | +## Repo-specific gotchas |
| 85 | + |
| 86 | +- Don't create new abstractions/refactors for docs content changes — content PRs should stay scoped to the Markdown/data being changed. |
| 87 | +- `main` is protected; `script/prevent-pushes-to-main.js` (pre-push hook) blocks direct pushes to `main`. |
| 88 | +- When adding a new article, the filename must kebab-case-match the `title` frontmatter, or a test will fail. |
| 89 | +- Windows contributors: use `\r?\n` instead of `\n` in regexes, prefer `path.posix`/the `slash` module for forward-slash paths, and prefer JS scripts over Bash — see the Windows section of `CONTRIBUTING.md`. |
| 90 | + |
| 91 | +## Subdirectory READMEs |
| 92 | + |
| 93 | +Many directories have their own README with more detail; consult them before making non-trivial changes in that area: `content/README.md`, `content/graphql/README.md`, `content/rest/README.md`, `contributing/README.md`, `data/README.md`, `data/reusables/README.md`, `data/variables/README.md`, `includes/README.md`, `includes/liquid-tags/README.md`, `components/README.md`, `lib/liquid-tags/README.md`, `lib/render-content/README.md`, `middleware/README.md`, `script/README.md`, `stylesheets/README.md`, `tests/README.md`. |
0 commit comments