Add `mathjax_config_file` to load MathJax configuration from a file (#14025)

Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
This commit is contained in:
Randolf ScholzandAdam Turner authored and GitHub committed 2025-11-24 20:59:21 +00:00
1 parent 5d0ad16861
commit 67762d88a2
5 files changed
+82 -15

No files matched your search

+3
View File
@@ -68,6 +68,9 @@ Features added
Patch by Jean-François B.
* #13508: Initial support for :pep:`695` type aliases.
Patch by Martin Matouš, Jeremy Maitin-Shepard, and Adam Turner.
* #14023: Add the new :confval:`mathjax_config_path` option
to load MathJax configuration from a file.
Patch by Randolf Scholz and Adam Turner.
Bugs fixed
----------
+34 -4
View File
@@ -197,8 +197,8 @@ are built:
:synopsis: Render math using JavaScript via MathJax.
.. warning::
Version 4.0 changes the version of MathJax used to version 3. You may need to
override ``mathjax_path`` to
Sphinx 4.0 changes the version of MathJax used to version 3.
You may need to override :confval:`mathjax_path` to
``https://cdn.jsdelivr.net/npm/mathjax@2/MathJax.js?config=TeX-AMS-MML_HTMLorMML``
or update your configuration options for version 3
(see :confval:`mathjax3_config`).
@@ -217,6 +217,13 @@ Sphinx but is set to automatically include it from a third-party site.
You should use the math :rst:dir:`directive <math>` and
:rst:role:`role <math>`, not the native MathJax ``$$``, ``\(``, etc.
.. tip::
MathJax configuration can be supplied in a JavaScript file
by using the :confval:`mathjax_config_path` option.
This is useful for more complex configurations that are hard to express
only using a Python dictionary, for example JavaScript functions.
.. confval:: mathjax_path
:type: :code-py:`str`
@@ -268,8 +275,9 @@ Sphinx but is set to automatically include it from a third-party site.
:default: :code-py:`None`
The configuration options for MathJax v3 (which is used by default).
The given dictionary is assigned to the JavaScript variable
``window.MathJax``.
If given, the dictionary is converted to a JSON object
and assigned to the JavaScript variable ``window.MathJax``.
For more information, please read `Configuring MathJax`__.
__ https://docs.mathjax.org/en/latest/web/configuration.html#configuration
@@ -318,6 +326,28 @@ Sphinx but is set to automatically include it from a third-party site.
This has been renamed to :confval:`mathjax2_config`.
:confval:`mathjax_config` is still supported for backwards compatibility.
.. confval:: mathjax_config_path
:type: :code-py:`str`
:default: :code-py:`''`
If given, this must be the path of a JavaScript (:file:`.js`) file
(path relative to the :term:`configuration directory`)
that contains the configuration options for MathJax.
Example:
.. code-block:: python
mathjax_config_path = 'mathjax-config.js'
.. important:: The user is responsible for ensuring that the given file
is compatible with the version of MathJax being used.
For more information, please read `Configuring MathJax`__.
__ https://docs.mathjax.org/en/latest/web/configuration.html#configuration
.. versionadded:: 8.3
:mod:`sphinxcontrib.jsmath` -- Render math via JavaScript
---------------------------------------------------------
+22 -8
View File
@@ -95,30 +95,43 @@ def install_mathjax(
builder = cast('StandaloneHTMLBuilder', app.builder)
page_has_equations = context.get('has_maths_elements', False)
# Enable mathjax only if equations exists
if app.registry.html_assets_policy == 'always' or page_has_equations:
# Enable mathjax only if equations exists
if app.config.mathjax2_config:
if app.config.mathjax_path == MATHJAX_URL:
logger.warning(
'mathjax_config/mathjax2_config does not work '
'for the current MathJax version, use mathjax3_config instead'
)
body = 'MathJax.Hub.Config(%s)' % json.dumps(app.config.mathjax2_config)
body = f'MathJax.Hub.Config({json.dumps(app.config.mathjax2_config)})'
builder.add_js_file('', type='text/x-mathjax-config', body=body)
if app.config.mathjax3_config:
body = 'window.MathJax = %s' % json.dumps(app.config.mathjax3_config)
body = f'window.MathJax = {json.dumps(app.config.mathjax3_config)}'
builder.add_js_file('', body=body)
if app.config.mathjax_config_path:
config_path = app.confdir / app.config.mathjax_config_path
if not config_path.exists():
msg = f'mathjax_config_path file not found: {config_path}'
raise ExtensionError(msg)
if not config_path.is_file() or config_path.suffix != '.js':
msg = f'mathjax_config_path: expected a .js file, but got {config_path}'
raise ExtensionError(msg)
body = config_path.read_text(encoding='utf-8')
builder.add_js_file('', body=body)
options = {}
if app.config.mathjax_options:
options.update(app.config.mathjax_options)
if 'async' not in options and 'defer' not in options:
if app.config.mathjax3_config:
# Load MathJax v3 via "defer" method
options['defer'] = 'defer'
else:
# Load other MathJax via "async" method
if app.config.mathjax2_config or app.config.mathjax_config:
# Load old MathJax versions via the 'async' method
options['async'] = 'async'
else:
# Load MathJax v3+ via the 'defer' method
options['defer'] = 'defer'
builder.add_js_file(app.config.mathjax_path, **options)
@@ -149,6 +162,7 @@ def setup(app: Sphinx) -> ExtensionMetadata:
app.add_config_value(
'mathjax3_config', None, 'html', types=frozenset({dict, NoneType})
)
app.add_config_value('mathjax_config_path', '', 'html', types=frozenset({str}))
app.connect('html-page-context', install_mathjax)
return {
@@ -0,0 +1 @@
window.MathJax = {"extensions": ["tex2jax.js"]}
+22 -3
View File
@@ -124,7 +124,7 @@ def test_mathjax_options(app: SphinxTestApp) -> None:
content = (app.outdir / 'index.html').read_text(encoding='utf8')
shutil.rmtree(app.outdir)
assert (
'<script async="async" integrity="sha384-0123456789" '
'<script defer="defer" integrity="sha384-0123456789" '
'src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js">'
'</script>'
) in content
@@ -376,6 +376,25 @@ def test_mathjax3_config(app: SphinxTestApp) -> None:
assert '<script>window.MathJax = {"extensions": ["tex2jax.js"]}</script>' in content
@pytest.mark.sphinx(
'html',
testroot='ext-math',
confoverrides={
'extensions': ['sphinx.ext.mathjax'],
'mathjax_config_path': '_static/custom_mathjax_config.js',
},
)
def test_mathjax_config_path_config(app: SphinxTestApp) -> None:
app.build(force_all=True)
content = (app.outdir / 'index.html').read_text(encoding='utf8')
assert MATHJAX_URL in content
assert f'<script defer="defer" src="{MATHJAX_URL}">' in content
assert (
'<script>window.MathJax = {"extensions": ["tex2jax.js"]}\n</script>'
) in content
@pytest.mark.sphinx(
'html',
testroot='ext-math',
@@ -441,7 +460,7 @@ def test_mathjax_path(app: SphinxTestApp) -> None:
app.build(force_all=True)
content = (app.outdir / 'index.html').read_text(encoding='utf8')
assert '<script async="async" src="_static/MathJax.js"></script>' in content
assert '<script defer="defer" src="_static/MathJax.js"></script>' in content
@pytest.mark.sphinx(
@@ -457,7 +476,7 @@ def test_mathjax_path_config(app: SphinxTestApp) -> None:
content = (app.outdir / 'index.html').read_text(encoding='utf8')
assert (
'<script async="async" src="_static/MathJax.js?config=scipy-mathjax"></script>'
'<script defer="defer" src="_static/MathJax.js?config=scipy-mathjax"></script>'
) in content