Contributing
Contributions are welcome. This page explains how to work on the plugin itself: how to get the repository running, which checks to run before you open a pull request, and where the parts live.
Repository layout
Section titled “Repository layout”This is a pnpm workspace. One directory holds everything published to npm. The rest proves that the plugin works and keeps it working.
Directorypackages/
Directorystarlight-pydocs/ the published package, the only thing shipped to npm
Directorylib/ framework-free code (no Astro imports), unit-tested here
- …
Directorycomponents/ the
.astrocomponents that render the API pages- …
Directorytests/ the Vitest suite
- …
Directorydocs/ this Starlight site, which uses the plugin and runs the Playwright suite
- …
Directoryexamples/
Directoryvanilla/ a plain Astro site that shows the plugin works without Starlight
- …
Directoryfixtures/ Python packages plus their checked-in
griffe dumpJSON- …
- prek.toml the formatting, linting and type-checking hooks
The package has no build step. It ships its .ts and .astro source, and the host Astro project
transpiles it. The dev server picks up edits inside packages/starlight-pydocs directly.
Get it running
Section titled “Get it running”You need Node 22.12 or later, and pnpm 10.33 (the version pinned in packageManager). You must use
pnpm: the workspace links starlight-pydocs into the docs site and the example with
workspace:*, and npm and yarn resolve this differently or not at all. Install pnpm with
npm install -g pnpm. Node v25 removed Corepack, so corepack enable no longer works on a
current Node.
You do not need Python to run the unit tests. The griffe dump JSON files under fixtures/ are
checked in, so the test suite never shells out. You do need uv for
two tasks: regenerating those dumps, and building this site, because the build extracts demopkg
and numpkg from their Python sources. If your PATH has neither uvx nor a python that can
import griffe, extraction fails and the build fails with it. Two package entries read a
pre-generated dump instead, so they need no Python at all: api/sphpkg and the second demopkg
entry at 1x/api/demopkg.
-
Clone the repository and install the workspace:
Terminal window git clone https://github.com/ewels/starlight-pydocscd starlight-pydocspnpm install -
Install prek. It is a separate tool, not an npm dependency. Let it write the git pre-commit hook, so every commit runs prettier, eslint and the type-checker:
Terminal window pipx install prekprek installSkip this step if you would rather run the checks by hand.
-
Start this documentation site:
pnpm devOpen
http://localhost:4321/starlight-pydocs. The site is served under its base path, so plainlocalhost:4321returns a 404. The first run extracts the fixture packages with griffe and caches the result innode_modules/.astro. Later starts are quick.
Checks
Section titled “Checks”| Task | Command |
|---|---|
| Unit tests (Vitest) | pnpm test |
| One test file | pnpm --filter starlight-pydocs exec vitest run tests/model.test.ts |
| Watch · coverage | pnpm --filter starlight-pydocs test:watch · … test:coverage |
| End-to-end tests (Playwright) | pnpm test:e2e |
| Type-check | pnpm typecheck |
| Lint · format | pnpm lint · pnpm format |
| Everything, as CI runs it | prek run --all-files |
| Regenerate the fixture dumps | pnpm gen:dumps |
| Regenerate the inventory | pnpm gen:inventory |
pnpm test:e2e builds and previews both sites (this one on port 4321 under base
/starlight-pydocs, the vanilla example on 4322), then runs the Playwright suite against them.
Every assertion runs against real generated output. It needs a browser binary the first time:
pnpm --filter starlight-pydocs-docs exec playwright install chromiumpnpm typecheck runs the typecheck script of every workspace package. It runs tsc --noEmit
over the framework-free code in packages/starlight-pydocs/lib, then astro check in this site
and in the vanilla example. Those astro check runs type-check the .astro components, not the
package’s own tsc script, because Astro compiles them where it consumes them.
Prettier does not format .mdx files. It reflows the Python signatures and directive examples in
these guides in ways that break them. Format MDX by hand. Starlight also does not support custom
heading IDs, so a heading takes whatever slug its text produces.
Changing a Python fixture
Section titled “Changing a Python fixture”fixtures/demopkg, fixtures/numpkg and fixtures/sphpkg are Python packages with a wide API
surface: three docstring styles, inheritance, overloads, __all__, pydantic models and
deprecations. Each has a checked-in griffe dump JSON beside it. The tests and this site read
that JSON, not the Python source.
Editing the Python source alone changes nothing. Regenerate the dumps and commit them in the same commit:
pnpm gen:dumps # needs uv on PATH; rewrites fixtures/*/dump.jsonpnpm gen:inventory # only when the fixture Sphinx inventory needs new namesA guarded test regenerates the dumps live when uv is available and compares them against the
committed ones. CI has uv installed, so this test catches any drift between the committed dumps
and the fixture sources.
Architecture rules
Section titled “Architecture rules”ARCHITECTURE.md records every architecture decision, the alternatives rejected and the griffe
behaviour notes. CLAUDE.md records the working conventions. Before you change the package, know
these two rules:
lib/never importsastroor@astrojs/starlight, and components never import@astrojs/starlight. Starlight glue lives only inindex.ts,libs/starlight.ts,middleware.tsandroutes/starlight.astro. Everything else must work in a plain Astro site.- The package registers no remark or rehype plugin. At
astro:config:done, it renders docstring prose through the host project’s configured markdown processor into a sidecar JSON. The components then consume that pre-rendered HTML.
Releases
Section titled “Releases”Notable changes go under the Unreleased heading at the top of
CHANGELOG.md, in the same
pull request as the change. The Changelog page is generated from
that file, so there is nothing to keep in sync by hand.
To cut a release:
-
Move the Unreleased entries into a new
## **Version X.Y.Z** (YYYY-MM-DD)section. Bumpversioninpackages/starlight-pydocs/package.jsonto match. -
Commit and push to
main. -
Publish a GitHub release with a tag matching the version, for example
v1.0.0.
Publishing the release triggers the release workflow. It publishes to npm through trusted publishing over OIDC, so there is no token to manage. It checks that the release tag matches the package version first. A mismatch fails the run instead of shipping the wrong version.
The first publish is the exception. You can only configure npm trusted publishing on a package
that already exists, so the first release goes out by hand: run npm login, then pnpm release.
Afterwards, add the GitHub repository as a trusted publisher in the package settings on
npmjs.com. Every later release then publishes itself.