sphinx-vite-builder 0.0.1a16__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,233 @@
1
+ # Node
2
+ node_modules/
3
+ *.tsbuildinfo
4
+ .vitest-cache/
5
+
6
+ # Byte-compiled / optimized / DLL files
7
+ __pycache__/
8
+ *.py[codz]
9
+ *$py.class
10
+
11
+ # C extensions
12
+ *.so
13
+
14
+ # Distribution / packaging
15
+ .Python
16
+ build/
17
+ develop-eggs/
18
+ dist/
19
+ downloads/
20
+ eggs/
21
+ .eggs/
22
+ lib/
23
+ lib64/
24
+ parts/
25
+ sdist/
26
+ var/
27
+ wheels/
28
+ share/python-wheels/
29
+ *.egg-info/
30
+ .installed.cfg
31
+ *.egg
32
+ MANIFEST
33
+
34
+ # PyInstaller
35
+ # Usually these files are written by a python script from a template
36
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
37
+ *.manifest
38
+ *.spec
39
+
40
+ # Installer logs
41
+ pip-log.txt
42
+ pip-delete-this-directory.txt
43
+
44
+ # Unit test / coverage reports
45
+ htmlcov/
46
+ .tox/
47
+ .nox/
48
+ .coverage
49
+ .coverage.*
50
+ .cache
51
+ nosetests.xml
52
+ coverage.xml
53
+ *.cover
54
+ *.py.cover
55
+ .hypothesis/
56
+ .pytest_cache/
57
+ cover/
58
+
59
+ # Translations
60
+ *.mo
61
+ *.pot
62
+
63
+ # Django stuff:
64
+ *.log
65
+ local_settings.py
66
+ db.sqlite3
67
+ db.sqlite3-journal
68
+
69
+ # Flask stuff:
70
+ instance/
71
+ .webassets-cache
72
+
73
+ # Scrapy stuff:
74
+ .scrapy
75
+
76
+ # Sphinx documentation
77
+ docs/_build/
78
+
79
+ # PyBuilder
80
+ .pybuilder/
81
+ target/
82
+
83
+ # Jupyter Notebook
84
+ .ipynb_checkpoints
85
+
86
+ # IPython
87
+ profile_default/
88
+ ipython_config.py
89
+
90
+ # pyenv
91
+ # For a library or package, you might want to ignore these files since the code is
92
+ # intended to run in multiple environments; otherwise, check them in:
93
+ # .python-version
94
+
95
+ # pipenv
96
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
97
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
98
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
99
+ # install all needed dependencies.
100
+ #Pipfile.lock
101
+
102
+ # UV
103
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
104
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
105
+ # commonly ignored for libraries.
106
+ #uv.lock
107
+
108
+ # poetry
109
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
110
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
111
+ # commonly ignored for libraries.
112
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
113
+ #poetry.lock
114
+ #poetry.toml
115
+
116
+ # pdm
117
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
118
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
119
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
120
+ #pdm.lock
121
+ #pdm.toml
122
+ .pdm-python
123
+ .pdm-build/
124
+
125
+ # pixi
126
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
127
+ #pixi.lock
128
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
129
+ # in the .venv directory. It is recommended not to include this directory in version control.
130
+ .pixi
131
+
132
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
133
+ __pypackages__/
134
+
135
+ # Celery stuff
136
+ celerybeat-schedule
137
+ celerybeat.pid
138
+
139
+ # SageMath parsed files
140
+ *.sage.py
141
+
142
+ # Environments
143
+ .env
144
+ .envrc
145
+ .venv
146
+ env/
147
+ venv/
148
+ ENV/
149
+ env.bak/
150
+ venv.bak/
151
+
152
+ # Spyder project settings
153
+ .spyderproject
154
+ .spyproject
155
+
156
+ # Rope project settings
157
+ .ropeproject
158
+
159
+ # mkdocs documentation
160
+ /site
161
+
162
+ # mypy
163
+ .mypy_cache/
164
+ .dmypy.json
165
+ dmypy.json
166
+
167
+ # Pyre type checker
168
+ .pyre/
169
+
170
+ # pytype static type analyzer
171
+ .pytype/
172
+
173
+ # Cython debug symbols
174
+ cython_debug/
175
+
176
+ # PyCharm
177
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
178
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
179
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
180
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
181
+ #.idea/
182
+
183
+ # Abstra
184
+ # Abstra is an AI-powered process automation framework.
185
+ # Ignore directories containing user credentials, local state, and settings.
186
+ # Learn more at https://abstra.io/docs
187
+ .abstra/
188
+
189
+ # Visual Studio Code
190
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
191
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
192
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
193
+ # you could uncomment the following to ignore the entire vscode folder
194
+ # .vscode/
195
+
196
+ # Ruff stuff:
197
+ .ruff_cache/
198
+
199
+ # PyPI configuration file
200
+ .pypirc
201
+
202
+ # Cursor
203
+ # Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
204
+ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
205
+ # refer to https://docs.cursor.com/context/ignore-files
206
+ .cursorignore
207
+ .cursorindexingignore
208
+
209
+ # Marimo
210
+ marimo/_static/
211
+ marimo/_lsp/
212
+ __marimo__/
213
+
214
+ # Generated by sphinx_fonts extension (downloaded at build time)
215
+ docs/_static/fonts/
216
+ docs/_static/css/fonts.css
217
+
218
+ # Claude Code
219
+ **/CLAUDE.local.md
220
+ **/CLAUDE.*.md
221
+ **/.claude/settings.local.json
222
+
223
+ # Playwright MCP
224
+ .playwright-mcp/
225
+
226
+ # Repo-local pytest mirror (do not track — validator-only)
227
+ out/
228
+
229
+ # Misc
230
+ .vim/
231
+ *.lprof
232
+ pip-wheel-metadata/
233
+ monkeytype.sqlite3
@@ -0,0 +1,208 @@
1
+ # AGENTS.md — `sphinx-vite-builder`
2
+
3
+ Guidance for AI agents (Claude Code, Cursor, Copilot, Codex, …) and
4
+ human contributors working on this package. Mirrors the higher-level
5
+ guidance at `gp-sphinx/CLAUDE.md`; `packages/sphinx-vite-builder/CLAUDE.md`
6
+ points here so Claude Code reads the same content as other agent runners.
7
+
8
+ ## What this package is
9
+
10
+ Two orthogonal entry points sharing one subprocess core:
11
+
12
+ 1. **PEP 517 build backend** at `sphinx_vite_builder.build`. Runs
13
+ `pnpm exec vite build` before delegating wheel/sdist construction
14
+ to `hatchling.build`. Consumer packages declare it via
15
+ `[build-system].build-backend = "sphinx_vite_builder.build"`.
16
+ 2. **Sphinx extension** at `sphinx_vite_builder:setup`. Hooks
17
+ `builder-inited` and `build-finished` so `sphinx-build` /
18
+ `sphinx-autobuild` automatically run vite — one-shot for prod, a
19
+ long-lived `vite build --watch` child process for autobuild — with
20
+ graceful teardown on signal / `atexit`.
21
+
22
+ Both heads consume the smart-subprocess core under
23
+ `sphinx_vite_builder._internal/`: `process.py` (`AsyncProcess` —
24
+ asyncio subprocess wrapper with POSIX session isolation,
25
+ SIGTERM-then-SIGKILL teardown, line-buffered stdout/stderr drainers,
26
+ captured stderr for error surfacing), `bus.py` (`AsyncioBus` — single
27
+ asyncio loop in a daemon thread for sync↔async bridging),
28
+ `vite.py` (orchestration: detect `web/`, check pnpm via `shutil.which`,
29
+ spawn install/build), and `errors.py` (`PnpmMissingError`,
30
+ `NodeModulesInstallError`, `ViteFailedError`).
31
+
32
+ **Phase 1 status:** The PEP 517 backend is fully implemented and
33
+ tested. The Sphinx extension `setup()` is a placeholder — it
34
+ registers cleanly in `conf.py` but doesn't yet hook the docs build
35
+ lifecycle. The full extension implementation (event handlers, vite
36
+ watch, teardown) lands in a follow-up release.
37
+
38
+ ## The design contract — keep this invariant
39
+
40
+ > **Sources should check for node, pnpm, etc and error if it's not
41
+ > good, then build. Wheels should have the build files baked in and
42
+ > not need node and pnpm at all.**
43
+
44
+ This is the central invariant. When you change anything in the
45
+ backend, vite orchestration, or release pipeline, ask: "does this
46
+ preserve the source-vs-wheel asymmetry?"
47
+
48
+ ### Source builds — fail loud, fail informatively
49
+
50
+ A consumer building from source (cloned tree, `[tool.uv.sources]` git
51
+ URL, `pip install <repo-path>`) goes through the PEP 517 chain. The
52
+ backend's `build_wheel` / `build_editable` / `build_sdist` hooks
53
+ **MUST** run vite, and vite **MUST** be available. If pnpm or Node is
54
+ missing:
55
+
56
+ - Raise the typed exception (`PnpmMissingError`,
57
+ `NodeModulesInstallError`, or `ViteFailedError`) — never a bare
58
+ `subprocess.CalledProcessError` or `FileNotFoundError`. The typed
59
+ exception lets consumers `except` cleanly via the
60
+ `SphinxViteBuilderError` base class.
61
+ - Each error's message MUST be a multi-line, copy-pasteable hint
62
+ formatted by `_format_*_hint()`. Include the canonical install
63
+ paths (`corepack enable`, `https://pnpm.io/installation`), the
64
+ resolved vite-root path, and the `SPHINX_VITE_BUILDER_SKIP=1`
65
+ escape-hatch line.
66
+ - Detect CI via `_detect_ci_provider()`. When CI is detected, append
67
+ the platform-specific YAML/config snippet from `_CI_SETUP_RECIPES`
68
+ so the consumer can copy-paste the fix into their pipeline.
69
+ Supported providers: GitHub Actions, CircleCI, Azure Pipelines,
70
+ GitLab CI, plus a generic fallback for `CI=true`.
71
+
72
+ ### Wheel installs — zero toolchain required
73
+
74
+ A consumer doing `pip install <package>==<version>` from PyPI gets a
75
+ wheel that **already contains** the vite-built `static/` tree
76
+ (populated by the backend at release time, packed via hatchling's
77
+ `artifacts` directive). The PEP 517 chain doesn't run. No backend
78
+ invocation. No `_run_vite_build()`. No `shutil.which("pnpm")`. The
79
+ end user sees Python and only Python.
80
+
81
+ ### Wheel-from-sdist — the bridge case
82
+
83
+ A consumer doing `pip install <pkg>.tar.gz` (or `--no-binary :all:`)
84
+ runs the wheel-from-sdist chain:
85
+
86
+ 1. pip/uv unpacks the sdist into a temp dir
87
+ 2. Calls our backend's `build_wheel` against the unpacked tree
88
+ 3. The backend's vite-orchestration sees no `web/` (excluded from
89
+ sdist via `[tool.hatch.build.targets.sdist] exclude = ["web/"]`)
90
+ and short-circuits cleanly — `_resolve_vite_root()` returns
91
+ `None`, vite is never invoked
92
+ 4. Hatchling packs the pre-baked `static/` (carried in the sdist via
93
+ `[tool.hatch.build] artifacts`) into the wheel
94
+
95
+ Net: **sdist install also requires zero toolchain**. The
96
+ `web/`-absent short-circuit is the load-bearing primitive.
97
+
98
+ ## QA permutations — keep them green
99
+
100
+ The install-path permutations every change must keep green:
101
+
102
+ | # | Path | Toolchain | Expected |
103
+ |---|---|---|---|
104
+ | 1 | wheel install from PyPI | none | succeeds; static present |
105
+ | 2 | sdist install from PyPI (`--no-binary :all:`) | none | succeeds; static present (backend short-circuits) |
106
+ | 3 | source build (`uv build` from cloned tree) | with pnpm + Node | succeeds; wheel contains static |
107
+ | 4 | source build (`uv build` from cloned tree) | none | fails with `PnpmMissingError` + CI-platform recipe |
108
+
109
+ When you add a new failure mode or a new short-circuit branch, add a
110
+ new row here AND a corresponding test in
111
+ `tests/test_sphinx_vite_builder_vite.py`.
112
+
113
+ ## Architecture map
114
+
115
+ ```
116
+ sphinx_vite_builder/
117
+ ├── __init__.py Sphinx extension entry: setup(app)
118
+ ├── build.py PEP 517/660 hooks (delegate to hatchling)
119
+ ├── py.typed
120
+ └── _internal/
121
+ ├── errors.py SphinxViteBuilderError + 3 subclasses
122
+ ├── process.py AsyncProcess (asyncio subprocess wrapper)
123
+ ├── bus.py AsyncioBus (sync↔async bridge)
124
+ └── vite.py run_vite_build() + CI detection + hint formatters
125
+ ```
126
+
127
+ Neither head calls the other; both consume `_internal/`. The PEP 517
128
+ hooks in `build.py` MUST stay 1:1 mirrors of `flit_core.buildapi` and
129
+ `hatchling.build` — every hook runs `run_vite_build()` then delegates.
130
+ Optional hooks (`get_requires_for_build_*`, `prepare_metadata_for_build_*`)
131
+ alias to hatchling by identity — vite has no influence on dependency
132
+ resolution or distribution metadata, so wrapping them would be wrong.
133
+
134
+ ## When you add a new public function
135
+
136
+ - Add doctests. Every public function MUST have working doctests
137
+ per the workspace convention. Use ELLIPSIS for variable output.
138
+ - Add NumPy-style docstrings: short summary, Parameters, Returns,
139
+ Raises, Examples.
140
+ - Add type annotations everywhere, including return types
141
+ (`-> None` on test functions). mypy runs strict mode.
142
+
143
+ ## When you add a new error path
144
+
145
+ - Add a new `*Error` subclass in `errors.py` if the failure has a
146
+ distinct semantic meaning. Inherit from `SphinxViteBuilderError`.
147
+ - Add a `_format_*_hint(...)` function in `vite.py` for the
148
+ copy-pasteable diagnostic. Wrap the raise:
149
+ `raise NewError(_format_new_error_hint(...))`.
150
+ - If the new path is reachable in CI, ensure the message includes
151
+ enough context to fix-from-the-message-itself. Add a CI-recipe
152
+ block via `_format_ci_recipe_block()` if relevant.
153
+ - Add tests for: positive case (path triggers correctly), each error
154
+ branch (specific exception, with key strings present in message),
155
+ inheritance from `SphinxViteBuilderError`.
156
+
157
+ ## When you change `release.yml` or the workspace
158
+
159
+ The workspace's `release.yml` MUST keep the pnpm + Node setup steps
160
+ that run before `uv build`, otherwise the wheels published to PyPI
161
+ would be empty of static (the prior broken-release pattern that
162
+ motivated this whole package). The required steps are:
163
+
164
+ ```yaml
165
+ - uses: pnpm/action-setup@v6
166
+ with:
167
+ version: 10
168
+ - uses: actions/setup-node@v6
169
+ with:
170
+ node-version: 22
171
+ cache: pnpm
172
+ ```
173
+
174
+ If you find yourself removing those, ask: "is the source-build path
175
+ still going to produce a populated `static/` in the wheel?" The
176
+ answer must be yes.
177
+
178
+ ## When in doubt
179
+
180
+ - Read the full plan at gp-sphinx issue #28.
181
+ - Look at how PR #29 wired everything together initially.
182
+ - Look at how libtmux-mcp PR #33 exercises both consumer paths in
183
+ CI — the WITH-wheels and WITHOUT-wheels permutations both have
184
+ green runs that are good reference points.
185
+ - Run the local QA matrix: clean venv install of the published
186
+ wheel, clean venv install of the published sdist (`--no-binary`),
187
+ clean clone + `uv build` with toolchain stripped (must fail
188
+ loudly), clean clone + `uv build` with toolchain present (must
189
+ succeed).
190
+
191
+ ## What NOT to do
192
+
193
+ - **Do not** add `web/` to the sdist. Excluding it is what makes the
194
+ wheel-from-sdist short-circuit work.
195
+ - **Do not** commit anything from `static/` to git. Build artefacts
196
+ are produced reproducibly; check-in is forbidden by workspace
197
+ policy.
198
+ - **Do not** silently swallow vite/pnpm subprocess failures. Every
199
+ non-zero exit goes through a typed exception with a
200
+ copy-pasteable hint.
201
+ - **Do not** auto-install pnpm via the backend (the maturin
202
+ `puccinialin`-style trick doesn't apply here — pnpm isn't
203
+ pip-installable). The contract is "pnpm is your responsibility,
204
+ here's how to install it on your platform".
205
+ - **Do not** change `SPHINX_VITE_BUILDER_SKIP=1` semantics without
206
+ thinking through the wheel-vs-source asymmetry. The escape hatch
207
+ is correct for wheel-only environments; using it during a real
208
+ source build silently produces broken artefacts.
@@ -0,0 +1,8 @@
1
+ # CLAUDE.md
2
+
3
+ Claude Code reads this file. Other agent runners (Cursor, Copilot,
4
+ Codex, …) read [`AGENTS.md`](AGENTS.md). The two files have identical
5
+ content via this passthrough — every guideline lives in `AGENTS.md`,
6
+ and edits go there.
7
+
8
+ → See [`AGENTS.md`](AGENTS.md).