From 1924eb07a9fa374ac4ffefaaa57c9950e1f8e47a Mon Sep 17 00:00:00 2001 From: Adrian Vollmer Date: Wed, 5 Oct 2022 17:11:12 +0200 Subject: [PATCH] Implement Sphinx extension --- README.md | 25 ++++++--------- setup.cfg | 3 ++ zundler/embed.py | 7 ++-- zundler/sphinxext/__init__.py | 60 +++++++++++++++++++++++++++++++---- 4 files changed, 70 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 151af1d..13817af 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,11 @@ Limitations 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 -expect. +* Some scripts may break as the execution flow is different than some scripts + 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 @@ -61,19 +64,11 @@ An entry point called `zundler` will appear in `~/.local/bin`. Bundling Sphinx docs -------------------- -Providing a Sphinx extension does not make sense, because it would need to -add a new builder. Similar to the `latexpdf` +The Zundler package provides a Sphinx extension that adds an appropriate +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 ----- @@ -81,8 +76,6 @@ Demos ... - - Copyright --------- diff --git a/setup.cfg b/setup.cfg index 8e50910..cfd9f32 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,11 +28,14 @@ packages = find: install_requires = sphinx bs4 + lxml importlib-metadata; python_version<"3.8" [options.entry_points] console_scripts = zundler = zundler.__main__:main +sphinx.builders = + zundler = zundler.sphinxext [mypy] python_version = 3.8 diff --git a/zundler/embed.py b/zundler/embed.py index 3f54ec3..feff2d7 100644 --- a/zundler/embed.py +++ b/zundler/embed.py @@ -9,7 +9,7 @@ It creates an HTML file that has three script tags: virtual file tree instead of the file system Also, two scripts are injected into all HTML files in the file tree. One as -the first child of , one as the last. The first does some +the first child of , one as the last child of . The first does some monkeypatching, the last sets up all magic. Author: Adrian Vollmer @@ -162,11 +162,12 @@ def embed_html_resources(html, base_dir, before, after): import bs4 soup = bs4.BeautifulSoup(html, 'lxml') body = soup.find('body') + head = soup.find('head') - if body and before: + if head and before: script = soup.new_tag("script") script.string = before - body.insert(0, script) + head.insert(0, script) if body and after: script = soup.new_tag("script") diff --git a/zundler/sphinxext/__init__.py b/zundler/sphinxext/__init__.py index ca8df50..aac8ee0 100644 --- a/zundler/sphinxext/__init__.py +++ b/zundler/sphinxext/__init__.py @@ -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): - def handle_finish(self) -> None: - super().handle_finish() + name = 'zundler' + 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): - 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)