.. _node-components: ################################ Using MathJax Components in Node ################################ It is possible to use MathJax in a node application in essentially the same way that it is used in a browser. In particular, you can load MathJax components and configure MathJax using a global :js:data:`MathJax` object and load a :ref:`combined component ` file or the :ref:`startup-component` component via node's ``import`` or ``require()`` commands. First :ref:`get a copy of the MathJax code library `. Here, we will assume you have used ``npm`` or ``pnpm`` to install the ``@mathjax/src@4`` package. You will need to change the ``require()`` or ``import`` statements accordingly in the examples below if you have loaded ``mathjax@4`` or obtained MathJax from the ``MathJax-src`` GitHub repository. In MathJax, the loading of components is asynchronous, and so you may need to use promises or the ``await`` command to mediate the flow of your program, particularly program startup. Once MathJax's components are loaded, however, you can call the non-promise-based functions, but should use the promise-based ones if you want to support autoloading of extensions, the ``\require`` macro in TeX input, or the v4 fonts with larger character coverage. .. warning:: In MathJax v4, with the introduction of new fonts that include many more characters than the original MathJax TeX fonts did, the fonts have been broken into smaller pieces so that your readers don't have to download the entire font and its data for characters that may never be used. That means that typesetting mathematics may need to operate asynchronously even if the TeX *doesn't* include ``\require`` or any auto-loaded extensions, as the output itself could need extra font data files to be loaded. Thus in version 4, it is always best to use the promise-based commands. The :ref:`synchronous examples ` show how to operate synchronously from the outset, if that is required. ----- Configuring and Loading Components in Node ========================================== As with MathJax in a browser, using MathJax components in a node application consists of two steps: configuring MathJax, and Loading a MathJax combined component to process the configuration. Just as in a browser, the configuration is specified in the global :js:data:`MathJax` variable. .. _web-vs-node: Configuration for Node vs. the Web ---------------------------------- If you are using MathJax components as part of a larger application that will be bundled for use in a web browser, then your MathJax configuration should be the same as the configuration you would use if you were loading MathJax into the web page directly, with one exception: you may need to provide the URL where MathJax should load any extensions that are needed once MathJax is running. Normally, MathJax determines that URL based on the ``src`` attribute of the ``script`` tag that loaded it, but since MathJax is part of your larger application, that URL is probably not appropriate, so you will need to specify the correct one yourself. This is done using the :js:data:`mathjax` property of the :js:data:`paths` section in the :js:data:`loader` block of your MathJax configuration. For example, .. code-block:: javascript global.MathJax = { loader: { paths: { mathjax: 'https://cdn.jsdelivr.net/npm/mathjax@4' } } } would set things up so that extensions would be loaded from the ``jsDelivr`` CDN. You could, of course, make the extensions available on your own server and set the URL to point to that. If you are using MathJax components as part of a server-side or command-line application, you should set the :js:data:`mathjax` path to ``@mathjax/src/bundle`` instead: .. code-block:: javascript global.MathJax = { loader: { paths: { mathjax: '@mathjax/src/bundle' } } } so that MathJax will take additional components from the ``bundle`` directory. .. note:: In version 4, the ``bundle`` directory replaces the ``es5`` directory from version 3. For non-browser applications, there are two additional steps you need to take. First, you must tell MathJax to use ``import()`` or ``require()`` as the mechanism for loading external files, and second, you need to load a non-browser :ref:`DOM adaptor `. MathJax provides a light-weight DOM implementation (called `liteDOM`) that is sufficient for MathJax's needs without unnecessary overhead, so you probably want to use that. If you need a more full-featured DOM implementation, you can use another one, such as ``jsdom`` or ``linkedom`` (MathJax does provide adaptors for these). It is even possible to use puppeteer with headless Chrome in order to be able to access a full DOM implementation from node. Both of these features are set in the :js:data:`loader` block of your MathJax configuration object, as illustrated in the sections below. .. _node-import-configuration: Configuring MathJax for Use with ``import`` ------------------------------------------- Because the configuration must be in place before the MathJax component is loaded, if you are using ``import`` commands rather than ``require()``, that means you either need to put the MathJax configuration into a separate file to be imported before MathJax itself, or you need to use the promise-based ``import()`` function to load MathJax. So you could create a file called ``mathjax-config.mjs`` containing .. code-block:: javascript global.MathJax = { loader: { paths: {mathjax: '@mathjax/src/bundle'}, load: ['adaptors/liteDOM'], require: (file => import(file)) }, // additional configuration here }; and then use .. code-block:: javascript import './mathjax-config.js'; import '@mathjax/src/bundle/tex-chtml.js'; await MathJax.startup.promise; // your code that uses MathJax here MathJax.done(); to load the ``tex-chtml`` combined component with that configuration, and wait for MathJax to set itself up. .. note:: In MathJax v4, the speech generation is performed in web-workers (in the browser) or worker-threads (in node applications), and once these are started, they will prevent the node application from ending if they are not shut down. So v4 includes the :js:meth:`MathJax.done()` function that terminates the workers, thus allowing the node program to end. You should call this when your program is ready to end so that it can shut down properly. Alternatively, you could do .. code-block:: javascript global.MathJax = { loader: { paths: {mathjax: '@mathjax/src/bundle'}, load: ['adaptors/liteDOM'], require: (file => import(file)) }, // additional configuration here }; await import('@mathjax/src/bundle/tex-chtml.js'); await MathJax.startup.promise; // your code that uses MathJax here MathJax.done(); to include the configuration in-line before loading the ``tex-chtml`` component. .. note:: ES6 modules usually use ``import``, and ``require()`` is not available, but it is possible to define ``require()`` if you are making a command-line or server-side application. MathJax provides a file that does that for you, so if you add .. code-block:: javascript import '@mathjax/src/bundle/require.mjs'; to your code, you can then use ``require()`` as described in the following section. .. _node-locale-configuration: Configuring the Speech Locale ----------------------------- The default speech language is English, and the default Braille code is Nemeth. You can use the :js:data:`sre` block of the :js:data:`options` section of your MathJax configuration to specify a different locale or Braille version, as illustrated below. .. code-block:: javascript global.MathJax = { loader: { paths: {mathjax: '@mathjax/src/bundle'}, load: ['adaptors/liteDOM'], require: (file) => import(file) }, options: { sre: { locale: 'de' } } // additional configuration here }; await import('@mathjax/src/bundle/tex-chtml.js'); await MathJax.startup.promise; // your code that uses MathJax here MathJax.done(); which configures MathJax to produce speech strings in German rather than English. .. _node-cjs-configuration: Configuring MathJax for Use with ``require()`` ---------------------------------------------- To use MathJax components in a CommonJS module, first set up the MathJax configuration, and then ``require()`` the combined component you want to load. So you can do .. code-block:: javascript MathJax = { loader: { paths: {mathjax: '@mathjax/src/bundle'}, load: ['adaptors/liteDOM'], require: require }, // additional configuration here }; require('@mathjax/src/bundle/tex-chtml.js'); MathJax.startup.promise .then(() => { //your MathJax code here }) .catch((err) => console.error(err.message)) .then(() => MathJax.done()); to configure MathJax for use with the ``tex-chtml`` combined component, and then wait for MathJax to start up, perform your commands (with error trapping), and then shut down MathJax. .. _node-startup-component: Loading Individual Components ----------------------------- If you are using MathJax components in a server-side or command-line application, the combined components that MathJax provides may include components that you don't need (such as the menu code and expression explorer). So you may want to configure MathJax explicitly to use only the components that you need. You do this by listing the needed components in the ``load`` array of the ``loader`` section of the MathJx configuration, and then load the ``startup.js`` module rather than a combined component. For example, .. code-block:: javascript global.MathJax = { loader: { paths: {mathjax: '@mathjax/src/bundle'}, load: ['input/tex', 'output/svg', 'adaptors/liteDOM'], require: (file => import(file)), }, output: {font: 'mathjax-newcm'} } await import('@mathjax/src/bundle/startup.js'); await MathJax.startup.promise; would load only the TeX input jax and the SVG output jax, along with the ``liteDOM`` adaptor, but without loading the menu code, the assistive tools, or any other components. Because the ``input/tex`` component includes the :ref:`require ` and :ref:`autoload ` extensions, the TeX that you process could still load TeX extensions that are needed. Because the ``output/svg`` component does not include a font, you need to configure that separately in the :js:data:`output` section of the configuration, as shown. .. _node-load-source: Loading MathJax Components from Source -------------------------------------- The examples above all load the webpacked versions of MathJax's components. It is possible to load the files from the source ``.js`` files in the ``mjs`` or ``cjs`` directories, which may be useful if you are modifying the MathJax source files and want to test your changes without having to repack all the components. To do this, you should set the :js:data:`source` mapping in the :js:data:`loader` section of the MathJax configuration, and then load the combined component from its source file in the ``components`` directory rather than the ``bundle`` directory. The :file:`source.js` file in ``components/mjs`` or ``components/cjs`` directory contains the mapping of component names to their source definitions, and you can use that to set the :js:data:`source` field of your MathJax configuration. You can obtain the :file:`source.js` file using :file:`@mathjax/src/components/js/source.js`, and it will select the ``mjs`` or ``cjs`` directory depending on whether you use ``import`` or ``require()`` to load it. So for use in ES6 modules, you can do .. code-block:: javascript import {source} from '@mathjax/src/components/js/source.js'; import '@mathjax/src/bundle/require.mjs'; // needed by speech-rule engine global.MathJax = { loader: { paths: {mathjax: '@mathjax/src/bundle'}, load: ['adaptors/liteDOM'], require: (file => import(file)), source: source } // additional configuration here } await import(source['tex-chtml']); await MathJax.startup.promise; // your code that uses MathJax here MathJax.done(); while for CommonJS modules, you can do .. code-block:: javascript const {source} = require('@mathjax/src/components/js/source.js'); MathJax = { loader: { paths: {mathjax: '@mathjax/src/bundle'}, load: ['adaptors/liteDOM'], require: require, source: source } // additional configuration here } require(source['tex-chtml']); MathJax.startup.promise .then(() => { //your MathJax code here }) .catch((err) => console.error(err.message)) .then(() => MathJax.done()); ----- .. _node-load-component: Calling MathJax from Components =============================== Once you have loaded a combined component file (or the ``startup`` component), you can use the normal MathJax commands to typeset mathematics. For example, in a browser application, you can call :js:meth:`MathJax.typesetPromise()` to typeset the page. For a command-line application, you could do .. code-block:: javascript const EM = 16; // size of an em in pixels const EX = 8; // size of an ex in pixels const WIDTH = 80 * EM; // width of container for linebreaking function typeset(math, display = true) { return MathJax.tex2svgPromise(math, { display: display, em: EM, ex: EX, containerWidth: WIDTH }).then((node) => { const adaptor = MathJax.startup.adaptor; return(adaptor.serializeXML(adaptor.tags(node, 'svg')[0])); }).catch(err => console.error(err)); } to define a :meth:`typeset()` command that takes a TeX string and an optional boolean that specifies whether the typesetting should be in display mode or in-line mode and returns a promise that is resolved when the typesetting is complete (while handling any waiting that had to be done to load extensions, fonts, etc.). The :meth:`typeset()` promise returns the serialized SVG output, so that you could do .. code-block:: javascript const svg = await typeset('\\sqrt{1+x^2}'); to get the SVG output. See the :ref:`stand-alone-svg` section for an example of generating SVG images that handles the CSS needed by some expressions in MathJax. ---- .. _node-component-example: Examples of Components in Node ============================== The following combines some of the ideas described above into a single, complete example of a command-line tool that takes three arguments: a TeX string to typeset, the language locale to use, and the Braille format to use. The last two are optional, and default to ``en`` and ``nemeth``. .. code-block:: javascript :linenos: global.MathJax = { loader: { paths: {mathjax: '@mathjax/src/bundle'}, load: ['adaptors/liteDOM'], require: (file) => import(file) }, options: { sre: { locale: process.argv[3] || 'en', braille: process.argv[4] || 'nemeth' } }, output: { linebreaks: { inline: false, } }, // additional configuration here }; await import('@mathjax/src/bundle/tex-svg.js'); await MathJax.startup.promise; const EM = 16; // size of an em in pixels const EX = 8; // size of an ex in pixels const WIDTH = 80 * EM; // width of container for linebreaking function typeset(math, display = true) { return MathJax.tex2svgPromise(math, { display: display, em: EM, ex: EX, containerWidth: WIDTH }).then((node) => { const adaptor = MathJax.startup.adaptor; return(adaptor.serializeXML(adaptor.tags(node, 'svg')[0])); }).catch(err => console.error(err)); } const math = process.argv[2] || ''; const svg = await typeset(math); console.log(svg); MathJax.done(); See the :ref:`stand-alone-svg` section for an example of generating SVG images that handles the CSS needed by some expressions in MathJax. See the `MathJax node demos `__ for more examples of how to use MathJax from a node application. In particular, see the `component-based examples `__ for illustrations of how to configure and load MathJax components. |-----|