Implement Sphinx extension

This commit is contained in:
Adrian Vollmer 2022-10-05 17:11:12 +02:00
parent bf36d10bc7
commit 1924eb07a9
4 changed files with 70 additions and 25 deletions

View File

@ -41,8 +41,11 @@ Limitations
This approach is quite hacky, but it might work well enough for some purposes. This approach is quite hacky, but it might work well enough for some purposes.
Some scripts may break as the execution flow is different than some scripts * Some scripts may break as the execution flow is different than some scripts
expect. expect. For example, HTML forms with `method="GET"` to local HTML files only
work if the receiving code uses URLSearchParams, as same-origin policies
forbid reading GET parameters otherwise.
* Opening links in a new tab won't work
Installation Installation
@ -61,19 +64,11 @@ An entry point called `zundler` will appear in `~/.local/bin`.
Bundling Sphinx docs Bundling Sphinx docs
-------------------- --------------------
Providing a Sphinx extension does not make sense, because it would need to The Zundler package provides a Sphinx extension that adds an appropriate
add a new builder. Similar to the `latexpdf` builder. The builder is a thin wrapper around the HTML builder, which runs
`zundler` at the end. It can be used with `sphinx-build -b zundler` or, if
there is a suitable Makefile, with `make zundler`.
Suggested Makefile addition:
```make
.PHONY: zundler
latexpdf:
$(SPHINXBUILD) -b zundler $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo "Bundle asset into one self-contained HTML file..."
$(MAKE) -C $(BUILDDIR)/html
@echo "Zundler finished; the HTML file is in $(BUILDDIR)/zundler."
```
Demos Demos
----- -----
@ -81,8 +76,6 @@ Demos
... ...
Copyright Copyright
--------- ---------

View File

@ -28,11 +28,14 @@ packages = find:
install_requires = install_requires =
sphinx sphinx
bs4 bs4
lxml
importlib-metadata; python_version<"3.8" importlib-metadata; python_version<"3.8"
[options.entry_points] [options.entry_points]
console_scripts = console_scripts =
zundler = zundler.__main__:main zundler = zundler.__main__:main
sphinx.builders =
zundler = zundler.sphinxext
[mypy] [mypy]
python_version = 3.8 python_version = 3.8

View File

@ -9,7 +9,7 @@ It creates an HTML file that has three script tags:
virtual file tree instead of the file system virtual file tree instead of the file system
Also, two scripts are injected into all HTML files in the file tree. One as Also, two scripts are injected into all HTML files in the file tree. One as
the first child of <body>, one as the last. The first does some the first child of <head>, one as the last child of <body>. The first does some
monkeypatching, the last sets up all magic. monkeypatching, the last sets up all magic.
Author: Adrian Vollmer Author: Adrian Vollmer
@ -162,11 +162,12 @@ def embed_html_resources(html, base_dir, before, after):
import bs4 import bs4
soup = bs4.BeautifulSoup(html, 'lxml') soup = bs4.BeautifulSoup(html, 'lxml')
body = soup.find('body') body = soup.find('body')
head = soup.find('head')
if body and before: if head and before:
script = soup.new_tag("script") script = soup.new_tag("script")
script.string = before script.string = before
body.insert(0, script) head.insert(0, script)
if body and after: if body and after:
script = soup.new_tag("script") script = soup.new_tag("script")

View File

@ -1,16 +1,64 @@
from sphinx.builders.html import StandaloneHTMLBuilder import os
from pathlib import Path
MAKEFILE = """ from sphinx.builders.html import StandaloneHTMLBuilder
""" from sphinx.locale import get_translation
from sphinx.util import logging, progress_message
from sphinx.util.osutil import relpath
__ = get_translation(__name__, 'console')
logger = logging.getLogger(__name__)
class ZundlerBuilder(StandaloneHTMLBuilder): class ZundlerBuilder(StandaloneHTMLBuilder):
def handle_finish(self) -> None: name = 'zundler'
super().handle_finish() epilog = ""
def __init__(self, app):
super().__init__(app)
self.epilog = (
'Your self-contained HTML file is now in %s.' %
relpath(self.app.original_outdir)
)
def finish(self):
super().finish()
from zundler.embed import embed_assets
input_path = os.path.join(
self.outdir,
self.config.root_doc + '.html',
)
output_path = os.path.join(
self.app.original_outdir,
self.config.root_doc + '.html',
)
with progress_message(__('embedding HTML assets')):
embed_assets(
input_path,
output_path=output_path,
)
def setup(app): def setup(app):
app.add_config_value('html_embed_assets', False, 'html') # Fix the outdir. We want to build the files into $builddir/html first,
# then $builddir/$target second.
outdir = os.path.join(
os.path.dirname(app.outdir),
'html',
)
doctreedir = os.path.join(
os.path.dirname(app.outdir),
'doctree',
)
app.original_outdir = app.outdir
app.outdir = outdir
app.doctreedir = doctreedir
Path(app.outdir).mkdir(parents=True, exist_ok=True)
app.add_builder(ZundlerBuilder) app.add_builder(ZundlerBuilder)