PEP 517 build backend + Sphinx extension that orchestrates Vite via pnpm
Project description
sphinx-vite-builder
PEP 517 build backend and Sphinx extension that transparently orchestrates Vite builds via pnpm for Sphinx-theme packages whose static assets (CSS / JS) are produced by a JavaScript toolchain.
What it solves
A common pattern for modern Sphinx themes is a Python package whose
theme/<name>/static/ directory ships built CSS and JS that were
produced by a JS build tool (Vite, webpack, …). The build artefacts are
gitignored — they're reproducibly built, not source code. But that
creates two friction points:
- Editable installs and source-tree builds crash with confusing
errors when the static dir is empty (e.g. hatchling's
Forced include not found). - CI workflows must duplicate
pnpm install + vite buildsetup steps in every job that touches the package.
sphinx-vite-builder owns the Vite invocation end-to-end — exactly the
way maturin owns Cargo for
Rust+Python packages, or
sphinx-theme-builder
owns webpack for older Sphinx themes.
The contract
Sources should check for node, pnpm, etc and error if it's not good, then build. Wheels should have the build files baked in and not need node and pnpm at all.
This is the central invariant of the package. The two install paths behave asymmetrically by design:
Wheel installs — zero toolchain required
A user running pip install <package> from PyPI gets a wheel that
already contains the vite-built static/ tree, populated by this
backend at release time. The PEP 517 chain doesn't run on the
consumer side. No backend invocation. No pnpm. No Node. The end
user sees Python and only Python.
No pnpm, no Node — just Python:
$ pip install gp-furo-theme
Source builds — fail loud, fail informatively
A contributor (or downstream packager building from source) goes
through the PEP 517 chain. The backend runs pnpm exec vite build
to produce static/, and that requires pnpm + Node on PATH. If the
toolchain is missing, the backend raises a typed exception with a
multi-line, copy-pasteable hint:
sphinx-vite-builder: cannot bootstrap the vite toolchain.
`pnpm` is not on PATH. Install it via one of:
corepack enable # Node 16.10+ ships corepack
curl -fsSL https://get.pnpm.io/install.sh | sh -
See https://pnpm.io/installation
…
Detected CI provider: GitHub Actions. Add the following to your pipeline
config (before the Python build step that triggers this backend):
- uses: pnpm/action-setup@v6
with:
version: 10
- uses: actions/setup-node@v6
with:
node-version: 22
cache: pnpm
The error includes the resolved vite-root path, the platform-specific
CI setup recipe (GitHub Actions, CircleCI, Azure Pipelines, GitLab CI,
or generic), and the SPHINX_VITE_BUILDER_SKIP=1 escape hatch for
environments that genuinely don't need vite to run.
The web/-absent short-circuit (sdist install bridge)
A user running pip install <pkg>.tar.gz from an sdist runs the
PEP 517 chain too — but the sdist excludes web/ (the Vite source
tree). The backend detects the absence, short-circuits cleanly, and
hatchling packs the pre-baked static/ (carried in the sdist via
[tool.hatch.build] artifacts) into the wheel. Sdist installs
need no toolchain either.
The asymmetry is the whole product: the same backend is strict
(running and failing loudly) when there's a web/ to act on, and
silent (skipping cleanly) when there's no web/ to begin with. The
two shapes match the two consumer worlds.
Two heads, one subprocess core
PEP 517 build backend
Drop-in replacement for hatchling.build. Runs pnpm exec vite build
before delegating wheel/sdist construction to hatchling.
# packages/your-theme/pyproject.toml
[build-system]
requires = ["hatchling>=1.0", "sphinx-vite-builder"]
build-backend = "sphinx_vite_builder.build"
[tool.hatch.build.targets.sdist]
exclude = ["web/"] # so the sdist→wheel chain hits the short-circuit
[tool.hatch.build]
artifacts = ["src/<your-theme>/theme/<theme-name>/static/"]
Sphinx extension (Phase 1: placeholder)
The extension entry point is currently a placeholder registered in
conf.py to prevent import errors. Full lifecycle integration —
running Vite before the docs build and spawning a watched Vite
process during sphinx-autobuild — lands in a follow-up release.
For now, the PEP 517 backend handles all Vite orchestration during source builds and wheel generation; that path is fully implemented and tested.
# docs/conf.py
extensions = ["sphinx_vite_builder"]
Fast-fail diagnostics — error type reference
| Error | When | Hint surface |
|---|---|---|
PnpmMissingError |
pnpm not on PATH during a source build |
corepack enable, pnpm.io/installation, per-CI YAML recipe, SPHINX_VITE_BUILDER_SKIP=1 |
NodeModulesInstallError |
pnpm install exited non-zero |
cd <vite-root> && pnpm install --frozen-lockfile rerun command, captured stderr |
ViteFailedError |
pnpm exec vite build exited non-zero |
invocation context (cwd, exit code), captured stderr |
All three inherit from SphinxViteBuilderError, so consumers can
except SphinxViteBuilderError for a single catch surface.
CI detection
The PnpmMissingError hint is self-healing when the backend
detects a CI environment. Detection precedence (most-specific wins):
| CI provider | Env var | Recipe shape |
|---|---|---|
| GitHub Actions | GITHUB_ACTIONS=true |
pnpm/action-setup@v6 + actions/setup-node@v6 |
| CircleCI | CIRCLECI=true |
corepack enable && corepack prepare pnpm@latest-10 --activate step |
| Azure Pipelines | TF_BUILD=True |
NodeTool@0 + corepack script |
| GitLab CI | GITLAB_CI=true |
before_script corepack invocations |
| Generic | CI=true |
"Use your CI's package-manager setup mechanism" |
Source: each provider's own canonical detection variable per the pnpm Continuous Integration docs.
License
MIT — see LICENSE.
Agent / contributor guidance
See AGENTS.md for the design contract, architecture
map, and conventions agents and contributors should follow when
making changes. (CLAUDE.md is a passthrough to
AGENTS.md for Claude Code.)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sphinx_vite_builder-0.0.1a16.dev2.tar.gz.
File metadata
- Download URL: sphinx_vite_builder-0.0.1a16.dev2.tar.gz
- Upload date:
- Size: 20.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
95d2a8317eb6409af7b767e576ab5359db3089db9e26a2922fcd5c0346fcbf40
|
|
| MD5 |
9e46c6b0be88a003be59b7985be7f404
|
|
| BLAKE2b-256 |
502dbaeeb8699c0dcedd68356e0b4bbf925292ffd0b755c5966bdec5a5633f2e
|
Provenance
The following attestation bundles were made for sphinx_vite_builder-0.0.1a16.dev2.tar.gz:
Publisher:
release.yml on git-pull/gp-sphinx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sphinx_vite_builder-0.0.1a16.dev2.tar.gz -
Subject digest:
95d2a8317eb6409af7b767e576ab5359db3089db9e26a2922fcd5c0346fcbf40 - Sigstore transparency entry: 1435876388
- Sigstore integration time:
-
Permalink:
git-pull/gp-sphinx@581566a02ef420c42abd8dcce2a54d96e67314a2 -
Branch / Tag:
refs/tags/v0.0.1a16.dev2 - Owner: https://github.com/git-pull
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@581566a02ef420c42abd8dcce2a54d96e67314a2 -
Trigger Event:
push
-
Statement type:
File details
Details for the file sphinx_vite_builder-0.0.1a16.dev2-py3-none-any.whl.
File metadata
- Download URL: sphinx_vite_builder-0.0.1a16.dev2-py3-none-any.whl
- Upload date:
- Size: 19.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ef6dd7a053091c62cdb9dac2a14975efa4ba30c4c0372ba0b206120371d85bff
|
|
| MD5 |
cb40669658015e61a712e66c7e0a9ddb
|
|
| BLAKE2b-256 |
0bb6249ee83de125d709a5916965240b3c91f51c903af4bdeef98c5da8d26b79
|
Provenance
The following attestation bundles were made for sphinx_vite_builder-0.0.1a16.dev2-py3-none-any.whl:
Publisher:
release.yml on git-pull/gp-sphinx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sphinx_vite_builder-0.0.1a16.dev2-py3-none-any.whl -
Subject digest:
ef6dd7a053091c62cdb9dac2a14975efa4ba30c4c0372ba0b206120371d85bff - Sigstore transparency entry: 1435878570
- Sigstore integration time:
-
Permalink:
git-pull/gp-sphinx@581566a02ef420c42abd8dcce2a54d96e67314a2 -
Branch / Tag:
refs/tags/v0.0.1a16.dev2 - Owner: https://github.com/git-pull
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@581566a02ef420c42abd8dcce2a54d96e67314a2 -
Trigger Event:
push
-
Statement type: