# 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

This is a pnpm workspace. One directory holds everything published to npm. The rest proves that
the plugin works and keeps it working.

- packages/
  - starlight-pydocs/ the published package, the only thing shipped to npm
    - lib/ framework-free code (no Astro imports), unit-tested here
    - components/ the `.astro` components that render the API pages
    - tests/ the Vitest suite
- docs/ this Starlight site, which uses the plugin and runs the Playwright suite
- examples/
  - vanilla/ a plain Astro site that shows the plugin works without Starlight
- fixtures/ Python packages plus their checked-in `griffe dump` JSON
- 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

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`](https://docs.astral.sh/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`.

1. Clone the repository and install the workspace:

   ```sh
   git clone https://github.com/ewels/starlight-pydocs
   cd starlight-pydocs
   pnpm install
   ```

2. Install [prek](https://prek.j178.dev). 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:

   ```sh
   pipx install prek
   prek install
   ```

   Skip this step if you would rather run the checks by hand.

3. Start this documentation site:

   ```sh frame="none"
   pnpm dev
   ```

   Open `http://localhost:4321/starlight-pydocs`. The site is served under its base path, so
   plain `localhost:4321` returns a 404. The first run extracts the fixture packages with griffe
   and caches the result in `node_modules/.astro`. Later starts are quick.

## 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:

```sh frame="none"
pnpm --filter starlight-pydocs-docs exec playwright install chromium
```

`pnpm 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

`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:

```sh
pnpm gen:dumps       # needs uv on PATH; rewrites fixtures/*/dump.json
pnpm gen:inventory   # only when the fixture Sphinx inventory needs new names
```

A 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

`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 imports `astro` or `@astrojs/starlight`, and components never import
  `@astrojs/starlight`. Starlight glue lives only in `index.ts`, `libs/starlight.ts`,
  `middleware.ts` and `routes/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

Notable changes go under the **Unreleased** heading at the top of
[`CHANGELOG.md`](https://github.com/ewels/starlight-pydocs/blob/main/CHANGELOG.md), in the same
pull request as the change. The [Changelog page](/starlight-pydocs/changelog/) is generated from
that file, so there is nothing to keep in sync by hand.

To cut a release:

1. Move the **Unreleased** entries into a new `## **Version X.Y.Z** (YYYY-MM-DD)` section. Bump
   `version` in `packages/starlight-pydocs/package.json` to match.

2. Commit and push to `main`.

3. Publish a [GitHub release](https://github.com/ewels/starlight-pydocs/releases/new) with a tag
   matching the version, for example `v1.0.0`.

Publishing the release triggers the
[release workflow](https://github.com/ewels/starlight-pydocs/blob/main/.github/workflows/release.yml).
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.