MathJax icon indicating copy to clipboard operation
MathJax copied to clipboard

esm.js is broken with Node v21.4.0; mathjax-full@3 is unusable from node

Open artagnon opened this issue 2 years ago • 4 comments

Issue Summary

When I run node -r esm mathjax.js, an invocation that used to work fine for years, I now get an error from esm.js:

TypeError: Function.prototype.apply was called on undefined, which is a undefined and not a function

The issue has nothing to do with MathJax, except for the fact that mathjax-full@3 requires esm on node. ESM is broken, and no longer supported on the latest version of Node.

Steps to Reproduce:

Run node -r esm test.js with the latest version of node and esm, with the following test.js:

console.log("Hello World!");

Technical details:

Latest versions of all packages.

Possible resolution:

Release [email protected] on npm, which doesn't require esm. This issue is a blocker for MathJaX on Node.

artagnon avatar Dec 22 '23 12:12 artagnon

The esm extension is not actually required to use MathJax unless you are loading the components from source (components/src) rather than from the webpacked versions in the es5 directory. Since require('mathjax').init({...}) loads from components/src, that does mean you have to use one of the other methods of loading MathJax (e.g., via loading the webpacked component files, or the direct access to the MathJax modules in the js directory). These are illustrate in the component, preload, and direct directories of the MathJax node demos repository. The components examples allow either loading from source or from the webpacked components, and the default is from source, which is why they include -r esm, but if you use the --dist option, you can run them without -r esm. The direct versions never used -r esm to start with.

If you wish to use the equivalent of require('mathjax').init({...}) but without -r esm, you can use

require('mathjax/es5/node-main.js').init({...}).then(mj => ...);

instead, as that will use the webpacked version, not the source version.

dpvc avatar Dec 27 '23 14:12 dpvc

Thanks, I totally forgot that MathJaX is webpacked. I converted my mathjax.js to an ESM file, using the webpacked MathJaX. However, there seems to be a small problem now: xypic.js (from XyJaX-v3) is suddenly not found, although it's in the same directory as mathjax.mjs. Could you maybe give me a hint about what went wrong? My mathjax.mjs is:

import { readFileSync, writeFileSync } from "fs";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import "colors";
import ProgressBar from "progress";
import * as mathjax from "mathjax-full/es5/node-main.js";

const argv = yargs(hideBin(process.argv)).argv;

mathjax
  .init({
    options: {
      typesetError: (_, math, err) => console.log(math.math + ": " + err),
    },
    loader: {
      paths: { mathjax: "mathjax-full/es5", custom: "." },
      load: ["input/tex-full", "output/chtml", "[custom]/xypic"],
    },
    tex: {
      packages: { "[+]": ["xypic"] },
      inlineMath: [["$", "$"]],
    },
    chtml: {
      fontURL:
        "https://cdn.jsdelivr.net/npm/mathjax@3/es5/output/chtml/fonts/woff-v2",
    },
  })
  .then((MathJax) => {
    let progress = new ProgressBar(
      `[${"TeX".green} ]: |:bar| :current/:total`,
      {
        total: argv._.length,
        incomplete: " ",
      }
    );
    argv._.forEach((r) => {
      // Read in the HTML file
      const html = (MathJax.startup.document = MathJax.startup.getDocument(
        readFileSync(r, "utf8")
      ));

      // xypic has used the adaptor
      const adaptor = MathJax.startup.adaptor;

      // Clear the font cache
      html.outputJax.clearCache();

      // Typeset the document, with the render hooks that xypic has put in place
      html.clear().render();

      // Output the resulting HTML in-place
      writeFileSync(
        r,
        adaptor.doctype(html.document) +
        adaptor.outerHTML(adaptor.root(html.document))
      );
      progress.tick();
    });
  })
  .catch((err) => console.log(err));

The error is:

$ node lib/mathjax.mjs
MathJax([custom]/xypic): Cannot find module './xypic.js'
Require stack:
- /Users/artagnon/src/artagnon.com/node_modules/mathjax-full/es5/node-main.js

Thanks!

artagnon avatar Dec 27 '23 15:12 artagnon

Ah, I fixed it. I was missing require.

require: (url) => import(url)

in the loader fixes the issue.

artagnon avatar Dec 27 '23 15:12 artagnon

OK, glad that worked for you. If I recall correctly, node-main.js sets things up so that the root directory is the directory from which node-main.js was loaded, so that it can find the MathJax components properly. Since MathJax can be loaded as mathjax-full or mathjax, it doesn't know which of those it is, so can't use normal package resolution, and it doesn't have to be in a node package, in any case.

dpvc avatar Dec 27 '23 18:12 dpvc