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.
Files changed (48) hide show
  1. doxtr_hugo-0.1.0/LICENSE +21 -0
  2. doxtr_hugo-0.1.0/PKG-INFO +274 -0
  3. doxtr_hugo-0.1.0/README.md +240 -0
  4. doxtr_hugo-0.1.0/doxtr_hugo/__init__.py +83 -0
  5. doxtr_hugo-0.1.0/doxtr_hugo/_preview_cli.py +136 -0
  6. doxtr_hugo-0.1.0/doxtr_hugo/assets.py +268 -0
  7. doxtr_hugo-0.1.0/doxtr_hugo/builder.py +443 -0
  8. doxtr_hugo-0.1.0/doxtr_hugo/config_defaults.py +39 -0
  9. doxtr_hugo-0.1.0/doxtr_hugo/diagrams.py +934 -0
  10. doxtr_hugo-0.1.0/doxtr_hugo/frontmatter.py +258 -0
  11. doxtr_hugo-0.1.0/doxtr_hugo/image_policy.py +183 -0
  12. doxtr_hugo-0.1.0/doxtr_hugo/links.py +151 -0
  13. doxtr_hugo-0.1.0/doxtr_hugo/math_render.py +444 -0
  14. doxtr_hugo-0.1.0/doxtr_hugo/py.typed +0 -0
  15. doxtr_hugo-0.1.0/doxtr_hugo/render_pass.py +457 -0
  16. doxtr_hugo-0.1.0/doxtr_hugo/site_skeleton.py +311 -0
  17. doxtr_hugo-0.1.0/doxtr_hugo/slug.py +336 -0
  18. doxtr_hugo-0.1.0/doxtr_hugo/templates/layouts/_default/_markup/render-image.html +37 -0
  19. doxtr_hugo-0.1.0/doxtr_hugo/templates/layouts/_default/baseof.html +33 -0
  20. doxtr_hugo-0.1.0/doxtr_hugo/templates/layouts/_default/list.html +13 -0
  21. doxtr_hugo-0.1.0/doxtr_hugo/templates/layouts/_default/single.html +6 -0
  22. doxtr_hugo-0.1.0/doxtr_hugo/templates/layouts/shortcodes/admonition.html +13 -0
  23. doxtr_hugo-0.1.0/doxtr_hugo/templates/layouts/shortcodes/figure.html +11 -0
  24. doxtr_hugo-0.1.0/doxtr_hugo/theme_support.py +562 -0
  25. doxtr_hugo-0.1.0/doxtr_hugo/toctree.py +237 -0
  26. doxtr_hugo-0.1.0/doxtr_hugo/translator.py +1265 -0
  27. doxtr_hugo-0.1.0/doxtr_hugo.egg-info/PKG-INFO +274 -0
  28. doxtr_hugo-0.1.0/doxtr_hugo.egg-info/SOURCES.txt +46 -0
  29. doxtr_hugo-0.1.0/doxtr_hugo.egg-info/dependency_links.txt +1 -0
  30. doxtr_hugo-0.1.0/doxtr_hugo.egg-info/requires.txt +19 -0
  31. doxtr_hugo-0.1.0/doxtr_hugo.egg-info/top_level.txt +1 -0
  32. doxtr_hugo-0.1.0/pyproject.toml +83 -0
  33. doxtr_hugo-0.1.0/setup.cfg +4 -0
  34. doxtr_hugo-0.1.0/tests/test_assets.py +244 -0
  35. doxtr_hugo-0.1.0/tests/test_diagrams.py +748 -0
  36. doxtr_hugo-0.1.0/tests/test_frontmatter.py +160 -0
  37. doxtr_hugo-0.1.0/tests/test_harness_verify_formats.py +212 -0
  38. doxtr_hugo-0.1.0/tests/test_image_policy.py +166 -0
  39. doxtr_hugo-0.1.0/tests/test_links.py +459 -0
  40. doxtr_hugo-0.1.0/tests/test_math_render.py +394 -0
  41. doxtr_hugo-0.1.0/tests/test_preview_cli.py +105 -0
  42. doxtr_hugo-0.1.0/tests/test_render_pass.py +314 -0
  43. doxtr_hugo-0.1.0/tests/test_scaffold.py +123 -0
  44. doxtr_hugo-0.1.0/tests/test_site_skeleton.py +216 -0
  45. doxtr_hugo-0.1.0/tests/test_slug.py +292 -0
  46. doxtr_hugo-0.1.0/tests/test_theme_support.py +246 -0
  47. doxtr_hugo-0.1.0/tests/test_toctree.py +220 -0
  48. doxtr_hugo-0.1.0/tests/test_translator.py +947 -0
@@ -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
+ }