doxtr-hugo 0.1.0__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.
- doxtr_hugo-0.1.0/LICENSE +21 -0
- doxtr_hugo-0.1.0/PKG-INFO +274 -0
- doxtr_hugo-0.1.0/README.md +240 -0
- doxtr_hugo-0.1.0/doxtr_hugo/__init__.py +83 -0
- doxtr_hugo-0.1.0/doxtr_hugo/_preview_cli.py +136 -0
- doxtr_hugo-0.1.0/doxtr_hugo/assets.py +268 -0
- doxtr_hugo-0.1.0/doxtr_hugo/builder.py +443 -0
- doxtr_hugo-0.1.0/doxtr_hugo/config_defaults.py +39 -0
- doxtr_hugo-0.1.0/doxtr_hugo/diagrams.py +934 -0
- doxtr_hugo-0.1.0/doxtr_hugo/frontmatter.py +258 -0
- doxtr_hugo-0.1.0/doxtr_hugo/image_policy.py +183 -0
- doxtr_hugo-0.1.0/doxtr_hugo/links.py +151 -0
- doxtr_hugo-0.1.0/doxtr_hugo/math_render.py +444 -0
- doxtr_hugo-0.1.0/doxtr_hugo/py.typed +0 -0
- doxtr_hugo-0.1.0/doxtr_hugo/render_pass.py +457 -0
- doxtr_hugo-0.1.0/doxtr_hugo/site_skeleton.py +311 -0
- doxtr_hugo-0.1.0/doxtr_hugo/slug.py +336 -0
- doxtr_hugo-0.1.0/doxtr_hugo/templates/layouts/_default/_markup/render-image.html +37 -0
- doxtr_hugo-0.1.0/doxtr_hugo/templates/layouts/_default/baseof.html +33 -0
- doxtr_hugo-0.1.0/doxtr_hugo/templates/layouts/_default/list.html +13 -0
- doxtr_hugo-0.1.0/doxtr_hugo/templates/layouts/_default/single.html +6 -0
- doxtr_hugo-0.1.0/doxtr_hugo/templates/layouts/shortcodes/admonition.html +13 -0
- doxtr_hugo-0.1.0/doxtr_hugo/templates/layouts/shortcodes/figure.html +11 -0
- doxtr_hugo-0.1.0/doxtr_hugo/theme_support.py +562 -0
- doxtr_hugo-0.1.0/doxtr_hugo/toctree.py +237 -0
- doxtr_hugo-0.1.0/doxtr_hugo/translator.py +1265 -0
- doxtr_hugo-0.1.0/doxtr_hugo.egg-info/PKG-INFO +274 -0
- doxtr_hugo-0.1.0/doxtr_hugo.egg-info/SOURCES.txt +46 -0
- doxtr_hugo-0.1.0/doxtr_hugo.egg-info/dependency_links.txt +1 -0
- doxtr_hugo-0.1.0/doxtr_hugo.egg-info/requires.txt +19 -0
- doxtr_hugo-0.1.0/doxtr_hugo.egg-info/top_level.txt +1 -0
- doxtr_hugo-0.1.0/pyproject.toml +83 -0
- doxtr_hugo-0.1.0/setup.cfg +4 -0
- doxtr_hugo-0.1.0/tests/test_assets.py +244 -0
- doxtr_hugo-0.1.0/tests/test_diagrams.py +748 -0
- doxtr_hugo-0.1.0/tests/test_frontmatter.py +160 -0
- doxtr_hugo-0.1.0/tests/test_harness_verify_formats.py +212 -0
- doxtr_hugo-0.1.0/tests/test_image_policy.py +166 -0
- doxtr_hugo-0.1.0/tests/test_links.py +459 -0
- doxtr_hugo-0.1.0/tests/test_math_render.py +394 -0
- doxtr_hugo-0.1.0/tests/test_preview_cli.py +105 -0
- doxtr_hugo-0.1.0/tests/test_render_pass.py +314 -0
- doxtr_hugo-0.1.0/tests/test_scaffold.py +123 -0
- doxtr_hugo-0.1.0/tests/test_site_skeleton.py +216 -0
- doxtr_hugo-0.1.0/tests/test_slug.py +292 -0
- doxtr_hugo-0.1.0/tests/test_theme_support.py +246 -0
- doxtr_hugo-0.1.0/tests/test_toctree.py +220 -0
- doxtr_hugo-0.1.0/tests/test_translator.py +947 -0
doxtr_hugo-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Doxtr
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: doxtr-hugo
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Sphinx builder that generates Hugo-ready Markdown and assets from a Sphinx project.
|
|
5
|
+
Author: Jens Frey
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/doxtr/doxtr-hugo
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Framework :: Sphinx :: Extension
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: sphinx>=5.0
|
|
19
|
+
Provides-Extra: diagrams
|
|
20
|
+
Requires-Dist: sphinxcontrib-plantuml>=0.9; extra == "diagrams"
|
|
21
|
+
Requires-Dist: sphinxcontrib-mermaid>=0.9; extra == "diagrams"
|
|
22
|
+
Requires-Dist: doxtr-d2>=0.1; extra == "diagrams"
|
|
23
|
+
Requires-Dist: diagrams>=0.23; extra == "diagrams"
|
|
24
|
+
Provides-Extra: yaml
|
|
25
|
+
Requires-Dist: PyYAML>=6.0; extra == "yaml"
|
|
26
|
+
Provides-Extra: all
|
|
27
|
+
Requires-Dist: doxtr-hugo[diagrams]; extra == "all"
|
|
28
|
+
Requires-Dist: doxtr-hugo[yaml]; extra == "all"
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
32
|
+
Requires-Dist: doxtr-hugo[all]; extra == "dev"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# doxtr-hugo
|
|
36
|
+
|
|
37
|
+
A [Sphinx](https://www.sphinx-doc.org/) builder for the
|
|
38
|
+
[doxtr](https://github.com/doxtr) ecosystem that turns a Sphinx documentation
|
|
39
|
+
source into content for the [Hugo](https://gohugo.io/) static site generator.
|
|
40
|
+
|
|
41
|
+
Author in reStructuredText/Sphinx and deploy the result as a Hugo site: the
|
|
42
|
+
extension adds a `hugo` builder (`sphinx-build -b hugo`) that walks the resolved
|
|
43
|
+
doctree and emits one Markdown file per document plus co-located assets, laid
|
|
44
|
+
out as a buildable Hugo site. It emits only formats Hugo renders natively
|
|
45
|
+
(CommonMark/Goldmark Markdown with front matter, and diagrams/figures
|
|
46
|
+
pre-rendered to static images — SVG preferred, PNG fallback), so no Hugo
|
|
47
|
+
modules, render-hook JavaScript, or the asset pipeline are required on the
|
|
48
|
+
consumer side.
|
|
49
|
+
|
|
50
|
+
> Status: early development. See `doxtr-hugo-implementation-guide.md` for the
|
|
51
|
+
> full specification and `IMPLEMENTATION_STATUS.md` for progress.
|
|
52
|
+
|
|
53
|
+
## Features
|
|
54
|
+
|
|
55
|
+
Supported constructs and capabilities:
|
|
56
|
+
|
|
57
|
+
- **Content translation** — section headings, paragraphs, inline markup
|
|
58
|
+
(emphasis/strong/literal), bullet and enumerated lists (nested), line blocks,
|
|
59
|
+
block quotes, definition lists, field lists, images and figures, and
|
|
60
|
+
`.. rubric::`.
|
|
61
|
+
- **Code blocks** — literal/`.. code-block::` blocks emitted as fenced Goldmark
|
|
62
|
+
code blocks with language hints.
|
|
63
|
+
- **Tables** — GFM pipe tables by default, with a raw-HTML fallback for cells
|
|
64
|
+
that span rows/columns (`doxtr_hugo_tables`).
|
|
65
|
+
- **Admonitions** — typed directives (note, warning, tip, caution, danger,
|
|
66
|
+
error, hint, important, attention, seealso) and generic `.. admonition::`
|
|
67
|
+
titles, emitted as a Hugo shortcode by default or portable raw HTML
|
|
68
|
+
(`doxtr_hugo_admonitions`).
|
|
69
|
+
- **Cross-references** — `:doc:`/`:ref:` and internal references emitted as Hugo
|
|
70
|
+
`{{< relref >}}` (build-time validated) by default, or plain relative links
|
|
71
|
+
(`doxtr_hugo_link_style`); external links and raw-HTML passthrough.
|
|
72
|
+
- **toctree → sections** — toctree drives the Hugo section/weight model (branch
|
|
73
|
+
vs. leaf pages, deterministic per-parent weights, `_index.md` generation) and
|
|
74
|
+
on-page navigation lists, with no hand-written front matter.
|
|
75
|
+
- **Diagrams** — five backends pre-rendered to static images: PlantUML,
|
|
76
|
+
Graphviz, Mermaid, d2 (`doxtr_d2`), and sphinx_diagrams (the `diagrams`
|
|
77
|
+
Python library, PNG-only). SVG is preferred, PNG is the portable fallback,
|
|
78
|
+
and a missing backend degrades gracefully (`doxtr_hugo_on_missing_backend`).
|
|
79
|
+
Custom backends register via `doxtr_hugo.register_backend`.
|
|
80
|
+
- **Math** — pre-rendered to transparent PNG by default (zero extra binary via
|
|
81
|
+
matplotlib mathtext), with `mathjax` and `mathml-passthrough` opt-in
|
|
82
|
+
(`doxtr_hugo_math`).
|
|
83
|
+
- **Invisible nodes** — RST comments (`.. a comment`), substitution
|
|
84
|
+
*definitions*, docutils system messages, `.. meta::`, and transform
|
|
85
|
+
`pending` placeholders are stripped from the output (their text never leaks
|
|
86
|
+
into the page); resolved substitution *usages* still render.
|
|
87
|
+
|
|
88
|
+
## Installation
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pip install doxtr-hugo
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The diagram backends are optional. To pull in the Sphinx-side extensions that
|
|
95
|
+
provide diagram directives (PlantUML, Mermaid, d2, and the `diagrams` library),
|
|
96
|
+
install the `diagrams` extra:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
pip install "doxtr-hugo[diagrams]"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
To install every optional feature at once (all diagram backends plus PyYAML for
|
|
103
|
+
YAML front matter), use the `all` extra:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
pip install "doxtr-hugo[all]"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The external rendering binaries (`plantuml`, `dot`, `mmdc`, `d2`) are **not**
|
|
110
|
+
Python dependencies — the builder only invokes them when present on `PATH` and
|
|
111
|
+
degrades gracefully otherwise (`doxtr_hugo_on_missing_backend`).
|
|
112
|
+
|
|
113
|
+
## Usage
|
|
114
|
+
|
|
115
|
+
Enable the extension in your Sphinx `conf.py`:
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
extensions = [
|
|
119
|
+
"doxtr_hugo",
|
|
120
|
+
]
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Then build the Hugo site:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# Either form works:
|
|
127
|
+
make hugo # -> build/hugo/
|
|
128
|
+
# or:
|
|
129
|
+
sphinx-build -b hugo source build/hugo
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Previewing locally
|
|
133
|
+
|
|
134
|
+
The extension does **not** ship or modify any `Makefile`. Registering the
|
|
135
|
+
builder via `app.add_builder()` is the entire integration surface; the
|
|
136
|
+
`Makefile` generated by `sphinx-quickstart` already forwards unknown targets to
|
|
137
|
+
`sphinx-build`, so `make hugo` works without any extension-side change.
|
|
138
|
+
|
|
139
|
+
With the default `doxtr_hugo_emit_skeleton = "full"`, the output in
|
|
140
|
+
`build/hugo/` is a self-contained, runnable Hugo site:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# Build the Hugo site (either form works)
|
|
144
|
+
make hugo # -> build/hugo/
|
|
145
|
+
# or:
|
|
146
|
+
sphinx-build -b hugo source build/hugo
|
|
147
|
+
|
|
148
|
+
# Preview it (requires the default emit_skeleton = "full")
|
|
149
|
+
cd build/hugo
|
|
150
|
+
hugo server
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Notes:
|
|
154
|
+
|
|
155
|
+
- The output lands in **`build/hugo/`** (the `make`/`sphinx-build` BUILDDIR),
|
|
156
|
+
which is a self-contained, runnable Hugo site because the builder's
|
|
157
|
+
`finish()` emitted `hugo.toml` + `layouts/`.
|
|
158
|
+
- `hugo server` requires `emit_skeleton = "full"` (the default). In
|
|
159
|
+
`"content-only"` mode there is no `hugo.toml`/`layouts/`, so preview must
|
|
160
|
+
happen from within the consumer's own Hugo project.
|
|
161
|
+
- Drafts: pages are emitted with `draft = false`, so plain `hugo server` shows
|
|
162
|
+
them; `hugo server -D` is only needed if the consumer later sets pages to
|
|
163
|
+
draft.
|
|
164
|
+
|
|
165
|
+
## Using with a third-party Hugo theme
|
|
166
|
+
|
|
167
|
+
The default `emit_skeleton = "full"` output ships doxtr-hugo's own
|
|
168
|
+
`layouts/_default/` so the site builds standalone. Those layouts **override a
|
|
169
|
+
theme**: Hugo's template precedence puts a site-level `layouts/_default/` above
|
|
170
|
+
any `--theme`, so building the full output under a theme makes every theme
|
|
171
|
+
render the skeleton's HTML rather than its own. To deploy under a third-party
|
|
172
|
+
theme, emit the **theme-portable** form instead:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
sphinx-build -b hugo \
|
|
176
|
+
-D doxtr_hugo_emit_skeleton=content-only \
|
|
177
|
+
source build/hugo-content
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
- `doxtr_hugo_emit_skeleton = "content-only"` writes only `content/**` (section
|
|
181
|
+
`_index.md` files + co-located assets) — no `hugo.toml`, no `layouts/`. Drop
|
|
182
|
+
that `content/**` into your own themed Hugo site.
|
|
183
|
+
- In `content-only` mode the builder defaults `doxtr_hugo_admonitions` to
|
|
184
|
+
`"html"` automatically (portable raw HTML), because a third-party theme does
|
|
185
|
+
**not** ship doxtr-hugo's `admonition` shortcode — shortcode admonitions would
|
|
186
|
+
fail there with `template for shortcode 'admonition' not found`. Set
|
|
187
|
+
`doxtr_hugo_admonitions` explicitly to override (e.g. `"shortcode"` if your
|
|
188
|
+
site provides its own `admonition` shortcode).
|
|
189
|
+
- If you instead keep the default `"full"` emit and add a theme, be aware the
|
|
190
|
+
emitted `layouts/` will shadow the theme; use `"content-only"` (above) for
|
|
191
|
+
real theme deployment.
|
|
192
|
+
|
|
193
|
+
See the configuration knobs `doxtr_hugo_emit_skeleton` and
|
|
194
|
+
`doxtr_hugo_admonitions` for the full surface.
|
|
195
|
+
|
|
196
|
+
## Configuring a theme (ready-to-serve output)
|
|
197
|
+
|
|
198
|
+
Instead of hand-assembling a themed site from the `content-only` output, you
|
|
199
|
+
can configure the theme **once in your Sphinx `conf.py`** and have
|
|
200
|
+
`sphinx-build -b hugo` emit a complete, version-controllable Hugo site under
|
|
201
|
+
`build/hugo/` whose `hugo.toml` is correctly configured for that theme:
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
# conf.py
|
|
205
|
+
doxtr_hugo_theme = "docsy" # a shipped profile, or any theme name
|
|
206
|
+
|
|
207
|
+
doxtr_hugo_theme_config = { # deep-merged into hugo.toml (you win)
|
|
208
|
+
"params": {"github_repo": "https://github.com/me/docs"},
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
# Optional: override how the theme is obtained (defaults to a Hugo Module).
|
|
212
|
+
doxtr_hugo_theme_source = {"kind": "module", "url": "github.com/google/docsy"}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Building then produces, in `build/hugo/`:
|
|
216
|
+
|
|
217
|
+
- a merged `hugo.toml` = **core defaults** (goldmark `unsafe` + extensions) +
|
|
218
|
+
the **theme profile** (e.g. docsy's `type="docs"` cascade, hugo-book's
|
|
219
|
+
`BookSection="*"`) + **your** `doxtr_hugo_theme_config` (highest precedence);
|
|
220
|
+
- a `README-hugo.md` listing the exact one-time commands still needed for that
|
|
221
|
+
theme (e.g. `hugo mod get`, installing Dart Sass, `npm install`) before
|
|
222
|
+
`hugo server` — the extension documents these, it never runs them;
|
|
223
|
+
- the image render hook so math/diagram images resolve under any theme.
|
|
224
|
+
|
|
225
|
+
Shipped profiles (edition/toolchain/config captured so the output just works):
|
|
226
|
+
`archie`, `hugo-terminal`, `hyde`, `risotto`, `notepadium`, `hugo-book`,
|
|
227
|
+
`docsy`. A theme with no shipped profile still works — it is emitted with core
|
|
228
|
+
defaults plus your `doxtr_hugo_theme_config`, and you supply the theme + config.
|
|
229
|
+
|
|
230
|
+
The emitted `build/hugo/` is Hugo **input**: commit it, then run the README's
|
|
231
|
+
commands and `hugo server`. For light themes vendored into `themes/` nothing
|
|
232
|
+
extra is needed; SCSS themes like docsy require the documented extended
|
|
233
|
+
Hugo + Dart Sass + npm steps.
|
|
234
|
+
|
|
235
|
+
### Overlay (escape hatch) and hooks
|
|
236
|
+
|
|
237
|
+
Anything the declarative config cannot express goes in a **sidecar overlay**
|
|
238
|
+
directory, copied verbatim into `build/hugo/` on top of the emitted site (your
|
|
239
|
+
files win):
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
source/.doxtr/hugo/ # doxtr_hugo_theme_overlay_dir (confdir-relative)
|
|
243
|
+
config/overlay.toml # extra raw Hugo config fragments
|
|
244
|
+
layouts/partials/head.html # custom/partial template overrides
|
|
245
|
+
assets/scss/_variables_project.scss
|
|
246
|
+
static/img/logo.png
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
The extension auto-excludes `.doxtr/**` from Sphinx's document scan, so overlay
|
|
250
|
+
files are never parsed as reStructuredText. For programmatic post-processing,
|
|
251
|
+
`doxtr_hugo_theme_hook = "mypkg.mymod:callable"` runs your own project code last
|
|
252
|
+
(called with `outdir`, `config`, `app`), after all files are written.
|
|
253
|
+
|
|
254
|
+
Configuration keys: `doxtr_hugo_theme`, `doxtr_hugo_theme_config`,
|
|
255
|
+
`doxtr_hugo_theme_source`, `doxtr_hugo_theme_overlay_dir`,
|
|
256
|
+
`doxtr_hugo_theme_hook`.
|
|
257
|
+
|
|
258
|
+
### Page titles (no duplicate heading)
|
|
259
|
+
|
|
260
|
+
By default (`doxtr_hugo_title_heading = "frontmatter"`) the page's leading
|
|
261
|
+
top-level heading is **not** emitted into the Markdown body — it is carried by
|
|
262
|
+
the `title` front matter, which every Hugo theme (and the built-in skeleton)
|
|
263
|
+
renders. Emitting it in the body too would show the title twice. Deeper headings
|
|
264
|
+
(`##` and below) are always kept. Set `doxtr_hugo_title_heading = "body"` to
|
|
265
|
+
restore the legacy behavior of writing the leading `# Title` into the body (for
|
|
266
|
+
a theme that does not render `.Title` itself).
|
|
267
|
+
|
|
268
|
+
## Changelog
|
|
269
|
+
|
|
270
|
+
See [CHANGELOG.md](CHANGELOG.md) for the release history.
|
|
271
|
+
|
|
272
|
+
## License
|
|
273
|
+
|
|
274
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# doxtr-hugo
|
|
2
|
+
|
|
3
|
+
A [Sphinx](https://www.sphinx-doc.org/) builder for the
|
|
4
|
+
[doxtr](https://github.com/doxtr) ecosystem that turns a Sphinx documentation
|
|
5
|
+
source into content for the [Hugo](https://gohugo.io/) static site generator.
|
|
6
|
+
|
|
7
|
+
Author in reStructuredText/Sphinx and deploy the result as a Hugo site: the
|
|
8
|
+
extension adds a `hugo` builder (`sphinx-build -b hugo`) that walks the resolved
|
|
9
|
+
doctree and emits one Markdown file per document plus co-located assets, laid
|
|
10
|
+
out as a buildable Hugo site. It emits only formats Hugo renders natively
|
|
11
|
+
(CommonMark/Goldmark Markdown with front matter, and diagrams/figures
|
|
12
|
+
pre-rendered to static images — SVG preferred, PNG fallback), so no Hugo
|
|
13
|
+
modules, render-hook JavaScript, or the asset pipeline are required on the
|
|
14
|
+
consumer side.
|
|
15
|
+
|
|
16
|
+
> Status: early development. See `doxtr-hugo-implementation-guide.md` for the
|
|
17
|
+
> full specification and `IMPLEMENTATION_STATUS.md` for progress.
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
Supported constructs and capabilities:
|
|
22
|
+
|
|
23
|
+
- **Content translation** — section headings, paragraphs, inline markup
|
|
24
|
+
(emphasis/strong/literal), bullet and enumerated lists (nested), line blocks,
|
|
25
|
+
block quotes, definition lists, field lists, images and figures, and
|
|
26
|
+
`.. rubric::`.
|
|
27
|
+
- **Code blocks** — literal/`.. code-block::` blocks emitted as fenced Goldmark
|
|
28
|
+
code blocks with language hints.
|
|
29
|
+
- **Tables** — GFM pipe tables by default, with a raw-HTML fallback for cells
|
|
30
|
+
that span rows/columns (`doxtr_hugo_tables`).
|
|
31
|
+
- **Admonitions** — typed directives (note, warning, tip, caution, danger,
|
|
32
|
+
error, hint, important, attention, seealso) and generic `.. admonition::`
|
|
33
|
+
titles, emitted as a Hugo shortcode by default or portable raw HTML
|
|
34
|
+
(`doxtr_hugo_admonitions`).
|
|
35
|
+
- **Cross-references** — `:doc:`/`:ref:` and internal references emitted as Hugo
|
|
36
|
+
`{{< relref >}}` (build-time validated) by default, or plain relative links
|
|
37
|
+
(`doxtr_hugo_link_style`); external links and raw-HTML passthrough.
|
|
38
|
+
- **toctree → sections** — toctree drives the Hugo section/weight model (branch
|
|
39
|
+
vs. leaf pages, deterministic per-parent weights, `_index.md` generation) and
|
|
40
|
+
on-page navigation lists, with no hand-written front matter.
|
|
41
|
+
- **Diagrams** — five backends pre-rendered to static images: PlantUML,
|
|
42
|
+
Graphviz, Mermaid, d2 (`doxtr_d2`), and sphinx_diagrams (the `diagrams`
|
|
43
|
+
Python library, PNG-only). SVG is preferred, PNG is the portable fallback,
|
|
44
|
+
and a missing backend degrades gracefully (`doxtr_hugo_on_missing_backend`).
|
|
45
|
+
Custom backends register via `doxtr_hugo.register_backend`.
|
|
46
|
+
- **Math** — pre-rendered to transparent PNG by default (zero extra binary via
|
|
47
|
+
matplotlib mathtext), with `mathjax` and `mathml-passthrough` opt-in
|
|
48
|
+
(`doxtr_hugo_math`).
|
|
49
|
+
- **Invisible nodes** — RST comments (`.. a comment`), substitution
|
|
50
|
+
*definitions*, docutils system messages, `.. meta::`, and transform
|
|
51
|
+
`pending` placeholders are stripped from the output (their text never leaks
|
|
52
|
+
into the page); resolved substitution *usages* still render.
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install doxtr-hugo
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The diagram backends are optional. To pull in the Sphinx-side extensions that
|
|
61
|
+
provide diagram directives (PlantUML, Mermaid, d2, and the `diagrams` library),
|
|
62
|
+
install the `diagrams` extra:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install "doxtr-hugo[diagrams]"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
To install every optional feature at once (all diagram backends plus PyYAML for
|
|
69
|
+
YAML front matter), use the `all` extra:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install "doxtr-hugo[all]"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The external rendering binaries (`plantuml`, `dot`, `mmdc`, `d2`) are **not**
|
|
76
|
+
Python dependencies — the builder only invokes them when present on `PATH` and
|
|
77
|
+
degrades gracefully otherwise (`doxtr_hugo_on_missing_backend`).
|
|
78
|
+
|
|
79
|
+
## Usage
|
|
80
|
+
|
|
81
|
+
Enable the extension in your Sphinx `conf.py`:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
extensions = [
|
|
85
|
+
"doxtr_hugo",
|
|
86
|
+
]
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Then build the Hugo site:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Either form works:
|
|
93
|
+
make hugo # -> build/hugo/
|
|
94
|
+
# or:
|
|
95
|
+
sphinx-build -b hugo source build/hugo
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Previewing locally
|
|
99
|
+
|
|
100
|
+
The extension does **not** ship or modify any `Makefile`. Registering the
|
|
101
|
+
builder via `app.add_builder()` is the entire integration surface; the
|
|
102
|
+
`Makefile` generated by `sphinx-quickstart` already forwards unknown targets to
|
|
103
|
+
`sphinx-build`, so `make hugo` works without any extension-side change.
|
|
104
|
+
|
|
105
|
+
With the default `doxtr_hugo_emit_skeleton = "full"`, the output in
|
|
106
|
+
`build/hugo/` is a self-contained, runnable Hugo site:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# Build the Hugo site (either form works)
|
|
110
|
+
make hugo # -> build/hugo/
|
|
111
|
+
# or:
|
|
112
|
+
sphinx-build -b hugo source build/hugo
|
|
113
|
+
|
|
114
|
+
# Preview it (requires the default emit_skeleton = "full")
|
|
115
|
+
cd build/hugo
|
|
116
|
+
hugo server
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Notes:
|
|
120
|
+
|
|
121
|
+
- The output lands in **`build/hugo/`** (the `make`/`sphinx-build` BUILDDIR),
|
|
122
|
+
which is a self-contained, runnable Hugo site because the builder's
|
|
123
|
+
`finish()` emitted `hugo.toml` + `layouts/`.
|
|
124
|
+
- `hugo server` requires `emit_skeleton = "full"` (the default). In
|
|
125
|
+
`"content-only"` mode there is no `hugo.toml`/`layouts/`, so preview must
|
|
126
|
+
happen from within the consumer's own Hugo project.
|
|
127
|
+
- Drafts: pages are emitted with `draft = false`, so plain `hugo server` shows
|
|
128
|
+
them; `hugo server -D` is only needed if the consumer later sets pages to
|
|
129
|
+
draft.
|
|
130
|
+
|
|
131
|
+
## Using with a third-party Hugo theme
|
|
132
|
+
|
|
133
|
+
The default `emit_skeleton = "full"` output ships doxtr-hugo's own
|
|
134
|
+
`layouts/_default/` so the site builds standalone. Those layouts **override a
|
|
135
|
+
theme**: Hugo's template precedence puts a site-level `layouts/_default/` above
|
|
136
|
+
any `--theme`, so building the full output under a theme makes every theme
|
|
137
|
+
render the skeleton's HTML rather than its own. To deploy under a third-party
|
|
138
|
+
theme, emit the **theme-portable** form instead:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
sphinx-build -b hugo \
|
|
142
|
+
-D doxtr_hugo_emit_skeleton=content-only \
|
|
143
|
+
source build/hugo-content
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
- `doxtr_hugo_emit_skeleton = "content-only"` writes only `content/**` (section
|
|
147
|
+
`_index.md` files + co-located assets) — no `hugo.toml`, no `layouts/`. Drop
|
|
148
|
+
that `content/**` into your own themed Hugo site.
|
|
149
|
+
- In `content-only` mode the builder defaults `doxtr_hugo_admonitions` to
|
|
150
|
+
`"html"` automatically (portable raw HTML), because a third-party theme does
|
|
151
|
+
**not** ship doxtr-hugo's `admonition` shortcode — shortcode admonitions would
|
|
152
|
+
fail there with `template for shortcode 'admonition' not found`. Set
|
|
153
|
+
`doxtr_hugo_admonitions` explicitly to override (e.g. `"shortcode"` if your
|
|
154
|
+
site provides its own `admonition` shortcode).
|
|
155
|
+
- If you instead keep the default `"full"` emit and add a theme, be aware the
|
|
156
|
+
emitted `layouts/` will shadow the theme; use `"content-only"` (above) for
|
|
157
|
+
real theme deployment.
|
|
158
|
+
|
|
159
|
+
See the configuration knobs `doxtr_hugo_emit_skeleton` and
|
|
160
|
+
`doxtr_hugo_admonitions` for the full surface.
|
|
161
|
+
|
|
162
|
+
## Configuring a theme (ready-to-serve output)
|
|
163
|
+
|
|
164
|
+
Instead of hand-assembling a themed site from the `content-only` output, you
|
|
165
|
+
can configure the theme **once in your Sphinx `conf.py`** and have
|
|
166
|
+
`sphinx-build -b hugo` emit a complete, version-controllable Hugo site under
|
|
167
|
+
`build/hugo/` whose `hugo.toml` is correctly configured for that theme:
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
# conf.py
|
|
171
|
+
doxtr_hugo_theme = "docsy" # a shipped profile, or any theme name
|
|
172
|
+
|
|
173
|
+
doxtr_hugo_theme_config = { # deep-merged into hugo.toml (you win)
|
|
174
|
+
"params": {"github_repo": "https://github.com/me/docs"},
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
# Optional: override how the theme is obtained (defaults to a Hugo Module).
|
|
178
|
+
doxtr_hugo_theme_source = {"kind": "module", "url": "github.com/google/docsy"}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Building then produces, in `build/hugo/`:
|
|
182
|
+
|
|
183
|
+
- a merged `hugo.toml` = **core defaults** (goldmark `unsafe` + extensions) +
|
|
184
|
+
the **theme profile** (e.g. docsy's `type="docs"` cascade, hugo-book's
|
|
185
|
+
`BookSection="*"`) + **your** `doxtr_hugo_theme_config` (highest precedence);
|
|
186
|
+
- a `README-hugo.md` listing the exact one-time commands still needed for that
|
|
187
|
+
theme (e.g. `hugo mod get`, installing Dart Sass, `npm install`) before
|
|
188
|
+
`hugo server` — the extension documents these, it never runs them;
|
|
189
|
+
- the image render hook so math/diagram images resolve under any theme.
|
|
190
|
+
|
|
191
|
+
Shipped profiles (edition/toolchain/config captured so the output just works):
|
|
192
|
+
`archie`, `hugo-terminal`, `hyde`, `risotto`, `notepadium`, `hugo-book`,
|
|
193
|
+
`docsy`. A theme with no shipped profile still works — it is emitted with core
|
|
194
|
+
defaults plus your `doxtr_hugo_theme_config`, and you supply the theme + config.
|
|
195
|
+
|
|
196
|
+
The emitted `build/hugo/` is Hugo **input**: commit it, then run the README's
|
|
197
|
+
commands and `hugo server`. For light themes vendored into `themes/` nothing
|
|
198
|
+
extra is needed; SCSS themes like docsy require the documented extended
|
|
199
|
+
Hugo + Dart Sass + npm steps.
|
|
200
|
+
|
|
201
|
+
### Overlay (escape hatch) and hooks
|
|
202
|
+
|
|
203
|
+
Anything the declarative config cannot express goes in a **sidecar overlay**
|
|
204
|
+
directory, copied verbatim into `build/hugo/` on top of the emitted site (your
|
|
205
|
+
files win):
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
source/.doxtr/hugo/ # doxtr_hugo_theme_overlay_dir (confdir-relative)
|
|
209
|
+
config/overlay.toml # extra raw Hugo config fragments
|
|
210
|
+
layouts/partials/head.html # custom/partial template overrides
|
|
211
|
+
assets/scss/_variables_project.scss
|
|
212
|
+
static/img/logo.png
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
The extension auto-excludes `.doxtr/**` from Sphinx's document scan, so overlay
|
|
216
|
+
files are never parsed as reStructuredText. For programmatic post-processing,
|
|
217
|
+
`doxtr_hugo_theme_hook = "mypkg.mymod:callable"` runs your own project code last
|
|
218
|
+
(called with `outdir`, `config`, `app`), after all files are written.
|
|
219
|
+
|
|
220
|
+
Configuration keys: `doxtr_hugo_theme`, `doxtr_hugo_theme_config`,
|
|
221
|
+
`doxtr_hugo_theme_source`, `doxtr_hugo_theme_overlay_dir`,
|
|
222
|
+
`doxtr_hugo_theme_hook`.
|
|
223
|
+
|
|
224
|
+
### Page titles (no duplicate heading)
|
|
225
|
+
|
|
226
|
+
By default (`doxtr_hugo_title_heading = "frontmatter"`) the page's leading
|
|
227
|
+
top-level heading is **not** emitted into the Markdown body — it is carried by
|
|
228
|
+
the `title` front matter, which every Hugo theme (and the built-in skeleton)
|
|
229
|
+
renders. Emitting it in the body too would show the title twice. Deeper headings
|
|
230
|
+
(`##` and below) are always kept. Set `doxtr_hugo_title_heading = "body"` to
|
|
231
|
+
restore the legacy behavior of writing the leading `# Title` into the body (for
|
|
232
|
+
a theme that does not render `.Title` itself).
|
|
233
|
+
|
|
234
|
+
## Changelog
|
|
235
|
+
|
|
236
|
+
See [CHANGELOG.md](CHANGELOG.md) for the release history.
|
|
237
|
+
|
|
238
|
+
## License
|
|
239
|
+
|
|
240
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""doxtr-hugo — a Sphinx builder that emits Hugo-ready Markdown and assets.
|
|
2
|
+
|
|
3
|
+
The extension registers a ``hugo`` builder (``sphinx-build -b hugo``) that walks
|
|
4
|
+
the resolved doctree and writes one Markdown file per document plus co-located
|
|
5
|
+
assets, laid out as a buildable Hugo site. See the implementation guide for the
|
|
6
|
+
full contract.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
__version__ = "0.1.0"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def register_backend(backend, node_names, *, runner=None, node_extra_args=None):
|
|
13
|
+
"""Register a custom diagram backend (public consumer seam).
|
|
14
|
+
|
|
15
|
+
Thin re-export of :func:`doxtr_hugo.diagrams.register_backend` so downstream
|
|
16
|
+
extensions can register a :class:`~doxtr_hugo.diagrams.DiagramBackend`, map
|
|
17
|
+
its doctree node class names, and optionally supply a custom subprocess
|
|
18
|
+
``runner`` and per-node ``node_extra_args`` hook — without importing a
|
|
19
|
+
private module path. See that function for full parameter docs.
|
|
20
|
+
"""
|
|
21
|
+
from .diagrams import register_backend as _register_backend
|
|
22
|
+
|
|
23
|
+
return _register_backend(
|
|
24
|
+
backend,
|
|
25
|
+
node_names,
|
|
26
|
+
runner=runner,
|
|
27
|
+
node_extra_args=node_extra_args,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _exclude_overlay_from_sphinx(app, config):
|
|
32
|
+
"""Idempotently exclude the theme-overlay sidecar from Sphinx's doc scan.
|
|
33
|
+
|
|
34
|
+
The overlay dir (``doxtr_hugo_theme_overlay_dir``, default ``.doxtr/hugo``)
|
|
35
|
+
holds Hugo input that must not be parsed as reStructuredText or flagged as
|
|
36
|
+
"not in any toctree". Add a glob for the dotted ``.doxtr`` namespace to
|
|
37
|
+
``exclude_patterns`` unless it is already covered, preserving any patterns
|
|
38
|
+
the user configured.
|
|
39
|
+
"""
|
|
40
|
+
overlay = getattr(config, "doxtr_hugo_theme_overlay_dir", "") or ""
|
|
41
|
+
# Only auto-exclude a confdir-relative overlay (an absolute path points
|
|
42
|
+
# outside the source tree and is never scanned by Sphinx anyway).
|
|
43
|
+
import os as _os
|
|
44
|
+
|
|
45
|
+
if not overlay or _os.path.isabs(overlay):
|
|
46
|
+
return
|
|
47
|
+
top = overlay.replace("\\", "/").split("/", 1)[0] or overlay
|
|
48
|
+
patterns = list(getattr(config, "exclude_patterns", []) or [])
|
|
49
|
+
glob = f"{top}/**"
|
|
50
|
+
if top not in patterns and glob not in patterns:
|
|
51
|
+
patterns.append(glob)
|
|
52
|
+
config.exclude_patterns = patterns
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def setup(app):
|
|
56
|
+
"""Register the Hugo builder and all ``doxtr_hugo_*`` config values.
|
|
57
|
+
|
|
58
|
+
This is the entire integration surface of the extension: adding the builder
|
|
59
|
+
makes ``sphinx-build -b hugo`` (and the ``make hugo`` target generated by
|
|
60
|
+
``sphinx-quickstart``) work, and registering the config values exposes the
|
|
61
|
+
configuration surface documented in ``config_defaults.DEFAULT_CONFIG``.
|
|
62
|
+
"""
|
|
63
|
+
from .builder import HugoBuilder
|
|
64
|
+
from .config_defaults import DEFAULT_CONFIG
|
|
65
|
+
|
|
66
|
+
app.add_builder(HugoBuilder)
|
|
67
|
+
for name, default in DEFAULT_CONFIG.items():
|
|
68
|
+
# "env" rebuild: a change to any of these values invalidates the
|
|
69
|
+
# emitted output, so Sphinx should rebuild the environment.
|
|
70
|
+
app.add_config_value(name, default, "env")
|
|
71
|
+
|
|
72
|
+
# The theme-overlay sidecar (``.doxtr/hugo/`` by default) lives under the
|
|
73
|
+
# source tree but is Hugo input, not Sphinx documents. Exclude it so Sphinx
|
|
74
|
+
# neither parses its files nor warns that they are not in any toctree. Done
|
|
75
|
+
# idempotently on ``config-inited`` so a user's ``exclude_patterns`` is
|
|
76
|
+
# preserved.
|
|
77
|
+
app.connect("config-inited", _exclude_overlay_from_sphinx)
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
"version": __version__,
|
|
81
|
+
"parallel_read_safe": True,
|
|
82
|
+
"parallel_write_safe": True,
|
|
83
|
+
}
|