diff --git a/CHANGES.rst b/CHANGES.rst
index f0f94fda396..aada4097bc2 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -68,6 +68,8 @@ 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: Allow ``mathjax3_config`` to be a string pointing to a JS file.
+ Patch by Randolf Scholz.
Bugs fixed
----------
diff --git a/doc/usage/extensions/math.rst b/doc/usage/extensions/math.rst
index fb41d66d8fb..d44624b070f 100644
--- a/doc/usage/extensions/math.rst
+++ b/doc/usage/extensions/math.rst
@@ -264,12 +264,13 @@ Sphinx but is set to automatically include it from a third-party site.
or "defer" key is set.
.. confval:: mathjax3_config
- :type: :code-py:`dict[str, Any] | None`
+ :type: :code-py:`str | dict[str, Any] | None`
: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``.
+ Expects a string with the relative path to a JavaScript file with the config.
+ Alternatively, a dictionary can be given, which 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
diff --git a/sphinx/ext/mathjax.py b/sphinx/ext/mathjax.py
index 62220cc697f..3f45bb5e675 100644
--- a/sphinx/ext/mathjax.py
+++ b/sphinx/ext/mathjax.py
@@ -105,9 +105,26 @@ def install_mathjax(
)
body = 'MathJax.Hub.Config(%s)' % 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)
- builder.add_js_file('', body=body)
+ match app.config.mathjax3_config:
+ case _ if not app.config.mathjax3_config:
+ # None, empty string or empty dict
+ pass
+ case str(config_filename):
+ config_filepath = app.srcdir / config_filename
+ if not config_filepath.exists():
+ msg = f'mathjax3_config file not found: {config_filepath!s}'
+ raise ExtensionError(msg)
+ if not config_filepath.is_file() or config_filepath.suffix != '.js':
+ msg = f'mathjax3_config: expected a .js file, but got {config_filepath!s}'
+ raise ExtensionError(msg)
+ body = config_filepath.read_text(encoding='utf-8')
+ builder.add_js_file('', body=body)
+ case dict(config_dict):
+ body = f'window.MathJax = {json.dumps(config_dict)}'
+ builder.add_js_file('', body=body)
+ case _:
+ msg = 'mathjax3_config must be a str (filename), dict, or None'
+ raise ExtensionError(msg)
options = {}
if app.config.mathjax_options:
@@ -147,7 +164,7 @@ def setup(app: Sphinx) -> ExtensionMetadata:
types=frozenset({dict, NoneType}),
)
app.add_config_value(
- 'mathjax3_config', None, 'html', types=frozenset({dict, NoneType})
+ 'mathjax3_config', None, 'html', types=frozenset({dict, str, NoneType})
)
app.connect('html-page-context', install_mathjax)
diff --git a/tests/roots/test-ext-math/_static/custom_mathjax_config.js b/tests/roots/test-ext-math/_static/custom_mathjax_config.js
new file mode 100644
index 00000000000..7a2bda39383
--- /dev/null
+++ b/tests/roots/test-ext-math/_static/custom_mathjax_config.js
@@ -0,0 +1 @@
+window.MathJax = {"extensions": ["tex2jax.js"]}
\ No newline at end of file
diff --git a/tests/test_extensions/test_ext_math.py b/tests/test_extensions/test_ext_math.py
index 9c8e620d655..629997ea4aa 100644
--- a/tests/test_extensions/test_ext_math.py
+++ b/tests/test_extensions/test_ext_math.py
@@ -376,6 +376,23 @@ def test_mathjax3_config(app: SphinxTestApp) -> None:
assert '' in content
+@pytest.mark.sphinx(
+ 'html',
+ testroot='ext-math',
+ confoverrides={
+ 'extensions': ['sphinx.ext.mathjax'],
+ 'mathjax3_config': '_static/custom_mathjax_config.js',
+ },
+)
+def test_mathjax3_js_config(app: SphinxTestApp) -> None:
+ app.build(force_all=True)
+
+ content = (app.outdir / 'index.html').read_text(encoding='utf8')
+ assert MATHJAX_URL in content
+ assert '' in content
+
+
@pytest.mark.sphinx(
'html',
testroot='ext-math',