mathjax 3 + xyJax + fabric.js (SVG only) _ migration to mathax 4 (part 2)

30 views
Skip to first unread message

jox joe

unread,
Jan 20, 2026, 12:36:18 PMJan 20
to MathJax Development
Hi Davide,

001*****************
My plan among others is to develop a MathJax4 extension.
This is why the migration from MathJax 3 to 4 is important.

002*****************
We spent time analysing the error messages.
I think the main problem is the wrong installation.

003***************** 
The pnpm compiler creates the following folder structure:

mathjax
   bundle
   cjs
   components
   lab
   mjs
   node-modules
   testsuite
   ts
   tsconfig
   .github
   .husky
   CONTRIBUTING.md
   eslint.config.mjs
   LICENCE
   packege.json
   pnpm-lock.yaml
   README.md
   tsconfig.json
   tslint.json
   typedoc.json
   .gitignore
   .prettierrc

My question:
Which folders do I need for the next step?
This means which folders/files can I delete?
This is the most important information for now.

004*****************
@ and # symbols are not recognised
by browserify

I resolved them manually as follows:

001/init.js *****************************************************************
var init_js_1 = require(".../wb/mathjax/components/cjs/startup/init.js");

//var global_js_1 = require("#js/components/global.js");
var global_js_1 = require(".../mathjax/cjs/components/global.js");
//var loader_js_1 = require("#js/components/loader.js");
var loader_js_1 = require(".../mathjax/cjs/components/loader.js");

002/loader.js****************************************************************
var loader_js_1 = require(".../mathjax/cjs/components/loader.js");

//var root_js_1 = require("#root/root.js");
var root_js_1 = require("./cjs/root.js");

003/core.js ****************************************************************
require(".../mathjax/components/cjs/core/core.js");

//var HTMLHandler_js_1 = require("#js/handlers/html/HTMLHandler.js");
var HTMLHandler_js_1 = require("../../../cjs/handlers/html/HTMLHandler.js"); //drjo cjs

//var browserAdaptor_js_1 = require("#js/adaptors/browserAdaptor.js");
var browserAdaptor_js_1 = require("../../../cjs/adaptors/browserAdaptor.js");

004/mathjax.js****************************************************************
var mathjax = require('.../mathjax/cjs/mathjax.js');//requested by davide
console.log(Array.from(Object.keys(mathjax)));
NO MODIFICATION WAS NEEDED

005/tex.js ****************************************************************
require(".../mathjax/components/cjs/input/tex/tex.js");

//var loader_js_1 = require("#js/components/loader.js");
var loader_js_1 = require("../../../../cjs/components/loader.js");

006/svg.js ****************************************************************
var svg_js_1 = require(".../mathjax/components/cjs/output/svg/svg.js");

//var svg_js_1 = require("#js/output/svg.js");
var svg_js_1 = require(" ../../../../cjs/output/svg.js");

//var DefaultFont_js_1 = require("#js/output/svg/DefaultFont.js");
var DefaultFont_js_1 = require("../../../../cjs/output/svg/DefaultFont.js");

007/autoload.js ****************************************************************
require(".../mathjax/components/cjs/input/tex/extensions/autoload/autoload.js");
NO MODIFICATION WAS NEEDED

008/browserAdaptor.js ****************************************************************
require('.../wb/mathjax/cjs/adaptors/browserAdaptor.js');
NO MODIFICATION WAS NEEDED

009/hasown.js****************************************************************
.../mathjax/components/cjs/startup/hasown.js

//var global_js_1 = require("#js/components/global.js");
var global_js_1 = require(".../wb/mathjax/cjs/components/global.js");

010/util.js ****************************************************************
.../mathjax/components/cjs/output/util.js

//var global_js_1 = require("#js/components/global.js");
var global_js_1 = require(".../mathjax/cjs/components/global.js");

//var package_js_1 = require("#js/components/package.js");
var package_js_1 = require(".../mathjax/cjs/components/package.js");

//var context_js_1 = require("#js/util/context.js");
var context_js_1 = require(".../mathjax/cjs/util/context.js");

011/DefaultFont.js****************************************************************
.../mathjax/cjs/output/svg/DefaultFont.js

//var default_js_1 = require("#default-font/svg/default.js");
var default_js_1 = require(".../mathjax/node_modules/@mathjax/mathjax-newcm-font/cjs/svg/default.js");

012/register.js****************************************************************
.../mathjax/components/cjs/input/tex/register.js

//var Options_js_1 = require("#js/util/Options.js");
var Options_js_1 = require(".../mathjax/cjs/util/Options.js");

****************************************************************
Error: Can't walk dependency graph: Cannot find module '@mathjax/src/cjs/output/svg/FontData.js'
from 'mathjax/node_modules/.pnpm/@mathjax+mathj...@4.1.0/node_modules/@mathjax/mathjax-newcm-font/cjs/svg.js'

//var FontData_js_1 = require("@mathjax/src/cjs/output/svg/FontData.js");
var FontData_js_1 = require(".../mathjax/node_modules/@mathjax/src/cjs/output/svg/FontData.js/FontData.js");

My question:
Are these manual substitutions correct?
I think they are not.

005*****************
SOME CRITICAL PLACES:
****************************************************************
util.js
var path = (config.fontPath || "[fonts]/%%FONT%%-font".concat(extension));
****************************************************************
Cannot find module '#default-font/svg/default.js' from '...mathjax/cjs/output/svg/DefaultFont.js'
****************************************************************
Error: Can't walk dependency graph: Cannot find module '@mathjax/src/cjs/output/svg/FontData.js'
from '...mathjax/node_modules/.pnpm/@mathjax+mathj...@4.1.0/node_modules/@mathjax/mathjax-newcm-font/cjs/svg.js'
*****************************************************************

006*****************
In the Mathjax configuration block we use the /bundle folder
The problem is that this does not support my deeper understanding.
QUESTION:
How can I do this using only the components without the bundle?

007*****************
If I can't recompile and install Mathjax 4.1.0 properly
I won't be able to learn how Mathjax 4 works.
Thank you for your great support.

008*****************
I assume that the automatic conversion
from ts/mjs to cjs is perfect.
For the time beeing, I need to stick to cjs.

Joe





jox joe

unread,
Jan 21, 2026, 3:19:39 PM (14 days ago) Jan 21
to MathJax Development
Hi Davide,
some testing-related information:


A/ ****************************
This is the text string:
\mathsf{gggg}

Message on the browser console:  
 FontData.prototype.loadDynamicFile = function (dynamic) {
        return __awaiter(this, void 0, void 0, function () {
            var _this = this;
            return __generator(this, function (_a) {
                if (dynamic.failed)
                    return [2, Promise.reject(new Error("dynamic file '".concat(dynamic.file, "' failed to load")))];
                if (!dynamic.promise) {
                    dynamic.promise = (0, AsyncLoad_js_1.asyncLoad)(this.dynamicFileName(dynamic)).catch(function (err) {
                        dynamic.failed = true;
                        console.warn(err); //SEE THE ERROR MESSAGE BELOW
                    });
                }
                return [2, dynamic.promise.then(function () { return dynamic.setup(_this); })];
            });
        });
    };
        
    //THE ERROR MESSAGE:
    wb-ver-1769014933000.js:91499 Can't load '[mathjax-newcm]/svg/dynamic/sans-serif.js': No mathjax.asyncLoad method specified
   
    Message displayed by  _tex2svg on the browser console:  
    _tex2svg  dynamic file 'sans-serif' failed to load
   
   
    
B/ ****************************     
    var mathjax = require('/mathjax/cjs/mathjax.js');
console.log(Array.from(Object.keys(mathjax)));


I can't see anything on the browser console.
    

C/ ****************************    
A message on the browser console:
   
wb-ver-1769019899000.js:107328 MathJax: Invalid option "menuOptions" (no default value).


D/ ****************************
the test string: 
\boldsymbol{ffffffffff}

Error message:
/mathjax/node_modules/@mathjax/src/bundle/input/tex/extensions/boldsymbol.js net::ERR_ABORTED 404 (Not Found)

output:
\the "\boldymbol ffffffffff"  (the boldsymbol is red)

Joe

jox joe

unread,
Jan 22, 2026, 6:21:52 AM (13 days ago) Jan 22
to MathJax Development
Hi Davide,

001*********************
var mathjax = require('.../mathjax/cjs/mathjax.js');//requested by davide
console.log(Array.from(Object.keys(mathjax))); 

I can see a large array on the console now.
It is an array containing a lot of elements
but none of them are related to dynamic load, I think. 

002*********************
I'm going to reinstall everything
on the server again. I'll let you know the result. 


003*********************
I think autoload is not working in this
environment.
Is this statement correct?

joe

jox joe

unread,
Jan 23, 2026, 7:50:31 AM (12 days ago) Jan 23
to MathJax Development
Hi Davide,
**********************************
**********************************
001: *****************************
Installed SW:
MathJax 4.1.0 (recompiled)
CJS version
SVG only 

**********************************
**********************************
002: *****************************
I reinstalled everything.
This is the most succesfull installation.
I can see less error messages than before.  
Examples:
1:
\boldsymbol(kkkkkk) is working properly
2:
\style{transform: scale(1.2 , 1.2) rotate(45)} {jjjjjjjj}
This is working as expected.
Further testing is in progress.

*************************
*************************
003: *********************
var mathjax = require('.../mathjax/cjs/mathjax.js');//requested by davide
console.log(Array.from(Object.keys(mathjax))); 

I can see a large array on the console now.
It is an array containing a lot of elements
but none of them are related to dynamic load, I think. 

*************************
*************************
004: *********************
This seems to be the most critical error:

tex string:
\mathsf{ggggggg}

output/shape on the screen:
nothing is displayed

relevant source code:

    FontData.prototype.loadDynamicFile = function (dynamic) {
        return __awaiter(this, void 0, void 0, function () {
            var _this = this;
            return __generator(this, function (_a) {
                if (dynamic.failed)
                    return [2, Promise.reject(new Error("dynamic file '".concat(dynamic.file, "' failed to load")))];
                if (!dynamic.promise) {
                    dynamic.promise = (0, AsyncLoad_js_1.asyncLoad)(this.dynamicFileName(dynamic)).catch(function (err) {
                        dynamic.failed = true;
                        console.warn(err); //!!! SEE THE ERROR MESSAGE BELOW !!!

                    });
                }
                return [2, dynamic.promise.then(function () { return dynamic.setup(_this); })];
            });
        });
    };
   
ERROR MESSAGE:
wb-ver-1769122863000.js:92171 Can't load '[mathjax-newcm]/svg/dynamic/sans-serif.js': No mathjax.asyncLoad method specified

The error message from _tex2svg:
_tex2svg  dynamic file 'sans-serif' failed to load

MY GUESS:
option 1: files are missing
option 2: the generated CJS version
is not able to handle the dynamic load
option 3: bad Mathjax configuration block
option 4: ???

*************************
*************************
005: *********************
QUESTION:
What should I put in the Mathjax configuration section
if I want to use the old Mathjax 3 default font?
(There is no dynamic file load in this case.)

Thank you.
Joe

Davide Cervone

unread,
Jan 23, 2026, 1:13:08 PM (12 days ago) Jan 23
to mathj...@googlegroups.com
We spent time analysing the error messages.
I think the main problem is the wrong installation.

Yes, I would say that is probably correct.

My question:
Which folders do I need for the next step?
This means which folders/files can I delete?

For use on the web, only the bundle folder is needed.  For compiling and building, you need most of the rest of it.

004*****************
@ and # symbols are not recognised
by browserify

You are correct that the # paths (that are specified by the "imports" block of the package.json) don't seem to be processed by browserify, but the paths starting with @ are fine.  That means you can replace "#js" with "@mathjax/src/cjs" rather than needing to use lots of dots for the path.

But I would recommend that you not modify the MathJax code at all, and instead use a browserify global transform module to replace the # paths on the fly.  That means you don't have to change the files, and so won't possibly get them wrong or miss any.  For example, you can create a file called transform.js that contains

const { Transform } = require('stream');

module.exports = function (file) {
  return new Transform({
    transform(chunk, enc, next) {
      if (file.match('@mathjax/src')) {
        chunk = String(chunk)
          .replace(/require\("#js/g,           'require("@mathjax/src/cjs')
          .replace(/require\("#source/g,       'require("@mathjax/src/components/cjs')
          .replace(/require\("#root/g,         'require("@mathjax/src/cjs/components/cjs/')
          .replace(/require\("#menu/g,         'require("mj-context-menu/cjs')
          .replace(/require\("#sre/g,          'require("speech-rule-engine/cjs')
          .replace(/require\("#mhchem/g,       'require("mhchemparser/dist')
          .replace(/require\("#default-font/g, 'require("@mathjax/mathjax-newcm-font/cjs');
      }
      this.push(chunk);
      next();
    }
  });
}

and then use "browserify -g ./transform.js ..." to bundle your code and it will take care of fixing the # paths for you.

Are these manual substitutions correct?
I think they are not.

I did not look through them, a the transform above is the way to do this.

Cannot find module '#default-font/svg/default.js' from '...mathjax/cjs/output/svg/DefaultFont.js'

The transform above takes care of this.

In the Mathjax configuration block we use the /bundle folder
The problem is that this does not support my deeper understanding.

I'm not sure what that means.  The bundled files are necessary for use with dynamic loading in the browser.  The code for them is mostly in the ts directory and it subdirectory, but there are some small control files in the components/mjs directory (its subdirectories correspond to the bundles themselves) that actual create the bundles.  You can see what files correspond to each bundle.  You can rebuild them using the package scripts in the package.json file.  "pnpm -s build-all" will compile and bundle all of MathJax's components.

How can I do this using only the components without the bundle?

If you want to support dynamic loading of components in a browser, you can't do without the bundles.  Those are what make that possible.

Otherwise, you would have to include the needed files into your browserify bundle as you do the xyjax and img extensions.  That is possible, but will enlarge your main file perhaps unnecessarily.

A message on the browser console:
   
wb-ver-1769019899000.js:107328 MathJax: Invalid option "menuOptions" (no default value).

The code snippet you original provided does not include the ui/menu component, which is what defined the menuOptions block.  Without the ui/menu component, this block is illegal, so this is the correct response to a configuration that include it.

the test string: 
\boldsymbol{ffffffffff}

Error message:
/mathjax/node_modules/@mathjax/src/bundle/input/tex/extensions/boldsymbol.js net::ERR_ABORTED 404 (Not Found)

output:
\the "\boldymbol ffffffffff"  (the boldsymbol is red)

Have you checked that /mathjax/node_modules/@mathjax/src/bundle/input/tex/extensions/boldsymbol.js is actually there?  If not, then find out where it is, and that can guide you to where your configuration needs to be changed (the mathjax path is probably wrong, or you haven't moved the fils to the server in the proper location).

Installed SW:
MathJax 4.1.0 (recompiled)
CJS version
SVG only 

You should not need to recompile MathJax unless you are changing the source code.  if you are, then problems may be the fault of your changes, and I can't help you with that.

var mathjax = require('.../mathjax/cjs/mathjax.js');//requested by davide
console.log(Array.from(Object.keys(mathjax))); 

I can see a large array on the console now.

There should only be 8 or so entries in the array.  Something like

"version""context""handlers""document""handleRetriesFor""retryAfter""asyncLoad""asyncIsSynchronous" ]

Note the presence of "asyncLoad".  Without seeing your list, I can' tell you what might be going wrong.

ERROR MESSAGE: 
wb-ver-1769122863000.js:92171 Can't load '[mathjax-newcm]/svg/dynamic/sans-serif.js': No mathjax.asyncLoad method specified

If "asyncLoad" is not in your array like the one I list above, then that is the problem, and it suggests that your bundle is not properly including components/cjs/core/core.js where the asyncLoad function is defined.

option 1: files are missing

That is certainly possible.  You don't list your directory structure or what files you have moved, so I can't tell.

option 2: the generated CJS version
is not able to handle the dynamic load

That is false.  dynamic loading works fine in both cjs and mjs.  This is a configuration or installation problem with your setup.

option 3: bad Mathjax configuration block

That is also possible.  You haven't given your current configuration, so I can't tell.

___________________________________

I finally broke down and installed browserify to test out your code, and it worked fine for me.  So there is something different in your installation that you aren't telling me about, and so I can't point it out to you.  Here is what I did:

mkdir project
cd project
pnpm install browserify
pnpm install @mathjax/src
pnpm install @mathjax/mathjax-newcm-font
ln -s node_modules/@mathjax/src/bundle mathjax
ln -s node_modules/@mathjax/mathjax-newcm-font mathjax-newcm

The last two lines link top-level directories for the Mathjax bundle and newcm font directories.  If you can't do symbolic links, just copy those directories to the top-level ones.

Then I created the transform.js file that I listed above.  Then I made a file  main.js containing

////BEGIN code
global.MathJax = {
  loader: {
    paths: {
      mathjax: './mathjax',
      'mathjax-newcm': './mathjax-newcm',
    }
  },
  options: {},
  tex: {
//    packages: {
//      '[+]': ['xypic', 'img']
//    }
//    ,
    processEnvironments: true,
    maxBuffer: 200 * 1024,
    maxMacros: 40000,
    noundefined: {
      color: 'red',
      background: '',
      size: ''
    }, //END , noundefined: 
    formatError(_, error) {
      throw error;
    },
    macros: {
      //
      RR: ['{\\bf{R}}'],
      bold: ['\\boldsymbol{#1}', 1],
      ddx: ['\\frac{d#2}{d#1}', 2, 'x'],
      abc: ['(#1)', 1, [null, '\\cba']],
      ud: ['{#1^ \\dagger}', 1],
      Tr: ['{\\mathrm{Tr}}'],
      bold2: ['{\\bf #1}', 1],
      c1y: ['{\\color{yellow}{#1}}'],
      cy: ['{\\color{yellow}#1}'],
      ktc: ['{\\color{yellow}{\\boldsymbol{\\mathsf{#1}}}}', 1],
      b: ['{\\boldsymbol{\\mathsf{#1}}}', 1]
    } //END , macros: 
  }, //END , tex:
  svg: {
    fontCache: 'local'
  }
}; //END MathJax =


var init_js_1 = require("@mathjax/src/components/cjs/startup/init.js");
var loader_js_1 = require("@mathjax/src/cjs/components/loader.js");
require("@mathjax/src/components/cjs/core/core.js");
require("@mathjax/src/components/cjs/input/tex/tex.js");
var svg_js_1 = require("@mathjax/src/components/cjs/output/svg/svg.js");
require('@mathjax/src/cjs/adaptors/browserAdaptor.js'); 
require('@mathjax/src/cjs/ui/menu/Menu.js');
//require('@mathjax/src4/build/xypic.js'); 
//require('@mathjax/src4/mathjax-img-main/browser/img.js');
loader_js_1.Loader.preLoaded('init', 'loader', 'startup', 'core', 'input/tex', 'output/svg' /*, 'xypic' 'img' */);
loader_js_1.Loader.saveVersion('tex-svg');
(0, svg_js_1.loadFont)(init_js_1.startup, true);

//this creates the stg
function _tex2svg(tex, callback, _errCallback) {
  MathJax.tex2svgPromise(tex, {
    display: false,
    em: 16, // em-size in pixels
    ex: 8, // ex-size in pixels
    containerWidth: 80 * 16
  }).then(function (node) {
    callback(node);
  }).catch(function (err) {
    _errCallback(err.message);
  });
}
////END code

global.tex2svg = _tex2svg;

which was your code from one of you earlier messages, but with the correct loader.js file being used, and the added global definition at the bottom.  I commented out the xypic and img packages, since I didn't take time to set them up.  I assume you can handle that yourself.  I also added the loader paths for mathjax and mathjax-newcm.

Then I did

pnpm browserify -g ./transform.js main.js -o tex2svg.js

to bundle the code.

Next I created a test HTML file called main.html:

<!DOCTYPE html>
<html>
<head>
<title>Test of browserify</title>
<script src="tex2svg.js"></script>
<script>
function typeset() {
  const tex = document.querySelector("#tex").value;
  const output = document.querySelector("#output");
  tex2svg(tex, (html) => {
    output.innerHTML = "";
    output.appendChild(html);
  });
}
</script>
</head>
<body>

<textarea id="tex" rows="10" cols="60"></textarea><br>
<input type="button" value="Typeset" onclick="typeset()">
<br><br>
<div id="output"></div>

</body>
</html>

Now you should be able to load this file in a browser, type some math in the text area and press the "Typeset" button to get the output.  You should be able to use \boldsymbol, \mathsf, etc, with no problem.

To run this on a server, move tex2svg.js and main.html to an appropriate location on the web server, and copy node_modules/@mathjax/src/bundle to a folder named mathjax in the same directory as main.html, and node_modules/@mathjax/mathjax-newcm-font to mathjax-newcm in the same directory as main.html.  Then open main.html (from the server) in your browser. 

What should I put in the Mathjax configuration section
if I want to use the old Mathjax 3 default font?

First, 

pnpm install @mathjax/mathjax-tex-font

and then edit the transform.js file to change the line for #default-font to be

          .replace(/require\("#default-font/g, 'require("@mathjax/mathjax-tex-font/cjs');

You are correct that this will not need dynamic loading, but the original setup that I had does the dynamic loading properly, so perhaps you can use that after all.  Note that mathjax-newcm font has much greater character coverage than mathjax-tex, so if you switch, you may have to be more careful about what symbols you use.
_________________________

At this point, I have done as much free support as I can offer you.  I have provided a working example based on your code that you should be able to extend to your situation.  If you need additional support, we can discuss privately the option of contracting for that, but I have spent more time on this than I should have, and will not be able to provide additional time for free.

Davide

Reply all
Reply to author
Forward
0 new messages