diff --git a/packages/rehype-mathjax/lib/chtml.js b/packages/rehype-mathjax/lib/chtml.js index 71c4b88..fe869f7 100644 --- a/packages/rehype-mathjax/lib/chtml.js +++ b/packages/rehype-mathjax/lib/chtml.js @@ -1,4 +1,19 @@ -import {CHTML as Chtml} from 'mathjax-full/js/output/chtml.js' +// CHTML font metric data — side-effect imports register metrics on the font +// class. Only math-essential files (~113 KB). See lib/svg.js for rationale. +import '@mathjax/mathjax-newcm-font/js/chtml/dynamic/accents.js' +import '@mathjax/mathjax-newcm-font/js/chtml/dynamic/arrows.js' +import '@mathjax/mathjax-newcm-font/js/chtml/dynamic/calligraphic.js' +import '@mathjax/mathjax-newcm-font/js/chtml/dynamic/double-struck.js' +import '@mathjax/mathjax-newcm-font/js/chtml/dynamic/fraktur.js' +import '@mathjax/mathjax-newcm-font/js/chtml/dynamic/marrows.js' +import '@mathjax/mathjax-newcm-font/js/chtml/dynamic/math.js' +import '@mathjax/mathjax-newcm-font/js/chtml/dynamic/mshapes.js' +import '@mathjax/mathjax-newcm-font/js/chtml/dynamic/script.js' +import '@mathjax/mathjax-newcm-font/js/chtml/dynamic/shapes.js' +import '@mathjax/mathjax-newcm-font/js/chtml/dynamic/symbols.js' +import '@mathjax/mathjax-newcm-font/js/chtml/dynamic/variants.js' + +import {CHTML as Chtml} from '@mathjax/src/js/output/chtml.js' import {createPlugin} from './create-plugin.js' import {createRenderer} from './create-renderer.js' diff --git a/packages/rehype-mathjax/lib/create-plugin.js b/packages/rehype-mathjax/lib/create-plugin.js index d3702ed..5369ec6 100644 --- a/packages/rehype-mathjax/lib/create-plugin.js +++ b/packages/rehype-mathjax/lib/create-plugin.js @@ -79,7 +79,7 @@ * // … * .use(rehypeMathjaxChtml, { * chtml: { - * fontURL: 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/output/chtml/fonts/woff-v2' + * fontURL: 'https://cdn.jsdelivr.net/npm/@mathjax/mathjax-newcm-font/chtml/woff2' * } * }) * // … diff --git a/packages/rehype-mathjax/lib/create-renderer.js b/packages/rehype-mathjax/lib/create-renderer.js index 9e1aa30..a5cd971 100644 --- a/packages/rehype-mathjax/lib/create-renderer.js +++ b/packages/rehype-mathjax/lib/create-renderer.js @@ -1,20 +1,24 @@ /** * @import {Element, Text} from 'hast' - * @import {LiteDocument} from 'mathjax-full/js/adaptors/lite/Document.js' - * @import {LiteElement} from 'mathjax-full/js/adaptors/lite/Element.js' - * @import {LiteText} from 'mathjax-full/js/adaptors/lite/Text.js' - * @import {MathDocument} from 'mathjax-full/js/core/MathDocument.js' - * @import {OutputJax} from 'mathjax-full/js/core/OutputJax.js' - * @import {HTMLHandler as HtmlHandler} from 'mathjax-full/js/handlers/html/HTMLHandler.js' + * @import {LiteDocument} from '@mathjax/src/js/adaptors/lite/Document.js' + * @import {LiteElement} from '@mathjax/src/js/adaptors/lite/Element.js' + * @import {LiteText} from '@mathjax/src/js/adaptors/lite/Text.js' + * @import {MathDocument} from '@mathjax/src/js/core/MathDocument.js' + * @import {OutputJax} from '@mathjax/src/js/core/OutputJax.js' + * @import {HTMLHandler as HtmlHandler} from '@mathjax/src/js/handlers/html/HTMLHandler.js' * @import {Options, Renderer} from './create-plugin.js' */ import {h} from 'hastscript' -import {liteAdaptor as liteAdapter} from 'mathjax-full/js/adaptors/liteAdaptor.js' -import {RegisterHTMLHandler as registerHtmlHandler} from 'mathjax-full/js/handlers/html.js' -import {AllPackages as allPackages} from 'mathjax-full/js/input/tex/AllPackages.js' -import {TeX as Tex} from 'mathjax-full/js/input/tex.js' -import {mathjax} from 'mathjax-full/js/mathjax.js' +import {liteAdaptor as liteAdapter} from '@mathjax/src/js/adaptors/liteAdaptor.js' +import {RegisterHTMLHandler as registerHtmlHandler} from '@mathjax/src/js/handlers/html.js' +import {TeX as Tex} from '@mathjax/src/js/input/tex.js' +import {mathjax} from '@mathjax/src/js/mathjax.js' +import {allPackages} from './tex-packages.js' +// No-op async loader: all font data is pre-loaded via static imports +// in the entry points (svg.js, chtml.js). This prevents MathJax from +// attempting dynamic loading at runtime. +mathjax.asyncLoad = () => Promise.resolve() /** * Create a renderer. @@ -38,6 +42,21 @@ export function createRenderer(options, output) { const adapter = liteAdapter() handler = registerHtmlHandler(adapter) document = mathjax.document('', {InputJax: input, OutputJax: output}) + // Apply pre-loaded dynamic font data to this output instance. + // The font side-effect imports in svg.js / chtml.js registered + // setup() functions on the font class via dynamicSetup(). We must + // call them on each new font instance because loadDynamicFiles() + // has a class-level `if (!df.promise)` guard that skips setup for + // instances created after the first. + const font = /** @type {any} */ (output).font + if (font) { + const dynamicFiles = font.constructor.dynamicFiles + for (const name of Object.keys(dynamicFiles)) { + const df = dynamicFiles[name] + df.promise ??= Promise.resolve() + df.setup(font) + } + } }, render(value, options) { // Cast as this practically results in an element instead of an `MmlNode`. diff --git a/packages/rehype-mathjax/lib/svg.js b/packages/rehype-mathjax/lib/svg.js index d3c73ae..cb26be4 100644 --- a/packages/rehype-mathjax/lib/svg.js +++ b/packages/rehype-mathjax/lib/svg.js @@ -1,4 +1,32 @@ -import {SVG as Svg} from 'mathjax-full/js/output/svg.js' +// SVG font glyph data — side-effect imports register SVG path strings on the +// font class for character ranges beyond the static base font. +// +// Only math-essential files are imported (~1.1 MB). Extended scripts (Cyrillic, +// Arabic, Hebrew, Braille, Cherokee, Devanagari, etc.) and extra font variants +// (sans-serif, monospace, phonetics, etc.) are omitted to keep the bundle close +// to v3's ~1 MB. Users needing those ranges can add them via a custom plugin. +// +// Full list of available files (40 total, ~9.9 MB): +// PUA, accents, accents-b-i, arabic, arrows, braille, braille-d, +// calligraphic, cherokee, cyrillic, cyrillic-ss, devanagari, double-struck, +// fraktur, greek, greek-ss, hebrew, latin, latin-b, latin-bi, latin-i, +// marrows, math, monospace, monospace-ex, monospace-l, mshapes, phonetics, +// phonetics-ss, sans-serif, sans-serif-b, sans-serif-bi, sans-serif-ex, +// sans-serif-i, sans-serif-r, script, shapes, symbols, symbols-b-i, variants +import '@mathjax/mathjax-newcm-font/js/svg/dynamic/accents.js' // 72 KB — accented math characters +import '@mathjax/mathjax-newcm-font/js/svg/dynamic/arrows.js' // 229 KB — extended arrows +import '@mathjax/mathjax-newcm-font/js/svg/dynamic/calligraphic.js' // 40 KB — \mathcal +import '@mathjax/mathjax-newcm-font/js/svg/dynamic/double-struck.js' // 42 KB — \mathbb (ℝ, ℂ, ℤ) +import '@mathjax/mathjax-newcm-font/js/svg/dynamic/fraktur.js' // 77 KB — \mathfrak +import '@mathjax/mathjax-newcm-font/js/svg/dynamic/marrows.js' // 43 KB — more arrow shapes +import '@mathjax/mathjax-newcm-font/js/svg/dynamic/math.js' // 199 KB — extra math symbols +import '@mathjax/mathjax-newcm-font/js/svg/dynamic/mshapes.js' // 45 KB — math shapes +import '@mathjax/mathjax-newcm-font/js/svg/dynamic/script.js' // 71 KB — \mathscr +import '@mathjax/mathjax-newcm-font/js/svg/dynamic/shapes.js' // 111 KB — shapes and delimiters +import '@mathjax/mathjax-newcm-font/js/svg/dynamic/symbols.js' // 178 KB — extra symbols +import '@mathjax/mathjax-newcm-font/js/svg/dynamic/variants.js' // 17 KB — TeX variant characters + +import {SVG as Svg} from '@mathjax/src/js/output/svg.js' import {createPlugin} from './create-plugin.js' import {createRenderer} from './create-renderer.js' diff --git a/packages/rehype-mathjax/lib/tex-packages.js b/packages/rehype-mathjax/lib/tex-packages.js new file mode 100644 index 0000000..d65a5e2 --- /dev/null +++ b/packages/rehype-mathjax/lib/tex-packages.js @@ -0,0 +1,94 @@ +// TeX extension side-effect imports (registers each package with MathJax). +// Includes all v4 packages except autoload (requires async asyncLoad) and +// bboldx (requires font variant not in NCM). +import '@mathjax/src/js/input/tex/action/ActionConfiguration.js' +import '@mathjax/src/js/input/tex/ams/AmsConfiguration.js' +import '@mathjax/src/js/input/tex/amscd/AmsCdConfiguration.js' +import '@mathjax/src/js/input/tex/base/BaseConfiguration.js' +import '@mathjax/src/js/input/tex/bbm/BbmConfiguration.js' +// bboldx omitted — requires -bboldx font variant not in the NCM font package. +import '@mathjax/src/js/input/tex/bbox/BboxConfiguration.js' +import '@mathjax/src/js/input/tex/begingroup/BegingroupConfiguration.js' +import '@mathjax/src/js/input/tex/boldsymbol/BoldsymbolConfiguration.js' +import '@mathjax/src/js/input/tex/braket/BraketConfiguration.js' +import '@mathjax/src/js/input/tex/bussproofs/BussproofsConfiguration.js' +import '@mathjax/src/js/input/tex/cancel/CancelConfiguration.js' +import '@mathjax/src/js/input/tex/cases/CasesConfiguration.js' +import '@mathjax/src/js/input/tex/centernot/CenternotConfiguration.js' +import '@mathjax/src/js/input/tex/color/ColorConfiguration.js' +import '@mathjax/src/js/input/tex/colortbl/ColortblConfiguration.js' +import '@mathjax/src/js/input/tex/colorv2/ColorV2Configuration.js' +import '@mathjax/src/js/input/tex/configmacros/ConfigMacrosConfiguration.js' +import '@mathjax/src/js/input/tex/dsfont/DsfontConfiguration.js' +import '@mathjax/src/js/input/tex/empheq/EmpheqConfiguration.js' +import '@mathjax/src/js/input/tex/enclose/EncloseConfiguration.js' +import '@mathjax/src/js/input/tex/extpfeil/ExtpfeilConfiguration.js' +import '@mathjax/src/js/input/tex/gensymb/GensymbConfiguration.js' +import '@mathjax/src/js/input/tex/html/HtmlConfiguration.js' +import '@mathjax/src/js/input/tex/mathtools/MathtoolsConfiguration.js' +import '@mathjax/src/js/input/tex/mhchem/MhchemConfiguration.js' +import '@mathjax/src/js/input/tex/newcommand/NewcommandConfiguration.js' +import '@mathjax/src/js/input/tex/noerrors/NoErrorsConfiguration.js' +import '@mathjax/src/js/input/tex/noundefined/NoUndefinedConfiguration.js' +import '@mathjax/src/js/input/tex/physics/PhysicsConfiguration.js' +import '@mathjax/src/js/input/tex/require/RequireConfiguration.js' +import '@mathjax/src/js/input/tex/setoptions/SetOptionsConfiguration.js' +import '@mathjax/src/js/input/tex/tagformat/TagFormatConfiguration.js' +import '@mathjax/src/js/input/tex/texhtml/TexHtmlConfiguration.js' +import '@mathjax/src/js/input/tex/textcomp/TextcompConfiguration.js' +import '@mathjax/src/js/input/tex/textmacros/TextMacrosConfiguration.js' +import '@mathjax/src/js/input/tex/unicode/UnicodeConfiguration.js' +import '@mathjax/src/js/input/tex/units/UnitsConfiguration.js' +import '@mathjax/src/js/input/tex/upgreek/UpgreekConfiguration.js' +import '@mathjax/src/js/input/tex/verb/VerbConfiguration.js' + +/** + * All TeX packages (all v4 extensions minus autoload and bboldx). + * + * Note: `physics` redefines `\div`, `\Re`, `\Im`, and trig/log functions. + * `colorv2` overrides `\color` from the `color` package. + * Both are included to match v3's AllPackages behavior. + * + * @type {ReadonlyArray} + */ +export const allPackages = [ + 'action', + 'ams', + 'amscd', + 'base', + 'bbm', + 'bbox', + 'begingroup', + 'boldsymbol', + 'braket', + 'bussproofs', + 'cancel', + 'cases', + 'centernot', + 'color', + 'colortbl', + 'colorv2', + 'configmacros', + 'dsfont', + 'empheq', + 'enclose', + 'extpfeil', + 'gensymb', + 'html', + 'mathtools', + 'mhchem', + 'newcommand', + 'noerrors', + 'noundefined', + 'physics', + 'require', + 'setoptions', + 'tagformat', + 'texhtml', + 'textcomp', + 'textmacros', + 'unicode', + 'units', + 'upgreek', + 'verb' +] diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index 0dc120f..15681fd 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -7,10 +7,10 @@ ], "dependencies": { "@types/hast": "^3.0.0", - "@types/mathjax": "^0.0.40", "hast-util-to-text": "^4.0.0", "hastscript": "^9.0.0", - "mathjax-full": "^3.0.0", + "@mathjax/mathjax-newcm-font": "^4.0.0", + "@mathjax/src": "^4.0.0", "unified": "^11.0.0", "unist-util-visit-parents": "^6.0.0", "vfile": "^6.0.0" @@ -60,12 +60,18 @@ "test-api": "node --conditions development test/index.js", "test": "npm run build && npm run test-api" }, - "sideEffects": false, + "sideEffects": [ + "./lib/svg.js", + "./lib/chtml.js", + "./lib/tex-packages.js", + "./lib/create-renderer.js" + ], "type": "module", "version": "7.1.0", "xo": { "prettier": true, "rules": { + "import/no-unassigned-import": "off", "unicorn/prefer-at": "off" } } diff --git a/packages/rehype-mathjax/readme.md b/packages/rehype-mathjax/readme.md index 3b9c5e2..28ca94d 100644 --- a/packages/rehype-mathjax/readme.md +++ b/packages/rehype-mathjax/readme.md @@ -181,7 +181,7 @@ For example: // … .use(rehypeMathjaxChtml, { chtml: { - fontURL: 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/output/chtml/fonts/woff-v2' + fontURL: 'https://cdn.jsdelivr.net/npm/@mathjax/mathjax-newcm-font/chtml/woff2' } }) // … diff --git a/packages/rehype-mathjax/test/fixture/document-svg.html b/packages/rehype-mathjax/test/fixture/document-svg.html index 765aa2f..b621cad 100644 --- a/packages/rehype-mathjax/test/fixture/document-svg.html +++ b/packages/rehype-mathjax/test/fixture/document-svg.html @@ -3,8 +3,41 @@ Math! -

Hello, !

+

Hello, !

- + \ No newline at end of file diff --git a/packages/rehype-mathjax/test/fixture/double-svg.html b/packages/rehype-mathjax/test/fixture/double-svg.html index 50174f5..db9cd22 100644 --- a/packages/rehype-mathjax/test/fixture/double-svg.html +++ b/packages/rehype-mathjax/test/fixture/double-svg.html @@ -1,7 +1,40 @@ -

Double math .

+

Double math .

+ \ No newline at end of file diff --git a/packages/rehype-mathjax/test/fixture/equation-numbering-1-chtml.html b/packages/rehype-mathjax/test/fixture/equation-numbering-1-chtml.html index e800303..e1b381c 100644 --- a/packages/rehype-mathjax/test/fixture/equation-numbering-1-chtml.html +++ b/packages/rehype-mathjax/test/fixture/equation-numbering-1-chtml.html @@ -1,9 +1,56 @@

Block math:

- -

See equation .

+𝐸=𝑚𝑐2(1) +

See equation (1).

diff --git a/packages/rehype-mathjax/test/fixture/equation-numbering-1-svg.html b/packages/rehype-mathjax/test/fixture/equation-numbering-1-svg.html index 4ff3036..d67cd66 100644 --- a/packages/rehype-mathjax/test/fixture/equation-numbering-1-svg.html +++ b/packages/rehype-mathjax/test/fixture/equation-numbering-1-svg.html @@ -1,9 +1,42 @@

Block math:

- -

See equation .

+ +

See equation .

+ \ No newline at end of file diff --git a/packages/rehype-mathjax/test/fixture/equation-numbering-2-svg.html b/packages/rehype-mathjax/test/fixture/equation-numbering-2-svg.html index 766e035..0721c2c 100644 --- a/packages/rehype-mathjax/test/fixture/equation-numbering-2-svg.html +++ b/packages/rehype-mathjax/test/fixture/equation-numbering-2-svg.html @@ -1,10 +1,43 @@

Block math:

- -

See equation .

-

Reference to an undefined equation .

+ +

See equation .

+

Reference to an undefined equation .

+ \ No newline at end of file diff --git a/packages/rehype-mathjax/test/fixture/markdown-code-fenced-svg.html b/packages/rehype-mathjax/test/fixture/markdown-code-fenced-svg.html index 84db67a..317db84 100644 --- a/packages/rehype-mathjax/test/fixture/markdown-code-fenced-svg.html +++ b/packages/rehype-mathjax/test/fixture/markdown-code-fenced-svg.html @@ -1,6 +1,40 @@ - + \ No newline at end of file diff --git a/packages/rehype-mathjax/test/fixture/markdown-svg.html b/packages/rehype-mathjax/test/fixture/markdown-svg.html index d45a34b..a4416c6 100644 --- a/packages/rehype-mathjax/test/fixture/markdown-svg.html +++ b/packages/rehype-mathjax/test/fixture/markdown-svg.html @@ -1,8 +1,41 @@ -

Inline math .

+

Inline math .

Block math:

- + \ No newline at end of file diff --git a/packages/rehype-mathjax/test/fixture/small-chtml.html b/packages/rehype-mathjax/test/fixture/small-chtml.html index e259a28..ecbd1ad 100644 --- a/packages/rehype-mathjax/test/fixture/small-chtml.html +++ b/packages/rehype-mathjax/test/fixture/small-chtml.html @@ -1,9 +1,53 @@ -

Inline math .

+

Inline math 𝛼.

Block math:

- +𝛾 diff --git a/packages/rehype-mathjax/test/fixture/small-svg.html b/packages/rehype-mathjax/test/fixture/small-svg.html index 1c10fdf..82c2cf5 100644 --- a/packages/rehype-mathjax/test/fixture/small-svg.html +++ b/packages/rehype-mathjax/test/fixture/small-svg.html @@ -1,9 +1,42 @@ -

Inline math .

+

Inline math .

Block math:

- + + \ No newline at end of file diff --git a/packages/rehype-mathjax/test/index.js b/packages/rehype-mathjax/test/index.js index b0cefe7..c71f04f 100644 --- a/packages/rehype-mathjax/test/index.js +++ b/packages/rehype-mathjax/test/index.js @@ -262,22 +262,83 @@ test('rehype-mathjax', async function (t) { ) }) - await t.test('should catch mathjax exceptions', async function () { - const file = await unified() + await t.test( + 'should render dynamic font characters (e.g. double-struck)', + async function () { + const value = String( + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxSvg) + .use(rehypeStringify) + .process('\\mathbb{R}') + ) + + // Double-struck R requires the dynamic `double-struck` font file. + // If it loaded correctly, the SVG output contains path data. + assert.match(value, /path.*d="/) + } + ) + + await t.test( + 'should render undefined commands gracefully (mathjax v4)', + async function () { + const file = await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxSvg) + .use(rehypeStringify) + .process('\\a{₹}.') + + const value = String(file).replace(/