diff --git a/CHANGES b/CHANGES index 761ed25397b..05e4ef2e097 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,8 @@ Dependencies Incompatible changes -------------------- +* #5497: Do not include MathJax.js and jsmath.js unless it is really needed + Deprecated ---------- diff --git a/sphinx/domains/math.py b/sphinx/domains/math.py index c88481746e5..d56f45e0eb2 100644 --- a/sphinx/domains/math.py +++ b/sphinx/domains/math.py @@ -44,6 +44,7 @@ class MathDomain(Domain): initial_data = { 'objects': {}, # labelid -> (docname, eqno) + 'has_equations': {}, # docname -> bool } # type: Dict[unicode, Dict[unicode, Tuple[unicode, int]]] dangling_warnings = { 'eq': 'equation not found: %(target)s', @@ -56,18 +57,30 @@ class MathDomain(Domain): 'numref': MathReferenceRole(), } + def process_doc(self, env, docname, document): + # type: (BuildEnvironment, unicode, nodes.Node) -> None + def math_node(node): + return isinstance(node, (nodes.math, nodes.math_block)) + + self.data['has_equations'][docname] = any(document.traverse(math_node)) + def clear_doc(self, docname): # type: (unicode) -> None for equation_id, (doc, eqno) in list(self.data['objects'].items()): if doc == docname: del self.data['objects'][equation_id] + self.data['has_equations'].pop(docname, None) + def merge_domaindata(self, docnames, otherdata): # type: (Iterable[unicode], Dict) -> None for labelid, (doc, eqno) in otherdata['objects'].items(): if doc in docnames: self.data['objects'][labelid] = (doc, eqno) + for docname in docnames: + self.data['has_equations'][docname] = otherdata['has_equations'][docname] + def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): # type: (BuildEnvironment, unicode, Builder, unicode, unicode, nodes.Node, nodes.Node) -> nodes.Node # NOQA assert typ in ('eq', 'numref') @@ -122,6 +135,10 @@ def get_next_equation_number(self, docname): targets = [eq for eq in self.data['objects'].values() if eq[0] == docname] return len(targets) + 1 + def has_equations(self): + # type: () -> bool + return any(self.data['has_equations'].values()) + def setup(app): # type: (Sphinx) -> Dict[unicode, Any] @@ -130,7 +147,7 @@ def setup(app): return { 'version': 'builtin', - 'env_version': 1, + 'env_version': 2, 'parallel_read_safe': True, 'parallel_write_safe': True, } diff --git a/sphinx/ext/jsmath.py b/sphinx/ext/jsmath.py index 0ee42050c62..3babd408e7d 100644 --- a/sphinx/ext/jsmath.py +++ b/sphinx/ext/jsmath.py @@ -21,6 +21,7 @@ # For type annotation from typing import Any, Dict # NOQA from sphinx.application import Sphinx # NOQA + from sphinx.environment import BuildEnvironment # NOQA def html_visit_math(self, node): @@ -58,14 +59,16 @@ def html_visit_displaymath(self, node): raise nodes.SkipNode -def builder_inited(app): - # type: (Sphinx) -> None +def install_jsmath(app, env): + # type: (Sphinx, BuildEnvironment) -> None if app.builder.format != 'html' or app.builder.math_renderer_name != 'jsmath': # type: ignore # NOQA - pass - elif not app.config.jsmath_path: + return + if not app.config.jsmath_path: raise ExtensionError('jsmath_path config value must be set for the ' 'jsmath extension to work') - if app.builder.format == 'html': + + if env.get_domain('math').has_equations(): # type: ignore + # Enable jsmath only if equations exists app.builder.add_js_file(app.config.jsmath_path) # type: ignore @@ -76,5 +79,5 @@ def setup(app): (html_visit_displaymath, None)) app.add_config_value('jsmath_path', '', False) - app.connect('builder-inited', builder_inited) + app.connect('env-check-consistency', install_jsmath) return {'version': sphinx.__display_version__, 'parallel_read_safe': True} diff --git a/sphinx/ext/mathjax.py b/sphinx/ext/mathjax.py index 50a2bae4d57..21406c4515b 100644 --- a/sphinx/ext/mathjax.py +++ b/sphinx/ext/mathjax.py @@ -24,6 +24,7 @@ # For type annotation from typing import Any, Dict # NOQA from sphinx.application import Sphinx # NOQA + from sphinx.environment import BuildEnvironment # NOQA def html_visit_math(self, node): @@ -68,14 +69,16 @@ def html_visit_displaymath(self, node): raise nodes.SkipNode -def builder_inited(app): - # type: (Sphinx) -> None +def install_mathjax(app, env): + # type: (Sphinx, BuildEnvironment) -> None if app.builder.format != 'html' or app.builder.math_renderer_name != 'mathjax': # type: ignore # NOQA - pass - elif not app.config.mathjax_path: + return + if not app.config.mathjax_path: raise ExtensionError('mathjax_path config value must be set for the ' 'mathjax extension to work') - if app.builder.format == 'html': + + if env.get_domain('math').has_equations(): # type: ignore + # Enable mathjax only if equations exists options = {'async': 'async'} if app.config.mathjax_options: options.update(app.config.mathjax_options) @@ -101,6 +104,6 @@ def setup(app): app.add_config_value('mathjax_inline', [r'\(', r'\)'], 'html') app.add_config_value('mathjax_display', [r'\[', r'\]'], 'html') app.add_config_value('mathjax_config', None, 'html') - app.connect('builder-inited', builder_inited) + app.connect('env-check-consistency', install_mathjax) return {'version': sphinx.__display_version__, 'parallel_read_safe': True} diff --git a/tests/test_ext_math.py b/tests/test_ext_math.py index 738ad8ce68b..5ccaa6047bb 100644 --- a/tests/test_ext_math.py +++ b/tests/test_ext_math.py @@ -255,7 +255,7 @@ def test_math_compat(app, status, warning): [nodes.math_block, "E = mc^2"])) -@pytest.mark.sphinx('html', testroot='basic', +@pytest.mark.sphinx('html', testroot='ext-math', confoverrides={'extensions': ['sphinx.ext.mathjax'], 'mathjax_config': {'extensions': ['tex2jax.js']}}) def test_mathjax_config(app, status, warning): @@ -265,3 +265,22 @@ def test_mathjax_config(app, status, warning): assert ('' in content) + + +@pytest.mark.sphinx('html', testroot='basic', + confoverrides={'extensions': ['sphinx.ext.mathjax']}) +def test_mathjax_is_not_installed_if_no_equations(app, status, warning): + app.builder.build_all() + + content = (app.outdir / 'index.html').text() + assert 'MathJax.js' not in content + + +@pytest.mark.sphinx('html', testroot='basic', + confoverrides={'extensions': ['sphinx.ext.jsmath'], + 'jsmath_path': 'jsmath.js'}) +def test_jsmath_is_not_installed_if_no_equations(app, status, warning): + app.builder.build_all() + + content = (app.outdir / 'index.html').text() + assert 'jsmath.js' not in content