From 5afd06dd4634581b3dcc4d1dbcbbe9278996346b Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 14 Oct 2020 13:31:09 +0200 Subject: [PATCH 01/98] Update dev-dependencies --- package.json | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 54c913b..c45c2be 100644 --- a/package.json +++ b/package.json @@ -20,25 +20,26 @@ "prettier": "^2.0.0", "rehype-parse": "^7.0.0", "rehype-stringify": "^8.0.0", - "remark-cli": "^8.0.0", + "remark-cli": "^9.0.0", "remark-html": "^13.0.0", - "remark-parse": "^9.0.0-alpha.1", - "remark-preset-wooorm": "^7.0.0", + "remark-parse": "^9.0.0", + "remark-preset-wooorm": "^8.0.0", "remark-rehype": "^8.0.0", - "remark-stringify": "^9.0.0-alpha.1", + "remark-stringify": "^9.0.0", "tape": "^5.0.0", "typescript": "^4.0.0", "unified": "^9.0.0", "unist-builder": "^2.0.0", + "unist-util-remove-position": "^3.0.0", "xo": "^0.33.0" }, "scripts": { "postinstall": "lerna bootstrap --no-ci", - "format": "remark . -qfo && prettier . --write && xo --fix", + "format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", "test-api": "tape \"packages/*/test.js\" \"packages/*/test/**/*.js\"", "test-coverage": "nyc --reporter lcov tape \"packages/*/test.js\" \"packages/*/test/**/*.js\"", "test-types": "lerna run test-types --concurrency 1 --stream", - "test": "npm run format && npm run test-types && npm run test-coverage" + "test": "npm run format && npm run test-coverage && npm run test-types" }, "nyc": { "check-coverage": true, From 39070ab37553332f22bb09918454813ec243fd73 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Ouimet Date: Fri, 5 Feb 2021 08:56:47 -0500 Subject: [PATCH 02/98] Add support for TeX input options in rehype-mathjax Closes GH-53. Closes GH-54. --- packages/rehype-mathjax/browser.js | 10 +- packages/rehype-mathjax/chtml.d.ts | 24 +- packages/rehype-mathjax/chtml.js | 6 +- packages/rehype-mathjax/index.d.ts | 24 +- packages/rehype-mathjax/lib/core.js | 20 +- packages/rehype-mathjax/lib/input.js | 4 +- packages/rehype-mathjax/lib/output-chtml.js | 6 - packages/rehype-mathjax/lib/renderer.js | 17 +- packages/rehype-mathjax/readme.md | 7 + packages/rehype-mathjax/svg.js | 4 +- packages/rehype-mathjax/test.ts | 4 + .../fixture/equation-numbering-1-chtml.html | 742 ++++++++++++++++++ .../fixture/equation-numbering-1-svg.html | 118 +++ .../test/fixture/equation-numbering-1.html | 3 + .../fixture/equation-numbering-2-svg.html | 119 +++ .../test/fixture/equation-numbering-2.html | 4 + packages/rehype-mathjax/test/index.js | 99 +++ 17 files changed, 1190 insertions(+), 21 deletions(-) create mode 100644 packages/rehype-mathjax/test/fixture/equation-numbering-1-chtml.html create mode 100644 packages/rehype-mathjax/test/fixture/equation-numbering-1-svg.html create mode 100644 packages/rehype-mathjax/test/fixture/equation-numbering-1.html create mode 100644 packages/rehype-mathjax/test/fixture/equation-numbering-2-svg.html create mode 100644 packages/rehype-mathjax/test/fixture/equation-numbering-2.html diff --git a/packages/rehype-mathjax/browser.js b/packages/rehype-mathjax/browser.js index 32f319e..20f38bf 100644 --- a/packages/rehype-mathjax/browser.js +++ b/packages/rehype-mathjax/browser.js @@ -1,6 +1,14 @@ const createPlugin = require('./lib/core') -module.exports = createPlugin('rehypeMathJaxBrowser', renderBrowser) +module.exports = createPlugin( + 'rehypeMathJaxBrowser', + renderBrowser, + false, + true +) + +/* To do next major: Make `options` match the format of MathJax options +`{tex: ...}` */ function renderBrowser(options) { const settings = options || {} diff --git a/packages/rehype-mathjax/chtml.d.ts b/packages/rehype-mathjax/chtml.d.ts index 9c9f657..e762fc3 100644 --- a/packages/rehype-mathjax/chtml.d.ts +++ b/packages/rehype-mathjax/chtml.d.ts @@ -16,6 +16,28 @@ interface MathJaxCHtmlOptions { adaptiveCSS?: boolean } -declare const renderCHtml: Plugin<[MathJaxCHtmlOptions]> +// http://docs.mathjax.org/en/latest/options/input/tex.html#the-configuration-block +interface MathJaxInputTexOptions { + packages: string[] + inlineMath: [[string, string]] + displayMath: [[string, string]] + processEscapes: boolean + processEnvironments: boolean + processRefs: boolean + digits: RegExp + tags: 'none' | 'ams' | 'all' + tagSide: 'left' | 'right' + tagIndent: string + useLabelIds: boolean + multlineWidth: string + maxMacros: number + maxBuffer: number + baseURL: string + formatError: (jax: any, err: any) => string +} + +declare const renderCHtml: Plugin< + [MathJaxCHtmlOptions & {tex?: Partial}] +> export = renderCHtml diff --git a/packages/rehype-mathjax/chtml.js b/packages/rehype-mathjax/chtml.js index 97acb24..cb467e3 100644 --- a/packages/rehype-mathjax/chtml.js +++ b/packages/rehype-mathjax/chtml.js @@ -3,8 +3,8 @@ const createOutput = require('./lib/output-chtml') const createRenderer = require('./lib/renderer') const createPlugin = require('./lib/core') -module.exports = createPlugin('rehypeMathJaxCHtml', renderCHtml) +module.exports = createPlugin('rehypeMathJaxCHtml', renderCHtml, true) -function renderCHtml(options) { - return createRenderer(createInput(), createOutput(options)) +function renderCHtml(inputOptions, outputOptions) { + return createRenderer(createInput(inputOptions), createOutput(outputOptions)) } diff --git a/packages/rehype-mathjax/index.d.ts b/packages/rehype-mathjax/index.d.ts index ce1c907..354b590 100644 --- a/packages/rehype-mathjax/index.d.ts +++ b/packages/rehype-mathjax/index.d.ts @@ -19,8 +19,30 @@ interface MathJaxSvgOptions { titleID: number } +// http://docs.mathjax.org/en/latest/options/input/tex.html#the-configuration-block +interface MathJaxInputTexOptions { + packages: string[] + inlineMath: [[string, string]] + displayMath: [[string, string]] + processEscapes: boolean + processEnvironments: boolean + processRefs: boolean + digits: RegExp + tags: 'none' | 'ams' | 'all' + tagSide: 'left' | 'right' + tagIndent: string + useLabelIds: boolean + multlineWidth: string + maxMacros: number + maxBuffer: number + baseURL: string + formatError: (jax: any, err: any) => string +} + type RenderSVGOptions = Partial -declare const renderSvg: Plugin<[RenderSVGOptions?]> +declare const renderSvg: Plugin< + [(RenderSVGOptions & {tex?: Partial})?] +> export = renderSvg diff --git a/packages/rehype-mathjax/lib/core.js b/packages/rehype-mathjax/lib/core.js index 434607d..43b8c12 100644 --- a/packages/rehype-mathjax/lib/core.js +++ b/packages/rehype-mathjax/lib/core.js @@ -2,19 +2,35 @@ const visit = require('unist-util-visit') module.exports = createPlugin -function createPlugin(displayName, createRenderer) { +/* To do next major: Remove `chtml` and `browser` flags once all the options use +the same format */ + +function createPlugin(displayName, createRenderer, chtml, browser) { attacher.displayName = displayName return attacher function attacher(options) { - const renderer = createRenderer(options) + if (chtml && (!options || !options.fontURL)) { + throw new Error( + 'rehype-mathjax: missing `fontURL` in options, which must be set to a URL to reach MathJaX fonts' + ) + } + + const inputOptions = browser ? options : (options || {}).tex + let outputOptions = options || {} + if ('tex' in outputOptions) { + outputOptions = Object.assign({}, outputOptions) + delete outputOptions.tex + } transform.displayName = displayName + 'Transform' return transform function transform(tree) { + const renderer = createRenderer(inputOptions, outputOptions) + let context = tree let found = false diff --git a/packages/rehype-mathjax/lib/input.js b/packages/rehype-mathjax/lib/input.js index 48eb87e..86a2de5 100644 --- a/packages/rehype-mathjax/lib/input.js +++ b/packages/rehype-mathjax/lib/input.js @@ -3,6 +3,6 @@ const packages = require('mathjax-full/js/input/tex/AllPackages').AllPackages module.exports = createInput -function createInput() { - return new Tex({packages: packages}) +function createInput(options) { + return new Tex(Object.assign({packages: packages}, options)) } diff --git a/packages/rehype-mathjax/lib/output-chtml.js b/packages/rehype-mathjax/lib/output-chtml.js index d43493d..304b0e7 100644 --- a/packages/rehype-mathjax/lib/output-chtml.js +++ b/packages/rehype-mathjax/lib/output-chtml.js @@ -3,11 +3,5 @@ const CHtml = require('mathjax-full/js/output/chtml').CHTML module.exports = createOutput function createOutput(options) { - if (!options || !options.fontURL) { - throw new Error( - 'rehype-mathjax: missing `fontURL` in options, which must be set to a URL to reach MathJaX fonts' - ) - } - return new CHtml(options) } diff --git a/packages/rehype-mathjax/lib/renderer.js b/packages/rehype-mathjax/lib/renderer.js index e667e83..4e4c8b4 100644 --- a/packages/rehype-mathjax/lib/renderer.js +++ b/packages/rehype-mathjax/lib/renderer.js @@ -6,10 +6,21 @@ const createAdaptor = require('./adaptor') module.exports = renderer -function renderer(input, output) { - const adaptor = createAdaptor() - register(adaptor) +const adaptor = createAdaptor() + +/* To do next major: Keep resultant HTML handler from `register(adaptor)` to +allow registering the AssistiveMmlHandler as in this demo: +https://github.com/mathjax/MathJax-demos-node/tree/master/direct */ + +/* To do next major: If registering AssistiveMmlHandler is supported through +configuration, move HTML handler registration to beginning of transformer and +unregister at the end of transformer with +`mathjax.handlers.unregister(handler)`. That is to prevent memory leak in +`mathjax.handlers` whenever a new instance of the plugin is used. */ +register(adaptor) + +function renderer(input, output) { const doc = mathjax.document('', {InputJax: input, OutputJax: output}) return {render: render, styleSheet: styleSheet} diff --git a/packages/rehype-mathjax/readme.md b/packages/rehype-mathjax/readme.md index 2901d5d..68b4fa8 100644 --- a/packages/rehype-mathjax/readme.md +++ b/packages/rehype-mathjax/readme.md @@ -112,6 +112,11 @@ Options are not passed to MathJax: do that yourself on the client. All options, except when using the browser plugin, are passed to [MathJax][mathjax-options]. +Specifically, they are passed to the chosen output processor. + +#### `options.tex` + +These options are passed to the [TeX input processor][mathjax-tex-options]. ## Security @@ -190,3 +195,5 @@ abide by its terms. [mathjax-svg]: http://docs.mathjax.org/en/latest/output/svg.html [mathjax-chtml]: http://docs.mathjax.org/en/latest/output/html.html + +[mathjax-tex-options]: http://docs.mathjax.org/en/latest/options/input/tex.html diff --git a/packages/rehype-mathjax/svg.js b/packages/rehype-mathjax/svg.js index e99f600..d5f1348 100644 --- a/packages/rehype-mathjax/svg.js +++ b/packages/rehype-mathjax/svg.js @@ -5,6 +5,6 @@ const createPlugin = require('./lib/core') module.exports = createPlugin('rehypeMathJaxSvg', renderSvg) -function renderSvg(options) { - return createRenderer(createInput(), createOutput(options)) +function renderSvg(inputOptions, outputOptions) { + return createRenderer(createInput(inputOptions), createOutput(outputOptions)) } diff --git a/packages/rehype-mathjax/test.ts b/packages/rehype-mathjax/test.ts index abab9d9..64312c5 100644 --- a/packages/rehype-mathjax/test.ts +++ b/packages/rehype-mathjax/test.ts @@ -7,11 +7,15 @@ import browser from 'rehype-mathjax/browser' unified().use(mathjax) // $ExpectType Processor unified().use(mathjax, {minScale: 3}) +// $ExpectType Processor +unified().use(mathjax, {minScale: 3, tex: {tags: 'ams'}}) // $ExpectError unified().use(mathjax, {invalidProp: true}) // $ExpectType Processor unified().use(chtml, {fontURL: 'url'}) +// $ExpectType Processor +unified().use(chtml, {fontURL: 'url', tex: {tags: 'ams'}}) // $ExpectError unified().use(chtml) // $ExpectError diff --git a/packages/rehype-mathjax/test/fixture/equation-numbering-1-chtml.html b/packages/rehype-mathjax/test/fixture/equation-numbering-1-chtml.html new file mode 100644 index 0000000..b1ba050 --- /dev/null +++ b/packages/rehype-mathjax/test/fixture/equation-numbering-1-chtml.html @@ -0,0 +1,742 @@ +

Block math:

+
+

See equation .

+ diff --git a/packages/rehype-mathjax/test/fixture/equation-numbering-1-svg.html b/packages/rehype-mathjax/test/fixture/equation-numbering-1-svg.html new file mode 100644 index 0000000..97107b9 --- /dev/null +++ b/packages/rehype-mathjax/test/fixture/equation-numbering-1-svg.html @@ -0,0 +1,118 @@ +

Block math:

+
+

See equation .

+ diff --git a/packages/rehype-mathjax/test/fixture/equation-numbering-1.html b/packages/rehype-mathjax/test/fixture/equation-numbering-1.html new file mode 100644 index 0000000..1a8bbc1 --- /dev/null +++ b/packages/rehype-mathjax/test/fixture/equation-numbering-1.html @@ -0,0 +1,3 @@ +

Block math:

+
\begin{equation}\label{mass-energy relation}E = m c^2\end{equation}
+

See equation \eqref{mass-energy relation}.

diff --git a/packages/rehype-mathjax/test/fixture/equation-numbering-2-svg.html b/packages/rehype-mathjax/test/fixture/equation-numbering-2-svg.html new file mode 100644 index 0000000..8a99a1f --- /dev/null +++ b/packages/rehype-mathjax/test/fixture/equation-numbering-2-svg.html @@ -0,0 +1,119 @@ +

Block math:

+
+

See equation .

+

Reference to an undefined equation .

+ diff --git a/packages/rehype-mathjax/test/fixture/equation-numbering-2.html b/packages/rehype-mathjax/test/fixture/equation-numbering-2.html new file mode 100644 index 0000000..6c6c37d --- /dev/null +++ b/packages/rehype-mathjax/test/fixture/equation-numbering-2.html @@ -0,0 +1,4 @@ +

Block math:

+
\begin{equation}\label{pythagorean theorem}a^2 + b^2 = c^2\end{equation}
+

See equation \eqref{pythagorean theorem}.

+

Reference to an undefined equation \eqref{mass-energy relation}.

diff --git a/packages/rehype-mathjax/test/index.js b/packages/rehype-mathjax/test/index.js index f13b39a..7ca8480 100644 --- a/packages/rehype-mathjax/test/index.js +++ b/packages/rehype-mathjax/test/index.js @@ -129,5 +129,104 @@ test('rehype-mathjax', function (t) { 'should support custom `inlineMath` and `displayMath` delimiters for browser' ) + t.equal( + unified() + .use(parseHtml, {fragment: true}) + .use(svg, {tex: {tags: 'ams'}}) + .use(stringify) + .processSync( + vfile.readSync({ + dirname: fixtures, + basename: 'equation-numbering-1.html' + }) + ) + .toString(), + String( + vfile.readSync({ + dirname: fixtures, + basename: 'equation-numbering-1-svg.html' + }) + ).trim(), + 'should render SVG with equation numbers' + ) + + t.equal( + unified() + .use(parseHtml, {fragment: true}) + .use(svg, {tex: {tags: 'ams'}}) + .use(stringify) + .processSync( + vfile.readSync({ + dirname: fixtures, + basename: 'equation-numbering-2.html' + }) + ) + .toString(), + String( + vfile.readSync({ + dirname: fixtures, + basename: 'equation-numbering-2-svg.html' + }) + ).trim(), + 'should render SVG with reference to an undefined equation' + ) + + t.equal( + unified() + .use(parseHtml, {fragment: true}) + .use(chtml, {fontURL: 'place/to/fonts', tex: {tags: 'ams'}}) + .use(stringify) + .processSync( + vfile.readSync({ + dirname: fixtures, + basename: 'equation-numbering-1.html' + }) + ) + .toString(), + String( + vfile.readSync({ + dirname: fixtures, + basename: 'equation-numbering-1-chtml.html' + }) + ).trim(), + 'should render CHTML with equation numbers' + ) + + t.equal( + (() => { + const processor = unified() + .use(parseHtml, {fragment: true}) + .use(svg, {tex: {tags: 'ams'}}) + .use(stringify) + return ['equation-numbering-1.html', 'equation-numbering-2.html'] + .map((basename) => + processor + .processSync( + vfile.readSync({ + dirname: fixtures, + basename: basename + }) + ) + .toString() + ) + .join('') + })(), + [ + String( + vfile.readSync({ + dirname: fixtures, + basename: 'equation-numbering-1-svg.html' + }) + ).trim(), + String( + vfile.readSync({ + dirname: fixtures, + basename: 'equation-numbering-2-svg.html' + }) + ).trim() + ].join(''), + 'should render SVG with equation numbers' + ) + t.end() }) From 6675ba01429a6eb413053489c3825c28a2e571e9 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 5 Feb 2021 15:04:39 +0100 Subject: [PATCH 03/98] Update dev-dependencies --- package.json | 8 +++----- packages/rehype-mathjax/chtml.d.ts | 4 ++-- packages/rehype-mathjax/index.d.ts | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index c45c2be..4af1f3e 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "unified": "^9.0.0", "unist-builder": "^2.0.0", "unist-util-remove-position": "^3.0.0", - "xo": "^0.33.0" + "xo": "^0.37.0" }, "scripts": { "postinstall": "lerna bootstrap --no-ci", @@ -59,10 +59,8 @@ "prettier": true, "esnext": false, "rules": { - "unicorn/no-fn-reference-in-iterator": "off", - "unicorn/string-content": "off", - "new-cap": "off", - "complexity": "off", + "@typescript-eslint/ban-types": "off", + "unicorn/no-array-callback-reference": "off", "no-eq-null": "off", "eqeqeq": [ 2, diff --git a/packages/rehype-mathjax/chtml.d.ts b/packages/rehype-mathjax/chtml.d.ts index e762fc3..6f8e560 100644 --- a/packages/rehype-mathjax/chtml.d.ts +++ b/packages/rehype-mathjax/chtml.d.ts @@ -8,7 +8,7 @@ interface MathJaxCHtmlOptions { mtextInheritFont?: boolean merrorInheritFont?: boolean mathmlSpacing?: boolean - skipAttributes?: {[index: string]: boolean} + skipAttributes?: Record exFactor?: number displayAlign?: 'left' | 'center' | 'right' displayIndent?: string @@ -33,7 +33,7 @@ interface MathJaxInputTexOptions { maxMacros: number maxBuffer: number baseURL: string - formatError: (jax: any, err: any) => string + formatError: (jax: any, error: any) => string } declare const renderCHtml: Plugin< diff --git a/packages/rehype-mathjax/index.d.ts b/packages/rehype-mathjax/index.d.ts index 354b590..f75e630 100644 --- a/packages/rehype-mathjax/index.d.ts +++ b/packages/rehype-mathjax/index.d.ts @@ -9,7 +9,7 @@ interface MathJaxSvgOptions { mtextInheritFont: boolean merrorInheritFont: boolean mathmlSpacing: boolean - skipAttributes: {[index: string]: boolean} + skipAttributes: Record exFactor: number displayAlign: 'left' | 'center' | 'right' displayIndent: string @@ -36,7 +36,7 @@ interface MathJaxInputTexOptions { maxMacros: number maxBuffer: number baseURL: string - formatError: (jax: any, err: any) => string + formatError: (jax: any, error: any) => string } type RenderSVGOptions = Partial From f0f852b5e0de8bbb3e168f616f094b7d544c1245 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 5 Feb 2021 15:08:01 +0100 Subject: [PATCH 04/98] Refactor code style --- packages/rehype-mathjax/browser.js | 5 ++--- packages/rehype-mathjax/lib/core.js | 5 ++--- packages/rehype-mathjax/lib/renderer.js | 20 ++++++++++---------- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/packages/rehype-mathjax/browser.js b/packages/rehype-mathjax/browser.js index 20f38bf..6d8c2d6 100644 --- a/packages/rehype-mathjax/browser.js +++ b/packages/rehype-mathjax/browser.js @@ -7,9 +7,8 @@ module.exports = createPlugin( true ) -/* To do next major: Make `options` match the format of MathJax options -`{tex: ...}` */ - +// To do next major: Make `options` match the format of MathJax options +// `{tex: ...}` function renderBrowser(options) { const settings = options || {} const display = settings.displayMath || ['\\[', '\\]'] diff --git a/packages/rehype-mathjax/lib/core.js b/packages/rehype-mathjax/lib/core.js index 43b8c12..db76590 100644 --- a/packages/rehype-mathjax/lib/core.js +++ b/packages/rehype-mathjax/lib/core.js @@ -2,9 +2,8 @@ const visit = require('unist-util-visit') module.exports = createPlugin -/* To do next major: Remove `chtml` and `browser` flags once all the options use -the same format */ - +// To do next major: Remove `chtml` and `browser` flags once all the options use +// the same format. function createPlugin(displayName, createRenderer, chtml, browser) { attacher.displayName = displayName diff --git a/packages/rehype-mathjax/lib/renderer.js b/packages/rehype-mathjax/lib/renderer.js index 4e4c8b4..1a6c927 100644 --- a/packages/rehype-mathjax/lib/renderer.js +++ b/packages/rehype-mathjax/lib/renderer.js @@ -8,16 +8,16 @@ module.exports = renderer const adaptor = createAdaptor() -/* To do next major: Keep resultant HTML handler from `register(adaptor)` to -allow registering the AssistiveMmlHandler as in this demo: -https://github.com/mathjax/MathJax-demos-node/tree/master/direct */ - -/* To do next major: If registering AssistiveMmlHandler is supported through -configuration, move HTML handler registration to beginning of transformer and -unregister at the end of transformer with -`mathjax.handlers.unregister(handler)`. That is to prevent memory leak in -`mathjax.handlers` whenever a new instance of the plugin is used. */ - +// To do next major: Keep resultant HTML handler from `register(adaptor)` to +// allow registering the `AssistiveMmlHandler` as in this demo: +// +// +// To do next major: If registering `AssistiveMmlHandler` is supported through +// configuration, move HTML handler registration to beginning of transformer and +// unregister at the end of transformer with +// `mathjax.handlers.unregister(handler)`. +// That is to prevent memory leak in `mathjax.handlers` whenever a new instance +// of the plugin is used. register(adaptor) function renderer(input, output) { From 29a7816fe60e691596efb68086f8d6cddaf4a372 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 5 Feb 2021 15:11:40 +0100 Subject: [PATCH 05/98] Use Actions --- .github/workflows/main.yml | 21 +++++++++++++++++++++ .travis.yml | 5 ----- packages/rehype-katex/readme.md | 4 ++-- packages/rehype-mathjax/readme.md | 4 ++-- packages/remark-html-katex/readme.md | 4 ++-- packages/remark-math/readme.md | 4 ++-- readme.md | 4 ++-- 7 files changed, 31 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/main.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..ffb6759 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,21 @@ +name: main +on: + - pull_request + - push +jobs: + main: + name: ${{matrix.node}} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: dcodeIO/setup-node-nvm@master + with: + node-version: ${{matrix.node}} + - run: npm install + - run: npm test + - uses: codecov/codecov-action@v1 + strategy: + matrix: + node: + - lts/dubnium + - node diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e108609..0000000 --- a/.travis.yml +++ /dev/null @@ -1,5 +0,0 @@ -language: node_js -node_js: - - lts/dubnium - - node -after_script: bash <(curl -s https://codecov.io/bash) diff --git a/packages/rehype-katex/readme.md b/packages/rehype-katex/readme.md index 6d60d37..0ddd417 100644 --- a/packages/rehype-katex/readme.md +++ b/packages/rehype-katex/readme.md @@ -107,9 +107,9 @@ abide by its terms. -[build-badge]: https://img.shields.io/travis/remarkjs/remark-math/main.svg +[build-badge]: https://github.com/remarkjs/remark-math/workflows/main/badge.svg -[build]: https://travis-ci.org/remarkjs/remark-math +[build]: https://github.com/remarkjs/remark-math/actions [coverage-badge]: https://img.shields.io/codecov/c/github/remarkjs/remark-math.svg diff --git a/packages/rehype-mathjax/readme.md b/packages/rehype-mathjax/readme.md index 68b4fa8..64e2f0b 100644 --- a/packages/rehype-mathjax/readme.md +++ b/packages/rehype-mathjax/readme.md @@ -142,9 +142,9 @@ abide by its terms. -[build-badge]: https://img.shields.io/travis/remarkjs/remark-math/main.svg +[build-badge]: https://github.com/remarkjs/remark-math/workflows/main/badge.svg -[build]: https://travis-ci.org/remarkjs/remark-math +[build]: https://github.com/remarkjs/remark-math/actions [coverage-badge]: https://img.shields.io/codecov/c/github/remarkjs/remark-math.svg diff --git a/packages/remark-html-katex/readme.md b/packages/remark-html-katex/readme.md index f5fb5d7..92dff28 100644 --- a/packages/remark-html-katex/readme.md +++ b/packages/remark-html-katex/readme.md @@ -107,9 +107,9 @@ abide by its terms. -[build-badge]: https://img.shields.io/travis/remarkjs/remark-math/main.svg +[build-badge]: https://github.com/remarkjs/remark-math/workflows/main/badge.svg -[build]: https://travis-ci.org/remarkjs/remark-math +[build]: https://github.com/remarkjs/remark-math/actions [coverage-badge]: https://img.shields.io/codecov/c/github/remarkjs/remark-math.svg diff --git a/packages/remark-math/readme.md b/packages/remark-math/readme.md index 43390c0..392d698 100644 --- a/packages/remark-math/readme.md +++ b/packages/remark-math/readme.md @@ -111,9 +111,9 @@ abide by its terms. -[build-badge]: https://img.shields.io/travis/remarkjs/remark-math/main.svg +[build-badge]: https://github.com/remarkjs/remark-math/workflows/main/badge.svg -[build]: https://travis-ci.org/remarkjs/remark-math +[build]: https://github.com/remarkjs/remark-math/actions [coverage-badge]: https://img.shields.io/codecov/c/github/remarkjs/remark-math.svg diff --git a/readme.md b/readme.md index 09c714a..a627aad 100644 --- a/readme.md +++ b/readme.md @@ -128,9 +128,9 @@ abide by its terms. -[build-badge]: https://img.shields.io/travis/remarkjs/remark-math/main.svg +[build-badge]: https://github.com/remarkjs/remark-math/workflows/main/badge.svg -[build]: https://travis-ci.org/remarkjs/remark-math +[build]: https://github.com/remarkjs/remark-math/actions [coverage-badge]: https://img.shields.io/codecov/c/github/remarkjs/remark-math.svg From 7bbce013c49ad8dcfbe8a8510a13a83991ad9fd7 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 5 Feb 2021 15:17:37 +0100 Subject: [PATCH 06/98] Update list of contributors --- package.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 4af1f3e..728ac18 100644 --- a/package.json +++ b/package.json @@ -8,10 +8,12 @@ "Junyoung Choi (https://rokt33r.github.io)", "Titus Wormer (https://wooorm.com)", "Victor Felder ", + "TANIGUCHI Masaya ", "Xiaoru Li ", - "John Jeng ", "Daniel Perez Alvarez ", - "TANIGUCHI Masaya " + "John Jeng ", + "Marc-Antoine Ouimet ", + "Rongjian Zhang " ], "devDependencies": { "dtslint": "^4.0.0", From 9469a2618ac9b26babf6207fa05c8716dce695e4 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 5 Feb 2021 15:18:10 +0100 Subject: [PATCH 07/98] rehype-mathjax: 3.1.0 --- packages/rehype-mathjax/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index fc06186..dbb2c1f 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -1,6 +1,6 @@ { "name": "rehype-mathjax", - "version": "3.0.0", + "version": "3.1.0", "description": "rehype plugin to transform inline and block math with MathJax", "license": "MIT", "keywords": [ From 11ce981fa4209bfe04ad9cad60b8462671c134ed Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 24 Apr 2021 14:46:25 +0200 Subject: [PATCH 08/98] Update dev-dependencies --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 728ac18..f60aa6d 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ ], "devDependencies": { "dtslint": "^4.0.0", - "lerna": "^3.0.0", + "lerna": "^4.0.0", "nyc": "^15.0.0", "prettier": "^2.0.0", "rehype-parse": "^7.0.0", @@ -33,7 +33,7 @@ "unified": "^9.0.0", "unist-builder": "^2.0.0", "unist-util-remove-position": "^3.0.0", - "xo": "^0.37.0" + "xo": "^0.38.0" }, "scripts": { "postinstall": "lerna bootstrap --no-ci", From 72d10000369d5f227307c900608e25c23fbb96da Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 24 Apr 2021 14:46:36 +0200 Subject: [PATCH 09/98] Update fixtures for changes in `mathjax` --- .../test/fixture/document-svg.html | 14 +++++++----- .../test/fixture/double-svg.html | 14 +++++++----- .../fixture/equation-numbering-1-chtml.html | 22 +++++++++---------- .../fixture/equation-numbering-1-svg.html | 16 ++++++++------ .../fixture/equation-numbering-2-svg.html | 18 ++++++++------- .../test/fixture/markdown-svg.html | 16 ++++++++------ .../test/fixture/small-chtml.html | 4 ---- .../test/fixture/small-svg.html | 16 ++++++++------ 8 files changed, 63 insertions(+), 57 deletions(-) diff --git a/packages/rehype-mathjax/test/fixture/document-svg.html b/packages/rehype-mathjax/test/fixture/document-svg.html index 902d724..3d1dbc5 100644 --- a/packages/rehype-mathjax/test/fixture/document-svg.html +++ b/packages/rehype-mathjax/test/fixture/document-svg.html @@ -9,6 +9,8 @@ mjx-container[jax="SVG"] > svg { overflow: visible; + min-height: 1px; + min-width: 1px; } mjx-container[jax="SVG"] > svg a { @@ -44,21 +46,21 @@ stroke: none; } -g[data-mml-node="mtable"] > line[data-line] { +g[data-mml-node="mtable"] > line[data-line], svg[data-table] > g > line[data-line] { stroke-width: 70px; fill: none; } -g[data-mml-node="mtable"] > rect[data-frame] { +g[data-mml-node="mtable"] > rect[data-frame], svg[data-table] > g > rect[data-frame] { stroke-width: 70px; fill: none; } -g[data-mml-node="mtable"] > .mjx-dashed { +g[data-mml-node="mtable"] > .mjx-dashed, svg[data-table] > g > .mjx-dashed { stroke-dasharray: 140; } -g[data-mml-node="mtable"] > .mjx-dotted { +g[data-mml-node="mtable"] > .mjx-dotted, svg[data-table] > g > .mjx-dotted { stroke-linecap: round; stroke-dasharray: 0,140; } @@ -113,12 +115,12 @@ overflow: visible; } -.MathJax path { +mjx-container[jax="SVG"] path[data-c], mjx-container[jax="SVG"] use[data-c] { stroke-width: 3; } -

Hello, !

+

Hello, !

diff --git a/packages/rehype-mathjax/test/fixture/double-svg.html b/packages/rehype-mathjax/test/fixture/double-svg.html index 2bf74c3..1497526 100644 --- a/packages/rehype-mathjax/test/fixture/double-svg.html +++ b/packages/rehype-mathjax/test/fixture/double-svg.html @@ -1,4 +1,4 @@ -

Double math .

+

Double math .

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 b1ba050..4ad8245 100644 --- a/packages/rehype-mathjax/test/fixture/equation-numbering-1-chtml.html +++ b/packages/rehype-mathjax/test/fixture/equation-numbering-1-chtml.html @@ -1,6 +1,6 @@

Block math:

-
-

See equation .

+
+

See equation .

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 97107b9..3e753a8 100644 --- a/packages/rehype-mathjax/test/fixture/equation-numbering-1-svg.html +++ b/packages/rehype-mathjax/test/fixture/equation-numbering-1-svg.html @@ -1,6 +1,6 @@

Block math:

-
-

See equation .

+
+

See equation .

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 8a99a1f..a7473e3 100644 --- a/packages/rehype-mathjax/test/fixture/equation-numbering-2-svg.html +++ b/packages/rehype-mathjax/test/fixture/equation-numbering-2-svg.html @@ -1,7 +1,7 @@

Block math:

-
-

See equation .

-

Reference to an undefined equation .

+
+

See equation .

+

Reference to an undefined equation .

diff --git a/packages/rehype-mathjax/test/fixture/markdown-svg.html b/packages/rehype-mathjax/test/fixture/markdown-svg.html index 7ac59a9..2f30c68 100644 --- a/packages/rehype-mathjax/test/fixture/markdown-svg.html +++ b/packages/rehype-mathjax/test/fixture/markdown-svg.html @@ -1,12 +1,14 @@ -

Inline math .

+

Inline math .

Block math:

-
diff --git a/packages/rehype-mathjax/test/fixture/small-chtml.html b/packages/rehype-mathjax/test/fixture/small-chtml.html index b02d444..64f4502 100644 --- a/packages/rehype-mathjax/test/fixture/small-chtml.html +++ b/packages/rehype-mathjax/test/fixture/small-chtml.html @@ -389,8 +389,4 @@ padding: 0.441em 0.543em 0.216em 0; content: "\3B3"; } - -[noIC] mjx-c.mjx-c1D6FE.TEX-I:last-child::before { - padding-right: 0.518em; -} diff --git a/packages/rehype-mathjax/test/fixture/small-svg.html b/packages/rehype-mathjax/test/fixture/small-svg.html index c53456e..b2d7d2b 100644 --- a/packages/rehype-mathjax/test/fixture/small-svg.html +++ b/packages/rehype-mathjax/test/fixture/small-svg.html @@ -1,6 +1,6 @@ -

Inline math .

+

Inline math .

Block math:

-
+
From 1abc0440b50647d9b5f5baf826b83a19c1605963 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 24 Apr 2021 14:54:18 +0200 Subject: [PATCH 10/98] rehype-katex: update `katex` See: Closes GH-58. --- packages/rehype-katex/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index c43f6e9..8e6d1ad 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -38,7 +38,7 @@ "dependencies": { "@types/katex": "^0.11.0", "hast-util-to-text": "^2.0.0", - "katex": "^0.12.0", + "katex": "^0.13.0", "rehype-parse": "^7.0.0", "unified": "^9.0.0", "unist-util-visit": "^2.0.0" From 8ad51d0075fa93f51f21d74d86309893a6ae0c4b Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 24 Apr 2021 14:57:09 +0200 Subject: [PATCH 11/98] rehype-katex: 5.0.0 --- packages/rehype-katex/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index 8e6d1ad..5c1bc0d 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -1,6 +1,6 @@ { "name": "rehype-katex", - "version": "4.0.0", + "version": "5.0.0", "description": "rehype plugin to transform inline and block math with KaTeX", "license": "MIT", "keywords": [ From a585a46ea6dcd2f5b8567aa26821d01b6f42098f Mon Sep 17 00:00:00 2001 From: Paul Kim Date: Tue, 1 Jun 2021 06:56:26 -0700 Subject: [PATCH 12/98] Update example in `readme.md` to use esm Closes GH-59. Reviewed-by: Titus Wormer --- readme.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/readme.md b/readme.md index a627aad..9e42c9e 100644 --- a/readme.md +++ b/readme.md @@ -34,13 +34,13 @@ $$ And our script, `example.js`, looks as follows: ```js -const vfile = require('to-vfile') -const unified = require('unified') -const markdown = require('remark-parse') -const math = require('remark-math') -const remark2rehype = require('remark-rehype') -const katex = require('rehype-katex') -const stringify = require('rehype-stringify') +import {toVFile} from 'to-vfile' +import unified from 'unified' +import markdown from 'remark-parse' +import math from 'remark-math' +import remark2rehype from 'remark-rehype' +import katex from 'rehype-katex' +import stringify from 'rehype-stringify' unified() .use(markdown) @@ -48,7 +48,7 @@ unified() .use(remark2rehype) .use(katex) .use(stringify) - .process(vfile.readSync('example.md'), function (err, file) { + .process(toVFile.readSync('example.md'), function (err, file) { if (err) throw err console.log(String(file)) }) From 6400b2063f6d46de306c6627dcc154a53d410245 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Tue, 1 Jun 2021 16:00:12 +0200 Subject: [PATCH 13/98] Refactor code-style --- package.json | 7 ++++--- packages/rehype-mathjax/lib/adaptor.browser.js | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index f60aa6d..43df025 100644 --- a/package.json +++ b/package.json @@ -62,12 +62,13 @@ "esnext": false, "rules": { "@typescript-eslint/ban-types": "off", - "unicorn/no-array-callback-reference": "off", - "no-eq-null": "off", "eqeqeq": [ 2, "allow-null" - ] + ], + "import/extensions": "off", + "no-eq-null": "off", + "unicorn/no-array-callback-reference": "off" } }, "remarkConfig": { diff --git a/packages/rehype-mathjax/lib/adaptor.browser.js b/packages/rehype-mathjax/lib/adaptor.browser.js index f74127c..7ae81b5 100644 --- a/packages/rehype-mathjax/lib/adaptor.browser.js +++ b/packages/rehype-mathjax/lib/adaptor.browser.js @@ -1 +1,2 @@ -module.exports = require('mathjax-full/js/adaptors/browserAdaptor').browserAdaptor +module.exports = + require('mathjax-full/js/adaptors/browserAdaptor').browserAdaptor From c7d0aee366f1d3c4bac75599471ce32ebc9093aa Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 7 Aug 2021 13:06:17 +0200 Subject: [PATCH 14/98] Add bb --- .github/workflows/bb.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/workflows/bb.yml diff --git a/.github/workflows/bb.yml b/.github/workflows/bb.yml new file mode 100644 index 0000000..0198fc3 --- /dev/null +++ b/.github/workflows/bb.yml @@ -0,0 +1,13 @@ +name: bb +on: + issues: + types: [opened, reopened, edited, closed, labeled, unlabeled] + pull_request_target: + types: [opened, reopened, edited, closed, labeled, unlabeled] +jobs: + main: + runs-on: ubuntu-latest + steps: + - uses: unifiedjs/beep-boop-beta@main + with: + repo-token: ${{secrets.GITHUB_TOKEN}} From 9ef66047914a9a0f746053c91017ced5a628afc3 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 7 Aug 2021 13:06:24 +0200 Subject: [PATCH 15/98] Update Node in Actions --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ffb6759..fe284ad 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,5 +17,5 @@ jobs: strategy: matrix: node: - - lts/dubnium + - lts/erbium - node From 2e4ae4bdf4da5b6d9a8f605d092dfd6de94064c2 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 7 Aug 2021 13:07:35 +0200 Subject: [PATCH 16/98] Add `.npmrc`s --- packages/rehype-katex/.npmrc | 1 + packages/rehype-mathjax/.npmrc | 1 + packages/remark-html-katex/.npmrc | 1 + packages/remark-math/.npmrc | 1 + 4 files changed, 4 insertions(+) create mode 100644 packages/rehype-katex/.npmrc create mode 100644 packages/rehype-mathjax/.npmrc create mode 100644 packages/remark-html-katex/.npmrc create mode 100644 packages/remark-math/.npmrc diff --git a/packages/rehype-katex/.npmrc b/packages/rehype-katex/.npmrc new file mode 100644 index 0000000..43c97e7 --- /dev/null +++ b/packages/rehype-katex/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/packages/rehype-mathjax/.npmrc b/packages/rehype-mathjax/.npmrc new file mode 100644 index 0000000..43c97e7 --- /dev/null +++ b/packages/rehype-mathjax/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/packages/remark-html-katex/.npmrc b/packages/remark-html-katex/.npmrc new file mode 100644 index 0000000..43c97e7 --- /dev/null +++ b/packages/remark-html-katex/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/packages/remark-math/.npmrc b/packages/remark-math/.npmrc new file mode 100644 index 0000000..43c97e7 --- /dev/null +++ b/packages/remark-math/.npmrc @@ -0,0 +1 @@ +package-lock=false From 5136ded7e6aa348724d297e6aad912cdec89391d Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 7 Aug 2021 13:14:34 +0200 Subject: [PATCH 17/98] Update fixtures for changes in mathjax --- .../fixture/equation-numbering-1-chtml.html | 322 +++++++++--------- .../test/fixture/small-chtml.html | 6 +- 2 files changed, 157 insertions(+), 171 deletions(-) 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 4ad8245..a16a386 100644 --- a/packages/rehype-mathjax/test/fixture/equation-numbering-1-chtml.html +++ b/packages/rehype-mathjax/test/fixture/equation-numbering-1-chtml.html @@ -125,6 +125,10 @@ visibility: hidden; } +_::-webkit-full-page-media, _:future, :root mjx-container { + will-change: opacity; +} + mjx-math { display: inline-block; text-align: left; @@ -164,136 +168,6 @@ text-align: right; } -mjx-mrow { - display: inline-block; - text-align: left; -} - -mjx-inferredMrow { - display: inline-block; - text-align: left; -} - -mjx-mi { - display: inline-block; - text-align: left; -} - -mjx-mo { - display: inline-block; - text-align: left; -} - -mjx-stretchy-h { - display: inline-table; - width: 100%; -} - -mjx-stretchy-h > * { - display: table-cell; - width: 0; -} - -mjx-stretchy-h > * > mjx-c { - display: inline-block; - transform: scalex(1.0000001); -} - -mjx-stretchy-h > * > mjx-c::before { - display: inline-block; - width: initial; -} - -mjx-stretchy-h > mjx-ext { - overflow: hidden; - width: 100%; -} - -mjx-stretchy-h > mjx-ext > mjx-c::before { - transform: scalex(500); -} - -mjx-stretchy-h > mjx-ext > mjx-c { - width: 0; -} - -mjx-stretchy-h > mjx-beg > mjx-c { - margin-right: -.1em; -} - -mjx-stretchy-h > mjx-end > mjx-c { - margin-left: -.1em; -} - -mjx-stretchy-v { - display: inline-block; -} - -mjx-stretchy-v > * { - display: block; -} - -mjx-stretchy-v > mjx-beg { - height: 0; -} - -mjx-stretchy-v > mjx-end > mjx-c { - display: block; -} - -mjx-stretchy-v > * > mjx-c { - transform: scaley(1.0000001); - transform-origin: left center; - overflow: hidden; -} - -mjx-stretchy-v > mjx-ext { - display: block; - height: 100%; - box-sizing: border-box; - border: 0px solid transparent; - overflow: hidden; -} - -mjx-stretchy-v > mjx-ext > mjx-c::before { - width: initial; - box-sizing: border-box; -} - -mjx-stretchy-v > mjx-ext > mjx-c { - transform: scaleY(500) translateY(.075em); - overflow: visible; -} - -mjx-mark { - display: inline-block; - height: 0px; -} - -mjx-mn { - display: inline-block; - text-align: left; -} - -mjx-msup { - display: inline-block; - text-align: left; -} - -mjx-mover { - display: inline-block; - text-align: left; -} - -mjx-mover:not([limits="false"]) { - padding-top: .1em; -} - -mjx-mover:not([limits="false"]) > * { - display: block; - text-align: left; -} - mjx-mtable { display: inline-block; text-align: center; @@ -317,6 +191,7 @@ mjx-table { display: inline-block; vertical-align: -.5ex; + box-sizing: border-box; } mjx-table > mjx-itable { @@ -370,53 +245,53 @@ min-width: 100%; } -mjx-mtr { +mjx-mlabeledtr { display: table-row; text-align: left; } -mjx-mtr[rowalign="top"] > mjx-mtd { +mjx-mlabeledtr[rowalign="top"] > mjx-mtd { vertical-align: top; } -mjx-mtr[rowalign="center"] > mjx-mtd { +mjx-mlabeledtr[rowalign="center"] > mjx-mtd { vertical-align: middle; } -mjx-mtr[rowalign="bottom"] > mjx-mtd { +mjx-mlabeledtr[rowalign="bottom"] > mjx-mtd { vertical-align: bottom; } -mjx-mtr[rowalign="baseline"] > mjx-mtd { +mjx-mlabeledtr[rowalign="baseline"] > mjx-mtd { vertical-align: baseline; } -mjx-mtr[rowalign="axis"] > mjx-mtd { +mjx-mlabeledtr[rowalign="axis"] > mjx-mtd { vertical-align: .25em; } -mjx-mlabeledtr { +mjx-mtr { display: table-row; text-align: left; } -mjx-mlabeledtr[rowalign="top"] > mjx-mtd { +mjx-mtr[rowalign="top"] > mjx-mtd { vertical-align: top; } -mjx-mlabeledtr[rowalign="center"] > mjx-mtd { +mjx-mtr[rowalign="center"] > mjx-mtd { vertical-align: middle; } -mjx-mlabeledtr[rowalign="bottom"] > mjx-mtd { +mjx-mtr[rowalign="bottom"] > mjx-mtd { vertical-align: bottom; } -mjx-mlabeledtr[rowalign="baseline"] > mjx-mtd { +mjx-mtr[rowalign="baseline"] > mjx-mtd { vertical-align: baseline; } -mjx-mlabeledtr[rowalign="axis"] > mjx-mtd { +mjx-mtr[rowalign="axis"] > mjx-mtd { vertical-align: .25em; } @@ -456,23 +331,27 @@ text-align: right; } -mjx-mtr mjx-mtd[rowalign="top"], mjx-mlabeledtr mjx-mtd[rowalign="top"] { +mjx-mtd[extra] { + padding: 0; +} + +mjx-mtd[rowalign="top"] { vertical-align: top; } -mjx-mtr mjx-mtd[rowalign="center"], mjx-mlabeledtr mjx-mtd[rowalign="center"] { +mjx-mtd[rowalign="center"] { vertical-align: middle; } -mjx-mtr mjx-mtd[rowalign="bottom"], mjx-mlabeledtr mjx-mtd[rowalign="bottom"] { +mjx-mtd[rowalign="bottom"] { vertical-align: bottom; } -mjx-mtr mjx-mtd[rowalign="baseline"], mjx-mlabeledtr mjx-mtd[rowalign="baseline"] { +mjx-mtd[rowalign="baseline"] { vertical-align: baseline; } -mjx-mtr mjx-mtd[rowalign="axis"], mjx-mlabeledtr mjx-mtd[rowalign="axis"] { +mjx-mtd[rowalign="axis"] { vertical-align: .25em; } @@ -485,6 +364,119 @@ padding: .75em 0 .2em 0; } +mjx-mi { + display: inline-block; + text-align: left; +} + +mjx-mo { + display: inline-block; + text-align: left; +} + +mjx-stretchy-h { + display: inline-table; + width: 100%; +} + +mjx-stretchy-h > * { + display: table-cell; + width: 0; +} + +mjx-stretchy-h > * > mjx-c { + display: inline-block; + transform: scalex(1.0000001); +} + +mjx-stretchy-h > * > mjx-c::before { + display: inline-block; + width: initial; +} + +mjx-stretchy-h > mjx-ext { + /* IE */ overflow: hidden; + /* others */ overflow: clip visible; + width: 100%; +} + +mjx-stretchy-h > mjx-ext > mjx-c::before { + transform: scalex(500); +} + +mjx-stretchy-h > mjx-ext > mjx-c { + width: 0; +} + +mjx-stretchy-h > mjx-beg > mjx-c { + margin-right: -.1em; +} + +mjx-stretchy-h > mjx-end > mjx-c { + margin-left: -.1em; +} + +mjx-stretchy-v { + display: inline-block; +} + +mjx-stretchy-v > * { + display: block; +} + +mjx-stretchy-v > mjx-beg { + height: 0; +} + +mjx-stretchy-v > mjx-end > mjx-c { + display: block; +} + +mjx-stretchy-v > * > mjx-c { + transform: scaley(1.0000001); + transform-origin: left center; + overflow: hidden; +} + +mjx-stretchy-v > mjx-ext { + display: block; + height: 100%; + box-sizing: border-box; + border: 0px solid transparent; + /* IE */ overflow: hidden; + /* others */ overflow: visible clip; +} + +mjx-stretchy-v > mjx-ext > mjx-c::before { + width: initial; + box-sizing: border-box; +} + +mjx-stretchy-v > mjx-ext > mjx-c { + transform: scaleY(500) translateY(.075em); + overflow: visible; +} + +mjx-mark { + display: inline-block; + height: 0px; +} + +mjx-msup { + display: inline-block; + text-align: left; +} + +mjx-mn { + display: inline-block; + text-align: left; +} + +mjx-mrow { + display: inline-block; + text-align: left; +} + mjx-c::before { display: block; width: 0; @@ -693,24 +685,14 @@ content: "("; } -mjx-c.mjx-c29::before { - padding: 0.75em 0.389em 0.25em 0; - content: ")"; -} - mjx-c.mjx-c31::before { padding: 0.666em 0.5em 0 0; content: "1"; } -mjx-c.mjx-c32::before { - padding: 0.666em 0.5em 0 0; - content: "2"; -} - -mjx-c.mjx-c3D::before { - padding: 0.583em 0.778em 0.082em 0; - content: "="; +mjx-c.mjx-c29::before { + padding: 0.75em 0.389em 0.25em 0; + content: ")"; } mjx-c.mjx-c1D438.TEX-I::before { @@ -718,9 +700,9 @@ content: "E"; } -mjx-c.mjx-c1D450.TEX-I::before { - padding: 0.442em 0.433em 0.011em 0; - content: "c"; +mjx-c.mjx-c3D::before { + padding: 0.583em 0.778em 0.082em 0; + content: "="; } mjx-c.mjx-c1D45A.TEX-I::before { @@ -728,13 +710,13 @@ content: "m"; } -mjx-c.mjx-c1D6FC.TEX-I::before { - padding: 0.442em 0.64em 0.011em 0; - content: "\3B1"; +mjx-c.mjx-c1D450.TEX-I::before { + padding: 0.442em 0.433em 0.011em 0; + content: "c"; } -mjx-c.mjx-c1D6FE.TEX-I::before { - padding: 0.441em 0.543em 0.216em 0; - content: "\3B3"; +mjx-c.mjx-c32::before { + padding: 0.666em 0.5em 0 0; + content: "2"; } - + \ 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 64f4502..dfe98bb 100644 --- a/packages/rehype-mathjax/test/fixture/small-chtml.html +++ b/packages/rehype-mathjax/test/fixture/small-chtml.html @@ -124,6 +124,10 @@ visibility: hidden; } +_::-webkit-full-page-media, _:future, :root mjx-container { + will-change: opacity; +} + mjx-math { display: inline-block; text-align: left; @@ -389,4 +393,4 @@ padding: 0.441em 0.543em 0.216em 0; content: "\3B3"; } - + \ No newline at end of file From 0b5f209b28a32a5975c3c7689ad9d5e0d1750555 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 7 Aug 2021 13:15:39 +0200 Subject: [PATCH 18/98] Replace `nyc` with `c8` --- .gitignore | 6 ++---- package.json | 12 +++--------- packages/rehype-katex/package.json | 4 +++- packages/rehype-mathjax/package.json | 4 +++- packages/remark-html-katex/package.json | 4 +++- packages/remark-math/index.js | 6 ++++-- packages/remark-math/package.json | 4 +++- 7 files changed, 21 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index c9396be..33d4929 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,5 @@ -.DS_Store -*.log -.nyc_output/ coverage/ node_modules/ -package-lock.json +.DS_Store +*.log yarn.lock diff --git a/package.json b/package.json index 43df025..071e733 100644 --- a/package.json +++ b/package.json @@ -16,9 +16,9 @@ "Rongjian Zhang " ], "devDependencies": { + "c8": "^7.0.0", "dtslint": "^4.0.0", "lerna": "^4.0.0", - "nyc": "^15.0.0", "prettier": "^2.0.0", "rehype-parse": "^7.0.0", "rehype-stringify": "^8.0.0", @@ -38,17 +38,11 @@ "scripts": { "postinstall": "lerna bootstrap --no-ci", "format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", - "test-api": "tape \"packages/*/test.js\" \"packages/*/test/**/*.js\"", - "test-coverage": "nyc --reporter lcov tape \"packages/*/test.js\" \"packages/*/test/**/*.js\"", + "test-api": "lerna run test-api", + "test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov npm run test-api", "test-types": "lerna run test-types --concurrency 1 --stream", "test": "npm run format && npm run test-coverage && npm run test-types" }, - "nyc": { - "check-coverage": true, - "lines": 100, - "functions": 100, - "branches": 100 - }, "prettier": { "tabWidth": 2, "useTabs": false, diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index 5c1bc0d..cd9c5fa 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -44,7 +44,9 @@ "unist-util-visit": "^2.0.0" }, "scripts": { - "test-types": "dtslint types" + "test-api": "node --conditions development test.js", + "test-types": "dtslint types", + "test": "npm run test-api && npm run test-types" }, "xo": false } diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index dbb2c1f..4f5b126 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -51,7 +51,9 @@ "unist-util-visit": "^2.0.0" }, "scripts": { - "test-types": "dtslint" + "test-api": "node --conditions development test/index.js", + "test-types": "dtslint", + "test": "npm run test-api && npm run test-types" }, "xo": false } diff --git a/packages/remark-html-katex/package.json b/packages/remark-html-katex/package.json index e322d8a..f69515f 100644 --- a/packages/remark-html-katex/package.json +++ b/packages/remark-html-katex/package.json @@ -41,7 +41,9 @@ "unist-util-visit": "^2.0.0" }, "scripts": { - "test-types": "dtslint types" + "test-api": "node --conditions development test.js", + "test-types": "dtslint types", + "test": "npm run test-api && npm run test-types" }, "xo": false } diff --git a/packages/remark-math/index.js b/packages/remark-math/index.js index 62ae445..06bfe1c 100644 --- a/packages/remark-math/index.js +++ b/packages/remark-math/index.js @@ -10,7 +10,8 @@ module.exports = math function math() { var data = this.data() - /* istanbul ignore next - old remark. */ + // Old remark. + /* c8 ignore next 14 */ if ( !warningIssued && ((this.Parser && @@ -31,7 +32,8 @@ function math() { add('toMarkdownExtensions', toMarkdown) function add(field, value) { - /* istanbul ignore if - other extensions. */ + // Other extensions. + /* c8 ignore next */ if (data[field]) data[field].push(value) else data[field] = [value] } diff --git a/packages/remark-math/package.json b/packages/remark-math/package.json index 8fe7fb9..7d4ee4e 100644 --- a/packages/remark-math/package.json +++ b/packages/remark-math/package.json @@ -40,7 +40,9 @@ "micromark-extension-math": "^0.1.0" }, "scripts": { - "test-types": "dtslint types" + "test-api": "node --conditions development test.js", + "test-types": "dtslint types", + "test": "npm run test-api && npm run test-types" }, "xo": false } From 1f047638ebbfe6c617e231702b82d3f739df41d5 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 7 Aug 2021 13:52:14 +0200 Subject: [PATCH 19/98] Use ESM --- package.json | 1 + packages/rehype-katex/index.js | 18 ++- packages/rehype-katex/package.json | 8 +- packages/rehype-katex/test.js | 78 ++++++------ packages/rehype-mathjax/browser.js | 5 +- packages/rehype-mathjax/chtml.js | 12 +- packages/rehype-mathjax/index.js | 4 +- .../rehype-mathjax/lib/adaptor.browser.js | 5 +- packages/rehype-mathjax/lib/adaptor.js | 10 +- packages/rehype-mathjax/lib/core.js | 6 +- packages/rehype-mathjax/lib/input.js | 10 +- packages/rehype-mathjax/lib/output-chtml.js | 8 +- packages/rehype-mathjax/lib/output-svg.js | 8 +- packages/rehype-mathjax/lib/renderer.js | 17 ++- packages/rehype-mathjax/package.json | 16 +-- packages/rehype-mathjax/svg.js | 12 +- packages/rehype-mathjax/test/index.js | 107 ++++++++-------- packages/remark-html-katex/index.js | 27 ++-- packages/remark-html-katex/package.json | 8 +- packages/remark-html-katex/test.js | 82 ++++++------ packages/remark-math/index.js | 11 +- packages/remark-math/package.json | 10 +- packages/remark-math/test.js | 118 +++++++++++------- 23 files changed, 303 insertions(+), 278 deletions(-) diff --git a/package.json b/package.json index 071e733..e6caa3c 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "Marc-Antoine Ouimet ", "Rongjian Zhang " ], + "type": "module", "devDependencies": { "c8": "^7.0.0", "dtslint": "^4.0.0", diff --git a/packages/rehype-katex/index.js b/packages/rehype-katex/index.js index 3e3ced5..d3eeb54 100644 --- a/packages/rehype-katex/index.js +++ b/packages/rehype-katex/index.js @@ -1,10 +1,8 @@ -const visit = require('unist-util-visit') -const katex = require('katex').renderToString -const unified = require('unified') -const parse = require('rehype-parse') -const toText = require('hast-util-to-text') - -module.exports = rehypeKatex +import visit from 'unist-util-visit' +import katex from 'katex' +import unified from 'unified' +import parse from 'rehype-parse' +import toText from 'hast-util-to-text' const assign = Object.assign @@ -12,7 +10,7 @@ const parseHtml = unified().use(parse, {fragment: true, position: false}) const source = 'rehype-katex' -function rehypeKatex(options) { +export default function rehypeKatex(options) { const settings = options || {} const throwOnError = settings.throwOnError || false @@ -35,7 +33,7 @@ function rehypeKatex(options) { let result try { - result = katex( + result = katex.renderToString( value, assign({}, settings, {displayMode: displayMode, throwOnError: true}) ) @@ -45,7 +43,7 @@ function rehypeKatex(options) { file[fn](error.message, element.position, origin) - result = katex( + result = katex.renderToString( value, assign({}, settings, { displayMode: displayMode, diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index cd9c5fa..9e1691a 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -29,12 +29,12 @@ "Junyoung Choi (https://rokt33r.github.io)", "Titus Wormer (https://wooorm.com)" ], + "sideEffects": false, + "type": "module", + "main": "index.js", "files": [ - "index.js", - "types/index.d.ts" + "index.js" ], - "main": "index.js", - "types": "types/index.d.ts", "dependencies": { "@types/katex": "^0.11.0", "hast-util-to-text": "^2.0.0", diff --git a/packages/rehype-katex/test.js b/packages/rehype-katex/test.js index 319c992..1d939fe 100644 --- a/packages/rehype-katex/test.js +++ b/packages/rehype-katex/test.js @@ -1,19 +1,19 @@ -const test = require('tape') -const katex = require('katex') -const unified = require('unified') -const parseMarkdown = require('remark-parse') -const remark2rehype = require('remark-rehype') -const parseHtml = require('rehype-parse') -const stringify = require('rehype-stringify') -const math = require('../remark-math') -const rehypeKatex = require('.') +import test from 'tape' +import katex from 'katex' +import unified from 'unified' +import remarkParse from 'remark-parse' +import remarkRehype from 'remark-rehype' +import rehypeParse from 'rehype-parse' +import rehypeStringify from 'rehype-stringify' +import remarkMath from '../remark-math/index.js' +import rehypeKatex from './index.js' test('rehype-katex', function (t) { t.deepEqual( unified() - .use(parseHtml, {fragment: true, position: false}) + .use(rehypeParse, {fragment: true, position: false}) .use(rehypeKatex) - .use(stringify) + .use(rehypeStringify) .processSync( [ '

Inline math \\alpha.

', @@ -23,8 +23,8 @@ test('rehype-katex', function (t) { ) .toString(), unified() - .use(parseHtml, {fragment: true, position: false}) - .use(stringify) + .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeStringify) .processSync( [ '

Inline math ' + @@ -42,11 +42,11 @@ test('rehype-katex', function (t) { t.deepEqual( unified() - .use(parseMarkdown, {position: false}) - .use(math) - .use(remark2rehype) + .use(remarkParse, {position: false}) + .use(remarkMath) + .use(remarkRehype) .use(rehypeKatex) - .use(stringify) + .use(rehypeStringify) .processSync( [ 'Inline math $\\alpha$.', @@ -60,8 +60,8 @@ test('rehype-katex', function (t) { ) .toString(), unified() - .use(parseHtml, {fragment: true, position: false}) - .use(stringify) + .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeStringify) .processSync( [ '

Inline math ' + @@ -79,16 +79,16 @@ test('rehype-katex', function (t) { t.deepEqual( unified() - .use(parseHtml, {fragment: true, position: false}) + .use(rehypeParse, {fragment: true, position: false}) .use(rehypeKatex) - .use(stringify) + .use(rehypeStringify) .processSync( '

Double math \\alpha.

' ) .toString(), unified() - .use(parseHtml, {fragment: true, position: false}) - .use(stringify) + .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeStringify) .processSync( '

Double math ' + katex.renderToString('\\alpha', {displayMode: true}) + @@ -102,14 +102,14 @@ test('rehype-katex', function (t) { t.deepEqual( unified() - .use(parseHtml, {fragment: true, position: false}) + .use(rehypeParse, {fragment: true, position: false}) .use(rehypeKatex, {macros: macros}) - .use(stringify) + .use(rehypeStringify) .processSync('\\RR') .toString(), unified() - .use(parseHtml, {fragment: true, position: false}) - .use(stringify) + .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeStringify) .processSync( '' + katex.renderToString('\\RR', {macros: macros}) + @@ -121,14 +121,14 @@ test('rehype-katex', function (t) { t.deepEqual( unified() - .use(parseHtml, {fragment: true, position: false}) + .use(rehypeParse, {fragment: true, position: false}) .use(rehypeKatex, {errorColor: 'orange'}) - .use(stringify) + .use(rehypeStringify) .processSync('\\alpa') .toString(), unified() - .use(parseHtml, {fragment: true, position: false}) - .use(stringify) + .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeStringify) .processSync( '' + katex.renderToString('\\alpa', { @@ -143,9 +143,9 @@ test('rehype-katex', function (t) { t.deepEqual( unified() - .use(parseHtml, {fragment: true}) + .use(rehypeParse, {fragment: true}) .use(rehypeKatex) - .use(stringify) + .use(rehypeStringify) .processSync( '

Lorem

\n

\\alpa

' ) @@ -158,9 +158,9 @@ test('rehype-katex', function (t) { try { unified() - .use(parseHtml, {fragment: true}) + .use(rehypeParse, {fragment: true}) .use(rehypeKatex, {throwOnError: true}) - .use(stringify) + .use(rehypeStringify) .processSync( '

Lorem

\n

\\alpa

' ) @@ -174,14 +174,14 @@ test('rehype-katex', function (t) { t.deepEqual( unified() - .use(parseHtml, {fragment: true, position: false}) + .use(rehypeParse, {fragment: true, position: false}) .use(rehypeKatex, {errorColor: 'orange', strict: 'ignore'}) - .use(stringify) + .use(rehypeStringify) .processSync('ê&') .toString(), unified() - .use(parseHtml, {fragment: true, position: false}) - .use(stringify) + .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeStringify) .processSync( 'ê&' ) diff --git a/packages/rehype-mathjax/browser.js b/packages/rehype-mathjax/browser.js index 6d8c2d6..d5545db 100644 --- a/packages/rehype-mathjax/browser.js +++ b/packages/rehype-mathjax/browser.js @@ -1,11 +1,12 @@ -const createPlugin = require('./lib/core') +import {createPlugin} from './lib/core.js' -module.exports = createPlugin( +const rehypeMathJaxBrowser = createPlugin( 'rehypeMathJaxBrowser', renderBrowser, false, true ) +export default rehypeMathJaxBrowser // To do next major: Make `options` match the format of MathJax options // `{tex: ...}` diff --git a/packages/rehype-mathjax/chtml.js b/packages/rehype-mathjax/chtml.js index cb467e3..8f15ed7 100644 --- a/packages/rehype-mathjax/chtml.js +++ b/packages/rehype-mathjax/chtml.js @@ -1,9 +1,11 @@ -const createInput = require('./lib/input') -const createOutput = require('./lib/output-chtml') -const createRenderer = require('./lib/renderer') -const createPlugin = require('./lib/core') +import {createInput} from './lib/input.js' +import {createOutput} from './lib/output-chtml.js' +import {createRenderer} from './lib/renderer.js' +import {createPlugin} from './lib/core.js' -module.exports = createPlugin('rehypeMathJaxCHtml', renderCHtml, true) +const rehypeMathJaxCHtml = createPlugin('rehypeMathJaxCHtml', renderCHtml, true) + +export default rehypeMathJaxCHtml function renderCHtml(inputOptions, outputOptions) { return createRenderer(createInput(inputOptions), createOutput(outputOptions)) diff --git a/packages/rehype-mathjax/index.js b/packages/rehype-mathjax/index.js index 98aecf8..1032b6e 100644 --- a/packages/rehype-mathjax/index.js +++ b/packages/rehype-mathjax/index.js @@ -1 +1,3 @@ -module.exports = require('./svg') +import rehypeMathJaxSvg from './svg.js' + +export default rehypeMathJaxSvg diff --git a/packages/rehype-mathjax/lib/adaptor.browser.js b/packages/rehype-mathjax/lib/adaptor.browser.js index 7ae81b5..8096f1f 100644 --- a/packages/rehype-mathjax/lib/adaptor.browser.js +++ b/packages/rehype-mathjax/lib/adaptor.browser.js @@ -1,2 +1,3 @@ -module.exports = - require('mathjax-full/js/adaptors/browserAdaptor').browserAdaptor +import {browserAdaptor} from 'mathjax-full/js/adaptors/browserAdaptor.js' + +export {browserAdaptor} diff --git a/packages/rehype-mathjax/lib/adaptor.js b/packages/rehype-mathjax/lib/adaptor.js index 42ab7f1..5dfeebe 100644 --- a/packages/rehype-mathjax/lib/adaptor.js +++ b/packages/rehype-mathjax/lib/adaptor.js @@ -1,8 +1,6 @@ -const JsDom = require('jsdom').JSDOM -const adaptor = require('mathjax-full/js/adaptors/jsdomAdaptor').jsdomAdaptor +import {JSDOM} from 'jsdom' +import {jsdomAdaptor} from 'mathjax-full/js/adaptors/jsdomAdaptor.js' -module.exports = createAdaptor - -function createAdaptor() { - return adaptor(JsDom) +export function createAdaptor() { + return jsdomAdaptor(JSDOM) } diff --git a/packages/rehype-mathjax/lib/core.js b/packages/rehype-mathjax/lib/core.js index db76590..a744010 100644 --- a/packages/rehype-mathjax/lib/core.js +++ b/packages/rehype-mathjax/lib/core.js @@ -1,10 +1,8 @@ -const visit = require('unist-util-visit') - -module.exports = createPlugin +import visit from 'unist-util-visit' // To do next major: Remove `chtml` and `browser` flags once all the options use // the same format. -function createPlugin(displayName, createRenderer, chtml, browser) { +export function createPlugin(displayName, createRenderer, chtml, browser) { attacher.displayName = displayName return attacher diff --git a/packages/rehype-mathjax/lib/input.js b/packages/rehype-mathjax/lib/input.js index 86a2de5..a73859c 100644 --- a/packages/rehype-mathjax/lib/input.js +++ b/packages/rehype-mathjax/lib/input.js @@ -1,8 +1,6 @@ -const Tex = require('mathjax-full/js/input/tex').TeX -const packages = require('mathjax-full/js/input/tex/AllPackages').AllPackages +import {TeX} from 'mathjax-full/js/input/tex.js' +import {AllPackages} from 'mathjax-full/js/input/tex/AllPackages.js' -module.exports = createInput - -function createInput(options) { - return new Tex(Object.assign({packages: packages}, options)) +export function createInput(options) { + return new TeX(Object.assign({packages: AllPackages}, options)) } diff --git a/packages/rehype-mathjax/lib/output-chtml.js b/packages/rehype-mathjax/lib/output-chtml.js index 304b0e7..e747e41 100644 --- a/packages/rehype-mathjax/lib/output-chtml.js +++ b/packages/rehype-mathjax/lib/output-chtml.js @@ -1,7 +1,5 @@ -const CHtml = require('mathjax-full/js/output/chtml').CHTML +import {CHTML} from 'mathjax-full/js/output/chtml.js' -module.exports = createOutput - -function createOutput(options) { - return new CHtml(options) +export function createOutput(options) { + return new CHTML(options) } diff --git a/packages/rehype-mathjax/lib/output-svg.js b/packages/rehype-mathjax/lib/output-svg.js index 8518ffd..c732cbc 100644 --- a/packages/rehype-mathjax/lib/output-svg.js +++ b/packages/rehype-mathjax/lib/output-svg.js @@ -1,7 +1,5 @@ -const Svg = require('mathjax-full/js/output/svg').SVG +import {SVG} from 'mathjax-full/js/output/svg.js' -module.exports = createOutput - -function createOutput(options) { - return new Svg(options) +export function createOutput(options) { + return new SVG(options) } diff --git a/packages/rehype-mathjax/lib/renderer.js b/packages/rehype-mathjax/lib/renderer.js index 1a6c927..d9c2ccf 100644 --- a/packages/rehype-mathjax/lib/renderer.js +++ b/packages/rehype-mathjax/lib/renderer.js @@ -1,10 +1,8 @@ -const mathjax = require('mathjax-full/js/mathjax').mathjax -const register = require('mathjax-full/js/handlers/html').RegisterHTMLHandler -const fromDom = require('hast-util-from-dom') -const toText = require('hast-util-to-text') -const createAdaptor = require('./adaptor') - -module.exports = renderer +import {mathjax} from 'mathjax-full/js/mathjax.js' +import {RegisterHTMLHandler} from 'mathjax-full/js/handlers/html.js' +import fromDom from 'hast-util-from-dom' +import toText from 'hast-util-to-text' +import {createAdaptor} from './adaptor.js' const adaptor = createAdaptor() @@ -18,9 +16,10 @@ const adaptor = createAdaptor() // `mathjax.handlers.unregister(handler)`. // That is to prevent memory leak in `mathjax.handlers` whenever a new instance // of the plugin is used. -register(adaptor) +/* eslint-disable-next-line new-cap */ +RegisterHTMLHandler(adaptor) -function renderer(input, output) { +export function createRenderer(input, output) { const doc = mathjax.document('', {InputJax: input, OutputJax: output}) return {render: render, styleSheet: styleSheet} diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index 4f5b126..bd617bf 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -24,24 +24,24 @@ "type": "opencollective", "url": "https://opencollective.com/unified" }, - "browser": { - "./lib/adaptor.js": "./lib/adaptor.browser.js" - }, "author": "TANIGUCHI Masaya (https://docs.casa)", "contributors": [ "TANIGUCHI Masaya (https://docs.casa)", "Titus Wormer (https://wooorm.com)" ], + "sideEffects": false, + "type": "module", + "main": "index.js", "files": [ "lib/", - "*.d.ts", "browser.js", "chtml.js", - "svg.js", - "index.js" + "index.js", + "svg.js" ], - "main": "index.js", - "types": "index.d.ts", + "browser": { + "./lib/adaptor.js": "./lib/adaptor.browser.js" + }, "dependencies": { "@types/mathjax": "^0.0.36", "hast-util-from-dom": "^3.0.0", diff --git a/packages/rehype-mathjax/svg.js b/packages/rehype-mathjax/svg.js index d5f1348..6cdae07 100644 --- a/packages/rehype-mathjax/svg.js +++ b/packages/rehype-mathjax/svg.js @@ -1,9 +1,11 @@ -const createInput = require('./lib/input') -const createOutput = require('./lib/output-svg') -const createRenderer = require('./lib/renderer') -const createPlugin = require('./lib/core') +import {createInput} from './lib/input.js' +import {createOutput} from './lib/output-svg.js' +import {createRenderer} from './lib/renderer.js' +import {createPlugin} from './lib/core.js' -module.exports = createPlugin('rehypeMathJaxSvg', renderSvg) +const rehypeMathJaxSvg = createPlugin('rehypeMathJaxSvg', renderSvg) + +export default rehypeMathJaxSvg function renderSvg(inputOptions, outputOptions) { return createRenderer(createInput(inputOptions), createOutput(outputOptions)) diff --git a/packages/rehype-mathjax/test/index.js b/packages/rehype-mathjax/test/index.js index 7ca8480..753c29f 100644 --- a/packages/rehype-mathjax/test/index.js +++ b/packages/rehype-mathjax/test/index.js @@ -1,24 +1,24 @@ -const path = require('path') -const test = require('tape') -const vfile = require('to-vfile') -const unified = require('unified') -const parseMarkdown = require('remark-parse') -const remark2rehype = require('remark-rehype') -const parseHtml = require('rehype-parse') -const stringify = require('rehype-stringify') -const math = require('../../remark-math') -const svg = require('..') -const chtml = require('../chtml') -const browser = require('../browser') +import path from 'path' +import test from 'tape' +import vfile from 'to-vfile' +import unified from 'unified' +import remarkParse from 'remark-parse' +import remarkRehype from 'remark-rehype' +import rehypeParse from 'rehype-parse' +import rehypeStringify from 'rehype-stringify' +import remarkMath from '../../remark-math/index.js' +import rehypeMathJaxSvg from '../svg.js' +import rehypeMathJaxChtml from '../chtml.js' +import rehypeMathJaxBrowser from '../browser.js' -const fixtures = path.join(__dirname, 'fixture') +const fixtures = path.join('test', 'fixture') test('rehype-mathjax', function (t) { t.equal( unified() - .use(parseHtml, {fragment: true}) - .use(svg) - .use(stringify) + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxSvg) + .use(rehypeStringify) .processSync(vfile.readSync({dirname: fixtures, basename: 'small.html'})) .toString(), String( @@ -29,7 +29,7 @@ test('rehype-mathjax', function (t) { t.throws( function () { - unified().use(chtml).freeze() + unified().use(rehypeMathJaxChtml).freeze() }, /rehype-mathjax: missing `fontURL` in options/, 'should crash for CHTML w/o `fontURL`' @@ -37,9 +37,9 @@ test('rehype-mathjax', function (t) { t.equal( unified() - .use(parseHtml, {fragment: true}) - .use(chtml, {fontURL: 'place/to/fonts'}) - .use(stringify) + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxChtml, {fontURL: 'place/to/fonts'}) + .use(rehypeStringify) .processSync(vfile.readSync({dirname: fixtures, basename: 'small.html'})) .toString(), String( @@ -50,9 +50,9 @@ test('rehype-mathjax', function (t) { t.equal( unified() - .use(parseHtml, {fragment: true}) - .use(browser) - .use(stringify) + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxBrowser) + .use(rehypeStringify) .processSync(vfile.readSync({dirname: fixtures, basename: 'small.html'})) .toString(), String(vfile.readSync({dirname: fixtures, basename: 'small-browser.html'})), @@ -61,11 +61,11 @@ test('rehype-mathjax', function (t) { t.equal( unified() - .use(parseMarkdown) - .use(math) - .use(remark2rehype) - .use(svg) - .use(stringify) + .use(remarkParse) + .use(remarkMath) + .use(remarkRehype) + .use(rehypeMathJaxSvg) + .use(rehypeStringify) .processSync(vfile.readSync({dirname: fixtures, basename: 'markdown.md'})) .toString(), String( @@ -76,9 +76,9 @@ test('rehype-mathjax', function (t) { t.equal( unified() - .use(parseHtml, {fragment: true}) - .use(svg) - .use(stringify) + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxSvg) + .use(rehypeStringify) .processSync(vfile.readSync({dirname: fixtures, basename: 'double.html'})) .toString(), String( @@ -89,9 +89,9 @@ test('rehype-mathjax', function (t) { t.equal( unified() - .use(parseHtml, {fragment: true}) - .use(svg) - .use(stringify) + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxSvg) + .use(rehypeStringify) .processSync(vfile.readSync({dirname: fixtures, basename: 'none.html'})) .toString(), String(vfile.readSync({dirname: fixtures, basename: 'none-svg.html'})), @@ -100,9 +100,9 @@ test('rehype-mathjax', function (t) { t.equal( unified() - .use(parseHtml) - .use(svg) - .use(stringify) + .use(rehypeParse) + .use(rehypeMathJaxSvg) + .use(rehypeStringify) .processSync( vfile.readSync({dirname: fixtures, basename: 'document.html'}) ) @@ -115,9 +115,12 @@ test('rehype-mathjax', function (t) { t.equal( unified() - .use(parseHtml, {fragment: true}) - .use(browser, {inlineMath: ['$', '$'], displayMath: ['$$', '$$']}) - .use(stringify) + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxBrowser, { + inlineMath: ['$', '$'], + displayMath: ['$$', '$$'] + }) + .use(rehypeStringify) .processSync(vfile.readSync({dirname: fixtures, basename: 'small.html'})) .toString(), String( @@ -131,9 +134,9 @@ test('rehype-mathjax', function (t) { t.equal( unified() - .use(parseHtml, {fragment: true}) - .use(svg, {tex: {tags: 'ams'}}) - .use(stringify) + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxSvg, {tex: {tags: 'ams'}}) + .use(rehypeStringify) .processSync( vfile.readSync({ dirname: fixtures, @@ -152,9 +155,9 @@ test('rehype-mathjax', function (t) { t.equal( unified() - .use(parseHtml, {fragment: true}) - .use(svg, {tex: {tags: 'ams'}}) - .use(stringify) + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxSvg, {tex: {tags: 'ams'}}) + .use(rehypeStringify) .processSync( vfile.readSync({ dirname: fixtures, @@ -173,9 +176,9 @@ test('rehype-mathjax', function (t) { t.equal( unified() - .use(parseHtml, {fragment: true}) - .use(chtml, {fontURL: 'place/to/fonts', tex: {tags: 'ams'}}) - .use(stringify) + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxChtml, {fontURL: 'place/to/fonts', tex: {tags: 'ams'}}) + .use(rehypeStringify) .processSync( vfile.readSync({ dirname: fixtures, @@ -195,9 +198,9 @@ test('rehype-mathjax', function (t) { t.equal( (() => { const processor = unified() - .use(parseHtml, {fragment: true}) - .use(svg, {tex: {tags: 'ams'}}) - .use(stringify) + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxSvg, {tex: {tags: 'ams'}}) + .use(rehypeStringify) return ['equation-numbering-1.html', 'equation-numbering-2.html'] .map((basename) => processor diff --git a/packages/remark-html-katex/index.js b/packages/remark-html-katex/index.js index a1d8543..dc3b06e 100644 --- a/packages/remark-html-katex/index.js +++ b/packages/remark-html-katex/index.js @@ -1,17 +1,13 @@ -const visit = require('unist-util-visit') -const katex = require('katex').renderToString -const unified = require('unified') -const parse = require('rehype-parse') +import visit from 'unist-util-visit' +import katex from 'katex' +import unified from 'unified' +import rehypeParse from 'rehype-parse' -module.exports = htmlKatex - -const assign = Object.assign - -const parseHtml = unified().use(parse, {fragment: true, position: false}) +const parseHtml = unified().use(rehypeParse, {fragment: true, position: false}) const source = 'remark-html-katex' -function htmlKatex(options) { +export default function remarkHtmlKatex(options) { const settings = options || {} const throwOnError = settings.throwOnError || false @@ -25,9 +21,12 @@ function htmlKatex(options) { let result try { - result = katex( + result = katex.renderToString( node.value, - assign({}, settings, {displayMode: displayMode, throwOnError: true}) + Object.assign({}, settings, { + displayMode: displayMode, + throwOnError: true + }) ) } catch (error) { const fn = throwOnError ? 'fail' : 'message' @@ -35,9 +34,9 @@ function htmlKatex(options) { file[fn](error.message, node.position, origin) - result = katex( + result = katex.renderToString( node.value, - assign({}, settings, { + Object.assign({}, settings, { displayMode: displayMode, throwOnError: false, strict: 'ignore' diff --git a/packages/remark-html-katex/package.json b/packages/remark-html-katex/package.json index f69515f..b5710e9 100644 --- a/packages/remark-html-katex/package.json +++ b/packages/remark-html-katex/package.json @@ -27,12 +27,12 @@ "Junyoung Choi (https://rokt33r.github.io)", "Titus Wormer (https://wooorm.com)" ], + "sideEffects": false, + "type": "module", + "main": "index.js", "files": [ - "index.js", - "types/index.d.ts" + "index.js" ], - "main": "index.js", - "types": "types/index.d.ts", "dependencies": { "@types/katex": "^0.11.0", "katex": "^0.12.0", diff --git a/packages/remark-html-katex/test.js b/packages/remark-html-katex/test.js index 0bbc1ff..54456a4 100644 --- a/packages/remark-html-katex/test.js +++ b/packages/remark-html-katex/test.js @@ -1,20 +1,20 @@ -const test = require('tape') -const katex = require('katex') -const unified = require('unified') -const parseMarkdown = require('remark-parse') -const parseHtml = require('rehype-parse') -const stringify = require('rehype-stringify') -const html = require('remark-html') -const math = require('../remark-math') -const htmlKatex = require('.') +import test from 'tape' +import katex from 'katex' +import unified from 'unified' +import remarkParse from 'remark-parse' +import rehypeParse from 'rehype-parse' +import rehypeStringify from 'rehype-stringify' +import remarkHtml from 'remark-html' +import remarkMath from '../remark-math/index.js' +import remarkHtmlKatex from './index.js' test('remark-html-katex', function (t) { t.deepEqual( unified() - .use(parseMarkdown, {position: false}) - .use(math) - .use(htmlKatex) - .use(html) + .use(remarkParse, {position: false}) + .use(remarkMath) + .use(remarkHtmlKatex) + .use(remarkHtml) .processSync( [ 'Inline math $\\alpha$.', @@ -28,8 +28,8 @@ test('remark-html-katex', function (t) { ) .toString(), unified() - .use(parseHtml, {fragment: true, position: false}) - .use(stringify) + .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeStringify) .processSync( [ '

Inline math ' + @@ -50,15 +50,15 @@ test('remark-html-katex', function (t) { t.deepEqual( unified() - .use(parseMarkdown, {position: false}) - .use(math) - .use(htmlKatex, {macros: macros}) - .use(html) + .use(remarkParse, {position: false}) + .use(remarkMath) + .use(remarkHtmlKatex, {macros: macros}) + .use(remarkHtml) .processSync('$\\RR$') .toString(), unified() - .use(parseHtml, {fragment: true, position: false}) - .use(stringify) + .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeStringify) .processSync( '

' + katex.renderToString('\\RR', {macros: macros}) + @@ -70,15 +70,15 @@ test('remark-html-katex', function (t) { t.deepEqual( unified() - .use(parseMarkdown, {position: false}) - .use(math) - .use(htmlKatex, {errorColor: 'orange'}) - .use(html) + .use(remarkParse, {position: false}) + .use(remarkMath) + .use(remarkHtmlKatex, {errorColor: 'orange'}) + .use(remarkHtml) .processSync('$\\alpa$') .toString(), unified() - .use(parseHtml, {fragment: true, position: false}) - .use(stringify) + .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeStringify) .processSync( '

' + katex.renderToString('\\alpa', { @@ -93,10 +93,10 @@ test('remark-html-katex', function (t) { t.deepLooseEqual( unified() - .use(parseMarkdown) - .use(math) - .use(htmlKatex) - .use(html) + .use(remarkParse) + .use(remarkMath) + .use(remarkHtmlKatex) + .use(remarkHtml) .processSync('Lorem\n$\\alpa$') .messages.map(String), [ @@ -107,10 +107,10 @@ test('remark-html-katex', function (t) { try { unified() - .use(parseMarkdown) - .use(math) - .use(htmlKatex, {throwOnError: true}) - .use(html) + .use(remarkParse) + .use(remarkMath) + .use(remarkHtmlKatex, {throwOnError: true}) + .use(remarkHtml) .processSync('Lorem\n$\\alpa$') } catch (error) { t.equal( @@ -122,15 +122,15 @@ test('remark-html-katex', function (t) { t.deepEqual( unified() - .use(parseMarkdown, {position: false}) - .use(math) - .use(htmlKatex, {errorColor: 'orange', strict: 'ignore'}) - .use(html) + .use(remarkParse, {position: false}) + .use(remarkMath) + .use(remarkHtmlKatex, {errorColor: 'orange', strict: 'ignore'}) + .use(remarkHtml) .processSync('$ê&$') .toString(), unified() - .use(parseHtml, {fragment: true, position: false}) - .use(stringify) + .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeStringify) .processSync( '

ê&

\n' ) diff --git a/packages/remark-math/index.js b/packages/remark-math/index.js index 06bfe1c..2064cc8 100644 --- a/packages/remark-math/index.js +++ b/packages/remark-math/index.js @@ -1,13 +1,10 @@ -'use strict' +import syntax from 'micromark-extension-math' +import fromMarkdown from 'mdast-util-math/from-markdown.js' +import toMarkdown from 'mdast-util-math/to-markdown.js' -var syntax = require('micromark-extension-math') -var fromMarkdown = require('mdast-util-math/from-markdown') -var toMarkdown = require('mdast-util-math/to-markdown') var warningIssued -module.exports = math - -function math() { +export default function remarkMath() { var data = this.data() // Old remark. diff --git a/packages/remark-math/package.json b/packages/remark-math/package.json index 7d4ee4e..55ca450 100644 --- a/packages/remark-math/package.json +++ b/packages/remark-math/package.json @@ -26,15 +26,15 @@ "Junyoung Choi (https://rokt33r.github.io)", "Titus Wormer (https://wooorm.com)" ], + "sideEffects": false, + "type": "module", + "main": "index.js", "files": [ - "index.js", "block.js", + "index.js", "inline.js", - "util.js", - "types/index.d.ts" + "util.js" ], - "main": "index.js", - "types": "types/index.d.ts", "dependencies": { "mdast-util-math": "^0.1.0", "micromark-extension-math": "^0.1.0" diff --git a/packages/remark-math/test.js b/packages/remark-math/test.js index ea52320..dcdc4cc 100644 --- a/packages/remark-math/test.js +++ b/packages/remark-math/test.js @@ -1,25 +1,25 @@ -const test = require('tape') -const unified = require('unified') -const parse = require('remark-parse') -const remark2rehype = require('remark-rehype') -const rehypeStringify = require('rehype-stringify') -const stringify = require('remark-stringify') -const u = require('unist-builder') -const removePosition = require('unist-util-remove-position') -const math = require('.') +import test from 'tape' +import unified from 'unified' +import remarkParse from 'remark-parse' +import remarkRehype from 'remark-rehype' +import rehypeStringify from 'rehype-stringify' +import remarkStringify from 'remark-stringify' +import u from 'unist-builder' +import removePosition from 'unist-util-remove-position' +import remarkMath from './index.js' -test('remark-math', function (t) { +test('remarkMath', function (t) { const toHtml = unified() - .use(parse) - .use(math) - .use(remark2rehype) + .use(remarkParse) + .use(remarkMath) + .use(remarkRehype) .use(rehypeStringify) t.deepEqual( removePosition( unified() - .use(parse) - .use(math) + .use(remarkParse) + .use(remarkMath) .parse('Math $\\alpha$\n\n$$\n\\beta+\\gamma\n$$'), true ), @@ -55,13 +55,19 @@ test('remark-math', function (t) { ) t.deepEqual( - removePosition(unified().use(parse).use(math).parse('\\$\\alpha$'), true), + removePosition( + unified().use(remarkParse).use(remarkMath).parse('\\$\\alpha$'), + true + ), u('root', [u('paragraph', [u('text', '$\\alpha$')])]), 'should ignore an escaped opening dollar sign' ) t.deepEqual( - removePosition(unified().use(parse).use(math).parse('$\\alpha\\$'), true), + removePosition( + unified().use(remarkParse).use(remarkMath).parse('$\\alpha\\$'), + true + ), u('root', [ u('paragraph', [ u( @@ -81,7 +87,10 @@ test('remark-math', function (t) { ) t.deepEqual( - removePosition(unified().use(parse).use(math).parse('\\\\$\\alpha$'), true), + removePosition( + unified().use(remarkParse).use(remarkMath).parse('\\\\$\\alpha$'), + true + ), u('root', [ u('paragraph', [ u('text', '\\'), @@ -102,13 +111,19 @@ test('remark-math', function (t) { ) t.deepEqual( - removePosition(unified().use(parse).use(math).parse('`$`\\alpha$'), true), + removePosition( + unified().use(remarkParse).use(remarkMath).parse('`$`\\alpha$'), + true + ), u('root', [u('paragraph', [u('inlineCode', '$'), u('text', '\\alpha$')])]), 'should ignore dollar signs in inline code (#1)' ) t.deepEqual( - removePosition(unified().use(parse).use(math).parse('$\\alpha`$`'), true), + removePosition( + unified().use(remarkParse).use(remarkMath).parse('$\\alpha`$`'), + true + ), u('root', [ u('paragraph', [ u( @@ -129,7 +144,10 @@ test('remark-math', function (t) { ) t.deepEqual( - removePosition(unified().use(parse).use(math).parse('$`\\alpha`$'), true), + removePosition( + unified().use(remarkParse).use(remarkMath).parse('$`\\alpha`$'), + true + ), u('root', [ u('paragraph', [ u( @@ -150,7 +168,7 @@ test('remark-math', function (t) { t.deepEqual( removePosition( - unified().use(parse).use(math).parse('$$ \\alpha$ $$'), + unified().use(remarkParse).use(remarkMath).parse('$$ \\alpha$ $$'), true ), u('root', [ @@ -173,7 +191,7 @@ test('remark-math', function (t) { t.deepEqual( removePosition( - unified().use(parse).use(math).parse('$$\n\\alpha\\$\n$$'), + unified().use(remarkParse).use(remarkMath).parse('$$\n\\alpha\\$\n$$'), true ), u('root', [ @@ -195,7 +213,10 @@ test('remark-math', function (t) { t.deepEqual( removePosition( - unified().use(parse).use(math).parse('tango\n$$\n\\alpha\n$$'), + unified() + .use(remarkParse) + .use(remarkMath) + .parse('tango\n$$\n\\alpha\n$$'), true ), u('root', [ @@ -217,7 +238,10 @@ test('remark-math', function (t) { ) t.deepEqual( - removePosition(unified().use(parse).use(math).parse('$$\\alpha$$'), true), + removePosition( + unified().use(remarkParse).use(remarkMath).parse('$$\\alpha$$'), + true + ), u('root', [ u('paragraph', [ u( @@ -238,7 +262,7 @@ test('remark-math', function (t) { t.deepEqual( removePosition( - unified().use(parse).use(math).parse('$$$\n\\alpha\n$$$'), + unified().use(remarkParse).use(remarkMath).parse('$$$\n\\alpha\n$$$'), true ), u('root', [ @@ -260,7 +284,10 @@ test('remark-math', function (t) { t.deepEqual( removePosition( - unified().use(parse).use(math).parse(' $$\n \\alpha\n $$'), + unified() + .use(remarkParse) + .use(remarkMath) + .parse(' $$\n \\alpha\n $$'), true ), u('root', [ @@ -282,9 +309,9 @@ test('remark-math', function (t) { t.deepEqual( unified() - .use(parse) - .use(stringify) - .use(math) + .use(remarkParse) + .use(remarkStringify) + .use(remarkMath) .processSync('Math $\\alpha$\n\n$$\n\\beta+\\gamma\n$$\n') .toString(), 'Math $\\alpha$\n\n$$\n\\beta+\\gamma\n$$\n', @@ -293,9 +320,9 @@ test('remark-math', function (t) { t.deepEqual( unified() - .use(parse) - .use(stringify) - .use(math) + .use(remarkParse) + .use(remarkStringify) + .use(remarkMath) .processSync('> $$\n> \\alpha\\beta\n> $$\n') .toString(), '> $$\n> \\alpha\\beta\n> $$\n', @@ -336,8 +363,8 @@ test('remark-math', function (t) { t.deepEqual( removePosition( unified() - .use(parse) - .use(math) + .use(remarkParse) + .use(remarkMath) .parse('$$\n\\alpha\n$$\n```\nbravo\n```\n'), true ), @@ -360,7 +387,10 @@ test('remark-math', function (t) { ) t.deepEqual( - removePosition(unified().use(parse).use(math).parse('$$\\alpha$$'), true), + removePosition( + unified().use(remarkParse).use(remarkMath).parse('$$\\alpha$$'), + true + ), u('root', [ u('paragraph', [ u( @@ -381,8 +411,8 @@ test('remark-math', function (t) { t.deepEqual( unified() - .use(stringify) - .use(math) + .use(remarkStringify) + .use(remarkMath) .stringify( u('root', [ u('paragraph', [u('text', 'Math '), u('inlineMath', '\\alpha')]), @@ -396,9 +426,9 @@ test('remark-math', function (t) { t.deepEqual( unified() - .use(parse) - .use(stringify) - .use(math) + .use(remarkParse) + .use(remarkStringify) + .use(remarkMath) .processSync('$$\\alpha$$') .toString(), '$\\alpha$\n', @@ -407,9 +437,9 @@ test('remark-math', function (t) { t.deepEqual( unified() - .use(parse) - .use(stringify) - .use(math) + .use(remarkParse) + .use(remarkStringify) + .use(remarkMath) .processSync('$$\\alpha$$') .toString(), '$\\alpha$\n', From 7efe5f6b60a021de1285968a52771ed7185b25d5 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 7 Aug 2021 13:55:10 +0200 Subject: [PATCH 20/98] Remove use of hidden API --- packages/rehype-katex/index.js | 5 +++-- packages/rehype-katex/package.json | 1 + packages/rehype-katex/test.js | 22 +++++++++++----------- packages/remark-html-katex/index.js | 5 +++-- packages/remark-html-katex/package.json | 1 + packages/remark-html-katex/test.js | 10 +++++----- 6 files changed, 24 insertions(+), 20 deletions(-) diff --git a/packages/rehype-katex/index.js b/packages/rehype-katex/index.js index d3eeb54..73192e0 100644 --- a/packages/rehype-katex/index.js +++ b/packages/rehype-katex/index.js @@ -1,4 +1,5 @@ import visit from 'unist-util-visit' +import removePosition from 'unist-util-remove-position' import katex from 'katex' import unified from 'unified' import parse from 'rehype-parse' @@ -6,7 +7,7 @@ import toText from 'hast-util-to-text' const assign = Object.assign -const parseHtml = unified().use(parse, {fragment: true, position: false}) +const parseHtml = unified().use(parse, {fragment: true}) const source = 'rehype-katex' @@ -53,7 +54,7 @@ export default function rehypeKatex(options) { ) } - element.children = parseHtml.parse(result).children + element.children = removePosition(parseHtml.parse(result), true).children } } } diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index 9e1691a..eb7a18c 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -41,6 +41,7 @@ "katex": "^0.13.0", "rehype-parse": "^7.0.0", "unified": "^9.0.0", + "unist-util-remove-position": "^3.0.0", "unist-util-visit": "^2.0.0" }, "scripts": { diff --git a/packages/rehype-katex/test.js b/packages/rehype-katex/test.js index 1d939fe..3fdad67 100644 --- a/packages/rehype-katex/test.js +++ b/packages/rehype-katex/test.js @@ -11,7 +11,7 @@ import rehypeKatex from './index.js' test('rehype-katex', function (t) { t.deepEqual( unified() - .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeParse, {fragment: true}) .use(rehypeKatex) .use(rehypeStringify) .processSync( @@ -23,7 +23,7 @@ test('rehype-katex', function (t) { ) .toString(), unified() - .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeParse, {fragment: true}) .use(rehypeStringify) .processSync( [ @@ -60,7 +60,7 @@ test('rehype-katex', function (t) { ) .toString(), unified() - .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeParse, {fragment: true}) .use(rehypeStringify) .processSync( [ @@ -79,7 +79,7 @@ test('rehype-katex', function (t) { t.deepEqual( unified() - .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeParse, {fragment: true}) .use(rehypeKatex) .use(rehypeStringify) .processSync( @@ -87,7 +87,7 @@ test('rehype-katex', function (t) { ) .toString(), unified() - .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeParse, {fragment: true}) .use(rehypeStringify) .processSync( '

Double math ' + @@ -102,13 +102,13 @@ test('rehype-katex', function (t) { t.deepEqual( unified() - .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeParse, {fragment: true}) .use(rehypeKatex, {macros: macros}) .use(rehypeStringify) .processSync('\\RR') .toString(), unified() - .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeParse, {fragment: true}) .use(rehypeStringify) .processSync( '' + @@ -121,13 +121,13 @@ test('rehype-katex', function (t) { t.deepEqual( unified() - .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeParse, {fragment: true}) .use(rehypeKatex, {errorColor: 'orange'}) .use(rehypeStringify) .processSync('\\alpa') .toString(), unified() - .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeParse, {fragment: true}) .use(rehypeStringify) .processSync( '' + @@ -174,13 +174,13 @@ test('rehype-katex', function (t) { t.deepEqual( unified() - .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeParse, {fragment: true}) .use(rehypeKatex, {errorColor: 'orange', strict: 'ignore'}) .use(rehypeStringify) .processSync('ê&') .toString(), unified() - .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeParse, {fragment: true}) .use(rehypeStringify) .processSync( 'ê&' diff --git a/packages/remark-html-katex/index.js b/packages/remark-html-katex/index.js index dc3b06e..594ff3c 100644 --- a/packages/remark-html-katex/index.js +++ b/packages/remark-html-katex/index.js @@ -1,9 +1,10 @@ import visit from 'unist-util-visit' +import removePosition from 'unist-util-remove-position' import katex from 'katex' import unified from 'unified' import rehypeParse from 'rehype-parse' -const parseHtml = unified().use(rehypeParse, {fragment: true, position: false}) +const parseHtml = unified().use(rehypeParse, {fragment: true}) const source = 'remark-html-katex' @@ -44,7 +45,7 @@ export default function remarkHtmlKatex(options) { ) } - node.data.hChildren = parseHtml.parse(result).children + node.data.hChildren = removePosition(parseHtml.parse(result)).children } } } diff --git a/packages/remark-html-katex/package.json b/packages/remark-html-katex/package.json index b5710e9..8961bbd 100644 --- a/packages/remark-html-katex/package.json +++ b/packages/remark-html-katex/package.json @@ -38,6 +38,7 @@ "katex": "^0.12.0", "rehype-parse": "^7.0.0", "unified": "^9.0.0", + "unist-util-remove-position": "^3.0.0", "unist-util-visit": "^2.0.0" }, "scripts": { diff --git a/packages/remark-html-katex/test.js b/packages/remark-html-katex/test.js index 54456a4..4119950 100644 --- a/packages/remark-html-katex/test.js +++ b/packages/remark-html-katex/test.js @@ -11,7 +11,7 @@ import remarkHtmlKatex from './index.js' test('remark-html-katex', function (t) { t.deepEqual( unified() - .use(remarkParse, {position: false}) + .use(remarkParse) .use(remarkMath) .use(remarkHtmlKatex) .use(remarkHtml) @@ -28,7 +28,7 @@ test('remark-html-katex', function (t) { ) .toString(), unified() - .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeParse, {fragment: true}) .use(rehypeStringify) .processSync( [ @@ -57,7 +57,7 @@ test('remark-html-katex', function (t) { .processSync('$\\RR$') .toString(), unified() - .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeParse, {fragment: true}) .use(rehypeStringify) .processSync( '

' + @@ -77,7 +77,7 @@ test('remark-html-katex', function (t) { .processSync('$\\alpa$') .toString(), unified() - .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeParse, {fragment: true}) .use(rehypeStringify) .processSync( '

' + @@ -129,7 +129,7 @@ test('remark-html-katex', function (t) { .processSync('$ê&$') .toString(), unified() - .use(rehypeParse, {fragment: true, position: false}) + .use(rehypeParse, {fragment: true}) .use(rehypeStringify) .processSync( '

ê&

\n' From ed4b492d9581dd343df59fe5f130f2a06d420ae0 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 7 Aug 2021 13:58:58 +0200 Subject: [PATCH 21/98] Reorganize files --- packages/rehype-mathjax/browser.js | 2 +- packages/rehype-mathjax/chtml.js | 13 ++++++++----- ...adaptor.browser.js => create-adaptor.browser.js} | 0 .../lib/{adaptor.js => create-adaptor.js} | 0 .../lib/{input.js => create-input.js} | 0 .../lib/{output-chtml.js => create-output-chtml.js} | 2 +- .../lib/{output-svg.js => create-output-svg.js} | 2 +- .../lib/{core.js => create-plugin.js} | 0 .../lib/{renderer.js => create-renderer.js} | 2 +- packages/rehype-mathjax/package.json | 2 +- packages/rehype-mathjax/svg.js | 13 ++++++++----- 11 files changed, 21 insertions(+), 15 deletions(-) rename packages/rehype-mathjax/lib/{adaptor.browser.js => create-adaptor.browser.js} (100%) rename packages/rehype-mathjax/lib/{adaptor.js => create-adaptor.js} (100%) rename packages/rehype-mathjax/lib/{input.js => create-input.js} (100%) rename packages/rehype-mathjax/lib/{output-chtml.js => create-output-chtml.js} (65%) rename packages/rehype-mathjax/lib/{output-svg.js => create-output-svg.js} (64%) rename packages/rehype-mathjax/lib/{core.js => create-plugin.js} (100%) rename packages/rehype-mathjax/lib/{renderer.js => create-renderer.js} (96%) diff --git a/packages/rehype-mathjax/browser.js b/packages/rehype-mathjax/browser.js index d5545db..fbbacb9 100644 --- a/packages/rehype-mathjax/browser.js +++ b/packages/rehype-mathjax/browser.js @@ -1,4 +1,4 @@ -import {createPlugin} from './lib/core.js' +import {createPlugin} from './lib/create-plugin.js' const rehypeMathJaxBrowser = createPlugin( 'rehypeMathJaxBrowser', diff --git a/packages/rehype-mathjax/chtml.js b/packages/rehype-mathjax/chtml.js index 8f15ed7..7aa2bcd 100644 --- a/packages/rehype-mathjax/chtml.js +++ b/packages/rehype-mathjax/chtml.js @@ -1,12 +1,15 @@ -import {createInput} from './lib/input.js' -import {createOutput} from './lib/output-chtml.js' -import {createRenderer} from './lib/renderer.js' -import {createPlugin} from './lib/core.js' +import {createInput} from './lib/create-input.js' +import {createOutputChtml} from './lib/create-output-chtml.js' +import {createRenderer} from './lib/create-renderer.js' +import {createPlugin} from './lib/create-plugin.js' const rehypeMathJaxCHtml = createPlugin('rehypeMathJaxCHtml', renderCHtml, true) export default rehypeMathJaxCHtml function renderCHtml(inputOptions, outputOptions) { - return createRenderer(createInput(inputOptions), createOutput(outputOptions)) + return createRenderer( + createInput(inputOptions), + createOutputChtml(outputOptions) + ) } diff --git a/packages/rehype-mathjax/lib/adaptor.browser.js b/packages/rehype-mathjax/lib/create-adaptor.browser.js similarity index 100% rename from packages/rehype-mathjax/lib/adaptor.browser.js rename to packages/rehype-mathjax/lib/create-adaptor.browser.js diff --git a/packages/rehype-mathjax/lib/adaptor.js b/packages/rehype-mathjax/lib/create-adaptor.js similarity index 100% rename from packages/rehype-mathjax/lib/adaptor.js rename to packages/rehype-mathjax/lib/create-adaptor.js diff --git a/packages/rehype-mathjax/lib/input.js b/packages/rehype-mathjax/lib/create-input.js similarity index 100% rename from packages/rehype-mathjax/lib/input.js rename to packages/rehype-mathjax/lib/create-input.js diff --git a/packages/rehype-mathjax/lib/output-chtml.js b/packages/rehype-mathjax/lib/create-output-chtml.js similarity index 65% rename from packages/rehype-mathjax/lib/output-chtml.js rename to packages/rehype-mathjax/lib/create-output-chtml.js index e747e41..e0e6b32 100644 --- a/packages/rehype-mathjax/lib/output-chtml.js +++ b/packages/rehype-mathjax/lib/create-output-chtml.js @@ -1,5 +1,5 @@ import {CHTML} from 'mathjax-full/js/output/chtml.js' -export function createOutput(options) { +export function createOutputChtml(options) { return new CHTML(options) } diff --git a/packages/rehype-mathjax/lib/output-svg.js b/packages/rehype-mathjax/lib/create-output-svg.js similarity index 64% rename from packages/rehype-mathjax/lib/output-svg.js rename to packages/rehype-mathjax/lib/create-output-svg.js index c732cbc..e3115a3 100644 --- a/packages/rehype-mathjax/lib/output-svg.js +++ b/packages/rehype-mathjax/lib/create-output-svg.js @@ -1,5 +1,5 @@ import {SVG} from 'mathjax-full/js/output/svg.js' -export function createOutput(options) { +export function createOutputSvg(options) { return new SVG(options) } diff --git a/packages/rehype-mathjax/lib/core.js b/packages/rehype-mathjax/lib/create-plugin.js similarity index 100% rename from packages/rehype-mathjax/lib/core.js rename to packages/rehype-mathjax/lib/create-plugin.js diff --git a/packages/rehype-mathjax/lib/renderer.js b/packages/rehype-mathjax/lib/create-renderer.js similarity index 96% rename from packages/rehype-mathjax/lib/renderer.js rename to packages/rehype-mathjax/lib/create-renderer.js index d9c2ccf..c3f943c 100644 --- a/packages/rehype-mathjax/lib/renderer.js +++ b/packages/rehype-mathjax/lib/create-renderer.js @@ -2,7 +2,7 @@ import {mathjax} from 'mathjax-full/js/mathjax.js' import {RegisterHTMLHandler} from 'mathjax-full/js/handlers/html.js' import fromDom from 'hast-util-from-dom' import toText from 'hast-util-to-text' -import {createAdaptor} from './adaptor.js' +import {createAdaptor} from './create-adaptor.js' const adaptor = createAdaptor() diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index bd617bf..631caca 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -40,7 +40,7 @@ "svg.js" ], "browser": { - "./lib/adaptor.js": "./lib/adaptor.browser.js" + "./lib/create-adaptor.js": "./lib/create-adaptor.browser.js" }, "dependencies": { "@types/mathjax": "^0.0.36", diff --git a/packages/rehype-mathjax/svg.js b/packages/rehype-mathjax/svg.js index 6cdae07..cbe9be5 100644 --- a/packages/rehype-mathjax/svg.js +++ b/packages/rehype-mathjax/svg.js @@ -1,12 +1,15 @@ -import {createInput} from './lib/input.js' -import {createOutput} from './lib/output-svg.js' -import {createRenderer} from './lib/renderer.js' -import {createPlugin} from './lib/core.js' +import {createInput} from './lib/create-input.js' +import {createOutputSvg} from './lib/create-output-svg.js' +import {createRenderer} from './lib/create-renderer.js' +import {createPlugin} from './lib/create-plugin.js' const rehypeMathJaxSvg = createPlugin('rehypeMathJaxSvg', renderSvg) export default rehypeMathJaxSvg function renderSvg(inputOptions, outputOptions) { - return createRenderer(createInput(inputOptions), createOutput(outputOptions)) + return createRenderer( + createInput(inputOptions), + createOutputSvg(outputOptions) + ) } From 769a8278d7d502dccc38b4688935eadeda092765 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 7 Aug 2021 14:04:12 +0200 Subject: [PATCH 22/98] Update dev-dependencies --- package.json | 21 +++++---- packages/rehype-mathjax/test/index.js | 66 +++++++++++---------------- packages/remark-math/test.js | 6 +-- 3 files changed, 41 insertions(+), 52 deletions(-) diff --git a/package.json b/package.json index e6caa3c..42ed609 100644 --- a/package.json +++ b/package.json @@ -21,19 +21,20 @@ "dtslint": "^4.0.0", "lerna": "^4.0.0", "prettier": "^2.0.0", - "rehype-parse": "^7.0.0", - "rehype-stringify": "^8.0.0", - "remark-cli": "^9.0.0", - "remark-html": "^13.0.0", - "remark-parse": "^9.0.0", + "rehype-parse": "^8.0.0", + "rehype-stringify": "^9.0.0", + "remark-cli": "^10.0.0", + "remark-html": "^14.0.0", + "remark-parse": "^10.0.0", "remark-preset-wooorm": "^8.0.0", - "remark-rehype": "^8.0.0", - "remark-stringify": "^9.0.0", + "remark-rehype": "^9.0.0", + "remark-stringify": "^10.0.0", "tape": "^5.0.0", + "to-vfile": "^7.0.0", "typescript": "^4.0.0", - "unified": "^9.0.0", - "unist-builder": "^2.0.0", - "unist-util-remove-position": "^3.0.0", + "unified": "^10.0.0", + "unist-builder": "^3.0.0", + "unist-util-remove-position": "^4.0.0", "xo": "^0.38.0" }, "scripts": { diff --git a/packages/rehype-mathjax/test/index.js b/packages/rehype-mathjax/test/index.js index 753c29f..acb82b6 100644 --- a/packages/rehype-mathjax/test/index.js +++ b/packages/rehype-mathjax/test/index.js @@ -1,7 +1,7 @@ import path from 'path' import test from 'tape' -import vfile from 'to-vfile' -import unified from 'unified' +import {readSync} from 'to-vfile' +import {unified} from 'unified' import remarkParse from 'remark-parse' import remarkRehype from 'remark-rehype' import rehypeParse from 'rehype-parse' @@ -19,11 +19,9 @@ test('rehype-mathjax', function (t) { .use(rehypeParse, {fragment: true}) .use(rehypeMathJaxSvg) .use(rehypeStringify) - .processSync(vfile.readSync({dirname: fixtures, basename: 'small.html'})) + .processSync(readSync({dirname: fixtures, basename: 'small.html'})) .toString(), - String( - vfile.readSync({dirname: fixtures, basename: 'small-svg.html'}) - ).trim(), + String(readSync({dirname: fixtures, basename: 'small-svg.html'})).trim(), 'should render SVG' ) @@ -40,11 +38,9 @@ test('rehype-mathjax', function (t) { .use(rehypeParse, {fragment: true}) .use(rehypeMathJaxChtml, {fontURL: 'place/to/fonts'}) .use(rehypeStringify) - .processSync(vfile.readSync({dirname: fixtures, basename: 'small.html'})) + .processSync(readSync({dirname: fixtures, basename: 'small.html'})) .toString(), - String( - vfile.readSync({dirname: fixtures, basename: 'small-chtml.html'}) - ).trim(), + String(readSync({dirname: fixtures, basename: 'small-chtml.html'})).trim(), 'should render CHTML' ) @@ -53,9 +49,9 @@ test('rehype-mathjax', function (t) { .use(rehypeParse, {fragment: true}) .use(rehypeMathJaxBrowser) .use(rehypeStringify) - .processSync(vfile.readSync({dirname: fixtures, basename: 'small.html'})) + .processSync(readSync({dirname: fixtures, basename: 'small.html'})) .toString(), - String(vfile.readSync({dirname: fixtures, basename: 'small-browser.html'})), + String(readSync({dirname: fixtures, basename: 'small-browser.html'})), 'should render browser' ) @@ -66,11 +62,9 @@ test('rehype-mathjax', function (t) { .use(remarkRehype) .use(rehypeMathJaxSvg) .use(rehypeStringify) - .processSync(vfile.readSync({dirname: fixtures, basename: 'markdown.md'})) + .processSync(readSync({dirname: fixtures, basename: 'markdown.md'})) .toString(), - String( - vfile.readSync({dirname: fixtures, basename: 'markdown-svg.html'}) - ).trim(), + String(readSync({dirname: fixtures, basename: 'markdown-svg.html'})).trim(), 'should integrate with `remark-math`' ) @@ -79,11 +73,9 @@ test('rehype-mathjax', function (t) { .use(rehypeParse, {fragment: true}) .use(rehypeMathJaxSvg) .use(rehypeStringify) - .processSync(vfile.readSync({dirname: fixtures, basename: 'double.html'})) + .processSync(readSync({dirname: fixtures, basename: 'double.html'})) .toString(), - String( - vfile.readSync({dirname: fixtures, basename: 'double-svg.html'}) - ).trim(), + String(readSync({dirname: fixtures, basename: 'double-svg.html'})).trim(), 'should transform `.math-inline.math-display`' ) @@ -92,9 +84,9 @@ test('rehype-mathjax', function (t) { .use(rehypeParse, {fragment: true}) .use(rehypeMathJaxSvg) .use(rehypeStringify) - .processSync(vfile.readSync({dirname: fixtures, basename: 'none.html'})) + .processSync(readSync({dirname: fixtures, basename: 'none.html'})) .toString(), - String(vfile.readSync({dirname: fixtures, basename: 'none-svg.html'})), + String(readSync({dirname: fixtures, basename: 'none-svg.html'})), 'should transform documents without math' ) @@ -103,13 +95,9 @@ test('rehype-mathjax', function (t) { .use(rehypeParse) .use(rehypeMathJaxSvg) .use(rehypeStringify) - .processSync( - vfile.readSync({dirname: fixtures, basename: 'document.html'}) - ) + .processSync(readSync({dirname: fixtures, basename: 'document.html'})) .toString(), - String( - vfile.readSync({dirname: fixtures, basename: 'document-svg.html'}) - ).trim(), + String(readSync({dirname: fixtures, basename: 'document-svg.html'})).trim(), 'should transform complete documents' ) @@ -121,10 +109,10 @@ test('rehype-mathjax', function (t) { displayMath: ['$$', '$$'] }) .use(rehypeStringify) - .processSync(vfile.readSync({dirname: fixtures, basename: 'small.html'})) + .processSync(readSync({dirname: fixtures, basename: 'small.html'})) .toString(), String( - vfile.readSync({ + readSync({ dirname: fixtures, basename: 'small-browser-delimiters.html' }) @@ -138,14 +126,14 @@ test('rehype-mathjax', function (t) { .use(rehypeMathJaxSvg, {tex: {tags: 'ams'}}) .use(rehypeStringify) .processSync( - vfile.readSync({ + readSync({ dirname: fixtures, basename: 'equation-numbering-1.html' }) ) .toString(), String( - vfile.readSync({ + readSync({ dirname: fixtures, basename: 'equation-numbering-1-svg.html' }) @@ -159,14 +147,14 @@ test('rehype-mathjax', function (t) { .use(rehypeMathJaxSvg, {tex: {tags: 'ams'}}) .use(rehypeStringify) .processSync( - vfile.readSync({ + readSync({ dirname: fixtures, basename: 'equation-numbering-2.html' }) ) .toString(), String( - vfile.readSync({ + readSync({ dirname: fixtures, basename: 'equation-numbering-2-svg.html' }) @@ -180,14 +168,14 @@ test('rehype-mathjax', function (t) { .use(rehypeMathJaxChtml, {fontURL: 'place/to/fonts', tex: {tags: 'ams'}}) .use(rehypeStringify) .processSync( - vfile.readSync({ + readSync({ dirname: fixtures, basename: 'equation-numbering-1.html' }) ) .toString(), String( - vfile.readSync({ + readSync({ dirname: fixtures, basename: 'equation-numbering-1-chtml.html' }) @@ -205,7 +193,7 @@ test('rehype-mathjax', function (t) { .map((basename) => processor .processSync( - vfile.readSync({ + readSync({ dirname: fixtures, basename: basename }) @@ -216,13 +204,13 @@ test('rehype-mathjax', function (t) { })(), [ String( - vfile.readSync({ + readSync({ dirname: fixtures, basename: 'equation-numbering-1-svg.html' }) ).trim(), String( - vfile.readSync({ + readSync({ dirname: fixtures, basename: 'equation-numbering-2-svg.html' }) diff --git a/packages/remark-math/test.js b/packages/remark-math/test.js index dcdc4cc..5a2f3b3 100644 --- a/packages/remark-math/test.js +++ b/packages/remark-math/test.js @@ -1,11 +1,11 @@ import test from 'tape' -import unified from 'unified' +import {u} from 'unist-builder' +import {removePosition} from 'unist-util-remove-position' +import {unified} from 'unified' import remarkParse from 'remark-parse' import remarkRehype from 'remark-rehype' import rehypeStringify from 'rehype-stringify' import remarkStringify from 'remark-stringify' -import u from 'unist-builder' -import removePosition from 'unist-util-remove-position' import remarkMath from './index.js' test('remarkMath', function (t) { From b7ecce0e1486c7fed1455bd0077d7ffea2d91075 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 7 Aug 2021 14:08:01 +0200 Subject: [PATCH 23/98] Update dependencies --- packages/rehype-katex/index.js | 12 ++++++------ packages/rehype-katex/package.json | 10 +++++----- packages/rehype-katex/test.js | 2 +- packages/rehype-mathjax/lib/create-plugin.js | 4 ++-- packages/rehype-mathjax/lib/create-renderer.js | 4 ++-- packages/rehype-mathjax/package.json | 6 +++--- packages/remark-html-katex/index.js | 6 +++--- packages/remark-html-katex/package.json | 8 ++++---- packages/remark-html-katex/test.js | 2 +- packages/remark-math/index.js | 11 +++++------ packages/remark-math/package.json | 4 ++-- 11 files changed, 34 insertions(+), 35 deletions(-) diff --git a/packages/rehype-katex/index.js b/packages/rehype-katex/index.js index 73192e0..1e47f1b 100644 --- a/packages/rehype-katex/index.js +++ b/packages/rehype-katex/index.js @@ -1,13 +1,13 @@ -import visit from 'unist-util-visit' -import removePosition from 'unist-util-remove-position' import katex from 'katex' -import unified from 'unified' -import parse from 'rehype-parse' -import toText from 'hast-util-to-text' +import {visit} from 'unist-util-visit' +import {removePosition} from 'unist-util-remove-position' +import {toText} from 'hast-util-to-text' +import {unified} from 'unified' +import rehypeParse from 'rehype-parse' const assign = Object.assign -const parseHtml = unified().use(parse, {fragment: true}) +const parseHtml = unified().use(rehypeParse, {fragment: true}) const source = 'rehype-katex' diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index eb7a18c..0159284 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -37,12 +37,12 @@ ], "dependencies": { "@types/katex": "^0.11.0", - "hast-util-to-text": "^2.0.0", + "hast-util-to-text": "^3.0.0", "katex": "^0.13.0", - "rehype-parse": "^7.0.0", - "unified": "^9.0.0", - "unist-util-remove-position": "^3.0.0", - "unist-util-visit": "^2.0.0" + "rehype-parse": "^8.0.0", + "unified": "^10.0.0", + "unist-util-remove-position": "^4.0.0", + "unist-util-visit": "^4.0.0" }, "scripts": { "test-api": "node --conditions development test.js", diff --git a/packages/rehype-katex/test.js b/packages/rehype-katex/test.js index 3fdad67..fbe1631 100644 --- a/packages/rehype-katex/test.js +++ b/packages/rehype-katex/test.js @@ -1,6 +1,6 @@ import test from 'tape' import katex from 'katex' -import unified from 'unified' +import {unified} from 'unified' import remarkParse from 'remark-parse' import remarkRehype from 'remark-rehype' import rehypeParse from 'rehype-parse' diff --git a/packages/rehype-mathjax/lib/create-plugin.js b/packages/rehype-mathjax/lib/create-plugin.js index a744010..1161f78 100644 --- a/packages/rehype-mathjax/lib/create-plugin.js +++ b/packages/rehype-mathjax/lib/create-plugin.js @@ -1,4 +1,4 @@ -import visit from 'unist-util-visit' +import {visit, SKIP} from 'unist-util-visit' // To do next major: Remove `chtml` and `browser` flags once all the options use // the same format. @@ -53,7 +53,7 @@ export function createPlugin(displayName, createRenderer, chtml, browser) { found = true renderer.render(node, {display: display}) - return visit.SKIP + return SKIP } } } diff --git a/packages/rehype-mathjax/lib/create-renderer.js b/packages/rehype-mathjax/lib/create-renderer.js index c3f943c..de8c207 100644 --- a/packages/rehype-mathjax/lib/create-renderer.js +++ b/packages/rehype-mathjax/lib/create-renderer.js @@ -1,7 +1,7 @@ import {mathjax} from 'mathjax-full/js/mathjax.js' import {RegisterHTMLHandler} from 'mathjax-full/js/handlers/html.js' -import fromDom from 'hast-util-from-dom' -import toText from 'hast-util-to-text' +import {fromDom} from 'hast-util-from-dom' +import {toText} from 'hast-util-to-text' import {createAdaptor} from './create-adaptor.js' const adaptor = createAdaptor() diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index 631caca..190903c 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -44,11 +44,11 @@ }, "dependencies": { "@types/mathjax": "^0.0.36", - "hast-util-from-dom": "^3.0.0", - "hast-util-to-text": "^2.0.0", + "hast-util-from-dom": "^4.0.0", + "hast-util-to-text": "^3.0.0", "jsdom": "^16.0.0", "mathjax-full": "^3.0.0", - "unist-util-visit": "^2.0.0" + "unist-util-visit": "^4.0.0" }, "scripts": { "test-api": "node --conditions development test/index.js", diff --git a/packages/remark-html-katex/index.js b/packages/remark-html-katex/index.js index 594ff3c..0893b8b 100644 --- a/packages/remark-html-katex/index.js +++ b/packages/remark-html-katex/index.js @@ -1,7 +1,7 @@ -import visit from 'unist-util-visit' -import removePosition from 'unist-util-remove-position' +import {visit} from 'unist-util-visit' +import {removePosition} from 'unist-util-remove-position' import katex from 'katex' -import unified from 'unified' +import {unified} from 'unified' import rehypeParse from 'rehype-parse' const parseHtml = unified().use(rehypeParse, {fragment: true}) diff --git a/packages/remark-html-katex/package.json b/packages/remark-html-katex/package.json index 8961bbd..5539ea3 100644 --- a/packages/remark-html-katex/package.json +++ b/packages/remark-html-katex/package.json @@ -36,10 +36,10 @@ "dependencies": { "@types/katex": "^0.11.0", "katex": "^0.12.0", - "rehype-parse": "^7.0.0", - "unified": "^9.0.0", - "unist-util-remove-position": "^3.0.0", - "unist-util-visit": "^2.0.0" + "rehype-parse": "^8.0.0", + "unified": "^10.0.0", + "unist-util-remove-position": "^4.0.0", + "unist-util-visit": "^4.0.0" }, "scripts": { "test-api": "node --conditions development test.js", diff --git a/packages/remark-html-katex/test.js b/packages/remark-html-katex/test.js index 4119950..132a5ae 100644 --- a/packages/remark-html-katex/test.js +++ b/packages/remark-html-katex/test.js @@ -1,6 +1,6 @@ import test from 'tape' import katex from 'katex' -import unified from 'unified' +import {unified} from 'unified' import remarkParse from 'remark-parse' import rehypeParse from 'rehype-parse' import rehypeStringify from 'rehype-stringify' diff --git a/packages/remark-math/index.js b/packages/remark-math/index.js index 2064cc8..459ab36 100644 --- a/packages/remark-math/index.js +++ b/packages/remark-math/index.js @@ -1,6 +1,5 @@ -import syntax from 'micromark-extension-math' -import fromMarkdown from 'mdast-util-math/from-markdown.js' -import toMarkdown from 'mdast-util-math/to-markdown.js' +import {math} from 'micromark-extension-math' +import {mathFromMarkdown, mathToMarkdown} from 'mdast-util-math' var warningIssued @@ -24,9 +23,9 @@ export default function remarkMath() { ) } - add('micromarkExtensions', syntax) - add('fromMarkdownExtensions', fromMarkdown) - add('toMarkdownExtensions', toMarkdown) + add('micromarkExtensions', math) + add('fromMarkdownExtensions', mathFromMarkdown) + add('toMarkdownExtensions', mathToMarkdown) function add(field, value) { // Other extensions. diff --git a/packages/remark-math/package.json b/packages/remark-math/package.json index 55ca450..369dbe6 100644 --- a/packages/remark-math/package.json +++ b/packages/remark-math/package.json @@ -36,8 +36,8 @@ "util.js" ], "dependencies": { - "mdast-util-math": "^0.1.0", - "micromark-extension-math": "^0.1.0" + "mdast-util-math": "^1.0.0", + "micromark-extension-math": "^1.0.0" }, "scripts": { "test-api": "node --conditions development test.js", From 2948e91ba8f1b5e1b4f89a9b23831f2bde76870d Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 7 Aug 2021 14:08:42 +0200 Subject: [PATCH 24/98] Update `katex` --- packages/remark-html-katex/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/remark-html-katex/package.json b/packages/remark-html-katex/package.json index 5539ea3..16d1541 100644 --- a/packages/remark-html-katex/package.json +++ b/packages/remark-html-katex/package.json @@ -35,7 +35,7 @@ ], "dependencies": { "@types/katex": "^0.11.0", - "katex": "^0.12.0", + "katex": "^0.13.0", "rehype-parse": "^8.0.0", "unified": "^10.0.0", "unist-util-remove-position": "^4.0.0", From a68e8b84a2f781fdc866211b8a5de7b550aa1ed5 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 7 Aug 2021 14:11:40 +0200 Subject: [PATCH 25/98] Refactor code-style --- package.json | 12 ++---------- packages/rehype-katex/index.js | 4 ++-- packages/rehype-katex/test.js | 8 ++++---- packages/rehype-mathjax/browser.js | 2 +- packages/rehype-mathjax/lib/create-plugin.js | 2 +- packages/rehype-mathjax/lib/create-renderer.js | 4 ++-- packages/rehype-mathjax/test/index.js | 6 +++--- packages/remark-html-katex/index.js | 4 ++-- packages/remark-html-katex/test.js | 8 ++++---- packages/remark-math/index.js | 4 ++-- packages/remark-math/test.js | 2 +- 11 files changed, 24 insertions(+), 32 deletions(-) diff --git a/package.json b/package.json index 42ed609..0abc081 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "unified": "^10.0.0", "unist-builder": "^3.0.0", "unist-util-remove-position": "^4.0.0", - "xo": "^0.38.0" + "xo": "^0.39.0" }, "scripts": { "postinstall": "lerna bootstrap --no-ci", @@ -55,16 +55,8 @@ }, "xo": { "prettier": true, - "esnext": false, "rules": { - "@typescript-eslint/ban-types": "off", - "eqeqeq": [ - 2, - "allow-null" - ], - "import/extensions": "off", - "no-eq-null": "off", - "unicorn/no-array-callback-reference": "off" + "@typescript-eslint/ban-types": "off" } }, "remarkConfig": { diff --git a/packages/rehype-katex/index.js b/packages/rehype-katex/index.js index 1e47f1b..aa252eb 100644 --- a/packages/rehype-katex/index.js +++ b/packages/rehype-katex/index.js @@ -36,7 +36,7 @@ export default function rehypeKatex(options) { try { result = katex.renderToString( value, - assign({}, settings, {displayMode: displayMode, throwOnError: true}) + assign({}, settings, {displayMode, throwOnError: true}) ) } catch (error) { const fn = throwOnError ? 'fail' : 'message' @@ -47,7 +47,7 @@ export default function rehypeKatex(options) { result = katex.renderToString( value, assign({}, settings, { - displayMode: displayMode, + displayMode, throwOnError: false, strict: 'ignore' }) diff --git a/packages/rehype-katex/test.js b/packages/rehype-katex/test.js index fbe1631..7af82d4 100644 --- a/packages/rehype-katex/test.js +++ b/packages/rehype-katex/test.js @@ -8,7 +8,7 @@ import rehypeStringify from 'rehype-stringify' import remarkMath from '../remark-math/index.js' import rehypeKatex from './index.js' -test('rehype-katex', function (t) { +test('rehype-katex', (t) => { t.deepEqual( unified() .use(rehypeParse, {fragment: true}) @@ -103,7 +103,7 @@ test('rehype-katex', function (t) { t.deepEqual( unified() .use(rehypeParse, {fragment: true}) - .use(rehypeKatex, {macros: macros}) + .use(rehypeKatex, {macros}) .use(rehypeStringify) .processSync('\\RR') .toString(), @@ -112,7 +112,7 @@ test('rehype-katex', function (t) { .use(rehypeStringify) .processSync( '' + - katex.renderToString('\\RR', {macros: macros}) + + katex.renderToString('\\RR', {macros}) + '' ) .toString(), @@ -149,7 +149,7 @@ test('rehype-katex', function (t) { .processSync( '

Lorem

\n

\\alpa

' ) - .messages.map(String), + .messages.map((d) => String(d)), [ '2:4-2:42: KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲' ], diff --git a/packages/rehype-mathjax/browser.js b/packages/rehype-mathjax/browser.js index fbbacb9..aa3a761 100644 --- a/packages/rehype-mathjax/browser.js +++ b/packages/rehype-mathjax/browser.js @@ -15,7 +15,7 @@ function renderBrowser(options) { const display = settings.displayMath || ['\\[', '\\]'] const inline = settings.inlineMath || ['\\(', '\\)'] - return {render: render} + return {render} function render(node, renderOptions) { const delimiters = renderOptions.display ? display : inline diff --git a/packages/rehype-mathjax/lib/create-plugin.js b/packages/rehype-mathjax/lib/create-plugin.js index 1161f78..5cf9ccd 100644 --- a/packages/rehype-mathjax/lib/create-plugin.js +++ b/packages/rehype-mathjax/lib/create-plugin.js @@ -51,7 +51,7 @@ export function createPlugin(displayName, createRenderer, chtml, browser) { } found = true - renderer.render(node, {display: display}) + renderer.render(node, {display}) return SKIP } diff --git a/packages/rehype-mathjax/lib/create-renderer.js b/packages/rehype-mathjax/lib/create-renderer.js index de8c207..8d86d5a 100644 --- a/packages/rehype-mathjax/lib/create-renderer.js +++ b/packages/rehype-mathjax/lib/create-renderer.js @@ -22,7 +22,7 @@ RegisterHTMLHandler(adaptor) export function createRenderer(input, output) { const doc = mathjax.document('', {InputJax: input, OutputJax: output}) - return {render: render, styleSheet: styleSheet} + return {render, styleSheet} function render(node, options) { node.children = [fromDom(doc.convert(toText(node), options))] @@ -35,7 +35,7 @@ export function createRenderer(input, output) { type: 'element', tagName: 'style', properties: {}, - children: [{type: 'text', value: value}] + children: [{type: 'text', value}] } } } diff --git a/packages/rehype-mathjax/test/index.js b/packages/rehype-mathjax/test/index.js index acb82b6..078ead8 100644 --- a/packages/rehype-mathjax/test/index.js +++ b/packages/rehype-mathjax/test/index.js @@ -13,7 +13,7 @@ import rehypeMathJaxBrowser from '../browser.js' const fixtures = path.join('test', 'fixture') -test('rehype-mathjax', function (t) { +test('rehype-mathjax', (t) => { t.equal( unified() .use(rehypeParse, {fragment: true}) @@ -26,7 +26,7 @@ test('rehype-mathjax', function (t) { ) t.throws( - function () { + () => { unified().use(rehypeMathJaxChtml).freeze() }, /rehype-mathjax: missing `fontURL` in options/, @@ -195,7 +195,7 @@ test('rehype-mathjax', function (t) { .processSync( readSync({ dirname: fixtures, - basename: basename + basename }) ) .toString() diff --git a/packages/remark-html-katex/index.js b/packages/remark-html-katex/index.js index 0893b8b..c16d980 100644 --- a/packages/remark-html-katex/index.js +++ b/packages/remark-html-katex/index.js @@ -25,7 +25,7 @@ export default function remarkHtmlKatex(options) { result = katex.renderToString( node.value, Object.assign({}, settings, { - displayMode: displayMode, + displayMode, throwOnError: true }) ) @@ -38,7 +38,7 @@ export default function remarkHtmlKatex(options) { result = katex.renderToString( node.value, Object.assign({}, settings, { - displayMode: displayMode, + displayMode, throwOnError: false, strict: 'ignore' }) diff --git a/packages/remark-html-katex/test.js b/packages/remark-html-katex/test.js index 132a5ae..2075285 100644 --- a/packages/remark-html-katex/test.js +++ b/packages/remark-html-katex/test.js @@ -8,7 +8,7 @@ import remarkHtml from 'remark-html' import remarkMath from '../remark-math/index.js' import remarkHtmlKatex from './index.js' -test('remark-html-katex', function (t) { +test('remark-html-katex', (t) => { t.deepEqual( unified() .use(remarkParse) @@ -52,7 +52,7 @@ test('remark-html-katex', function (t) { unified() .use(remarkParse, {position: false}) .use(remarkMath) - .use(remarkHtmlKatex, {macros: macros}) + .use(remarkHtmlKatex, {macros}) .use(remarkHtml) .processSync('$\\RR$') .toString(), @@ -61,7 +61,7 @@ test('remark-html-katex', function (t) { .use(rehypeStringify) .processSync( '

' + - katex.renderToString('\\RR', {macros: macros}) + + katex.renderToString('\\RR', {macros}) + '

\n' ) .toString(), @@ -98,7 +98,7 @@ test('remark-html-katex', function (t) { .use(remarkHtmlKatex) .use(remarkHtml) .processSync('Lorem\n$\\alpa$') - .messages.map(String), + .messages.map((d) => String(d)), [ '2:1-2:8: KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲' ], diff --git a/packages/remark-math/index.js b/packages/remark-math/index.js index 459ab36..d8ab764 100644 --- a/packages/remark-math/index.js +++ b/packages/remark-math/index.js @@ -1,10 +1,10 @@ import {math} from 'micromark-extension-math' import {mathFromMarkdown, mathToMarkdown} from 'mdast-util-math' -var warningIssued +let warningIssued export default function remarkMath() { - var data = this.data() + const data = this.data() // Old remark. /* c8 ignore next 14 */ diff --git a/packages/remark-math/test.js b/packages/remark-math/test.js index 5a2f3b3..e174d7c 100644 --- a/packages/remark-math/test.js +++ b/packages/remark-math/test.js @@ -8,7 +8,7 @@ import rehypeStringify from 'rehype-stringify' import remarkStringify from 'remark-stringify' import remarkMath from './index.js' -test('remarkMath', function (t) { +test('remarkMath', (t) => { const toHtml = unified() .use(remarkParse) .use(remarkMath) From 5d86d872320fcf5e4677ae683dea0eb876e61509 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 7 Aug 2021 14:34:13 +0200 Subject: [PATCH 26/98] Update docs --- packages/rehype-katex/readme.md | 32 ++++++++++++++-------- packages/rehype-mathjax/readme.md | 41 ++++++++++++++++++---------- packages/remark-html-katex/readme.md | 36 ++++++++++++++---------- packages/remark-math/readme.md | 40 ++++++++++++++++----------- readme.md | 35 ++++++++++++++---------- 5 files changed, 112 insertions(+), 72 deletions(-) diff --git a/packages/rehype-katex/readme.md b/packages/rehype-katex/readme.md index 0ddd417..9e09b2e 100644 --- a/packages/rehype-katex/readme.md +++ b/packages/rehype-katex/readme.md @@ -13,6 +13,9 @@ ## Install +This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c): +Node 12+ is needed to use it and it must be `import`ed instead of `require`d. + [npm][]: ```sh @@ -34,21 +37,23 @@ Say we have the following file, `example.html`: ``` -And our script, `example.js`, looks as follows: +And our module, `example.js`, looks as follows: ```js -const vfile = require('to-vfile') -const unified = require('unified') -const parse = require('rehype-parse') -const katex = require('rehype-katex') -const stringify = require('rehype-stringify') +import {readSync} from 'to-vfile' +import {unified} from 'unified' +import rehypeParse from 'rehype-parse' +import rehypeKatex from 'rehype-katex' +import rehypeStringify from 'rehype-stringify' + +const file = readSync('example.html') unified() - .use(parse, {fragment: true}) - .use(katex) - .use(stringify) - .process(vfile.readSync('example.html'), function (err, file) { - if (err) throw err + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex) + .use(rehypeStringify) + .process(file) + .then((file) => { console.log(String(file)) }) ``` @@ -67,7 +72,10 @@ Now, running `node example` yields: ## API -### `rehype().use(katex[, options])` +This package exports no identifiers. +The default export is `rehypeKatex`. + +### `unified().use(rehypeKatex[, options])` Transform `` and `
` with [KaTeX][]. diff --git a/packages/rehype-mathjax/readme.md b/packages/rehype-mathjax/readme.md index 64e2f0b..754b83d 100644 --- a/packages/rehype-mathjax/readme.md +++ b/packages/rehype-mathjax/readme.md @@ -13,6 +13,9 @@ ## Install +This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c): +Node 12+ is needed to use it and it must be `import`ed instead of `require`d. + [npm][]: ```sh @@ -34,21 +37,23 @@ Say we have the following file, `example.html`:
``` -And our script, `example.js`, looks as follows: +And our module, `example.js`, looks as follows: ```js -const vfile = require('to-vfile') -const unified = require('unified') -const parse = require('rehype-parse') -const mathjax = require('rehype-mathjax') -const stringify = require('rehype-stringify') +import {readSync} from 'to-vfile' +import {unified} from 'unified' +import rehypeParse from 'rehype-parse' +import rehypeMathJax from 'rehype-mathjax' +import rehypeStringify from 'rehype-stringify' + +const file = readSync('example.html') unified() - .use(parse, {fragment: true}) - .use(mathjax) - .use(stringify) - .process(vfile.readSync('example.html'), function (err, file) { - if (err) throw err + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJax) + .use(rehypeStringify) + .process(file) + .then((file) => { console.log(String(file)) }) ``` @@ -77,7 +82,10 @@ mjx-container[jax="SVG"] > svg { ## API -### `rehype().use(rehypeMathJax[, options])` +This package exports no identifiers. +The default export is `rehypeMathJaxSvg`. + +### `unified().use(rehypeMathJaxSvg[, options])` Transform `` and `
` with [MathJax][]. @@ -87,18 +95,21 @@ big memory and size footprint: SVG, CHTML, and browser: ###### SVG -Render math as [SVG][mathjax-svg] (`require('rehype-mathjax/svg')`, default). +Render math as [SVG][mathjax-svg] (`import rehypeMathJaxSvg from +'rehype-mathjax/svg.js'`, default). About 566kb minzipped. ###### CHTML -Render math as [CHTML][mathjax-chtml] (`require('rehype-mathjax/chtml')`). +Render math as [CHTML][mathjax-chtml] (`import rehypeMathJaxChtml from +'rehype-mathjax/chtml.js'`). About 154kb minzipped. Needs a `fontURL` to be passed. ###### Browser -Tiny wrapper to render MathJax client-side (`require('rehype-mathjax/browser')`). +Tiny wrapper to render MathJax client-side (`import rehypeMathJaxBrowser from +'rehype-mathjax/browser.js'`). About 1kb minzipped. Uses `options.displayMath` (default: `['\\[', '\\]']`) for display, and diff --git a/packages/remark-html-katex/readme.md b/packages/remark-html-katex/readme.md index 92dff28..7e5b8a8 100644 --- a/packages/remark-html-katex/readme.md +++ b/packages/remark-html-katex/readme.md @@ -17,6 +17,9 @@ ## Install +This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c): +Node 12+ is needed to use it and it must be `import`ed instead of `require`d. + [npm][]: ```sh @@ -35,23 +38,25 @@ L = \frac{1}{2} \rho v^2 S C_L $$ ``` -And our script, `example.js`, looks as follows: +And our module, `example.js`, looks as follows: ```js -const vfile = require('to-vfile') -const unified = require('unified') -const markdown = require('remark-parse') -const math = require('remark-math') -const htmlKatex = require('remark-html-katex') -const html = require('remark-html') +import {readSync} from 'to-vfile' +import {unified} from 'unified' +import remarkParse from 'remark-parse' +import remarkMath from 'remark-math' +import remarkHtmlKatex from 'remark-html-katex' +import remarkHtml from 'remark-html' + +const file = readSync('example.md') unified() - .use(markdown) - .use(math) - .use(htmlKatex) - .use(html) - .process(vfile.readSync('example.md'), function (err, file) { - if (err) throw err + .use(remarkParse) + .use(remarkMath) + .use(remarkHtmlKatex) + .use(remarkHtml) + .process(file) + .then((file) => { console.log(String(file)) }) ``` @@ -65,7 +70,10 @@ Now, running `node example` yields: ## API -### `remark().use(htmlKatex[, options])` +This package exports no identifiers. +The default export is `remarkHtmlKatex`. + +### `unified().use(remarkHtmlKatex[, options])` Transform `inlineMath` and `math` nodes with [KaTeX][] for [`remark-html`][remark-html]. diff --git a/packages/remark-math/readme.md b/packages/remark-math/readme.md index 392d698..f192465 100644 --- a/packages/remark-math/readme.md +++ b/packages/remark-math/readme.md @@ -20,6 +20,9 @@ Use version 4 for remark 13+. ## Install +This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c): +Node 12+ is needed to use it and it must be `import`ed instead of `require`d. + [npm][]: ```sh @@ -38,25 +41,27 @@ L = \frac{1}{2} \rho v^2 S C_L $$ ``` -And our script, `example.js`, looks as follows: +And our module, `example.js`, looks as follows: ```js -const vfile = require('to-vfile') -const unified = require('unified') -const markdown = require('remark-parse') -const math = require('remark-math') -const remark2rehype = require('remark-rehype') -const katex = require('rehype-katex') -const stringify = require('rehype-stringify') +import {readSync} from 'to-vfile' +import {unified} from 'unified' +import remarkParse from 'remark-parse' +import remarkMath from 'remark-math' +import remarkRehype from 'remark-rehype' +import rehypeKatex from 'rehype-katex' +import rehypeStringify from 'rehype-stringify' + +const file = readSync('example.md') unified() - .use(markdown) - .use(math) - .use(remark2rehype) - .use(katex) - .use(stringify) - .process(vfile.readSync('example.md'), function(err, file) { - if (err) throw err + .use(remarkParse) + .use(remarkMath) + .use(remarkRehype) + .use(rehypeKatex) + .use(rehypeStringify) + .process(file) + .then((file) => { console.log(String(file)) }) ``` @@ -70,7 +75,10 @@ Now, running `node example` yields: ## API -### `remark().use(math[, options])` +This package exports no identifiers. +The default export is `remarkMath`. + +### `unified().use(remarkMath[, options])` Parse and stringify math. diff --git a/readme.md b/readme.md index 9e42c9e..0f5f0bf 100644 --- a/readme.md +++ b/readme.md @@ -12,6 +12,9 @@ ## Install +This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c): +Node 12+ is needed to use it and it must be `import`ed instead of `require`d. + [npm][]: ```sh @@ -31,25 +34,27 @@ L = \frac{1}{2} \rho v^2 S C_L $$ ``` -And our script, `example.js`, looks as follows: +And our module, `example.js`, looks as follows: ```js -import {toVFile} from 'to-vfile' -import unified from 'unified' -import markdown from 'remark-parse' -import math from 'remark-math' -import remark2rehype from 'remark-rehype' -import katex from 'rehype-katex' -import stringify from 'rehype-stringify' +import {readSync} from 'to-vfile' +import {unified} from 'unified' +import remarkParse from 'remark-parse' +import remarkMath from 'remark-math' +import remarkRehype from 'remark-rehype' +import rehypeKatex from 'rehype-katex' +import rehypeStringify from 'rehype-stringify' + +const file = readSync('example.md') unified() - .use(markdown) - .use(math) - .use(remark2rehype) - .use(katex) - .use(stringify) - .process(toVFile.readSync('example.md'), function (err, file) { - if (err) throw err + .use(remarkParse) + .use(remarkMath) + .use(remarkRehype) + .use(rehypeKatex) + .use(rehypeStringify) + .process(file) + .then((file) => { console.log(String(file)) }) ``` From edf4ca23f3e016aab71dd2fde12b13f33958ef45 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 9 Aug 2021 13:08:14 +0200 Subject: [PATCH 27/98] Add JSDoc based types --- .gitignore | 1 + package.json | 19 ++- packages/rehype-katex/index.js | 28 ++-- packages/rehype-katex/package.json | 15 +- packages/rehype-katex/test.js | 2 +- packages/rehype-katex/tsconfig.json | 4 + packages/rehype-katex/types/index.d.ts | 8 -- packages/rehype-katex/types/test.ts | 9 -- packages/rehype-katex/types/tsconfig.json | 9 -- packages/rehype-katex/types/tslint.json | 7 - packages/rehype-mathjax/browser.d.ts | 12 -- packages/rehype-mathjax/browser.js | 54 +++++--- packages/rehype-mathjax/chtml.d.ts | 43 ------ packages/rehype-mathjax/chtml.js | 23 ++-- packages/rehype-mathjax/index.d.ts | 48 ------- packages/rehype-mathjax/lib/create-input.js | 3 + .../rehype-mathjax/lib/create-output-chtml.js | 4 + .../rehype-mathjax/lib/create-output-svg.js | 4 + packages/rehype-mathjax/lib/create-plugin.js | 129 ++++++++++++++---- .../rehype-mathjax/lib/create-renderer.js | 43 ++++-- packages/rehype-mathjax/package.json | 28 +++- packages/rehype-mathjax/svg.js | 21 +-- packages/rehype-mathjax/test.ts | 42 ------ packages/rehype-mathjax/tsconfig.json | 13 +- packages/rehype-mathjax/tslint.json | 7 - packages/remark-html-katex/index.js | 87 ++++++------ packages/remark-html-katex/package.json | 15 +- packages/remark-html-katex/test.js | 21 ++- packages/remark-html-katex/tsconfig.json | 4 + packages/remark-html-katex/types/index.d.ts | 8 -- packages/remark-html-katex/types/test.ts | 11 -- .../remark-html-katex/types/tsconfig.json | 9 -- packages/remark-html-katex/types/tslint.json | 7 - packages/remark-math/index.js | 27 +++- packages/remark-math/package.json | 23 ++-- packages/remark-math/tsconfig.json | 4 + packages/remark-math/types/index.d.ts | 5 - packages/remark-math/types/test.ts | 7 - packages/remark-math/types/tsconfig.json | 9 -- packages/remark-math/types/tslint.json | 7 - packages/remark-math/util.js | 10 -- tsconfig.json | 16 +++ 42 files changed, 420 insertions(+), 426 deletions(-) create mode 100644 packages/rehype-katex/tsconfig.json delete mode 100644 packages/rehype-katex/types/index.d.ts delete mode 100644 packages/rehype-katex/types/test.ts delete mode 100644 packages/rehype-katex/types/tsconfig.json delete mode 100644 packages/rehype-katex/types/tslint.json delete mode 100644 packages/rehype-mathjax/browser.d.ts delete mode 100644 packages/rehype-mathjax/chtml.d.ts delete mode 100644 packages/rehype-mathjax/index.d.ts delete mode 100644 packages/rehype-mathjax/test.ts delete mode 100644 packages/rehype-mathjax/tslint.json create mode 100644 packages/remark-html-katex/tsconfig.json delete mode 100644 packages/remark-html-katex/types/index.d.ts delete mode 100644 packages/remark-html-katex/types/test.ts delete mode 100644 packages/remark-html-katex/types/tsconfig.json delete mode 100644 packages/remark-html-katex/types/tslint.json create mode 100644 packages/remark-math/tsconfig.json delete mode 100644 packages/remark-math/types/index.d.ts delete mode 100644 packages/remark-math/types/test.ts delete mode 100644 packages/remark-math/types/tsconfig.json delete mode 100644 packages/remark-math/types/tslint.json delete mode 100644 packages/remark-math/util.js create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index 33d4929..53a29e3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ coverage/ node_modules/ .DS_Store +*.d.ts *.log yarn.lock diff --git a/package.json b/package.json index 0abc081..c987296 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,8 @@ ], "type": "module", "devDependencies": { + "@types/tape": "^4.0.0", "c8": "^7.0.0", - "dtslint": "^4.0.0", "lerna": "^4.0.0", "prettier": "^2.0.0", "rehype-parse": "^8.0.0", @@ -29,8 +29,10 @@ "remark-preset-wooorm": "^8.0.0", "remark-rehype": "^9.0.0", "remark-stringify": "^10.0.0", + "rimraf": "^3.0.0", "tape": "^5.0.0", "to-vfile": "^7.0.0", + "type-coverage": "^2.0.0", "typescript": "^4.0.0", "unified": "^10.0.0", "unist-builder": "^3.0.0", @@ -39,11 +41,11 @@ }, "scripts": { "postinstall": "lerna bootstrap --no-ci", + "build": "lerna run build", "format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", "test-api": "lerna run test-api", "test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov npm run test-api", - "test-types": "lerna run test-types --concurrency 1 --stream", - "test": "npm run format && npm run test-coverage && npm run test-types" + "test": "npm run build && npm run format && npm run test-coverage" }, "prettier": { "tabWidth": 2, @@ -54,14 +56,17 @@ "trailingComma": "none" }, "xo": { - "prettier": true, - "rules": { - "@typescript-eslint/ban-types": "off" - } + "prettier": true }, "remarkConfig": { "plugins": [ "preset-wooorm" ] + }, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true } } diff --git a/packages/rehype-katex/index.js b/packages/rehype-katex/index.js index aa252eb..989b3a2 100644 --- a/packages/rehype-katex/index.js +++ b/packages/rehype-katex/index.js @@ -1,3 +1,8 @@ +/** + * @typedef {import('hast').Root} Root + * @typedef {import('katex').KatexOptions} Options + */ + import katex from 'katex' import {visit} from 'unist-util-visit' import {removePosition} from 'unist-util-remove-position' @@ -11,17 +16,22 @@ const parseHtml = unified().use(rehypeParse, {fragment: true}) const source = 'rehype-katex' +/** + * Plugin to transform `` and `
` + * with KaTeX. + * + * @type {import('unified').Plugin<[Options?]|void[], Root>} + */ export default function rehypeKatex(options) { const settings = options || {} const throwOnError = settings.throwOnError || false - return transformMath - - function transformMath(tree, file) { - visit(tree, 'element', onelement) - - function onelement(element) { - const classes = element.properties.className || [] + return (tree, file) => { + visit(tree, 'element', (element) => { + const classes = + element.properties && Array.isArray(element.properties.className) + ? element.properties.className + : [] const inline = classes.includes('math-inline') const displayMode = classes.includes('math-display') @@ -31,6 +41,7 @@ export default function rehypeKatex(options) { const value = toText(element) + /** @type {string} */ let result try { @@ -54,7 +65,8 @@ export default function rehypeKatex(options) { ) } + // @ts-expect-error: assume no `doctypes` in KaTeX result. element.children = removePosition(parseHtml.parse(result), true).children - } + }) } } diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index 0159284..031032e 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -32,10 +32,13 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/hast": "^2.0.0", "@types/katex": "^0.11.0", "hast-util-to-text": "^3.0.0", "katex": "^0.13.0", @@ -45,9 +48,15 @@ "unist-util-visit": "^4.0.0" }, "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage", "test-api": "node --conditions development test.js", - "test-types": "dtslint types", - "test": "npm run test-api && npm run test-types" + "test": "npm run build && npm run test-api" }, - "xo": false + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/rehype-katex/test.js b/packages/rehype-katex/test.js index 7af82d4..3d28c08 100644 --- a/packages/rehype-katex/test.js +++ b/packages/rehype-katex/test.js @@ -42,7 +42,7 @@ test('rehype-katex', (t) => { t.deepEqual( unified() - .use(remarkParse, {position: false}) + .use(remarkParse) .use(remarkMath) .use(remarkRehype) .use(rehypeKatex) diff --git a/packages/rehype-katex/tsconfig.json b/packages/rehype-katex/tsconfig.json new file mode 100644 index 0000000..7e61871 --- /dev/null +++ b/packages/rehype-katex/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/rehype-katex/types/index.d.ts b/packages/rehype-katex/types/index.d.ts deleted file mode 100644 index 49a3185..0000000 --- a/packages/rehype-katex/types/index.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {Plugin} from 'unified' -import {KatexOptions} from 'katex' - -type RehypeKatexOptions = KatexOptions - -declare const rehypeKatex: Plugin<[RehypeKatexOptions?]> - -export = rehypeKatex diff --git a/packages/rehype-katex/types/test.ts b/packages/rehype-katex/types/test.ts deleted file mode 100644 index 9323ffa..0000000 --- a/packages/rehype-katex/types/test.ts +++ /dev/null @@ -1,9 +0,0 @@ -import unified from 'unified' -import katex from 'rehype-katex' - -// $ExpectType Processor -unified().use(katex) -// $ExpectType Processor -unified().use(katex, {output: 'html'}) -// $ExpectError -unified().use(katex, {invalidProp: true}) diff --git a/packages/rehype-katex/types/tsconfig.json b/packages/rehype-katex/types/tsconfig.json deleted file mode 100644 index 728b585..0000000 --- a/packages/rehype-katex/types/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "compilerOptions": { - "lib": ["es2015", "dom"], - "strict": true, - "baseUrl": ".", - "paths": { "rehype-katex": ["."] }, - "esModuleInterop": true - } -} diff --git a/packages/rehype-katex/types/tslint.json b/packages/rehype-katex/types/tslint.json deleted file mode 100644 index 70c4494..0000000 --- a/packages/rehype-katex/types/tslint.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "dtslint/dtslint.json", - "rules": { - "semicolon": false, - "whitespace": false - } -} diff --git a/packages/rehype-mathjax/browser.d.ts b/packages/rehype-mathjax/browser.d.ts deleted file mode 100644 index 35cbab6..0000000 --- a/packages/rehype-mathjax/browser.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {Plugin} from 'unified' // eslint-disable-line import/no-extraneous-dependencies - -// http://docs.mathjax.org/en/latest/options/input/tex.html#the-configuration-block -type MathNotation = [string, string] -interface BrowserOptions { - displayMath?: MathNotation | MathNotation[] - inlineMath?: MathNotation | MathNotation[] -} - -declare const renderBrowser: Plugin<[BrowserOptions?]> - -export = renderBrowser diff --git a/packages/rehype-mathjax/browser.js b/packages/rehype-mathjax/browser.js index aa3a761..b88a8ba 100644 --- a/packages/rehype-mathjax/browser.js +++ b/packages/rehype-mathjax/browser.js @@ -1,25 +1,39 @@ +/** + * @typedef {import('hast').Root} Root + * @typedef {import('hast').Element} Element + * @typedef {import('./lib/create-plugin').MathNotation} MathNotation + * @typedef {import('./lib/create-plugin').BrowserOptions} Options + */ + import {createPlugin} from './lib/create-plugin.js' -const rehypeMathJaxBrowser = createPlugin( - 'rehypeMathJaxBrowser', - renderBrowser, - false, - true -) -export default rehypeMathJaxBrowser +const rehypeMathJaxBrowser = + /** @type {import('unified').Plugin<[Options?]|void[], Root>} */ ( + createPlugin( + // To do next major: Make `options` match the format of MathJax options + // `{tex: ...}` + (_, options) => { + /** @type {MathNotation} */ + let display = ['\\[', '\\]'] + /** @type {MathNotation} */ + let inline = ['\\(', '\\)'] -// To do next major: Make `options` match the format of MathJax options -// `{tex: ...}` -function renderBrowser(options) { - const settings = options || {} - const display = settings.displayMath || ['\\[', '\\]'] - const inline = settings.inlineMath || ['\\(', '\\)'] + if ('displayMath' in options && options.displayMath) { + display = options.displayMath + } - return {render} + if ('inlineMath' in options && options.inlineMath) { + inline = options.inlineMath + } - function render(node, renderOptions) { - const delimiters = renderOptions.display ? display : inline - node.children.unshift({type: 'text', value: delimiters[0]}) - node.children.push({type: 'text', value: delimiters[1]}) - } -} + return { + render(node, options) { + const delimiters = options.display ? display : inline + node.children.unshift({type: 'text', value: delimiters[0]}) + node.children.push({type: 'text', value: delimiters[1]}) + } + } + } + ) + ) +export default rehypeMathJaxBrowser diff --git a/packages/rehype-mathjax/chtml.d.ts b/packages/rehype-mathjax/chtml.d.ts deleted file mode 100644 index 6f8e560..0000000 --- a/packages/rehype-mathjax/chtml.d.ts +++ /dev/null @@ -1,43 +0,0 @@ -import {Plugin} from 'unified' // eslint-disable-line import/no-extraneous-dependencies - -// http://docs.mathjax.org/en/latest/options/output/chtml.html#the-configuration-block -interface MathJaxCHtmlOptions { - scale?: number - minScale?: number - matchFontHeight?: boolean - mtextInheritFont?: boolean - merrorInheritFont?: boolean - mathmlSpacing?: boolean - skipAttributes?: Record - exFactor?: number - displayAlign?: 'left' | 'center' | 'right' - displayIndent?: string - fontURL: string - adaptiveCSS?: boolean -} - -// http://docs.mathjax.org/en/latest/options/input/tex.html#the-configuration-block -interface MathJaxInputTexOptions { - packages: string[] - inlineMath: [[string, string]] - displayMath: [[string, string]] - processEscapes: boolean - processEnvironments: boolean - processRefs: boolean - digits: RegExp - tags: 'none' | 'ams' | 'all' - tagSide: 'left' | 'right' - tagIndent: string - useLabelIds: boolean - multlineWidth: string - maxMacros: number - maxBuffer: number - baseURL: string - formatError: (jax: any, error: any) => string -} - -declare const renderCHtml: Plugin< - [MathJaxCHtmlOptions & {tex?: Partial}] -> - -export = renderCHtml diff --git a/packages/rehype-mathjax/chtml.js b/packages/rehype-mathjax/chtml.js index 7aa2bcd..2ccff67 100644 --- a/packages/rehype-mathjax/chtml.js +++ b/packages/rehype-mathjax/chtml.js @@ -1,15 +1,20 @@ -import {createInput} from './lib/create-input.js' +/** + * @typedef {import('hast').Root} Root + * @typedef {import('./lib/create-plugin').CHtmlOptions} Options + */ + import {createOutputChtml} from './lib/create-output-chtml.js' import {createRenderer} from './lib/create-renderer.js' import {createPlugin} from './lib/create-plugin.js' -const rehypeMathJaxCHtml = createPlugin('rehypeMathJaxCHtml', renderCHtml, true) +const rehypeMathJaxCHtml = + /** @type {import('unified').Plugin<[Options?]|void[], Root>} */ + ( + createPlugin( + (inputOptions, outputOptions) => + createRenderer(inputOptions, createOutputChtml(outputOptions)), + true + ) + ) export default rehypeMathJaxCHtml - -function renderCHtml(inputOptions, outputOptions) { - return createRenderer( - createInput(inputOptions), - createOutputChtml(outputOptions) - ) -} diff --git a/packages/rehype-mathjax/index.d.ts b/packages/rehype-mathjax/index.d.ts deleted file mode 100644 index f75e630..0000000 --- a/packages/rehype-mathjax/index.d.ts +++ /dev/null @@ -1,48 +0,0 @@ -// Minimum TypeScript Version: 3.2 -import {Plugin} from 'unified' // eslint-disable-line import/no-extraneous-dependencies - -// Should be ported back to MathJax repo -// http://docs.mathjax.org/en/latest/options/output/svg.html#the-configuration-block -interface MathJaxSvgOptions { - scale: number - minScale: number - mtextInheritFont: boolean - merrorInheritFont: boolean - mathmlSpacing: boolean - skipAttributes: Record - exFactor: number - displayAlign: 'left' | 'center' | 'right' - displayIndent: string - fontCache: 'local' | 'global' - localID: string | null - internalSpeechTitles: true - titleID: number -} - -// http://docs.mathjax.org/en/latest/options/input/tex.html#the-configuration-block -interface MathJaxInputTexOptions { - packages: string[] - inlineMath: [[string, string]] - displayMath: [[string, string]] - processEscapes: boolean - processEnvironments: boolean - processRefs: boolean - digits: RegExp - tags: 'none' | 'ams' | 'all' - tagSide: 'left' | 'right' - tagIndent: string - useLabelIds: boolean - multlineWidth: string - maxMacros: number - maxBuffer: number - baseURL: string - formatError: (jax: any, error: any) => string -} - -type RenderSVGOptions = Partial - -declare const renderSvg: Plugin< - [(RenderSVGOptions & {tex?: Partial})?] -> - -export = renderSvg diff --git a/packages/rehype-mathjax/lib/create-input.js b/packages/rehype-mathjax/lib/create-input.js index a73859c..78e6f5d 100644 --- a/packages/rehype-mathjax/lib/create-input.js +++ b/packages/rehype-mathjax/lib/create-input.js @@ -1,6 +1,9 @@ import {TeX} from 'mathjax-full/js/input/tex.js' import {AllPackages} from 'mathjax-full/js/input/tex/AllPackages.js' +/** + * @param {unknown} options + */ export function createInput(options) { return new TeX(Object.assign({packages: AllPackages}, options)) } diff --git a/packages/rehype-mathjax/lib/create-output-chtml.js b/packages/rehype-mathjax/lib/create-output-chtml.js index e0e6b32..afc725b 100644 --- a/packages/rehype-mathjax/lib/create-output-chtml.js +++ b/packages/rehype-mathjax/lib/create-output-chtml.js @@ -1,5 +1,9 @@ import {CHTML} from 'mathjax-full/js/output/chtml.js' +/** + * @param {unknown} options + */ export function createOutputChtml(options) { + // @ts-expect-error: assume options work (mathjax types are not exported) return new CHTML(options) } diff --git a/packages/rehype-mathjax/lib/create-output-svg.js b/packages/rehype-mathjax/lib/create-output-svg.js index e3115a3..7fcc71c 100644 --- a/packages/rehype-mathjax/lib/create-output-svg.js +++ b/packages/rehype-mathjax/lib/create-output-svg.js @@ -1,5 +1,9 @@ import {SVG} from 'mathjax-full/js/output/svg.js' +/** + * @param {unknown} options + */ export function createOutputSvg(options) { + // @ts-expect-error: assume options work (mathjax types are not exported) return new SVG(options) } diff --git a/packages/rehype-mathjax/lib/create-plugin.js b/packages/rehype-mathjax/lib/create-plugin.js index 5cf9ccd..e7ee18d 100644 --- a/packages/rehype-mathjax/lib/create-plugin.js +++ b/packages/rehype-mathjax/lib/create-plugin.js @@ -1,44 +1,115 @@ +/** + * @typedef {import('hast').Root} Root + * @typedef {import('hast').Element} Element + * + * @typedef {[string, string]} MathNotation + * Markers to use for math. + * See: + * + * @typedef BrowserOptions + * Configuration. + * @property {MathNotation} [displayMath] + * Markers to use for blocks. + * @property {MathNotation} [inlineMath] + * Markers to use for inlines. + * + * @typedef MathJaxSvgOptions + * + * @property {number} [scale] + * @property {number} [minScale] + * @property {boolean} [mtextInheritFont] + * @property {boolean} [merrorInheritFont] + * @property {boolean} [mathmlSpacing] + * @property {Record} [skipAttributes] + * @property {number} [exFactor] + * @property {'left'|'center'|'right'} [displayAlign] + * @property {string} [displayIndent] + * @property {'local'|'global'} [fontCache] + * @property {string|null} [localID] + * @property {boolean} [internalSpeechTitles] + * @property {number} [titleID] + * + * @typedef MathJaxCHtmlOptions + * + * @property {number} [scale] + * @property {number} [minScale] + * @property {boolean} [matchFontHeight] + * @property {boolean} [mtextInheritFont] + * @property {boolean} [merrorInheritFont] + * @property {boolean} [mathmlSpacing] + * @property {Record} [skipAttributes] + * @property {number} [exFactor] + * @property {'left'|'center'|'right'} [displayAlign] + * @property {string} [displayIndent] + * @property {string} fontURL + * @property {boolean} [adaptiveCSS] + * + * @typedef MathJaxInputTexOptions + * + * @property {string[]} [packages] + * @property {MathNotation[]} [inlineMath] + * @property {MathNotation[]} [displayMath] + * @property {boolean} [processEscapes] + * @property {boolean} [processEnvironments] + * @property {boolean} [processRefs] + * @property {RegExp} [digits] + * @property {'none'|'ams'|'all'} [tags] + * @property {'left'|'right'} [tagSide] + * @property {string} [tagIndent] + * @property {boolean} [useLabelIds] + * @property {string} [multlineWidth] + * @property {number} [maxMacros] + * @property {number} [maxBuffer] + * @property {string} [baseURL] + * @property {(jax: any, error: any) => string} [formatError] + * + * @typedef {MathJaxCHtmlOptions & {tex?: MathJaxInputTexOptions}} CHtmlOptions + * @typedef {MathJaxSvgOptions & {tex?: MathJaxInputTexOptions}} SvgOptions + * + * @typedef {BrowserOptions|CHtmlOptions|SvgOptions} Options + * + * @typedef Renderer + * @property {(node: Element, options: {display: boolean}) => void} render + * @property {() => Element} [styleSheet] + * + * @callback CreateRenderer + * @param {MathJaxInputTexOptions} inputOptions + * @param {MathJaxCHtmlOptions|MathJaxSvgOptions|BrowserOptions} outputOptions + * @returns {Renderer} + */ + import {visit, SKIP} from 'unist-util-visit' // To do next major: Remove `chtml` and `browser` flags once all the options use // the same format. -export function createPlugin(displayName, createRenderer, chtml, browser) { - attacher.displayName = displayName - - return attacher - function attacher(options) { - if (chtml && (!options || !options.fontURL)) { +/** + * @param {CreateRenderer} createRenderer + * @param {boolean} [chtml=false] + */ +export function createPlugin(createRenderer, chtml) { + /** @type {import('unified').Plugin<[Options?]|void[], Root>} */ + return (options = {}) => { + if (chtml && (!('fontURL' in options) || !options.fontURL)) { throw new Error( 'rehype-mathjax: missing `fontURL` in options, which must be set to a URL to reach MathJaX fonts' ) } - const inputOptions = browser ? options : (options || {}).tex - let outputOptions = options || {} - if ('tex' in outputOptions) { - outputOptions = Object.assign({}, outputOptions) - delete outputOptions.tex - } - - transform.displayName = displayName + 'Transform' - - return transform - - function transform(tree) { - const renderer = createRenderer(inputOptions, outputOptions) + // @ts-expect-error: hush. + const {tex, ...outputOptions} = options + return (tree) => { + const renderer = createRenderer(tex || {}, outputOptions) + /** @type {Root|Element} */ let context = tree let found = false - visit(tree, 'element', onelement) - - if (found && renderer.styleSheet) { - context.children.push(renderer.styleSheet()) - } - - function onelement(node) { - const classes = node.properties.className || [] + visit(tree, 'element', (node) => { + const classes = + node.properties && Array.isArray(node.properties.className) + ? node.properties.className + : [] const inline = classes.includes('math-inline') const display = classes.includes('math-display') @@ -54,6 +125,10 @@ export function createPlugin(displayName, createRenderer, chtml, browser) { renderer.render(node, {display}) return SKIP + }) + + if (found && renderer.styleSheet) { + context.children.push(renderer.styleSheet()) } } } diff --git a/packages/rehype-mathjax/lib/create-renderer.js b/packages/rehype-mathjax/lib/create-renderer.js index 8d86d5a..50e19ea 100644 --- a/packages/rehype-mathjax/lib/create-renderer.js +++ b/packages/rehype-mathjax/lib/create-renderer.js @@ -1,7 +1,16 @@ +/** + * @typedef {import('hast').Element} Element + * @typedef {import('mathjax-full/js/core/OutputJax').OutputJax} OutputJax + * @typedef {import('./create-plugin.js').CreateRenderer} CreateRenderer + * + * For some reason MathJax types can’t be imported. + */ + import {mathjax} from 'mathjax-full/js/mathjax.js' import {RegisterHTMLHandler} from 'mathjax-full/js/handlers/html.js' import {fromDom} from 'hast-util-from-dom' import {toText} from 'hast-util-to-text' +import {createInput} from './create-input.js' import {createAdaptor} from './create-adaptor.js' const adaptor = createAdaptor() @@ -19,23 +28,29 @@ const adaptor = createAdaptor() /* eslint-disable-next-line new-cap */ RegisterHTMLHandler(adaptor) -export function createRenderer(input, output) { +/** + * @type {CreateRenderer} + * @param {OutputJax} output + */ +export function createRenderer(inputOptions, output) { + const input = createInput(inputOptions) const doc = mathjax.document('', {InputJax: input, OutputJax: output}) - return {render, styleSheet} - - function render(node, options) { - node.children = [fromDom(doc.convert(toText(node), options))] - } - - function styleSheet() { - const value = adaptor.textContent(output.styleSheet(doc)) + return { + render(node, options) { + const domNode = doc.convert(toText(node), options) + // @ts-expect-error: assume no `doctypes` + node.children = [fromDom(domNode)] + }, + styleSheet() { + const value = adaptor.textContent(output.styleSheet(doc)) - return { - type: 'element', - tagName: 'style', - properties: {}, - children: [{type: 'text', value}] + return { + type: 'element', + tagName: 'style', + properties: {}, + children: [{type: 'text', value}] + } } } } diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index 190903c..19d5378 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -32,28 +32,50 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ "lib/", + "browser.d.ts", "browser.js", + "chtml.d.ts", "chtml.js", + "index.d.ts", "index.js", + "svg.d.ts", "svg.js" ], "browser": { "./lib/create-adaptor.js": "./lib/create-adaptor.browser.js" }, "dependencies": { + "@types/hast": "^2.0.0", "@types/mathjax": "^0.0.36", "hast-util-from-dom": "^4.0.0", "hast-util-to-text": "^3.0.0", "jsdom": "^16.0.0", "mathjax-full": "^3.0.0", + "unified": "^10.0.0", "unist-util-visit": "^4.0.0" }, + "devDependencies": { + "@types/jsdom": "^16.0.0" + }, "scripts": { + "build": "rimraf \"lib/**/*.d.ts\" \"test/**/*.d.ts\" \"*.d.ts\" && tsc && type-coverage", "test-api": "node --conditions development test/index.js", - "test-types": "dtslint", - "test": "npm run test-api && npm run test-types" + "test": "npm run build && npm run test-api" }, - "xo": false + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true, + "#": "needed `any`s", + "ignoreFiles": [ + "lib/create-plugin.d.ts", + "lib/create-renderer.d.ts", + "lib/create-renderer.js" + ] + } } diff --git a/packages/rehype-mathjax/svg.js b/packages/rehype-mathjax/svg.js index cbe9be5..54dbdcf 100644 --- a/packages/rehype-mathjax/svg.js +++ b/packages/rehype-mathjax/svg.js @@ -1,15 +1,18 @@ -import {createInput} from './lib/create-input.js' +/** + * @typedef {import('hast').Root} Root + * @typedef {import('./lib/create-plugin').SvgOptions} Options + */ + import {createOutputSvg} from './lib/create-output-svg.js' import {createRenderer} from './lib/create-renderer.js' import {createPlugin} from './lib/create-plugin.js' -const rehypeMathJaxSvg = createPlugin('rehypeMathJaxSvg', renderSvg) +const rehypeMathJaxSvg = + /** @type {import('unified').Plugin<[Options?]|void[], Root>} */ + ( + createPlugin((inputOptions, outputOptions) => + createRenderer(inputOptions, createOutputSvg(outputOptions)) + ) + ) export default rehypeMathJaxSvg - -function renderSvg(inputOptions, outputOptions) { - return createRenderer( - createInput(inputOptions), - createOutputSvg(outputOptions) - ) -} diff --git a/packages/rehype-mathjax/test.ts b/packages/rehype-mathjax/test.ts deleted file mode 100644 index 64312c5..0000000 --- a/packages/rehype-mathjax/test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import unified from 'unified' // eslint-disable-line import/no-extraneous-dependencies -import mathjax from 'rehype-mathjax' -import chtml from 'rehype-mathjax/chtml' -import browser from 'rehype-mathjax/browser' - -// $ExpectType Processor -unified().use(mathjax) -// $ExpectType Processor -unified().use(mathjax, {minScale: 3}) -// $ExpectType Processor -unified().use(mathjax, {minScale: 3, tex: {tags: 'ams'}}) -// $ExpectError -unified().use(mathjax, {invalidProp: true}) - -// $ExpectType Processor -unified().use(chtml, {fontURL: 'url'}) -// $ExpectType Processor -unified().use(chtml, {fontURL: 'url', tex: {tags: 'ams'}}) -// $ExpectError -unified().use(chtml) -// $ExpectError -unified().use(chtml, {}) -// $ExpectError -unified().use(chtml, {adaptiveCSS: true}) -// $ExpectError -unified().use(chtml, {fontURL: 'url', invalidProp: true}) - -// $ExpectType Processor -unified().use(browser) -// $ExpectType Processor -unified().use(browser, {displayMath: ['$$', '$$']}) -// $ExpectType Processor -unified().use(browser, { - displayMath: [ - ['$$', '$$'], - ['((', '))'] - ] -}) -// $ExpectError -unified().use(browser, {displayMath: ['$$']}) -// $ExpectError -unified().use(browser, {invalidProp: true}) diff --git a/packages/rehype-mathjax/tsconfig.json b/packages/rehype-mathjax/tsconfig.json index 20c9189..5fec52d 100644 --- a/packages/rehype-mathjax/tsconfig.json +++ b/packages/rehype-mathjax/tsconfig.json @@ -1,13 +1,4 @@ { - "compilerOptions": { - "lib": ["es2015", "dom"], - "strict": true, - "baseUrl": ".", - "paths": { - "rehype-mathjax": ["."], - "rehype-mathjax/chtml": ["./chtml"], - "rehype-mathjax/browser": ["./browser"] - }, - "esModuleInterop": true - } + "extends": "../../tsconfig.json", + "include": ["lib/**/*.js", "test/**/*.js", "*.js"] } diff --git a/packages/rehype-mathjax/tslint.json b/packages/rehype-mathjax/tslint.json deleted file mode 100644 index 70c4494..0000000 --- a/packages/rehype-mathjax/tslint.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "dtslint/dtslint.json", - "rules": { - "semicolon": false, - "whitespace": false - } -} diff --git a/packages/remark-html-katex/index.js b/packages/remark-html-katex/index.js index c16d980..e4c228d 100644 --- a/packages/remark-html-katex/index.js +++ b/packages/remark-html-katex/index.js @@ -1,3 +1,8 @@ +/** + * @typedef {import('mdast').Root} Root + * @typedef {import('katex').KatexOptions} Options + */ + import {visit} from 'unist-util-visit' import {removePosition} from 'unist-util-remove-position' import katex from 'katex' @@ -8,44 +13,50 @@ const parseHtml = unified().use(rehypeParse, {fragment: true}) const source = 'remark-html-katex' -export default function remarkHtmlKatex(options) { - const settings = options || {} - const throwOnError = settings.throwOnError || false - - return transform - - function transform(tree, file) { - visit(tree, ['inlineMath', 'math'], onmath) - - function onmath(node) { - const displayMode = node.type === 'math' - let result - - try { - result = katex.renderToString( - node.value, - Object.assign({}, settings, { - displayMode, - throwOnError: true - }) - ) - } catch (error) { - const fn = throwOnError ? 'fail' : 'message' - const origin = [source, error.name.toLowerCase()].join(':') - - file[fn](error.message, node.position, origin) - - result = katex.renderToString( - node.value, - Object.assign({}, settings, { - displayMode, - throwOnError: false, - strict: 'ignore' - }) - ) +/** + * Plugin to transform `inlineMath` and `math` nodes with KaTeX for + * `remark-html`. + * + * @type {import('unified').Plugin<[Options?]|void[], Root>} + */ +export default function remarkHtmlKatex(options = {}) { + const throwOnError = options.throwOnError || false + + return (tree, file) => { + visit(tree, (node) => { + if (node.type === 'inlineMath' || node.type === 'math') { + const displayMode = node.type === 'math' + /** @type {string} */ + let result + + try { + result = katex.renderToString( + node.value, + Object.assign({}, options, { + displayMode, + throwOnError: true + }) + ) + } catch (error) { + const fn = throwOnError ? 'fail' : 'message' + const origin = [source, error.name.toLowerCase()].join(':') + + file[fn](error.message, node.position, origin) + + result = katex.renderToString( + node.value, + Object.assign({}, options, { + displayMode, + throwOnError: false, + strict: 'ignore' + }) + ) + } + + const data = node.data || (node.data = {}) + + data.hChildren = removePosition(parseHtml.parse(result)).children } - - node.data.hChildren = removePosition(parseHtml.parse(result)).children - } + }) } } diff --git a/packages/remark-html-katex/package.json b/packages/remark-html-katex/package.json index 16d1541..449a1fa 100644 --- a/packages/remark-html-katex/package.json +++ b/packages/remark-html-katex/package.json @@ -30,11 +30,14 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { "@types/katex": "^0.11.0", + "@types/mdast": "^3.0.0", "katex": "^0.13.0", "rehype-parse": "^8.0.0", "unified": "^10.0.0", @@ -42,9 +45,15 @@ "unist-util-visit": "^4.0.0" }, "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage", "test-api": "node --conditions development test.js", - "test-types": "dtslint types", - "test": "npm run test-api && npm run test-types" + "test": "npm run build && npm run test-api" }, - "xo": false + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-html-katex/test.js b/packages/remark-html-katex/test.js index 2075285..e9ae15f 100644 --- a/packages/remark-html-katex/test.js +++ b/packages/remark-html-katex/test.js @@ -50,7 +50,7 @@ test('remark-html-katex', (t) => { t.deepEqual( unified() - .use(remarkParse, {position: false}) + .use(remarkParse) .use(remarkMath) .use(remarkHtmlKatex, {macros}) .use(remarkHtml) @@ -70,7 +70,7 @@ test('remark-html-katex', (t) => { t.deepEqual( unified() - .use(remarkParse, {position: false}) + .use(remarkParse) .use(remarkMath) .use(remarkHtmlKatex, {errorColor: 'orange'}) .use(remarkHtml) @@ -122,7 +122,7 @@ test('remark-html-katex', (t) => { t.deepEqual( unified() - .use(remarkParse, {position: false}) + .use(remarkParse) .use(remarkMath) .use(remarkHtmlKatex, {errorColor: 'orange', strict: 'ignore'}) .use(remarkHtml) @@ -138,5 +138,20 @@ test('remark-html-katex', (t) => { 'should support `strict: ignore`' ) + const pipeline = unified() + .use(remarkHtmlKatex, {errorColor: 'orange', strict: 'ignore'}) + .use(remarkHtml) + + t.deepEqual( + pipeline.stringify( + pipeline.runSync({ + type: 'root', + children: [{type: 'inlineMath', value: '\\alpha'}] + }) + ), + '
' + katex.renderToString('\\alpha') + '
\n', + 'should support generated nodes' + ) + t.end() }) diff --git a/packages/remark-html-katex/tsconfig.json b/packages/remark-html-katex/tsconfig.json new file mode 100644 index 0000000..7e61871 --- /dev/null +++ b/packages/remark-html-katex/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-html-katex/types/index.d.ts b/packages/remark-html-katex/types/index.d.ts deleted file mode 100644 index c826b5b..0000000 --- a/packages/remark-html-katex/types/index.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {Plugin} from 'unified' -import {KatexOptions} from 'katex' - -type HtmlKatexOptions = KatexOptions - -declare const htmlKatex: Plugin<[HtmlKatexOptions?]> - -export = htmlKatex diff --git a/packages/remark-html-katex/types/test.ts b/packages/remark-html-katex/types/test.ts deleted file mode 100644 index e301a45..0000000 --- a/packages/remark-html-katex/types/test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import unified from 'unified' -import html from 'remark-html-katex' - -// $ExpectType Processor -unified().use(html) -// $ExpectType Processor -unified().use(html, {output: 'mathml'}) -// $ExpectError -unified().use(html, {output: true}) -// $ExpectError -unified().use(html, {invalidProp: true}) diff --git a/packages/remark-html-katex/types/tsconfig.json b/packages/remark-html-katex/types/tsconfig.json deleted file mode 100644 index 38c7beb..0000000 --- a/packages/remark-html-katex/types/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "compilerOptions": { - "lib": ["es2015", "dom"], - "strict": true, - "baseUrl": ".", - "paths": {"remark-html-katex": ["."]}, - "esModuleInterop": true - } -} diff --git a/packages/remark-html-katex/types/tslint.json b/packages/remark-html-katex/types/tslint.json deleted file mode 100644 index 70c4494..0000000 --- a/packages/remark-html-katex/types/tslint.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "dtslint/dtslint.json", - "rules": { - "semicolon": false, - "whitespace": false - } -} diff --git a/packages/remark-math/index.js b/packages/remark-math/index.js index d8ab764..a6e2271 100644 --- a/packages/remark-math/index.js +++ b/packages/remark-math/index.js @@ -1,8 +1,20 @@ +/** + * @typedef {import('mdast').Root} Root + * + * @typedef {import('mdast-util-math')} DoNotTouchAsThisImportIncludesMathInTree + */ + import {math} from 'micromark-extension-math' import {mathFromMarkdown, mathToMarkdown} from 'mdast-util-math' +/** @type {boolean|undefined} */ let warningIssued +/** + * Plugin to support math. + * + * @type {import('unified').Plugin} + */ export default function remarkMath() { const data = this.data() @@ -27,10 +39,17 @@ export default function remarkMath() { add('fromMarkdownExtensions', mathFromMarkdown) add('toMarkdownExtensions', mathToMarkdown) + /** + * @param {string} field + * @param {unknown} value + */ function add(field, value) { - // Other extensions. - /* c8 ignore next */ - if (data[field]) data[field].push(value) - else data[field] = [value] + const list = /** @type {unknown[]} */ ( + // Other extensions + /* c8 ignore next 2 */ + data[field] ? data[field] : (data[field] = []) + ) + + list.push(value) } } diff --git a/packages/remark-math/package.json b/packages/remark-math/package.json index 369dbe6..534b3e0 100644 --- a/packages/remark-math/package.json +++ b/packages/remark-math/package.json @@ -29,20 +29,27 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ - "block.js", - "index.js", - "inline.js", - "util.js" + "index.d.ts", + "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", "mdast-util-math": "^1.0.0", - "micromark-extension-math": "^1.0.0" + "micromark-extension-math": "^1.0.0", + "unified": "^10.0.0" }, "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage", "test-api": "node --conditions development test.js", - "test-types": "dtslint types", - "test": "npm run test-api && npm run test-types" + "test": "npm run build && npm run test-api" }, - "xo": false + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-math/tsconfig.json b/packages/remark-math/tsconfig.json new file mode 100644 index 0000000..7e61871 --- /dev/null +++ b/packages/remark-math/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-math/types/index.d.ts b/packages/remark-math/types/index.d.ts deleted file mode 100644 index 436c521..0000000 --- a/packages/remark-math/types/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import {Plugin} from 'unified' // eslint-disable-line import/no-extraneous-dependencies - -declare const remarkMath: Plugin<[]> - -export = remarkMath diff --git a/packages/remark-math/types/test.ts b/packages/remark-math/types/test.ts deleted file mode 100644 index f5995be..0000000 --- a/packages/remark-math/types/test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import unified from 'unified' // eslint-disable-line import/no-extraneous-dependencies -import math from 'remark-math' - -// $ExpectType Processor -unified().use(math) -// $ExpectError -unified().use(math, {invalidProp: true}) diff --git a/packages/remark-math/types/tsconfig.json b/packages/remark-math/types/tsconfig.json deleted file mode 100644 index 3440cc6..0000000 --- a/packages/remark-math/types/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "compilerOptions": { - "lib": ["es2015", "dom"], - "strict": true, - "baseUrl": ".", - "paths": {"remark-math": ["."]}, - "esModuleInterop": true - } -} diff --git a/packages/remark-math/types/tslint.json b/packages/remark-math/types/tslint.json deleted file mode 100644 index 70c4494..0000000 --- a/packages/remark-math/types/tslint.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "dtslint/dtslint.json", - "rules": { - "semicolon": false, - "whitespace": false - } -} diff --git a/packages/remark-math/util.js b/packages/remark-math/util.js deleted file mode 100644 index 0f61138..0000000 --- a/packages/remark-math/util.js +++ /dev/null @@ -1,10 +0,0 @@ -exports.isRemarkParser = isRemarkParser -exports.isRemarkCompiler = isRemarkCompiler - -function isRemarkParser(parser) { - return Boolean(parser && parser.prototype && parser.prototype.blockTokenizers) -} - -function isRemarkCompiler(compiler) { - return Boolean(compiler && compiler.prototype && compiler.prototype.visitors) -} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..e31adf8 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,16 @@ +{ + "include": ["*.js"], + "compilerOptions": { + "target": "ES2020", + "lib": ["ES2020"], + "module": "ES2020", + "moduleResolution": "node", + "allowJs": true, + "checkJs": true, + "declaration": true, + "emitDeclarationOnly": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true, + "strict": true + } +} From 0b77136996d1eaea2db1aa745fef0a058424ce6e Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 9 Aug 2021 13:08:34 +0200 Subject: [PATCH 28/98] Remove warning for remark 12 --- packages/remark-math/index.js | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/packages/remark-math/index.js b/packages/remark-math/index.js index a6e2271..266a061 100644 --- a/packages/remark-math/index.js +++ b/packages/remark-math/index.js @@ -7,9 +7,6 @@ import {math} from 'micromark-extension-math' import {mathFromMarkdown, mathToMarkdown} from 'mdast-util-math' -/** @type {boolean|undefined} */ -let warningIssued - /** * Plugin to support math. * @@ -18,23 +15,6 @@ let warningIssued export default function remarkMath() { const data = this.data() - // Old remark. - /* c8 ignore next 14 */ - if ( - !warningIssued && - ((this.Parser && - this.Parser.prototype && - this.Parser.prototype.blockTokenizers) || - (this.Compiler && - this.Compiler.prototype && - this.Compiler.prototype.visitors)) - ) { - warningIssued = true - console.warn( - '[remark-math] Warning: please upgrade to remark 13 to use this plugin' - ) - } - add('micromarkExtensions', math) add('fromMarkdownExtensions', mathFromMarkdown) add('toMarkdownExtensions', mathToMarkdown) From 6899b0703a0491b3f883fb431fb1fd1c47bfa4e2 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 9 Aug 2021 13:39:28 +0200 Subject: [PATCH 29/98] Add improved internal types --- packages/rehype-mathjax/browser.js | 1 + .../rehype-mathjax/lib/create-adaptor.browser.js | 2 +- packages/rehype-mathjax/lib/create-input.js | 8 +++++++- packages/rehype-mathjax/lib/create-output-chtml.js | 9 +++++++-- packages/rehype-mathjax/lib/create-output-svg.js | 9 +++++++-- packages/rehype-mathjax/lib/create-renderer.js | 14 ++++++++------ packages/rehype-mathjax/package.json | 1 + 7 files changed, 32 insertions(+), 12 deletions(-) diff --git a/packages/rehype-mathjax/browser.js b/packages/rehype-mathjax/browser.js index b88a8ba..a1bd647 100644 --- a/packages/rehype-mathjax/browser.js +++ b/packages/rehype-mathjax/browser.js @@ -36,4 +36,5 @@ const rehypeMathJaxBrowser = } ) ) + export default rehypeMathJaxBrowser diff --git a/packages/rehype-mathjax/lib/create-adaptor.browser.js b/packages/rehype-mathjax/lib/create-adaptor.browser.js index 8096f1f..cd44dfc 100644 --- a/packages/rehype-mathjax/lib/create-adaptor.browser.js +++ b/packages/rehype-mathjax/lib/create-adaptor.browser.js @@ -1,3 +1,3 @@ import {browserAdaptor} from 'mathjax-full/js/adaptors/browserAdaptor.js' -export {browserAdaptor} +export {browserAdaptor as createAdaptor} diff --git a/packages/rehype-mathjax/lib/create-input.js b/packages/rehype-mathjax/lib/create-input.js index 78e6f5d..30b3d00 100644 --- a/packages/rehype-mathjax/lib/create-input.js +++ b/packages/rehype-mathjax/lib/create-input.js @@ -1,8 +1,14 @@ +/** + * @typedef {import('mathjax-full/js/util/Options.js').OptionList} OptionList + * @typedef {import('mathjax-full/js/input/tex.js').TeX} TeX_ + */ + import {TeX} from 'mathjax-full/js/input/tex.js' import {AllPackages} from 'mathjax-full/js/input/tex/AllPackages.js' /** - * @param {unknown} options + * @param {OptionList} options + * @returns {TeX_} */ export function createInput(options) { return new TeX(Object.assign({packages: AllPackages}, options)) diff --git a/packages/rehype-mathjax/lib/create-output-chtml.js b/packages/rehype-mathjax/lib/create-output-chtml.js index afc725b..cb6fdd9 100644 --- a/packages/rehype-mathjax/lib/create-output-chtml.js +++ b/packages/rehype-mathjax/lib/create-output-chtml.js @@ -1,9 +1,14 @@ +/** + * @typedef {import('mathjax-full/js/util/Options.js').OptionList} OptionList + * @typedef {import('mathjax-full/js/output/chtml.js').CHTML} CHTML_ + */ + import {CHTML} from 'mathjax-full/js/output/chtml.js' /** - * @param {unknown} options + * @param {OptionList} options + * @returns {CHTML_} */ export function createOutputChtml(options) { - // @ts-expect-error: assume options work (mathjax types are not exported) return new CHTML(options) } diff --git a/packages/rehype-mathjax/lib/create-output-svg.js b/packages/rehype-mathjax/lib/create-output-svg.js index 7fcc71c..c2763f4 100644 --- a/packages/rehype-mathjax/lib/create-output-svg.js +++ b/packages/rehype-mathjax/lib/create-output-svg.js @@ -1,9 +1,14 @@ +/** + * @typedef {import('mathjax-full/js/util/Options.js').OptionList} OptionList + * @typedef {import('mathjax-full/js/output/svg.js').SVG} SVG_ + */ + import {SVG} from 'mathjax-full/js/output/svg.js' /** - * @param {unknown} options + * @param {OptionList} options + * @returns {SVG_} */ export function createOutputSvg(options) { - // @ts-expect-error: assume options work (mathjax types are not exported) return new SVG(options) } diff --git a/packages/rehype-mathjax/lib/create-renderer.js b/packages/rehype-mathjax/lib/create-renderer.js index 50e19ea..b953444 100644 --- a/packages/rehype-mathjax/lib/create-renderer.js +++ b/packages/rehype-mathjax/lib/create-renderer.js @@ -1,9 +1,8 @@ /** * @typedef {import('hast').Element} Element - * @typedef {import('mathjax-full/js/core/OutputJax').OutputJax} OutputJax + * @typedef {import('mathjax-full/js/core/OutputJax').OutputJax} OutputJax + * @typedef {import('mathjax-full/js/core/MathDocument.js').MathDocument} MathDocument * @typedef {import('./create-plugin.js').CreateRenderer} CreateRenderer - * - * For some reason MathJax types can’t be imported. */ import {mathjax} from 'mathjax-full/js/mathjax.js' @@ -34,13 +33,16 @@ RegisterHTMLHandler(adaptor) */ export function createRenderer(inputOptions, output) { const input = createInput(inputOptions) + /** @type {MathDocument} */ const doc = mathjax.document('', {InputJax: input, OutputJax: output}) return { render(node, options) { - const domNode = doc.convert(toText(node), options) - // @ts-expect-error: assume no `doctypes` - node.children = [fromDom(domNode)] + // @ts-expect-error: assume mathml nodes can be handled by + // `hast-util-from-dom`. + const domNode = fromDom(doc.convert(toText(node), options)) + // @ts-expect-error: `fromDom` returns an element for a given element. + node.children = [domNode] }, styleSheet() { const value = adaptor.textContent(output.styleSheet(doc)) diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index 19d5378..2e1a654 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -50,6 +50,7 @@ "dependencies": { "@types/hast": "^2.0.0", "@types/mathjax": "^0.0.36", + "@types/web": "^0.0.15", "hast-util-from-dom": "^4.0.0", "hast-util-to-text": "^3.0.0", "jsdom": "^16.0.0", From c6fc7a07e4f32a58062e8cc8ba2a5289128394aa Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 9 Aug 2021 14:24:18 +0200 Subject: [PATCH 30/98] rehype-mathjax: change options to match `mathjax` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, output options for SVG and CHTML were set on the main options object. MathJax itself puts them in separate fields. Similarly, the “browser” plugin supported `inlineMath` and `displayMath` option fields that were close to `tex.inlineMath` and `tex.displayMath`. We now match MathJax’ option format: ```diff - .use(rehypeMathJaxChtml, {fontURL: 'place/to/fonts'}) + .use(rehypeMathJaxChtml, {chtml: {fontURL: 'place/to/fonts'}}) ``` ```diff - .use(rehypeMathJaxSvg, {scale: 1}) + .use(rehypeMathJaxSvg, {svg: {scale: 1}}) ``` Note that `inlineMath` and `displayMath` are now lists of pairs, instead of a single pair! ```diff - .use(rehypeMathJaxBrowser, {inlineMath: ['\\(', '\\)']}) + .use(rehypeMathJaxBrowser, {tex: {inlineMath: [['\\(', '\\)']]}}) ``` --- packages/rehype-mathjax/browser.js | 42 +++++------------ packages/rehype-mathjax/chtml.js | 21 +++++---- packages/rehype-mathjax/lib/create-input.js | 15 ------ .../rehype-mathjax/lib/create-output-chtml.js | 14 ------ .../rehype-mathjax/lib/create-output-svg.js | 14 ------ packages/rehype-mathjax/lib/create-plugin.js | 47 ++++++------------- .../rehype-mathjax/lib/create-renderer.js | 14 ++++-- packages/rehype-mathjax/readme.md | 17 ++++++- packages/rehype-mathjax/svg.js | 15 +++--- packages/rehype-mathjax/test/index.js | 20 ++++++-- 10 files changed, 85 insertions(+), 134 deletions(-) delete mode 100644 packages/rehype-mathjax/lib/create-input.js delete mode 100644 packages/rehype-mathjax/lib/create-output-chtml.js delete mode 100644 packages/rehype-mathjax/lib/create-output-svg.js diff --git a/packages/rehype-mathjax/browser.js b/packages/rehype-mathjax/browser.js index a1bd647..246d075 100644 --- a/packages/rehype-mathjax/browser.js +++ b/packages/rehype-mathjax/browser.js @@ -2,39 +2,23 @@ * @typedef {import('hast').Root} Root * @typedef {import('hast').Element} Element * @typedef {import('./lib/create-plugin').MathNotation} MathNotation - * @typedef {import('./lib/create-plugin').BrowserOptions} Options + * @typedef {import('./lib/create-plugin').Options} Options */ import {createPlugin} from './lib/create-plugin.js' -const rehypeMathJaxBrowser = - /** @type {import('unified').Plugin<[Options?]|void[], Root>} */ ( - createPlugin( - // To do next major: Make `options` match the format of MathJax options - // `{tex: ...}` - (_, options) => { - /** @type {MathNotation} */ - let display = ['\\[', '\\]'] - /** @type {MathNotation} */ - let inline = ['\\(', '\\)'] +const rehypeMathJaxBrowser = createPlugin((options) => { + const tex = options.tex || {} + const display = tex.displayMath || [['\\[', '\\]']] + const inline = tex.inlineMath || [['\\(', '\\)']] - if ('displayMath' in options && options.displayMath) { - display = options.displayMath - } - - if ('inlineMath' in options && options.inlineMath) { - inline = options.inlineMath - } - - return { - render(node, options) { - const delimiters = options.display ? display : inline - node.children.unshift({type: 'text', value: delimiters[0]}) - node.children.push({type: 'text', value: delimiters[1]}) - } - } - } - ) - ) + return { + render(node, options) { + const delimiters = (options.display ? display : inline)[0] + node.children.unshift({type: 'text', value: delimiters[0]}) + node.children.push({type: 'text', value: delimiters[1]}) + } + } +}) export default rehypeMathJaxBrowser diff --git a/packages/rehype-mathjax/chtml.js b/packages/rehype-mathjax/chtml.js index 2ccff67..90e3397 100644 --- a/packages/rehype-mathjax/chtml.js +++ b/packages/rehype-mathjax/chtml.js @@ -1,20 +1,21 @@ /** * @typedef {import('hast').Root} Root - * @typedef {import('./lib/create-plugin').CHtmlOptions} Options + * @typedef {import('mathjax-full/js/output/chtml.js').CHTML} CHTML_ + * @typedef {import('./lib/create-plugin').Options} Options */ -import {createOutputChtml} from './lib/create-output-chtml.js' +import {CHTML} from 'mathjax-full/js/output/chtml.js' import {createRenderer} from './lib/create-renderer.js' import {createPlugin} from './lib/create-plugin.js' -const rehypeMathJaxCHtml = - /** @type {import('unified').Plugin<[Options?]|void[], Root>} */ - ( - createPlugin( - (inputOptions, outputOptions) => - createRenderer(inputOptions, createOutputChtml(outputOptions)), - true +const rehypeMathJaxCHtml = createPlugin((options) => { + if (!options.chtml || !options.chtml.fontURL) { + throw new Error( + 'rehype-mathjax: missing `fontURL` in options, which must be set to a URL to reach MathJaX fonts' ) - ) + } + + return createRenderer(options, new CHTML(options.chtml)) +}) export default rehypeMathJaxCHtml diff --git a/packages/rehype-mathjax/lib/create-input.js b/packages/rehype-mathjax/lib/create-input.js deleted file mode 100644 index 30b3d00..0000000 --- a/packages/rehype-mathjax/lib/create-input.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @typedef {import('mathjax-full/js/util/Options.js').OptionList} OptionList - * @typedef {import('mathjax-full/js/input/tex.js').TeX} TeX_ - */ - -import {TeX} from 'mathjax-full/js/input/tex.js' -import {AllPackages} from 'mathjax-full/js/input/tex/AllPackages.js' - -/** - * @param {OptionList} options - * @returns {TeX_} - */ -export function createInput(options) { - return new TeX(Object.assign({packages: AllPackages}, options)) -} diff --git a/packages/rehype-mathjax/lib/create-output-chtml.js b/packages/rehype-mathjax/lib/create-output-chtml.js deleted file mode 100644 index cb6fdd9..0000000 --- a/packages/rehype-mathjax/lib/create-output-chtml.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @typedef {import('mathjax-full/js/util/Options.js').OptionList} OptionList - * @typedef {import('mathjax-full/js/output/chtml.js').CHTML} CHTML_ - */ - -import {CHTML} from 'mathjax-full/js/output/chtml.js' - -/** - * @param {OptionList} options - * @returns {CHTML_} - */ -export function createOutputChtml(options) { - return new CHTML(options) -} diff --git a/packages/rehype-mathjax/lib/create-output-svg.js b/packages/rehype-mathjax/lib/create-output-svg.js deleted file mode 100644 index c2763f4..0000000 --- a/packages/rehype-mathjax/lib/create-output-svg.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @typedef {import('mathjax-full/js/util/Options.js').OptionList} OptionList - * @typedef {import('mathjax-full/js/output/svg.js').SVG} SVG_ - */ - -import {SVG} from 'mathjax-full/js/output/svg.js' - -/** - * @param {OptionList} options - * @returns {SVG_} - */ -export function createOutputSvg(options) { - return new SVG(options) -} diff --git a/packages/rehype-mathjax/lib/create-plugin.js b/packages/rehype-mathjax/lib/create-plugin.js index e7ee18d..97a6569 100644 --- a/packages/rehype-mathjax/lib/create-plugin.js +++ b/packages/rehype-mathjax/lib/create-plugin.js @@ -6,14 +6,7 @@ * Markers to use for math. * See: * - * @typedef BrowserOptions - * Configuration. - * @property {MathNotation} [displayMath] - * Markers to use for blocks. - * @property {MathNotation} [inlineMath] - * Markers to use for inlines. - * - * @typedef MathJaxSvgOptions + * @typedef OutputSvgOptions * * @property {number} [scale] * @property {number} [minScale] @@ -29,7 +22,7 @@ * @property {boolean} [internalSpeechTitles] * @property {number} [titleID] * - * @typedef MathJaxCHtmlOptions + * @typedef OutputCHtmlOptions * * @property {number} [scale] * @property {number} [minScale] @@ -44,7 +37,7 @@ * @property {string} fontURL * @property {boolean} [adaptiveCSS] * - * @typedef MathJaxInputTexOptions + * @typedef InputTexOptions * * @property {string[]} [packages] * @property {MathNotation[]} [inlineMath] @@ -63,47 +56,37 @@ * @property {string} [baseURL] * @property {(jax: any, error: any) => string} [formatError] * - * @typedef {MathJaxCHtmlOptions & {tex?: MathJaxInputTexOptions}} CHtmlOptions - * @typedef {MathJaxSvgOptions & {tex?: MathJaxInputTexOptions}} SvgOptions - * - * @typedef {BrowserOptions|CHtmlOptions|SvgOptions} Options + * @typedef Options + * Configuration. + * @property {InputTexOptions} [tex] + * Configuration for the input TeX. + * @property {OutputCHtmlOptions} [chtml] + * Configuration for the output (when CHTML). + * @property {OutputSvgOptions} [svg] + * Configuration for the output (when SVG). * * @typedef Renderer * @property {(node: Element, options: {display: boolean}) => void} render * @property {() => Element} [styleSheet] * * @callback CreateRenderer - * @param {MathJaxInputTexOptions} inputOptions - * @param {MathJaxCHtmlOptions|MathJaxSvgOptions|BrowserOptions} outputOptions + * @param {Options} options * @returns {Renderer} */ import {visit, SKIP} from 'unist-util-visit' -// To do next major: Remove `chtml` and `browser` flags once all the options use -// the same format. - /** * @param {CreateRenderer} createRenderer - * @param {boolean} [chtml=false] */ -export function createPlugin(createRenderer, chtml) { +export function createPlugin(createRenderer) { /** @type {import('unified').Plugin<[Options?]|void[], Root>} */ return (options = {}) => { - if (chtml && (!('fontURL' in options) || !options.fontURL)) { - throw new Error( - 'rehype-mathjax: missing `fontURL` in options, which must be set to a URL to reach MathJaX fonts' - ) - } - - // @ts-expect-error: hush. - const {tex, ...outputOptions} = options - return (tree) => { - const renderer = createRenderer(tex || {}, outputOptions) + const renderer = createRenderer(options) + let found = false /** @type {Root|Element} */ let context = tree - let found = false visit(tree, 'element', (node) => { const classes = diff --git a/packages/rehype-mathjax/lib/create-renderer.js b/packages/rehype-mathjax/lib/create-renderer.js index b953444..cd97255 100644 --- a/packages/rehype-mathjax/lib/create-renderer.js +++ b/packages/rehype-mathjax/lib/create-renderer.js @@ -2,14 +2,17 @@ * @typedef {import('hast').Element} Element * @typedef {import('mathjax-full/js/core/OutputJax').OutputJax} OutputJax * @typedef {import('mathjax-full/js/core/MathDocument.js').MathDocument} MathDocument - * @typedef {import('./create-plugin.js').CreateRenderer} CreateRenderer + * @typedef {import('mathjax-full/js/input/tex.js').TeX} TeX_ + * @typedef {import('./create-plugin.js').Options} Options + * @typedef {import('./create-plugin.js').Renderer} Renderer */ import {mathjax} from 'mathjax-full/js/mathjax.js' import {RegisterHTMLHandler} from 'mathjax-full/js/handlers/html.js' +import {TeX} from 'mathjax-full/js/input/tex.js' +import {AllPackages} from 'mathjax-full/js/input/tex/AllPackages.js' import {fromDom} from 'hast-util-from-dom' import {toText} from 'hast-util-to-text' -import {createInput} from './create-input.js' import {createAdaptor} from './create-adaptor.js' const adaptor = createAdaptor() @@ -28,11 +31,12 @@ const adaptor = createAdaptor() RegisterHTMLHandler(adaptor) /** - * @type {CreateRenderer} + * @param {Options} options * @param {OutputJax} output + * @returns {Renderer} */ -export function createRenderer(inputOptions, output) { - const input = createInput(inputOptions) +export function createRenderer(options, output) { + const input = new TeX(Object.assign({packages: AllPackages}, options.tex)) /** @type {MathDocument} */ const doc = mathjax.document('', {InputJax: input, OutputJax: output}) diff --git a/packages/rehype-mathjax/readme.md b/packages/rehype-mathjax/readme.md index 754b83d..f3fc00b 100644 --- a/packages/rehype-mathjax/readme.md +++ b/packages/rehype-mathjax/readme.md @@ -123,11 +123,22 @@ Options are not passed to MathJax: do that yourself on the client. All options, except when using the browser plugin, are passed to [MathJax][mathjax-options]. -Specifically, they are passed to the chosen output processor. #### `options.tex` These options are passed to the [TeX input processor][mathjax-tex-options]. +The browser plugin uses the first delimiter pair in `tex.displayMath` and +`tex.inlineMath` to instead wrap math. + +#### `options.chtml` + +These options are passed to the [CommonHTML output +processor][mathjax-chtml-options]. +Passing `fontURL` is required! + +#### `options.svg` + +These options are passed to the [SVG output processor][mathjax-svg-options]. ## Security @@ -208,3 +219,7 @@ abide by its terms. [mathjax-chtml]: http://docs.mathjax.org/en/latest/output/html.html [mathjax-tex-options]: http://docs.mathjax.org/en/latest/options/input/tex.html + +[mathjax-svg-options]: http://docs.mathjax.org/en/latest/options/output/svg.html + +[mathjax-chtml-options]: http://docs.mathjax.org/en/latest/options/output/chtml.html diff --git a/packages/rehype-mathjax/svg.js b/packages/rehype-mathjax/svg.js index 54dbdcf..23fc64a 100644 --- a/packages/rehype-mathjax/svg.js +++ b/packages/rehype-mathjax/svg.js @@ -1,18 +1,15 @@ /** * @typedef {import('hast').Root} Root - * @typedef {import('./lib/create-plugin').SvgOptions} Options + * @typedef {import('mathjax-full/js/output/svg.js').SVG} SVG_ + * @typedef {import('./lib/create-plugin.js').Options} Options */ -import {createOutputSvg} from './lib/create-output-svg.js' +import {SVG} from 'mathjax-full/js/output/svg.js' import {createRenderer} from './lib/create-renderer.js' import {createPlugin} from './lib/create-plugin.js' -const rehypeMathJaxSvg = - /** @type {import('unified').Plugin<[Options?]|void[], Root>} */ - ( - createPlugin((inputOptions, outputOptions) => - createRenderer(inputOptions, createOutputSvg(outputOptions)) - ) - ) +const rehypeMathJaxSvg = createPlugin((options) => + createRenderer(options, new SVG(options.svg)) +) export default rehypeMathJaxSvg diff --git a/packages/rehype-mathjax/test/index.js b/packages/rehype-mathjax/test/index.js index 078ead8..c5c1c74 100644 --- a/packages/rehype-mathjax/test/index.js +++ b/packages/rehype-mathjax/test/index.js @@ -27,7 +27,12 @@ test('rehype-mathjax', (t) => { t.throws( () => { - unified().use(rehypeMathJaxChtml).freeze() + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxChtml) + .use(rehypeStringify) + .processSync(readSync({dirname: fixtures, basename: 'small.html'})) + .toString() }, /rehype-mathjax: missing `fontURL` in options/, 'should crash for CHTML w/o `fontURL`' @@ -36,7 +41,7 @@ test('rehype-mathjax', (t) => { t.equal( unified() .use(rehypeParse, {fragment: true}) - .use(rehypeMathJaxChtml, {fontURL: 'place/to/fonts'}) + .use(rehypeMathJaxChtml, {chtml: {fontURL: 'place/to/fonts'}}) .use(rehypeStringify) .processSync(readSync({dirname: fixtures, basename: 'small.html'})) .toString(), @@ -105,8 +110,10 @@ test('rehype-mathjax', (t) => { unified() .use(rehypeParse, {fragment: true}) .use(rehypeMathJaxBrowser, { - inlineMath: ['$', '$'], - displayMath: ['$$', '$$'] + tex: { + inlineMath: [['$', '$']], + displayMath: [['$$', '$$']] + } }) .use(rehypeStringify) .processSync(readSync({dirname: fixtures, basename: 'small.html'})) @@ -165,7 +172,10 @@ test('rehype-mathjax', (t) => { t.equal( unified() .use(rehypeParse, {fragment: true}) - .use(rehypeMathJaxChtml, {fontURL: 'place/to/fonts', tex: {tags: 'ams'}}) + .use(rehypeMathJaxChtml, { + chtml: {fontURL: 'place/to/fonts'}, + tex: {tags: 'ams'} + }) .use(rehypeStringify) .processSync( readSync({ From db0d5a5b382a6771a49198ff71a4ebb06b4524c4 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 9 Aug 2021 15:30:21 +0200 Subject: [PATCH 31/98] Update `xo` --- package.json | 2 +- packages/rehype-mathjax/lib/create-plugin.js | 5 ++--- packages/rehype-mathjax/test/index.js | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index c987296..aa4d983 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "unified": "^10.0.0", "unist-builder": "^3.0.0", "unist-util-remove-position": "^4.0.0", - "xo": "^0.39.0" + "xo": "^0.44.0" }, "scripts": { "postinstall": "lerna bootstrap --no-ci", diff --git a/packages/rehype-mathjax/lib/create-plugin.js b/packages/rehype-mathjax/lib/create-plugin.js index 97a6569..0ac2e1a 100644 --- a/packages/rehype-mathjax/lib/create-plugin.js +++ b/packages/rehype-mathjax/lib/create-plugin.js @@ -81,8 +81,8 @@ import {visit, SKIP} from 'unist-util-visit' */ export function createPlugin(createRenderer) { /** @type {import('unified').Plugin<[Options?]|void[], Root>} */ - return (options = {}) => { - return (tree) => { + return (options = {}) => + (tree) => { const renderer = createRenderer(options) let found = false /** @type {Root|Element} */ @@ -114,5 +114,4 @@ export function createPlugin(createRenderer) { context.children.push(renderer.styleSheet()) } } - } } diff --git a/packages/rehype-mathjax/test/index.js b/packages/rehype-mathjax/test/index.js index c5c1c74..5fb9fde 100644 --- a/packages/rehype-mathjax/test/index.js +++ b/packages/rehype-mathjax/test/index.js @@ -1,4 +1,4 @@ -import path from 'path' +import path from 'node:path' import test from 'tape' import {readSync} from 'to-vfile' import {unified} from 'unified' From 52d3acb691f5d2c74f008b30c1b159ed7dd19826 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 9 Aug 2021 15:37:15 +0200 Subject: [PATCH 32/98] Fix coverage on Erbium --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index aa4d983..7c428dc 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "build": "lerna run build", "format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", "test-api": "lerna run test-api", - "test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov npm run test-api", + "test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov --exclude \"packages/*/test.js\" --exclude \"packages/*/test/**/*.js\" npm run test-api", "test": "npm run build && npm run format && npm run test-coverage" }, "prettier": { From 9193bc22f33e4de36406291bc40314c0200e5d1f Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 9 Aug 2021 15:41:35 +0200 Subject: [PATCH 33/98] 5.0.0 --- packages/remark-math/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/remark-math/package.json b/packages/remark-math/package.json index 534b3e0..dc6341b 100644 --- a/packages/remark-math/package.json +++ b/packages/remark-math/package.json @@ -1,6 +1,6 @@ { "name": "remark-math", - "version": "4.0.0", + "version": "5.0.0", "description": "remark plugin to parse and stringify math", "license": "MIT", "keywords": [ From 5cfcbbf057c9b5709388595a406ae89db9b0b77e Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 9 Aug 2021 15:42:37 +0200 Subject: [PATCH 34/98] remark-html-katex: 4.0.0 --- packages/remark-html-katex/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/remark-html-katex/package.json b/packages/remark-html-katex/package.json index 449a1fa..2049c56 100644 --- a/packages/remark-html-katex/package.json +++ b/packages/remark-html-katex/package.json @@ -1,6 +1,6 @@ { "name": "remark-html-katex", - "version": "3.0.0", + "version": "4.0.0", "description": "remark plugin to transform inline and block math with KaTeX", "license": "MIT", "keywords": [ From 3f97570b17cc43a3f11ff1a2ad3bf5e8e3fa85fb Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 9 Aug 2021 15:43:04 +0200 Subject: [PATCH 35/98] rehype-mathjax: 4.0.0 --- packages/rehype-mathjax/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index 2e1a654..d3d76b7 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -1,6 +1,6 @@ { "name": "rehype-mathjax", - "version": "3.1.0", + "version": "4.0.0", "description": "rehype plugin to transform inline and block math with MathJax", "license": "MIT", "keywords": [ From 52b145855df58e0cd85c2171452f192181cfb43d Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 9 Aug 2021 15:43:37 +0200 Subject: [PATCH 36/98] rehype-katex: 6.0.0 --- packages/rehype-katex/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index 031032e..88376a4 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -1,6 +1,6 @@ { "name": "rehype-katex", - "version": "5.0.0", + "version": "6.0.0", "description": "rehype plugin to transform inline and block math with KaTeX", "license": "MIT", "keywords": [ From fa1795345ed0dd6f32c00fad875c62a025b5a1eb Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 16 Aug 2021 12:55:07 +0200 Subject: [PATCH 37/98] Mark `remark-html-katex` as legacy --- packages/remark-html-katex/package.json | 16 +-- packages/remark-html-katex/readme.md | 168 ++---------------------- readme.md | 13 +- 3 files changed, 15 insertions(+), 182 deletions(-) diff --git a/packages/remark-html-katex/package.json b/packages/remark-html-katex/package.json index 2049c56..d17fb73 100644 --- a/packages/remark-html-katex/package.json +++ b/packages/remark-html-katex/package.json @@ -1,21 +1,9 @@ { "name": "remark-html-katex", "version": "4.0.0", - "description": "remark plugin to transform inline and block math with KaTeX", + "description": "Legacy remark plugin to transform math with KaTeX — please use `rehype-katex` instead", "license": "MIT", - "keywords": [ - "unified", - "remark", - "remark-plugin", - "plugin", - "mdast", - "markdown", - "html", - "math", - "katex", - "latex", - "tex" - ], + "keywords": [], "repository": "https://github.com/remarkjs/remark-math/tree/main/packages/remark-html-katex", "bugs": "https://github.com/remarkjs/remark-math/issues", "funding": { diff --git a/packages/remark-html-katex/readme.md b/packages/remark-html-katex/readme.md index 7e5b8a8..a2d4524 100644 --- a/packages/remark-html-katex/readme.md +++ b/packages/remark-html-katex/readme.md @@ -1,113 +1,15 @@ # remark-html-katex -[![Build][build-badge]][build] -[![Coverage][coverage-badge]][coverage] -[![Downloads][downloads-badge]][downloads] -[![Size][size-badge]][size] -[![Sponsors][sponsors-badge]][collective] -[![Backers][backers-badge]][collective] -[![Chat][chat-badge]][chat] +**Stability: Legacy**. +This package is no longer recommended for use. +It’s still covered by semantic-versioning guarantees and not yet deprecated, +but use of this package should be avoided. +Please use `remark-rehype` to move from remark (markdown) to rehype (HTML) +and then replace `remark-html-katex` with [`rehype-katex`][rehype-katex] +or [`rehype-mathjax`][rehype-mathjax]. -[**remark**][remark] plugin to transform `inlineMath` and `math` nodes with -[KaTeX][] for [`remark-html`][remark-html]. - -> This package integrates with [`remark-html`][remark-html]. -> It’s better to work with [**rehype**][rehype], which is specifically made -> for HTML, and to use [`rehype-katex`][rehype-katex] instead of this package. - -## Install - -This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c): -Node 12+ is needed to use it and it must be `import`ed instead of `require`d. - -[npm][]: - -```sh -npm install remark-html-katex -``` - -## Use - -Say we have the following file, `example.md`: - -```markdown -Lift($L$) can be determined by Lift Coefficient ($C_L$) like the following equation. - -$$ -L = \frac{1}{2} \rho v^2 S C_L -$$ -``` - -And our module, `example.js`, looks as follows: - -```js -import {readSync} from 'to-vfile' -import {unified} from 'unified' -import remarkParse from 'remark-parse' -import remarkMath from 'remark-math' -import remarkHtmlKatex from 'remark-html-katex' -import remarkHtml from 'remark-html' - -const file = readSync('example.md') - -unified() - .use(remarkParse) - .use(remarkMath) - .use(remarkHtmlKatex) - .use(remarkHtml) - .process(file) - .then((file) => { - console.log(String(file)) - }) -``` - -Now, running `node example` yields: - -```html -

Lift(LL) can be determined by Lift Coefficient (CLC_L) like the following equation.

-
L=12ρv2SCLL = \frac{1}{2} \rho v^2 S C_L
-``` - -## API - -This package exports no identifiers. -The default export is `remarkHtmlKatex`. - -### `unified().use(remarkHtmlKatex[, options])` - -Transform `inlineMath` and `math` nodes with [KaTeX][] for -[`remark-html`][remark-html]. - -#### `options` - -##### `options.throwOnError` - -Throw if a KaTeX parse error occurs (`boolean`, default: `false`). -See [KaTeX options][katex-options]. - -##### `options.<*>` - -All other options, except for `displayMode`, are passed to -[KaTeX][katex-options]. - -## Security - -Use of `remark-html-katex` renders user content with [KaTeX][], so any -vulnerability in KaTeX can open you to a [cross-site scripting (XSS)][xss] -attack. - -Always be wary of user input and use the [`sanitize`][remark-html-sanitize] -option of `remark-html`. - -## Contribute - -See [`contributing.md`][contributing] in [`remarkjs/.github`][health] for ways -to get started. -See [`support.md`][support] for ways to get help. - -This project has a [code of conduct][coc]. -By interacting with this repository, organization, or community you agree to -abide by its terms. +Legacy [documentation for this package](https://github.com/remarkjs/remark-math/tree/52b145855df58e0cd85c2171452f192181cfb43d/packages/remark-html-katex) +is still available in Git. ## License @@ -115,58 +17,10 @@ abide by its terms. -[build-badge]: https://github.com/remarkjs/remark-math/workflows/main/badge.svg - -[build]: https://github.com/remarkjs/remark-math/actions - -[coverage-badge]: https://img.shields.io/codecov/c/github/remarkjs/remark-math.svg - -[coverage]: https://codecov.io/github/remarkjs/remark-math - -[downloads-badge]: https://img.shields.io/npm/dm/remark-html-katex.svg - -[downloads]: https://www.npmjs.com/package/remark-html-katex - -[size-badge]: https://img.shields.io/bundlephobia/minzip/remark-html-katex.svg - -[size]: https://bundlephobia.com/result?p=remark-html-katex - -[sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg - -[backers-badge]: https://opencollective.com/unified/backers/badge.svg - -[collective]: https://opencollective.com/unified - -[chat-badge]: https://img.shields.io/badge/chat-discussions-success.svg - -[chat]: https://github.com/remarkjs/remark/discussions - -[npm]: https://docs.npmjs.com/cli/install - -[health]: https://github.com/remarkjs/.github - -[contributing]: https://github.com/remarkjs/.github/blob/HEAD/contributing.md - -[support]: https://github.com/remarkjs/.github/blob/HEAD/support.md - -[coc]: https://github.com/remarkjs/.github/blob/HEAD/code-of-conduct.md - [license]: https://github.com/remarkjs/remark-math/blob/main/license [author]: https://rokt33r.github.io -[remark]: https://github.com/remarkjs/remark - -[remark-html]: https://github.com/remarkjs/remark-html - -[remark-html-sanitize]: https://github.com/remarkjs/remark-html#optionssanitize - -[rehype]: https://github.com/rehypejs/rehype - -[xss]: https://en.wikipedia.org/wiki/Cross-site_scripting - -[katex]: https://github.com/Khan/KaTeX - -[katex-options]: https://katex.org/docs/options.html - [rehype-katex]: https://github.com/remarkjs/remark-math/tree/main/packages/rehype-katex + +[rehype-mathjax]: https://github.com/remarkjs/remark-math/tree/main/packages/rehype-mathjax diff --git a/readme.md b/readme.md index 0f5f0bf..bdfb6c4 100644 --- a/readme.md +++ b/readme.md @@ -86,20 +86,15 @@ This repo houses four packages: — Parses `$` as `inlineMath` and `$$` as `math` nodes * [`rehype-katex`][rehype-katex] — Transforms math nodes with [KaTeX][] - (✨ recommended) * [`rehype-mathjax`][rehype-mathjax] — Transforms math nodes with [MathJax][] - (✨ recommended) -* [`remark-html-katex`][remark-html-katex] - — Transforms math nodes with [KaTeX][] for [`remark-html`][remark-html] - (discouraged) See their readmes for more information. ## Security -Use of `rehype-katex`, `rehype-mathjax`, or `remark-html-katex` renders user -content with [KaTeX][], so any vulnerability in KaTeX can open you to a +Use of `rehype-katex` or `rehype-mathjax` renders user content with [KaTeX][], +so any vulnerability in KaTeX can open you to a [cross-site scripting (XSS)][xss] attack. Always be wary of user input and use [`rehype-sanitize`][rehype-sanitize]. @@ -175,8 +170,6 @@ abide by its terms. [remark]: https://github.com/remarkjs/remark -[remark-html]: https://github.com/remarkjs/remark-html - [rehype]: https://github.com/rehypejs/rehype [rehype-sanitize]: https://github.com/rehypejs/rehype-sanitize @@ -193,6 +186,4 @@ abide by its terms. [rehype-mathjax]: ./packages/rehype-mathjax -[remark-html-katex]: ./packages/remark-html-katex - [screenshot]: screenshot.png From 8ec6c9660db449de9d78e9d8fc4e63cdad792d63 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 16 Aug 2021 13:03:30 +0200 Subject: [PATCH 38/98] Update dev-dependencies --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7c428dc..65f5a56 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "remark-cli": "^10.0.0", "remark-html": "^14.0.0", "remark-parse": "^10.0.0", - "remark-preset-wooorm": "^8.0.0", + "remark-preset-wooorm": "^9.0.0", "remark-rehype": "^9.0.0", "remark-stringify": "^10.0.0", "rimraf": "^3.0.0", From 7676a20c3a35bf542d10b6b1f49fc807a9347569 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 16 Aug 2021 13:03:50 +0200 Subject: [PATCH 39/98] remark-html-katex: 4.0.1 --- packages/remark-html-katex/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/remark-html-katex/package.json b/packages/remark-html-katex/package.json index d17fb73..cc774ab 100644 --- a/packages/remark-html-katex/package.json +++ b/packages/remark-html-katex/package.json @@ -1,6 +1,6 @@ { "name": "remark-html-katex", - "version": "4.0.0", + "version": "4.0.1", "description": "Legacy remark plugin to transform math with KaTeX — please use `rehype-katex` instead", "license": "MIT", "keywords": [], From 180941642552a478832c0a68ee3cf932a9280b7d Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 23 Aug 2021 11:07:17 +0200 Subject: [PATCH 40/98] Update docs for KaTeX 13 CSS files --- packages/rehype-katex/readme.md | 4 ++++ readme.md | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/rehype-katex/readme.md b/packages/rehype-katex/readme.md index 9e09b2e..dbb5c2b 100644 --- a/packages/rehype-katex/readme.md +++ b/packages/rehype-katex/readme.md @@ -44,6 +44,7 @@ import {readSync} from 'to-vfile' import {unified} from 'unified' import rehypeParse from 'rehype-parse' import rehypeKatex from 'rehype-katex' +import rehypeDocument from 'rehype-document' import rehypeStringify from 'rehype-stringify' const file = readSync('example.html') @@ -51,6 +52,9 @@ const file = readSync('example.html') unified() .use(rehypeParse, {fragment: true}) .use(rehypeKatex) + .use(rehypeDocument, { + css: 'https://cdn.jsdelivr.net/npm/katex@0.13.13/dist/katex.min.css' + }) .use(rehypeStringify) .process(file) .then((file) => { diff --git a/readme.md b/readme.md index bdfb6c4..d3727bd 100644 --- a/readme.md +++ b/readme.md @@ -75,7 +75,7 @@ But in a browser, that looks something like this: > properly: > > ```html -> +> > ``` ## Packages From b65a156ab66f6f9c91f3c97dc21a9ed7b2273257 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 23 Aug 2021 11:18:30 +0200 Subject: [PATCH 41/98] Fix support for comments Closes GH-61. Closes GH-62. Co-authored-by: Nauman Umer --- packages/rehype-katex/index.js | 2 +- packages/rehype-katex/package.json | 2 +- packages/rehype-katex/test.js | 24 +++++++++++++++++++ .../rehype-mathjax/lib/create-renderer.js | 8 ++++--- packages/rehype-mathjax/package.json | 2 +- 5 files changed, 32 insertions(+), 6 deletions(-) diff --git a/packages/rehype-katex/index.js b/packages/rehype-katex/index.js index 989b3a2..3e3a7a3 100644 --- a/packages/rehype-katex/index.js +++ b/packages/rehype-katex/index.js @@ -39,7 +39,7 @@ export default function rehypeKatex(options) { return } - const value = toText(element) + const value = toText(element, {whitespace: 'pre'}) /** @type {string} */ let result diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index 88376a4..ebef54c 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -40,7 +40,7 @@ "dependencies": { "@types/hast": "^2.0.0", "@types/katex": "^0.11.0", - "hast-util-to-text": "^3.0.0", + "hast-util-to-text": "^3.1.0", "katex": "^0.13.0", "rehype-parse": "^8.0.0", "unified": "^10.0.0", diff --git a/packages/rehype-katex/test.js b/packages/rehype-katex/test.js index 3d28c08..8fd0c54 100644 --- a/packages/rehype-katex/test.js +++ b/packages/rehype-katex/test.js @@ -189,5 +189,29 @@ test('rehype-katex', (t) => { 'should support `strict: ignore`' ) + t.deepEqual( + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex, {errorColor: 'orange', strict: 'ignore'}) + .use(rehypeStringify) + .processSync( + '
\\begin{split}\n f(-2) &= \\sqrt{-2+4} \\\\\n &= x % Test Comment\n\\end{split}
' + ) + .toString(), + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeStringify) + .processSync( + '
' + + katex.renderToString( + '\\begin{split}\n f(-2) &= \\sqrt{-2+4} \\\\\n &= x % Test Comment\n\\end{split}', + {displayMode: true} + ) + + '
' + ) + .toString(), + 'should support comments' + ) + t.end() }) diff --git a/packages/rehype-mathjax/lib/create-renderer.js b/packages/rehype-mathjax/lib/create-renderer.js index cd97255..5e796e0 100644 --- a/packages/rehype-mathjax/lib/create-renderer.js +++ b/packages/rehype-mathjax/lib/create-renderer.js @@ -42,9 +42,11 @@ export function createRenderer(options, output) { return { render(node, options) { - // @ts-expect-error: assume mathml nodes can be handled by - // `hast-util-from-dom`. - const domNode = fromDom(doc.convert(toText(node), options)) + const domNode = fromDom( + // @ts-expect-error: assume mathml nodes can be handled by + // `hast-util-from-dom`. + doc.convert(toText(node, {whitespace: 'pre'}), options) + ) // @ts-expect-error: `fromDom` returns an element for a given element. node.children = [domNode] }, diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index d3d76b7..a5af735 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -52,7 +52,7 @@ "@types/mathjax": "^0.0.36", "@types/web": "^0.0.15", "hast-util-from-dom": "^4.0.0", - "hast-util-to-text": "^3.0.0", + "hast-util-to-text": "^3.1.0", "jsdom": "^16.0.0", "mathjax-full": "^3.0.0", "unified": "^10.0.0", From d90fd19e36e62753d177e02176bedfb5dc23543b Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 23 Aug 2021 11:22:44 +0200 Subject: [PATCH 42/98] rehype-katex: 6.0.1 --- packages/rehype-katex/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index ebef54c..5d8a01c 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -1,6 +1,6 @@ { "name": "rehype-katex", - "version": "6.0.0", + "version": "6.0.1", "description": "rehype plugin to transform inline and block math with KaTeX", "license": "MIT", "keywords": [ From a98996fc053897fbbd3d7d55f62881647249d6ff Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 23 Aug 2021 11:23:18 +0200 Subject: [PATCH 43/98] rehype-mathjax: 4.0.1 --- packages/rehype-mathjax/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index a5af735..db90e02 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -1,6 +1,6 @@ { "name": "rehype-mathjax", - "version": "4.0.0", + "version": "4.0.1", "description": "rehype plugin to transform inline and block math with MathJax", "license": "MIT", "keywords": [ From ce0ba8c76da3853aabce5464de8d2bc34fba915c Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 25 Aug 2021 11:38:46 +0200 Subject: [PATCH 44/98] Add `singleDollarTextMath` option This option can be used to prevent math (text) from forming if only one dollar is used. Closes GH-63. --- packages/remark-math/index.js | 11 ++++++----- packages/remark-math/package.json | 4 ++-- packages/remark-math/readme.md | 9 +++++++++ packages/remark-math/test.js | 23 +++++++++++++++++++++++ readme.md | 2 +- 5 files changed, 41 insertions(+), 8 deletions(-) diff --git a/packages/remark-math/index.js b/packages/remark-math/index.js index 266a061..85fcd02 100644 --- a/packages/remark-math/index.js +++ b/packages/remark-math/index.js @@ -1,5 +1,6 @@ /** * @typedef {import('mdast').Root} Root + * @typedef {import('mdast-util-math').ToOptions} Options * * @typedef {import('mdast-util-math')} DoNotTouchAsThisImportIncludesMathInTree */ @@ -10,14 +11,14 @@ import {mathFromMarkdown, mathToMarkdown} from 'mdast-util-math' /** * Plugin to support math. * - * @type {import('unified').Plugin} + * @type {import('unified').Plugin<[Options?] | void[], Root, Root>} */ -export default function remarkMath() { +export default function remarkMath(options = {}) { const data = this.data() - add('micromarkExtensions', math) - add('fromMarkdownExtensions', mathFromMarkdown) - add('toMarkdownExtensions', mathToMarkdown) + add('micromarkExtensions', math(options)) + add('fromMarkdownExtensions', mathFromMarkdown()) + add('toMarkdownExtensions', mathToMarkdown(options)) /** * @param {string} field diff --git a/packages/remark-math/package.json b/packages/remark-math/package.json index dc6341b..a5e4a65 100644 --- a/packages/remark-math/package.json +++ b/packages/remark-math/package.json @@ -36,8 +36,8 @@ ], "dependencies": { "@types/mdast": "^3.0.0", - "mdast-util-math": "^1.0.0", - "micromark-extension-math": "^1.0.0", + "mdast-util-math": "^2.0.0", + "micromark-extension-math": "^2.0.0", "unified": "^10.0.0" }, "scripts": { diff --git a/packages/remark-math/readme.md b/packages/remark-math/readme.md index f192465..6849090 100644 --- a/packages/remark-math/readme.md +++ b/packages/remark-math/readme.md @@ -88,6 +88,15 @@ Get’s useful when combined with [`rehype-katex`][rehype-katex] or See [`micromark/micromark-extension-math`][extension-math] for more info on what syntax is supported. +##### `options` + +###### `options.singleDollarTextMath` + +Whether to support math (text) with a single dollar (`boolean`, default: +`true`). +Single dollars work in Pandoc and many other places, but often interfere with +“normal” dollars in text. + #### Notes ##### Escaping diff --git a/packages/remark-math/test.js b/packages/remark-math/test.js index e174d7c..2e8c532 100644 --- a/packages/remark-math/test.js +++ b/packages/remark-math/test.js @@ -318,6 +318,29 @@ test('remarkMath', (t) => { 'should stringify inline and block math' ) + t.deepEqual( + unified() + .use(remarkParse) + .use(remarkStringify) + .use(remarkMath, {singleDollarTextMath: false}) + .processSync('Math $\\alpha$\n\n$$\\beta+\\gamma$$\n') + .toString(), + 'Math $\\alpha$\n\n$$\\beta+\\gamma$$\n', + 'should support `singleDollarTextMath: false` (1)' + ) + + t.deepEqual( + unified() + .use(remarkParse) + .use(remarkMath, {singleDollarTextMath: false}) + .use(remarkRehype) + .use(rehypeStringify) + .processSync('Math $\\alpha$\n\n$$\\beta+\\gamma$$\n') + .toString(), + '

Math $\\alpha$

\n

\\beta+\\gamma

', + 'should support `singleDollarTextMath: false` (2)' + ) + t.deepEqual( unified() .use(remarkParse) diff --git a/readme.md b/readme.md index d3727bd..8238d86 100644 --- a/readme.md +++ b/readme.md @@ -80,7 +80,7 @@ But in a browser, that looks something like this: ## Packages -This repo houses four packages: +This repo houses three packages: * [`remark-math`][remark-math] — Parses `$` as `inlineMath` and `$$` as `math` nodes From f2af1bd8209267bf2b037cb72f24b4838c219ff9 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 25 Aug 2021 11:42:19 +0200 Subject: [PATCH 45/98] 5.1.0 --- packages/remark-math/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/remark-math/package.json b/packages/remark-math/package.json index a5e4a65..bbc1e31 100644 --- a/packages/remark-math/package.json +++ b/packages/remark-math/package.json @@ -1,6 +1,6 @@ { "name": "remark-math", - "version": "5.0.0", + "version": "5.1.0", "description": "remark plugin to parse and stringify math", "license": "MIT", "keywords": [ From 68b797d35971162401f624383a73d3ad018d72f6 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 8 Oct 2021 11:25:04 +0200 Subject: [PATCH 46/98] Fix type error introduced by new typescript --- packages/rehype-katex/index.js | 3 ++- packages/rehype-katex/test.js | 3 ++- packages/remark-html-katex/index.js | 3 ++- packages/remark-html-katex/test.js | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/rehype-katex/index.js b/packages/rehype-katex/index.js index 3e3a7a3..a18e8aa 100644 --- a/packages/rehype-katex/index.js +++ b/packages/rehype-katex/index.js @@ -49,7 +49,8 @@ export default function rehypeKatex(options) { value, assign({}, settings, {displayMode, throwOnError: true}) ) - } catch (error) { + } catch (exception) { + const error = /** @type {Error} */ (exception) const fn = throwOnError ? 'fail' : 'message' const origin = [source, error.name.toLowerCase()].join(':') diff --git a/packages/rehype-katex/test.js b/packages/rehype-katex/test.js index 8fd0c54..017daee 100644 --- a/packages/rehype-katex/test.js +++ b/packages/rehype-katex/test.js @@ -164,7 +164,8 @@ test('rehype-katex', (t) => { .processSync( '

Lorem

\n

\\alpa

' ) - } catch (error) { + } catch (exception) { + const error = /** @type {Error} */ (exception) t.equal( error.message, 'KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲', diff --git a/packages/remark-html-katex/index.js b/packages/remark-html-katex/index.js index e4c228d..a0c19e4 100644 --- a/packages/remark-html-katex/index.js +++ b/packages/remark-html-katex/index.js @@ -37,7 +37,8 @@ export default function remarkHtmlKatex(options = {}) { throwOnError: true }) ) - } catch (error) { + } catch (exception) { + const error = /** @type {Error} */ (exception) const fn = throwOnError ? 'fail' : 'message' const origin = [source, error.name.toLowerCase()].join(':') diff --git a/packages/remark-html-katex/test.js b/packages/remark-html-katex/test.js index e9ae15f..c6b1267 100644 --- a/packages/remark-html-katex/test.js +++ b/packages/remark-html-katex/test.js @@ -112,7 +112,8 @@ test('remark-html-katex', (t) => { .use(remarkHtmlKatex, {throwOnError: true}) .use(remarkHtml) .processSync('Lorem\n$\\alpa$') - } catch (error) { + } catch (exception) { + const error = /** @type {Error} */ (exception) t.equal( error.message, 'KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲', From 10686c404f7b13335683a7b00ca8c08884fb0319 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 8 Oct 2021 11:29:47 +0200 Subject: [PATCH 47/98] Update dev-dependencies --- package.json | 6 +++--- packages/rehype-katex/index.js | 4 ++-- packages/rehype-katex/test.js | 4 ++-- packages/rehype-mathjax/package.json | 2 +- packages/remark-html-katex/index.js | 4 ++-- packages/remark-html-katex/test.js | 18 +++++++++--------- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 65f5a56..ff8dc38 100644 --- a/package.json +++ b/package.json @@ -24,10 +24,10 @@ "rehype-parse": "^8.0.0", "rehype-stringify": "^9.0.0", "remark-cli": "^10.0.0", - "remark-html": "^14.0.0", + "remark-html": "^15.0.0", "remark-parse": "^10.0.0", "remark-preset-wooorm": "^9.0.0", - "remark-rehype": "^9.0.0", + "remark-rehype": "^10.0.0", "remark-stringify": "^10.0.0", "rimraf": "^3.0.0", "tape": "^5.0.0", @@ -37,7 +37,7 @@ "unified": "^10.0.0", "unist-builder": "^3.0.0", "unist-util-remove-position": "^4.0.0", - "xo": "^0.44.0" + "xo": "^0.45.0" }, "scripts": { "postinstall": "lerna bootstrap --no-ci", diff --git a/packages/rehype-katex/index.js b/packages/rehype-katex/index.js index a18e8aa..5577798 100644 --- a/packages/rehype-katex/index.js +++ b/packages/rehype-katex/index.js @@ -49,8 +49,8 @@ export default function rehypeKatex(options) { value, assign({}, settings, {displayMode, throwOnError: true}) ) - } catch (exception) { - const error = /** @type {Error} */ (exception) + } catch (error_) { + const error = /** @type {Error} */ (error_) const fn = throwOnError ? 'fail' : 'message' const origin = [source, error.name.toLowerCase()].join(':') diff --git a/packages/rehype-katex/test.js b/packages/rehype-katex/test.js index 017daee..968fbb2 100644 --- a/packages/rehype-katex/test.js +++ b/packages/rehype-katex/test.js @@ -164,8 +164,8 @@ test('rehype-katex', (t) => { .processSync( '

Lorem

\n

\\alpa

' ) - } catch (exception) { - const error = /** @type {Error} */ (exception) + } catch (error_) { + const error = /** @type {Error} */ (error_) t.equal( error.message, 'KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲', diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index db90e02..15e0a54 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -53,7 +53,7 @@ "@types/web": "^0.0.15", "hast-util-from-dom": "^4.0.0", "hast-util-to-text": "^3.1.0", - "jsdom": "^16.0.0", + "jsdom": "^17.0.0", "mathjax-full": "^3.0.0", "unified": "^10.0.0", "unist-util-visit": "^4.0.0" diff --git a/packages/remark-html-katex/index.js b/packages/remark-html-katex/index.js index a0c19e4..daa5000 100644 --- a/packages/remark-html-katex/index.js +++ b/packages/remark-html-katex/index.js @@ -37,8 +37,8 @@ export default function remarkHtmlKatex(options = {}) { throwOnError: true }) ) - } catch (exception) { - const error = /** @type {Error} */ (exception) + } catch (error_) { + const error = /** @type {Error} */ (error_) const fn = throwOnError ? 'fail' : 'message' const origin = [source, error.name.toLowerCase()].join(':') diff --git a/packages/remark-html-katex/test.js b/packages/remark-html-katex/test.js index c6b1267..dbb1f65 100644 --- a/packages/remark-html-katex/test.js +++ b/packages/remark-html-katex/test.js @@ -14,7 +14,7 @@ test('remark-html-katex', (t) => { .use(remarkParse) .use(remarkMath) .use(remarkHtmlKatex) - .use(remarkHtml) + .use(remarkHtml, {sanitize: false}) .processSync( [ 'Inline math $\\alpha$.', @@ -53,7 +53,7 @@ test('remark-html-katex', (t) => { .use(remarkParse) .use(remarkMath) .use(remarkHtmlKatex, {macros}) - .use(remarkHtml) + .use(remarkHtml, {sanitize: false}) .processSync('$\\RR$') .toString(), unified() @@ -73,7 +73,7 @@ test('remark-html-katex', (t) => { .use(remarkParse) .use(remarkMath) .use(remarkHtmlKatex, {errorColor: 'orange'}) - .use(remarkHtml) + .use(remarkHtml, {sanitize: false}) .processSync('$\\alpa$') .toString(), unified() @@ -96,7 +96,7 @@ test('remark-html-katex', (t) => { .use(remarkParse) .use(remarkMath) .use(remarkHtmlKatex) - .use(remarkHtml) + .use(remarkHtml, {sanitize: false}) .processSync('Lorem\n$\\alpa$') .messages.map((d) => String(d)), [ @@ -110,10 +110,10 @@ test('remark-html-katex', (t) => { .use(remarkParse) .use(remarkMath) .use(remarkHtmlKatex, {throwOnError: true}) - .use(remarkHtml) + .use(remarkHtml, {sanitize: false}) .processSync('Lorem\n$\\alpa$') - } catch (exception) { - const error = /** @type {Error} */ (exception) + } catch (error_) { + const error = /** @type {Error} */ (error_) t.equal( error.message, 'KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲', @@ -126,7 +126,7 @@ test('remark-html-katex', (t) => { .use(remarkParse) .use(remarkMath) .use(remarkHtmlKatex, {errorColor: 'orange', strict: 'ignore'}) - .use(remarkHtml) + .use(remarkHtml, {sanitize: false}) .processSync('$ê&$') .toString(), unified() @@ -141,7 +141,7 @@ test('remark-html-katex', (t) => { const pipeline = unified() .use(remarkHtmlKatex, {errorColor: 'orange', strict: 'ignore'}) - .use(remarkHtml) + .use(remarkHtml, {sanitize: false}) t.deepEqual( pipeline.stringify( From a54c27ba350b7b855a06934634f6edb62ff0755c Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 8 Oct 2021 11:33:34 +0200 Subject: [PATCH 48/98] Use npm workspaces --- .github/workflows/main.yml | 1 + lerna.json | 3 --- package.json | 12 ++++++++---- 3 files changed, 9 insertions(+), 7 deletions(-) delete mode 100644 lerna.json diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fe284ad..fdb1410 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -11,6 +11,7 @@ jobs: - uses: dcodeIO/setup-node-nvm@master with: node-version: ${{matrix.node}} + - run: npm install -g npm - run: npm install - run: npm test - uses: codecov/codecov-action@v1 diff --git a/lerna.json b/lerna.json deleted file mode 100644 index 053f72e..0000000 --- a/lerna.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "version": "independent" -} diff --git a/package.json b/package.json index ff8dc38..e3822b3 100644 --- a/package.json +++ b/package.json @@ -16,10 +16,15 @@ "Rongjian Zhang " ], "type": "module", + "workspaces": [ + "packages/remark-math", + "packages/remark-html-katex", + "packages/rehype-katex", + "packages/rehype-mathjax" + ], "devDependencies": { "@types/tape": "^4.0.0", "c8": "^7.0.0", - "lerna": "^4.0.0", "prettier": "^2.0.0", "rehype-parse": "^8.0.0", "rehype-stringify": "^9.0.0", @@ -40,10 +45,9 @@ "xo": "^0.45.0" }, "scripts": { - "postinstall": "lerna bootstrap --no-ci", - "build": "lerna run build", + "build": "npm run build --workspaces", "format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", - "test-api": "lerna run test-api", + "test-api": "npm run test-api --workspaces", "test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov --exclude \"packages/*/test.js\" --exclude \"packages/*/test/**/*.js\" npm run test-api", "test": "npm run build && npm run format && npm run test-coverage" }, From d2f2dc74578e43d923e98bcc43ba540bb5271c92 Mon Sep 17 00:00:00 2001 From: Mac Lockard Date: Sat, 9 Oct 2021 01:58:38 -0700 Subject: [PATCH 49/98] Add docs on how to use w/ `rehype-sanitize` Reviewed-by: Christian Murphy Reviewed-by: Titus Wormer Closes GH-67. Closes GH-68. --- readme.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/readme.md b/readme.md index 8238d86..0cacf69 100644 --- a/readme.md +++ b/readme.md @@ -99,6 +99,39 @@ so any vulnerability in KaTeX can open you to a Always be wary of user input and use [`rehype-sanitize`][rehype-sanitize]. +If you are using [`rehype-sanitize`][rehype-sanitize] and trust [KaTeX][], you +can allow the classes added by `remark-math` by extending the default schema +like so: + +```js +const mathSanitizeSchema = { + ...defaultSchema, + attributes: { + ...defaultSchema.attributes, + div: [ + ...defaultSchema.attributes.div, + ['className', 'math', 'math-display'] + ], + span: [ + ['className', 'math', 'math-inline'] + ] + } +} +``` + +And applying the `rehype-katex` plugin *after* the +[`rehype-sanitize`][rehype-sanitize] plugin like so: + +```js +[ + rehypeRaw, + // … + [rehypeSanitize, mathSanitizeSchema], + rehypeKatex + // … +] +``` + ## Related * [`remark-breaks`](https://github.com/remarkjs/remark-breaks) From 849960cbb86662adb4dae0ce1338338c53527b6b Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 8 Nov 2021 12:40:44 +0100 Subject: [PATCH 50/98] Update dev-dependencies --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e3822b3..6936636 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "unified": "^10.0.0", "unist-builder": "^3.0.0", "unist-util-remove-position": "^4.0.0", - "xo": "^0.45.0" + "xo": "^0.46.0" }, "scripts": { "build": "npm run build --workspaces", From b2066bb943068f8513a08daab06f9cc6deacae3e Mon Sep 17 00:00:00 2001 From: Titus Date: Thu, 11 Nov 2021 15:02:59 +0100 Subject: [PATCH 51/98] Add improved docs Closes GH-70. Reviewed-by: Merlijn Vos Reviewed-by: Christian Murphy --- packages/rehype-katex/readme.md | 210 +++++++++++++++++++++++----- packages/rehype-mathjax/browser.js | 3 - packages/rehype-mathjax/chtml.js | 2 - packages/rehype-mathjax/index.js | 4 + packages/rehype-mathjax/readme.md | 213 +++++++++++++++++++++++------ packages/rehype-mathjax/svg.js | 2 - packages/remark-math/readme.md | 203 ++++++++++++++++++++------- readme.md | 204 +++++++++++++-------------- 8 files changed, 610 insertions(+), 231 deletions(-) diff --git a/packages/rehype-katex/readme.md b/packages/rehype-katex/readme.md index dbb5c2b..d963f72 100644 --- a/packages/rehype-katex/readme.md +++ b/packages/rehype-katex/readme.md @@ -8,23 +8,71 @@ [![Backers][backers-badge]][collective] [![Chat][chat-badge]][chat] -[**rehype**][rehype] plugin to transform `` and +**[rehype][]** plugin to render `` and `
` with [KaTeX][]. -## Install +## Contents + +* [What is this?](#what-is-this) +* [When should I use this?](#when-should-i-use-this) +* [Install](#install) +* [Use](#use) +* [API](#api) + * [`unified().use(rehypeKatex[, options])`](#unifieduserehypekatex-options) +* [CSS](#css) +* [Syntax tree](#syntax-tree) +* [Types](#types) +* [Security](#security) +* [Related](#related) +* [Contribute](#contribute) +* [License](#license) + +## What is this? + +This package is a [unified][] ([rehype][]) plugin to render math. +You can combine it with [`remark-math`][remark-math] for math in markdown or add +`math-inline` and `math-display` classes in HTML. -This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c): -Node 12+ is needed to use it and it must be `import`ed instead of `require`d. +**unified** is a project that transforms content with abstract syntax trees +(ASTs). +**rehype** adds support for HTML to unified. +**hast** is the HTML AST that rehype uses. +This is a rehype plugin that transforms hast. -[npm][]: +## When should I use this? + +This project is useful as it renders math with KaTeX at compile time, which +means that there is no client side JavaScript needed. + +A different plugin, [`rehype-mathjax`][rehype-mathjax], is similar but uses +[MathJax][] instead. + +## Install + +This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c). +In Node.js (version 12.20+, 14.14+, or 16.0+), install with [npm][]: ```sh npm install rehype-katex ``` +In Deno with [Skypack][]: + +```js +import rehypeKatex from 'https://cdn.skypack.dev/rehype-katex@6?dts' +``` + +In browsers with [Skypack][]: + +```html + +``` + ## Use -Say we have the following file, `example.html`: +Say we have the following file `example.html`: ```html

@@ -37,41 +85,51 @@ Say we have the following file, `example.html`:

``` -And our module, `example.js`, looks as follows: +And our module `example.js` looks as follows: ```js -import {readSync} from 'to-vfile' +import {read} from 'to-vfile' import {unified} from 'unified' import rehypeParse from 'rehype-parse' import rehypeKatex from 'rehype-katex' import rehypeDocument from 'rehype-document' import rehypeStringify from 'rehype-stringify' -const file = readSync('example.html') +main() -unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex) - .use(rehypeDocument, { - css: 'https://cdn.jsdelivr.net/npm/katex@0.13.13/dist/katex.min.css' - }) - .use(rehypeStringify) - .process(file) - .then((file) => { - console.log(String(file)) - }) +async function main() { + const file = await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex) + .use(rehypeDocument, { + css: 'https://cdn.jsdelivr.net/npm/katex@0.13.13/dist/katex.min.css' + }) + .use(rehypeStringify) + .process(await read('example.html')) + + console.log(String(file)) +} ``` -Now, running `node example` yields: +Now running `node example.js` yields: ```html + + + + +example + + + +

- Lift(LL) can be determined by Lift Coefficient - (CLC_L) like the following equation. + Lift() can be determined by Lift Coefficient + () like the following equation.

- -
L=12ρv2SCLL = \frac{1}{2} \rho v^2 S C_L
- +
+ + ``` ## API @@ -84,25 +142,97 @@ The default export is `rehypeKatex`. Transform `` and `
` with [KaTeX][]. -#### `options` +##### `options` -##### `options.throwOnError` +Configuration (optional). +All options, except for `displayMode`, are passed to [KaTeX][katex-options]. + +###### `options.throwOnError` Throw if a KaTeX parse error occurs (`boolean`, default: `false`). See [KaTeX options][katex-options]. -##### `options.<*>` +## CSS -All other options, except for `displayMode`, are passed to -[KaTeX][katex-options]. +The HTML produced by KaTeX requires CSS to render correctly. +You should use `katex.css` somewhere on the page where the math is shown to +style it properly. +At the time of writing, the last version is: -## Security +```html + +``` + +## Syntax tree + +This plugin transforms elements with a class name of either `math-inline` and/or +`math-display`. + +## Types + +This package is fully typed with [TypeScript][]. +An extra `Options` type is exported, which models the accepted options. + +## Compatibility -Use of `rehype-katex` renders user content with [KaTeX][], so any vulnerability -in KaTeX can open you to a [cross-site scripting (XSS)][xss] attack. +Projects maintained by the unified collective are compatible with all maintained +versions of Node.js. +As of now, that is Node.js 12.20+, 14.14+, and 16.0+. +Our projects sometimes work with older versions, but this is not guaranteed. +This plugin works with unified version 6+ and rehype version 4+. + +## Security + +Using `rehype-katex` should be safe assuming that you trust KaTeX. +Any vulnerability in it could open you to a [cross-site scripting (XSS)][xss] +attack. Always be wary of user input and use [`rehype-sanitize`][rehype-sanitize]. +When you don’t trust user content but do trust KaTeX, you can allow the classes +added by `remark-math` while disallowing anything else in the `rehype-sanitize` +schema, and run `rehype-katex` afterwards. +Like so: + +```js +import rehypeSanitize, {defaultSchema} from 'rehype-stringify' + +const mathSanitizeSchema = { + ...defaultSchema, + attributes: { + ...defaultSchema.attributes, + div: [ + ...defaultSchema.attributes.div, + ['className', 'math', 'math-display'] + ], + span: [ + ['className', 'math', 'math-inline'] + ] + } +} + +// … + +unified() + // … + .use(rehypeSanitize, mathSanitizeSchema) + .use(rehypeKatex) + // … +``` + +## Related + +* [`rehype-mathjax`][rehype-mathjax] + — same but with MathJax +* [`rehype-highlight`](https://github.com/rehypejs/rehype-highlight) + — highlight code blocks +* [`rehype-autolink-headings`](https://github.com/rehypejs/rehype-autolink-headings) + — add links to headings +* [`rehype-sanitize`](https://github.com/rehypejs/rehype-sanitize) + — sanitize HTML +* [`rehype-document`](https://github.com/rehypejs/rehype-document) + — wrap a document around the tree + ## Contribute See [`contributing.md`][contributing] in [`remarkjs/.github`][health] for ways @@ -147,6 +277,8 @@ abide by its terms. [npm]: https://docs.npmjs.com/cli/install +[skypack]: https://www.skypack.dev + [health]: https://github.com/remarkjs/.github [contributing]: https://github.com/remarkjs/.github/blob/HEAD/contributing.md @@ -159,12 +291,22 @@ abide by its terms. [author]: https://rokt33r.github.io +[unified]: https://github.com/unifiedjs/unified + [rehype]: https://github.com/rehypejs/rehype [xss]: https://en.wikipedia.org/wiki/Cross-site_scripting +[typescript]: https://www.typescriptlang.org + [rehype-sanitize]: https://github.com/rehypejs/rehype-sanitize [katex]: https://github.com/Khan/KaTeX [katex-options]: https://katex.org/docs/options.html + +[mathjax]: https://www.mathjax.org + +[remark-math]: ../remark-math + +[rehype-mathjax]: ../rehype-mathjax diff --git a/packages/rehype-mathjax/browser.js b/packages/rehype-mathjax/browser.js index 246d075..f5ecbbd 100644 --- a/packages/rehype-mathjax/browser.js +++ b/packages/rehype-mathjax/browser.js @@ -1,7 +1,4 @@ /** - * @typedef {import('hast').Root} Root - * @typedef {import('hast').Element} Element - * @typedef {import('./lib/create-plugin').MathNotation} MathNotation * @typedef {import('./lib/create-plugin').Options} Options */ diff --git a/packages/rehype-mathjax/chtml.js b/packages/rehype-mathjax/chtml.js index 90e3397..5953e21 100644 --- a/packages/rehype-mathjax/chtml.js +++ b/packages/rehype-mathjax/chtml.js @@ -1,6 +1,4 @@ /** - * @typedef {import('hast').Root} Root - * @typedef {import('mathjax-full/js/output/chtml.js').CHTML} CHTML_ * @typedef {import('./lib/create-plugin').Options} Options */ diff --git a/packages/rehype-mathjax/index.js b/packages/rehype-mathjax/index.js index 1032b6e..57f23b0 100644 --- a/packages/rehype-mathjax/index.js +++ b/packages/rehype-mathjax/index.js @@ -1,3 +1,7 @@ +/** + * @typedef {import('./lib/create-plugin.js').Options} Options + */ + import rehypeMathJaxSvg from './svg.js' export default rehypeMathJaxSvg diff --git a/packages/rehype-mathjax/readme.md b/packages/rehype-mathjax/readme.md index f3fc00b..d47424a 100644 --- a/packages/rehype-mathjax/readme.md +++ b/packages/rehype-mathjax/readme.md @@ -8,23 +8,71 @@ [![Backers][backers-badge]][collective] [![Chat][chat-badge]][chat] -[**rehype**][rehype] plugin to transform `` and +**[rehype][]** plugin to render `` and `
` with [MathJax][]. -## Install +## Contents + +* [What is this?](#what-is-this) +* [When should I use this?](#when-should-i-use-this) +* [Install](#install) +* [Use](#use) +* [API](#api) + * [`unified().use(rehypeMathjaxSvg[, options])`](#unifieduserehypemathjaxsvg-options) +* [CSS](#css) +* [Syntax tree](#syntax-tree) +* [Types](#types) +* [Security](#security) +* [Related](#related) +* [Contribute](#contribute) +* [License](#license) + +## What is this? + +This package is a [unified][] ([rehype][]) plugin to render math. +You can combine it with [`remark-math`][remark-math] for math in markdown or add +`math-inline` and `math-display` classes in HTML. + +**unified** is a project that transforms content with abstract syntax trees +(ASTs). +**rehype** adds support for HTML to unified. +**hast** is the HTML AST that rehype uses. +This is a rehype plugin that transforms hast. + +## When should I use this? + +This project is useful as it renders math with MathJax at compile time, which +means that there is no client side JavaScript needed. -This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c): -Node 12+ is needed to use it and it must be `import`ed instead of `require`d. +A different plugin, [`rehype-katex`][rehype-katex], is similar but uses +[KaTeX][] instead. -[npm][]: +## Install + +This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c). +In Node.js (version 12.20+, 14.14+, or 16.0+), install with [npm][]: ```sh npm install rehype-mathjax ``` +In Deno with [Skypack][]: + +```js +import rehypeMathjax from 'https://cdn.skypack.dev/rehype-mathjax@4?dts' +``` + +In browsers with [Skypack][]: + +```html + +``` + ## Use -Say we have the following file, `example.html`: +Say we have the following file `example.html`: ```html

@@ -37,45 +85,41 @@ Say we have the following file, `example.html`:

``` -And our module, `example.js`, looks as follows: +And our module `example.js` looks as follows: ```js -import {readSync} from 'to-vfile' +import {read} from 'to-vfile' import {unified} from 'unified' import rehypeParse from 'rehype-parse' -import rehypeMathJax from 'rehype-mathjax' +import rehypeMathjax from 'rehype-mathjax' import rehypeStringify from 'rehype-stringify' -const file = readSync('example.html') +main() -unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeMathJax) - .use(rehypeStringify) - .process(file) - .then((file) => { - console.log(String(file)) - }) +async function main() { + const file = await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeMathjax) + .use(rehypeStringify) + .process(await read('example.html')) + + console.log(String(file)) +} ``` -Now, running `node example` yields: +Now running `node example.js` yields: ```html

- Lift() can be determined by Lift Coefficient - () like the following equation. + Lift() can be determined by Lift Coefficient + () like the following equation.

-
+
``` @@ -83,36 +127,36 @@ mjx-container[jax="SVG"] > svg { ## API This package exports no identifiers. -The default export is `rehypeMathJaxSvg`. +The default export is `rehypeMathjaxSvg`. -### `unified().use(rehypeMathJaxSvg[, options])` +### `unified().use(rehypeMathjaxSvg[, options])` Transform `` and `
` with [MathJax][]. This package includes three plugins, split out because MathJax targets have a -big memory and size footprint: SVG, CHTML, and browser: +big memory and bundle size footprint: SVG, CHTML, and browser: ###### SVG -Render math as [SVG][mathjax-svg] (`import rehypeMathJaxSvg from -'rehype-mathjax/svg.js'`, default). +Render math as [SVG][mathjax-svg] +(`import rehypeMathjaxSvg from 'rehype-mathjax/svg.js'`, default). About 566kb minzipped. ###### CHTML -Render math as [CHTML][mathjax-chtml] (`import rehypeMathJaxChtml from -'rehype-mathjax/chtml.js'`). +Render math as [CHTML][mathjax-chtml] +(`import rehypeMathjaxChtml from 'rehype-mathjax/chtml.js'`). About 154kb minzipped. Needs a `fontURL` to be passed. ###### Browser -Tiny wrapper to render MathJax client-side (`import rehypeMathJaxBrowser from -'rehype-mathjax/browser.js'`). +Tiny wrapper that expects MathJax to do work client side +(`import rehypeMathjaxBrowser from 'rehype-mathjax/browser.js'`). About 1kb minzipped. -Uses `options.displayMath` (default: `['\\[', '\\]']`) for display, and +Uses `options.displayMath` (default: `['\\[', '\\]']`) for display math and `options.inlineMath` (default: `['\\(', '\\)']`) for inline math. You need to load MathJax on the client yourself and start it with corresponding @@ -135,19 +179,96 @@ The browser plugin uses the first delimiter pair in `tex.displayMath` and These options are passed to the [CommonHTML output processor][mathjax-chtml-options]. Passing `fontURL` is required! +For example: + +```js + // … + .use(rehypeMathjaxChtml, { + chtml: { + fontURL: 'https://cdn.jsdelivr.net/npm/mathjax@3/components/output/chtml/fonts/woff-v2' + } + }) + // … +``` #### `options.svg` These options are passed to the [SVG output processor][mathjax-svg-options]. +## CSS + +The HTML produced by MathJax does not require any extra CSS to render correctly. + +## Syntax tree + +This plugin transforms elements with a class name of either `math-inline` and/or +`math-display`. + +## Types + +This package is fully typed with [TypeScript][]. +An extra `Options` type is exported, which models the accepted options. + +## Compatibility + +Projects maintained by the unified collective are compatible with all maintained +versions of Node.js. +As of now, that is Node.js 12.20+, 14.14+, and 16.0+. +Our projects sometimes work with older versions, but this is not guaranteed. + +This plugin works with unified version 6+ and rehype version 4+. + ## Security -Use of `rehype-mathjax` renders user content with [MathJax][], so any -vulnerability in MathJax can open you to a [cross-site scripting (XSS)][xss] +Using `rehype-mathjax` should be safe assuming that you trust MathJax. +Any vulnerability in it could open you to a [cross-site scripting (XSS)][xss] attack. - Always be wary of user input and use [`rehype-sanitize`][rehype-sanitize]. +When you don’t trust user content but do trust MathKax, you can allow the +classes added by `remark-math` while disallowing anything else in the +`rehype-sanitize` schema, and run `rehype-katex` afterwards. +Like so: + +```js +import rehypeSanitize, {defaultSchema} from 'rehype-stringify' + +const mathSanitizeSchema = { + ...defaultSchema, + attributes: { + ...defaultSchema.attributes, + div: [ + ...defaultSchema.attributes.div, + ['className', 'math', 'math-display'] + ], + span: [ + ['className', 'math', 'math-inline'] + ] + } +} + +// … + +unified() + // … + .use(rehypeSanitize, mathSanitizeSchema) + .use(rehypeMathjax) + // … +``` + +## Related + +* [`rehype-katex`][rehype-katex] + — same but with KaTeX +* [`rehype-highlight`](https://github.com/rehypejs/rehype-highlight) + — highlight code blocks +* [`rehype-autolink-headings`](https://github.com/rehypejs/rehype-autolink-headings) + — add links to headings +* [`rehype-sanitize`](https://github.com/rehypejs/rehype-sanitize) + — sanitize HTML +* [`rehype-document`](https://github.com/rehypejs/rehype-document) + — wrap a document around the tree + ## Contribute See [`contributing.md`][contributing] in [`remarkjs/.github`][health] for ways @@ -192,6 +313,8 @@ abide by its terms. [npm]: https://docs.npmjs.com/cli/install +[skypack]: https://www.skypack.dev + [health]: https://github.com/remarkjs/.github [contributing]: https://github.com/remarkjs/.github/blob/HEAD/contributing.md @@ -206,8 +329,12 @@ abide by its terms. [rehype]: https://github.com/rehypejs/rehype +[unified]: https://github.com/unifiedjs/unified + [xss]: https://en.wikipedia.org/wiki/Cross-site_scripting +[typescript]: https://www.typescriptlang.org + [rehype-sanitize]: https://github.com/rehypejs/rehype-sanitize [mathjax]: https://mathjax.org/ @@ -223,3 +350,9 @@ abide by its terms. [mathjax-svg-options]: http://docs.mathjax.org/en/latest/options/output/svg.html [mathjax-chtml-options]: http://docs.mathjax.org/en/latest/options/output/chtml.html + +[katex]: https://github.com/Khan/KaTeX + +[remark-math]: ../remark-math + +[rehype-katex]: ../rehype-katex diff --git a/packages/rehype-mathjax/svg.js b/packages/rehype-mathjax/svg.js index 23fc64a..dd16ab8 100644 --- a/packages/rehype-mathjax/svg.js +++ b/packages/rehype-mathjax/svg.js @@ -1,6 +1,4 @@ /** - * @typedef {import('hast').Root} Root - * @typedef {import('mathjax-full/js/output/svg.js').SVG} SVG_ * @typedef {import('./lib/create-plugin.js').Options} Options */ diff --git a/packages/remark-math/readme.md b/packages/remark-math/readme.md index 6849090..cbf8291 100644 --- a/packages/remark-math/readme.md +++ b/packages/remark-math/readme.md @@ -8,43 +8,87 @@ [![Backers][backers-badge]][collective] [![Chat][chat-badge]][chat] -[**remark**][remark] plugin to parse and stringify math. - -## Important! - -This plugin is affected by the new parser in remark -([`micromark`](https://github.com/micromark/micromark), -see [`remarkjs/remark#536`](https://github.com/remarkjs/remark/pull/536)). -Use version 3 while you’re still on remark 12. -Use version 4 for remark 13+. +**[remark][]** plugin to support math (`$C_L$`). + +## Contents + +* [What is this?](#what-is-this) +* [When should I use this?](#when-should-i-use-this) +* [Install](#install) +* [Use](#use) +* [API](#api) + * [`unified().use(remarkMath[, options])`](#unifieduseremarkmath-options) +* [Syntax](#syntax) +* [HTML](#html) +* [Syntax tree](#syntax-tree) +* [Types](#types) +* [Compatibility](#compatibility) +* [Security](#security) +* [Related](#related) +* [Contribute](#contribute) +* [License](#license) + +## What is this? + +This package is a [unified][] ([remark][]) plugin to add support for math. +You can use this to add support for parsing and serializing this syntax +extension. + +**unified** is a project that transforms content with abstract syntax trees +(ASTs). +**remark** adds support for markdown to unified. +**mdast** is the markdown AST that remark uses. +**micromark** is the markdown parser we use. +This is a remark plugin that adds support for the math syntax and AST to remark. + +## When should I use this? + +This project is useful when you want to support math in markdown. +Extending markdown with a syntax extension makes the markdown less portable. +LaTeX equations are also quite hard. +But this mechanism works well when you want authors, that have some LaTeX +experience, to be able to embed rich diagrams of math in scientific text. ## Install -This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c): -Node 12+ is needed to use it and it must be `import`ed instead of `require`d. - -[npm][]: +This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c). +In Node.js (version 12.20+, 14.14+, or 16.0+), install with [npm][]: ```sh npm install remark-math ``` +In Deno with [Skypack][]: + +```js +import remarkMath from 'https://cdn.skypack.dev/remark-math@5?dts' +``` + +In browsers with [Skypack][]: + +```html + +``` + ## Use -Say we have the following file, `example.md`: +Say we have the following file `example.md`: ```markdown -Lift($L$) can be determined by Lift Coefficient ($C_L$) like the following equation. +Lift($L$) can be determined by Lift Coefficient ($C_L$) like the following +equation. $$ L = \frac{1}{2} \rho v^2 S C_L $$ ``` -And our module, `example.js`, looks as follows: +And our module `example.js` looks as follows: ```js -import {readSync} from 'to-vfile' +import {read} from 'to-vfile' import {unified} from 'unified' import remarkParse from 'remark-parse' import remarkMath from 'remark-math' @@ -52,25 +96,26 @@ import remarkRehype from 'remark-rehype' import rehypeKatex from 'rehype-katex' import rehypeStringify from 'rehype-stringify' -const file = readSync('example.md') - -unified() - .use(remarkParse) - .use(remarkMath) - .use(remarkRehype) - .use(rehypeKatex) - .use(rehypeStringify) - .process(file) - .then((file) => { - console.log(String(file)) - }) +main() + +async function main() { + const file = await unified() + .use(remarkParse) + .use(remarkMath) + .use(remarkRehype) + .use(rehypeKatex) + .use(rehypeStringify) + .process(await read('example.md')) + + console.log(String(file)) +} ``` -Now, running `node example` yields: +Now running `node example.js` yields: ```html -

Lift(LL) can be determined by Lift Coefficient (CLC_L) like the following equation.

-
L=12ρv2SCLL = \frac{1}{2} \rho v^2 S C_L
+

Lift() can be determined by Lift Coefficient () like the following equation.

+
``` ## API @@ -80,16 +125,12 @@ The default export is `remarkMath`. ### `unified().use(remarkMath[, options])` -Parse and stringify math. - -Get’s useful when combined with [`rehype-katex`][rehype-katex] or -[`rehype-mathjax`][rehype-mathjax]. - -See [`micromark/micromark-extension-math`][extension-math] for more info on what -syntax is supported. +Plugin to support math. ##### `options` +Configuration (optional). + ###### `options.singleDollarTextMath` Whether to support math (text) with a single dollar (`boolean`, default: @@ -97,21 +138,81 @@ Whether to support math (text) with a single dollar (`boolean`, default: Single dollars work in Pandoc and many other places, but often interfere with “normal” dollars in text. -#### Notes +## Syntax + +This plugin applies a micromark extensions to parse the syntax. +See its readme for parse details: + +* [`micromark-extension-math`](https://github.com/micromark/micromark-extension-math#syntax) + +> 👉 **Note**: `$math$` works similar to `` `code` ``. +> That means escapes don’t work inside math but you can use more dollars around +> the math instead: `$$\raisebox{0.25em}{$\frac a b$}$$` -##### Escaping +## HTML -Markdown escapes don’t work in math, similar to code, but you can use more -dollars around the math instead: `$$\raisebox{0.25em}{$\frac -a b$}$$` +This plugin integrates with [`remark-rehype`][remark-rehype]. +When mdast (markdown AST) is turned into hast (the HTML AST) the math nodes +are turned into `` and `
` +elements. + +## Syntax tree + +This plugin applies one mdast utility to build and serialize the AST. +See its readme for the node types supported in the tree: + +* [`mdast-util-math`](https://github.com/syntax-tree/mdast-util-math#syntax-tree) + +## Types + +This package is fully typed with [TypeScript][]. +It exports an extra `Options` type which models the interface of the accepted +options. + +If you’re working with the syntax tree, make sure to import this plugin +somewhere in your types, as that registers the new node types in the tree. + +```js +/** @typedef {import('remark-math')} */ + +import {visit} from 'unist-util-visit' + +/** @type {import('unified').Plugin<[], import('mdast').Root>} */ +export default function myRemarkPlugin() => { + return (tree) => { + visit(tree, (node) => { + // `node` can now be one of the nodes for math. + }) + } +} +``` + +## Compatibility + +Projects maintained by the unified collective are compatible with all maintained +versions of Node.js. +As of now, that is Node.js 12.20+, 14.14+, and 16.0+. +Our projects sometimes work with older versions, but this is not guaranteed. + +This plugin works with unified version 6+ and remark version 14+. +The previous major (version 4) worked with remark 13. ## Security -Use of `remark-math` itself doesn’t open you up to [cross-site scripting +Use of `remark-math` itself does not open you up to [cross-site scripting (XSS)][xss] attacks. - Always be wary of user input and use [`rehype-sanitize`][rehype-sanitize]. +## Related + +* [`remark-gfm`](https://github.com/remarkjs/remark-gfm) + — support GFM (autolink literals, footnotes, strikethrough, tables, + tasklists) +* [`remark-frontmatter`](https://github.com/remarkjs/remark-frontmatter) + — support frontmatter (YAML, TOML, and more) +* [`remark-directive`](https://github.com/remarkjs/remark-directive) + — support directives + ## Contribute See [`contributing.md`][contributing] in [`remarkjs/.github`][health] for ways @@ -156,6 +257,8 @@ abide by its terms. [npm]: https://docs.npmjs.com/cli/install +[skypack]: https://www.skypack.dev + [health]: https://github.com/remarkjs/.github [contributing]: https://github.com/remarkjs/.github/blob/HEAD/contributing.md @@ -168,14 +271,14 @@ abide by its terms. [author]: https://rokt33r.github.io +[unified]: https://github.com/unifiedjs/unified + [remark]: https://github.com/remarkjs/remark [xss]: https://en.wikipedia.org/wiki/Cross-site_scripting -[rehype-katex]: https://github.com/remarkjs/remark-math/tree/main/packages/rehype-katex - -[rehype-mathjax]: https://github.com/remarkjs/remark-math/tree/main/packages/rehype-mathjax +[typescript]: https://www.typescriptlang.org -[extension-math]: https://github.com/micromark/micromark-extension-math +[remark-rehype]: https://github.com/remarkjs/remark-rehype [rehype-sanitize]: https://github.com/rehypejs/rehype-sanitize diff --git a/readme.md b/readme.md index 0cacf69..55b6995 100644 --- a/readme.md +++ b/readme.md @@ -8,22 +8,58 @@ [![Backers][backers-badge]][collective] [![Chat][chat-badge]][chat] -[**remark**][remark] and [**rehype**][rehype] plugins to support math! +This project is a monorepo that contains several packages for dealing with +math in markdown and HTML. -## Install +## Contents -This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c): -Node 12+ is needed to use it and it must be `import`ed instead of `require`d. +* [What is this?](#what-is-this) +* [When should I use this?](#when-should-i-use-this) +* [Examples](#examples) + * [Example: KaTeX](#example-katex) + * [Example: MathJax](#example-mathjax) +* [Security](#security) +* [Contribute](#contribute) +* [License](#license) -[npm][]: +## What is this? -```sh -npm install remark-math rehype-katex -``` +This repository contains [unified][] ([remark][] and [rehype][]) plugins to add +support for math. +You can use them to add support for parsing and serializing this syntax +extension and to render math with KaTeX or MathJax. + +* [`remark-math`][remark-math] + — remark plugin to support a math syntax in markdown +* [`rehype-katex`][rehype-katex] + — rehype plugin to render math in HTML with [KaTeX][] +* [`rehype-mathjax`][rehype-mathjax] + — rehype plugin to render math in HTML with [MathJax][] + +You typically use `remark-math` combined with either `rehype-katex` or +`rehype-mathjax`. + +**unified** is a project that transforms content with abstract syntax trees +(ASTs). +**remark** adds support for markdown and **rehype** adds support for HTML to +unified. + +## When should I use this? + +This project is useful when you want to support LaTeX math. +This mechanism works well when you want authors, that have some LaTeX +experience, to be able to embed rich diagrams of math to scientific +documentation. +The syntax of math in markdown does not work everywhere so it makes markdown +less portable. +This project is also useful as it renders math with KaTeX or MathJax at compile +time, which means that there is no client side JavaScript needed. -## Use +## Examples -Say we have the following file, `example.md`: +### Example: KaTeX + +Say we have the following file `example.md`: ```markdown Lift($L$) can be determined by Lift Coefficient ($C_L$) like the following @@ -34,10 +70,10 @@ L = \frac{1}{2} \rho v^2 S C_L $$ ``` -And our module, `example.js`, looks as follows: +And our module `example.js` looks as follows: ```js -import {readSync} from 'to-vfile' +import {read} from 'to-vfile' import {unified} from 'unified' import remarkParse from 'remark-parse' import remarkMath from 'remark-math' @@ -45,105 +81,77 @@ import remarkRehype from 'remark-rehype' import rehypeKatex from 'rehype-katex' import rehypeStringify from 'rehype-stringify' -const file = readSync('example.md') - -unified() - .use(remarkParse) - .use(remarkMath) - .use(remarkRehype) - .use(rehypeKatex) - .use(rehypeStringify) - .process(file) - .then((file) => { - console.log(String(file)) - }) -``` +main() -Now, running `node example` yields: +async function main() { + const file = await unified() + .use(remarkParse) + .use(remarkMath) + .use(remarkRehype) + .use(rehypeKatex) + .use(rehypeStringify) + .process(await read('example.md')) -```html -

Lift(LL) can be determined by Lift Coefficient (CLC_L) like the following equation.

-
L=12ρv2SCLL = \frac{1}{2} \rho v^2 S C_L
+ console.log(String(file)) +} ``` -Wow, that’s a lot! -But in a browser, that looks something like this: +Now running `node example.js` yields: -![][screenshot] +```html +

Lift() can be determined by Lift Coefficient () like the following equation.

+
+``` -> Note: you should also use `katex.css` somewhere on the page to style math +> 👉 **Note**: KaTeX requires CSS to render correctly. +> Use `katex.css` somewhere on the page where the math is shown to style it > properly: > > ```html > > ``` -## Packages - -This repo houses three packages: - -* [`remark-math`][remark-math] - — Parses `$` as `inlineMath` and `$$` as `math` nodes -* [`rehype-katex`][rehype-katex] - — Transforms math nodes with [KaTeX][] -* [`rehype-mathjax`][rehype-mathjax] - — Transforms math nodes with [MathJax][] - -See their readmes for more information. - -## Security - -Use of `rehype-katex` or `rehype-mathjax` renders user content with [KaTeX][], -so any vulnerability in KaTeX can open you to a -[cross-site scripting (XSS)][xss] attack. - -Always be wary of user input and use [`rehype-sanitize`][rehype-sanitize]. - -If you are using [`rehype-sanitize`][rehype-sanitize] and trust [KaTeX][], you -can allow the classes added by `remark-math` by extending the default schema -like so: - -```js -const mathSanitizeSchema = { - ...defaultSchema, - attributes: { - ...defaultSchema.attributes, - div: [ - ...defaultSchema.attributes.div, - ['className', 'math', 'math-display'] - ], - span: [ - ['className', 'math', 'math-inline'] - ] - } -} +### Example: MathJax + +Supporting either MathJax or KaTeX is very similar. +Take the above KaTeX example and change: + +```diff +@@ -3,7 +3,7 @@ import {unified} from 'unified' + import remarkParse from 'remark-parse' + import remarkMath from 'remark-math' + import remarkRehype from 'remark-rehype' +-import rehypeKatex from 'rehype-katex' ++import rehypeMathjax from 'rehype-mathjax' + import rehypeStringify from 'rehype-stringify' + + main() +@@ -13,7 +13,7 @@ async function main() { + .use(remarkParse) + .use(remarkMath) + .use(remarkRehype) +- .use(rehypeKatex) ++ .use(rehypeMathjax) + .use(rehypeStringify) + .process(await read('example.md')) ``` -And applying the `rehype-katex` plugin *after* the -[`rehype-sanitize`][rehype-sanitize] plugin like so: +Now running `node example.js` yields: -```js -[ - rehypeRaw, - // … - [rehypeSanitize, mathSanitizeSchema], - rehypeKatex - // … -] +```html +

Lift() can be determined by Lift Coefficient () like the following +equation.

+
+ ``` -## Related +## Security -* [`remark-breaks`](https://github.com/remarkjs/remark-breaks) - – Support hard breaks without needing spaces (like on issues) -* [`remark-gfm`](https://github.com/remarkjs/remark-gfm) - — GFM (autolink literals, strikethrough, tables, tasklists) -* [`remark-github`](https://github.com/remarkjs/remark-github) - — Autolink references like in GitHub issues, PRs, and comments -* [`remark-footnotes`](https://github.com/remarkjs/remark-footnotes) - — Footnotes -* [`remark-frontmatter`](https://github.com/remarkjs/remark-frontmatter) - — Frontmatter (YAML, TOML, and more) +Using `rehype-katex` or `rehype-mathjax` should be safe assuming that you trust +KaTeX and MathJax. +Any vulnerability in them could open you to a [cross-site scripting (XSS)][xss] +attack. +See their readmes for more info. ## Contribute @@ -157,7 +165,7 @@ abide by its terms. ## License -[MIT][license] © [Junyoung Choi][author] +[MIT][license] © [Junyoung Choi][author] and TANIGUCHI Masaya @@ -187,8 +195,6 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions -[npm]: https://docs.npmjs.com/cli/install - [health]: https://github.com/remarkjs/.github [contributing]: https://github.com/remarkjs/.github/blob/HEAD/contributing.md @@ -201,12 +207,12 @@ abide by its terms. [author]: https://rokt33r.github.io +[unified]: https://github.com/unifiedjs/unified + [remark]: https://github.com/remarkjs/remark [rehype]: https://github.com/rehypejs/rehype -[rehype-sanitize]: https://github.com/rehypejs/rehype-sanitize - [katex]: https://github.com/Khan/KaTeX [mathjax]: https://mathjax.org/ @@ -218,5 +224,3 @@ abide by its terms. [rehype-katex]: ./packages/rehype-katex [rehype-mathjax]: ./packages/rehype-mathjax - -[screenshot]: screenshot.png From fa4ada159424bedb2509a55c4a3889df0ab57828 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Thu, 11 Nov 2021 15:06:52 +0100 Subject: [PATCH 52/98] rehype-katex: update `katex` --- packages/rehype-katex/package.json | 2 +- packages/rehype-katex/readme.md | 7 ++++--- packages/remark-html-katex/package.json | 2 +- readme.md | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index 5d8a01c..b7d8031 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -41,7 +41,7 @@ "@types/hast": "^2.0.0", "@types/katex": "^0.11.0", "hast-util-to-text": "^3.1.0", - "katex": "^0.13.0", + "katex": "^0.15.0", "rehype-parse": "^8.0.0", "unified": "^10.0.0", "unist-util-remove-position": "^4.0.0", diff --git a/packages/rehype-katex/readme.md b/packages/rehype-katex/readme.md index d963f72..e68eccc 100644 --- a/packages/rehype-katex/readme.md +++ b/packages/rehype-katex/readme.md @@ -22,6 +22,7 @@ * [CSS](#css) * [Syntax tree](#syntax-tree) * [Types](#types) +* [Compatibility](#compatibility) * [Security](#security) * [Related](#related) * [Contribute](#contribute) @@ -102,7 +103,7 @@ async function main() { .use(rehypeParse, {fragment: true}) .use(rehypeKatex) .use(rehypeDocument, { - css: 'https://cdn.jsdelivr.net/npm/katex@0.13.13/dist/katex.min.css' + css: 'https://cdn.jsdelivr.net/npm/katex@0.15.0/dist/katex.min.css' }) .use(rehypeStringify) .process(await read('example.html')) @@ -120,7 +121,7 @@ Now running `node example.js` yields: example - +

@@ -160,7 +161,7 @@ style it properly. At the time of writing, the last version is: ```html - + ``` ## Syntax tree diff --git a/packages/remark-html-katex/package.json b/packages/remark-html-katex/package.json index cc774ab..e11e21e 100644 --- a/packages/remark-html-katex/package.json +++ b/packages/remark-html-katex/package.json @@ -26,7 +26,7 @@ "dependencies": { "@types/katex": "^0.11.0", "@types/mdast": "^3.0.0", - "katex": "^0.13.0", + "katex": "^0.15.0", "rehype-parse": "^8.0.0", "unified": "^10.0.0", "unist-util-remove-position": "^4.0.0", diff --git a/readme.md b/readme.md index 55b6995..dc3e195 100644 --- a/readme.md +++ b/readme.md @@ -108,7 +108,7 @@ Now running `node example.js` yields: > properly: > > ```html -> +> > ``` ### Example: MathJax From f9c559e872b01aeaf5a7d8c80ec4a2a0fb91f256 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Thu, 11 Nov 2021 15:07:11 +0100 Subject: [PATCH 53/98] rehype-mathjax: update `jsdom` --- packages/rehype-mathjax/package.json | 6 +++--- packages/rehype-mathjax/readme.md | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index 15e0a54..bc87b71 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -49,11 +49,11 @@ }, "dependencies": { "@types/hast": "^2.0.0", - "@types/mathjax": "^0.0.36", - "@types/web": "^0.0.15", + "@types/mathjax": "^0.0.37", + "@types/web": "^0.0.46", "hast-util-from-dom": "^4.0.0", "hast-util-to-text": "^3.1.0", - "jsdom": "^17.0.0", + "jsdom": "^18.0.0", "mathjax-full": "^3.0.0", "unified": "^10.0.0", "unist-util-visit": "^4.0.0" diff --git a/packages/rehype-mathjax/readme.md b/packages/rehype-mathjax/readme.md index d47424a..2add318 100644 --- a/packages/rehype-mathjax/readme.md +++ b/packages/rehype-mathjax/readme.md @@ -22,6 +22,7 @@ * [CSS](#css) * [Syntax tree](#syntax-tree) * [Types](#types) +* [Compatibility](#compatibility) * [Security](#security) * [Related](#related) * [Contribute](#contribute) From e7e36f07029ffdfcd65ec16d0c5a0c5b85d6e192 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Thu, 11 Nov 2021 15:10:14 +0100 Subject: [PATCH 54/98] Remove unused file --- screenshot.png | Bin 238342 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 screenshot.png diff --git a/screenshot.png b/screenshot.png deleted file mode 100644 index 9bc94bbdaf9bc0d1703259a36dd1ab7927414a08..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238342 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

(Y-QlM^7BY>?ANQizTR<*VoERtjvGS z(n1irMUrJeRnoFLwCH@O{<60~bzKuRF{S^~@^hON-q7Cbg*MZ-lB5fT{i+1+();I< z;M6u62==jJTS znH^4>u|tZ8+-4bi7hRw@UB~>rr=K;2;eS)J~^63c09oQm3JZ*Mm4BG}$WGm>Io2e3a2 z8TXgF+{lzVxVdVtY>w5%P!NVt8auO{re!%;R=t(wylQui>o@AP8^`lE`uB1d-7Cnt_T~lri zLUy1BjG?z^`-1tJc${LYP3(%4$%p^9w|b?A6A(6@ve*JRl#wXTY+A7~9oBum_m*lB zL8Q)D-8=O2qV}K{gxI}0`l>xIMkTt%0B8dvGRwX5W}%X@(W+FF`tfRNag|OyGlkC_ zzI{t<_%&Y;7B@Ov(5PiEXE(lQN_;}8fvre#%(Y=wn-zKN)xL!}oP_kE8N-HV0(yYv3v$1f-F3@(Hji?#vT5g(Wt zTjal~jyGakXr1Bn(9#0?*ObRx*`l>+3O8-`lWpVjVh||5Et&*WQXlQ31{%X7Dl84z zKD1gDw15WZx#|FmIhtHPsgGblqJQ`%5!&;Z=GT}_f00CLHTtns;)U^ECDO?ILvYg&IUDc!2{S1hqgbMwX)5hUezn^tgbjZ5$=%wB`3Um4Fa@5}tKQLij?6auwvsyP7#nipXFbHImR3V5U)R+qM14J(q7yXtK9+YUXI>Voy zgF}qGg_R-IHc^ap_fJSCE$^Kx&Dka@Jfn*&xfT9xUzU#mn#rA%MRmE#tVbd!Wj?WkG6Eq`(lr4#udE89l8_;bJs_1a-n zHXnqoqhdg*^lHy+H1hF#q>1F!F?>{#6oBSiRt1_dQ(z>L=kca^zenv*vMUJX@w$}# zcNa_ET5bFmj>wAI51qks3}P<7-d*_zMGsWEA{TC~<%m3tE0e<><<~L^sL+uuo+)<6 zJ#x;_y4&}t1WIeyZ(^1bL0R9QFlu!p)6XkF>1}E@-X1`u3?ZR`Hcb^mvuM#^tlqO{ zACGw5p1kn&9&X`ArU;Dq4v3j`TbJ6U>(6nIzYMknHyS9KXsi|cYLIEy1s7<(aF3zx z`F04oxW`+*LMVut^2$HORQ?u*3TgtqAEMlj@l;f=y9zb*4pOj~lg!Z93a8)(A?6?%fr`l#cyI#hF-JFK?{zGw2E4ql`j2)%;3^`0Pl zks(v9qqBH;5cd4?g@Eoj1&p@`v$)7tQ>3w>`$gN5YnL)Vy|_@$R!jyY<1vAP4PCq$&O9h`72jx1C7E?$YGU~NL?(=$9H^wG32QJr z=$l0wFXls1*Wj6)S)_UXs?Xgt=^;Ma)Jmoqm%a7ui0!gM^gZ9FPnIXjX_(!Cd) z0)?rWk9MW%YmEPe@m@b;y)I$`cM4^{5;t4{V?S2b?JhN?yO*n?4@`-?@tT#X{(VP| z^ugF|;@_P8hB9&6VJp9Y=*+JV8My7MQ>E}tMfnAu2CL1^!tFO&{h zCr~!$wD*ROo~Z()W-v`$Hd?re<)H`DFvZq3?MaCd3R-Eaw<#ASgw@JYyTwVuL8&mf z9RJ->0yR<9oi`f<%Rh-uZN;I8xR0nKUelni!+|PUc}ewuDLyDjiZw5cAw(u9Of}bd z*(b;7z3)jbBChV(%Jo*yjs|~?uk3MiP-8AD$ zL@-mrV*&@p6hl2x%fnUllx<5bhhIryX8$K2{vUUrcmfZCje-Y*tAGS~`AzM4mu`}h zhKWqhtIfyLUy?Jg*!nHX>_f#GF2 zR|%OiKh@CN+1oE8Gk#wfwc|BsV${_}`E^I;!Q#OGIcE7EKD>nom~4U77;}y4qbj1M zO4hQ5ZO%|l%(;KbgkKX5E&}@%&&)uuCa-0KK(7NGWRSX1A8;=uB}H` zV>s>tP7CT*a?(@*cn+dcDUW{-tD6y8oe@1juQudfPj)bIg6TnXzvWGNd`(&okK z(AC|byMHG~+aq+y^m?e_$T+eod1_&=9b7l<--@*w1RLuVsm7BI0;OQ7*;iND&dgZM zz)7q|xXmUt))FN=Vty;%%)R_H8gn&@S)2s{c!N_)-if%rIG^;r6DPb<3&PGoI-q^$ zk`c=zF!kD-6s~QxOO078sY-9e)7YE%-d%^&$MFOpPJ8hqskSRsoRgaUspiN<9_cbrNHr>LEr;ycjy z;Q22{NkO7+!-J0a<{O9R*KmEYMa#m`uSw;vu8TP(Nj+yim<(lE#Ba$LY#r#br#&hW zx&K z25F8=HjPU7eh8xm1f=J?r}bm=wz?NjgUMPq`Bp$HydgD%PwfRGu94IUXv`HC3PstB zf4^-3!&VI|XB*GzAR`$wfPaMMndQF4xW~lgWH8fDgoD;RFh zd__C<#im5n!+o%9O4Qx!txk7cm$31+bN$RoSR*09g1b)w0_5kIY^ag{{BY~qiLY|Q z&dI9tENC)#K#x8OF(g;%6USu{3^1p?62lxcz5SW;Ni$Wamnl`T2-9zZf@gjZUkoZp zMBkrpS2$k6U-E?(XBvknDlqaB?>m2JqEoF4Cf;aez0(VYu&+KtF6!FMlb)FuPR;;T z?e!+bopz{q(DRF!opvAyM1Lm}au2&y?{)O?=$*;6O}WmQ%h*ZgMu@38Hy7S}uOa#0 z=azr$wDmnc)8)!E%H=mir?##C9U@b$)W(^&W~??Df-b@ zv35M8ty>&~C`t@)f6~Y~y^(M|_ZmILZu~_Q6X;plDM_2uMpviXCkDSy92FlNPUb(lf`>fJ>_!ZG0 zLuT!$MY@l&iy@16H`S>hw<{_)K%pn3wM3t0FFlSAV)gZy8F~n^Kw1qU;&A<4zTkK0 zm1I0@W9EZSm~wJEkWxchmXgn@>%LEX1@nB=-Dz4TlybI6apg>fO>hEEl_*YTm-g&E z4s?^#E()gUO{n3cZA>C&mj<8t@8b~3l7D3ApDOu{$;zTL} z6@;R~z*6adOsfDX@8lP}nqj}P7vjj}JY*)&@ai+^V#jZV?7=`1G6 z65siis$jFrxhkn&B&)SdjC0NkSRJ&DQ}yQ5#9)6{7^&_07fHff=fPuXj7(PnAn3GlnkU%nbKcgSBH&Id^U$sjhMon>ipyHbQ&Z)Bdo$za zI$_3DRY(lQ<4tx_zB(Nz3>x#wudI}z7>&AHW7MKkAqcI;K`{u^$1ilL5(4l9Hyj1f zw551GI>Xj9YC;YdUl2KyzlR@Db~X@HuGl#_Z?#XNj(rrRL4T(q8F%Qa40GnyLYzr; zfIGkP9ID;qCka#u6J-j(q;hgl7@jBbeL`t@#m4VRNj{$kV+JIXl~wKE3Ta?iI0x!Q zOlWin(K(8AR(Q%_L8Mwu>fhR}9cAj`-tlrbZ7tbF+d)H~mFz*6GyKP)kX{=6i{807 zgi&sGH?Lp9Wp#}ear&Z-=^VsqG=K1D zZLyF0ewm^*Mv2V^x%S>WY<@JNPF@jsL8O0bCuv{#uvEEbX6)}zaa%SR6k%3cz6R{H z`+A?AHhg0v1@6rbWK-DRKW{t|pZNij*$C!WRKxL}PRFQWGWKa;iSb^Lp^tJ<<^x%r z3^DG7k$-YPiN=+MSzq^4H>qda4-%hQS?(y@vs#)J2v>$(i}iJNQ<<19w5L{2vN|;E z+{EF=KJ$GHFn`~ICPNjzRdOBot?=J~fb~2;@8q4$mFwSU*dBu64ub46%pRIHSjTGd z^k~_spir#~iwf#pvLGNue71SQp0zeITHu^%#H@*jQZhTQMa(nnDq0$AMbWWivNue` zi14QB2aWYLF6O7a-J#h!9r5w1+kO-6ngw;}8Y89zr5%#qZn)3Dpv=Ur9%P_%ewB%n zmO!r%S%C`B#tZwf0&(BQu1VbO?(2+|5R*WGvp&oW*3*}85*-$|Whv^8@fyKD%-YA} zHPlQ#)0#BNj!(u~8{~6b0-|l^f69?5IRfZ$FOlE(=H!~wAhlC-lA}KV$zuH%^f&OK zGAd1pZ3GyxVRhPxK`A$~m>Z=ilYiJZ7N&;RVO_e-z2LxH)E_i#=Z`iQhC6ukgT~-! z_vZpB+=@yBM`h=TE_6X#Sc3(^tZbAAtc|20XqBU+w@q!ILk>(e*WVOAjx+a^Nru5q z_~k6hWSbAaeDC)3yta;n=-$(J9|7E>$^Z@e_5DdPPvIVG3`oL8*W`N=&vNBbWfJ1j zMzYWFNQhrZ!YSxdEwIc4m2n8w)OQuyw{QQ>42OrVYx!uur1n?nis0)w^-vXq^=W7TZb>UEgL z25u56Xw8#Pqio4b2}Gx|M_U_E4`C((6aHfNrIGZF;;D!AX7R4_*n}R{+|neh)VKMTgL@f+DGx8C{{7>8F1Y~ZMxhOZ(Ym}1 zqKPPg^&{qW1U0w#ZsJv1U;YDrg=j%1t`Qt5$l=WnVt&c`-9dIquXOW;!ilPPmvRj? zX2I030P;_**D_8!^3b{X$-(x*EkMs)X=2X((5Mir*JciG)8qCuP9$(WzenbqTzURa z4w)!(DTcN|7aq5BJ7Ghrft^J@G;K|E9$Z$Z34Jj$gGr!?2r+wyl}xj*>7{n1vO(-? zIu~)4#8$@O!ocrU+>Ag38U$fK!53(_xq7Kua#;^Z!czQokz^$T_RQ>+FiXIu2^#Or z;>WCwgTPrO&>{!UTxIXz7f7-Sl=jjBg+p2F_xOsUU2($t`J0{IIQ}B(43wxubd123 zq1bA=qDj(*oW+aD)_i`ot`G^h3^uk_J6_J~h72?DI`*#&Xpv3r77c<0%gjkQ7zWdQ zoLfb>OLvfc|0Z!9c8XaO9v^o*WP8(Yh>CKZ>O>U&CfZ}6!iQ>*AkNC5;M9a)Ei`VS5L6r?B&t90{H>AnYQZVG$u zS^AhUX8lbPM=FyNf_FzRNg=f^*4w4382J}DK{i2emXh?_Clk_A40HlI>D=?u3BTy5 zS5_Z5u-^)?$+}#fuRd*-D-bjnI|?mv z;QR%RfbR#m@TuUM7(806#wwX-Q10j*sRyF{e50|WoqyUIl5a}8SHj>}6It;H$>-Dl z(j462=)H7_B+L&9m_$>EoY}xeOcf++oM%5AahIOZL;M`)pMIhX9zL4JAjhN$Z?=BL zA4)i>^-l)tJ?YTyl5(bh+flDpk)D>xZRo%?A2=_XO6jN zaesBTVkQ?_Bjx=5$s>^u>wP<%1u#de>>u=-u&tOlTEw8k?5iuGAWCE=R-a0yyevPx zbF%!mhqM|_oVF~61X)>yU+;JZ^_;da=&lR7Eg0_3dgii9mY2n`mfE1ic@OkeS#gt8 zq^V>wk`oJN^>%chOmR*D(nvYOJ(ALgtSv>(*iD?nfklqW#cViiRHW)?pZ6O)P+4Mw zQLn2d(!g~8&>Xu_3*nl}ytC?Fkk}0{+~EglL{;C_%xL_f3fjaN^YNY)?G~geg%JBB zOb(r;iYTqxi_8hB288!U`=4a2$2pt@%xh3`M9Sbuz$&?LTV|Sg-(u3B-J7XSX;Ayy zUoV8v*fw|JYD5;Kx5%gFppEhN9n?m8Wjt)wPHKThu?28ED)oWOxQVm}sEI~~+lo2Y zccz+3n{fL!N~N<9`n!qQQER2*+Aqj{? zv@BW*m>L|jaSry7|G0Q0(6ym~;i1oDi?pctZJhhH@8u?a1PoyfzYD^)$9u{_Dq(RJ z81GqSP12Y%+>HsQQm!Bx1cSwU0U)Vb;`?P{&v}$wjlhVysq%Xh5#6?mG@-R7xlmfu zo>j-JAnLq{!qD>)0q>G1YHtD_5k%BSJSy=mK(Xj#J6Vr4|BjEk?<56`{oANX1cD>< zPWqfh%aX0cT^dib$XGM79Y`g%PuNwE|MA33J2|!yzUyS9GQY7Lf6LLXySEGb%{^Xv z{z}o0mL>|n#CUAhr)zqVlitV+Ocf)<<&->F=}@6UJ!T^?DS2bM5+|z#L8T&$)mIs+ zFJYqT8Td#B9}L-HAtnfv7lOq}(2`2zDoz{6p7pB`hicE~JmB>xyd(@-2dIq$>*q@iD2dzg!@mwTOsKjG2@} ziST6TnSgN11GBGeV#%~dx!|nvPS=6F`%am)L_v>^4j9@S6xHWTV3`0EYHCk26+SHC zmlfL&a{?grZhE3@kqYC91XLy%?7{XFPD#P(Gin}`%hYZ_NdsOg&dpgu<9hqu|6S&s zqpa4_K5DnmFE@wTN!zRb;(B$z1&jGH&rFl1J{S;0{%dxWcMX>(l|Zl4MX*UDW$ZI) z-w%TdCu?3AP&A<>zJk=~uvwRg(C8Mm5axqqJ$f6Jlqat3TyN?r$eM_$M6Q76G%cW) zbUc69KKOX!QTN_kF?qRaz4TjZ8?`Z>SIDAfI)Q7k zd_!VrZ`AU@`-%68vyi1+-zR3v(rbXaQ(Ni_9k;zYulz zAIK!{MNaN-ku40Ye|6#4`JBkdDHcx!`b_|u`4CDnmzkgb6+H|`n?#{Nmhj%JH6p1v zo^Mq;d-u`eFsUp^Em|5I($?2A`mX@=_&q18yyEXFRlgtSOYmvH3>pMk8zF8IdFYBk zS+t{!h?+W!tS#ZqoQ8bfkRKCU_m9=mJ(1IZ0(1OLos2ZRK{5-hai|eL=;<$MC&{9D zylQY=%(Xaa&cphcG$CK`ygMGLwzFhdR(Y@Ph#AxgG?7K-Ufikd!hQ=Z)%s_&D~hjI znGYdTSqwW{C{T3g1hM4NLur66$-Qs`PJgke{3YSr(u3pfgU|kk-!S&Zd`Z5lh&<~P zFKk4luyAEwamGQRpe(SBgw~zK6j$h9xv=#mg(r%#nl4c>Q z%GAsEr$#%t2(mzV6paZM^t*&DaUb44(U{aRW`3kj{kSA%=aL7OxA8_6n^cYxg*wc( z*``|N%MBw5eD1g6Vd%?os#xEofm=9f<5|YplXcN5MX8Z9qmK1&J$pyEh1)XKnXmsb zw$8sC^J=Dd`#RvG3Nll!vdx%}ZrT2^D0nU`-7z$_gU0bCn4+bg>17)f;O`(AQ9k+z^5RcpR18sf)t=VL27mf-`46 z?bxdIB$pE2NVta{%-Tb_KN{~9+nXnnPL(^a@Rj76T8;637@e=zow=M5${t1<(B_~L zO&wQ-&Eu-39b4BbW!_X+2x4e3eVdOwlwyIjHffM%mv97VqHS`r*r)nh1m;$}>IzfL zV~J}GBo@-p*)MM^j{NT=Dc~a0OW+*u-~9oR=&lcw-?zX2{)wQ+z@!~T!&RH_4x%k#!K@cmP%Xh za%A$63fj)pe_I;encPDfZ#C$y$8Yf(&P9XWcqnF-NvF*k>w@OuFDyyGjDx+0Zhc~V zzc~U)xat!Mw7;kjYC?*(4F~;<#oo^toTQgWvCBUI0~GJv1DM&b`IX*#dk@ShA-8&d z){i@X+RCs&G=BK~Jp!+AEBF6lBq(^|ALidTFT{HGw`Z+8KV+DnR9lDxz<`yq&wYXOHwVl2`U^$i5W&kvb?*6;(Mr~zq0kPl}62euisz<^Z) zho;34)&R_MKDvR-3eoF=*3xZM`oJrr57MNg<5RZ)qDZHh7DYSjV$H!i*XJs`bYF~z zGV#lK6iQ%DTFQIDjpxH(p}abS6}DpgrVe=U=-fUlK3bmCPE8NUEg+d$a7E9{U<0t% z^*FhBjGE5u5`5}2!)0M@20Uu9aWj$*T9|kwHg|8%r1=@Hw$P)!eTUEnVOEYe<&u3gXeC{8@8?+ zDj_+;$d+!8!ri?#p&#dEeuZ{{s+}WN>ofkWU~8{W^v$$3=J{WbQWWpL1;*fOvBT|S z(-vUyAK;9@Vj558NdaYFf0SGACBGM=R@rxFlg{?;m-xV<##=jo8#B+|v@L(Ec>y`G zNt;*LIvZT&te^Vp!smWpvFfJ?#)0Q%7>|g>`Qb>vAwsj(mx*he_P3-A8<^KD2D`3H zlJ`of20#w3WLlKD-+ZHLa$+E^X6|h%g?t_wom|z#jQ$I}PwrJ-K`Z52kZ4w&$OlSC zfxoZFhvrQ@++(#hrWa-<72r3e#Xa!`<17BlJ*!a z=nrJ?j0JGU<}(U+=I8&|gER)HZqIMXS&C5YU{J>L#{f>X;6Xu zpPm|1-re8a-KmULmKb+-zL4`assTS1wveyeI1?wO2u!crnD2h1kP-2Hy&wY?NylZ} zYgu;6uxn+HwoB>8!{7yzzX`wdcNVz7@TDfz&^7@@B-0F8H()cs-1EPlJj?;L+G@}D zyXQA{&JBNWZ&ov+4R_L?quiKk$ItS=d|}~_&=YM5?tS49rB;#Y^Xc{V9}~|Me_UR< z-M>0sUbwzOW8UlAxrN-VFBK9L@b#jTkcpSwiV^vkm6Q6H{B92IRvmjstQmQXjCIKi^EU?;dB6O)`voC&-YFjBiUuxvR|HU@a$A37QTa~8m# z_y2lu3B*6&YY(vAc&FzVclX!843~3cju7b?U?NG z*2uNPE{wz5!N$Y?Ns56%=ZG&1u1qa~-xG?&Re0|miAgCIy(Dz)9KXZgP=$kKFNBdE zy|J351x9W-TbN2Ic4H1_`E}`-OiOcJ!$`n(oAdRFq!(s>^>|q%+qB#Wd&Cz1_^p51 z)QEz^PC9&pv@v>dd#NT-wIO(I2y^O%-gS@tT5Y zHn}^K1Dwg%uDYjhI~&MgcwfhFM{$2~^NpCrFyqjLt}k=I{^$Uh=0n1?0AX71U2s=$ z>tf@~*`$vu+guOX-?=~IZLmwW8B?5ZQ~7%3_=`#(xx^-{@!(2BVm_J<7?NLeyChOi zWFH)l<^1AuS_`F(!-LltX;GN5j(*)Kqp($97ehy6f2P1ed}yb#A|CTY`S+6>>l>5k zfp6u8mo*!<#Uf|K6aizpIH^({A-q+BWS6*}b_kHq_hbGsWbNAcUwiUiG8b*4!ql>5!XeMo;OsjF1{-rWUl+|hg8fien6*l-*T5H*^_9zQxL@lw zX$P`3*q3_iQx1yQUuTOTm&yZzPo%koEz;aIpZcBgbC6v4`o@AS zI|vz=3{_L?@W5zYE8L#+VyRtAZu!0tLa?Gq0J9vQ06lM*-JKCBv8T9G&w&$V3mteXo#B%@QzCOX<>>xqm07L+>; zKjl9c%7LBoJ@nVQ2$CVvzD3iGoYUnGq!1eX`Xs#a)aehzob?)M&y(Up2rXlr&3CQX zwUKf}NcPvS&3+k_t(u`q=Z%V)_6wVju0Q$xVTz-;yMPT`9N=ytky|?h1M5q;FPRyP ztpAIvxBhFo|Ki3s5Kt)KVap640o%=-0W>rbqm|S=qIPe6Eu#Cs7~W?vG|*3 zen-YIa75{~#r&~;cw$_z^3!+N$XnBwtd~AfRdMUtPVEvL)BfsR0jo4gl#lw}G0N2K ziREI&j;c$o9^b?gr_&-NI>lf6L7^I{sC1bUE3xvNRaU<&G;AbC+3cbg?^*6H9p%r` z4eu+L3+V5RSO-PejU0_UEX?x7cd>a!P^}PX0%id9o+j!|V$%7u0)nA}q$yC;X zT4kh!KW%3(-nk#!Et#?Pr6yKs0^*hE8KyiN$-kPsp;|4H$5xGK2wz3>EATqnd@Udh{pqg#GPM zd)V1U*j4v@eBa;=8bPfMr&^STD6M`a?ON`9(^7LO19gQS(q%aw&;@YTH^X)^f2|*H zgwrfa=^KoyK>KLm&|=77_{^%seuH&m)yC>oVkEq?JGgJ-(rZ7Yx=YHDMgLJwf+(4` zB}v;V%@8j~EET12<9a%OJYw>L8L+qRpC8KIRUp7EsBr^VcR{Wj^T38wDd@?H`f<_r zRF-E$r}_!GbEg@E2#~*biCK?7;lCQ0uMk z#$LmUXx7G|Ol7vyPg(IyO{1zXT}^&#UNP;FjCrn#z3~O}<%|W_xcP@hSJc09C%%?U z-x}5ZnbCDipNEYBI1?sS^7rWbYfFaC^M#AwO*fJ4pP|nY-zdKqVz`(@ueX%rT#N5k zGwJgTL@#d4-W6L%WA-K39GE)W%bN^;QZ~6cUPHo8UO@WZCkni?j&0-Wq+CDFj{V^K zR-pNCl4`c*IT;XG3j0M{bao@Ex&ua5EEvUHXfB1>@*Jk}^ zsAEsWxE2>*7cNh}F865`nBBAa8|7oPWbw{t3>z=Py`8O`$f@4QJ$Lx-^f3=n;!aX} zr112&5!{jM**eupf_N+X#^`%hFz)ottU9wGR?LWlOgzvC0#3kPR@Y4Cyi5|5AG20?k)j*2gBxLUwEBYR8S^XNnvfO3#@{(r{#(fX2RdaJW!&2*sa+qy>22i z=zw;S|NW18=W4jsvna^%vetffl$&E|ay?Ug$)cdc7!){Sx-k|!)2SN2V;S`CkZ?tD zdPm4EW9R13s#f@p*1r4CmZMafHwcUQcSd5u$p1q6>1*uY=p#_Ec1WNd<)^l83z5>z};%7ce)RN`=@&)e-Ev^mp{ zPQXD5usLm$o#r_xg@r~yFM_cdW6HnAU%!3N@JR;u~Cd_c~*qi&fe;zzm9@E4bM%4~>a-m0wGHb~pd)=MF9|%NH!3a`TeO zCm^K0ShP<1g|k)X)hkEJ#nQ%2Fpiho>+{*KKt!N5ihC1oqeKpN68b!>0&N3La%Rp;1D==U*{~UAmCs%l;GmV?FHv$BXa0N+_v<3pS0*0(kaVY?+^GwbZ+e)I2c6g6qT_ig*Ct#c z;828f{@|aJ&F4&vUa0_ETY=23gOT@Eb)r_D{`X}LruE3^=DbAAd;*sy-H{KJyv>Mm zh%UY-*!mgaQV})pL@qEuH~qPSG2*g(iEyu=|9iOL$Tj-{;X_^!xgVE=ufJI{Fk^9$ zN%q6S6tHlZeJOJ-(Wak+{LrrB!DNZi4+jVMHQKQHp;pLlFfJ#VWh=kgf;i>F(G))= zv~5y`Sg$w6zyABV-XxrYyTam2?;+fo?wC_gbf_IIy+~Q|I9W2u3P>Qk3yf9`D7%jD z|A!BTmaqZ@rpaZ}oLja}J87u2CSwZtL|F)Ei{sn`4`M3}az~NYLQe3QqMmkE@%Rm4 z8oh(hUjph+!khasZ2hR=J~J8B)Mw(5N)P#Ivze_F`=wgv3E>LrkFvub){Q{wv2%cA zE1oOU=RobGQ6Pijvk!y*g={nVt;6;Yze3;3@kJ$89cqkQG%Gjzy`^ngp7hFucT!6; zq8g9~K+6g5KNMq(a~f01Pif3JP#OkdUlM+#yh__WesIQoHIwl^?N?pFe;f+6F?@eR zu*>e1Uq_p=ygTj5uN|Q8U)wwIY1S`Jv?Q-kcYQ)XP5I+3Y00^_!z`p)ccT{^ue3+M zy4V)?7#DRFX2@djN<4icR&R@W?vV1Rmhx%m08?J9^@Ioc#@d{Fk6~Z=^r^gvDVO3- z_jPFeIbzC)il(po`Nzty)le+F%mycr!iLiNc?>FRqvfl&P>=>9l6t7LYXW^2N0T?Z zI0qjq!P4|}OGC}n+vD0Gkt+`H8lx{5+V7KcBy_MAfAzKaIHn|@caY8b@M~aM2w8;|&^0x~j zk&Gpg%z<)mAqGcvHuU`TCaj1o`uTq!LB!G36dnWgk3TgS>=jFO`-ozC*11D+RL1=I zEU|DIc>*zTbU$oke_+zgui^lEGHJ=@22Azge%7^r!qu=m1OYr|Z>OgGWtEg>2i zyINu{r2|2Qd@C+$O>IR+Wo$OWTbc>Ec^%=zjiYRnEPeUbu@WNxt(vgWNX@`(sjxg& ze@8wotJLFg>3AyVW%&V*xQT<+6ZSxc#_=<=A?rT5jpQ)hH>v2If+_-b56pwTYL%wI z`CBPR<}7wa*R=}1<}y7#v4)s4FFL-c_||8m0Sf^Q`@cJH``6$4jba+Yi?{cS4n}8qutx)Z^;ff`jw)f8UKR z-^v>`IW8}s+y%q&pR)n*53ntldVC0}U@O}xOVSdHl_D=fVM$e=6_pffjUO2P3 zN_VrbpNR^7E~R>>$_)4(kb)*Gy~+VS#80YK-0OaIz^7xSR!aG86Cv6p#-c9Mex;Dn zGGyq5!>jAl$8k~J3}3HDO}A~Cvd!@xNnV0-KmH)WebxM#pdnAmMJNw``P22ip6dml zyS0qEBPW%0M4vi3#&Sq!YhT}5dE6tb-N~qn*B;RvDab+42|e8&d6lAZUc&QARV~Nb z3KY{izBZ+;!aD*N;^vMp9R0$2VWidh9=6X@&HV5>rbvG={U)oX)oMM4 z@s9HlohihO#Vz!oiZ=K6F{}utpBPN)WlQ-*(MQTon`4;4EN`}QK2dcLGI5GOo)3c2 zW6O>mwhw$DZ08@SJOg9>I>z3y)CG%*664wG zkfBb#?JC-5#+AZ0gfb@;2shqoWGAVcuuvnxZBak({_7e$|1gz{-h5t1tBA#tt`1tt zy?ppfgempaGloF9XPeJ3o+Vaho)QR6uktbj(NL$J^u_amZ@#n*bj8TB*s%@;&pv9O zd)0Z9y(((nI&em=B;{4twYEKFS!ZOE3mf|$RACV1;)`aQ; zd$>L~l~A@8L}TgeEDW6WrE*|vpb$7&50k6>%_O;#!xOes@#*U*hT^KS#|cJm1PzAl z^{c|p)P;>IgpRH)44GzTK4*?z9%SG$FaI9*X0H|)b%zvIEdb$1B%En%0NzZRA$!BC-VX*hO#%0Q{{yMSvgX}gw5!MEzyxo5wB z_#uYMJ^Wqd059RfEjcpOTH6fQUxg_O5+u>s4@~tm{b;Z7CXkV>$Hme^$lWKQb-h76 z9`tp*#8Zx=k4gPM8uy#7&#s0XBy$T~@v?d~^VVw4S0jwXakZ-cf+N4g^|`%<{M;00 z+@CKSKWxLMQJwyWYU_=tlxzaq->Js+-I&#^#;HY#s!;3PDc?~&g5HE84QOHE6UAlI z9!Cy?9jtO!{F{*zmpx&=Y)XO=s|}r&9EqXbew|~|tz!oce3LaB&ZEK2N{(*l>)_re zW{&(Nw#i|&Bcc88G~+tF4uhUzRR==GW1$zjgMwx#GrKc3V<=<@q;CV8HW9zZLL-|U^5w=i$lEpKx@cbm~(BL>53@XFRp&~ zLHLR0Px7n9HtI*F@*YCSTSqQsy7z1-v6Y}Xh%!>g-y zBaG4aF52bDzJHwA5fjI*x43dM>9NUSiAd(OOZi74P;wRbj_MXz;8w*&EIIOjgcMDk zS)7`iC_ES}r+O8|1cTL|053(fL)5>Gb^)v5TMjZ?>i2t$(B2eK=PCO!(BPaxOwQTK zkK72`Zyc0ZHV@SF5KdN?EDM<-!2>cUe($YkhpgAjs8||nN<1xzES%yhtofu6Nut(T-X27JM#3A~yxFvYwXnPQ{P@?ac8@dOou2#^F17YiGl5hqin@$1@>D%^-46l zNwh3nDZ^pokEbSQp0kI@e~CI{KE0WKbD?yYFd6M zY3jP&2X_ypXbj8mWMG>daD_pB$B=imF@7htJENux<5@6YwJk#M$La;wpVxrv0~v+y1;f;g|+6B z-7@v1whI?}{fJzl!(1c+Ku1anB9qP86M8@Hdo{gZ&0g*%Yh5!+AVf7Rc|j4U#H6aZ ztk29zRp>zY%#f!=fSzMzhpLqVzDgS&90otG3kOe8le&-Nfa`$Whr1?p53cHp2%$6F2QKcMIYQSzKSKfML0^_GvFRMT|dpN4Aw;i$sDg2EWNdPZ1H0 z+u5O5wvWZ1lV4l|kov}fsZCg>ao6HA+C!=-@#vLEuP8lgr0sYP(MF@Io+vw$f{L?UwIYo?noO8$-We1>=vvs01tS@y<-dWE`nTl)`~A@;o7Uo}`GtkA|C{mH(u(8C z(zj%EE!6F;is{$&y@l3;m6dL{?l!hi84aUgeom#^-p{z^aFt9F>k3!*pik%Mdfwzo z#!!`)P8qvC6h2|&lsYE)FvshdqiNKZaP~m3B7^*p`kFO6-qkWLz*halyE5)2$f2=p zA^GS70l|-F|RQgjx;ZEdxUOX zT}VherS>*R9^urS%qmH0r6hh|xzZbDJ6UbHn&?Gx`-^973p9jWXv{8fRf!il4y3e0 zpnQ^hwVA9!cQU0^ECdu5q$QFAzS6t7I*--Q3w6#GRyw9Wt;L#UR57!|5U9-H@bB3>Fqi3_oD~1N{fIHS#@~Qa=$q7 z0QVtNuu5<_k;-bt+3s)laSv1>5S5t~f)7g^%`^`O&5c4v0_(*c=D{WA3Qc=8Wdaem zLiflDmm)SOPtnVIU&->l8^N$DmIr1;{*O>`I`ha!NWvu#+MDr)-JxgrFM3HWzs*jc z6!E$9)%0$KEu}lT(|bzp5Z~E^heSBmUeKEu6$eG0c#g;)Y}xgRv3r(esfsn?>jQ$sayv@bD!4(}Ucc*0f7VN0DK zIZZllg~A8*PdTd?TD<=(Zq{~k2kMkf2p9``l|TU&c4UloaKvv=>0J%uSy++s;oK|V zk@8<&ywTjz1}Mt)W<+r+n~}Fr&@aw5NdrJeRJo{?RXtEJ8i?2MwZnh^U1QyI=0OJZ zo)E*k7ZRR`8=mKaMJI4qUnM4^zOJbTsa(K+@kdr?CoZzmMS=Oa8Q+5G?LM=6hkqL> zx&o1aj%=EIhE}D?DuT-yR}U}%1r(u&mCs{^vpBI5HR|$xQ?FQ>0yO+f%;0TwAr9;Z zBl{F0Y`ZgK)zKn>wae8$Pa}$Nk#fRn!?(oeCQ-994ufC-2A*{v5Zakyl>Z8p-ol8dleLq;4p;SE zr*YfKTiR7`X2^*FEL=sciOaAo_#^SnUrEdrfCB}QmOf{LZjN{k%@IEbEbvJGZjW;4 zdLs*x55@bYRQl~m8%(D0&Yq=~>mZVCTm4eQN_h!*3S*rGf{dXamdrZl1TA@f$uwbv zKPg$otsD8n`QzWzj+ICe-sv?SYw)vQ>PwNk-=?RH&mlAxyd*|P@rL7n{a5lN)PU}W z@;aUpLK#UsMm0wirq@rUls+ng?`_Mw)-{mFP$mz__fb|x_#|NR0|k&7mhqqZr1e6# zUdl25%{!}cah8zolgLc0{gmW*L!~qAV)2wb>NoVM&lCPZU;D-PVT+$Dmh|D%+a^E|J>8d zP-vQGb@moE$_{aiX1)Ye_8;2K3Odj<=b0((%-;MMx6C04tX!`~wcfzhIFM1V!hwfR zDagqkT+|%Uu$xupPOc7cdfFGMQeV!R zwaSI5&h_pkfU)lDQ40YRUnvFuMK(Ja0bseu(nDoQ{v;%t=93;IBV-7 z&i3}lnk=~UGW69pI9^p!n?VW_>Z7nKWAgQR?;GWE{EyiaW9EZ>DW7a`R(2ZWR?$6k zac#Wwv6LqX5|ppXBl+IW@skL%CrLK91hRJwKd}`KD-NH9)sgb56R$4Ha}QfXlMN-x z*hI7gTu111Q(J|4VQd*CHoXkMzNPn-h4p7g1M&t7?F`-iCNtHC zxgLH@GTq$hrZ(7(yNn}xFP(RXDO9ge=a>E&D*3z|X(gV1@IhLJu44ys(jK1nxhiD0 z*s^)>PdjiC485h-wz9W0YvidW4%eZQ5~5Aoz^>Z(@0PZnQAvnW6D18Vj@H|jcd>g- zHD!IWEZW^Iim&}iMj>7bf`rCHxMObm#l2I=aldvK;(2Kfd8{2xqB4iiOY&FcAbGrc zHjmc>TCDf9Y&3Y8gFYwpQ#zvifB3XZ5IjUxZ?k}`v;*_?26ND@m=(8c; zPbs2vuW)>Kr2whI4w2}bjr%^D*c=og%ExXqqOTBQD=liMq+$Emft(hUb!b6^J5NZp z^hy=8veSZPjajztVA-sqs~%BD4KKSlD3rqzN#JS9V8P=w`+{mbL*gEM{mCNH=1G<`jk0JC6X}u@*AAZyyUA z7(CJ1Y8|dQP<5eYc0`C$W1u@+hi7ta zBUS3JU5GF0Aa|aAq(vi}Lb&%jK}KBdaoWZs1(@>uxWc2Ik6>V|Z#B#l&AamC;eyr` zrcUMBkxg+#u+&r!j1j7&w>H`Mgnm+x9#ap2-- zM=}kn6mUbik0cMYDS6&O+ytWDXdBnp>*VZ0c{@*Czd!iOPpQE|s0tk(f!HRw+IbH% zrNqvUy2^YPsJI8I3D>Lc<%5Pl7>5Rnd4etR8s{D8;96bD{+HN>G5`@};+_AUh%vU5 zTEbP59i!F=2+Ja<-F(m2g*-0;5qURw8`?}Mv7z5?`rc>7P`%K3g|CaTE)xDebN(-= zm5U3K-J`2(VS#vA5f#&f;>HEvuZVJk%mrU4N{nJO$KUE;I#-wIQE0kZz93x^S>vo) z0xN~=kru{O`kw6+zA@b5QJ;IDtmGx)>X zC(FIxG~1o(@Vp>PR=(pm+9jtzphG@xi^MSgFW&&C_BfNwxA$1VMes$q;Ph?O)S&}W z?wem%2_fQp@7|(wQMP=XUI@9?wPiA{k<2|iR%_Jxi;nu^K-$I8sy>Z>^i)kwm#q3g ze8x(4q zVnsfL{8p>)Z%}FAV^YO9UA=9i{~CZf@%Bs|d2fs~MrjYLgqqQ;8s6>dCyIY6ZNu=k z@_45bAgs>^mlwQKF!i`rN?c_>(Y^Bk;88(RD_VatXOEUDdK0z>V5?AL-^VC+necUD z7@V*-dH%HID*kWDvD7t4fL^IAEk#-${Trot0^gOT1!Q}T(IK~yUngm1*zaEm(*dLs z`nq-RlU`Jb3|q=Xpr~l)DEA9pz8iT`{x{xN-qbD0MJkbUj3fB2-=EoQ!I$oeMU-CtlTvkkEk2*V+M#H=sHa>YQ^1|+mOBV!!OEf`I*-!;=Jl%aEyJ5Yk`L44!v| zy-(yQb1xo6YXXGAjlN5I>OSq$mk0G4ob|GUxSsil%004LqsYBbFm{maEW=85{&5cP z=?;o;ZRE_(BdZ~Y zl0<*Qt4(`cm+^EWBy>eCmce@xdPD6*zg`WDCCAj3Sk{ZCM`qYY`ufzq?c+=;JP z_QMK{zNtm;mR5PUZV6YD4knt77qV$hL8OM+N3+E2Tp6#|dLjJ+mc0&jl@oja4Qmr& zYTpR9SX%9`@4d+?bgXmd0f_@_o(n_3q{?^y+5}z<3X)P?8ClScm6Tu`KNE7EkpUzJ-JZ?2?x`4bV9`isq_|Ky!J9 zIC!D_)->$NB7KAMGW7HJrN*2`9pU_q5+dbmjH02ieTRx;0v|odVfO_;BWirG@JyO^ z=twe7of4&P+?$YAf7f8J!L0WijNWC!fHJOyuXTqqa#~8TgvyF??OUAdm-k?btEdoBe(1{#l30k_Es00!i>KVZf!Q{$WVqx>;)tOg|7CCeC*X=RC9n z7|cUSa$crq>Q2q(GMPcGvjALER)EBH%%UnL{b&%Gr%SiT!R9YB71qrH9OfJ$lsR)a zOLkIUT0TByGW2T#`M{r}g+TihMQ2`n;3550QR;es-PGsFnwI{$!LT$UmN(FyDp&{( z{pu5D;U|&FBveX;OeQwFJJcPX{eliYi(;)IxUkg0L;4CK#c*x>MEy2=IW;?EwBuTR z_pX*hu48U8UM%wW-py5dB-Jo6Gb zXIr3{Ern-$eRh>TX8bqtdo!aO7z^}-V`GN8^}J zj^bJ_ zOkD0WRd&xeGV^m!gipX0*3Y@248_;1jfirW2K<7h+~QtGS#xuXt^Ky)<~gU5EDUJ8 zbG%KYlh0vKrKVVMymMEeOvTBxS2j4oY+q0H6{}Y_rX&xnVmu!x2Y%_!*cl0cD=MRQ zdgecm^yEM5@#qiboc-jOyfqF*nf3ZUaJ-R=6(P2 zLP6)*fL245`@HyP6V{}}>SU7VCB8FTU~|^jESIA&kFgQRm^fJ5M@8^fr=Sho;YgwW~kjV-(FKWF|Dp+~NYEz7N>XQek+_j+3N zo($=fSEzBfOqsUAz^5}Ydr2Ouab2Bici8CZbrl%$@a)5zG9GP5I{{+bFYvB7LQQQ4 z3jS?WU!iREshQ(6-;at}h2|B#5YAz&d_*6sO=Fq9U29D!>!V4AfCmI-Q`38rtMFuL#dOGZxDUSD zK?qyx<@!`5kr7I^Ro}CXddK%5sA|2oV6Ukk`|fO=L?))%snL>lh#l-GCDD~{JQ9EV zx2gK<)A0drw{2xts`6f7|EM1owI1%IvWEhj<~hD z`qZC80uYQ^MoT&I(}~p<05-~5h=!o8$4p;YP*^kUE6mMs3i;dOYe@(!Sw0nL2F_(K zcY2JXXXMqp%P4y`(JoQJ8`ZIZU`aaicl zFt+k))?cj;5I<^q*{Wsk!S=x~@LTcwr!pu6shc`20}ug?bV&LAQ)l5C&L8a|(o0u2 zGwA1PhQ8h0doa-n_fa+VcfUBz8L=wAe{Yq)rFU=jo&corq(wY71e>dR@htLL=V8<&>x`ZZl^^#d(vn-|`nLq_1-zCNsWi zv&nlOF%Cj9j(o9+`dclM3;pbvq8f7Q$S9)>V;DCW3>fPksei!<4T>2>sgE{EB6k)s zshq&juSb4q5aiyyTC}Fou5g7tNy_K$y7wVB%}cB?(6}i3p@Omdw4VnjUL9(6Ul$5` z;g#k^^&1$6KMOwkvGQ-tmCjvEFH=!9i5XY?jlsWp55-o)j1GxyITys6qV7_*i@*ki zVoQSm`}oH&K6|C?2|>qF8p-b7Qh`r@hi%#)W%%PeR9kn24r`p^A+I;~qIVKm>eeq6 z!}h{epeY4rL#{k?%7Xjb2o%JiyQG<`X^x>?6hW|mury|w?>dN2Z05M=Ic>+f=H{qk z(B}`fl|L?fTh>L+$m-Q7BlCu~DpXZ?htcP`-CsS4LD_onwbrOXm&7?Le{7KIT)eI=fbSeQzM41E;F|mf(>yyF6(u$!mnCLv}W9pscUVHLM;;dYL-% zi&Z(7&2y=pd16*8yQ*fkWdArtmpm_S+iAv(9)2IwH&eNlf!Hf%5;%cucXT)L^pIOz z_#tQJTr>m5&jf_7UHy9V?au6_v#3t!^lN`5b$TVt#Sjk9ivs!pTS?7Zb3C^6J(9qe zG5I0Z4r!>{>#*>x2SVKX2^db#H|NwE(_|ZW|BAFHSpi#M`#wsvL_s-e18nURggJF>+*4+H9H4HHNCOQ~7G>#ti z^C(}3+j3LAPN?tc2@E}|=5AZEet8wg+S!IR{#KXw-gCv-qPf{_ zos+{F<6V88H!JShY@YFyOM4ut`Ut{i#_7Mk$siUDXY8W_SHvfH+=f}l`|>WW z&WNGH5w>^RAUW91JDl{5A7H5<|CFoRuq~~yKU(8IPPS585QZMvSh~ncXUC(Rpl4C1 z#&MNmTRt@%OHJtHN6DdDnTC!C&yWwE*XWTBl{X!N)P{_{eKkXk|4^(~3VGQKwO7Nv z+}SBo4_(@S0~|{JXPeX0T^(NI+LDq$hJ}VBK}i3}|3Ho;|6RR%%)#RH=EMYQNB?`- zDv7FQ0d@4@J#>^W_qlEF?k@M`6|qRU5_vBPazf}h(OtP(7NUS(5D|6ho78g-%KSY zjS$WLB^!++@>n@%AD*|InSKLT2{Z-lm!P|%nsGC1K!HdeXWhc`{J-*W{9k!U0cH@O zJW}JxAO5xfop^_%LezsLDFISx+Q1nai}U#-Rq|eQ!2nPDW5V-W;Nj<2i+Wz%7yACZk5vs z2Iwz05X&Z*@@vW4NU<0rz}OU%-YJkKuAX;-c$Tj z5~nI{AIObhk=L}T;|cSu=#GmRkGag`6Vm!3Aot^4MI8UslM@S!r^<>|u!bQEF^c+7 zbN`^A(M&4qDGy*I-wBX#pf`K1G}6ly9-kusbGxI|JNo5Q)nd$p@4B^eHBW&2{9LuW zIoe&!{)QULS}_RykxS^FwpEw>TOH=WOio`F%{)@cAChd4$|ci8(o}hNA}bw>o!o70 zw>%JxHP3(#S~?OF*DVt}C9FFi?0#la(G#b zn@C zb7}<~>$}Zg4&J{N>eN=fr`nGFm*AOAi{$j2-VyCxQF%X^;4=WRxCoQ=x#`z2a!>;+ zYs@~Fc-2n`PQO{;K6kUy!S(PT2|5tY^c5)YqT?Ux@j^co`zusEQl}A+R&XUgZ0Y;7y;HY{yoGxiHnY?BLgy^bG1Jn~+?}~j za087`d>J8{2^$Fi`xWqTN@wor-K7Rk(7)X?_Ul(odTc&dw77RQxuzN~?JQHAIt;ji zFGLj6u#Rf0CheDO#wTeBvy=q2Wb5i?X7`XV)R7tAtzN0VuVVcj@*bQ@kcu31dPJv_Q)I<3)d?SzfI(+oBop;|6s-+tGm19(zP;tYBCxazNBkmZ#XO)xf5F8K0E%j3F%r2oVwt|L01d6QQpk4}?un$veNU*Mnf zC;>aI3Fi@iCy<%fG`05mT>P2R>cNG zlvfV0)-gSsU0aW%tG5+i?odwCZ_4{(`){>WZ5#-6-WTmzpQD#xH)<4oiX{+}@{=Y% z5L6!vO$Y4K^(zx7qo#!`y+8VT#CUU+TlQRA2}jEMMx z%N_FWVMm8aaamG9YQ@cGUE7Xf)Ec+=jlYe+8_%$(4utWtF#D&bD#bJH!C&6dfD|U; zu@dG-l0&Jo!kY96Dq##s4TlS9_Hgb1Q@RB2{&l1K{!@;&K3IGCtm3NhZa-vNy zapWQUnrUlg!iPPBS?m~5AXmd$0S;H~qy}W;RvMEe3bQvTi5q#!(F3a6ed+xade}zD zEOoE{n|}^>%Rki9=;%go%TaGk*{`J82PXb~9wB`W^l)bj4#8Q^D(4pH%zEQ*OaJwR ze(Cy^U9Cp8{LqEnSZO$1P9H8U@1vQwLH8IRK@unLvo4R+S92h^qgz&!W(q{LAu5{%SOG z{eYse1EI;s=}_t=HRMRDWQY27ch77lTE^9n$G-mViK_Na^gp^JKB)nGaY~WNf4r|w zi+R#YnZi}Jk0>?u5X$&E7tL}zW!gElGc6zt?ug?}7EgaWEXYJy-TPm+`~UP54$1xa zfrPTK-QxWSYW5TKS;d^cloLDhcAWJ+;n@%6UqDDBBVPEK`#(JkbmJuNoO}d4+vj3} z|B-6FqREg;hkDdr7J(#kwpYFK%7(4T#9G5*Vy&(2Ru}-{%*Rb>Zc~SPI&*8+{oEd= zC!=iyE5DJ_Ptl~dZMtgIO5>AVPj#Ho(bEhIXxi0g-#7(E*CvCNg5hkBD^c0i(wCbN zi1*qiu9;h3JTr4;AE6RQ&p~aIp9~dTAMUQ@)5#Tj-{Z-beC=^nQh)76 z8M-=@%EecSRmrR%6~Q51`MWHa&Nio)GP0*YW$mL@yFTel1}%MJ9jJ;M6k(fl+2sAj zxJ`&-^>izDoU=4{M)>v^eQIjLMMepz0%9kcGGV}ont6e+0>n*V>n*p?uO5_qAeY&*%(8ik&e%kY-6?-)Fdk@o%A{Vn4jRO)xpBuPh&3+14y zN@-)y88d)5t^J#&-WpRK)_fW#*#<7zmK2CT=QsRdl6GoWj6oT~mJ>ovG+X=s!55n7 zNr?UD;ScPJ9NzL)RoH5 z4vf#w8lPWY{)b$c37^Cwd8(l=?8Yxm7FZT{P$bZjEk|1>Etb?tEGn}}l9blh-$dF! z|2J(iDIest^Z|>l3|~tIBCHB)%Kx|io3aTD8)=*IL_Sr+gmk?mLglxGzK z&g}RQ4o>sdoZ1AoQ>lDr4 zX`FKz!$`~eDjG@pDK>oqiQP||ERg7(i2djP?E=^trPZ#UIr-05it=A-N^bznP3}o1 zp|Y8V;hRmn!}#=I_`jXif2UMX5F!?s09R|OQg-n1V_0Cr1M@5n6w%0N&760f;&G5- zd%|T#BUF1$Ld8_y?wSSEBZt-5eJW?WnlK}Kq1irNSB%W1wh1(mKgvJ(w4K|Z3kK2O zmT%+_Cs$|#&%~rHd`78D`sPZ@Dk>$>akhp)h;zm(!39adfI|WZnDunfo*w=+SMsYK5-yF&OoxZmJ`;oeHk1*ZOZ*woXE(4j}BO5Ex$D%B&uR^n*A?heYpY$H7)^Hm# zIG^8s`(jLV+T128i()B7E5!0U{OM$#!ePG&r zMwD0P=o>!*$g4h0GPjDODo!kJDCG0B&TRkKX|mnAP|x+&v_%(P(PUB7$qn_b4@rFu zME!3Jkl*{kYyZlHO&V$9GWyTixwU9#sB49n$BqAV%{5J;w&=|~>{9-1;ajIY^GS8g zy=}$I5-dnCa_^AR74Y)mn05Q~kFhlqZs-a>i zrvWb5U&tr{`dM24i9Igu3tb6!a@=Bss=NB+&H*vEWgp-=VhBHUw;g!$N#jQF7yWS8 z-H{ZzuEWa49oMFo4qF7@Y@&>e-PYmvlMHdIltAYAo3}|u_7fRFLmzq2;3|%oQ$JB@ zx2K>J;Y{9np?=R(RQ;oI;7VL>!m&zK)q=$gRQ-(V3W8?ZWpk+0lF}^9Jtd^ z=Tk~34po~C^t8R~jqYNFcf_Vgbl5p1)4()VQZa5IX8en<;O68XVY@Y|)uVxXhqv4r z(|tZ9`LxnMq@a@z*>NcUe5P~I*Tgo%jyprG z@zZ+W8Q!QJu?kgmCE3o%%}Y-%Hgw>4Egg-w@}?yLbpZ5IQ2R!-RwDnz_S2hohRD)v07#ZRupu5c0JeE zE_HiW8Ib4rLZ#x=X*vVv4qX}l^|E^&;qK{9w`uke>E|sVlha)$(USO(YBzxoayxZS z*DZMo6iTjcvMNlK?+<5?8qWbUfxN|(g2dm{$60ZrxM)^lp-ovqiR#;v~ARX#?HQonDX&wGv zXa{B(eLyV#Na+*FHMlh7^8RCzIJ*1;&m0gpYltjmi-?Pp6hW6zoB&%Dkc2fjwQ{U9 z*f4RSzf*!RDubTQbFY{=Pk2&jH9cE@rZLw*E*5Ixo({J4@5(u+Rr!?X+(O#_d~z!M zhx+#l$hm1feL|hQ@+w%cUc>wAddIXIu$nsVq>B(|*KR1lbynjX9>fnU3~wLO+h}4Q z_d^)VuxtN-tYk9(!==C7`lL!fVzJ^T_*c5l+7-=;LN2qiJ6p+deW;x3Oj%H&Nh?7* z$jb!fKOV>j(@xV1sDpBj9uk-WEAF5=x*l@D{tsjC0o7EyZ4Ij+SSTt=FCtxf?}>`i zAqua6fK(v@L6N3F=t_qONDUBrj7W#jB!Dz20fEqhNDE*nQWGJxFTVG?-|^i4o^$@O z$6zO9WQ;sz&$ZTE>zS)Yn)h%*dB&tVm^Jk0LQve!GtZ7vo&qSZ+qE;(snS*`A zl@R|=z`FHuHAg7R%rg_b`%_1Wb&D@Xb(3MvbaCzk=Ft-WvE|*64h+W>JFHc?=7;s? zu73RQLP_pmc*h4Gnx>y8W8HmrgC?3Sd#9r{{UyM{=b0DH&-GYz@V-OM2^TZH*C*_1 zGFQiMs;_&Ez8m+dc>n(%+jWFG�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%0Dc^zjns&f4hSeNB^7LVs?0+~1p?ToK8LI9daz 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

_Xx;0fP` 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 Date: Thu, 11 Nov 2021 15:15:54 +0100 Subject: [PATCH 55/98] 5.1.1 --- packages/remark-math/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/remark-math/package.json b/packages/remark-math/package.json index bbc1e31..1a3b47e 100644 --- a/packages/remark-math/package.json +++ b/packages/remark-math/package.json @@ -1,6 +1,6 @@ { "name": "remark-math", - "version": "5.1.0", + "version": "5.1.1", "description": "remark plugin to parse and stringify math", "license": "MIT", "keywords": [ From 26bb0e35e7db88adb463697c652a84b442e88fb5 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Thu, 11 Nov 2021 15:16:27 +0100 Subject: [PATCH 56/98] rehype-katex: 6.0.2 --- packages/rehype-katex/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index b7d8031..fe06d74 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -1,6 +1,6 @@ { "name": "rehype-katex", - "version": "6.0.1", + "version": "6.0.2", "description": "rehype plugin to transform inline and block math with KaTeX", "license": "MIT", "keywords": [ From fec9acce1220f761b22a4480f75dab8cb7d9014f Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Thu, 11 Nov 2021 15:16:55 +0100 Subject: [PATCH 57/98] rehype-mathjax: 4.0.2 --- packages/rehype-mathjax/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index bc87b71..e928023 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -1,6 +1,6 @@ { "name": "rehype-mathjax", - "version": "4.0.1", + "version": "4.0.2", "description": "rehype plugin to transform inline and block math with MathJax", "license": "MIT", "keywords": [ From 326f164a1dfc5fb7aabea2643409d1c8133807d4 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 17 Nov 2021 11:38:39 +0100 Subject: [PATCH 58/98] Add `remark-mdx` to list of related projects --- packages/remark-math/readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/remark-math/readme.md b/packages/remark-math/readme.md index cbf8291..db6fa95 100644 --- a/packages/remark-math/readme.md +++ b/packages/remark-math/readme.md @@ -212,6 +212,8 @@ Always be wary of user input and use [`rehype-sanitize`][rehype-sanitize]. — support frontmatter (YAML, TOML, and more) * [`remark-directive`](https://github.com/remarkjs/remark-directive) — support directives +* [`remark-mdx`](https://github.com/mdx-js/mdx/tree/main/packages/mdx) + — support MDX (JSX, expressions, ESM) ## Contribute From 21a430e12b390fa7fdaa9286dc8204faec058485 Mon Sep 17 00:00:00 2001 From: Titus Date: Wed, 24 Nov 2021 12:03:00 +0100 Subject: [PATCH 59/98] Fix link --- packages/remark-math/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/remark-math/readme.md b/packages/remark-math/readme.md index db6fa95..91a3e16 100644 --- a/packages/remark-math/readme.md +++ b/packages/remark-math/readme.md @@ -212,7 +212,7 @@ Always be wary of user input and use [`rehype-sanitize`][rehype-sanitize]. — support frontmatter (YAML, TOML, and more) * [`remark-directive`](https://github.com/remarkjs/remark-directive) — support directives -* [`remark-mdx`](https://github.com/mdx-js/mdx/tree/main/packages/mdx) +* [`remark-mdx`](https://github.com/mdx-js/mdx/tree/main/packages/remark-mdx) — support MDX (JSX, expressions, ESM) ## Contribute From 08c7efcd4027b494e037125e0038d88aeb64a9b8 Mon Sep 17 00:00:00 2001 From: Enteleform Date: Fri, 14 Jan 2022 03:24:16 -0500 Subject: [PATCH 60/98] Fix mathjax font url Related-to GH-72. Closes GH-73. Reviewed-by: Titus Wormer --- packages/rehype-mathjax/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rehype-mathjax/readme.md b/packages/rehype-mathjax/readme.md index 2add318..5b154e5 100644 --- a/packages/rehype-mathjax/readme.md +++ b/packages/rehype-mathjax/readme.md @@ -186,7 +186,7 @@ For example: // … .use(rehypeMathjaxChtml, { chtml: { - fontURL: 'https://cdn.jsdelivr.net/npm/mathjax@3/components/output/chtml/fonts/woff-v2' + fontURL: 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/output/chtml/fonts/woff-v2' } }) // … From c4d9ace775d3e306f9cf55216801584bc995d043 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 1 Apr 2022 20:10:57 +0200 Subject: [PATCH 61/98] Update dev-dependencies --- package.json | 2 +- packages/rehype-katex/package.json | 2 +- packages/rehype-mathjax/index.js | 4 +--- packages/rehype-mathjax/lib/create-adaptor.browser.js | 4 +--- packages/rehype-mathjax/package.json | 4 ++-- packages/remark-html-katex/package.json | 2 +- 6 files changed, 7 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 6936636..c8cd37e 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "unified": "^10.0.0", "unist-builder": "^3.0.0", "unist-util-remove-position": "^4.0.0", - "xo": "^0.46.0" + "xo": "^0.48.0" }, "scripts": { "build": "npm run build --workspaces", diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index fe06d74..3d86ace 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -39,7 +39,7 @@ ], "dependencies": { "@types/hast": "^2.0.0", - "@types/katex": "^0.11.0", + "@types/katex": "^0.14.0", "hast-util-to-text": "^3.1.0", "katex": "^0.15.0", "rehype-parse": "^8.0.0", diff --git a/packages/rehype-mathjax/index.js b/packages/rehype-mathjax/index.js index 57f23b0..05dedc2 100644 --- a/packages/rehype-mathjax/index.js +++ b/packages/rehype-mathjax/index.js @@ -2,6 +2,4 @@ * @typedef {import('./lib/create-plugin.js').Options} Options */ -import rehypeMathJaxSvg from './svg.js' - -export default rehypeMathJaxSvg +export {default} from './svg.js' diff --git a/packages/rehype-mathjax/lib/create-adaptor.browser.js b/packages/rehype-mathjax/lib/create-adaptor.browser.js index cd44dfc..c38bed8 100644 --- a/packages/rehype-mathjax/lib/create-adaptor.browser.js +++ b/packages/rehype-mathjax/lib/create-adaptor.browser.js @@ -1,3 +1 @@ -import {browserAdaptor} from 'mathjax-full/js/adaptors/browserAdaptor.js' - -export {browserAdaptor as createAdaptor} +export {browserAdaptor as createAdaptor} from 'mathjax-full/js/adaptors/browserAdaptor.js' diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index e928023..f21df98 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -50,10 +50,10 @@ "dependencies": { "@types/hast": "^2.0.0", "@types/mathjax": "^0.0.37", - "@types/web": "^0.0.46", + "@types/web": "^0.0.61", "hast-util-from-dom": "^4.0.0", "hast-util-to-text": "^3.1.0", - "jsdom": "^18.0.0", + "jsdom": "^19.0.0", "mathjax-full": "^3.0.0", "unified": "^10.0.0", "unist-util-visit": "^4.0.0" diff --git a/packages/remark-html-katex/package.json b/packages/remark-html-katex/package.json index e11e21e..d87fd2b 100644 --- a/packages/remark-html-katex/package.json +++ b/packages/remark-html-katex/package.json @@ -24,7 +24,7 @@ "index.js" ], "dependencies": { - "@types/katex": "^0.11.0", + "@types/katex": "^0.14.0", "@types/mdast": "^3.0.0", "katex": "^0.15.0", "rehype-parse": "^8.0.0", From accaf29d7f01bf4707e3b6d36bc8085d7b28dbd6 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 1 Apr 2022 20:12:30 +0200 Subject: [PATCH 62/98] Replace skypack w/ esm.sh --- packages/rehype-katex/readme.md | 10 +++++----- packages/rehype-mathjax/readme.md | 10 +++++----- packages/remark-math/readme.md | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/rehype-katex/readme.md b/packages/rehype-katex/readme.md index e68eccc..69fe1fd 100644 --- a/packages/rehype-katex/readme.md +++ b/packages/rehype-katex/readme.md @@ -57,17 +57,17 @@ In Node.js (version 12.20+, 14.14+, or 16.0+), install with [npm][]: npm install rehype-katex ``` -In Deno with [Skypack][]: +In Deno with [`esm.sh`][esmsh]: ```js -import rehypeKatex from 'https://cdn.skypack.dev/rehype-katex@6?dts' +import rehypeKatex from 'https://esm.sh/rehype-katex@6' ``` -In browsers with [Skypack][]: +In browsers with [`esm.sh`][esmsh]: ```html ``` @@ -278,7 +278,7 @@ abide by its terms. [npm]: https://docs.npmjs.com/cli/install -[skypack]: https://www.skypack.dev +[esmsh]: https://esm.sh [health]: https://github.com/remarkjs/.github diff --git a/packages/rehype-mathjax/readme.md b/packages/rehype-mathjax/readme.md index 5b154e5..3673b9e 100644 --- a/packages/rehype-mathjax/readme.md +++ b/packages/rehype-mathjax/readme.md @@ -57,17 +57,17 @@ In Node.js (version 12.20+, 14.14+, or 16.0+), install with [npm][]: npm install rehype-mathjax ``` -In Deno with [Skypack][]: +In Deno with [`esm.sh`][esmsh]: ```js -import rehypeMathjax from 'https://cdn.skypack.dev/rehype-mathjax@4?dts' +import rehypeMathjax from 'https://esm.sh/rehype-mathjax@4' ``` -In browsers with [Skypack][]: +In browsers with [`esm.sh`][esmsh]: ```html ``` @@ -314,7 +314,7 @@ abide by its terms. [npm]: https://docs.npmjs.com/cli/install -[skypack]: https://www.skypack.dev +[esmsh]: https://esm.sh [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-math/readme.md b/packages/remark-math/readme.md index 91a3e16..d5f2c9b 100644 --- a/packages/remark-math/readme.md +++ b/packages/remark-math/readme.md @@ -58,17 +58,17 @@ In Node.js (version 12.20+, 14.14+, or 16.0+), install with [npm][]: npm install remark-math ``` -In Deno with [Skypack][]: +In Deno with [`esm.sh`][esmsh]: ```js -import remarkMath from 'https://cdn.skypack.dev/remark-math@5?dts' +import remarkMath from 'https://esm.sh/remark-math@5' ``` -In browsers with [Skypack][]: +In browsers with [`esm.sh`][esmsh]: ```html ``` @@ -259,7 +259,7 @@ abide by its terms. [npm]: https://docs.npmjs.com/cli/install -[skypack]: https://www.skypack.dev +[esmsh]: https://esm.sh [health]: https://github.com/remarkjs/.github From 63a5f76dc6928705905e84002665081cbe2f725f Mon Sep 17 00:00:00 2001 From: Titus Date: Sun, 3 Apr 2022 18:18:38 +0200 Subject: [PATCH 63/98] Add `.gitattributes` --- .gitattributes | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..85a6b2d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# https://github.com/github/linguist/blob/HEAD/docs/overrides.md +packages/*/test/**/*.html linguist-vendored From 9b2371f1530bc6d17e07fd6cf0316cbf1700122d Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 27 Jul 2022 18:23:17 +0200 Subject: [PATCH 64/98] Update dev-dependencies --- .github/workflows/main.yml | 2 +- package.json | 4 ++-- packages/rehype-katex/test.js | 2 +- packages/remark-html-katex/test.js | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fdb1410..cd4523e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,5 +18,5 @@ jobs: strategy: matrix: node: - - lts/erbium + - lts/fermium - node diff --git a/package.json b/package.json index c8cd37e..764b274 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "prettier": "^2.0.0", "rehype-parse": "^8.0.0", "rehype-stringify": "^9.0.0", - "remark-cli": "^10.0.0", + "remark-cli": "^11.0.0", "remark-html": "^15.0.0", "remark-parse": "^10.0.0", "remark-preset-wooorm": "^9.0.0", @@ -42,7 +42,7 @@ "unified": "^10.0.0", "unist-builder": "^3.0.0", "unist-util-remove-position": "^4.0.0", - "xo": "^0.48.0" + "xo": "^0.51.0" }, "scripts": { "build": "npm run build --workspaces", diff --git a/packages/rehype-katex/test.js b/packages/rehype-katex/test.js index 968fbb2..5316bd7 100644 --- a/packages/rehype-katex/test.js +++ b/packages/rehype-katex/test.js @@ -149,7 +149,7 @@ test('rehype-katex', (t) => { .processSync( '

Lorem

\n

\\alpa

' ) - .messages.map((d) => String(d)), + .messages.map(String), [ '2:4-2:42: KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲' ], diff --git a/packages/remark-html-katex/test.js b/packages/remark-html-katex/test.js index dbb1f65..394a852 100644 --- a/packages/remark-html-katex/test.js +++ b/packages/remark-html-katex/test.js @@ -98,7 +98,7 @@ test('remark-html-katex', (t) => { .use(remarkHtmlKatex) .use(remarkHtml, {sanitize: false}) .processSync('Lorem\n$\\alpa$') - .messages.map((d) => String(d)), + .messages.map(String), [ '2:1-2:8: KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲' ], From e53e67d225e354bbbbb77fa90dd185594288e5d5 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 27 Jul 2022 18:23:27 +0200 Subject: [PATCH 65/98] Fix fixtures for changes in mathjax --- .../rehype-mathjax/test/fixture/equation-numbering-1-chtml.html | 1 + packages/rehype-mathjax/test/fixture/small-chtml.html | 1 + 2 files changed, 2 insertions(+) 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 a16a386..0ff7b92 100644 --- a/packages/rehype-mathjax/test/fixture/equation-numbering-1-chtml.html +++ b/packages/rehype-mathjax/test/fixture/equation-numbering-1-chtml.html @@ -139,6 +139,7 @@ font-size: 100%; font-size-adjust: none; letter-spacing: normal; + border-collapse: collapse; word-wrap: normal; word-spacing: normal; white-space: nowrap; diff --git a/packages/rehype-mathjax/test/fixture/small-chtml.html b/packages/rehype-mathjax/test/fixture/small-chtml.html index dfe98bb..213b0df 100644 --- a/packages/rehype-mathjax/test/fixture/small-chtml.html +++ b/packages/rehype-mathjax/test/fixture/small-chtml.html @@ -138,6 +138,7 @@ font-size: 100%; font-size-adjust: none; letter-spacing: normal; + border-collapse: collapse; word-wrap: normal; word-spacing: normal; white-space: nowrap; From 7e63ca3475457eedaf6be1a737735c37353e46df Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 27 Jul 2022 18:28:00 +0200 Subject: [PATCH 66/98] Update KaTeX --- packages/rehype-katex/package.json | 2 +- packages/rehype-katex/readme.md | 8 +++++--- packages/remark-html-katex/package.json | 2 +- readme.md | 7 ++++++- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index 3d86ace..106fb07 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -41,7 +41,7 @@ "@types/hast": "^2.0.0", "@types/katex": "^0.14.0", "hast-util-to-text": "^3.1.0", - "katex": "^0.15.0", + "katex": "^0.16.0", "rehype-parse": "^8.0.0", "unified": "^10.0.0", "unist-util-remove-position": "^4.0.0", diff --git a/packages/rehype-katex/readme.md b/packages/rehype-katex/readme.md index 69fe1fd..b997a89 100644 --- a/packages/rehype-katex/readme.md +++ b/packages/rehype-katex/readme.md @@ -103,7 +103,7 @@ async function main() { .use(rehypeParse, {fragment: true}) .use(rehypeKatex) .use(rehypeDocument, { - css: 'https://cdn.jsdelivr.net/npm/katex@0.15.0/dist/katex.min.css' + css: 'https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css' }) .use(rehypeStringify) .process(await read('example.html')) @@ -121,7 +121,7 @@ Now running `node example.js` yields: example - +

@@ -161,9 +161,11 @@ style it properly. At the time of writing, the last version is: ```html - + ``` + + ## Syntax tree This plugin transforms elements with a class name of either `math-inline` and/or diff --git a/packages/remark-html-katex/package.json b/packages/remark-html-katex/package.json index d87fd2b..161c9ba 100644 --- a/packages/remark-html-katex/package.json +++ b/packages/remark-html-katex/package.json @@ -26,7 +26,7 @@ "dependencies": { "@types/katex": "^0.14.0", "@types/mdast": "^3.0.0", - "katex": "^0.15.0", + "katex": "^0.16.0", "rehype-parse": "^8.0.0", "unified": "^10.0.0", "unist-util-remove-position": "^4.0.0", diff --git a/readme.md b/readme.md index dc3e195..4cadf53 100644 --- a/readme.md +++ b/readme.md @@ -108,9 +108,14 @@ Now running `node example.js` yields: > properly: > > ```html -> +> > ``` + + ### Example: MathJax Supporting either MathJax or KaTeX is very similar. From 8735960df461ef6089564d94f7a6883766aa2477 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 27 Jul 2022 18:28:56 +0200 Subject: [PATCH 67/98] Update `jsdom` --- packages/rehype-mathjax/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index f21df98..2ca4df1 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -53,13 +53,13 @@ "@types/web": "^0.0.61", "hast-util-from-dom": "^4.0.0", "hast-util-to-text": "^3.1.0", - "jsdom": "^19.0.0", + "jsdom": "^20.0.0", "mathjax-full": "^3.0.0", "unified": "^10.0.0", "unist-util-visit": "^4.0.0" }, "devDependencies": { - "@types/jsdom": "^16.0.0" + "@types/jsdom": "^20.0.0" }, "scripts": { "build": "rimraf \"lib/**/*.d.ts\" \"test/**/*.d.ts\" \"*.d.ts\" && tsc && type-coverage", From 00f853197ebdb2318a87a28d888c187d4282242c Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 27 Jul 2022 18:29:23 +0200 Subject: [PATCH 68/98] Update `@types/web` --- packages/rehype-mathjax/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index 2ca4df1..1d63015 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -50,7 +50,7 @@ "dependencies": { "@types/hast": "^2.0.0", "@types/mathjax": "^0.0.37", - "@types/web": "^0.0.61", + "@types/web": "^0.0.70", "hast-util-from-dom": "^4.0.0", "hast-util-to-text": "^3.1.0", "jsdom": "^20.0.0", From ee0970f1448e59f15558ef78c76835d49aab1052 Mon Sep 17 00:00:00 2001 From: xiaoliu Date: Thu, 28 Jul 2022 00:31:57 +0800 Subject: [PATCH 69/98] Fix typo in readme of `rehype-katex` Closes GH-76. Reviewed-by: Christian Murphy Reviewed-by: Titus Wormer --- packages/rehype-katex/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rehype-katex/readme.md b/packages/rehype-katex/readme.md index b997a89..cfa7ba9 100644 --- a/packages/rehype-katex/readme.md +++ b/packages/rehype-katex/readme.md @@ -198,7 +198,7 @@ schema, and run `rehype-katex` afterwards. Like so: ```js -import rehypeSanitize, {defaultSchema} from 'rehype-stringify' +import rehypeSanitize, {defaultSchema} from 'rehype-sanitize' const mathSanitizeSchema = { ...defaultSchema, From 218948d516686f19e12d8d57c8c737e78f9b21e4 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 27 Jul 2022 18:32:26 +0200 Subject: [PATCH 70/98] Update top-level await in readmes --- packages/rehype-katex/readme.md | 24 ++++++++----------- packages/rehype-mathjax/readme.md | 16 +++++-------- packages/remark-math/readme.md | 22 +++++++---------- readme.md | 39 ++++++++++++++----------------- 4 files changed, 42 insertions(+), 59 deletions(-) diff --git a/packages/rehype-katex/readme.md b/packages/rehype-katex/readme.md index cfa7ba9..7994f39 100644 --- a/packages/rehype-katex/readme.md +++ b/packages/rehype-katex/readme.md @@ -96,20 +96,16 @@ import rehypeKatex from 'rehype-katex' import rehypeDocument from 'rehype-document' import rehypeStringify from 'rehype-stringify' -main() - -async function main() { - const file = await unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex) - .use(rehypeDocument, { - css: 'https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css' - }) - .use(rehypeStringify) - .process(await read('example.html')) - - console.log(String(file)) -} +const file = await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex) + .use(rehypeDocument, { + css: 'https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css' + }) + .use(rehypeStringify) + .process(await read('example.html')) + +console.log(String(file)) ``` Now running `node example.js` yields: diff --git a/packages/rehype-mathjax/readme.md b/packages/rehype-mathjax/readme.md index 3673b9e..a607754 100644 --- a/packages/rehype-mathjax/readme.md +++ b/packages/rehype-mathjax/readme.md @@ -95,17 +95,13 @@ import rehypeParse from 'rehype-parse' import rehypeMathjax from 'rehype-mathjax' import rehypeStringify from 'rehype-stringify' -main() - -async function main() { - const file = await unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeMathjax) - .use(rehypeStringify) - .process(await read('example.html')) +const file = await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeMathjax) + .use(rehypeStringify) + .process(await read('example.html')) - console.log(String(file)) -} +console.log(String(file)) ``` Now running `node example.js` yields: diff --git a/packages/remark-math/readme.md b/packages/remark-math/readme.md index d5f2c9b..3820c1e 100644 --- a/packages/remark-math/readme.md +++ b/packages/remark-math/readme.md @@ -96,19 +96,15 @@ import remarkRehype from 'remark-rehype' import rehypeKatex from 'rehype-katex' import rehypeStringify from 'rehype-stringify' -main() - -async function main() { - const file = await unified() - .use(remarkParse) - .use(remarkMath) - .use(remarkRehype) - .use(rehypeKatex) - .use(rehypeStringify) - .process(await read('example.md')) - - console.log(String(file)) -} +const file = await unified() + .use(remarkParse) + .use(remarkMath) + .use(remarkRehype) + .use(rehypeKatex) + .use(rehypeStringify) + .process(await read('example.md')) + +console.log(String(file)) ``` Now running `node example.js` yields: diff --git a/readme.md b/readme.md index 4cadf53..c765094 100644 --- a/readme.md +++ b/readme.md @@ -81,19 +81,15 @@ import remarkRehype from 'remark-rehype' import rehypeKatex from 'rehype-katex' import rehypeStringify from 'rehype-stringify' -main() - -async function main() { - const file = await unified() - .use(remarkParse) - .use(remarkMath) - .use(remarkRehype) - .use(rehypeKatex) - .use(rehypeStringify) - .process(await read('example.md')) - - console.log(String(file)) -} +const file = await unified() + .use(remarkParse) + .use(remarkMath) + .use(remarkRehype) + .use(rehypeKatex) + .use(rehypeStringify) + .process(await read('example.md')) + +console.log(String(file)) ``` Now running `node example.js` yields: @@ -130,15 +126,14 @@ Take the above KaTeX example and change: +import rehypeMathjax from 'rehype-mathjax' import rehypeStringify from 'rehype-stringify' - main() -@@ -13,7 +13,7 @@ async function main() { - .use(remarkParse) - .use(remarkMath) - .use(remarkRehype) -- .use(rehypeKatex) -+ .use(rehypeMathjax) - .use(rehypeStringify) - .process(await read('example.md')) +@@ -13,7 +13,7 @@ + .use(remarkParse) + .use(remarkMath) + .use(remarkRehype) +- .use(rehypeKatex) ++ .use(rehypeMathjax) + .use(rehypeStringify) + .process(await read('example.md')) ``` Now running `node example.js` yields: From f4596b4cddb85f89f2c40ec42b67e0f4995d6ead Mon Sep 17 00:00:00 2001 From: Titus Date: Fri, 7 Oct 2022 11:13:53 +0200 Subject: [PATCH 71/98] Add some notes on working like code Related-to: remarkjs/remark#1056. --- packages/remark-math/readme.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/remark-math/readme.md b/packages/remark-math/readme.md index 3820c1e..26e0d98 100644 --- a/packages/remark-math/readme.md +++ b/packages/remark-math/readme.md @@ -134,9 +134,14 @@ Whether to support math (text) with a single dollar (`boolean`, default: Single dollars work in Pandoc and many other places, but often interfere with “normal” dollars in text. +If you turn this off, you can still use two or more dollars for text math. + ## Syntax This plugin applies a micromark extensions to parse the syntax. +That basically follows how code works in markdown, except that dollars (`$`) +are used instead of backticks (`` ` ``), and that two dollars instead of 3 is +enough for blocks. See its readme for parse details: * [`micromark-extension-math`](https://github.com/micromark/micromark-extension-math#syntax) From 817ba5427f384135cb19d329129866a66edf2bbe Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 12 Oct 2022 21:41:00 +0200 Subject: [PATCH 72/98] Update dev-dependencies --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 764b274..d286327 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "unified": "^10.0.0", "unist-builder": "^3.0.0", "unist-util-remove-position": "^4.0.0", - "xo": "^0.51.0" + "xo": "^0.52.0" }, "scripts": { "build": "npm run build --workspaces", From 7e82f934e77d62b7366825633bc7f1d5d5e5a861 Mon Sep 17 00:00:00 2001 From: Peter Kaufman Date: Mon, 24 Oct 2022 05:42:21 -0400 Subject: [PATCH 73/98] Refactor Wording, few details on syntax in readme Closes GH-79. --- packages/remark-math/readme.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/remark-math/readme.md b/packages/remark-math/readme.md index 26e0d98..d814308 100644 --- a/packages/remark-math/readme.md +++ b/packages/remark-math/readme.md @@ -139,9 +139,10 @@ If you turn this off, you can still use two or more dollars for text math. ## Syntax This plugin applies a micromark extensions to parse the syntax. -That basically follows how code works in markdown, except that dollars (`$`) -are used instead of backticks (`` ` ``), and that two dollars instead of 3 is -enough for blocks. +The syntax basically follows how code works in markdown, except that dollars (`$`) +are used instead of backticks (`` ` ``) and that 2 or more dollars instead of 3 +or more backticks is enough for blocks. + See its readme for parse details: * [`micromark-extension-math`](https://github.com/micromark/micromark-extension-math#syntax) @@ -150,6 +151,17 @@ See its readme for parse details: > That means escapes don’t work inside math but you can use more dollars around > the math instead: `$$\raisebox{0.25em}{$\frac a b$}$$` +> 👉 **Note**: Like code, the difference between “inline” and “block”, +> is in the line endings: +> +> ```markdown +> $$inline$$ +> +> $$ +> block +> $$ +> ``` + ## HTML This plugin integrates with [`remark-rehype`][remark-rehype]. From a32340688b2954e1b560c180d0e5f7601f79b302 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sun, 23 Apr 2023 14:35:57 +0200 Subject: [PATCH 74/98] Update dev-dependencies --- package.json | 11 +++++++---- packages/remark-math/index.js | 3 ++- packages/remark-math/readme.md | 4 ++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index d286327..5154f10 100644 --- a/package.json +++ b/package.json @@ -38,11 +38,11 @@ "tape": "^5.0.0", "to-vfile": "^7.0.0", "type-coverage": "^2.0.0", - "typescript": "^4.0.0", + "typescript": "^5.0.0", "unified": "^10.0.0", "unist-builder": "^3.0.0", "unist-util-remove-position": "^4.0.0", - "xo": "^0.52.0" + "xo": "^0.54.0" }, "scripts": { "build": "npm run build --workspaces", @@ -60,11 +60,14 @@ "trailingComma": "none" }, "xo": { - "prettier": true + "prettier": true, + "rules": { + "unicorn/prefer-logical-operator-over-ternary": "off" + } }, "remarkConfig": { "plugins": [ - "preset-wooorm" + "remark-preset-wooorm" ] }, "typeCoverage": { diff --git a/packages/remark-math/index.js b/packages/remark-math/index.js index 85fcd02..9d11d7a 100644 --- a/packages/remark-math/index.js +++ b/packages/remark-math/index.js @@ -11,6 +11,7 @@ import {mathFromMarkdown, mathToMarkdown} from 'mdast-util-math' /** * Plugin to support math. * + * @this {import('unified').Processor} * @type {import('unified').Plugin<[Options?] | void[], Root, Root>} */ export default function remarkMath(options = {}) { @@ -25,7 +26,7 @@ export default function remarkMath(options = {}) { * @param {unknown} value */ function add(field, value) { - const list = /** @type {unknown[]} */ ( + const list = /** @type {Array} */ ( // Other extensions /* c8 ignore next 2 */ data[field] ? data[field] : (data[field] = []) diff --git a/packages/remark-math/readme.md b/packages/remark-math/readme.md index d814308..0826258 100644 --- a/packages/remark-math/readme.md +++ b/packages/remark-math/readme.md @@ -153,10 +153,10 @@ See its readme for parse details: > 👉 **Note**: Like code, the difference between “inline” and “block”, > is in the line endings: -> +> > ```markdown > $$inline$$ -> +> > $$ > block > $$ From e28098ac9dd5b52fdd2c4262453989b357f36482 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sun, 23 Apr 2023 14:57:49 +0200 Subject: [PATCH 75/98] Fix to catch non-parse katex errors Closes GH-82. --- packages/rehype-katex/index.js | 19 +++++++++++++++++++ packages/rehype-katex/test.js | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/packages/rehype-katex/index.js b/packages/rehype-katex/index.js index 5577798..58847e0 100644 --- a/packages/rehype-katex/index.js +++ b/packages/rehype-katex/index.js @@ -56,6 +56,25 @@ export default function rehypeKatex(options) { file[fn](error.message, element.position, origin) + // KaTeX can handle `ParseError` itself, but not others. + // Generate similar markup if this is an other error. + // See: . + if (error.name !== 'ParseError') { + element.children = [ + { + type: 'element', + tagName: 'span', + properties: { + className: ['katex-error'], + title: String(error), + style: 'color:' + (settings.errorColor || '#cc0000') + }, + children: [{type: 'text', value}] + } + ] + return + } + result = katex.renderToString( value, assign({}, settings, { diff --git a/packages/rehype-katex/test.js b/packages/rehype-katex/test.js index 5316bd7..608109e 100644 --- a/packages/rehype-katex/test.js +++ b/packages/rehype-katex/test.js @@ -214,5 +214,24 @@ test('rehype-katex', (t) => { 'should support comments' ) + t.deepEqual( + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex) + .use(rehypeStringify) + .processSync( + '\\begin{split}\n\\end{{split}}\n' + ) + .toString(), + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeStringify) + .processSync( + '\\begin{split}\n\\end{{split}}\n' + ) + .toString(), + 'should not crash on non-parse errors' + ) + t.end() }) From f7a35ea4dade42ed6c731d440ac24444a2180b94 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sun, 23 Apr 2023 15:04:44 +0200 Subject: [PATCH 76/98] Replace dependency to improve browser bundle size --- packages/rehype-katex/index.js | 9 +++------ packages/rehype-katex/package.json | 4 +--- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/rehype-katex/index.js b/packages/rehype-katex/index.js index 58847e0..75188f0 100644 --- a/packages/rehype-katex/index.js +++ b/packages/rehype-katex/index.js @@ -5,15 +5,11 @@ import katex from 'katex' import {visit} from 'unist-util-visit' -import {removePosition} from 'unist-util-remove-position' import {toText} from 'hast-util-to-text' -import {unified} from 'unified' -import rehypeParse from 'rehype-parse' +import {fromHtmlIsomorphic} from 'hast-util-from-html-isomorphic' const assign = Object.assign -const parseHtml = unified().use(rehypeParse, {fragment: true}) - const source = 'rehype-katex' /** @@ -85,8 +81,9 @@ export default function rehypeKatex(options) { ) } + const root = fromHtmlIsomorphic(result, {fragment: true}) // @ts-expect-error: assume no `doctypes` in KaTeX result. - element.children = removePosition(parseHtml.parse(result), true).children + element.children = root.children }) } } diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index 106fb07..1c06217 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -40,11 +40,9 @@ "dependencies": { "@types/hast": "^2.0.0", "@types/katex": "^0.14.0", + "hast-util-from-html-isomorphic": "^1.0.0", "hast-util-to-text": "^3.1.0", "katex": "^0.16.0", - "rehype-parse": "^8.0.0", - "unified": "^10.0.0", - "unist-util-remove-position": "^4.0.0", "unist-util-visit": "^4.0.0" }, "scripts": { From e18d024ce76f90bd6b89366091aa2a5dda771864 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sun, 23 Apr 2023 15:09:09 +0200 Subject: [PATCH 77/98] rehype-katex: 6.0.3 --- packages/rehype-katex/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index 1c06217..7984414 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -1,6 +1,6 @@ { "name": "rehype-katex", - "version": "6.0.2", + "version": "6.0.3", "description": "rehype plugin to transform inline and block math with KaTeX", "license": "MIT", "keywords": [ From 276816dd1ce84e7b1d1d488e827649dd86b1a931 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Tue, 18 Jul 2023 17:23:36 +0200 Subject: [PATCH 78/98] rehype-mathjax: remove unused type dependency Closes GH-75. --- packages/rehype-mathjax/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index 1d63015..b504ad4 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -50,7 +50,6 @@ "dependencies": { "@types/hast": "^2.0.0", "@types/mathjax": "^0.0.37", - "@types/web": "^0.0.70", "hast-util-from-dom": "^4.0.0", "hast-util-to-text": "^3.1.0", "jsdom": "^20.0.0", From 6d9970e8231b680a2cd881f8f5d52a96121b1f9a Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Tue, 18 Jul 2023 17:25:09 +0200 Subject: [PATCH 79/98] rehype-mathjax: 4.0.3 --- packages/rehype-mathjax/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index b504ad4..22cbd7e 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -1,6 +1,6 @@ { "name": "rehype-mathjax", - "version": "4.0.2", + "version": "4.0.3", "description": "rehype plugin to transform inline and block math with MathJax", "license": "MIT", "keywords": [ From dfc0b5bd0cca1bffac3003fb04e507847a79b7c8 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 16 Sep 2023 13:24:09 +0200 Subject: [PATCH 80/98] Update dev-dependencies --- package.json | 19 ++++++++----------- packages/rehype-katex/package.json | 4 +++- packages/rehype-mathjax/package.json | 4 +++- packages/remark-html-katex/package.json | 4 +++- packages/remark-math/index.js | 2 +- packages/remark-math/package.json | 4 +++- 6 files changed, 21 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 5154f10..5e58d09 100644 --- a/package.json +++ b/package.json @@ -23,9 +23,9 @@ "packages/rehype-mathjax" ], "devDependencies": { - "@types/tape": "^4.0.0", - "c8": "^7.0.0", - "prettier": "^2.0.0", + "@types/tape": "^5.0.0", + "c8": "^8.0.0", + "prettier": "^3.0.0", "rehype-parse": "^8.0.0", "rehype-stringify": "^9.0.0", "remark-cli": "^11.0.0", @@ -42,11 +42,11 @@ "unified": "^10.0.0", "unist-builder": "^3.0.0", "unist-util-remove-position": "^4.0.0", - "xo": "^0.54.0" + "xo": "^0.56.0" }, "scripts": { "build": "npm run build --workspaces", - "format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", + "format": "remark . -qfo && prettier . -w --log-level warn && xo --fix", "test-api": "npm run test-api --workspaces", "test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov --exclude \"packages/*/test.js\" --exclude \"packages/*/test/**/*.js\" npm run test-api", "test": "npm run build && npm run format && npm run test-coverage" @@ -59,12 +59,6 @@ "semi": false, "trailingComma": "none" }, - "xo": { - "prettier": true, - "rules": { - "unicorn/prefer-logical-operator-over-ternary": "off" - } - }, "remarkConfig": { "plugins": [ "remark-preset-wooorm" @@ -75,5 +69,8 @@ "detail": true, "strict": true, "ignoreCatch": true + }, + "xo": { + "prettier": true } } diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index 7984414..7fb1584 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -50,11 +50,13 @@ "test-api": "node --conditions development test.js", "test": "npm run build && npm run test-api" }, - "xo": false, "typeCoverage": { "atLeast": 100, "detail": true, "strict": true, "ignoreCatch": true + }, + "xo": { + "prettier": true } } diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index 22cbd7e..b1e0091 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -65,7 +65,6 @@ "test-api": "node --conditions development test/index.js", "test": "npm run build && npm run test-api" }, - "xo": false, "typeCoverage": { "atLeast": 100, "detail": true, @@ -77,5 +76,8 @@ "lib/create-renderer.d.ts", "lib/create-renderer.js" ] + }, + "xo": { + "prettier": true } } diff --git a/packages/remark-html-katex/package.json b/packages/remark-html-katex/package.json index 161c9ba..ae4e79e 100644 --- a/packages/remark-html-katex/package.json +++ b/packages/remark-html-katex/package.json @@ -37,11 +37,13 @@ "test-api": "node --conditions development test.js", "test": "npm run build && npm run test-api" }, - "xo": false, "typeCoverage": { "atLeast": 100, "detail": true, "strict": true, "ignoreCatch": true + }, + "xo": { + "prettier": true } } diff --git a/packages/remark-math/index.js b/packages/remark-math/index.js index 9d11d7a..006dc99 100644 --- a/packages/remark-math/index.js +++ b/packages/remark-math/index.js @@ -29,7 +29,7 @@ export default function remarkMath(options = {}) { const list = /** @type {Array} */ ( // Other extensions /* c8 ignore next 2 */ - data[field] ? data[field] : (data[field] = []) + data[field] || (data[field] = []) ) list.push(value) diff --git a/packages/remark-math/package.json b/packages/remark-math/package.json index 1a3b47e..3da2cde 100644 --- a/packages/remark-math/package.json +++ b/packages/remark-math/package.json @@ -45,11 +45,13 @@ "test-api": "node --conditions development test.js", "test": "npm run build && npm run test-api" }, - "xo": false, "typeCoverage": { "atLeast": 100, "detail": true, "strict": true, "ignoreCatch": true + }, + "xo": { + "prettier": true } } From 9b148682b77dd1cfd46ca758c21c369e4c804f03 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 16 Sep 2023 13:26:09 +0200 Subject: [PATCH 81/98] Update `@types/katex` --- packages/rehype-katex/package.json | 2 +- packages/remark-html-katex/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index 7fb1584..0b0eaed 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -39,7 +39,7 @@ ], "dependencies": { "@types/hast": "^2.0.0", - "@types/katex": "^0.14.0", + "@types/katex": "^0.16.0", "hast-util-from-html-isomorphic": "^1.0.0", "hast-util-to-text": "^3.1.0", "katex": "^0.16.0", diff --git a/packages/remark-html-katex/package.json b/packages/remark-html-katex/package.json index ae4e79e..50c4bd9 100644 --- a/packages/remark-html-katex/package.json +++ b/packages/remark-html-katex/package.json @@ -24,7 +24,7 @@ "index.js" ], "dependencies": { - "@types/katex": "^0.14.0", + "@types/katex": "^0.16.0", "@types/mdast": "^3.0.0", "katex": "^0.16.0", "rehype-parse": "^8.0.0", From 162033b10dd2dd96fbe00b79a103b17675b1c5d4 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 16 Sep 2023 13:26:13 +0200 Subject: [PATCH 82/98] Update `jsdom` --- packages/rehype-mathjax/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index b1e0091..b589985 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -52,13 +52,13 @@ "@types/mathjax": "^0.0.37", "hast-util-from-dom": "^4.0.0", "hast-util-to-text": "^3.1.0", - "jsdom": "^20.0.0", + "jsdom": "^21.0.0", "mathjax-full": "^3.0.0", "unified": "^10.0.0", "unist-util-visit": "^4.0.0" }, "devDependencies": { - "@types/jsdom": "^20.0.0" + "@types/jsdom": "^21.0.0" }, "scripts": { "build": "rimraf \"lib/**/*.d.ts\" \"test/**/*.d.ts\" \"*.d.ts\" && tsc && type-coverage", From 3edab5adf5349c260baa40c13d7db98a97b6e590 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 16 Sep 2023 13:35:03 +0200 Subject: [PATCH 83/98] Refactor `tsconfig.json`s --- package.json | 11 ++++++++--- packages/rehype-katex/package.json | 7 ------- packages/rehype-katex/tsconfig.json | 3 +-- packages/rehype-mathjax/browser.js | 2 +- packages/rehype-mathjax/chtml.js | 2 +- .../rehype-mathjax/lib/create-renderer.js | 2 +- packages/rehype-mathjax/package.json | 13 ------------- packages/rehype-mathjax/tsconfig.json | 3 +-- packages/remark-html-katex/package.json | 7 ------- packages/remark-html-katex/tsconfig.json | 3 +-- packages/remark-math/package.json | 7 ------- packages/remark-math/tsconfig.json | 3 +-- tsconfig.json | 19 ++++++++++--------- 13 files changed, 25 insertions(+), 57 deletions(-) diff --git a/package.json b/package.json index 5e58d09..df9ef26 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,6 @@ "remark-preset-wooorm": "^9.0.0", "remark-rehype": "^10.0.0", "remark-stringify": "^10.0.0", - "rimraf": "^3.0.0", "tape": "^5.0.0", "to-vfile": "^7.0.0", "type-coverage": "^2.0.0", @@ -45,7 +44,7 @@ "xo": "^0.56.0" }, "scripts": { - "build": "npm run build --workspaces", + "build": "tsc --build --clean && tsc --build && type-coverage", "format": "remark . -qfo && prettier . -w --log-level warn && xo --fix", "test-api": "npm run test-api --workspaces", "test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov --exclude \"packages/*/test.js\" --exclude \"packages/*/test/**/*.js\" npm run test-api", @@ -68,7 +67,13 @@ "atLeast": 100, "detail": true, "strict": true, - "ignoreCatch": true + "ignoreCatch": true, + "#": "needed `any`s", + "ignoreFiles": [ + "packages/rehype-mathjax/lib/create-plugin.d.ts", + "packages/rehype-mathjax/lib/create-renderer.d.ts", + "packages/rehype-mathjax/lib/create-renderer.js" + ] }, "xo": { "prettier": true diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index 0b0eaed..c44bdd6 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -46,16 +46,9 @@ "unist-util-visit": "^4.0.0" }, "scripts": { - "build": "rimraf \"*.d.ts\" && tsc && type-coverage", "test-api": "node --conditions development test.js", "test": "npm run build && npm run test-api" }, - "typeCoverage": { - "atLeast": 100, - "detail": true, - "strict": true, - "ignoreCatch": true - }, "xo": { "prettier": true } diff --git a/packages/rehype-katex/tsconfig.json b/packages/rehype-katex/tsconfig.json index 7e61871..4082f16 100644 --- a/packages/rehype-katex/tsconfig.json +++ b/packages/rehype-katex/tsconfig.json @@ -1,4 +1,3 @@ { - "extends": "../../tsconfig.json", - "include": ["*.js"] + "extends": "../../tsconfig.json" } diff --git a/packages/rehype-mathjax/browser.js b/packages/rehype-mathjax/browser.js index f5ecbbd..5655c4f 100644 --- a/packages/rehype-mathjax/browser.js +++ b/packages/rehype-mathjax/browser.js @@ -1,5 +1,5 @@ /** - * @typedef {import('./lib/create-plugin').Options} Options + * @typedef {import('./lib/create-plugin.js').Options} Options */ import {createPlugin} from './lib/create-plugin.js' diff --git a/packages/rehype-mathjax/chtml.js b/packages/rehype-mathjax/chtml.js index 5953e21..9ea9653 100644 --- a/packages/rehype-mathjax/chtml.js +++ b/packages/rehype-mathjax/chtml.js @@ -1,5 +1,5 @@ /** - * @typedef {import('./lib/create-plugin').Options} Options + * @typedef {import('./lib/create-plugin.js').Options} Options */ import {CHTML} from 'mathjax-full/js/output/chtml.js' diff --git a/packages/rehype-mathjax/lib/create-renderer.js b/packages/rehype-mathjax/lib/create-renderer.js index 5e796e0..af90027 100644 --- a/packages/rehype-mathjax/lib/create-renderer.js +++ b/packages/rehype-mathjax/lib/create-renderer.js @@ -1,6 +1,6 @@ /** * @typedef {import('hast').Element} Element - * @typedef {import('mathjax-full/js/core/OutputJax').OutputJax} OutputJax + * @typedef {import('mathjax-full/js/core/OutputJax.js').OutputJax} OutputJax * @typedef {import('mathjax-full/js/core/MathDocument.js').MathDocument} MathDocument * @typedef {import('mathjax-full/js/input/tex.js').TeX} TeX_ * @typedef {import('./create-plugin.js').Options} Options diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index b589985..3dc594e 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -61,22 +61,9 @@ "@types/jsdom": "^21.0.0" }, "scripts": { - "build": "rimraf \"lib/**/*.d.ts\" \"test/**/*.d.ts\" \"*.d.ts\" && tsc && type-coverage", "test-api": "node --conditions development test/index.js", "test": "npm run build && npm run test-api" }, - "typeCoverage": { - "atLeast": 100, - "detail": true, - "strict": true, - "ignoreCatch": true, - "#": "needed `any`s", - "ignoreFiles": [ - "lib/create-plugin.d.ts", - "lib/create-renderer.d.ts", - "lib/create-renderer.js" - ] - }, "xo": { "prettier": true } diff --git a/packages/rehype-mathjax/tsconfig.json b/packages/rehype-mathjax/tsconfig.json index 5fec52d..4082f16 100644 --- a/packages/rehype-mathjax/tsconfig.json +++ b/packages/rehype-mathjax/tsconfig.json @@ -1,4 +1,3 @@ { - "extends": "../../tsconfig.json", - "include": ["lib/**/*.js", "test/**/*.js", "*.js"] + "extends": "../../tsconfig.json" } diff --git a/packages/remark-html-katex/package.json b/packages/remark-html-katex/package.json index 50c4bd9..471a24b 100644 --- a/packages/remark-html-katex/package.json +++ b/packages/remark-html-katex/package.json @@ -33,16 +33,9 @@ "unist-util-visit": "^4.0.0" }, "scripts": { - "build": "rimraf \"*.d.ts\" && tsc && type-coverage", "test-api": "node --conditions development test.js", "test": "npm run build && npm run test-api" }, - "typeCoverage": { - "atLeast": 100, - "detail": true, - "strict": true, - "ignoreCatch": true - }, "xo": { "prettier": true } diff --git a/packages/remark-html-katex/tsconfig.json b/packages/remark-html-katex/tsconfig.json index 7e61871..4082f16 100644 --- a/packages/remark-html-katex/tsconfig.json +++ b/packages/remark-html-katex/tsconfig.json @@ -1,4 +1,3 @@ { - "extends": "../../tsconfig.json", - "include": ["*.js"] + "extends": "../../tsconfig.json" } diff --git a/packages/remark-math/package.json b/packages/remark-math/package.json index 3da2cde..0d77ad4 100644 --- a/packages/remark-math/package.json +++ b/packages/remark-math/package.json @@ -41,16 +41,9 @@ "unified": "^10.0.0" }, "scripts": { - "build": "rimraf \"*.d.ts\" && tsc && type-coverage", "test-api": "node --conditions development test.js", "test": "npm run build && npm run test-api" }, - "typeCoverage": { - "atLeast": 100, - "detail": true, - "strict": true, - "ignoreCatch": true - }, "xo": { "prettier": true } diff --git a/packages/remark-math/tsconfig.json b/packages/remark-math/tsconfig.json index 7e61871..4082f16 100644 --- a/packages/remark-math/tsconfig.json +++ b/packages/remark-math/tsconfig.json @@ -1,4 +1,3 @@ { - "extends": "../../tsconfig.json", - "include": ["*.js"] + "extends": "../../tsconfig.json" } diff --git a/tsconfig.json b/tsconfig.json index e31adf8..93d2959 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,16 +1,17 @@ { - "include": ["*.js"], "compilerOptions": { - "target": "ES2020", - "lib": ["ES2020"], - "module": "ES2020", - "moduleResolution": "node", - "allowJs": true, "checkJs": true, + "customConditions": ["development"], "declaration": true, "emitDeclarationOnly": true, - "allowSyntheticDefaultImports": true, + "exactOptionalPropertyTypes": true, + "lib": ["es2020"], + "module": "node16", + // To do: remove when `hast-util-from-parse5` is updated. "skipLibCheck": true, - "strict": true - } + "strict": true, + "target": "es2020" + }, + "exclude": ["coverage/", "node_modules/"], + "include": ["**/*.js"] } From fbe1dd986116312b817830c299fedac2b647b43a Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 16 Sep 2023 13:44:10 +0200 Subject: [PATCH 84/98] Refactor `package.json`s --- package.json | 26 +++++++++++++------------- packages/rehype-katex/package.json | 18 +++++++++--------- packages/rehype-mathjax/package.json | 18 +++++++++--------- packages/remark-math/package.json | 16 ++++++++-------- 4 files changed, 39 insertions(+), 39 deletions(-) diff --git a/package.json b/package.json index df9ef26..4d8148a 100644 --- a/package.json +++ b/package.json @@ -17,10 +17,10 @@ ], "type": "module", "workspaces": [ - "packages/remark-math", - "packages/remark-html-katex", - "packages/rehype-katex", - "packages/rehype-mathjax" + "packages/remark-html-katex/", + "packages/rehype-katex/", + "packages/remark-math/", + "packages/rehype-mathjax/" ], "devDependencies": { "@types/tape": "^5.0.0", @@ -45,18 +45,18 @@ }, "scripts": { "build": "tsc --build --clean && tsc --build && type-coverage", - "format": "remark . -qfo && prettier . -w --log-level warn && xo --fix", + "format": "remark . --frail --output --quiet && prettier . --log-level warn --write && xo --fix", + "test": "npm run build && npm run format && npm run test-coverage", "test-api": "npm run test-api --workspaces", - "test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov --exclude \"packages/*/test.js\" --exclude \"packages/*/test/**/*.js\" npm run test-api", - "test": "npm run build && npm run format && npm run test-coverage" + "test-coverage": "c8 --100 --reporter lcov npm run test-api" }, "prettier": { - "tabWidth": 2, - "useTabs": false, - "singleQuote": true, "bracketSpacing": false, "semi": false, - "trailingComma": "none" + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "none", + "useTabs": false }, "remarkConfig": { "plugins": [ @@ -66,14 +66,14 @@ "typeCoverage": { "atLeast": 100, "detail": true, - "strict": true, "ignoreCatch": true, "#": "needed `any`s", "ignoreFiles": [ "packages/rehype-mathjax/lib/create-plugin.d.ts", "packages/rehype-mathjax/lib/create-renderer.d.ts", "packages/rehype-mathjax/lib/create-renderer.js" - ] + ], + "strict": true }, "xo": { "prettier": true diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index c44bdd6..bb3203e 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -4,19 +4,19 @@ "description": "rehype plugin to transform inline and block math with KaTeX", "license": "MIT", "keywords": [ - "unified", - "remark", - "rehype", - "rehype-plugin", - "plugin", - "mdast", - "markdown", "hast", "html", - "math", "katex", "latex", - "tex" + "markdown", + "math", + "mdast", + "plugin", + "rehype", + "rehype-plugin", + "remark", + "tex", + "unified" ], "repository": "https://github.com/remarkjs/remark-math/tree/main/packages/rehype-katex", "bugs": "https://github.com/remarkjs/remark-math/issues", diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index 3dc594e..d82f187 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -4,19 +4,19 @@ "description": "rehype plugin to transform inline and block math with MathJax", "license": "MIT", "keywords": [ - "unified", - "remark", - "rehype", - "rehype-plugin", - "plugin", - "mdast", - "markdown", "hast", "html", + "latex", + "markdown", "math", "mathjax", - "latex", - "tex" + "mdast", + "plugin", + "rehype", + "rehype-plugin", + "remark", + "tex", + "unified" ], "repository": "https://github.com/remarkjs/remark-math/tree/main/packages/rehype-mathjax", "bugs": "https://github.com/remarkjs/remark-math/issues", diff --git a/packages/remark-math/package.json b/packages/remark-math/package.json index 0d77ad4..ca6a1f6 100644 --- a/packages/remark-math/package.json +++ b/packages/remark-math/package.json @@ -4,16 +4,16 @@ "description": "remark plugin to parse and stringify math", "license": "MIT", "keywords": [ - "unified", - "remark", - "remark-plugin", - "plugin", - "mdast", - "markdown", - "math", "katex", "latex", - "tex" + "markdown", + "math", + "mdast", + "plugin", + "remark", + "remark-plugin", + "tex", + "unified" ], "repository": "https://github.com/remarkjs/remark-math/tree/main/packages/remark-math", "bugs": "https://github.com/remarkjs/remark-math/issues", From dc90cb04be346821bacbdec5b05326eda1ff710b Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 16 Sep 2023 13:44:20 +0200 Subject: [PATCH 85/98] Refactor Actions --- .github/workflows/main.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cd4523e..fb63387 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,16 +7,15 @@ jobs: name: ${{matrix.node}} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: dcodeIO/setup-node-nvm@master + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 with: node-version: ${{matrix.node}} - - run: npm install -g npm - run: npm install - run: npm test - - uses: codecov/codecov-action@v1 + - uses: codecov/codecov-action@v3 strategy: matrix: node: - - lts/fermium + - lts/gallium - node From a65a6c620decb73b64a636e249a3058935b8e199 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 16 Sep 2023 13:45:04 +0200 Subject: [PATCH 86/98] Add `ignore-scripts` to `.npmrc`s --- .npmrc | 1 + packages/rehype-katex/.npmrc | 1 + packages/rehype-mathjax/.npmrc | 1 + packages/remark-html-katex/.npmrc | 1 + packages/remark-math/.npmrc | 1 + 5 files changed, 5 insertions(+) diff --git a/.npmrc b/.npmrc index 43c97e7..3757b30 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,2 @@ +ignore-scripts=true package-lock=false diff --git a/packages/rehype-katex/.npmrc b/packages/rehype-katex/.npmrc index 43c97e7..3757b30 100644 --- a/packages/rehype-katex/.npmrc +++ b/packages/rehype-katex/.npmrc @@ -1 +1,2 @@ +ignore-scripts=true package-lock=false diff --git a/packages/rehype-mathjax/.npmrc b/packages/rehype-mathjax/.npmrc index 43c97e7..3757b30 100644 --- a/packages/rehype-mathjax/.npmrc +++ b/packages/rehype-mathjax/.npmrc @@ -1 +1,2 @@ +ignore-scripts=true package-lock=false diff --git a/packages/remark-html-katex/.npmrc b/packages/remark-html-katex/.npmrc index 43c97e7..3757b30 100644 --- a/packages/remark-html-katex/.npmrc +++ b/packages/remark-html-katex/.npmrc @@ -1 +1,2 @@ +ignore-scripts=true package-lock=false diff --git a/packages/remark-math/.npmrc b/packages/remark-math/.npmrc index 43c97e7..3757b30 100644 --- a/packages/remark-math/.npmrc +++ b/packages/remark-math/.npmrc @@ -1 +1,2 @@ +ignore-scripts=true package-lock=false From 8e027e6a90ce4de3bab4a3bbb3174291526088e6 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 16 Sep 2023 13:58:00 +0200 Subject: [PATCH 87/98] Refactor to use `node:test` --- package.json | 3 +- packages/rehype-katex/test.js | 441 +++++---- packages/rehype-mathjax/test/index.js | 392 ++++---- packages/remark-html-katex/test.js | 283 +++--- packages/remark-math/test.js | 1294 ++++++++++++++----------- 5 files changed, 1333 insertions(+), 1080 deletions(-) diff --git a/package.json b/package.json index 4d8148a..12ef205 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "packages/rehype-mathjax/" ], "devDependencies": { - "@types/tape": "^5.0.0", + "@types/node": "^20.0.0", "c8": "^8.0.0", "prettier": "^3.0.0", "rehype-parse": "^8.0.0", @@ -34,7 +34,6 @@ "remark-preset-wooorm": "^9.0.0", "remark-rehype": "^10.0.0", "remark-stringify": "^10.0.0", - "tape": "^5.0.0", "to-vfile": "^7.0.0", "type-coverage": "^2.0.0", "typescript": "^5.0.0", diff --git a/packages/rehype-katex/test.js b/packages/rehype-katex/test.js index 608109e..6155f83 100644 --- a/packages/rehype-katex/test.js +++ b/packages/rehype-katex/test.js @@ -1,4 +1,5 @@ -import test from 'tape' +import assert from 'node:assert/strict' +import test from 'node:test' import katex from 'katex' import {unified} from 'unified' import remarkParse from 'remark-parse' @@ -8,230 +9,244 @@ import rehypeStringify from 'rehype-stringify' import remarkMath from '../remark-math/index.js' import rehypeKatex from './index.js' -test('rehype-katex', (t) => { - t.deepEqual( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex) - .use(rehypeStringify) - .processSync( - [ - '

Inline math \\alpha.

', - '

Block math:

', - '
\\gamma
' - ].join('\n') - ) - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - [ - '

Inline math ' + - katex.renderToString('\\alpha') + - '.

', - '

Block math:

', - '
' + - katex.renderToString('\\gamma', {displayMode: true}) + - '
' - ].join('\n') - ) - .toString(), - 'should transform math with katex' - ) +test('rehype-katex', async function (t) { + await t.test('should transform math with katex', async function () { + assert.deepEqual( + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex) + .use(rehypeStringify) + .processSync( + [ + '

Inline math \\alpha.

', + '

Block math:

', + '
\\gamma
' + ].join('\n') + ) + .toString(), + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeStringify) + .processSync( + [ + '

Inline math ' + + katex.renderToString('\\alpha') + + '.

', + '

Block math:

', + '
' + + katex.renderToString('\\gamma', {displayMode: true}) + + '
' + ].join('\n') + ) + .toString() + ) + }) - t.deepEqual( - unified() - .use(remarkParse) - .use(remarkMath) - .use(remarkRehype) - .use(rehypeKatex) - .use(rehypeStringify) - .processSync( - [ - 'Inline math $\\alpha$.', - '', - 'Block math:', - '', - '$$', - '\\gamma', - '$$' - ].join('\n') - ) - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - [ - '

Inline math ' + - katex.renderToString('\\alpha') + - '.

', - '

Block math:

', - '
' + - katex.renderToString('\\gamma', {displayMode: true}) + - '
' - ].join('\n') - ) - .toString(), - 'should integrate with `remark-math`' - ) + await t.test('should integrate with `remark-math`', async function () { + assert.deepEqual( + unified() + .use(remarkParse) + .use(remarkMath) + .use(remarkRehype) + .use(rehypeKatex) + .use(rehypeStringify) + .processSync( + [ + 'Inline math $\\alpha$.', + '', + 'Block math:', + '', + '$$', + '\\gamma', + '$$' + ].join('\n') + ) + .toString(), + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeStringify) + .processSync( + [ + '

Inline math ' + + katex.renderToString('\\alpha') + + '.

', + '

Block math:

', + '
' + + katex.renderToString('\\gamma', {displayMode: true}) + + '
' + ].join('\n') + ) + .toString() + ) + }) - t.deepEqual( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex) - .use(rehypeStringify) - .processSync( - '

Double math \\alpha.

' + await t.test( + 'should transform `.math-inline.math-display` math with `displayMode: true`', + async function () { + assert.deepEqual( + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex) + .use(rehypeStringify) + .processSync( + '

Double math \\alpha.

' + ) + .toString(), + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeStringify) + .processSync( + '

Double math ' + + katex.renderToString('\\alpha', {displayMode: true}) + + '.

' + ) + .toString() ) - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - '

Double math ' + - katex.renderToString('\\alpha', {displayMode: true}) + - '.

' - ) - .toString(), - 'should transform `.math-inline.math-display` math with `displayMode: true`' + } ) - const macros = {'\\RR': '\\mathbb{R}'} - - t.deepEqual( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex, {macros}) - .use(rehypeStringify) - .processSync('\\RR') - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - '' + - katex.renderToString('\\RR', {macros}) + - '' - ) - .toString(), - 'should support `macros`' - ) + await t.test('should support `macros`', async function () { + const macros = {'\\RR': '\\mathbb{R}'} - t.deepEqual( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex, {errorColor: 'orange'}) - .use(rehypeStringify) - .processSync('\\alpa') - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - '' + - katex.renderToString('\\alpa', { - throwOnError: false, - errorColor: 'orange' - }) + - '' - ) - .toString(), - 'should support `errorColor`' - ) + assert.deepEqual( + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex, {macros}) + .use(rehypeStringify) + .processSync('\\RR') + .toString(), + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeStringify) + .processSync( + '' + + katex.renderToString('\\RR', {macros}) + + '' + ) + .toString() + ) + }) - t.deepEqual( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex) - .use(rehypeStringify) - .processSync( - '

Lorem

\n

\\alpa

' - ) - .messages.map(String), - [ - '2:4-2:42: KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲' - ], - 'should create a message for errors' - ) + await t.test('should support `errorColor`', async function () { + assert.deepEqual( + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex, {errorColor: 'orange'}) + .use(rehypeStringify) + .processSync('\\alpa') + .toString(), + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeStringify) + .processSync( + '' + + katex.renderToString('\\alpa', { + throwOnError: false, + errorColor: 'orange' + }) + + '' + ) + .toString() + ) + }) - try { - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex, {throwOnError: true}) - .use(rehypeStringify) - .processSync( - '

Lorem

\n

\\alpa

' - ) - } catch (error_) { - const error = /** @type {Error} */ (error_) - t.equal( - error.message, - 'KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲', - 'should throw an error if `throwOnError: true`' + await t.test('should create a message for errors', async function () { + assert.deepEqual( + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex) + .use(rehypeStringify) + .processSync( + '

Lorem

\n

\\alpa

' + ) + .messages.map(String), + [ + '2:4-2:42: KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲' + ] ) - } + }) - t.deepEqual( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex, {errorColor: 'orange', strict: 'ignore'}) - .use(rehypeStringify) - .processSync('ê&') - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - 'ê&' - ) - .toString(), - 'should support `strict: ignore`' + await t.test( + 'should throw an error if `throwOnError: true`', + async function () { + try { + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex, {throwOnError: true}) + .use(rehypeStringify) + .processSync( + '

Lorem

\n

\\alpa

' + ) + } catch (error_) { + const error = /** @type {Error} */ (error_) + assert.equal( + error.message, + 'KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲' + ) + } + } ) - t.deepEqual( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex, {errorColor: 'orange', strict: 'ignore'}) - .use(rehypeStringify) - .processSync( - '
\\begin{split}\n f(-2) &= \\sqrt{-2+4} \\\\\n &= x % Test Comment\n\\end{split}
' - ) - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - '
' + - katex.renderToString( - '\\begin{split}\n f(-2) &= \\sqrt{-2+4} \\\\\n &= x % Test Comment\n\\end{split}', - {displayMode: true} - ) + - '
' - ) - .toString(), - 'should support comments' - ) + await t.test('should support `strict: ignore`', async function () { + assert.deepEqual( + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex, {errorColor: 'orange', strict: 'ignore'}) + .use(rehypeStringify) + .processSync('ê&') + .toString(), + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeStringify) + .processSync( + 'ê&' + ) + .toString() + ) + }) - t.deepEqual( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex) - .use(rehypeStringify) - .processSync( - '\\begin{split}\n\\end{{split}}\n' - ) - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - '\\begin{split}\n\\end{{split}}\n' - ) - .toString(), - 'should not crash on non-parse errors' - ) + await t.test('should support comments', async function () { + assert.deepEqual( + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex, {errorColor: 'orange', strict: 'ignore'}) + .use(rehypeStringify) + .processSync( + '
\\begin{split}\n f(-2) &= \\sqrt{-2+4} \\\\\n &= x % Test Comment\n\\end{split}
' + ) + .toString(), + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeStringify) + .processSync( + '
' + + katex.renderToString( + '\\begin{split}\n f(-2) &= \\sqrt{-2+4} \\\\\n &= x % Test Comment\n\\end{split}', + {displayMode: true} + ) + + '
' + ) + .toString() + ) + }) - t.end() + await t.test('should not crash on non-parse errors', async function () { + assert.deepEqual( + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex) + .use(rehypeStringify) + .processSync( + '\\begin{split}\n\\end{{split}}\n' + ) + .toString(), + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeStringify) + .processSync( + '\\begin{split}\n\\end{{split}}\n' + ) + .toString() + ) + }) }) diff --git a/packages/rehype-mathjax/test/index.js b/packages/rehype-mathjax/test/index.js index 5fb9fde..b4ef53f 100644 --- a/packages/rehype-mathjax/test/index.js +++ b/packages/rehype-mathjax/test/index.js @@ -1,5 +1,6 @@ +import assert from 'node:assert/strict' +import test from 'node:test' import path from 'node:path' -import test from 'tape' import {readSync} from 'to-vfile' import {unified} from 'unified' import remarkParse from 'remark-parse' @@ -13,221 +14,244 @@ import rehypeMathJaxBrowser from '../browser.js' const fixtures = path.join('test', 'fixture') -test('rehype-mathjax', (t) => { - t.equal( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeMathJaxSvg) - .use(rehypeStringify) - .processSync(readSync({dirname: fixtures, basename: 'small.html'})) - .toString(), - String(readSync({dirname: fixtures, basename: 'small-svg.html'})).trim(), - 'should render SVG' - ) +test('rehype-mathjax', async function (t) { + await t.test('should render SVG', async function () { + assert.equal( + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxSvg) + .use(rehypeStringify) + .processSync(readSync({dirname: fixtures, basename: 'small.html'})) + .toString(), + String(readSync({dirname: fixtures, basename: 'small-svg.html'})).trim() + ) + }) - t.throws( - () => { + await t.test('should crash for CHTML w/o `fontURL`', async function () { + assert.throws(function () { unified() .use(rehypeParse, {fragment: true}) .use(rehypeMathJaxChtml) .use(rehypeStringify) .processSync(readSync({dirname: fixtures, basename: 'small.html'})) .toString() - }, - /rehype-mathjax: missing `fontURL` in options/, - 'should crash for CHTML w/o `fontURL`' - ) - - t.equal( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeMathJaxChtml, {chtml: {fontURL: 'place/to/fonts'}}) - .use(rehypeStringify) - .processSync(readSync({dirname: fixtures, basename: 'small.html'})) - .toString(), - String(readSync({dirname: fixtures, basename: 'small-chtml.html'})).trim(), - 'should render CHTML' - ) + }, /rehype-mathjax: missing `fontURL` in options/) + }) - t.equal( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeMathJaxBrowser) - .use(rehypeStringify) - .processSync(readSync({dirname: fixtures, basename: 'small.html'})) - .toString(), - String(readSync({dirname: fixtures, basename: 'small-browser.html'})), - 'should render browser' - ) + await t.test('should render CHTML', async function () { + assert.equal( + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxChtml, {chtml: {fontURL: 'place/to/fonts'}}) + .use(rehypeStringify) + .processSync(readSync({dirname: fixtures, basename: 'small.html'})) + .toString(), + String(readSync({dirname: fixtures, basename: 'small-chtml.html'})).trim() + ) + }) - t.equal( - unified() - .use(remarkParse) - .use(remarkMath) - .use(remarkRehype) - .use(rehypeMathJaxSvg) - .use(rehypeStringify) - .processSync(readSync({dirname: fixtures, basename: 'markdown.md'})) - .toString(), - String(readSync({dirname: fixtures, basename: 'markdown-svg.html'})).trim(), - 'should integrate with `remark-math`' - ) + await t.test('should render browser', async function () { + assert.equal( + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxBrowser) + .use(rehypeStringify) + .processSync(readSync({dirname: fixtures, basename: 'small.html'})) + .toString(), + String(readSync({dirname: fixtures, basename: 'small-browser.html'})) + ) + }) - t.equal( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeMathJaxSvg) - .use(rehypeStringify) - .processSync(readSync({dirname: fixtures, basename: 'double.html'})) - .toString(), - String(readSync({dirname: fixtures, basename: 'double-svg.html'})).trim(), - 'should transform `.math-inline.math-display`' - ) + await t.test('should integrate with `remark-math`', async function () { + assert.equal( + unified() + .use(remarkParse) + .use(remarkMath) + .use(remarkRehype) + .use(rehypeMathJaxSvg) + .use(rehypeStringify) + .processSync(readSync({dirname: fixtures, basename: 'markdown.md'})) + .toString(), + String( + readSync({dirname: fixtures, basename: 'markdown-svg.html'}) + ).trim() + ) + }) - t.equal( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeMathJaxSvg) - .use(rehypeStringify) - .processSync(readSync({dirname: fixtures, basename: 'none.html'})) - .toString(), - String(readSync({dirname: fixtures, basename: 'none-svg.html'})), - 'should transform documents without math' + await t.test( + 'should transform `.math-inline.math-display`', + async function () { + assert.equal( + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxSvg) + .use(rehypeStringify) + .processSync(readSync({dirname: fixtures, basename: 'double.html'})) + .toString(), + String( + readSync({dirname: fixtures, basename: 'double-svg.html'}) + ).trim() + ) + } ) - t.equal( - unified() - .use(rehypeParse) - .use(rehypeMathJaxSvg) - .use(rehypeStringify) - .processSync(readSync({dirname: fixtures, basename: 'document.html'})) - .toString(), - String(readSync({dirname: fixtures, basename: 'document-svg.html'})).trim(), - 'should transform complete documents' - ) + await t.test('should transform documents without math', async function () { + assert.equal( + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxSvg) + .use(rehypeStringify) + .processSync(readSync({dirname: fixtures, basename: 'none.html'})) + .toString(), + String(readSync({dirname: fixtures, basename: 'none-svg.html'})) + ) + }) - t.equal( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeMathJaxBrowser, { - tex: { - inlineMath: [['$', '$']], - displayMath: [['$$', '$$']] - } - }) - .use(rehypeStringify) - .processSync(readSync({dirname: fixtures, basename: 'small.html'})) - .toString(), - String( - readSync({ - dirname: fixtures, - basename: 'small-browser-delimiters.html' - }) - ), - 'should support custom `inlineMath` and `displayMath` delimiters for browser' - ) + await t.test('should transform complete documents', async function () { + assert.equal( + unified() + .use(rehypeParse) + .use(rehypeMathJaxSvg) + .use(rehypeStringify) + .processSync(readSync({dirname: fixtures, basename: 'document.html'})) + .toString(), + String( + readSync({dirname: fixtures, basename: 'document-svg.html'}) + ).trim() + ) + }) - t.equal( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeMathJaxSvg, {tex: {tags: 'ams'}}) - .use(rehypeStringify) - .processSync( - readSync({ - dirname: fixtures, - basename: 'equation-numbering-1.html' - }) + await t.test( + 'should support custom `inlineMath` and `displayMath` delimiters for browser', + async function () { + assert.equal( + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxBrowser, { + tex: { + inlineMath: [['$', '$']], + displayMath: [['$$', '$$']] + } + }) + .use(rehypeStringify) + .processSync(readSync({dirname: fixtures, basename: 'small.html'})) + .toString(), + String( + readSync({ + dirname: fixtures, + basename: 'small-browser-delimiters.html' + }) + ) ) - .toString(), - String( - readSync({ - dirname: fixtures, - basename: 'equation-numbering-1-svg.html' - }) - ).trim(), - 'should render SVG with equation numbers' + } ) - t.equal( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeMathJaxSvg, {tex: {tags: 'ams'}}) - .use(rehypeStringify) - .processSync( + await t.test('should render SVG with equation numbers', async function () { + assert.equal( + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxSvg, {tex: {tags: 'ams'}}) + .use(rehypeStringify) + .processSync( + readSync({ + dirname: fixtures, + basename: 'equation-numbering-1.html' + }) + ) + .toString(), + String( readSync({ dirname: fixtures, - basename: 'equation-numbering-2.html' + basename: 'equation-numbering-1-svg.html' }) - ) - .toString(), - String( - readSync({ - dirname: fixtures, - basename: 'equation-numbering-2-svg.html' - }) - ).trim(), - 'should render SVG with reference to an undefined equation' - ) + ).trim() + ) + }) - t.equal( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeMathJaxChtml, { - chtml: {fontURL: 'place/to/fonts'}, - tex: {tags: 'ams'} - }) - .use(rehypeStringify) - .processSync( - readSync({ - dirname: fixtures, - basename: 'equation-numbering-1.html' - }) + await t.test( + 'should render SVG with reference to an undefined equation', + async function () { + assert.equal( + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxSvg, {tex: {tags: 'ams'}}) + .use(rehypeStringify) + .processSync( + readSync({ + dirname: fixtures, + basename: 'equation-numbering-2.html' + }) + ) + .toString(), + String( + readSync({ + dirname: fixtures, + basename: 'equation-numbering-2-svg.html' + }) + ).trim() ) - .toString(), - String( - readSync({ - dirname: fixtures, - basename: 'equation-numbering-1-chtml.html' - }) - ).trim(), - 'should render CHTML with equation numbers' + } ) - t.equal( - (() => { - const processor = unified() + await t.test('should render CHTML with equation numbers', async function () { + assert.equal( + unified() .use(rehypeParse, {fragment: true}) - .use(rehypeMathJaxSvg, {tex: {tags: 'ams'}}) + .use(rehypeMathJaxChtml, { + chtml: {fontURL: 'place/to/fonts'}, + tex: {tags: 'ams'} + }) .use(rehypeStringify) - return ['equation-numbering-1.html', 'equation-numbering-2.html'] - .map((basename) => - processor - .processSync( - readSync({ - dirname: fixtures, - basename - }) - ) - .toString() + .processSync( + readSync({ + dirname: fixtures, + basename: 'equation-numbering-1.html' + }) ) - .join('') - })(), - [ + .toString(), String( readSync({ dirname: fixtures, - basename: 'equation-numbering-1-svg.html' - }) - ).trim(), - String( - readSync({ - dirname: fixtures, - basename: 'equation-numbering-2-svg.html' + basename: 'equation-numbering-1-chtml.html' }) ).trim() - ].join(''), - 'should render SVG with equation numbers' - ) + ) + }) - t.end() + await t.test('should render SVG with equation numbers', async function () { + assert.equal( + (function () { + const processor = unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxSvg, {tex: {tags: 'ams'}}) + .use(rehypeStringify) + return ['equation-numbering-1.html', 'equation-numbering-2.html'] + .map(function (basename) { + return processor + .processSync( + readSync({ + dirname: fixtures, + basename + }) + ) + .toString() + }) + .join('') + })(), + [ + String( + readSync({ + dirname: fixtures, + basename: 'equation-numbering-1-svg.html' + }) + ).trim(), + String( + readSync({ + dirname: fixtures, + basename: 'equation-numbering-2-svg.html' + }) + ).trim() + ].join('') + ) + }) }) diff --git a/packages/remark-html-katex/test.js b/packages/remark-html-katex/test.js index 394a852..26feedf 100644 --- a/packages/remark-html-katex/test.js +++ b/packages/remark-html-katex/test.js @@ -1,4 +1,5 @@ -import test from 'tape' +import assert from 'node:assert/strict' +import test from 'node:test' import katex from 'katex' import {unified} from 'unified' import remarkParse from 'remark-parse' @@ -8,151 +9,159 @@ import remarkHtml from 'remark-html' import remarkMath from '../remark-math/index.js' import remarkHtmlKatex from './index.js' -test('remark-html-katex', (t) => { - t.deepEqual( - unified() - .use(remarkParse) - .use(remarkMath) - .use(remarkHtmlKatex) - .use(remarkHtml, {sanitize: false}) - .processSync( - [ - 'Inline math $\\alpha$.', - '', - 'Block math:', - '', - '$$', - '\\gamma', - '$$' - ].join('\n') - ) - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - [ - '

Inline math ' + - katex.renderToString('\\alpha') + - '.

', - '

Block math:

', - '
' + - katex.renderToString('\\gamma', {displayMode: true}) + - '
', - '' - ].join('\n') - ) - .toString(), - 'should transform math with katex' - ) +test('remark-html-katex', async function (t) { + await t.test('should transform math with katex', async function () { + assert.deepEqual( + unified() + .use(remarkParse) + .use(remarkMath) + .use(remarkHtmlKatex) + .use(remarkHtml, {sanitize: false}) + .processSync( + [ + 'Inline math $\\alpha$.', + '', + 'Block math:', + '', + '$$', + '\\gamma', + '$$' + ].join('\n') + ) + .toString(), + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeStringify) + .processSync( + [ + '

Inline math ' + + katex.renderToString('\\alpha') + + '.

', + '

Block math:

', + '
' + + katex.renderToString('\\gamma', {displayMode: true}) + + '
', + '' + ].join('\n') + ) + .toString() + ) + }) - const macros = {'\\RR': '\\mathbb{R}'} + await t.test('should support `macros`', async function () { + const macros = {'\\RR': '\\mathbb{R}'} - t.deepEqual( - unified() - .use(remarkParse) - .use(remarkMath) - .use(remarkHtmlKatex, {macros}) - .use(remarkHtml, {sanitize: false}) - .processSync('$\\RR$') - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - '

' + - katex.renderToString('\\RR', {macros}) + - '

\n' - ) - .toString(), - 'should support `macros`' - ) + assert.deepEqual( + unified() + .use(remarkParse) + .use(remarkMath) + .use(remarkHtmlKatex, {macros}) + .use(remarkHtml, {sanitize: false}) + .processSync('$\\RR$') + .toString(), + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeStringify) + .processSync( + '

' + + katex.renderToString('\\RR', {macros}) + + '

\n' + ) + .toString() + ) + }) - t.deepEqual( - unified() - .use(remarkParse) - .use(remarkMath) - .use(remarkHtmlKatex, {errorColor: 'orange'}) - .use(remarkHtml, {sanitize: false}) - .processSync('$\\alpa$') - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - '

' + - katex.renderToString('\\alpa', { - throwOnError: false, - errorColor: 'orange' - }) + - '

\n' - ) - .toString(), - 'should support `errorColor`' - ) + await t.test('should support `errorColor`', async function () { + assert.deepEqual( + unified() + .use(remarkParse) + .use(remarkMath) + .use(remarkHtmlKatex, {errorColor: 'orange'}) + .use(remarkHtml, {sanitize: false}) + .processSync('$\\alpa$') + .toString(), + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeStringify) + .processSync( + '

' + + katex.renderToString('\\alpa', { + throwOnError: false, + errorColor: 'orange' + }) + + '

\n' + ) + .toString() + ) + }) - t.deepLooseEqual( - unified() - .use(remarkParse) - .use(remarkMath) - .use(remarkHtmlKatex) - .use(remarkHtml, {sanitize: false}) - .processSync('Lorem\n$\\alpa$') - .messages.map(String), - [ - '2:1-2:8: KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲' - ], - 'should create a message for errors' + await t.test('should create a message for errors', async function () { + assert.deepEqual( + unified() + .use(remarkParse) + .use(remarkMath) + .use(remarkHtmlKatex) + .use(remarkHtml, {sanitize: false}) + .processSync('Lorem\n$\\alpa$') + .messages.map(String), + [ + '2:1-2:8: KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲' + ] + ) + }) + + await t.test( + 'should throw an error if `throwOnError: true`', + async function () { + try { + unified() + .use(remarkParse) + .use(remarkMath) + .use(remarkHtmlKatex, {throwOnError: true}) + .use(remarkHtml, {sanitize: false}) + .processSync('Lorem\n$\\alpa$') + } catch (error_) { + const error = /** @type {Error} */ (error_) + assert.equal( + error.message, + 'KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲' + ) + } + } ) - try { - unified() - .use(remarkParse) - .use(remarkMath) - .use(remarkHtmlKatex, {throwOnError: true}) - .use(remarkHtml, {sanitize: false}) - .processSync('Lorem\n$\\alpa$') - } catch (error_) { - const error = /** @type {Error} */ (error_) - t.equal( - error.message, - 'KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲', - 'should throw an error if `throwOnError: true`' + await t.test('should support `strict: ignore`', async function () { + assert.deepEqual( + unified() + .use(remarkParse) + .use(remarkMath) + .use(remarkHtmlKatex, {errorColor: 'orange', strict: 'ignore'}) + .use(remarkHtml, {sanitize: false}) + .processSync('$ê&$') + .toString(), + unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeStringify) + .processSync( + '

ê&

\n' + ) + .toString() ) - } + }) - t.deepEqual( - unified() - .use(remarkParse) - .use(remarkMath) + await t.test('should support generated nodes', async function () { + const pipeline = unified() .use(remarkHtmlKatex, {errorColor: 'orange', strict: 'ignore'}) .use(remarkHtml, {sanitize: false}) - .processSync('$ê&$') - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - '

ê&

\n' - ) - .toString(), - 'should support `strict: ignore`' - ) - const pipeline = unified() - .use(remarkHtmlKatex, {errorColor: 'orange', strict: 'ignore'}) - .use(remarkHtml, {sanitize: false}) - - t.deepEqual( - pipeline.stringify( - pipeline.runSync({ - type: 'root', - children: [{type: 'inlineMath', value: '\\alpha'}] - }) - ), - '
' + katex.renderToString('\\alpha') + '
\n', - 'should support generated nodes' - ) - - t.end() + assert.deepEqual( + pipeline.stringify( + pipeline.runSync({ + type: 'root', + children: [{type: 'inlineMath', value: '\\alpha'}] + }) + ), + '
' + katex.renderToString('\\alpha') + '
\n' + ) + }) }) diff --git a/packages/remark-math/test.js b/packages/remark-math/test.js index 2e8c532..1f364ff 100644 --- a/packages/remark-math/test.js +++ b/packages/remark-math/test.js @@ -1,4 +1,5 @@ -import test from 'tape' +import assert from 'node:assert/strict' +import test from 'node:test' import {u} from 'unist-builder' import {removePosition} from 'unist-util-remove-position' import {unified} from 'unified' @@ -8,592 +9,797 @@ import rehypeStringify from 'rehype-stringify' import remarkStringify from 'remark-stringify' import remarkMath from './index.js' -test('remarkMath', (t) => { +test('remarkMath', async function (t) { const toHtml = unified() .use(remarkParse) .use(remarkMath) .use(remarkRehype) .use(rehypeStringify) - t.deepEqual( - removePosition( - unified() - .use(remarkParse) - .use(remarkMath) - .parse('Math $\\alpha$\n\n$$\n\\beta+\\gamma\n$$'), - true - ), - u('root', [ - u('paragraph', [ - u('text', 'Math '), - u( - 'inlineMath', - { - data: { - hName: 'span', - hProperties: {className: ['math', 'math-inline']}, - hChildren: [u('text', '\\alpha')] - } - }, - '\\alpha' - ) - ]), - u( - 'math', - { - meta: null, - data: { - hName: 'div', - hProperties: {className: ['math', 'math-display']}, - hChildren: [u('text', '\\beta+\\gamma')] - } - }, - '\\beta+\\gamma' - ) - ]), - 'should parse inline and block math' - ) - - t.deepEqual( - removePosition( - unified().use(remarkParse).use(remarkMath).parse('\\$\\alpha$'), - true - ), - u('root', [u('paragraph', [u('text', '$\\alpha$')])]), - 'should ignore an escaped opening dollar sign' - ) - - t.deepEqual( - removePosition( - unified().use(remarkParse).use(remarkMath).parse('$\\alpha\\$'), - true - ), - u('root', [ - u('paragraph', [ - u( - 'inlineMath', - { - data: { - hName: 'span', - hProperties: {className: ['math', 'math-inline']}, - hChildren: [u('text', '\\alpha\\')] - } - }, - '\\alpha\\' - ) - ]) - ]), - 'should *not* ignore an escaped closing dollar sign' - ) - - t.deepEqual( - removePosition( - unified().use(remarkParse).use(remarkMath).parse('\\\\$\\alpha$'), - true - ), - u('root', [ - u('paragraph', [ - u('text', '\\'), + await t.test('should parse inline and block math', async function () { + assert.deepEqual( + removePosition( + unified() + .use(remarkParse) + .use(remarkMath) + .parse('Math $\\alpha$\n\n$$\n\\beta+\\gamma\n$$'), + true + ), + u('root', [ + u('paragraph', [ + u('text', 'Math '), + u( + 'inlineMath', + { + data: { + hName: 'span', + hProperties: {className: ['math', 'math-inline']}, + hChildren: [u('text', '\\alpha')] + } + }, + '\\alpha' + ) + ]), u( - 'inlineMath', + 'math', { + meta: null, data: { - hName: 'span', - hProperties: {className: ['math', 'math-inline']}, - hChildren: [u('text', '\\alpha')] + hName: 'div', + hProperties: {className: ['math', 'math-display']}, + hChildren: [u('text', '\\beta+\\gamma')] } }, - '\\alpha' + '\\beta+\\gamma' ) ]) - ]), - 'should support a escaped escape before a dollar sign' - ) - - t.deepEqual( - removePosition( - unified().use(remarkParse).use(remarkMath).parse('`$`\\alpha$'), - true - ), - u('root', [u('paragraph', [u('inlineCode', '$'), u('text', '\\alpha$')])]), - 'should ignore dollar signs in inline code (#1)' - ) - - t.deepEqual( - removePosition( - unified().use(remarkParse).use(remarkMath).parse('$\\alpha`$`'), - true - ), - u('root', [ - u('paragraph', [ - u( - 'inlineMath', - { - data: { - hName: 'span', - hProperties: {className: ['math', 'math-inline']}, - hChildren: [u('text', '\\alpha`')] - } - }, - '\\alpha`' + ) + }) + + await t.test( + 'should ignore an escaped opening dollar sign', + async function () { + assert.deepEqual( + removePosition( + unified().use(remarkParse).use(remarkMath).parse('\\$\\alpha$'), + true ), - u('text', '`') - ]) - ]), - 'should allow backticks in math' + u('root', [u('paragraph', [u('text', '$\\alpha$')])]) + ) + } ) - t.deepEqual( - removePosition( - unified().use(remarkParse).use(remarkMath).parse('$`\\alpha`$'), - true - ), - u('root', [ - u('paragraph', [ - u( - 'inlineMath', - { - data: { - hName: 'span', - hProperties: {className: ['math', 'math-inline']}, - hChildren: [u('text', '`\\alpha`')] - } - }, - '`\\alpha`' - ) - ]) - ]), - 'should support backticks in inline math' + await t.test( + 'should *not* ignore an escaped closing dollar sign', + async function () { + assert.deepEqual( + removePosition( + unified().use(remarkParse).use(remarkMath).parse('$\\alpha\\$'), + true + ), + u('root', [ + u('paragraph', [ + u( + 'inlineMath', + { + data: { + hName: 'span', + hProperties: {className: ['math', 'math-inline']}, + hChildren: [u('text', '\\alpha\\')] + } + }, + '\\alpha\\' + ) + ]) + ]) + ) + } ) - t.deepEqual( - removePosition( - unified().use(remarkParse).use(remarkMath).parse('$$ \\alpha$ $$'), - true - ), - u('root', [ - u('paragraph', [ - u( - 'inlineMath', - { - data: { - hName: 'span', - hProperties: {className: ['math', 'math-inline']}, - hChildren: [u('text', '\\alpha$')] - } - }, - '\\alpha$' - ) + await t.test( + 'should support a escaped escape before a dollar sign', + async function () { + assert.deepEqual( + removePosition( + unified().use(remarkParse).use(remarkMath).parse('\\\\$\\alpha$'), + true + ), + u('root', [ + u('paragraph', [ + u('text', '\\'), + u( + 'inlineMath', + { + data: { + hName: 'span', + hProperties: {className: ['math', 'math-inline']}, + hChildren: [u('text', '\\alpha')] + } + }, + '\\alpha' + ) + ]) + ]) + ) + } + ) + + await t.test( + 'should ignore dollar signs in inline code (#1)', + async function () { + assert.deepEqual( + removePosition( + unified().use(remarkParse).use(remarkMath).parse('`$`\\alpha$'), + true + ), + u('root', [ + u('paragraph', [u('inlineCode', '$'), u('text', '\\alpha$')]) + ]) + ) + } + ) + + await t.test('should allow backticks in math', async function () { + assert.deepEqual( + removePosition( + unified().use(remarkParse).use(remarkMath).parse('$\\alpha`$`'), + true + ), + u('root', [ + u('paragraph', [ + u( + 'inlineMath', + { + data: { + hName: 'span', + hProperties: {className: ['math', 'math-inline']}, + hChildren: [u('text', '\\alpha`')] + } + }, + '\\alpha`' + ), + u('text', '`') + ]) ]) - ]), - 'should support a super factorial in inline math' - ) - - t.deepEqual( - removePosition( - unified().use(remarkParse).use(remarkMath).parse('$$\n\\alpha\\$\n$$'), - true - ), - u('root', [ - u( - 'math', - { - meta: null, - data: { - hName: 'div', - hProperties: {className: ['math', 'math-display']}, - hChildren: [u('text', '\\alpha\\$')] - } - }, - '\\alpha\\$' - ) - ]), - 'should support a super factorial in block math' - ) - - t.deepEqual( - removePosition( - unified() - .use(remarkParse) - .use(remarkMath) - .parse('tango\n$$\n\\alpha\n$$'), - true - ), - u('root', [ - u('paragraph', [u('text', 'tango')]), - u( - 'math', - { - meta: null, - data: { - hName: 'div', - hProperties: {className: ['math', 'math-display']}, - hChildren: [u('text', '\\alpha')] - } - }, - '\\alpha' - ) - ]), - 'should support a math block right after a paragraph' - ) - - t.deepEqual( - removePosition( - unified().use(remarkParse).use(remarkMath).parse('$$\\alpha$$'), - true - ), - u('root', [ - u('paragraph', [ + ) + }) + + await t.test('should support backticks in inline math', async function () { + assert.deepEqual( + removePosition( + unified().use(remarkParse).use(remarkMath).parse('$`\\alpha`$'), + true + ), + u('root', [ + u('paragraph', [ + u( + 'inlineMath', + { + data: { + hName: 'span', + hProperties: {className: ['math', 'math-inline']}, + hChildren: [u('text', '`\\alpha`')] + } + }, + '`\\alpha`' + ) + ]) + ]) + ) + }) + + await t.test( + 'should support a super factorial in inline math', + async function () { + assert.deepEqual( + removePosition( + unified().use(remarkParse).use(remarkMath).parse('$$ \\alpha$ $$'), + true + ), + u('root', [ + u('paragraph', [ + u( + 'inlineMath', + { + data: { + hName: 'span', + hProperties: {className: ['math', 'math-inline']}, + hChildren: [u('text', '\\alpha$')] + } + }, + '\\alpha$' + ) + ]) + ]) + ) + } + ) + + await t.test( + 'should support a super factorial in block math', + async function () { + assert.deepEqual( + removePosition( + unified() + .use(remarkParse) + .use(remarkMath) + .parse('$$\n\\alpha\\$\n$$'), + true + ), + u('root', [ + u( + 'math', + { + meta: null, + data: { + hName: 'div', + hProperties: {className: ['math', 'math-display']}, + hChildren: [u('text', '\\alpha\\$')] + } + }, + '\\alpha\\$' + ) + ]) + ) + } + ) + + await t.test( + 'should support a math block right after a paragraph', + async function () { + assert.deepEqual( + removePosition( + unified() + .use(remarkParse) + .use(remarkMath) + .parse('tango\n$$\n\\alpha\n$$'), + true + ), + u('root', [ + u('paragraph', [u('text', 'tango')]), + u( + 'math', + { + meta: null, + data: { + hName: 'div', + hProperties: {className: ['math', 'math-display']}, + hChildren: [u('text', '\\alpha')] + } + }, + '\\alpha' + ) + ]) + ) + } + ) + + await t.test( + 'should support inline math with double dollars', + async function () { + assert.deepEqual( + removePosition( + unified().use(remarkParse).use(remarkMath).parse('$$\\alpha$$'), + true + ), + u('root', [ + u('paragraph', [ + u( + 'inlineMath', + { + data: { + hName: 'span', + hProperties: {className: ['math', 'math-inline']}, + hChildren: [u('text', '\\alpha')] + } + }, + '\\alpha' + ) + ]) + ]) + ) + } + ) + + await t.test( + 'should support block math with triple dollars', + async function () { + assert.deepEqual( + removePosition( + unified().use(remarkParse).use(remarkMath).parse('$$$\n\\alpha\n$$$'), + true + ), + u('root', [ + u( + 'math', + { + meta: null, + data: { + hName: 'div', + hProperties: {className: ['math', 'math-display']}, + hChildren: [u('text', '\\alpha')] + } + }, + '\\alpha' + ) + ]) + ) + } + ) + + await t.test('should support indented block math', async function () { + assert.deepEqual( + removePosition( + unified() + .use(remarkParse) + .use(remarkMath) + .parse(' $$\n \\alpha\n $$'), + true + ), + u('root', [ u( - 'inlineMath', + 'math', { + meta: null, data: { - hName: 'span', - hProperties: {className: ['math', 'math-inline']}, - hChildren: [u('text', '\\alpha')] + hName: 'div', + hProperties: {className: ['math', 'math-display']}, + hChildren: [u('text', ' \\alpha')] } }, - '\\alpha' + ' \\alpha' ) ]) - ]), - 'should support inline math with double dollars' - ) - - t.deepEqual( - removePosition( - unified().use(remarkParse).use(remarkMath).parse('$$$\n\\alpha\n$$$'), - true - ), - u('root', [ - u( - 'math', - { - meta: null, - data: { - hName: 'div', - hProperties: {className: ['math', 'math-display']}, - hChildren: [u('text', '\\alpha')] - } - }, - '\\alpha' - ) - ]), - 'should support block math with triple dollars' - ) - - t.deepEqual( - removePosition( + ) + }) + + await t.test('should stringify inline and block math', async function () { + assert.deepEqual( unified() .use(remarkParse) + .use(remarkStringify) .use(remarkMath) - .parse(' $$\n \\alpha\n $$'), - true - ), - u('root', [ - u( - 'math', - { - meta: null, - data: { - hName: 'div', - hProperties: {className: ['math', 'math-display']}, - hChildren: [u('text', ' \\alpha')] - } - }, - ' \\alpha' - ) - ]), - 'should support indented block math' - ) - - t.deepEqual( - unified() - .use(remarkParse) - .use(remarkStringify) - .use(remarkMath) - .processSync('Math $\\alpha$\n\n$$\n\\beta+\\gamma\n$$\n') - .toString(), - 'Math $\\alpha$\n\n$$\n\\beta+\\gamma\n$$\n', - 'should stringify inline and block math' - ) - - t.deepEqual( - unified() - .use(remarkParse) - .use(remarkStringify) - .use(remarkMath, {singleDollarTextMath: false}) - .processSync('Math $\\alpha$\n\n$$\\beta+\\gamma$$\n') - .toString(), - 'Math $\\alpha$\n\n$$\\beta+\\gamma$$\n', - 'should support `singleDollarTextMath: false` (1)' - ) - - t.deepEqual( - unified() - .use(remarkParse) - .use(remarkMath, {singleDollarTextMath: false}) - .use(remarkRehype) - .use(rehypeStringify) - .processSync('Math $\\alpha$\n\n$$\\beta+\\gamma$$\n') - .toString(), - '

Math $\\alpha$

\n

\\beta+\\gamma

', - 'should support `singleDollarTextMath: false` (2)' - ) - - t.deepEqual( - unified() - .use(remarkParse) - .use(remarkStringify) - .use(remarkMath) - .processSync('> $$\n> \\alpha\\beta\n> $$\n') - .toString(), - '> $$\n> \\alpha\\beta\n> $$\n', - 'should stringify math in a blockquote' - ) - - t.deepEqual( - String(toHtml.processSync('$$just two dollars')), - '
', - 'should support an opening fence w/ meta, w/o closing fence' - ) - t.deepEqual( - String(toHtml.processSync('$$ must\n\\alpha\n$$')), - '
\\alpha
', - 'should support `meta`' - ) - t.deepEqual( - String(toHtml.processSync('$$ \n\\alpha\n$$')), - '
\\alpha
', - 'should include values after the opening fence' - ) - t.deepEqual( - String(toHtml.processSync('$$\n\\alpha\nmust $$')), - '
\\alpha\nmust $$
', - 'should not support values before the closing fence' - ) - t.deepEqual( - String(toHtml.processSync('$$\n\\alpha\n $$')), - '
\\alpha
', - 'should include values before the closing fence (except for spacing #2)' - ) - t.deepEqual( - String(toHtml.processSync('$$\n\\alpha\n$$ ')), - '
\\alpha
', - 'should exclude spacing after the closing fence' - ) - - t.deepEqual( - removePosition( + .processSync('Math $\\alpha$\n\n$$\n\\beta+\\gamma\n$$\n') + .toString(), + 'Math $\\alpha$\n\n$$\n\\beta+\\gamma\n$$\n' + ) + }) + + await t.test( + 'should support `singleDollarTextMath: false` (1)', + async function () { + assert.deepEqual( + unified() + .use(remarkParse) + .use(remarkStringify) + .use(remarkMath, {singleDollarTextMath: false}) + .processSync('Math $\\alpha$\n\n$$\\beta+\\gamma$$\n') + .toString(), + 'Math $\\alpha$\n\n$$\\beta+\\gamma$$\n' + ) + } + ) + + await t.test( + 'should support `singleDollarTextMath: false` (2)', + async function () { + assert.deepEqual( + unified() + .use(remarkParse) + .use(remarkMath, {singleDollarTextMath: false}) + .use(remarkRehype) + .use(rehypeStringify) + .processSync('Math $\\alpha$\n\n$$\\beta+\\gamma$$\n') + .toString(), + '

Math $\\alpha$

\n

\\beta+\\gamma

' + ) + } + ) + + await t.test('should stringify math in a blockquote', async function () { + assert.deepEqual( unified() .use(remarkParse) + .use(remarkStringify) .use(remarkMath) - .parse('$$\n\\alpha\n$$\n```\nbravo\n```\n'), - true - ), - u('root', [ - u( - 'math', - { - meta: null, - data: { - hName: 'div', - hProperties: {className: ['math', 'math-display']}, - hChildren: [u('text', '\\alpha')] - } - }, - '\\alpha' + .processSync('> $$\n> \\alpha\\beta\n> $$\n') + .toString(), + '> $$\n> \\alpha\\beta\n> $$\n' + ) + }) + + await t.test( + 'should support an opening fence w/ meta, w/o closing fence', + async function () { + assert.deepEqual( + String(toHtml.processSync('$$just two dollars')), + '
' + ) + } + ) + + await t.test('should support `meta`', async function () { + assert.deepEqual( + String(toHtml.processSync('$$ must\n\\alpha\n$$')), + '
\\alpha
' + ) + }) + + await t.test( + 'should include values after the opening fence', + async function () { + assert.deepEqual( + String(toHtml.processSync('$$ \n\\alpha\n$$')), + '
\\alpha
' + ) + } + ) + + await t.test( + 'should not support values before the closing fence', + async function () { + assert.deepEqual( + String(toHtml.processSync('$$\n\\alpha\nmust $$')), + '
\\alpha\nmust $$
' + ) + } + ) + + await t.test( + 'should include values before the closing fence (except for spacing #2)', + async function () { + assert.deepEqual( + String(toHtml.processSync('$$\n\\alpha\n $$')), + '
\\alpha
' + ) + } + ) + + await t.test( + 'should exclude spacing after the closing fence', + async function () { + assert.deepEqual( + String(toHtml.processSync('$$\n\\alpha\n$$ ')), + '
\\alpha
' + ) + } + ) + + await t.test('should not affect the next block', async function () { + assert.deepEqual( + removePosition( + unified() + .use(remarkParse) + .use(remarkMath) + .parse('$$\n\\alpha\n$$\n```\nbravo\n```\n'), + true ), - u('code', {lang: null, meta: null}, 'bravo') - ]), - 'should not affect the next block' - ) - - t.deepEqual( - removePosition( - unified().use(remarkParse).use(remarkMath).parse('$$\\alpha$$'), - true - ), - u('root', [ - u('paragraph', [ + u('root', [ u( - 'inlineMath', + 'math', { + meta: null, data: { - hName: 'span', - hProperties: {className: ['math', 'math-inline']}, + hName: 'div', + hProperties: {className: ['math', 'math-display']}, hChildren: [u('text', '\\alpha')] } }, '\\alpha' - ) + ), + u('code', {lang: null, meta: null}, 'bravo') ]) - ]), - 'should support two dollar signs on inline math' - ) + ) + }) - t.deepEqual( - unified() - .use(remarkStringify) - .use(remarkMath) - .stringify( + await t.test( + 'should support two dollar signs on inline math', + async function () { + assert.deepEqual( + removePosition( + unified().use(remarkParse).use(remarkMath).parse('$$\\alpha$$'), + true + ), u('root', [ - u('paragraph', [u('text', 'Math '), u('inlineMath', '\\alpha')]), - u('math', '\\beta+\\gamma') + u('paragraph', [ + u( + 'inlineMath', + { + data: { + hName: 'span', + hProperties: {className: ['math', 'math-inline']}, + hChildren: [u('text', '\\alpha')] + } + }, + '\\alpha' + ) + ]) ]) ) - .toString(), - 'Math $\\alpha$\n\n$$\n\\beta+\\gamma\n$$\n', - 'should stringify a tree' - ) - - t.deepEqual( - unified() - .use(remarkParse) - .use(remarkStringify) - .use(remarkMath) - .processSync('$$\\alpha$$') - .toString(), - '$\\alpha$\n', - 'should stringify inline math with double dollars using one dollar by default' - ) - - t.deepEqual( - unified() - .use(remarkParse) - .use(remarkStringify) - .use(remarkMath) - .processSync('$$\\alpha$$') - .toString(), - '$\\alpha$\n', - 'should stringify inline math with double dollars using one dollar' - ) - - t.deepEqual( - String(toHtml.processSync('$1+1 = 2$')), - '

1+1 = 2

', - 'markdown-it-katex#01' - ) - t.deepEqual( - String(toHtml.processSync('$$1+1 = 2$$')), - '

1+1 = 2

', - 'markdown-it-katex#02 (deviation)' - ) - t.deepEqual( - String(toHtml.processSync('foo$1+1 = 2$bar')), - '

foo1+1 = 2bar

', - 'markdown-it-katex#03: no whitespace before and after is fine' - ) - t.deepEqual( - String(toHtml.processSync('foo$-1+1 = 2$bar')), - '

foo-1+1 = 2bar

', - 'markdown-it-katex#04: even when it starts with a negative sign' - ) - t.deepEqual( - String(toHtml.processSync('aaa $$ bbb')), - '

aaa $$ bbb

', - 'markdown-it-katex#05: shouldn’t render empty content' - ) - t.deepEqual( - String(toHtml.processSync('aaa $5.99 bbb')), - '

aaa $5.99 bbb

', - 'markdown-it-katex#06: should require a closing delimiter' - ) - t.deepEqual( - String(toHtml.processSync('foo $1+1\n\n= 2$ bar')), - '

foo $1+1

\n

= 2$ bar

', - 'markdown-it-katex#07: paragraph break in inline math is not allowed' - ) - t.deepEqual( - String(toHtml.processSync('foo $1 *i* 1$ bar')), - '

foo 1 *i* 1 bar

', - 'markdown-it-katex#08: inline math with apparent markup should not be processed' - ) - t.deepEqual( - String(toHtml.processSync(' $$\n 1+1 = 2\n $$')), - '
1+1 = 2
', - 'markdown-it-katex#09: block math can be indented up to 3 spaces' - ) - t.deepEqual( - String(toHtml.processSync(' $$\n 1+1 = 2\n $$')), - '
$$\n1+1 = 2\n$$\n
', - 'markdown-it-katex#10: …but 4 means a code block' - ) - t.deepEqual( - String(toHtml.processSync('foo $1 + 1\n= 2$ bar')), - '

foo 1 + 1\n= 2 bar

', - 'markdown-it-katex#11: multiline inline math' - ) - t.deepEqual( - String(toHtml.processSync('$$\n\n 1\n+ 1\n\n= 2\n\n$$')), - '
\n 1\n+ 1\n\n= 2\n
', - 'markdown-it-katex#12: multiline display math' - ) - t.deepEqual( - String(toHtml.processSync('$n$-th order')), - '

n-th order

', - 'markdown-it-katex#13: text can immediately follow inline math' - ) - t.deepEqual( - String(toHtml.processSync('$$\n1+1 = 2')), - '
1+1 = 2
', - 'markdown-it-katex#14: display math self-closes at the end of document' - ) - t.deepEqual( - String(toHtml.processSync('* $1+1 = 2$\n* $$\n 1+1 = 2\n $$')), - '
    \n
  • 1+1 = 2
  • \n
  • \n
    1+1 = 2
    \n
  • \n
', - 'markdown-it-katex#15: display and inline math can appear in lists' - ) - t.deepEqual( - String(toHtml.processSync('$$1+1 = 2$$')), - '

1+1 = 2

', - 'markdown-it-katex#16: display math can be written in one line (deviation)' - ) - // To do: this is broken. - t.deepEqual( - String(toHtml.processSync('$$\n[\n[1, 2]\n[3, 4]\n]\n$$')), - '
[\n[1, 2]\n[3, 4]\n]
', - 'markdown-it-katex#17: …or on multiple lines with expression starting and ending on delimited lines (deviation)' - ) - t.deepEqual( - String(toHtml.processSync('Foo \\$1$ bar\n\\$\\$\n1\n\\$\\$')), - '

Foo $1 bar\n\\$\n1\n$$

', - 'markdown-it-katex#18: escaped delimiters should not render math (deviated)' - ) - t.deepEqual( - String( - toHtml.processSync('Thus, $20,000 and USD$30,000 won’t parse as math.') - ), - '

Thus, 20,000 and USD30,000 won’t parse as math.

', - 'markdown-it-katex#19: numbers can not follow closing inline math (deviated)' - ) - t.deepEqual( - String(toHtml.processSync('It is 2$ for a can of soda, not 1$.')), - '

It is 2 for a can of soda, not 1.

', - 'markdown-it-katex#20: require non whitespace to right of opening inline math (deviated)' - ) - t.deepEqual( - String( - toHtml.processSync('I’ll give $20 today, if you give me more $ tomorrow.') - ), - '

I’ll give 20 today, if you give me more tomorrow.

', - 'markdown-it-katex#21: require non whitespace to left of closing inline math (deviated)' - ) - // #22 “inline blockmath is not (currently) registered” <-- we do support it! - t.deepEqual( - String(toHtml.processSync('Money adds: $\\$X + \\$Y = \\$Z$.')), - '

Money adds: \\X + $Y = $Z$.

', - 'markdown-it-katex#23: escaped delimiters in math mode (deviated)' - ) - t.deepEqual( - String( - toHtml.processSync( - 'Weird-o: $\\displaystyle{\\begin{pmatrix} \\$ & 1\\\\\\$ \\end{pmatrix}}$.' - ) - ), - '

Weird-o: \\displaystyle{\\begin{pmatrix} \\ & 1\\$ \\end{pmatrix}}$.

', - 'markdown-it-katex#24: multiple escaped delimiters in math module (deviated)' - ) - - t.end() + } + ) + + await t.test('should stringify a tree', async function () { + assert.deepEqual( + unified() + .use(remarkStringify) + .use(remarkMath) + .stringify( + u('root', [ + u('paragraph', [u('text', 'Math '), u('inlineMath', '\\alpha')]), + u('math', '\\beta+\\gamma') + ]) + ) + .toString(), + 'Math $\\alpha$\n\n$$\n\\beta+\\gamma\n$$\n' + ) + }) + + await t.test( + 'should stringify inline math with double dollars using one dollar by default', + async function () { + assert.deepEqual( + unified() + .use(remarkParse) + .use(remarkStringify) + .use(remarkMath) + .processSync('$$\\alpha$$') + .toString(), + '$\\alpha$\n' + ) + } + ) + + await t.test( + 'should stringify inline math with double dollars using one dollar', + async function () { + assert.deepEqual( + unified() + .use(remarkParse) + .use(remarkStringify) + .use(remarkMath) + .processSync('$$\\alpha$$') + .toString(), + '$\\alpha$\n' + ) + } + ) + + await t.test('should do markdown-it-katex#01', async function () { + assert.deepEqual( + String(toHtml.processSync('$1+1 = 2$')), + '

1+1 = 2

' + ) + }) + + await t.test('should do markdown-it-katex#02 (deviation)', async function () { + assert.deepEqual( + String(toHtml.processSync('$$1+1 = 2$$')), + '

1+1 = 2

' + ) + }) + + await t.test( + 'should do markdown-it-katex#03: no whitespace before and after is fine', + async function () { + assert.deepEqual( + String(toHtml.processSync('foo$1+1 = 2$bar')), + '

foo1+1 = 2bar

' + ) + } + ) + + await t.test( + 'should do markdown-it-katex#04: even when it starts with a negative sign', + async function () { + assert.deepEqual( + String(toHtml.processSync('foo$-1+1 = 2$bar')), + '

foo-1+1 = 2bar

' + ) + } + ) + + await t.test( + 'should do markdown-it-katex#05: shouldn’t render empty content', + async function () { + assert.deepEqual( + String(toHtml.processSync('aaa $$ bbb')), + '

aaa $$ bbb

' + ) + } + ) + + await t.test( + 'should do markdown-it-katex#06: should require a closing delimiter', + async function () { + assert.deepEqual( + String(toHtml.processSync('aaa $5.99 bbb')), + '

aaa $5.99 bbb

' + ) + } + ) + + await t.test( + 'should do markdown-it-katex#07: paragraph break in inline math is not allowed', + async function () { + assert.deepEqual( + String(toHtml.processSync('foo $1+1\n\n= 2$ bar')), + '

foo $1+1

\n

= 2$ bar

' + ) + } + ) + + await t.test( + 'should do markdown-it-katex#08: inline math with apparent markup should not be processed', + async function () { + assert.deepEqual( + String(toHtml.processSync('foo $1 *i* 1$ bar')), + '

foo 1 *i* 1 bar

' + ) + } + ) + + await t.test( + 'should do markdown-it-katex#09: block math can be indented up to 3 spaces', + async function () { + assert.deepEqual( + String(toHtml.processSync(' $$\n 1+1 = 2\n $$')), + '
1+1 = 2
' + ) + } + ) + + await t.test( + 'should do markdown-it-katex#10: …but 4 means a code block', + async function () { + assert.deepEqual( + String(toHtml.processSync(' $$\n 1+1 = 2\n $$')), + '
$$\n1+1 = 2\n$$\n
' + ) + } + ) + + await t.test( + 'should do markdown-it-katex#11: multiline inline math', + async function () { + assert.deepEqual( + String(toHtml.processSync('foo $1 + 1\n= 2$ bar')), + '

foo 1 + 1\n= 2 bar

' + ) + } + ) + + await t.test( + 'should do markdown-it-katex#12: multiline display math', + async function () { + assert.deepEqual( + String(toHtml.processSync('$$\n\n 1\n+ 1\n\n= 2\n\n$$')), + '
\n 1\n+ 1\n\n= 2\n
' + ) + } + ) + + await t.test( + 'should do markdown-it-katex#13: text can immediately follow inline math', + async function () { + assert.deepEqual( + String(toHtml.processSync('$n$-th order')), + '

n-th order

' + ) + } + ) + + await t.test( + 'should do markdown-it-katex#14: display math self-closes at the end of document', + async function () { + assert.deepEqual( + String(toHtml.processSync('$$\n1+1 = 2')), + '
1+1 = 2
' + ) + } + ) + + await t.test( + 'should do markdown-it-katex#15: display and inline math can appear in lists', + async function () { + assert.deepEqual( + String(toHtml.processSync('* $1+1 = 2$\n* $$\n 1+1 = 2\n $$')), + '
    \n
  • 1+1 = 2
  • \n
  • \n
    1+1 = 2
    \n
  • \n
' + ) + } + ) + + await t.test( + 'should do markdown-it-katex#16: display math can be written in one line (deviation)', + async function () { + assert.deepEqual( + String(toHtml.processSync('$$1+1 = 2$$')), + '

1+1 = 2

' + ) + } + ) + + await t.test( + 'should do markdown-it-katex#17: …or on multiple lines with expression starting and ending on delimited lines (deviation)', + async function () { + // To do: this is broken. + assert.deepEqual( + String(toHtml.processSync('$$\n[\n[1, 2]\n[3, 4]\n]\n$$')), + '
[\n[1, 2]\n[3, 4]\n]
' + ) + } + ) + + await t.test( + 'should do markdown-it-katex#18: escaped delimiters should not render math (deviated)', + async function () { + assert.deepEqual( + String(toHtml.processSync('Foo \\$1$ bar\n\\$\\$\n1\n\\$\\$')), + '

Foo $1 bar\n\\$\n1\n$$

' + ) + } + ) + + await t.test( + 'should do markdown-it-katex#19: numbers can not follow closing inline math (deviated)', + async function () { + assert.deepEqual( + String( + toHtml.processSync( + 'Thus, $20,000 and USD$30,000 won’t parse as math.' + ) + ), + '

Thus, 20,000 and USD30,000 won’t parse as math.

' + ) + } + ) + + await t.test( + 'should do markdown-it-katex#20: require non whitespace to right of opening inline math (deviated)', + async function () { + assert.deepEqual( + String(toHtml.processSync('It is 2$ for a can of soda, not 1$.')), + '

It is 2 for a can of soda, not 1.

' + ) + } + ) + + await t.test( + 'should do markdown-it-katex#21: require non whitespace to left of closing inline math (deviated)', + async function () { + assert.deepEqual( + String( + toHtml.processSync( + 'I’ll give $20 today, if you give me more $ tomorrow.' + ) + ), + '

I’ll give 20 today, if you give me more tomorrow.

' + ) + } + ) + + await t.test( + 'should do markdown-it-katex#23: escaped delimiters in math mode (deviated)', + async function () { + // #22 “inline blockmath is not (currently) registered” <-- we do support it! + assert.deepEqual( + String(toHtml.processSync('Money adds: $\\$X + \\$Y = \\$Z$.')), + '

Money adds: \\X + $Y = $Z$.

' + ) + } + ) + + await t.test( + 'should do markdown-it-katex#24: multiple escaped delimiters in math module (deviated)', + async function () { + assert.deepEqual( + String( + toHtml.processSync( + 'Weird-o: $\\displaystyle{\\begin{pmatrix} \\$ & 1\\\\\\$ \\end{pmatrix}}$.' + ) + ), + '

Weird-o: \\displaystyle{\\begin{pmatrix} \\ & 1\\$ \\end{pmatrix}}$.

' + ) + } + ) }) From fc32531aafa7c0658b78b7ec9d5e086b340860ad Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 18 Sep 2023 14:46:22 +0200 Subject: [PATCH 88/98] Change to use ``, update `@types/{hast,mdast}`, etc Importantly, this switches from generating `span` and `div` to `` (and `
`).
This is due to https://github.com/syntax-tree/mdast-util-math/releases/tag/3.0.0,
and solves #77.

It also updates all utilities, plugins, and types.

Closes GH-77.
---
 package.json                                  |  16 +-
 packages/rehype-katex/index.js                |   1 +
 packages/rehype-katex/package.json            |   8 +-
 packages/rehype-katex/readme.md               |   8 +-
 packages/rehype-katex/test.js                 |  45 +--
 packages/rehype-mathjax/package.json          |  10 +-
 packages/rehype-mathjax/readme.md             |   8 +-
 .../test/fixture/markdown-svg.html            |   4 +-
 packages/rehype-mathjax/test/index.js         |   1 +
 packages/remark-html-katex/index.js           |  13 +-
 packages/remark-html-katex/package.json       |  10 +-
 packages/remark-html-katex/test.js            |  25 +-
 packages/remark-math/index.js                 |   1 +
 packages/remark-math/package.json             |   8 +-
 packages/remark-math/readme.md                |   2 +-
 packages/remark-math/test.js                  | 349 +++++++++++-------
 readme.md                                     |   4 +-
 17 files changed, 306 insertions(+), 207 deletions(-)

diff --git a/package.json b/package.json
index 12ef205..168ef95 100644
--- a/package.json
+++ b/package.json
@@ -26,20 +26,20 @@
     "@types/node": "^20.0.0",
     "c8": "^8.0.0",
     "prettier": "^3.0.0",
-    "rehype-parse": "^8.0.0",
-    "rehype-stringify": "^9.0.0",
+    "rehype-parse": "^9.0.0",
+    "rehype-stringify": "^10.0.0",
     "remark-cli": "^11.0.0",
     "remark-html": "^15.0.0",
-    "remark-parse": "^10.0.0",
+    "remark-parse": "^11.0.0",
     "remark-preset-wooorm": "^9.0.0",
     "remark-rehype": "^10.0.0",
-    "remark-stringify": "^10.0.0",
-    "to-vfile": "^7.0.0",
+    "remark-stringify": "^11.0.0",
+    "to-vfile": "^8.0.0",
     "type-coverage": "^2.0.0",
     "typescript": "^5.0.0",
-    "unified": "^10.0.0",
-    "unist-builder": "^3.0.0",
-    "unist-util-remove-position": "^4.0.0",
+    "unified": "^11.0.0",
+    "unist-builder": "^4.0.0",
+    "unist-util-remove-position": "^5.0.0",
     "xo": "^0.56.0"
   },
   "scripts": {
diff --git a/packages/rehype-katex/index.js b/packages/rehype-katex/index.js
index 75188f0..def3bbd 100644
--- a/packages/rehype-katex/index.js
+++ b/packages/rehype-katex/index.js
@@ -82,6 +82,7 @@ export default function rehypeKatex(options) {
       }
 
       const root = fromHtmlIsomorphic(result, {fragment: true})
+      // To do: cast content.
       // @ts-expect-error: assume no `doctypes` in KaTeX result.
       element.children = root.children
     })
diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json
index bb3203e..d6326f9 100644
--- a/packages/rehype-katex/package.json
+++ b/packages/rehype-katex/package.json
@@ -38,12 +38,12 @@
     "index.js"
   ],
   "dependencies": {
-    "@types/hast": "^2.0.0",
+    "@types/hast": "^3.0.0",
     "@types/katex": "^0.16.0",
-    "hast-util-from-html-isomorphic": "^1.0.0",
-    "hast-util-to-text": "^3.1.0",
+    "hast-util-from-html-isomorphic": "^2.0.0",
+    "hast-util-to-text": "^4.0.0",
     "katex": "^0.16.0",
-    "unist-util-visit": "^4.0.0"
+    "unist-util-visit": "^5.0.0"
   },
   "scripts": {
     "test-api": "node --conditions development test.js",
diff --git a/packages/rehype-katex/readme.md b/packages/rehype-katex/readme.md
index 7994f39..845398a 100644
--- a/packages/rehype-katex/readme.md
+++ b/packages/rehype-katex/readme.md
@@ -77,8 +77,8 @@ Say we have the following file `example.html`:
 
 ```html
 

- Lift(L) can be determined by Lift Coefficient - (C_L) like the following equation. + Lift(L) can be determined by Lift Coefficient + (C_L) like the following equation.

@@ -121,8 +121,8 @@ Now running `node example.js` yields:

- Lift() can be determined by Lift Coefficient - () like the following equation. + Lift() can be determined by Lift Coefficient + () like the following equation.

diff --git a/packages/rehype-katex/test.js b/packages/rehype-katex/test.js index 6155f83..f2833c4 100644 --- a/packages/rehype-katex/test.js +++ b/packages/rehype-katex/test.js @@ -18,7 +18,7 @@ test('rehype-katex', async function (t) { .use(rehypeStringify) .processSync( [ - '

Inline math \\alpha.

', + '

Inline math \\alpha.

', '

Block math:

', '
\\gamma
' ].join('\n') @@ -29,9 +29,9 @@ test('rehype-katex', async function (t) { .use(rehypeStringify) .processSync( [ - '

Inline math ' + + '

Inline math ' + katex.renderToString('\\alpha') + - '.

', + '.

', '

Block math:

', '
' + katex.renderToString('\\gamma', {displayMode: true}) + @@ -47,6 +47,7 @@ test('rehype-katex', async function (t) { unified() .use(remarkParse) .use(remarkMath) + // @ts-expect-error: to do: remove when `remark-rehype` is released. .use(remarkRehype) .use(rehypeKatex) .use(rehypeStringify) @@ -67,13 +68,13 @@ test('rehype-katex', async function (t) { .use(rehypeStringify) .processSync( [ - '

Inline math ' + + '

Inline math ' + katex.renderToString('\\alpha') + - '.

', + '.

', '

Block math:

', - '
' + + '
' +
               katex.renderToString('\\gamma', {displayMode: true}) +
-              '
' + '
' ].join('\n') ) .toString() @@ -89,16 +90,16 @@ test('rehype-katex', async function (t) { .use(rehypeKatex) .use(rehypeStringify) .processSync( - '

Double math \\alpha.

' + '

Double math \\alpha.

' ) .toString(), unified() .use(rehypeParse, {fragment: true}) .use(rehypeStringify) .processSync( - '

Double math ' + + '

Double math ' + katex.renderToString('\\alpha', {displayMode: true}) + - '.

' + '.

' ) .toString() ) @@ -113,15 +114,15 @@ test('rehype-katex', async function (t) { .use(rehypeParse, {fragment: true}) .use(rehypeKatex, {macros}) .use(rehypeStringify) - .processSync('\\RR') + .processSync('\\RR') .toString(), unified() .use(rehypeParse, {fragment: true}) .use(rehypeStringify) .processSync( - '' + + '' + katex.renderToString('\\RR', {macros}) + - '' + '' ) .toString() ) @@ -133,18 +134,18 @@ test('rehype-katex', async function (t) { .use(rehypeParse, {fragment: true}) .use(rehypeKatex, {errorColor: 'orange'}) .use(rehypeStringify) - .processSync('\\alpa') + .processSync('\\alpa') .toString(), unified() .use(rehypeParse, {fragment: true}) .use(rehypeStringify) .processSync( - '' + + '' + katex.renderToString('\\alpa', { throwOnError: false, errorColor: 'orange' }) + - '' + '' ) .toString() ) @@ -157,7 +158,7 @@ test('rehype-katex', async function (t) { .use(rehypeKatex) .use(rehypeStringify) .processSync( - '

Lorem

\n

\\alpa

' + '

Lorem

\n

\\alpa

' ) .messages.map(String), [ @@ -175,7 +176,7 @@ test('rehype-katex', async function (t) { .use(rehypeKatex, {throwOnError: true}) .use(rehypeStringify) .processSync( - '

Lorem

\n

\\alpa

' + '

Lorem

\n

\\alpa

' ) } catch (error_) { const error = /** @type {Error} */ (error_) @@ -193,13 +194,13 @@ test('rehype-katex', async function (t) { .use(rehypeParse, {fragment: true}) .use(rehypeKatex, {errorColor: 'orange', strict: 'ignore'}) .use(rehypeStringify) - .processSync('ê&') + .processSync('ê&') .toString(), unified() .use(rehypeParse, {fragment: true}) .use(rehypeStringify) .processSync( - 'ê&' + 'ê&' ) .toString() ) @@ -237,14 +238,14 @@ test('rehype-katex', async function (t) { .use(rehypeKatex) .use(rehypeStringify) .processSync( - '\\begin{split}\n\\end{{split}}\n' + '\\begin{split}\n\\end{{split}}\n' ) .toString(), unified() .use(rehypeParse, {fragment: true}) .use(rehypeStringify) .processSync( - '\\begin{split}\n\\end{{split}}\n' + '\\begin{split}\n\\end{{split}}\n' ) .toString() ) diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index d82f187..dc3782d 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -48,14 +48,14 @@ "./lib/create-adaptor.js": "./lib/create-adaptor.browser.js" }, "dependencies": { - "@types/hast": "^2.0.0", + "@types/hast": "^3.0.0", "@types/mathjax": "^0.0.37", - "hast-util-from-dom": "^4.0.0", - "hast-util-to-text": "^3.1.0", + "hast-util-from-dom": "^5.0.0", + "hast-util-to-text": "^4.0.0", "jsdom": "^21.0.0", "mathjax-full": "^3.0.0", - "unified": "^10.0.0", - "unist-util-visit": "^4.0.0" + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0" }, "devDependencies": { "@types/jsdom": "^21.0.0" diff --git a/packages/rehype-mathjax/readme.md b/packages/rehype-mathjax/readme.md index a607754..71e89c0 100644 --- a/packages/rehype-mathjax/readme.md +++ b/packages/rehype-mathjax/readme.md @@ -77,8 +77,8 @@ Say we have the following file `example.html`: ```html

- Lift(L) can be determined by Lift Coefficient - (C_L) like the following equation. + Lift(L) can be determined by Lift Coefficient + (C_L) like the following equation.

@@ -108,8 +108,8 @@ Now running `node example.js` yields: ```html

- Lift() can be determined by Lift Coefficient - () like the following equation. + Lift() can be determined by Lift Coefficient + () like the following equation.

diff --git a/packages/rehype-mathjax/test/fixture/markdown-svg.html b/packages/rehype-mathjax/test/fixture/markdown-svg.html index 2f30c68..10a53c3 100644 --- a/packages/rehype-mathjax/test/fixture/markdown-svg.html +++ b/packages/rehype-mathjax/test/fixture/markdown-svg.html @@ -1,6 +1,6 @@ -

Inline math .

+

Inline math .

Block math:

-
From 3aab0e142b9aeeb567bf21255da4c3b8217a9c42 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 18 Sep 2023 14:50:28 +0200 Subject: [PATCH 89/98] Deprecate `remark-html-katex` --- package.json | 1 - packages/remark-html-katex/.npmrc | 2 - packages/remark-html-katex/index.js | 72 ---------- packages/remark-html-katex/package.json | 42 ------ packages/remark-html-katex/readme.md | 26 +--- packages/remark-html-katex/test.js | 174 ----------------------- packages/remark-html-katex/tsconfig.json | 3 - 7 files changed, 4 insertions(+), 316 deletions(-) delete mode 100644 packages/remark-html-katex/.npmrc delete mode 100644 packages/remark-html-katex/index.js delete mode 100644 packages/remark-html-katex/package.json delete mode 100644 packages/remark-html-katex/test.js delete mode 100644 packages/remark-html-katex/tsconfig.json diff --git a/package.json b/package.json index 168ef95..b337678 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,6 @@ ], "type": "module", "workspaces": [ - "packages/remark-html-katex/", "packages/rehype-katex/", "packages/remark-math/", "packages/rehype-mathjax/" diff --git a/packages/remark-html-katex/.npmrc b/packages/remark-html-katex/.npmrc deleted file mode 100644 index 3757b30..0000000 --- a/packages/remark-html-katex/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -ignore-scripts=true -package-lock=false diff --git a/packages/remark-html-katex/index.js b/packages/remark-html-katex/index.js deleted file mode 100644 index d17ccbd..0000000 --- a/packages/remark-html-katex/index.js +++ /dev/null @@ -1,72 +0,0 @@ -/** - * @typedef {import('mdast').Root} Root - * @typedef {import('katex').KatexOptions} Options - */ - -import {visit} from 'unist-util-visit' -import {removePosition} from 'unist-util-remove-position' -import katex from 'katex' -import {unified} from 'unified' -import rehypeParse from 'rehype-parse' - -const parseHtml = unified().use(rehypeParse, {fragment: true}) - -const source = 'remark-html-katex' - -/** - * Plugin to transform `inlineMath` and `math` nodes with KaTeX for - * `remark-html`. - * - * @type {import('unified').Plugin<[Options?]|void[], Root>} - */ -export default function remarkHtmlKatex(options = {}) { - const throwOnError = options.throwOnError || false - - return (tree, file) => { - visit(tree, (node) => { - if (node.type === 'inlineMath' || node.type === 'math') { - const displayMode = node.type === 'math' - /** @type {string} */ - let result - - try { - result = katex.renderToString( - node.value, - Object.assign({}, options, { - displayMode, - throwOnError: true - }) - ) - } catch (error_) { - const error = /** @type {Error} */ (error_) - const fn = throwOnError ? 'fail' : 'message' - const origin = [source, error.name.toLowerCase()].join(':') - - file[fn](error.message, node.position, origin) - - result = katex.renderToString( - node.value, - Object.assign({}, options, { - displayMode, - throwOnError: false, - strict: 'ignore' - }) - ) - } - - const tree = parseHtml.parse(result) - removePosition(tree) - - if (node.type === 'inlineMath') { - const data = node.data || (node.data = {}) - // @ts-expect-error: fine. - data.hChildren = tree.children - } else { - // @ts-expect-error: fine. - const scope = node.data.hChildren[0] - scope.children = tree.children - } - } - }) - } -} diff --git a/packages/remark-html-katex/package.json b/packages/remark-html-katex/package.json deleted file mode 100644 index f5929f8..0000000 --- a/packages/remark-html-katex/package.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "remark-html-katex", - "version": "4.0.1", - "description": "Legacy remark plugin to transform math with KaTeX — please use `rehype-katex` instead", - "license": "MIT", - "keywords": [], - "repository": "https://github.com/remarkjs/remark-math/tree/main/packages/remark-html-katex", - "bugs": "https://github.com/remarkjs/remark-math/issues", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "author": "Junyoung Choi (https://rokt33r.github.io)", - "contributors": [ - "Junyoung Choi (https://rokt33r.github.io)", - "Titus Wormer (https://wooorm.com)" - ], - "sideEffects": false, - "type": "module", - "main": "index.js", - "types": "index.d.ts", - "files": [ - "index.d.ts", - "index.js" - ], - "dependencies": { - "@types/katex": "^0.16.0", - "@types/mdast": "^4.0.0", - "katex": "^0.16.0", - "rehype-parse": "^9.0.0", - "unified": "^11.0.0", - "unist-util-remove-position": "^5.0.0", - "unist-util-visit": "^5.0.0" - }, - "scripts": { - "test-api": "node --conditions development test.js", - "test": "npm run build && npm run test-api" - }, - "xo": { - "prettier": true - } -} diff --git a/packages/remark-html-katex/readme.md b/packages/remark-html-katex/readme.md index a2d4524..15682a6 100644 --- a/packages/remark-html-katex/readme.md +++ b/packages/remark-html-katex/readme.md @@ -1,26 +1,8 @@ # remark-html-katex -**Stability: Legacy**. -This package is no longer recommended for use. -It’s still covered by semantic-versioning guarantees and not yet deprecated, -but use of this package should be avoided. -Please use `remark-rehype` to move from remark (markdown) to rehype (HTML) -and then replace `remark-html-katex` with [`rehype-katex`][rehype-katex] -or [`rehype-mathjax`][rehype-mathjax]. +Deprecated. -Legacy [documentation for this package](https://github.com/remarkjs/remark-math/tree/52b145855df58e0cd85c2171452f192181cfb43d/packages/remark-html-katex) -is still available in Git. +[Git][] is still intact and previous versions can still be used without +warnings. -## License - -[MIT][license] © [Junyoung Choi][author] - - - -[license]: https://github.com/remarkjs/remark-math/blob/main/license - -[author]: https://rokt33r.github.io - -[rehype-katex]: https://github.com/remarkjs/remark-math/tree/main/packages/rehype-katex - -[rehype-mathjax]: https://github.com/remarkjs/remark-math/tree/main/packages/rehype-mathjax +[git]: https://github.com/remarkjs/remark-math/tree/6d9970e/packages/remark-html-katex diff --git a/packages/remark-html-katex/test.js b/packages/remark-html-katex/test.js deleted file mode 100644 index 1f07b4a..0000000 --- a/packages/remark-html-katex/test.js +++ /dev/null @@ -1,174 +0,0 @@ -import assert from 'node:assert/strict' -import test from 'node:test' -import katex from 'katex' -import {unified} from 'unified' -import remarkParse from 'remark-parse' -import rehypeParse from 'rehype-parse' -import rehypeStringify from 'rehype-stringify' -import remarkHtml from 'remark-html' -import remarkMath from '../remark-math/index.js' -import remarkHtmlKatex from './index.js' - -test('remark-html-katex', async function (t) { - await t.test('should transform math with katex', async function () { - assert.deepEqual( - unified() - .use(remarkParse) - .use(remarkMath) - .use(remarkHtmlKatex) - // @ts-expect-error: to do: remove when `remark-html` is released. - .use(remarkHtml, {sanitize: false}) - .processSync( - [ - 'Inline math $\\alpha$.', - '', - 'Block math:', - '', - '$$', - '\\gamma', - '$$' - ].join('\n') - ) - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - [ - '

Inline math ' + - katex.renderToString('\\alpha') + - '.

', - '

Block math:

', - '
' +
-              katex.renderToString('\\gamma', {displayMode: true}) +
-              '
', - '' - ].join('\n') - ) - .toString() - ) - }) - - await t.test('should support `macros`', async function () { - const macros = {'\\RR': '\\mathbb{R}'} - - assert.deepEqual( - unified() - .use(remarkParse) - .use(remarkMath) - .use(remarkHtmlKatex, {macros}) - // @ts-expect-error: to do: remove when `remark-html` is released. - .use(remarkHtml, {sanitize: false}) - .processSync('$\\RR$') - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - '

' + - katex.renderToString('\\RR', {macros}) + - '

\n' - ) - .toString() - ) - }) - - await t.test('should support `errorColor`', async function () { - assert.deepEqual( - unified() - .use(remarkParse) - .use(remarkMath) - .use(remarkHtmlKatex, {errorColor: 'orange'}) - // @ts-expect-error: to do: remove when `remark-html` is released. - .use(remarkHtml, {sanitize: false}) - .processSync('$\\alpa$') - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - '

' + - katex.renderToString('\\alpa', { - throwOnError: false, - errorColor: 'orange' - }) + - '

\n' - ) - .toString() - ) - }) - - await t.test('should create a message for errors', async function () { - assert.deepEqual( - unified() - .use(remarkParse) - .use(remarkMath) - .use(remarkHtmlKatex) - // @ts-expect-error: to do: remove when `remark-html` is released. - .use(remarkHtml, {sanitize: false}) - .processSync('Lorem\n$\\alpa$') - .messages.map(String), - [ - '2:1-2:8: KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲' - ] - ) - }) - - await t.test( - 'should throw an error if `throwOnError: true`', - async function () { - try { - unified() - .use(remarkParse) - .use(remarkMath) - .use(remarkHtmlKatex, {throwOnError: true}) - // @ts-expect-error: to do: remove when `remark-html` is released. - .use(remarkHtml, {sanitize: false}) - .processSync('Lorem\n$\\alpa$') - } catch (error_) { - const error = /** @type {Error} */ (error_) - assert.equal( - error.message, - 'KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲' - ) - } - } - ) - - await t.test('should support `strict: ignore`', async function () { - assert.deepEqual( - unified() - .use(remarkParse) - .use(remarkMath) - .use(remarkHtmlKatex, {errorColor: 'orange', strict: 'ignore'}) - // @ts-expect-error: to do: remove when `remark-html` is released. - .use(remarkHtml, {sanitize: false}) - .processSync('$ê&$') - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - '

ê&

\n' - ) - .toString() - ) - }) - - await t.test('should support generated nodes', async function () { - const pipeline = unified() - .use(remarkHtmlKatex, {errorColor: 'orange', strict: 'ignore'}) - // @ts-expect-error: to do: remove when `remark-html` is released. - .use(remarkHtml, {sanitize: false}) - - assert.deepEqual( - pipeline.stringify( - pipeline.runSync({ - type: 'root', - children: [{type: 'inlineMath', value: '\\alpha'}] - }) - ), - '
' + katex.renderToString('\\alpha') + '
\n' - ) - }) -}) diff --git a/packages/remark-html-katex/tsconfig.json b/packages/remark-html-katex/tsconfig.json deleted file mode 100644 index 4082f16..0000000 --- a/packages/remark-html-katex/tsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../tsconfig.json" -} From 3658eddeaa4b3e5b7765ccd1e85e9666ecf65838 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 18 Sep 2023 14:50:38 +0200 Subject: [PATCH 90/98] Update `jsdom` --- packages/rehype-mathjax/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index dc3782d..44caa6f 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -52,7 +52,7 @@ "@types/mathjax": "^0.0.37", "hast-util-from-dom": "^5.0.0", "hast-util-to-text": "^4.0.0", - "jsdom": "^21.0.0", + "jsdom": "^22.0.0", "mathjax-full": "^3.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0" From 2cc07437c3863c4a4bba41fe2d4aa00a6d41067e Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 18 Sep 2023 16:36:50 +0200 Subject: [PATCH 91/98] Refactor code-style --- package.json | 3 - packages/rehype-katex/index.js | 86 ++- packages/rehype-katex/package.json | 3 +- packages/rehype-katex/test.js | 387 +++++----- packages/rehype-mathjax/browser.js | 8 +- packages/rehype-mathjax/chtml.js | 4 +- packages/rehype-mathjax/lib/create-plugin.js | 240 +++++-- .../rehype-mathjax/lib/create-renderer.js | 29 +- packages/rehype-mathjax/svg.js | 9 +- packages/rehype-mathjax/test/index.js | 330 +++++---- packages/remark-math/index.js | 52 +- packages/remark-math/readme.md | 6 +- packages/remark-math/test.js | 676 +++++++++--------- tsconfig.json | 2 +- 14 files changed, 1007 insertions(+), 828 deletions(-) diff --git a/package.json b/package.json index b337678..0409100 100644 --- a/package.json +++ b/package.json @@ -28,16 +28,13 @@ "rehype-parse": "^9.0.0", "rehype-stringify": "^10.0.0", "remark-cli": "^11.0.0", - "remark-html": "^15.0.0", "remark-parse": "^11.0.0", "remark-preset-wooorm": "^9.0.0", "remark-rehype": "^10.0.0", "remark-stringify": "^11.0.0", - "to-vfile": "^8.0.0", "type-coverage": "^2.0.0", "typescript": "^5.0.0", "unified": "^11.0.0", - "unist-builder": "^4.0.0", "unist-util-remove-position": "^5.0.0", "xo": "^0.56.0" }, diff --git a/packages/rehype-katex/index.js b/packages/rehype-katex/index.js index def3bbd..7f0fe78 100644 --- a/packages/rehype-katex/index.js +++ b/packages/rehype-katex/index.js @@ -1,33 +1,50 @@ /** + * @typedef {import('hast').ElementContent} ElementContent * @typedef {import('hast').Root} Root + * * @typedef {import('katex').KatexOptions} Options + * + * @typedef {import('vfile').VFile} VFile */ +import {fromHtmlIsomorphic} from 'hast-util-from-html-isomorphic' +import {toText} from 'hast-util-to-text' import katex from 'katex' import {visit} from 'unist-util-visit' -import {toText} from 'hast-util-to-text' -import {fromHtmlIsomorphic} from 'hast-util-from-html-isomorphic' - -const assign = Object.assign -const source = 'rehype-katex' +/** @type {Readonly} */ +const emptyOptions = {} +/** @type {ReadonlyArray} */ +const emptyClasses = [] /** * Plugin to transform `` and `
` * with KaTeX. * - * @type {import('unified').Plugin<[Options?]|void[], Root>} + * @param {Readonly | null | undefined} [options] + * Configuration (optional). + * @returns + * Transform. */ export default function rehypeKatex(options) { - const settings = options || {} + const settings = options || emptyOptions const throwOnError = settings.throwOnError || false - return (tree, file) => { - visit(tree, 'element', (element) => { - const classes = - element.properties && Array.isArray(element.properties.className) - ? element.properties.className - : [] + /** + * Transform. + * + * @param {Root} tree + * Tree. + * @param {VFile} file + * File. + * @returns {undefined} + * Nothing. + */ + return function (tree, file) { + visit(tree, 'element', function (element) { + const classes = Array.isArray(element.properties.className) + ? element.properties.className + : emptyClasses const inline = classes.includes('math-inline') const displayMode = classes.includes('math-display') @@ -41,29 +58,30 @@ export default function rehypeKatex(options) { let result try { - result = katex.renderToString( - value, - assign({}, settings, {displayMode, throwOnError: true}) - ) - } catch (error_) { - const error = /** @type {Error} */ (error_) + result = katex.renderToString(value, { + ...settings, + displayMode, + throwOnError: true + }) + } catch (error) { + const exception = /** @type {Error} */ (error) const fn = throwOnError ? 'fail' : 'message' - const origin = [source, error.name.toLowerCase()].join(':') + const origin = ['rehype-katex', exception.name.toLowerCase()].join(':') - file[fn](error.message, element.position, origin) + file[fn](exception.message, element.position, origin) // KaTeX can handle `ParseError` itself, but not others. // Generate similar markup if this is an other error. // See: . - if (error.name !== 'ParseError') { + if (exception.name !== 'ParseError') { element.children = [ { type: 'element', tagName: 'span', properties: { className: ['katex-error'], - title: String(error), - style: 'color:' + (settings.errorColor || '#cc0000') + style: 'color:' + (settings.errorColor || '#cc0000'), + title: String(error) }, children: [{type: 'text', value}] } @@ -71,20 +89,18 @@ export default function rehypeKatex(options) { return } - result = katex.renderToString( - value, - assign({}, settings, { - displayMode, - throwOnError: false, - strict: 'ignore' - }) - ) + result = katex.renderToString(value, { + ...settings, + displayMode, + strict: 'ignore', + throwOnError: false + }) } const root = fromHtmlIsomorphic(result, {fragment: true}) - // To do: cast content. - // @ts-expect-error: assume no `doctypes` in KaTeX result. - element.children = root.children + // Cast because there will not be `doctypes` in KaTeX result. + const content = /** @type {Array} */ (root.children) + element.children = content }) } } diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index d6326f9..d030eb7 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -43,7 +43,8 @@ "hast-util-from-html-isomorphic": "^2.0.0", "hast-util-to-text": "^4.0.0", "katex": "^0.16.0", - "unist-util-visit": "^5.0.0" + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" }, "scripts": { "test-api": "node --conditions development test.js", diff --git a/packages/rehype-katex/test.js b/packages/rehype-katex/test.js index f2833c4..9c5f40b 100644 --- a/packages/rehype-katex/test.js +++ b/packages/rehype-katex/test.js @@ -1,83 +1,93 @@ import assert from 'node:assert/strict' import test from 'node:test' import katex from 'katex' -import {unified} from 'unified' -import remarkParse from 'remark-parse' -import remarkRehype from 'remark-rehype' +import rehypeKatex from 'rehype-katex' import rehypeParse from 'rehype-parse' import rehypeStringify from 'rehype-stringify' -import remarkMath from '../remark-math/index.js' -import rehypeKatex from './index.js' +import remarkMath from 'remark-math' +import remarkParse from 'remark-parse' +import remarkRehype from 'remark-rehype' +import {unified} from 'unified' test('rehype-katex', async function (t) { + await t.test('should expose the public api', async function () { + assert.deepEqual(Object.keys(await import('rehype-katex')).sort(), [ + 'default' + ]) + }) + await t.test('should transform math with katex', async function () { assert.deepEqual( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex) - .use(rehypeStringify) - .processSync( - [ - '

Inline math \\alpha.

', - '

Block math:

', - '
\\gamma
' - ].join('\n') - ) - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - [ - '

Inline math ' + - katex.renderToString('\\alpha') + - '.

', - '

Block math:

', - '
' + - katex.renderToString('\\gamma', {displayMode: true}) + - '
' - ].join('\n') - ) - .toString() + String( + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex) + .use(rehypeStringify) + .process( + [ + '

Inline math \\alpha.

', + '

Block math:

', + '
\\gamma
' + ].join('\n') + ) + ), + String( + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeStringify) + .process( + [ + '

Inline math ' + + katex.renderToString('\\alpha') + + '.

', + '

Block math:

', + '
' + + katex.renderToString('\\gamma', {displayMode: true}) + + '
' + ].join('\n') + ) + ) ) }) await t.test('should integrate with `remark-math`', async function () { assert.deepEqual( - unified() - .use(remarkParse) - .use(remarkMath) - // @ts-expect-error: to do: remove when `remark-rehype` is released. - .use(remarkRehype) - .use(rehypeKatex) - .use(rehypeStringify) - .processSync( - [ - 'Inline math $\\alpha$.', - '', - 'Block math:', - '', - '$$', - '\\gamma', - '$$' - ].join('\n') - ) - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - [ - '

Inline math ' + - katex.renderToString('\\alpha') + - '.

', - '

Block math:

', - '
' +
-              katex.renderToString('\\gamma', {displayMode: true}) +
-              '
' - ].join('\n') - ) - .toString() + String( + await unified() + .use(remarkParse) + .use(remarkMath) + // @ts-expect-error: to do: remove when `remark-rehype` is released. + .use(remarkRehype) + .use(rehypeKatex) + .use(rehypeStringify) + .process( + [ + 'Inline math $\\alpha$.', + '', + 'Block math:', + '', + '$$', + '\\gamma', + '$$' + ].join('\n') + ) + ), + String( + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeStringify) + .process( + [ + '

Inline math ' + + katex.renderToString('\\alpha') + + '.

', + '

Block math:

', + '
' +
+                katex.renderToString('\\gamma', {displayMode: true}) +
+                '
' + ].join('\n') + ) + ) ) }) @@ -85,23 +95,25 @@ test('rehype-katex', async function (t) { 'should transform `.math-inline.math-display` math with `displayMode: true`', async function () { assert.deepEqual( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex) - .use(rehypeStringify) - .processSync( - '

Double math \\alpha.

' - ) - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - '

Double math ' + - katex.renderToString('\\alpha', {displayMode: true}) + - '.

' - ) - .toString() + String( + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex) + .use(rehypeStringify) + .process( + '

Double math \\alpha.

' + ) + ), + String( + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeStringify) + .process( + '

Double math ' + + katex.renderToString('\\alpha', {displayMode: true}) + + '.

' + ) + ) ) } ) @@ -110,79 +122,80 @@ test('rehype-katex', async function (t) { const macros = {'\\RR': '\\mathbb{R}'} assert.deepEqual( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex, {macros}) - .use(rehypeStringify) - .processSync('\\RR') - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - '' + - katex.renderToString('\\RR', {macros}) + - '' - ) - .toString() + String( + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex, {macros}) + .use(rehypeStringify) + .process('\\RR') + ), + String( + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeStringify) + .process( + '' + + katex.renderToString('\\RR', {macros}) + + '' + ) + ) ) }) await t.test('should support `errorColor`', async function () { assert.deepEqual( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex, {errorColor: 'orange'}) - .use(rehypeStringify) - .processSync('\\alpa') - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - '' + - katex.renderToString('\\alpa', { - throwOnError: false, - errorColor: 'orange' - }) + - '' - ) - .toString() + String( + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex, {errorColor: 'orange'}) + .use(rehypeStringify) + .process('\\alpa') + ), + String( + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeStringify) + .process( + '' + + katex.renderToString('\\alpa', { + errorColor: 'orange', + throwOnError: false + }) + + '' + ) + ) ) }) await t.test('should create a message for errors', async function () { - assert.deepEqual( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex) - .use(rehypeStringify) - .processSync( - '

Lorem

\n

\\alpa

' - ) - .messages.map(String), - [ - '2:4-2:42: KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲' - ] - ) + const file = await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex) + .use(rehypeStringify) + .process('

Lorem

\n

\\alpa

') + + assert.deepEqual(file.messages.map(String), [ + '2:4-2:42: KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲' + ]) }) await t.test( 'should throw an error if `throwOnError: true`', async function () { try { - unified() + await unified() .use(rehypeParse, {fragment: true}) .use(rehypeKatex, {throwOnError: true}) .use(rehypeStringify) - .processSync( + .process( '

Lorem

\n

\\alpa

' ) - } catch (error_) { - const error = /** @type {Error} */ (error_) - assert.equal( - error.message, - 'KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲' + /* c8 ignore next 2 -- some c8 bug. */ + assert.fail() + } catch (error) { + assert.match( + String(error), + /KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲/ ) } } @@ -190,64 +203,70 @@ test('rehype-katex', async function (t) { await t.test('should support `strict: ignore`', async function () { assert.deepEqual( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex, {errorColor: 'orange', strict: 'ignore'}) - .use(rehypeStringify) - .processSync('ê&') - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - 'ê&' - ) - .toString() + String( + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex, {errorColor: 'orange', strict: 'ignore'}) + .use(rehypeStringify) + .process('ê&') + ), + String( + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeStringify) + .process( + 'ê&' + ) + ) ) }) await t.test('should support comments', async function () { assert.deepEqual( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex, {errorColor: 'orange', strict: 'ignore'}) - .use(rehypeStringify) - .processSync( - '
\\begin{split}\n f(-2) &= \\sqrt{-2+4} \\\\\n &= x % Test Comment\n\\end{split}
' - ) - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - '
' + - katex.renderToString( - '\\begin{split}\n f(-2) &= \\sqrt{-2+4} \\\\\n &= x % Test Comment\n\\end{split}', - {displayMode: true} - ) + - '
' - ) - .toString() + String( + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex, {errorColor: 'orange', strict: 'ignore'}) + .use(rehypeStringify) + .process( + '
\\begin{split}\n f(-2) &= \\sqrt{-2+4} \\\\\n &= x % Test Comment\n\\end{split}
' + ) + ), + String( + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeStringify) + .process( + '
' + + katex.renderToString( + '\\begin{split}\n f(-2) &= \\sqrt{-2+4} \\\\\n &= x % Test Comment\n\\end{split}', + {displayMode: true} + ) + + '
' + ) + ) ) }) await t.test('should not crash on non-parse errors', async function () { assert.deepEqual( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex) - .use(rehypeStringify) - .processSync( - '\\begin{split}\n\\end{{split}}\n' - ) - .toString(), - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeStringify) - .processSync( - '\\begin{split}\n\\end{{split}}\n' - ) - .toString() + String( + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex) + .use(rehypeStringify) + .process( + '\\begin{split}\n\\end{{split}}\n' + ) + ), + String( + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeStringify) + .process( + '\\begin{split}\n\\end{{split}}\n' + ) + ) ) }) }) diff --git a/packages/rehype-mathjax/browser.js b/packages/rehype-mathjax/browser.js index 5655c4f..6481d87 100644 --- a/packages/rehype-mathjax/browser.js +++ b/packages/rehype-mathjax/browser.js @@ -1,11 +1,15 @@ /** * @typedef {import('./lib/create-plugin.js').Options} Options + * @typedef {import('./lib/create-plugin.js').InputTexOptions} InputTexOptions */ import {createPlugin} from './lib/create-plugin.js' -const rehypeMathJaxBrowser = createPlugin((options) => { - const tex = options.tex || {} +/** @type {Readonly} */ +const emptyTexOptions = {} + +const rehypeMathJaxBrowser = createPlugin(function (options) { + const tex = options.tex || emptyTexOptions const display = tex.displayMath || [['\\[', '\\]']] const inline = tex.inlineMath || [['\\(', '\\)']] diff --git a/packages/rehype-mathjax/chtml.js b/packages/rehype-mathjax/chtml.js index 9ea9653..75946ad 100644 --- a/packages/rehype-mathjax/chtml.js +++ b/packages/rehype-mathjax/chtml.js @@ -3,10 +3,10 @@ */ import {CHTML} from 'mathjax-full/js/output/chtml.js' -import {createRenderer} from './lib/create-renderer.js' import {createPlugin} from './lib/create-plugin.js' +import {createRenderer} from './lib/create-renderer.js' -const rehypeMathJaxCHtml = createPlugin((options) => { +const rehypeMathJaxCHtml = createPlugin(function (options) { if (!options.chtml || !options.chtml.fontURL) { throw new Error( 'rehype-mathjax: missing `fontURL` in options, which must be set to a URL to reach MathJaX fonts' diff --git a/packages/rehype-mathjax/lib/create-plugin.js b/packages/rehype-mathjax/lib/create-plugin.js index 0ac2e1a..e631849 100644 --- a/packages/rehype-mathjax/lib/create-plugin.js +++ b/packages/rehype-mathjax/lib/create-plugin.js @@ -1,98 +1,201 @@ /** - * @typedef {import('hast').Root} Root * @typedef {import('hast').Element} Element + * @typedef {import('hast').Root} Root + */ + +/** + * @callback CreateRenderer + * Create a renderer. + * @param {Readonly} options + * Configuration. + * @returns {Renderer} + * Rendeder. + * + * @callback FormatError + * Format an error. + * @param {any} jax + * MathJax object. + * @param {any} error + * Error. + * @returns {string} + * Formatted error. + * + * @typedef InputTexOptions + * Configuration for input tex math. + * + * @property {string | null | undefined} [baseURL] + * URL for use with links to tags, when there is a `` tag in effect + * (optional). + * @property {RegExp | null | undefined} [digits] + * Pattern for recognizing numbers (optional). + * @property {ReadonlyArray | null | undefined} [displayMath] + * Start/end delimiter pairs for display math (optional). + * @property {FormatError | null | undefined} [formatError] + * Function called when TeX syntax errors occur (optional). + * @property {ReadonlyArray | null | undefined} [inlineMath] + * Start/end delimiter pairs for in-line math (optional). + * @property {number | null | undefined} [maxBuffer] + * Max size for the internal TeX string (5K) (optional). + * @property {number | null | undefined} [maxMacros] + * Max number of macro substitutions per expression (optional). + * @property {ReadonlyArray | null | undefined} [packages] + * Extensions to use (optional). + * @property {boolean | null | undefined} [processEnvironments] + * Process `\begin{xxx}...\end{xxx}` outside math mode (optional). + * @property {boolean | null | undefined} [processEscapes] + * Use `\$` to produce a literal dollar sign (optional). + * @property {boolean | null | undefined} [processRefs] + * Process `\ref{...}` outside of math mode (optional). + * @property {string | null | undefined} [tagIndent] + * Amount to indent tags (optional). + * @property {'left' | 'right' | null | undefined} [tagSide] + * Side for `\tag` macros (optional). + * @property {'all' | 'ams' | 'none' | null | undefined} [tags] + * Optional. + * @property {boolean | null | undefined} [useLabelIds] + * Use label name rather than tag for ids (optional). * * @typedef {[string, string]} MathNotation * Markers to use for math. * See: * - * @typedef OutputSvgOptions - * - * @property {number} [scale] - * @property {number} [minScale] - * @property {boolean} [mtextInheritFont] - * @property {boolean} [merrorInheritFont] - * @property {boolean} [mathmlSpacing] - * @property {Record} [skipAttributes] - * @property {number} [exFactor] - * @property {'left'|'center'|'right'} [displayAlign] - * @property {string} [displayIndent] - * @property {'local'|'global'} [fontCache] - * @property {string|null} [localID] - * @property {boolean} [internalSpeechTitles] - * @property {number} [titleID] + * @typedef Options + * Configuration. + * @property {Readonly | null | undefined} [chtml] + * Configuration for the output, when CHTML (optional). + * @property {Readonly | null | undefined} [svg] + * Configuration for the output, when SVG (optional). + * @property {Readonly | null | undefined} [tex] + * Configuration for the input TeX (optional). * * @typedef OutputCHtmlOptions + * Configuration for output CHTML. * - * @property {number} [scale] - * @property {number} [minScale] - * @property {boolean} [matchFontHeight] - * @property {boolean} [mtextInheritFont] - * @property {boolean} [merrorInheritFont] - * @property {boolean} [mathmlSpacing] - * @property {Record} [skipAttributes] - * @property {number} [exFactor] - * @property {'left'|'center'|'right'} [displayAlign] - * @property {string} [displayIndent] + * @property {boolean | null | undefined} [adaptiveCSS] + * `true` means only produce CSS that is used in the processed equations (optional). + * @property {'center' | 'left' | 'right' | null | undefined} [displayAlign] + * Default for indentalign when set to `'auto'` (optional). + * @property {string | null | undefined} [displayIndent] + * Default for indentshift when set to `'auto'` (optional). + * @property {number | null | undefined} [exFactor] + * Default size of ex in em units (optional). * @property {string} fontURL - * @property {boolean} [adaptiveCSS] + * The URL where the fonts are found (**required**). + * @property {boolean | null | undefined} [matchFontHeight] + * `true` to match ex-height of surrounding font (optional). + * @property {boolean | null | undefined} [mathmlSpacing] + * `true` for MathML spacing rules, false for TeX rules (optional). + * @property {boolean | null | undefined} [merrorInheritFont] + * `true` to make merror text use surrounding font (optional). + * @property {number | null | undefined} [minScale] + * Smallest scaling factor to use (optional). + * @property {boolean | null | undefined} [mtextInheritFont] + * `true` to make mtext elements use surrounding font (optional). + * @property {number | null | undefined} [scale] + * Global scaling factor for all expressions (optional). + * @property {Readonly> | null | undefined} [skipAttributes] + * RFDa and other attributes NOT to copy to the output (optional). * - * @typedef InputTexOptions - * - * @property {string[]} [packages] - * @property {MathNotation[]} [inlineMath] - * @property {MathNotation[]} [displayMath] - * @property {boolean} [processEscapes] - * @property {boolean} [processEnvironments] - * @property {boolean} [processRefs] - * @property {RegExp} [digits] - * @property {'none'|'ams'|'all'} [tags] - * @property {'left'|'right'} [tagSide] - * @property {string} [tagIndent] - * @property {boolean} [useLabelIds] - * @property {string} [multlineWidth] - * @property {number} [maxMacros] - * @property {number} [maxBuffer] - * @property {string} [baseURL] - * @property {(jax: any, error: any) => string} [formatError] + * @typedef OutputSvgOptions + * Configuration for output SVG. + * + * @property {'center' | 'left' | 'right' | null | undefined} [displayAlign] + * Default for indentalign when set to `'auto'` (optional). + * @property {string | null | undefined} [displayIndent] + * Default for indentshift when set to `'auto'` (optional). + * @property {number | null | undefined} [exFactor] + * Default size of ex in em units (optional). + * @property {'global' | 'local' | 'none' | null | undefined} [fontCache] + * Or `'global'` or `'none'` (optional). + * @property {boolean | null | undefined} [internalSpeechTitles] + * Insert `` tags with speech content (optional). + * @property {string | null | undefined} [localID] + * ID to use for local font cache, for single equation processing (optional). + * @property {boolean | null | undefined} [mathmlSpacing] + * `true` for MathML spacing rules, `false` for TeX rules (optional). + * @property {boolean | null | undefined} [merrorInheritFont] + * `true` to make merror text use surrounding font (optional). + * @property {number | null | undefined} [minScale] + * Smallest scaling factor to use (optional). + * @property {boolean | null | undefined} [mtextInheritFont] + * `true` to make mtext elements use surrounding font (optional). + * @property {number | null | undefined} [scale] + * Global scaling factor for all expressions (optional). + * @property {Readonly<Record<string, boolean>> | null | undefined} [skipAttributes] + * RFDa and other attributes *not* to copy to the output (optional). + * @property {number | null | undefined} [titleID] + * Initial ID number to use for `aria-labeledby` titles (optional). * - * @typedef Options + * @callback Render + * Render a math node. + * @param {Element} element + * Math node. + * @param {Readonly<RenderOptions>} options * Configuration. - * @property {InputTexOptions} [tex] - * Configuration for the input TeX. - * @property {OutputCHtmlOptions} [chtml] - * Configuration for the output (when CHTML). - * @property {OutputSvgOptions} [svg] - * Configuration for the output (when SVG). + * @returns {undefined} + * Nothing. + * + * @typedef RenderOptions + * Configuration. + * @property {boolean} display + * Whether to render display math. * * @typedef Renderer - * @property {(node: Element, options: {display: boolean}) => void} render - * @property {() => Element} [styleSheet] + * Renderer. + * @property {Render} render + * Render a math node. + * @property {StyleSheet | null | undefined} [styleSheet] + * Render a style sheet (optional). * - * @callback CreateRenderer - * @param {Options} options - * @returns {Renderer} + * @callback StyleSheet + * Render a style sheet. + * @returns {Element} + * Style sheet. */ -import {visit, SKIP} from 'unist-util-visit' +import {SKIP, visit} from 'unist-util-visit' + +/** @type {Readonly<Options>} */ +const emptyOptions = {} +/** @type {ReadonlyArray<unknown>} */ +const emptyClasses = [] /** + * Create a plugin. + * * @param {CreateRenderer} createRenderer + * Create a renderer. + * @returns + * Plugin. */ export function createPlugin(createRenderer) { - /** @type {import('unified').Plugin<[Options?]|void[], Root>} */ - return (options = {}) => - (tree) => { - const renderer = createRenderer(options) + /** + * Plugin. + * + * @param {Readonly<Options> | null | undefined} [options] + * Configuration (optional). + * @returns + * Transform. + */ + return function (options) { + /** + * Transform. + * + * @param {Root} tree + * Tree. + * @returns {undefined} + * Nothing. + */ + return function (tree) { + const renderer = createRenderer(options || emptyOptions) let found = false - /** @type {Root|Element} */ + /** @type {Element | Root} */ let context = tree - visit(tree, 'element', (node) => { - const classes = - node.properties && Array.isArray(node.properties.className) - ? node.properties.className - : [] + visit(tree, 'element', function (node) { + const classes = Array.isArray(node.properties.className) + ? node.properties.className + : emptyClasses const inline = classes.includes('math-inline') const display = classes.includes('math-display') @@ -114,4 +217,5 @@ export function createPlugin(createRenderer) { context.children.push(renderer.styleSheet()) } } + } } diff --git a/packages/rehype-mathjax/lib/create-renderer.js b/packages/rehype-mathjax/lib/create-renderer.js index af90027..cb38b5c 100644 --- a/packages/rehype-mathjax/lib/create-renderer.js +++ b/packages/rehype-mathjax/lib/create-renderer.js @@ -1,18 +1,17 @@ /** * @typedef {import('hast').Element} Element - * @typedef {import('mathjax-full/js/core/OutputJax.js').OutputJax<HTMLElement, Text, Document>} OutputJax * @typedef {import('mathjax-full/js/core/MathDocument.js').MathDocument<HTMLElement, Text, Document>} MathDocument - * @typedef {import('mathjax-full/js/input/tex.js').TeX<HTMLElement, Text, Document>} TeX_ + * @typedef {import('mathjax-full/js/core/OutputJax.js').OutputJax<HTMLElement, Text, Document>} OutputJax * @typedef {import('./create-plugin.js').Options} Options * @typedef {import('./create-plugin.js').Renderer} Renderer */ -import {mathjax} from 'mathjax-full/js/mathjax.js' +import {fromDom} from 'hast-util-from-dom' +import {toText} from 'hast-util-to-text' import {RegisterHTMLHandler} from 'mathjax-full/js/handlers/html.js' import {TeX} from 'mathjax-full/js/input/tex.js' import {AllPackages} from 'mathjax-full/js/input/tex/AllPackages.js' -import {fromDom} from 'hast-util-from-dom' -import {toText} from 'hast-util-to-text' +import {mathjax} from 'mathjax-full/js/mathjax.js' import {createAdaptor} from './create-adaptor.js' const adaptor = createAdaptor() @@ -31,24 +30,30 @@ const adaptor = createAdaptor() RegisterHTMLHandler(adaptor) /** + * Create a renderer. + * * @param {Options} options + * Configuration. * @param {OutputJax} output + * Output jax. * @returns {Renderer} + * Rendeder. */ export function createRenderer(options, output) { - const input = new TeX(Object.assign({packages: AllPackages}, options.tex)) + const input = new TeX({packages: AllPackages, ...options.tex}) /** @type {MathDocument} */ const doc = mathjax.document('', {InputJax: input, OutputJax: output}) return { render(node, options) { - const domNode = fromDom( - // @ts-expect-error: assume mathml nodes can be handled by - // `hast-util-from-dom`. - doc.convert(toText(node, {whitespace: 'pre'}), options) + const mathText = toText(node, {whitespace: 'pre'}) + // Cast as this practically results in `HTMLElement`. + const domNode = /** @type {HTMLElement} */ ( + doc.convert(mathText, options) ) - // @ts-expect-error: `fromDom` returns an element for a given element. - node.children = [domNode] + // Cast as `HTMLElement` results in an `Element`. + const hastNode = /** @type {Element} */ (fromDom(domNode)) + node.children = [hastNode] }, styleSheet() { const value = adaptor.textContent(output.styleSheet(doc)) diff --git a/packages/rehype-mathjax/svg.js b/packages/rehype-mathjax/svg.js index dd16ab8..27e6fb2 100644 --- a/packages/rehype-mathjax/svg.js +++ b/packages/rehype-mathjax/svg.js @@ -3,11 +3,12 @@ */ import {SVG} from 'mathjax-full/js/output/svg.js' -import {createRenderer} from './lib/create-renderer.js' import {createPlugin} from './lib/create-plugin.js' +import {createRenderer} from './lib/create-renderer.js' -const rehypeMathJaxSvg = createPlugin((options) => - createRenderer(options, new SVG(options.svg)) -) +const rehypeMathJaxSvg = createPlugin(function (options) { + // `mathjax-types` do not allow `null`. + return createRenderer(options, new SVG(options.svg || undefined)) +}) export default rehypeMathJaxSvg diff --git a/packages/rehype-mathjax/test/index.js b/packages/rehype-mathjax/test/index.js index a016119..eeccf24 100644 --- a/packages/rehype-mathjax/test/index.js +++ b/packages/rehype-mathjax/test/index.js @@ -1,81 +1,128 @@ import assert from 'node:assert/strict' +import fs from 'node:fs/promises' import test from 'node:test' -import path from 'node:path' -import {readSync} from 'to-vfile' -import {unified} from 'unified' -import remarkParse from 'remark-parse' -import remarkRehype from 'remark-rehype' +import rehypeMathJaxBrowser from 'rehype-mathjax/browser.js' +import rehypeMathJaxChtml from 'rehype-mathjax/chtml.js' +import rehypeMathJaxSvg from 'rehype-mathjax/svg.js' import rehypeParse from 'rehype-parse' import rehypeStringify from 'rehype-stringify' -import remarkMath from '../../remark-math/index.js' -import rehypeMathJaxSvg from '../svg.js' -import rehypeMathJaxChtml from '../chtml.js' -import rehypeMathJaxBrowser from '../browser.js' +import remarkMath from 'remark-math' +import remarkParse from 'remark-parse' +import remarkRehype from 'remark-rehype' +import {unified} from 'unified' -const fixtures = path.join('test', 'fixture') +const base = new URL('fixture/', import.meta.url) test('rehype-mathjax', async function (t) { + await t.test( + 'should expose the public api for `rehype-mathjax`', + async function () { + assert.deepEqual(Object.keys(await import('rehype-mathjax')).sort(), [ + 'default' + ]) + } + ) + + await t.test( + 'should expose the public api for `rehype-mathjax/browser.js`', + async function () { + assert.deepEqual( + Object.keys(await import('rehype-mathjax/browser.js')).sort(), + ['default'] + ) + } + ) + + await t.test( + 'should expose the public api for `rehype-mathjax/chtml.js`', + async function () { + assert.deepEqual( + Object.keys(await import('rehype-mathjax/chtml.js')).sort(), + ['default'] + ) + } + ) + + await t.test( + 'should expose the public api for `rehype-mathjax/svg.js`', + async function () { + assert.deepEqual( + Object.keys(await import('rehype-mathjax/svg.js')).sort(), + ['default'] + ) + } + ) + await t.test('should render SVG', async function () { assert.equal( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeMathJaxSvg) - .use(rehypeStringify) - .processSync(readSync({dirname: fixtures, basename: 'small.html'})) - .toString(), - String(readSync({dirname: fixtures, basename: 'small-svg.html'})).trim() + String( + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxSvg) + .use(rehypeStringify) + .process(await fs.readFile(new URL('small.html', base))) + ), + String(await fs.readFile(new URL('small-svg.html', base))).trim() ) }) await t.test('should crash for CHTML w/o `fontURL`', async function () { - assert.throws(function () { - unified() + try { + await unified() .use(rehypeParse, {fragment: true}) .use(rehypeMathJaxChtml) .use(rehypeStringify) - .processSync(readSync({dirname: fixtures, basename: 'small.html'})) - .toString() - }, /rehype-mathjax: missing `fontURL` in options/) + .process( + await fs.readFile(new URL('equation-numbering-2-svg.html', base)) + ) + assert.fail() + } catch (error) { + assert.match( + String(error), + /rehype-mathjax: missing `fontURL` in options/ + ) + } }) await t.test('should render CHTML', async function () { assert.equal( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeMathJaxChtml, {chtml: {fontURL: 'place/to/fonts'}}) - .use(rehypeStringify) - .processSync(readSync({dirname: fixtures, basename: 'small.html'})) - .toString(), - String(readSync({dirname: fixtures, basename: 'small-chtml.html'})).trim() + String( + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxChtml, {chtml: {fontURL: 'place/to/fonts'}}) + .use(rehypeStringify) + .process(await fs.readFile(new URL('small.html', base))) + ), + String(await fs.readFile(new URL('small-chtml.html', base))).trim() ) }) await t.test('should render browser', async function () { assert.equal( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeMathJaxBrowser) - .use(rehypeStringify) - .processSync(readSync({dirname: fixtures, basename: 'small.html'})) - .toString(), - String(readSync({dirname: fixtures, basename: 'small-browser.html'})) + String( + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxBrowser) + .use(rehypeStringify) + .process(await fs.readFile(new URL('small.html', base))) + ), + String(await fs.readFile(new URL('small-browser.html', base))) ) }) await t.test('should integrate with `remark-math`', async function () { assert.equal( - unified() - .use(remarkParse) - .use(remarkMath) - // @ts-expect-error: to do: remove when `remark-rehype` is released. - .use(remarkRehype) - .use(rehypeMathJaxSvg) - .use(rehypeStringify) - .processSync(readSync({dirname: fixtures, basename: 'markdown.md'})) - .toString(), String( - readSync({dirname: fixtures, basename: 'markdown-svg.html'}) - ).trim() + await unified() + .use(remarkParse) + .use(remarkMath) + // @ts-expect-error: to do: remove when `remark-rehype` is released. + .use(remarkRehype) + .use(rehypeMathJaxSvg) + .use(rehypeStringify) + .process(await fs.readFile(new URL('markdown.md', base))) + ), + String(await fs.readFile(new URL('markdown-svg.html', base))).trim() ) }) @@ -83,42 +130,41 @@ test('rehype-mathjax', async function (t) { 'should transform `.math-inline.math-display`', async function () { assert.equal( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeMathJaxSvg) - .use(rehypeStringify) - .processSync(readSync({dirname: fixtures, basename: 'double.html'})) - .toString(), String( - readSync({dirname: fixtures, basename: 'double-svg.html'}) - ).trim() + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxSvg) + .use(rehypeStringify) + .process(await fs.readFile(new URL('double.html', base))) + ), + String(await fs.readFile(new URL('double-svg.html', base))).trim() ) } ) await t.test('should transform documents without math', async function () { assert.equal( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeMathJaxSvg) - .use(rehypeStringify) - .processSync(readSync({dirname: fixtures, basename: 'none.html'})) - .toString(), - String(readSync({dirname: fixtures, basename: 'none-svg.html'})) + String( + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxSvg) + .use(rehypeStringify) + .process(await fs.readFile(new URL('none.html', base))) + ), + String(await fs.readFile(new URL('none-svg.html', base))) ) }) await t.test('should transform complete documents', async function () { assert.equal( - unified() - .use(rehypeParse) - .use(rehypeMathJaxSvg) - .use(rehypeStringify) - .processSync(readSync({dirname: fixtures, basename: 'document.html'})) - .toString(), String( - readSync({dirname: fixtures, basename: 'document-svg.html'}) - ).trim() + await unified() + .use(rehypeParse) + .use(rehypeMathJaxSvg) + .use(rehypeStringify) + .process(await fs.readFile(new URL('document.html', base))) + ), + String(await fs.readFile(new URL('document-svg.html', base))).trim() ) }) @@ -126,22 +172,20 @@ test('rehype-mathjax', async function (t) { 'should support custom `inlineMath` and `displayMath` delimiters for browser', async function () { assert.equal( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeMathJaxBrowser, { - tex: { - inlineMath: [['$', '$']], - displayMath: [['$$', '$$']] - } - }) - .use(rehypeStringify) - .processSync(readSync({dirname: fixtures, basename: 'small.html'})) - .toString(), String( - readSync({ - dirname: fixtures, - basename: 'small-browser-delimiters.html' - }) + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxBrowser, { + tex: { + displayMath: [['$$', '$$']], + inlineMath: [['$', '$']] + } + }) + .use(rehypeStringify) + .process(await fs.readFile(new URL('small.html', base))) + ), + String( + await fs.readFile(new URL('small-browser-delimiters.html', base)) ) ) } @@ -149,22 +193,17 @@ test('rehype-mathjax', async function (t) { await t.test('should render SVG with equation numbers', async function () { assert.equal( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeMathJaxSvg, {tex: {tags: 'ams'}}) - .use(rehypeStringify) - .processSync( - readSync({ - dirname: fixtures, - basename: 'equation-numbering-1.html' - }) - ) - .toString(), String( - readSync({ - dirname: fixtures, - basename: 'equation-numbering-1-svg.html' - }) + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxSvg, {tex: {tags: 'ams'}}) + .use(rehypeStringify) + .process( + await fs.readFile(new URL('equation-numbering-1.html', base)) + ) + ), + String( + await fs.readFile(new URL('equation-numbering-1-svg.html', base)) ).trim() ) }) @@ -173,22 +212,17 @@ test('rehype-mathjax', async function (t) { 'should render SVG with reference to an undefined equation', async function () { assert.equal( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeMathJaxSvg, {tex: {tags: 'ams'}}) - .use(rehypeStringify) - .processSync( - readSync({ - dirname: fixtures, - basename: 'equation-numbering-2.html' - }) - ) - .toString(), String( - readSync({ - dirname: fixtures, - basename: 'equation-numbering-2-svg.html' - }) + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeMathJaxSvg, {tex: {tags: 'ams'}}) + .use(rehypeStringify) + .process( + await fs.readFile(new URL('equation-numbering-2.html', base)) + ) + ), + String( + await fs.readFile(new URL('equation-numbering-2-svg.html', base)) ).trim() ) } @@ -196,63 +230,21 @@ test('rehype-mathjax', async function (t) { await t.test('should render CHTML with equation numbers', async function () { assert.equal( - unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeMathJaxChtml, { - chtml: {fontURL: 'place/to/fonts'}, - tex: {tags: 'ams'} - }) - .use(rehypeStringify) - .processSync( - readSync({ - dirname: fixtures, - basename: 'equation-numbering-1.html' - }) - ) - .toString(), String( - readSync({ - dirname: fixtures, - basename: 'equation-numbering-1-chtml.html' - }) - ).trim() - ) - }) - - await t.test('should render SVG with equation numbers', async function () { - assert.equal( - (function () { - const processor = unified() + await unified() .use(rehypeParse, {fragment: true}) - .use(rehypeMathJaxSvg, {tex: {tags: 'ams'}}) - .use(rehypeStringify) - return ['equation-numbering-1.html', 'equation-numbering-2.html'] - .map(function (basename) { - return processor - .processSync( - readSync({ - dirname: fixtures, - basename - }) - ) - .toString() + .use(rehypeMathJaxChtml, { + chtml: {fontURL: 'place/to/fonts'}, + tex: {tags: 'ams'} }) - .join('') - })(), - [ - String( - readSync({ - dirname: fixtures, - basename: 'equation-numbering-1-svg.html' - }) - ).trim(), - String( - readSync({ - dirname: fixtures, - basename: 'equation-numbering-2-svg.html' - }) - ).trim() - ].join('') + .use(rehypeStringify) + .process( + await fs.readFile(new URL('equation-numbering-1.html', base)) + ) + ), + String( + await fs.readFile(new URL('equation-numbering-1-chtml.html', base)) + ).trim() ) }) }) diff --git a/packages/remark-math/index.js b/packages/remark-math/index.js index 71a7ae2..a2cc9a8 100644 --- a/packages/remark-math/index.js +++ b/packages/remark-math/index.js @@ -1,38 +1,42 @@ +/// <reference types="mdast-util-math" /> +/// <reference types="remark-parse" /> +/// <reference types="remark-stringify" /> + /** * @typedef {import('mdast').Root} Root * @typedef {import('mdast-util-math').ToOptions} Options - * - * @typedef {import('mdast-util-math')} DoNotTouchAsThisImportIncludesMathInTree + * @typedef {import('unified').Processor<Root>} Processor */ -import {math} from 'micromark-extension-math' import {mathFromMarkdown, mathToMarkdown} from 'mdast-util-math' +import {math} from 'micromark-extension-math' + +/** @type {Readonly<Options>} */ +const emptyOptions = {} /** * Plugin to support math. * - * @this {import('unified').Processor} - * @type {import('unified').Plugin<[Options?] | void[], Root, Root>} + * @param {Readonly<Options> | null | undefined} [options] + * Configuration (optional). + * @returns {undefined} + * Nothing. */ -export default function remarkMath(options = {}) { - const data = this.data() - - add('micromarkExtensions', math(options)) - add('fromMarkdownExtensions', mathFromMarkdown()) - add('toMarkdownExtensions', mathToMarkdown(options)) +export default function remarkMath(options) { + // @ts-expect-error: TS is wrong about `this`. + // eslint-disable-next-line unicorn/no-this-assignment + const self = /** @type {Processor} */ (this) + const settings = options || emptyOptions + const data = self.data() - /** - * @param {string} field - * @param {unknown} value - */ - function add(field, value) { - const list = /** @type {Array<unknown>} */ ( - // Other extensions - /* c8 ignore next 2 */ - // @ts-expect-error: to do: refactor. - data[field] || (data[field] = []) - ) + const micromarkExtensions = + data.micromarkExtensions || (data.micromarkExtensions = []) + const fromMarkdownExtensions = + data.fromMarkdownExtensions || (data.fromMarkdownExtensions = []) + const toMarkdownExtensions = + data.toMarkdownExtensions || (data.toMarkdownExtensions = []) - list.push(value) - } + micromarkExtensions.push(math(settings)) + fromMarkdownExtensions.push(mathFromMarkdown()) + toMarkdownExtensions.push(mathToMarkdown(settings)) } diff --git a/packages/remark-math/readme.md b/packages/remark-math/readme.md index b3f21e9..35d4359 100644 --- a/packages/remark-math/readme.md +++ b/packages/remark-math/readme.md @@ -191,9 +191,9 @@ somewhere in your types, as that registers the new node types in the tree. import {visit} from 'unist-util-visit' /** @type {import('unified').Plugin<[], import('mdast').Root>} */ -export default function myRemarkPlugin() => { - return (tree) => { - visit(tree, (node) => { +export default function myRemarkPlugin() { + return function (tree) { + visit(tree, function(node) { // `node` can now be one of the nodes for math. }) } diff --git a/packages/remark-math/test.js b/packages/remark-math/test.js index 3c4249c..3db31a1 100644 --- a/packages/remark-math/test.js +++ b/packages/remark-math/test.js @@ -1,12 +1,11 @@ import assert from 'node:assert/strict' import test from 'node:test' -import {u} from 'unist-builder' -import {removePosition} from 'unist-util-remove-position' -import {unified} from 'unified' +import rehypeStringify from 'rehype-stringify' import remarkParse from 'remark-parse' import remarkRehype from 'remark-rehype' -import rehypeStringify from 'rehype-stringify' import remarkStringify from 'remark-stringify' +import {unified} from 'unified' +import {removePosition} from 'unist-util-remove-position' import remarkMath from './index.js' test('remarkMath', async function (t) { @@ -17,6 +16,12 @@ test('remarkMath', async function (t) { .use(remarkRehype) .use(rehypeStringify) + await t.test('should expose the public api', async function () { + assert.deepEqual(Object.keys(await import('remark-math')).sort(), [ + 'default' + ]) + }) + await t.test('should parse inline and block math', async function () { const tree = unified() .use(remarkParse) @@ -25,43 +30,42 @@ test('remarkMath', async function (t) { removePosition(tree, {force: true}) - assert.deepEqual( - tree, - u('root', [ - u('paragraph', [ - u('text', 'Math '), - u( - 'inlineMath', + assert.deepEqual(tree, { + type: 'root', + children: [ + { + type: 'paragraph', + children: [ + {type: 'text', value: 'Math '}, { + type: 'inlineMath', data: { hName: 'code', hProperties: {className: ['language-math', 'math-inline']}, - hChildren: [u('text', '\\alpha')] - } - }, - '\\alpha' - ) - ]), - u( - 'math', - { - meta: null, - data: { - hName: 'pre', - hChildren: [ - { - type: 'element', - tagName: 'code', - properties: {className: ['language-math', 'math-display']}, - children: [{type: 'text', value: '\\beta+\\gamma'}] - } - ] + hChildren: [{type: 'text', value: '\\alpha'}] + }, + value: '\\alpha' } + ] + }, + { + type: 'math', + meta: null, + data: { + hName: 'pre', + hChildren: [ + { + type: 'element', + tagName: 'code', + properties: {className: ['language-math', 'math-display']}, + children: [{type: 'text', value: '\\beta+\\gamma'}] + } + ] }, - '\\beta+\\gamma' - ) - ]) - ) + value: '\\beta+\\gamma' + } + ] + }) }) await t.test( @@ -74,10 +78,12 @@ test('remarkMath', async function (t) { removePosition(tree, {force: true}) - assert.deepEqual( - tree, - u('root', [u('paragraph', [u('text', '$\\alpha$')])]) - ) + assert.deepEqual(tree, { + type: 'root', + children: [ + {type: 'paragraph', children: [{type: 'text', value: '$\\alpha$'}]} + ] + }) } ) @@ -91,24 +97,26 @@ test('remarkMath', async function (t) { removePosition(tree, {force: true}) - assert.deepEqual( - tree, - u('root', [ - u('paragraph', [ - u( - 'inlineMath', + assert.deepEqual(tree, { + type: 'root', + children: [ + { + type: 'paragraph', + children: [ { + type: 'inlineMath', + data: { hName: 'code', hProperties: {className: ['language-math', 'math-inline']}, - hChildren: [u('text', '\\alpha\\')] - } - }, - '\\alpha\\' - ) - ]) - ]) - ) + hChildren: [{type: 'text', value: '\\alpha\\'}] + }, + value: '\\alpha\\' + } + ] + } + ] + }) } ) @@ -122,25 +130,26 @@ test('remarkMath', async function (t) { removePosition(tree, {force: true}) - assert.deepEqual( - tree, - u('root', [ - u('paragraph', [ - u('text', '\\'), - u( - 'inlineMath', + assert.deepEqual(tree, { + type: 'root', + children: [ + { + type: 'paragraph', + children: [ + {type: 'text', value: '\\'}, { + type: 'inlineMath', data: { hName: 'code', hProperties: {className: ['language-math', 'math-inline']}, - hChildren: [u('text', '\\alpha')] - } - }, - '\\alpha' - ) - ]) - ]) - ) + hChildren: [{type: 'text', value: '\\alpha'}] + }, + value: '\\alpha' + } + ] + } + ] + }) } ) @@ -154,12 +163,18 @@ test('remarkMath', async function (t) { removePosition(tree, {force: true}) - assert.deepEqual( - tree, - u('root', [ - u('paragraph', [u('inlineCode', '$'), u('text', '\\alpha$')]) - ]) - ) + assert.deepEqual(tree, { + type: 'root', + children: [ + { + type: 'paragraph', + children: [ + {type: 'inlineCode', value: '$'}, + {type: 'text', value: '\\alpha$'} + ] + } + ] + }) } ) @@ -168,25 +183,26 @@ test('remarkMath', async function (t) { removePosition(tree, {force: true}) - assert.deepEqual( - tree, - u('root', [ - u('paragraph', [ - u( - 'inlineMath', + assert.deepEqual(tree, { + type: 'root', + children: [ + { + type: 'paragraph', + children: [ { + type: 'inlineMath', data: { hName: 'code', hProperties: {className: ['language-math', 'math-inline']}, - hChildren: [u('text', '\\alpha`')] - } + hChildren: [{type: 'text', value: '\\alpha`'}] + }, + value: '\\alpha`' }, - '\\alpha`' - ), - u('text', '`') - ]) - ]) - ) + {type: 'text', value: '`'} + ] + } + ] + }) }) await t.test('should support backticks in inline math', async function () { @@ -197,21 +213,25 @@ test('remarkMath', async function (t) { assert.deepEqual( tree, - u('root', [ - u('paragraph', [ - u( - 'inlineMath', - { - data: { - hName: 'code', - hProperties: {className: ['language-math', 'math-inline']}, - hChildren: [u('text', '`\\alpha`')] + { + type: 'root', + children: [ + { + type: 'paragraph', + children: [ + { + type: 'inlineMath', + data: { + hName: 'code', + hProperties: {className: ['language-math', 'math-inline']}, + hChildren: [{type: 'text', value: '`\\alpha`'}] + }, + value: '`\\alpha`' } - }, - '`\\alpha`' - ) - ]) - ]) + ] + } + ] + } ) }) @@ -225,24 +245,25 @@ test('remarkMath', async function (t) { removePosition(tree, {force: true}) - assert.deepEqual( - tree, - u('root', [ - u('paragraph', [ - u( - 'inlineMath', + assert.deepEqual(tree, { + type: 'root', + children: [ + { + type: 'paragraph', + children: [ { + type: 'inlineMath', data: { hName: 'code', hProperties: {className: ['language-math', 'math-inline']}, - hChildren: [u('text', '\\alpha$')] - } - }, - '\\alpha$' - ) - ]) - ]) - ) + hChildren: [{type: 'text', value: '\\alpha$'}] + }, + value: '\\alpha$' + } + ] + } + ] + }) } ) @@ -256,29 +277,28 @@ test('remarkMath', async function (t) { removePosition(tree, {force: true}) - assert.deepEqual( - tree, - u('root', [ - u( - 'math', - { - meta: null, - data: { - hName: 'pre', - hChildren: [ - { - type: 'element', - tagName: 'code', - properties: {className: ['language-math', 'math-display']}, - children: [{type: 'text', value: '\\alpha\\$'}] - } - ] - } + assert.deepEqual(tree, { + type: 'root', + children: [ + { + type: 'math', + + meta: null, + data: { + hName: 'pre', + hChildren: [ + { + type: 'element', + tagName: 'code', + properties: {className: ['language-math', 'math-display']}, + children: [{type: 'text', value: '\\alpha\\$'}] + } + ] }, - '\\alpha\\$' - ) - ]) - ) + value: '\\alpha\\$' + } + ] + }) } ) @@ -295,11 +315,13 @@ test('remarkMath', async function (t) { assert.deepEqual( tree, - u('root', [ - u('paragraph', [u('text', 'tango')]), - u( - 'math', + { + type: 'root', + children: [ + {type: 'paragraph', children: [{type: 'text', value: 'tango'}]}, { + type: 'math', + meta: null, data: { hName: 'pre', @@ -307,15 +329,17 @@ test('remarkMath', async function (t) { { type: 'element', tagName: 'code', - properties: {className: ['language-math', 'math-display']}, + properties: { + className: ['language-math', 'math-display'] + }, children: [{type: 'text', value: '\\alpha'}] } ] - } - }, - '\\alpha' - ) - ]) + }, + value: '\\alpha' + } + ] + } ) } ) @@ -330,24 +354,26 @@ test('remarkMath', async function (t) { removePosition(tree, {force: true}) - assert.deepEqual( - tree, - u('root', [ - u('paragraph', [ - u( - 'inlineMath', + assert.deepEqual(tree, { + type: 'root', + children: [ + { + type: 'paragraph', + children: [ { + type: 'inlineMath', + data: { hName: 'code', hProperties: {className: ['language-math', 'math-inline']}, - hChildren: [u('text', '\\alpha')] - } - }, - '\\alpha' - ) - ]) - ]) - ) + hChildren: [{type: 'text', value: '\\alpha'}] + }, + value: '\\alpha' + } + ] + } + ] + }) } ) @@ -361,29 +387,28 @@ test('remarkMath', async function (t) { removePosition(tree, {force: true}) - assert.deepEqual( - tree, - u('root', [ - u( - 'math', - { - meta: null, - data: { - hName: 'pre', - hChildren: [ - { - type: 'element', - tagName: 'code', - properties: {className: ['language-math', 'math-display']}, - children: [{type: 'text', value: '\\alpha'}] - } - ] - } + assert.deepEqual(tree, { + type: 'root', + children: [ + { + type: 'math', + + meta: null, + data: { + hName: 'pre', + hChildren: [ + { + type: 'element', + tagName: 'code', + properties: {className: ['language-math', 'math-display']}, + children: [{type: 'text', value: '\\alpha'}] + } + ] }, - '\\alpha' - ) - ]) - ) + value: '\\alpha' + } + ] + }) } ) @@ -395,39 +420,38 @@ test('remarkMath', async function (t) { removePosition(tree, {force: true}) - assert.deepEqual( - tree, - u('root', [ - u( - 'math', - { - meta: null, - data: { - hName: 'pre', - hChildren: [ - { - type: 'element', - tagName: 'code', - properties: {className: ['language-math', 'math-display']}, - children: [{type: 'text', value: ' \\alpha'}] - } - ] - } + assert.deepEqual(tree, { + type: 'root', + children: [ + { + type: 'math', + meta: null, + data: { + hName: 'pre', + hChildren: [ + { + type: 'element', + tagName: 'code', + properties: {className: ['language-math', 'math-display']}, + children: [{type: 'text', value: ' \\alpha'}] + } + ] }, - ' \\alpha' - ) - ]) - ) + value: ' \\alpha' + } + ] + }) }) await t.test('should stringify inline and block math', async function () { assert.deepEqual( - unified() - .use(remarkParse) - .use(remarkStringify) - .use(remarkMath) - .processSync('Math $\\alpha$\n\n$$\n\\beta+\\gamma\n$$\n') - .toString(), + String( + await unified() + .use(remarkParse) + .use(remarkStringify) + .use(remarkMath) + .process('Math $\\alpha$\n\n$$\n\\beta+\\gamma\n$$\n') + ), 'Math $\\alpha$\n\n$$\n\\beta+\\gamma\n$$\n' ) }) @@ -436,12 +460,13 @@ test('remarkMath', async function (t) { 'should support `singleDollarTextMath: false` (1)', async function () { assert.deepEqual( - unified() - .use(remarkParse) - .use(remarkStringify) - .use(remarkMath, {singleDollarTextMath: false}) - .processSync('Math $\\alpha$\n\n$$\\beta+\\gamma$$\n') - .toString(), + String( + await unified() + .use(remarkParse) + .use(remarkStringify) + .use(remarkMath, {singleDollarTextMath: false}) + .process('Math $\\alpha$\n\n$$\\beta+\\gamma$$\n') + ), 'Math $\\alpha$\n\n$$\\beta+\\gamma$$\n' ) } @@ -451,14 +476,15 @@ test('remarkMath', async function (t) { 'should support `singleDollarTextMath: false` (2)', async function () { assert.deepEqual( - unified() - .use(remarkParse) - .use(remarkMath, {singleDollarTextMath: false}) - // @ts-expect-error: to do: remove when `remark-rehype` is released. - .use(remarkRehype) - .use(rehypeStringify) - .processSync('Math $\\alpha$\n\n$$\\beta+\\gamma$$\n') - .toString(), + String( + await unified() + .use(remarkParse) + .use(remarkMath, {singleDollarTextMath: false}) + // @ts-expect-error: to do: remove when `remark-rehype` is released. + .use(remarkRehype) + .use(rehypeStringify) + .process('Math $\\alpha$\n\n$$\\beta+\\gamma$$\n') + ), '<p>Math $\\alpha$</p>\n<p><code class="language-math math-inline">\\beta+\\gamma</code></p>' ) } @@ -466,12 +492,13 @@ test('remarkMath', async function (t) { await t.test('should stringify math in a blockquote', async function () { assert.deepEqual( - unified() - .use(remarkParse) - .use(remarkStringify) - .use(remarkMath) - .processSync('> $$\n> \\alpha\\beta\n> $$\n') - .toString(), + String( + await unified() + .use(remarkParse) + .use(remarkStringify) + .use(remarkMath) + .process('> $$\n> \\alpha\\beta\n> $$\n') + ), '> $$\n> \\alpha\\beta\n> $$\n' ) }) @@ -480,7 +507,7 @@ test('remarkMath', async function (t) { 'should support an opening fence w/ meta, w/o closing fence', async function () { assert.deepEqual( - String(toHtml.processSync('$$just two dollars')), + String(await toHtml.process('$$just two dollars')), '<pre><code class="language-math math-display"></code></pre>' ) } @@ -488,7 +515,7 @@ test('remarkMath', async function (t) { await t.test('should support `meta`', async function () { assert.deepEqual( - String(toHtml.processSync('$$ must\n\\alpha\n$$')), + String(await toHtml.process('$$ must\n\\alpha\n$$')), '<pre><code class="language-math math-display">\\alpha</code></pre>' ) }) @@ -497,7 +524,7 @@ test('remarkMath', async function (t) { 'should include values after the opening fence', async function () { assert.deepEqual( - String(toHtml.processSync('$$ \n\\alpha\n$$')), + String(await toHtml.process('$$ \n\\alpha\n$$')), '<pre><code class="language-math math-display">\\alpha</code></pre>' ) } @@ -507,7 +534,7 @@ test('remarkMath', async function (t) { 'should not support values before the closing fence', async function () { assert.deepEqual( - String(toHtml.processSync('$$\n\\alpha\nmust $$')), + String(await toHtml.process('$$\n\\alpha\nmust $$')), '<pre><code class="language-math math-display">\\alpha\nmust $$</code></pre>' ) } @@ -517,7 +544,7 @@ test('remarkMath', async function (t) { 'should include values before the closing fence (except for spacing #2)', async function () { assert.deepEqual( - String(toHtml.processSync('$$\n\\alpha\n $$')), + String(await toHtml.process('$$\n\\alpha\n $$')), '<pre><code class="language-math math-display">\\alpha</code></pre>' ) } @@ -527,7 +554,7 @@ test('remarkMath', async function (t) { 'should exclude spacing after the closing fence', async function () { assert.deepEqual( - String(toHtml.processSync('$$\n\\alpha\n$$ ')), + String(await toHtml.process('$$\n\\alpha\n$$ ')), '<pre><code class="language-math math-display">\\alpha</code></pre>' ) } @@ -541,30 +568,28 @@ test('remarkMath', async function (t) { removePosition(tree, {force: true}) - assert.deepEqual( - tree, - u('root', [ - u( - 'math', - { - meta: null, - data: { - hName: 'pre', - hChildren: [ - { - type: 'element', - tagName: 'code', - properties: {className: ['language-math', 'math-display']}, - children: [{type: 'text', value: '\\alpha'}] - } - ] - } + assert.deepEqual(tree, { + type: 'root', + children: [ + { + type: 'math', + meta: null, + data: { + hName: 'pre', + hChildren: [ + { + type: 'element', + tagName: 'code', + properties: {className: ['language-math', 'math-display']}, + children: [{type: 'text', value: '\\alpha'}] + } + ] }, - '\\alpha' - ), - u('code', {lang: null, meta: null}, 'bravo') - ]) - ) + value: '\\alpha' + }, + {type: 'code', lang: null, meta: null, value: 'bravo'} + ] + }) }) await t.test( @@ -577,39 +602,48 @@ test('remarkMath', async function (t) { removePosition(tree, {force: true}) - assert.deepEqual( - tree, - u('root', [ - u('paragraph', [ - u( - 'inlineMath', + assert.deepEqual(tree, { + type: 'root', + children: [ + { + type: 'paragraph', + children: [ { + type: 'inlineMath', data: { hName: 'code', hProperties: {className: ['language-math', 'math-inline']}, - hChildren: [u('text', '\\alpha')] - } - }, - '\\alpha' - ) - ]) - ]) - ) + hChildren: [{type: 'text', value: '\\alpha'}] + }, + value: '\\alpha' + } + ] + } + ] + }) } ) await t.test('should stringify a tree', async function () { assert.deepEqual( - unified() - .use(remarkStringify) - .use(remarkMath) - .stringify( - u('root', [ - u('paragraph', [u('text', 'Math '), u('inlineMath', '\\alpha')]), - u('math', '\\beta+\\gamma') - ]) - ) - .toString(), + String( + await unified() + .use(remarkStringify) + .use(remarkMath) + .stringify({ + type: 'root', + children: [ + { + type: 'paragraph', + children: [ + {type: 'text', value: 'Math '}, + {type: 'inlineMath', value: '\\alpha'} + ] + }, + {type: 'math', value: '\\beta+\\gamma'} + ] + }) + ), 'Math $\\alpha$\n\n$$\n\\beta+\\gamma\n$$\n' ) }) @@ -618,12 +652,13 @@ test('remarkMath', async function (t) { 'should stringify inline math with double dollars using one dollar by default', async function () { assert.deepEqual( - unified() - .use(remarkParse) - .use(remarkStringify) - .use(remarkMath) - .processSync('$$\\alpha$$') - .toString(), + String( + await unified() + .use(remarkParse) + .use(remarkStringify) + .use(remarkMath) + .process('$$\\alpha$$') + ), '$\\alpha$\n' ) } @@ -633,12 +668,13 @@ test('remarkMath', async function (t) { 'should stringify inline math with double dollars using one dollar', async function () { assert.deepEqual( - unified() - .use(remarkParse) - .use(remarkStringify) - .use(remarkMath) - .processSync('$$\\alpha$$') - .toString(), + String( + await unified() + .use(remarkParse) + .use(remarkStringify) + .use(remarkMath) + .process('$$\\alpha$$') + ), '$\\alpha$\n' ) } @@ -646,14 +682,14 @@ test('remarkMath', async function (t) { await t.test('should do markdown-it-katex#01', async function () { assert.deepEqual( - String(toHtml.processSync('$1+1 = 2$')), + String(await toHtml.process('$1+1 = 2$')), '<p><code class="language-math math-inline">1+1 = 2</code></p>' ) }) await t.test('should do markdown-it-katex#02 (deviation)', async function () { assert.deepEqual( - String(toHtml.processSync('$$1+1 = 2$$')), + String(await toHtml.process('$$1+1 = 2$$')), '<p><code class="language-math math-inline">1+1 = 2</code></p>' ) }) @@ -662,7 +698,7 @@ test('remarkMath', async function (t) { 'should do markdown-it-katex#03: no whitespace before and after is fine', async function () { assert.deepEqual( - String(toHtml.processSync('foo$1+1 = 2$bar')), + String(await toHtml.process('foo$1+1 = 2$bar')), '<p>foo<code class="language-math math-inline">1+1 = 2</code>bar</p>' ) } @@ -672,7 +708,7 @@ test('remarkMath', async function (t) { 'should do markdown-it-katex#04: even when it starts with a negative sign', async function () { assert.deepEqual( - String(toHtml.processSync('foo$-1+1 = 2$bar')), + String(await toHtml.process('foo$-1+1 = 2$bar')), '<p>foo<code class="language-math math-inline">-1+1 = 2</code>bar</p>' ) } @@ -682,7 +718,7 @@ test('remarkMath', async function (t) { 'should do markdown-it-katex#05: shouldn’t render empty content', async function () { assert.deepEqual( - String(toHtml.processSync('aaa $$ bbb')), + String(await toHtml.process('aaa $$ bbb')), '<p>aaa $$ bbb</p>' ) } @@ -692,7 +728,7 @@ test('remarkMath', async function (t) { 'should do markdown-it-katex#06: should require a closing delimiter', async function () { assert.deepEqual( - String(toHtml.processSync('aaa $5.99 bbb')), + String(await toHtml.process('aaa $5.99 bbb')), '<p>aaa $5.99 bbb</p>' ) } @@ -702,7 +738,7 @@ test('remarkMath', async function (t) { 'should do markdown-it-katex#07: paragraph break in inline math is not allowed', async function () { assert.deepEqual( - String(toHtml.processSync('foo $1+1\n\n= 2$ bar')), + String(await toHtml.process('foo $1+1\n\n= 2$ bar')), '<p>foo $1+1</p>\n<p>= 2$ bar</p>' ) } @@ -712,7 +748,7 @@ test('remarkMath', async function (t) { 'should do markdown-it-katex#08: inline math with apparent markup should not be processed', async function () { assert.deepEqual( - String(toHtml.processSync('foo $1 *i* 1$ bar')), + String(await toHtml.process('foo $1 *i* 1$ bar')), '<p>foo <code class="language-math math-inline">1 *i* 1</code> bar</p>' ) } @@ -722,7 +758,7 @@ test('remarkMath', async function (t) { 'should do markdown-it-katex#09: block math can be indented up to 3 spaces', async function () { assert.deepEqual( - String(toHtml.processSync(' $$\n 1+1 = 2\n $$')), + String(await toHtml.process(' $$\n 1+1 = 2\n $$')), '<pre><code class="language-math math-display">1+1 = 2</code></pre>' ) } @@ -732,7 +768,7 @@ test('remarkMath', async function (t) { 'should do markdown-it-katex#10: …but 4 means a code block', async function () { assert.deepEqual( - String(toHtml.processSync(' $$\n 1+1 = 2\n $$')), + String(await toHtml.process(' $$\n 1+1 = 2\n $$')), '<pre><code>$$\n1+1 = 2\n$$\n</code></pre>' ) } @@ -742,7 +778,7 @@ test('remarkMath', async function (t) { 'should do markdown-it-katex#11: multiline inline math', async function () { assert.deepEqual( - String(toHtml.processSync('foo $1 + 1\n= 2$ bar')), + String(await toHtml.process('foo $1 + 1\n= 2$ bar')), '<p>foo <code class="language-math math-inline">1 + 1\n= 2</code> bar</p>' ) } @@ -752,7 +788,7 @@ test('remarkMath', async function (t) { 'should do markdown-it-katex#12: multiline display math', async function () { assert.deepEqual( - String(toHtml.processSync('$$\n\n 1\n+ 1\n\n= 2\n\n$$')), + String(await toHtml.process('$$\n\n 1\n+ 1\n\n= 2\n\n$$')), '<pre><code class="language-math math-display">\n 1\n+ 1\n\n= 2\n</code></pre>' ) } @@ -762,7 +798,7 @@ test('remarkMath', async function (t) { 'should do markdown-it-katex#13: text can immediately follow inline math', async function () { assert.deepEqual( - String(toHtml.processSync('$n$-th order')), + String(await toHtml.process('$n$-th order')), '<p><code class="language-math math-inline">n</code>-th order</p>' ) } @@ -772,7 +808,7 @@ test('remarkMath', async function (t) { 'should do markdown-it-katex#14: display math self-closes at the end of document', async function () { assert.deepEqual( - String(toHtml.processSync('$$\n1+1 = 2')), + String(await toHtml.process('$$\n1+1 = 2')), '<pre><code class="language-math math-display">1+1 = 2</code></pre>' ) } @@ -782,7 +818,7 @@ test('remarkMath', async function (t) { 'should do markdown-it-katex#15: display and inline math can appear in lists', async function () { assert.deepEqual( - String(toHtml.processSync('* $1+1 = 2$\n* $$\n 1+1 = 2\n $$')), + String(await toHtml.process('* $1+1 = 2$\n* $$\n 1+1 = 2\n $$')), '<ul>\n<li><code class="language-math math-inline">1+1 = 2</code></li>\n<li>\n<pre><code class="language-math math-display">1+1 = 2</code></pre>\n</li>\n</ul>' ) } @@ -792,7 +828,7 @@ test('remarkMath', async function (t) { 'should do markdown-it-katex#16: display math can be written in one line (deviation)', async function () { assert.deepEqual( - String(toHtml.processSync('$$1+1 = 2$$')), + String(await toHtml.process('$$1+1 = 2$$')), '<p><code class="language-math math-inline">1+1 = 2</code></p>' ) } @@ -803,7 +839,7 @@ test('remarkMath', async function (t) { async function () { // To do: this is broken. assert.deepEqual( - String(toHtml.processSync('$$\n[\n[1, 2]\n[3, 4]\n]\n$$')), + String(await toHtml.process('$$\n[\n[1, 2]\n[3, 4]\n]\n$$')), '<pre><code class="language-math math-display">[\n[1, 2]\n[3, 4]\n]</code></pre>' ) } @@ -813,7 +849,7 @@ test('remarkMath', async function (t) { 'should do markdown-it-katex#18: escaped delimiters should not render math (deviated)', async function () { assert.deepEqual( - String(toHtml.processSync('Foo \\$1$ bar\n\\$\\$\n1\n\\$\\$')), + String(await toHtml.process('Foo \\$1$ bar\n\\$\\$\n1\n\\$\\$')), '<p>Foo $1<code class="language-math math-inline"> bar\n\\</code>$\n1\n$$</p>' ) } @@ -824,7 +860,7 @@ test('remarkMath', async function (t) { async function () { assert.deepEqual( String( - toHtml.processSync( + await toHtml.process( 'Thus, $20,000 and USD$30,000 won’t parse as math.' ) ), @@ -837,7 +873,7 @@ test('remarkMath', async function (t) { 'should do markdown-it-katex#20: require non whitespace to right of opening inline math (deviated)', async function () { assert.deepEqual( - String(toHtml.processSync('It is 2$ for a can of soda, not 1$.')), + String(await toHtml.process('It is 2$ for a can of soda, not 1$.')), '<p>It is 2<code class="language-math math-inline"> for a can of soda, not 1</code>.</p>' ) } @@ -848,7 +884,7 @@ test('remarkMath', async function (t) { async function () { assert.deepEqual( String( - toHtml.processSync( + await toHtml.process( 'I’ll give $20 today, if you give me more $ tomorrow.' ) ), @@ -862,7 +898,7 @@ test('remarkMath', async function (t) { async function () { // #22 “inline blockmath is not (currently) registered” <-- we do support it! assert.deepEqual( - String(toHtml.processSync('Money adds: $\\$X + \\$Y = \\$Z$.')), + String(await toHtml.process('Money adds: $\\$X + \\$Y = \\$Z$.')), '<p>Money adds: <code class="language-math math-inline">\\</code>X + $Y = $Z$.</p>' ) } @@ -873,7 +909,7 @@ test('remarkMath', async function (t) { async function () { assert.deepEqual( String( - toHtml.processSync( + await toHtml.process( 'Weird-o: $\\displaystyle{\\begin{pmatrix} \\$ & 1\\\\\\$ \\end{pmatrix}}$.' ) ), diff --git a/tsconfig.json b/tsconfig.json index 93d2959..617fe63 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,7 @@ "exactOptionalPropertyTypes": true, "lib": ["es2020"], "module": "node16", - // To do: remove when `hast-util-from-parse5` is updated. + // To do: remove when `mathjax-full` types are fixed. "skipLibCheck": true, "strict": true, "target": "es2020" From 232f0d7ee05dcbcf7d06c1915538e7592c70f210 Mon Sep 17 00:00:00 2001 From: Titus Wormer <tituswormer@gmail.com> Date: Mon, 18 Sep 2023 17:03:12 +0200 Subject: [PATCH 92/98] Remove `throwOnError` option, improve message This improves KaTeX errors and removes the `throwOnError` option: messages are always emitted as warnings. --- packages/rehype-katex/index.js | 40 ++++++++++------- packages/rehype-katex/readme.md | 5 --- packages/rehype-katex/test.js | 78 ++++++++++++++++----------------- tsconfig.json | 2 +- 4 files changed, 63 insertions(+), 62 deletions(-) diff --git a/packages/rehype-katex/index.js b/packages/rehype-katex/index.js index 7f0fe78..d60ab01 100644 --- a/packages/rehype-katex/index.js +++ b/packages/rehype-katex/index.js @@ -2,11 +2,15 @@ * @typedef {import('hast').ElementContent} ElementContent * @typedef {import('hast').Root} Root * - * @typedef {import('katex').KatexOptions} Options + * @typedef {import('katex').KatexOptions} KatexOptions * * @typedef {import('vfile').VFile} VFile */ +/** + * @typedef {Omit<KatexOptions, 'throwOnError'>} Options + */ + import {fromHtmlIsomorphic} from 'hast-util-from-html-isomorphic' import {toText} from 'hast-util-to-text' import katex from 'katex' @@ -28,7 +32,6 @@ const emptyClasses = [] */ export default function rehypeKatex(options) { const settings = options || emptyOptions - const throwOnError = settings.throwOnError || false /** * Transform. @@ -41,7 +44,7 @@ export default function rehypeKatex(options) { * Nothing. */ return function (tree, file) { - visit(tree, 'element', function (element) { + visit(tree, 'element', function (element, _, parent) { const classes = Array.isArray(element.properties.className) ? element.properties.className : emptyClasses @@ -64,16 +67,30 @@ export default function rehypeKatex(options) { throwOnError: true }) } catch (error) { - const exception = /** @type {Error} */ (error) - const fn = throwOnError ? 'fail' : 'message' - const origin = ['rehype-katex', exception.name.toLowerCase()].join(':') + const cause = /** @type {Error} */ (error) + const ruleId = cause.name.toLowerCase() - file[fn](exception.message, element.position, origin) + file.message('Could not render math with KaTeX', { + /* c8 ignore next -- verbose to test */ + ancestors: parent ? [parent, element] : [element], + cause, + place: element.position, + ruleId, + source: 'rehype-katex' + }) // KaTeX can handle `ParseError` itself, but not others. + if (ruleId === 'parseerror') { + result = katex.renderToString(value, { + ...settings, + displayMode, + strict: 'ignore', + throwOnError: false + }) + } // Generate similar markup if this is an other error. // See: <https://github.com/KaTeX/KaTeX/blob/5dc7af0/docs/error.md>. - if (exception.name !== 'ParseError') { + else { element.children = [ { type: 'element', @@ -88,13 +105,6 @@ export default function rehypeKatex(options) { ] return } - - result = katex.renderToString(value, { - ...settings, - displayMode, - strict: 'ignore', - throwOnError: false - }) } const root = fromHtmlIsomorphic(result, {fragment: true}) diff --git a/packages/rehype-katex/readme.md b/packages/rehype-katex/readme.md index 845398a..bf11238 100644 --- a/packages/rehype-katex/readme.md +++ b/packages/rehype-katex/readme.md @@ -144,11 +144,6 @@ Transform `<span class="math-inline">` and `<div class="math-display">` with Configuration (optional). All options, except for `displayMode`, are passed to [KaTeX][katex-options]. -###### `options.throwOnError` - -Throw if a KaTeX parse error occurs (`boolean`, default: `false`). -See [KaTeX options][katex-options]. - ## CSS The HTML produced by KaTeX requires CSS to render correctly. diff --git a/packages/rehype-katex/test.js b/packages/rehype-katex/test.js index 9c5f40b..f7bbb08 100644 --- a/packages/rehype-katex/test.js +++ b/packages/rehype-katex/test.js @@ -142,15 +142,15 @@ test('rehype-katex', async function (t) { ) }) - await t.test('should support `errorColor`', async function () { + await t.test('should handle errors, support `errorColor`', async function () { + const file = await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex, {errorColor: 'orange'}) + .use(rehypeStringify) + .process('<code class="math-inline">\\alpa</code>') + assert.deepEqual( - String( - await unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex, {errorColor: 'orange'}) - .use(rehypeStringify) - .process('<code class="math-inline">\\alpa</code>') - ), + String(file), String( await unified() .use(rehypeParse, {fragment: true}) @@ -165,41 +165,37 @@ test('rehype-katex', async function (t) { ) ) ) - }) - - await t.test('should create a message for errors', async function () { - const file = await unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex) - .use(rehypeStringify) - .process('<p>Lorem</p>\n<p><code class="math-inline">\\alpa</code></p>') - assert.deepEqual(file.messages.map(String), [ - '2:4-2:42: KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲' - ]) - }) - - await t.test( - 'should throw an error if `throwOnError: true`', - async function () { - try { - await unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex, {throwOnError: true}) - .use(rehypeStringify) - .process( - '<p>Lorem</p>\n<p><code class="math-inline">\\alpa</code></p>' - ) - /* c8 ignore next 2 -- some c8 bug. */ - assert.fail() - } catch (error) { - assert.match( - String(error), - /KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲/ - ) + assert.equal(file.messages.length, 1) + const message = file.messages[0] + assert(message) + assert(message.cause) + assert(message.ancestors) + assert.match( + String(message.cause), + /KaTeX parse error: Undefined control sequence/ + ) + assert.equal(message.ancestors.length, 2) + assert.deepEqual( + {...file.messages[0], cause: undefined, ancestors: []}, + { + ancestors: [], + cause: undefined, + column: 1, + fatal: false, + line: 1, + message: 'Could not render math with KaTeX', + name: '1:1-1:39', + place: { + start: {column: 1, line: 1, offset: 0}, + end: {column: 39, line: 1, offset: 38} + }, + reason: 'Could not render math with KaTeX', + ruleId: 'parseerror', + source: 'rehype-katex' } - } - ) + ) + }) await t.test('should support `strict: ignore`', async function () { assert.deepEqual( diff --git a/tsconfig.json b/tsconfig.json index 617fe63..4ade2fc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,7 @@ "declaration": true, "emitDeclarationOnly": true, "exactOptionalPropertyTypes": true, - "lib": ["es2020"], + "lib": ["es2022"], "module": "node16", // To do: remove when `mathjax-full` types are fixed. "skipLibCheck": true, From e8039d7459e4fcda53d03e516a74a13974ada4b7 Mon Sep 17 00:00:00 2001 From: Titus Wormer <tituswormer@gmail.com> Date: Mon, 18 Sep 2023 17:03:41 +0200 Subject: [PATCH 93/98] Change to require Node.js 16 --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 4ade2fc..6d8e683 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,7 @@ // To do: remove when `mathjax-full` types are fixed. "skipLibCheck": true, "strict": true, - "target": "es2020" + "target": "es2022" }, "exclude": ["coverage/", "node_modules/"], "include": ["**/*.js"] From 23914b6741409ed7436cc37a960309615a68990f Mon Sep 17 00:00:00 2001 From: Titus Wormer <tituswormer@gmail.com> Date: Mon, 18 Sep 2023 17:08:32 +0200 Subject: [PATCH 94/98] Refactor to move code to `lib/` --- packages/rehype-katex/index.js | 115 +----------------------- packages/rehype-katex/lib/index.js | 116 +++++++++++++++++++++++++ packages/rehype-katex/package.json | 1 + packages/rehype-mathjax/browser.js | 22 +---- packages/rehype-mathjax/chtml.js | 16 +--- packages/rehype-mathjax/index.js | 2 +- packages/rehype-mathjax/lib/browser.js | 24 +++++ packages/rehype-mathjax/lib/chtml.js | 15 ++++ packages/rehype-mathjax/lib/svg.js | 10 +++ packages/rehype-mathjax/svg.js | 11 +-- packages/remark-math/index.js | 41 +-------- packages/remark-math/lib/index.js | 42 +++++++++ 12 files changed, 216 insertions(+), 199 deletions(-) create mode 100644 packages/rehype-katex/lib/index.js create mode 100644 packages/rehype-mathjax/lib/browser.js create mode 100644 packages/rehype-mathjax/lib/chtml.js create mode 100644 packages/rehype-mathjax/lib/svg.js create mode 100644 packages/remark-math/lib/index.js diff --git a/packages/rehype-katex/index.js b/packages/rehype-katex/index.js index d60ab01..a49a278 100644 --- a/packages/rehype-katex/index.js +++ b/packages/rehype-katex/index.js @@ -1,116 +1,5 @@ /** - * @typedef {import('hast').ElementContent} ElementContent - * @typedef {import('hast').Root} Root - * - * @typedef {import('katex').KatexOptions} KatexOptions - * - * @typedef {import('vfile').VFile} VFile + * @typedef {import('./lib/index.js').Options} Options */ -/** - * @typedef {Omit<KatexOptions, 'throwOnError'>} Options - */ - -import {fromHtmlIsomorphic} from 'hast-util-from-html-isomorphic' -import {toText} from 'hast-util-to-text' -import katex from 'katex' -import {visit} from 'unist-util-visit' - -/** @type {Readonly<Options>} */ -const emptyOptions = {} -/** @type {ReadonlyArray<unknown>} */ -const emptyClasses = [] - -/** - * Plugin to transform `<span class=math-inline>` and `<div class=math-display>` - * with KaTeX. - * - * @param {Readonly<Options> | null | undefined} [options] - * Configuration (optional). - * @returns - * Transform. - */ -export default function rehypeKatex(options) { - const settings = options || emptyOptions - - /** - * Transform. - * - * @param {Root} tree - * Tree. - * @param {VFile} file - * File. - * @returns {undefined} - * Nothing. - */ - return function (tree, file) { - visit(tree, 'element', function (element, _, parent) { - const classes = Array.isArray(element.properties.className) - ? element.properties.className - : emptyClasses - const inline = classes.includes('math-inline') - const displayMode = classes.includes('math-display') - - if (!inline && !displayMode) { - return - } - - const value = toText(element, {whitespace: 'pre'}) - - /** @type {string} */ - let result - - try { - result = katex.renderToString(value, { - ...settings, - displayMode, - throwOnError: true - }) - } catch (error) { - const cause = /** @type {Error} */ (error) - const ruleId = cause.name.toLowerCase() - - file.message('Could not render math with KaTeX', { - /* c8 ignore next -- verbose to test */ - ancestors: parent ? [parent, element] : [element], - cause, - place: element.position, - ruleId, - source: 'rehype-katex' - }) - - // KaTeX can handle `ParseError` itself, but not others. - if (ruleId === 'parseerror') { - result = katex.renderToString(value, { - ...settings, - displayMode, - strict: 'ignore', - throwOnError: false - }) - } - // Generate similar markup if this is an other error. - // See: <https://github.com/KaTeX/KaTeX/blob/5dc7af0/docs/error.md>. - else { - element.children = [ - { - type: 'element', - tagName: 'span', - properties: { - className: ['katex-error'], - style: 'color:' + (settings.errorColor || '#cc0000'), - title: String(error) - }, - children: [{type: 'text', value}] - } - ] - return - } - } - - const root = fromHtmlIsomorphic(result, {fragment: true}) - // Cast because there will not be `doctypes` in KaTeX result. - const content = /** @type {Array<ElementContent>} */ (root.children) - element.children = content - }) - } -} +export {default} from './lib/index.js' diff --git a/packages/rehype-katex/lib/index.js b/packages/rehype-katex/lib/index.js new file mode 100644 index 0000000..d60ab01 --- /dev/null +++ b/packages/rehype-katex/lib/index.js @@ -0,0 +1,116 @@ +/** + * @typedef {import('hast').ElementContent} ElementContent + * @typedef {import('hast').Root} Root + * + * @typedef {import('katex').KatexOptions} KatexOptions + * + * @typedef {import('vfile').VFile} VFile + */ + +/** + * @typedef {Omit<KatexOptions, 'throwOnError'>} Options + */ + +import {fromHtmlIsomorphic} from 'hast-util-from-html-isomorphic' +import {toText} from 'hast-util-to-text' +import katex from 'katex' +import {visit} from 'unist-util-visit' + +/** @type {Readonly<Options>} */ +const emptyOptions = {} +/** @type {ReadonlyArray<unknown>} */ +const emptyClasses = [] + +/** + * Plugin to transform `<span class=math-inline>` and `<div class=math-display>` + * with KaTeX. + * + * @param {Readonly<Options> | null | undefined} [options] + * Configuration (optional). + * @returns + * Transform. + */ +export default function rehypeKatex(options) { + const settings = options || emptyOptions + + /** + * Transform. + * + * @param {Root} tree + * Tree. + * @param {VFile} file + * File. + * @returns {undefined} + * Nothing. + */ + return function (tree, file) { + visit(tree, 'element', function (element, _, parent) { + const classes = Array.isArray(element.properties.className) + ? element.properties.className + : emptyClasses + const inline = classes.includes('math-inline') + const displayMode = classes.includes('math-display') + + if (!inline && !displayMode) { + return + } + + const value = toText(element, {whitespace: 'pre'}) + + /** @type {string} */ + let result + + try { + result = katex.renderToString(value, { + ...settings, + displayMode, + throwOnError: true + }) + } catch (error) { + const cause = /** @type {Error} */ (error) + const ruleId = cause.name.toLowerCase() + + file.message('Could not render math with KaTeX', { + /* c8 ignore next -- verbose to test */ + ancestors: parent ? [parent, element] : [element], + cause, + place: element.position, + ruleId, + source: 'rehype-katex' + }) + + // KaTeX can handle `ParseError` itself, but not others. + if (ruleId === 'parseerror') { + result = katex.renderToString(value, { + ...settings, + displayMode, + strict: 'ignore', + throwOnError: false + }) + } + // Generate similar markup if this is an other error. + // See: <https://github.com/KaTeX/KaTeX/blob/5dc7af0/docs/error.md>. + else { + element.children = [ + { + type: 'element', + tagName: 'span', + properties: { + className: ['katex-error'], + style: 'color:' + (settings.errorColor || '#cc0000'), + title: String(error) + }, + children: [{type: 'text', value}] + } + ] + return + } + } + + const root = fromHtmlIsomorphic(result, {fragment: true}) + // Cast because there will not be `doctypes` in KaTeX result. + const content = /** @type {Array<ElementContent>} */ (root.children) + element.children = content + }) + } +} diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index d030eb7..55299a8 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -34,6 +34,7 @@ "main": "index.js", "types": "index.d.ts", "files": [ + "lib/", "index.d.ts", "index.js" ], diff --git a/packages/rehype-mathjax/browser.js b/packages/rehype-mathjax/browser.js index 6481d87..8937594 100644 --- a/packages/rehype-mathjax/browser.js +++ b/packages/rehype-mathjax/browser.js @@ -1,25 +1,5 @@ /** * @typedef {import('./lib/create-plugin.js').Options} Options - * @typedef {import('./lib/create-plugin.js').InputTexOptions} InputTexOptions */ -import {createPlugin} from './lib/create-plugin.js' - -/** @type {Readonly<InputTexOptions>} */ -const emptyTexOptions = {} - -const rehypeMathJaxBrowser = createPlugin(function (options) { - const tex = options.tex || emptyTexOptions - const display = tex.displayMath || [['\\[', '\\]']] - const inline = tex.inlineMath || [['\\(', '\\)']] - - return { - render(node, options) { - const delimiters = (options.display ? display : inline)[0] - node.children.unshift({type: 'text', value: delimiters[0]}) - node.children.push({type: 'text', value: delimiters[1]}) - } - } -}) - -export default rehypeMathJaxBrowser +export {default} from './lib/browser.js' diff --git a/packages/rehype-mathjax/chtml.js b/packages/rehype-mathjax/chtml.js index 75946ad..8845600 100644 --- a/packages/rehype-mathjax/chtml.js +++ b/packages/rehype-mathjax/chtml.js @@ -2,18 +2,4 @@ * @typedef {import('./lib/create-plugin.js').Options} Options */ -import {CHTML} from 'mathjax-full/js/output/chtml.js' -import {createPlugin} from './lib/create-plugin.js' -import {createRenderer} from './lib/create-renderer.js' - -const rehypeMathJaxCHtml = createPlugin(function (options) { - if (!options.chtml || !options.chtml.fontURL) { - throw new Error( - 'rehype-mathjax: missing `fontURL` in options, which must be set to a URL to reach MathJaX fonts' - ) - } - - return createRenderer(options, new CHTML(options.chtml)) -}) - -export default rehypeMathJaxCHtml +export {default} from './lib/chtml.js' diff --git a/packages/rehype-mathjax/index.js b/packages/rehype-mathjax/index.js index 05dedc2..9c3f749 100644 --- a/packages/rehype-mathjax/index.js +++ b/packages/rehype-mathjax/index.js @@ -2,4 +2,4 @@ * @typedef {import('./lib/create-plugin.js').Options} Options */ -export {default} from './svg.js' +export {default} from './lib/svg.js' diff --git a/packages/rehype-mathjax/lib/browser.js b/packages/rehype-mathjax/lib/browser.js new file mode 100644 index 0000000..09b25ae --- /dev/null +++ b/packages/rehype-mathjax/lib/browser.js @@ -0,0 +1,24 @@ +/** + * @typedef {import('./create-plugin.js').InputTexOptions} InputTexOptions + */ + +import {createPlugin} from './create-plugin.js' + +/** @type {Readonly<InputTexOptions>} */ +const emptyTexOptions = {} + +const rehypeMathJaxBrowser = createPlugin(function (options) { + const tex = options.tex || emptyTexOptions + const display = tex.displayMath || [['\\[', '\\]']] + const inline = tex.inlineMath || [['\\(', '\\)']] + + return { + render(node, options) { + const delimiters = (options.display ? display : inline)[0] + node.children.unshift({type: 'text', value: delimiters[0]}) + node.children.push({type: 'text', value: delimiters[1]}) + } + } +}) + +export default rehypeMathJaxBrowser diff --git a/packages/rehype-mathjax/lib/chtml.js b/packages/rehype-mathjax/lib/chtml.js new file mode 100644 index 0000000..75e64a1 --- /dev/null +++ b/packages/rehype-mathjax/lib/chtml.js @@ -0,0 +1,15 @@ +import {CHTML} from 'mathjax-full/js/output/chtml.js' +import {createPlugin} from './create-plugin.js' +import {createRenderer} from './create-renderer.js' + +const rehypeMathJaxCHtml = createPlugin(function (options) { + if (!options.chtml || !options.chtml.fontURL) { + throw new Error( + 'rehype-mathjax: missing `fontURL` in options, which must be set to a URL to reach MathJaX fonts' + ) + } + + return createRenderer(options, new CHTML(options.chtml)) +}) + +export default rehypeMathJaxCHtml diff --git a/packages/rehype-mathjax/lib/svg.js b/packages/rehype-mathjax/lib/svg.js new file mode 100644 index 0000000..9a95d6e --- /dev/null +++ b/packages/rehype-mathjax/lib/svg.js @@ -0,0 +1,10 @@ +import {SVG} from 'mathjax-full/js/output/svg.js' +import {createPlugin} from './create-plugin.js' +import {createRenderer} from './create-renderer.js' + +const rehypeMathJaxSvg = createPlugin(function (options) { + // MathJax types do not allow `null`. + return createRenderer(options, new SVG(options.svg || undefined)) +}) + +export default rehypeMathJaxSvg diff --git a/packages/rehype-mathjax/svg.js b/packages/rehype-mathjax/svg.js index 27e6fb2..9c3f749 100644 --- a/packages/rehype-mathjax/svg.js +++ b/packages/rehype-mathjax/svg.js @@ -2,13 +2,4 @@ * @typedef {import('./lib/create-plugin.js').Options} Options */ -import {SVG} from 'mathjax-full/js/output/svg.js' -import {createPlugin} from './lib/create-plugin.js' -import {createRenderer} from './lib/create-renderer.js' - -const rehypeMathJaxSvg = createPlugin(function (options) { - // `mathjax-types` do not allow `null`. - return createRenderer(options, new SVG(options.svg || undefined)) -}) - -export default rehypeMathJaxSvg +export {default} from './lib/svg.js' diff --git a/packages/remark-math/index.js b/packages/remark-math/index.js index a2cc9a8..a49a278 100644 --- a/packages/remark-math/index.js +++ b/packages/remark-math/index.js @@ -1,42 +1,5 @@ -/// <reference types="mdast-util-math" /> -/// <reference types="remark-parse" /> -/// <reference types="remark-stringify" /> - /** - * @typedef {import('mdast').Root} Root - * @typedef {import('mdast-util-math').ToOptions} Options - * @typedef {import('unified').Processor<Root>} Processor + * @typedef {import('./lib/index.js').Options} Options */ -import {mathFromMarkdown, mathToMarkdown} from 'mdast-util-math' -import {math} from 'micromark-extension-math' - -/** @type {Readonly<Options>} */ -const emptyOptions = {} - -/** - * Plugin to support math. - * - * @param {Readonly<Options> | null | undefined} [options] - * Configuration (optional). - * @returns {undefined} - * Nothing. - */ -export default function remarkMath(options) { - // @ts-expect-error: TS is wrong about `this`. - // eslint-disable-next-line unicorn/no-this-assignment - const self = /** @type {Processor} */ (this) - const settings = options || emptyOptions - const data = self.data() - - const micromarkExtensions = - data.micromarkExtensions || (data.micromarkExtensions = []) - const fromMarkdownExtensions = - data.fromMarkdownExtensions || (data.fromMarkdownExtensions = []) - const toMarkdownExtensions = - data.toMarkdownExtensions || (data.toMarkdownExtensions = []) - - micromarkExtensions.push(math(settings)) - fromMarkdownExtensions.push(mathFromMarkdown()) - toMarkdownExtensions.push(mathToMarkdown(settings)) -} +export {default} from './lib/index.js' diff --git a/packages/remark-math/lib/index.js b/packages/remark-math/lib/index.js new file mode 100644 index 0000000..a2cc9a8 --- /dev/null +++ b/packages/remark-math/lib/index.js @@ -0,0 +1,42 @@ +/// <reference types="mdast-util-math" /> +/// <reference types="remark-parse" /> +/// <reference types="remark-stringify" /> + +/** + * @typedef {import('mdast').Root} Root + * @typedef {import('mdast-util-math').ToOptions} Options + * @typedef {import('unified').Processor<Root>} Processor + */ + +import {mathFromMarkdown, mathToMarkdown} from 'mdast-util-math' +import {math} from 'micromark-extension-math' + +/** @type {Readonly<Options>} */ +const emptyOptions = {} + +/** + * Plugin to support math. + * + * @param {Readonly<Options> | null | undefined} [options] + * Configuration (optional). + * @returns {undefined} + * Nothing. + */ +export default function remarkMath(options) { + // @ts-expect-error: TS is wrong about `this`. + // eslint-disable-next-line unicorn/no-this-assignment + const self = /** @type {Processor} */ (this) + const settings = options || emptyOptions + const data = self.data() + + const micromarkExtensions = + data.micromarkExtensions || (data.micromarkExtensions = []) + const fromMarkdownExtensions = + data.fromMarkdownExtensions || (data.fromMarkdownExtensions = []) + const toMarkdownExtensions = + data.toMarkdownExtensions || (data.toMarkdownExtensions = []) + + micromarkExtensions.push(math(settings)) + fromMarkdownExtensions.push(mathFromMarkdown()) + toMarkdownExtensions.push(mathToMarkdown(settings)) +} From 4223ed9d10257db66fd526716504ce9832f15ff1 Mon Sep 17 00:00:00 2001 From: Titus Wormer <tituswormer@gmail.com> Date: Mon, 18 Sep 2023 17:18:33 +0200 Subject: [PATCH 95/98] Change to use `exports` --- packages/rehype-katex/package.json | 3 +-- packages/rehype-mathjax/index.js | 5 ---- .../lib/create-adapter.browser.js | 1 + .../lib/create-adapter.default.js | 6 +++++ .../lib/create-adaptor.browser.js | 1 - packages/rehype-mathjax/lib/create-adaptor.js | 6 ----- .../rehype-mathjax/lib/create-renderer.js | 10 +++---- packages/rehype-mathjax/package.json | 27 +++++++++++++------ packages/rehype-mathjax/test/index.js | 23 ++++++++-------- packages/remark-math/package.json | 4 +-- 10 files changed, 45 insertions(+), 41 deletions(-) delete mode 100644 packages/rehype-mathjax/index.js create mode 100644 packages/rehype-mathjax/lib/create-adapter.browser.js create mode 100644 packages/rehype-mathjax/lib/create-adapter.default.js delete mode 100644 packages/rehype-mathjax/lib/create-adaptor.browser.js delete mode 100644 packages/rehype-mathjax/lib/create-adaptor.js diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index 55299a8..8eaec3f 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -31,8 +31,7 @@ ], "sideEffects": false, "type": "module", - "main": "index.js", - "types": "index.d.ts", + "exports": "./index.js", "files": [ "lib/", "index.d.ts", diff --git a/packages/rehype-mathjax/index.js b/packages/rehype-mathjax/index.js deleted file mode 100644 index 9c3f749..0000000 --- a/packages/rehype-mathjax/index.js +++ /dev/null @@ -1,5 +0,0 @@ -/** - * @typedef {import('./lib/create-plugin.js').Options} Options - */ - -export {default} from './lib/svg.js' diff --git a/packages/rehype-mathjax/lib/create-adapter.browser.js b/packages/rehype-mathjax/lib/create-adapter.browser.js new file mode 100644 index 0000000..c6729ab --- /dev/null +++ b/packages/rehype-mathjax/lib/create-adapter.browser.js @@ -0,0 +1 @@ +export {browserAdaptor as createAdapter} from 'mathjax-full/js/adaptors/browserAdaptor.js' diff --git a/packages/rehype-mathjax/lib/create-adapter.default.js b/packages/rehype-mathjax/lib/create-adapter.default.js new file mode 100644 index 0000000..0cf7705 --- /dev/null +++ b/packages/rehype-mathjax/lib/create-adapter.default.js @@ -0,0 +1,6 @@ +import {JSDOM} from 'jsdom' +import {jsdomAdaptor as jsdomAdapter} from 'mathjax-full/js/adaptors/jsdomAdaptor.js' + +export function createAdapter() { + return jsdomAdapter(JSDOM) +} diff --git a/packages/rehype-mathjax/lib/create-adaptor.browser.js b/packages/rehype-mathjax/lib/create-adaptor.browser.js deleted file mode 100644 index c38bed8..0000000 --- a/packages/rehype-mathjax/lib/create-adaptor.browser.js +++ /dev/null @@ -1 +0,0 @@ -export {browserAdaptor as createAdaptor} from 'mathjax-full/js/adaptors/browserAdaptor.js' diff --git a/packages/rehype-mathjax/lib/create-adaptor.js b/packages/rehype-mathjax/lib/create-adaptor.js deleted file mode 100644 index 5dfeebe..0000000 --- a/packages/rehype-mathjax/lib/create-adaptor.js +++ /dev/null @@ -1,6 +0,0 @@ -import {JSDOM} from 'jsdom' -import {jsdomAdaptor} from 'mathjax-full/js/adaptors/jsdomAdaptor.js' - -export function createAdaptor() { - return jsdomAdaptor(JSDOM) -} diff --git a/packages/rehype-mathjax/lib/create-renderer.js b/packages/rehype-mathjax/lib/create-renderer.js index cb38b5c..2f4d486 100644 --- a/packages/rehype-mathjax/lib/create-renderer.js +++ b/packages/rehype-mathjax/lib/create-renderer.js @@ -12,11 +12,11 @@ import {RegisterHTMLHandler} from 'mathjax-full/js/handlers/html.js' import {TeX} from 'mathjax-full/js/input/tex.js' import {AllPackages} from 'mathjax-full/js/input/tex/AllPackages.js' import {mathjax} from 'mathjax-full/js/mathjax.js' -import {createAdaptor} from './create-adaptor.js' +import {createAdapter} from '#create-adapter' -const adaptor = createAdaptor() +const adapter = createAdapter() -// To do next major: Keep resultant HTML handler from `register(adaptor)` to +// To do next major: Keep resultant HTML handler from `register(adapter)` to // allow registering the `AssistiveMmlHandler` as in this demo: // <https://github.com/mathjax/MathJax-demos-node/tree/master/direct> // @@ -27,7 +27,7 @@ const adaptor = createAdaptor() // That is to prevent memory leak in `mathjax.handlers` whenever a new instance // of the plugin is used. /* eslint-disable-next-line new-cap */ -RegisterHTMLHandler(adaptor) +RegisterHTMLHandler(adapter) /** * Create a renderer. @@ -56,7 +56,7 @@ export function createRenderer(options, output) { node.children = [hastNode] }, styleSheet() { - const value = adaptor.textContent(output.styleSheet(doc)) + const value = adapter.textContent(output.styleSheet(doc)) return { type: 'element', diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index 44caa6f..9bc9ce2 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -31,22 +31,30 @@ ], "sideEffects": false, "type": "module", - "main": "index.js", - "types": "index.d.ts", + "exports": { + ".": "./svg.js", + "./browser": "./browser.js", + "./chtml": "./chtml.js", + "./svg": "./svg.js" + }, + "imports": { + "#create-adapter": { + "deno": "./lib/create-adapter.default.js", + "react-native": "./lib/create-adapter.default.js", + "worker": "./lib/create-adapter.default.js", + "browser": "./lib/create-adapter.browser.js", + "default": "./lib/create-adapter.default.js" + } + }, "files": [ "lib/", "browser.d.ts", "browser.js", "chtml.d.ts", "chtml.js", - "index.d.ts", - "index.js", "svg.d.ts", "svg.js" ], - "browser": { - "./lib/create-adaptor.js": "./lib/create-adaptor.browser.js" - }, "dependencies": { "@types/hast": "^3.0.0", "@types/mathjax": "^0.0.37", @@ -65,6 +73,9 @@ "test": "npm run build && npm run test-api" }, "xo": { - "prettier": true + "prettier": true, + "rules": { + "n/file-extension-in-import": "off" + } } } diff --git a/packages/rehype-mathjax/test/index.js b/packages/rehype-mathjax/test/index.js index eeccf24..dd5eb19 100644 --- a/packages/rehype-mathjax/test/index.js +++ b/packages/rehype-mathjax/test/index.js @@ -1,9 +1,9 @@ import assert from 'node:assert/strict' import fs from 'node:fs/promises' import test from 'node:test' -import rehypeMathJaxBrowser from 'rehype-mathjax/browser.js' -import rehypeMathJaxChtml from 'rehype-mathjax/chtml.js' -import rehypeMathJaxSvg from 'rehype-mathjax/svg.js' +import rehypeMathJaxBrowser from 'rehype-mathjax/browser' +import rehypeMathJaxChtml from 'rehype-mathjax/chtml' +import rehypeMathJaxSvg from 'rehype-mathjax/svg' import rehypeParse from 'rehype-parse' import rehypeStringify from 'rehype-stringify' import remarkMath from 'remark-math' @@ -24,32 +24,31 @@ test('rehype-mathjax', async function (t) { ) await t.test( - 'should expose the public api for `rehype-mathjax/browser.js`', + 'should expose the public api for `rehype-mathjax/browser`', async function () { assert.deepEqual( - Object.keys(await import('rehype-mathjax/browser.js')).sort(), + Object.keys(await import('rehype-mathjax/browser')).sort(), ['default'] ) } ) await t.test( - 'should expose the public api for `rehype-mathjax/chtml.js`', + 'should expose the public api for `rehype-mathjax/chtml`', async function () { assert.deepEqual( - Object.keys(await import('rehype-mathjax/chtml.js')).sort(), + Object.keys(await import('rehype-mathjax/chtml')).sort(), ['default'] ) } ) await t.test( - 'should expose the public api for `rehype-mathjax/svg.js`', + 'should expose the public api for `rehype-mathjax/svg`', async function () { - assert.deepEqual( - Object.keys(await import('rehype-mathjax/svg.js')).sort(), - ['default'] - ) + assert.deepEqual(Object.keys(await import('rehype-mathjax/svg')).sort(), [ + 'default' + ]) } ) diff --git a/packages/remark-math/package.json b/packages/remark-math/package.json index b3a5621..c1a10c7 100644 --- a/packages/remark-math/package.json +++ b/packages/remark-math/package.json @@ -28,9 +28,9 @@ ], "sideEffects": false, "type": "module", - "main": "index.js", - "types": "index.d.ts", + "exports": "./index.js", "files": [ + "lib/", "index.d.ts", "index.js" ], From f1733469de2f86d1173637cb35b8077dee6d908f Mon Sep 17 00:00:00 2001 From: Titus Wormer <tituswormer@gmail.com> Date: Tue, 19 Sep 2023 12:15:28 +0200 Subject: [PATCH 96/98] Change to replace wrapping elements Previously, spans and divs were generated by `remark-math`. They were left as-is by `rehype-katex` and `rehype-mathjax`. With fc32531, (`<pre>` and) `<code>` elements are generated by `remark-math`. That is to allow folks to use normal markdown, as in ` ```math `, to generate math blocks. However, `<span>` and `<div>` do not contribute to how a document is displayed by browsers, but `<pre>` and `<code>` do. To solve that, this commit *replaces* those elements instead of changing their contents, when using `rehype-katex` or `rehype-mathjax`. --- packages/rehype-katex/lib/index.js | 59 ++++++--- packages/rehype-katex/package.json | 7 +- packages/rehype-katex/test.js | 84 +++++++------ packages/rehype-mathjax/lib/browser.js | 5 +- packages/rehype-mathjax/lib/create-plugin.js | 61 ++++++--- .../rehype-mathjax/lib/create-renderer.js | 10 +- packages/rehype-mathjax/package.json | 5 +- .../test/fixture/document-svg.html | 2 +- .../test/fixture/double-svg.html | 2 +- .../fixture/equation-numbering-1-chtml.html | 6 +- .../fixture/equation-numbering-1-svg.html | 4 +- .../fixture/equation-numbering-2-svg.html | 6 +- .../fixture/markdown-code-fenced-svg.html | 117 ++++++++++++++++++ .../test/fixture/markdown-svg.html | 4 +- .../fixture/small-browser-delimiters.html | 4 +- .../test/fixture/small-browser.html | 4 +- .../test/fixture/small-chtml.html | 6 +- .../test/fixture/small-svg.html | 4 +- packages/rehype-mathjax/test/index.js | 17 +++ packages/remark-math/lib/index.js | 2 +- 20 files changed, 304 insertions(+), 105 deletions(-) create mode 100644 packages/rehype-mathjax/test/fixture/markdown-code-fenced-svg.html diff --git a/packages/rehype-katex/lib/index.js b/packages/rehype-katex/lib/index.js index d60ab01..8272e2d 100644 --- a/packages/rehype-katex/lib/index.js +++ b/packages/rehype-katex/lib/index.js @@ -14,7 +14,7 @@ import {fromHtmlIsomorphic} from 'hast-util-from-html-isomorphic' import {toText} from 'hast-util-to-text' import katex from 'katex' -import {visit} from 'unist-util-visit' +import {SKIP, visitParents} from 'unist-util-visit-parents' /** @type {Readonly<Options>} */ const emptyOptions = {} @@ -44,20 +44,46 @@ export default function rehypeKatex(options) { * Nothing. */ return function (tree, file) { - visit(tree, 'element', function (element, _, parent) { + visitParents(tree, 'element', function (element, parents) { const classes = Array.isArray(element.properties.className) ? element.properties.className : emptyClasses - const inline = classes.includes('math-inline') - const displayMode = classes.includes('math-display') + // This class can be generated from markdown with ` ```math `. + const languageMath = classes.includes('language-math') + // This class is used by `remark-math` for flow math (block, `$$\nmath\n$$`). + const mathDisplay = classes.includes('math-display') + // This class is used by `remark-math` for text math (inline, `$math$`). + const mathInline = classes.includes('math-inline') + let displayMode = mathDisplay - if (!inline && !displayMode) { + // Any class is fine. + if (!languageMath && !mathDisplay && !mathInline) { return } - const value = toText(element, {whitespace: 'pre'}) + let parent = parents[parents.length - 1] + let scope = element - /** @type {string} */ + // If this was generated with ` ```math `, replace the `<pre>` and use + // display. + if ( + element.tagName === 'code' && + languageMath && + parent && + parent.type === 'element' && + parent.tagName === 'pre' + ) { + scope = parent + parent = parents[parents.length - 2] + displayMode = true + } + + /* c8 ignore next -- verbose to test. */ + if (!parent) return + + const value = toText(scope, {whitespace: 'pre'}) + + /** @type {Array<ElementContent> | string | undefined} */ let result try { @@ -71,8 +97,7 @@ export default function rehypeKatex(options) { const ruleId = cause.name.toLowerCase() file.message('Could not render math with KaTeX', { - /* c8 ignore next -- verbose to test */ - ancestors: parent ? [parent, element] : [element], + ancestors: [...parents, element], cause, place: element.position, ruleId, @@ -91,7 +116,7 @@ export default function rehypeKatex(options) { // Generate similar markup if this is an other error. // See: <https://github.com/KaTeX/KaTeX/blob/5dc7af0/docs/error.md>. else { - element.children = [ + result = [ { type: 'element', tagName: 'span', @@ -103,14 +128,18 @@ export default function rehypeKatex(options) { children: [{type: 'text', value}] } ] - return } } - const root = fromHtmlIsomorphic(result, {fragment: true}) - // Cast because there will not be `doctypes` in KaTeX result. - const content = /** @type {Array<ElementContent>} */ (root.children) - element.children = content + if (typeof result === 'string') { + const root = fromHtmlIsomorphic(result, {fragment: true}) + // Cast as we don’t expect `doctypes` in KaTeX result. + result = /** @type {Array<ElementContent>} */ (root.children) + } + + const index = parent.children.indexOf(scope) + parent.children.splice(index, 1, ...result) + return SKIP }) } } diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index 8eaec3f..55b6c3e 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -43,7 +43,7 @@ "hast-util-from-html-isomorphic": "^2.0.0", "hast-util-to-text": "^4.0.0", "katex": "^0.16.0", - "unist-util-visit": "^5.0.0", + "unist-util-visit-parents": "^6.0.0", "vfile": "^6.0.0" }, "scripts": { @@ -51,6 +51,9 @@ "test": "npm run build && npm run test-api" }, "xo": { - "prettier": true + "prettier": true, + "rules": { + "unicorn/prefer-at": "off" + } } } diff --git a/packages/rehype-katex/test.js b/packages/rehype-katex/test.js index f7bbb08..77b8406 100644 --- a/packages/rehype-katex/test.js +++ b/packages/rehype-katex/test.js @@ -25,7 +25,7 @@ test('rehype-katex', async function (t) { .use(rehypeStringify) .process( [ - '<p>Inline math <code class="math-inline">\\alpha</code>.</p>', + '<p>Inline math <span class="math-inline">\\alpha</span>.</p>', '<p>Block math:</p>', '<div class="math-display">\\gamma</div>' ].join('\n') @@ -37,19 +37,35 @@ test('rehype-katex', async function (t) { .use(rehypeStringify) .process( [ - '<p>Inline math <code class="math-inline">' + - katex.renderToString('\\alpha') + - '</code>.</p>', + '<p>Inline math ' + katex.renderToString('\\alpha') + '.</p>', '<p>Block math:</p>', - '<div class="math-display">' + - katex.renderToString('\\gamma', {displayMode: true}) + - '</div>' + katex.renderToString('\\gamma', {displayMode: true}) ].join('\n') ) ) ) }) + await t.test('should support markdown fenced code', async function () { + assert.deepEqual( + String( + await unified() + .use(remarkParse) + // @ts-expect-error: to do: remove when `remark-rehype` is released. + .use(remarkRehype) + .use(rehypeKatex) + .use(rehypeStringify) + .process('```math\n\\gamma\n```') + ), + String( + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeStringify) + .process(katex.renderToString('\\gamma\n', {displayMode: true})) + ) + ) + }) + await t.test('should integrate with `remark-math`', async function () { assert.deepEqual( String( @@ -78,13 +94,9 @@ test('rehype-katex', async function (t) { .use(rehypeStringify) .process( [ - '<p>Inline math <code class="language-math math-inline">' + - katex.renderToString('\\alpha') + - '</code>.</p>', + '<p>Inline math ' + katex.renderToString('\\alpha') + '.</p>', '<p>Block math:</p>', - '<pre><code class="language-math math-display">' + - katex.renderToString('\\gamma', {displayMode: true}) + - '</code></pre>' + katex.renderToString('\\gamma', {displayMode: true}) ].join('\n') ) ) @@ -109,9 +121,9 @@ test('rehype-katex', async function (t) { .use(rehypeParse, {fragment: true}) .use(rehypeStringify) .process( - '<p>Double math <code class="math-inline math-display">' + + '<p>Double math ' + katex.renderToString('\\alpha', {displayMode: true}) + - '</code>.</p>' + '.</p>' ) ) ) @@ -127,17 +139,13 @@ test('rehype-katex', async function (t) { .use(rehypeParse, {fragment: true}) .use(rehypeKatex, {macros}) .use(rehypeStringify) - .process('<code class="math-inline">\\RR</code>') + .process('<span class="math-inline">\\RR</span>') ), String( await unified() .use(rehypeParse, {fragment: true}) .use(rehypeStringify) - .process( - '<code class="math-inline">' + - katex.renderToString('\\RR', {macros}) + - '</code>' - ) + .process(katex.renderToString('\\RR', {macros})) ) ) }) @@ -147,7 +155,7 @@ test('rehype-katex', async function (t) { .use(rehypeParse, {fragment: true}) .use(rehypeKatex, {errorColor: 'orange'}) .use(rehypeStringify) - .process('<code class="math-inline">\\alpa</code>') + .process('<span class="math-inline">\\alpa</span>') assert.deepEqual( String(file), @@ -156,12 +164,10 @@ test('rehype-katex', async function (t) { .use(rehypeParse, {fragment: true}) .use(rehypeStringify) .process( - '<code class="math-inline">' + - katex.renderToString('\\alpa', { - errorColor: 'orange', - throwOnError: false - }) + - '</code>' + katex.renderToString('\\alpa', { + errorColor: 'orange', + throwOnError: false + }) ) ) ) @@ -170,11 +176,11 @@ test('rehype-katex', async function (t) { const message = file.messages[0] assert(message) assert(message.cause) - assert(message.ancestors) assert.match( String(message.cause), /KaTeX parse error: Undefined control sequence/ ) + assert(message.ancestors) assert.equal(message.ancestors.length, 2) assert.deepEqual( {...file.messages[0], cause: undefined, ancestors: []}, @@ -204,14 +210,14 @@ test('rehype-katex', async function (t) { .use(rehypeParse, {fragment: true}) .use(rehypeKatex, {errorColor: 'orange', strict: 'ignore'}) .use(rehypeStringify) - .process('<code class="math-inline">ê&</code>') + .process('<span class="math-inline">ê&</span>') ), String( await unified() .use(rehypeParse, {fragment: true}) .use(rehypeStringify) .process( - '<code class="math-inline"><span class="katex-error" title="ParseError: KaTeX parse error: Expected \'EOF\', got \'&\' at position 2: ê&̲" style="color:orange">ê&</span></code>' + '<span class="katex-error" title="ParseError: KaTeX parse error: Expected \'EOF\', got \'&\' at position 2: ê&̲" style="color:orange">ê&</span>' ) ) ) @@ -225,7 +231,7 @@ test('rehype-katex', async function (t) { .use(rehypeKatex, {errorColor: 'orange', strict: 'ignore'}) .use(rehypeStringify) .process( - '<div class="math math-display">\\begin{split}\n f(-2) &= \\sqrt{-2+4} \\\\\n &= x % Test Comment\n\\end{split}</div>' + '<div class="math-display">\\begin{split}\n f(-2) &= \\sqrt{-2+4} \\\\\n &= x % Test Comment\n\\end{split}</div>' ) ), String( @@ -233,12 +239,10 @@ test('rehype-katex', async function (t) { .use(rehypeParse, {fragment: true}) .use(rehypeStringify) .process( - '<div class="math math-display">' + - katex.renderToString( - '\\begin{split}\n f(-2) &= \\sqrt{-2+4} \\\\\n &= x % Test Comment\n\\end{split}', - {displayMode: true} - ) + - '</div>' + katex.renderToString( + '\\begin{split}\n f(-2) &= \\sqrt{-2+4} \\\\\n &= x % Test Comment\n\\end{split}', + {displayMode: true} + ) ) ) ) @@ -252,7 +256,7 @@ test('rehype-katex', async function (t) { .use(rehypeKatex) .use(rehypeStringify) .process( - '<code class="math-display">\\begin{split}\n\\end{{split}}\n</code>' + '<span class="math-display">\\begin{split}\n\\end{{split}}\n</span>' ) ), String( @@ -260,7 +264,7 @@ test('rehype-katex', async function (t) { .use(rehypeParse, {fragment: true}) .use(rehypeStringify) .process( - '<code class="math-display"><span class="katex-error" style="color:#cc0000" title="Error: Expected node of type textord, but got node of type ordgroup">\\begin{split}\n\\end{{split}}\n</span></code>' + '<span class="katex-error" style="color:#cc0000" title="Error: Expected node of type textord, but got node of type ordgroup">\\begin{split}\n\\end{{split}}\n</span>' ) ) ) diff --git a/packages/rehype-mathjax/lib/browser.js b/packages/rehype-mathjax/lib/browser.js index 09b25ae..91cdebd 100644 --- a/packages/rehype-mathjax/lib/browser.js +++ b/packages/rehype-mathjax/lib/browser.js @@ -13,10 +13,9 @@ const rehypeMathJaxBrowser = createPlugin(function (options) { const inline = tex.inlineMath || [['\\(', '\\)']] return { - render(node, options) { + render(value, options) { const delimiters = (options.display ? display : inline)[0] - node.children.unshift({type: 'text', value: delimiters[0]}) - node.children.push({type: 'text', value: delimiters[1]}) + return [{type: 'text', value: delimiters[0] + value + delimiters[1]}] } } }) diff --git a/packages/rehype-mathjax/lib/create-plugin.js b/packages/rehype-mathjax/lib/create-plugin.js index e631849..9f8130e 100644 --- a/packages/rehype-mathjax/lib/create-plugin.js +++ b/packages/rehype-mathjax/lib/create-plugin.js @@ -1,5 +1,6 @@ /** * @typedef {import('hast').Element} Element + * @typedef {import('hast').ElementContent} ElementContent * @typedef {import('hast').Root} Root */ @@ -128,12 +129,12 @@ * * @callback Render * Render a math node. - * @param {Element} element - * Math node. + * @param {string} value + * Math value. * @param {Readonly<RenderOptions>} options * Configuration. - * @returns {undefined} - * Nothing. + * @returns {Array<ElementContent>} + * Content. * * @typedef RenderOptions * Configuration. @@ -153,7 +154,8 @@ * Style sheet. */ -import {SKIP, visit} from 'unist-util-visit' +import {toText} from 'hast-util-to-text' +import {SKIP, visitParents} from 'unist-util-visit-parents' /** @type {Readonly<Options>} */ const emptyOptions = {} @@ -192,23 +194,54 @@ export function createPlugin(createRenderer) { /** @type {Element | Root} */ let context = tree - visit(tree, 'element', function (node) { - const classes = Array.isArray(node.properties.className) - ? node.properties.className + visitParents(tree, 'element', function (element, parents) { + const classes = Array.isArray(element.properties.className) + ? element.properties.className : emptyClasses - const inline = classes.includes('math-inline') - const display = classes.includes('math-display') + // This class can be generated from markdown with ` ```math `. + const languageMath = classes.includes('language-math') + // This class is used by `remark-math` for flow math (block, `$$\nmath\n$$`). + const mathDisplay = classes.includes('math-display') + // This class is used by `remark-math` for text math (inline, `$math$`). + const mathInline = classes.includes('math-inline') + let display = mathDisplay - if (node.tagName === 'head') { - context = node + // Find `<head>`. + if (element.tagName === 'head') { + context = element } - if (!inline && !display) { + // Any class is fine. + if (!languageMath && !mathDisplay && !mathInline) { return } + let parent = parents[parents.length - 1] + let scope = element + + // If this was generated with ` ```math `, replace the `<pre>` and use + // display. + if ( + element.tagName === 'code' && + languageMath && + parent && + parent.type === 'element' && + parent.tagName === 'pre' + ) { + scope = parent + parent = parents[parents.length - 2] + display = true + } + + /* c8 ignore next -- verbose to test. */ + if (!parent) return + found = true - renderer.render(node, {display}) + const text = toText(scope, {whitespace: 'pre'}) + const result = renderer.render(text, {display}) + + const index = parent.children.indexOf(scope) + parent.children.splice(index, 1, ...result) return SKIP }) diff --git a/packages/rehype-mathjax/lib/create-renderer.js b/packages/rehype-mathjax/lib/create-renderer.js index 2f4d486..ff1c493 100644 --- a/packages/rehype-mathjax/lib/create-renderer.js +++ b/packages/rehype-mathjax/lib/create-renderer.js @@ -7,7 +7,6 @@ */ import {fromDom} from 'hast-util-from-dom' -import {toText} from 'hast-util-to-text' import {RegisterHTMLHandler} from 'mathjax-full/js/handlers/html.js' import {TeX} from 'mathjax-full/js/input/tex.js' import {AllPackages} from 'mathjax-full/js/input/tex/AllPackages.js' @@ -45,15 +44,12 @@ export function createRenderer(options, output) { const doc = mathjax.document('', {InputJax: input, OutputJax: output}) return { - render(node, options) { - const mathText = toText(node, {whitespace: 'pre'}) + render(value, options) { // Cast as this practically results in `HTMLElement`. - const domNode = /** @type {HTMLElement} */ ( - doc.convert(mathText, options) - ) + const domNode = /** @type {HTMLElement} */ (doc.convert(value, options)) // Cast as `HTMLElement` results in an `Element`. const hastNode = /** @type {Element} */ (fromDom(domNode)) - node.children = [hastNode] + return [hastNode] }, styleSheet() { const value = adapter.textContent(output.styleSheet(doc)) diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index 9bc9ce2..6cce96c 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -63,7 +63,7 @@ "jsdom": "^22.0.0", "mathjax-full": "^3.0.0", "unified": "^11.0.0", - "unist-util-visit": "^5.0.0" + "unist-util-visit-parents": "^6.0.0" }, "devDependencies": { "@types/jsdom": "^21.0.0" @@ -75,7 +75,8 @@ "xo": { "prettier": true, "rules": { - "n/file-extension-in-import": "off" + "n/file-extension-in-import": "off", + "unicorn/prefer-at": "off" } } } diff --git a/packages/rehype-mathjax/test/fixture/document-svg.html b/packages/rehype-mathjax/test/fixture/document-svg.html index 3d1dbc5..765aa2f 100644 --- a/packages/rehype-mathjax/test/fixture/document-svg.html +++ b/packages/rehype-mathjax/test/fixture/document-svg.html @@ -120,7 +120,7 @@ } </style> <style> .commit-tease, .user-profile-mini-avatar, .avatar, .vcard-details, .signup-prompt-bg { display: none !IMPORTANT; } </style> <script> document.addEventListener('DOMContentLoaded', function() { this.querySelectorAll('a').forEach(anchor => { anchor.addEventListener('click', e => { e.preventDefault(); const redact = new URLSearchParams(window.location.search).get('redact'); const hasExistingParams = anchor.href.includes('?'); window.location.href = anchor.href + (hasExistingParams ? `&redact=${redact}` : `?redact=${redact}`); }); }); }); </script> </head> <body> -<h1>Hello, <span class="math-inline"><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.025ex;" xmlns="http://www.w3.org/2000/svg" width="1.448ex" height="1.025ex" role="img" focusable="false" viewBox="0 -442 640 453" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="MJX-1-TEX-I-1D6FC" d="M34 156Q34 270 120 356T309 442Q379 442 421 402T478 304Q484 275 485 237V208Q534 282 560 374Q564 388 566 390T582 393Q603 393 603 385Q603 376 594 346T558 261T497 161L486 147L487 123Q489 67 495 47T514 26Q528 28 540 37T557 60Q559 67 562 68T577 70Q597 70 597 62Q597 56 591 43Q579 19 556 5T512 -10H505Q438 -10 414 62L411 69L400 61Q390 53 370 41T325 18T267 -2T203 -11Q124 -11 79 39T34 156ZM208 26Q257 26 306 47T379 90L403 112Q401 255 396 290Q382 405 304 405Q235 405 183 332Q156 292 139 224T121 120Q121 71 146 49T208 26Z"></path></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><use data-c="1D6FC" xlink:href="#MJX-1-TEX-I-1D6FC"></use></g></g></g></svg></mjx-container></span>!</h1> +<h1>Hello, <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.025ex;" xmlns="http://www.w3.org/2000/svg" width="1.448ex" height="1.025ex" role="img" focusable="false" viewBox="0 -442 640 453" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="MJX-1-TEX-I-1D6FC" d="M34 156Q34 270 120 356T309 442Q379 442 421 402T478 304Q484 275 485 237V208Q534 282 560 374Q564 388 566 390T582 393Q603 393 603 385Q603 376 594 346T558 261T497 161L486 147L487 123Q489 67 495 47T514 26Q528 28 540 37T557 60Q559 67 562 68T577 70Q597 70 597 62Q597 56 591 43Q579 19 556 5T512 -10H505Q438 -10 414 62L411 69L400 61Q390 53 370 41T325 18T267 -2T203 -11Q124 -11 79 39T34 156ZM208 26Q257 26 306 47T379 90L403 112Q401 255 396 290Q382 405 304 405Q235 405 183 332Q156 292 139 224T121 120Q121 71 146 49T208 26Z"></path></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><use data-c="1D6FC" xlink:href="#MJX-1-TEX-I-1D6FC"></use></g></g></g></svg></mjx-container>!</h1> <script src="example.js"></script> diff --git a/packages/rehype-mathjax/test/fixture/double-svg.html b/packages/rehype-mathjax/test/fixture/double-svg.html index 1497526..50174f5 100644 --- a/packages/rehype-mathjax/test/fixture/double-svg.html +++ b/packages/rehype-mathjax/test/fixture/double-svg.html @@ -1,4 +1,4 @@ -<p>Double math <span class="math-inline math-display"><mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -0.025ex;" xmlns="http://www.w3.org/2000/svg" width="1.448ex" height="1.025ex" role="img" focusable="false" viewBox="0 -442 640 453" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="MJX-1-TEX-I-1D6FC" d="M34 156Q34 270 120 356T309 442Q379 442 421 402T478 304Q484 275 485 237V208Q534 282 560 374Q564 388 566 390T582 393Q603 393 603 385Q603 376 594 346T558 261T497 161L486 147L487 123Q489 67 495 47T514 26Q528 28 540 37T557 60Q559 67 562 68T577 70Q597 70 597 62Q597 56 591 43Q579 19 556 5T512 -10H505Q438 -10 414 62L411 69L400 61Q390 53 370 41T325 18T267 -2T203 -11Q124 -11 79 39T34 156ZM208 26Q257 26 306 47T379 90L403 112Q401 255 396 290Q382 405 304 405Q235 405 183 332Q156 292 139 224T121 120Q121 71 146 49T208 26Z"></path></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><use data-c="1D6FC" xlink:href="#MJX-1-TEX-I-1D6FC"></use></g></g></g></svg></mjx-container></span>.</p> +<p>Double math <mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -0.025ex;" xmlns="http://www.w3.org/2000/svg" width="1.448ex" height="1.025ex" role="img" focusable="false" viewBox="0 -442 640 453" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="MJX-1-TEX-I-1D6FC" d="M34 156Q34 270 120 356T309 442Q379 442 421 402T478 304Q484 275 485 237V208Q534 282 560 374Q564 388 566 390T582 393Q603 393 603 385Q603 376 594 346T558 261T497 161L486 147L487 123Q489 67 495 47T514 26Q528 28 540 37T557 60Q559 67 562 68T577 70Q597 70 597 62Q597 56 591 43Q579 19 556 5T512 -10H505Q438 -10 414 62L411 69L400 61Q390 53 370 41T325 18T267 -2T203 -11Q124 -11 79 39T34 156ZM208 26Q257 26 306 47T379 90L403 112Q401 255 396 290Q382 405 304 405Q235 405 183 332Q156 292 139 224T121 120Q121 71 146 49T208 26Z"></path></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><use data-c="1D6FC" xlink:href="#MJX-1-TEX-I-1D6FC"></use></g></g></g></svg></mjx-container>.</p> <style> mjx-container[jax="SVG"] { direction: ltr; 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 0ff7b92..ea2afca 100644 --- a/packages/rehype-mathjax/test/fixture/equation-numbering-1-chtml.html +++ b/packages/rehype-mathjax/test/fixture/equation-numbering-1-chtml.html @@ -1,6 +1,6 @@ <p>Block math:</p> -<div class="math-display"><mjx-container class="MathJax" jax="CHTML" display="true" width="full" style="min-width: 8.001em;"><mjx-math width="full" display="true" class="MJX-TEX"><mjx-mtable width="full" style="min-width: 8.001em;" side="right"><mjx-table style="width: auto; min-width: 3.845em; margin: 0px 2.078em;"><mjx-itable width="full"><mjx-mlabeledtr><mjx-mtd><mjx-mi class="mjx-i"><mjx-c class="mjx-c1D438 TEX-I"></mjx-c></mjx-mi><mjx-mo class="mjx-n" space="4"><mjx-c class="mjx-c3D"></mjx-c></mjx-mo><mjx-mi class="mjx-i" space="4"><mjx-c class="mjx-c1D45A TEX-I"></mjx-c></mjx-mi><mjx-msup><mjx-mi class="mjx-i"><mjx-c class="mjx-c1D450 TEX-I"></mjx-c></mjx-mi><mjx-script style="vertical-align: 0.413em;"><mjx-mn class="mjx-n" size="s"><mjx-c class="mjx-c32"></mjx-c></mjx-mn></mjx-script></mjx-msup><mjx-tstrut></mjx-tstrut></mjx-mtd></mjx-mlabeledtr></mjx-itable></mjx-table><mjx-labels style="width: 8.001em;"><mjx-itable align="right" style="right: 0px;"><mjx-mtr style="height: 1.134em;"><mjx-mtd id="mjx-eqn:mass-energy_relation"><mjx-mtext class="mjx-n"><mjx-c class="mjx-c28"></mjx-c><mjx-c class="mjx-c31"></mjx-c><mjx-c class="mjx-c29"></mjx-c></mjx-mtext><mjx-tstrut style="height: 1.134em; vertical-align: -0.25em;"></mjx-tstrut></mjx-mtd></mjx-mtr></mjx-itable></mjx-labels></mjx-mtable></mjx-math></mjx-container></div> -<p>See equation <span class="math-inline"><mjx-container class="MathJax" jax="CHTML"><mjx-math class="MJX-TEX"><a href="#mjx-eqn%3Amass-energy_relation"><mjx-mrow class="MathJax_ref"><mjx-mtext class="mjx-n"><mjx-c class="mjx-c28"></mjx-c><mjx-c class="mjx-c31"></mjx-c><mjx-c class="mjx-c29"></mjx-c></mjx-mtext></mjx-mrow></a></mjx-math></mjx-container></span>.</p> +<mjx-container class="MathJax" jax="CHTML" display="true" width="full" style="min-width: 8.001em;"><mjx-math width="full" display="true" class="MJX-TEX"><mjx-mtable width="full" style="min-width: 8.001em;" side="right"><mjx-table style="width: auto; min-width: 3.845em; margin: 0px 2.078em;"><mjx-itable width="full"><mjx-mlabeledtr><mjx-mtd><mjx-mi class="mjx-i"><mjx-c class="mjx-c1D438 TEX-I"></mjx-c></mjx-mi><mjx-mo class="mjx-n" space="4"><mjx-c class="mjx-c3D"></mjx-c></mjx-mo><mjx-mi class="mjx-i" space="4"><mjx-c class="mjx-c1D45A TEX-I"></mjx-c></mjx-mi><mjx-msup><mjx-mi class="mjx-i"><mjx-c class="mjx-c1D450 TEX-I"></mjx-c></mjx-mi><mjx-script style="vertical-align: 0.413em;"><mjx-mn class="mjx-n" size="s"><mjx-c class="mjx-c32"></mjx-c></mjx-mn></mjx-script></mjx-msup><mjx-tstrut></mjx-tstrut></mjx-mtd></mjx-mlabeledtr></mjx-itable></mjx-table><mjx-labels style="width: 8.001em;"><mjx-itable align="right" style="right: 0px;"><mjx-mtr style="height: 1.134em;"><mjx-mtd id="mjx-eqn:mass-energy_relation"><mjx-mtext class="mjx-n"><mjx-c class="mjx-c28"></mjx-c><mjx-c class="mjx-c31"></mjx-c><mjx-c class="mjx-c29"></mjx-c></mjx-mtext><mjx-tstrut style="height: 1.134em; vertical-align: -0.25em;"></mjx-tstrut></mjx-mtd></mjx-mtr></mjx-itable></mjx-labels></mjx-mtable></mjx-math></mjx-container> +<p>See equation <mjx-container class="MathJax" jax="CHTML"><mjx-math class="MJX-TEX"><a href="#mjx-eqn%3Amass-energy_relation"><mjx-mrow class="MathJax_ref"><mjx-mtext class="mjx-n"><mjx-c class="mjx-c28"></mjx-c><mjx-c class="mjx-c31"></mjx-c><mjx-c class="mjx-c29"></mjx-c></mjx-mtext></mjx-mrow></a></mjx-math></mjx-container>.</p> <style> mjx-container[jax="CHTML"] { line-height: 0; @@ -720,4 +720,4 @@ padding: 0.666em 0.5em 0 0; content: "2"; } -</style> \ No newline at end of file +</style> 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 3e753a8..4ff3036 100644 --- a/packages/rehype-mathjax/test/fixture/equation-numbering-1-svg.html +++ b/packages/rehype-mathjax/test/fixture/equation-numbering-1-svg.html @@ -1,6 +1,6 @@ <p>Block math:</p> -<div class="math-display"><mjx-container class="MathJax" jax="SVG" display="true" width="full" style="min-width: 18.102ex;"><svg style="vertical-align: -0.717ex; min-width: 18.102ex;" xmlns="http://www.w3.org/2000/svg" width="100%" height="2.565ex" role="img" focusable="false" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="MJX-1-TEX-I-1D438" d="M492 213Q472 213 472 226Q472 230 477 250T482 285Q482 316 461 323T364 330H312Q311 328 277 192T243 52Q243 48 254 48T334 46Q428 46 458 48T518 61Q567 77 599 117T670 248Q680 270 683 272Q690 274 698 274Q718 274 718 261Q613 7 608 2Q605 0 322 0H133Q31 0 31 11Q31 13 34 25Q38 41 42 43T65 46Q92 46 125 49Q139 52 144 61Q146 66 215 342T285 622Q285 629 281 629Q273 632 228 634H197Q191 640 191 642T193 659Q197 676 203 680H757Q764 676 764 669Q764 664 751 557T737 447Q735 440 717 440H705Q698 445 698 453L701 476Q704 500 704 528Q704 558 697 578T678 609T643 625T596 632T532 634H485Q397 633 392 631Q388 629 386 622Q385 619 355 499T324 377Q347 376 372 376H398Q464 376 489 391T534 472Q538 488 540 490T557 493Q562 493 565 493T570 492T572 491T574 487T577 483L544 351Q511 218 508 216Q505 213 492 213Z"></path><path id="MJX-1-TEX-N-3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path><path id="MJX-1-TEX-I-1D45A" d="M21 287Q22 293 24 303T36 341T56 388T88 425T132 442T175 435T205 417T221 395T229 376L231 369Q231 367 232 367L243 378Q303 442 384 442Q401 442 415 440T441 433T460 423T475 411T485 398T493 385T497 373T500 364T502 357L510 367Q573 442 659 442Q713 442 746 415T780 336Q780 285 742 178T704 50Q705 36 709 31T724 26Q752 26 776 56T815 138Q818 149 821 151T837 153Q857 153 857 145Q857 144 853 130Q845 101 831 73T785 17T716 -10Q669 -10 648 17T627 73Q627 92 663 193T700 345Q700 404 656 404H651Q565 404 506 303L499 291L466 157Q433 26 428 16Q415 -11 385 -11Q372 -11 364 -4T353 8T350 18Q350 29 384 161L420 307Q423 322 423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 181Q151 335 151 342Q154 357 154 369Q154 405 129 405Q107 405 92 377T69 316T57 280Q55 278 41 278H27Q21 284 21 287Z"></path><path id="MJX-1-TEX-I-1D450" d="M34 159Q34 268 120 355T306 442Q362 442 394 418T427 355Q427 326 408 306T360 285Q341 285 330 295T319 325T330 359T352 380T366 386H367Q367 388 361 392T340 400T306 404Q276 404 249 390Q228 381 206 359Q162 315 142 235T121 119Q121 73 147 50Q169 26 205 26H209Q321 26 394 111Q403 121 406 121Q410 121 419 112T429 98T420 83T391 55T346 25T282 0T202 -11Q127 -11 81 37T34 159Z"></path><path id="MJX-1-TEX-N-32" d="M109 429Q82 429 66 447T50 491Q50 562 103 614T235 666Q326 666 387 610T449 465Q449 422 429 383T381 315T301 241Q265 210 201 149L142 93L218 92Q375 92 385 97Q392 99 409 186V189H449V186Q448 183 436 95T421 3V0H50V19V31Q50 38 56 46T86 81Q115 113 136 137Q145 147 170 174T204 211T233 244T261 278T284 308T305 340T320 369T333 401T340 431T343 464Q343 527 309 573T212 619Q179 619 154 602T119 569T109 550Q109 549 114 549Q132 549 151 535T170 489Q170 464 154 447T109 429Z"></path><path id="MJX-1-TEX-N-28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path><path id="MJX-1-TEX-N-31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path><path id="MJX-1-TEX-N-29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(0.0181,-0.0181) translate(0, -817)"><g data-mml-node="math"><g data-mml-node="mtable" transform="translate(2078,0) translate(-2078,0)"><g transform="translate(0 817) matrix(1 0 0 -1 0 0) scale(55.25)"><svg data-table="true" preserveAspectRatio="xMidYMid" viewBox="1922.6 -817 1 1133.9"><g transform="matrix(1 0 0 -1 0 0)"><g data-mml-node="mlabeledtr" transform="translate(0,-67)"><g data-mml-node="mtd"><g data-mml-node="mi"><use data-c="1D438" xlink:href="#MJX-1-TEX-I-1D438"></use></g><g data-mml-node="mo" transform="translate(1041.8,0)"><use data-c="3D" xlink:href="#MJX-1-TEX-N-3D"></use></g><g data-mml-node="mi" transform="translate(2097.6,0)"><use data-c="1D45A" xlink:href="#MJX-1-TEX-I-1D45A"></use></g><g data-mml-node="msup" transform="translate(2975.6,0)"><g data-mml-node="mi"><use data-c="1D450" xlink:href="#MJX-1-TEX-I-1D450"></use></g><g data-mml-node="mn" transform="translate(466,413) scale(0.707)"><use data-c="32" xlink:href="#MJX-1-TEX-N-32"></use></g></g></g></g></g></svg><svg data-labels="true" preserveAspectRatio="xMaxYMid" viewBox="1278 -817 1 1133.9"><g data-labels="true" transform="matrix(1 0 0 -1 0 0)"><g data-mml-node="mtd" id="mjx-eqn:mass-energy_relation" transform="translate(0,683)"><text data-id-align="true"></text><g data-idbox="true" transform="translate(0,-750)"><g data-mml-node="mtext"><use data-c="28" xlink:href="#MJX-1-TEX-N-28"></use><use data-c="31" xlink:href="#MJX-1-TEX-N-31" transform="translate(389,0)"></use><use data-c="29" xlink:href="#MJX-1-TEX-N-29" transform="translate(889,0)"></use></g></g></g></g></svg></g></g></g></g></svg></mjx-container></div> -<p>See equation <span class="math-inline"><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="2.891ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 1278 1000" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="MJX-2-TEX-N-28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path><path id="MJX-2-TEX-N-31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path><path id="MJX-2-TEX-N-29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><a href="#mjx-eqn%3Amass-energy_relation"><g data-mml-node="mrow" class="MathJax_ref"><rect data-hitbox="true" fill="none" stroke="none" pointer-events="all" width="1278" height="1000" y="-250"></rect><g data-mml-node="mtext"><use data-c="28" xlink:href="#MJX-2-TEX-N-28"></use><use data-c="31" xlink:href="#MJX-2-TEX-N-31" transform="translate(389,0)"></use><use data-c="29" xlink:href="#MJX-2-TEX-N-29" transform="translate(889,0)"></use></g></g></a></g></g></svg></mjx-container></span>.</p> +<mjx-container class="MathJax" jax="SVG" display="true" width="full" style="min-width: 18.102ex;"><svg style="vertical-align: -0.717ex; min-width: 18.102ex;" xmlns="http://www.w3.org/2000/svg" width="100%" height="2.565ex" role="img" focusable="false" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="MJX-1-TEX-I-1D438" d="M492 213Q472 213 472 226Q472 230 477 250T482 285Q482 316 461 323T364 330H312Q311 328 277 192T243 52Q243 48 254 48T334 46Q428 46 458 48T518 61Q567 77 599 117T670 248Q680 270 683 272Q690 274 698 274Q718 274 718 261Q613 7 608 2Q605 0 322 0H133Q31 0 31 11Q31 13 34 25Q38 41 42 43T65 46Q92 46 125 49Q139 52 144 61Q146 66 215 342T285 622Q285 629 281 629Q273 632 228 634H197Q191 640 191 642T193 659Q197 676 203 680H757Q764 676 764 669Q764 664 751 557T737 447Q735 440 717 440H705Q698 445 698 453L701 476Q704 500 704 528Q704 558 697 578T678 609T643 625T596 632T532 634H485Q397 633 392 631Q388 629 386 622Q385 619 355 499T324 377Q347 376 372 376H398Q464 376 489 391T534 472Q538 488 540 490T557 493Q562 493 565 493T570 492T572 491T574 487T577 483L544 351Q511 218 508 216Q505 213 492 213Z"></path><path id="MJX-1-TEX-N-3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path><path id="MJX-1-TEX-I-1D45A" d="M21 287Q22 293 24 303T36 341T56 388T88 425T132 442T175 435T205 417T221 395T229 376L231 369Q231 367 232 367L243 378Q303 442 384 442Q401 442 415 440T441 433T460 423T475 411T485 398T493 385T497 373T500 364T502 357L510 367Q573 442 659 442Q713 442 746 415T780 336Q780 285 742 178T704 50Q705 36 709 31T724 26Q752 26 776 56T815 138Q818 149 821 151T837 153Q857 153 857 145Q857 144 853 130Q845 101 831 73T785 17T716 -10Q669 -10 648 17T627 73Q627 92 663 193T700 345Q700 404 656 404H651Q565 404 506 303L499 291L466 157Q433 26 428 16Q415 -11 385 -11Q372 -11 364 -4T353 8T350 18Q350 29 384 161L420 307Q423 322 423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 181Q151 335 151 342Q154 357 154 369Q154 405 129 405Q107 405 92 377T69 316T57 280Q55 278 41 278H27Q21 284 21 287Z"></path><path id="MJX-1-TEX-I-1D450" d="M34 159Q34 268 120 355T306 442Q362 442 394 418T427 355Q427 326 408 306T360 285Q341 285 330 295T319 325T330 359T352 380T366 386H367Q367 388 361 392T340 400T306 404Q276 404 249 390Q228 381 206 359Q162 315 142 235T121 119Q121 73 147 50Q169 26 205 26H209Q321 26 394 111Q403 121 406 121Q410 121 419 112T429 98T420 83T391 55T346 25T282 0T202 -11Q127 -11 81 37T34 159Z"></path><path id="MJX-1-TEX-N-32" d="M109 429Q82 429 66 447T50 491Q50 562 103 614T235 666Q326 666 387 610T449 465Q449 422 429 383T381 315T301 241Q265 210 201 149L142 93L218 92Q375 92 385 97Q392 99 409 186V189H449V186Q448 183 436 95T421 3V0H50V19V31Q50 38 56 46T86 81Q115 113 136 137Q145 147 170 174T204 211T233 244T261 278T284 308T305 340T320 369T333 401T340 431T343 464Q343 527 309 573T212 619Q179 619 154 602T119 569T109 550Q109 549 114 549Q132 549 151 535T170 489Q170 464 154 447T109 429Z"></path><path id="MJX-1-TEX-N-28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path><path id="MJX-1-TEX-N-31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path><path id="MJX-1-TEX-N-29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(0.0181,-0.0181) translate(0, -817)"><g data-mml-node="math"><g data-mml-node="mtable" transform="translate(2078,0) translate(-2078,0)"><g transform="translate(0 817) matrix(1 0 0 -1 0 0) scale(55.25)"><svg data-table="true" preserveAspectRatio="xMidYMid" viewBox="1922.6 -817 1 1133.9"><g transform="matrix(1 0 0 -1 0 0)"><g data-mml-node="mlabeledtr" transform="translate(0,-67)"><g data-mml-node="mtd"><g data-mml-node="mi"><use data-c="1D438" xlink:href="#MJX-1-TEX-I-1D438"></use></g><g data-mml-node="mo" transform="translate(1041.8,0)"><use data-c="3D" xlink:href="#MJX-1-TEX-N-3D"></use></g><g data-mml-node="mi" transform="translate(2097.6,0)"><use data-c="1D45A" xlink:href="#MJX-1-TEX-I-1D45A"></use></g><g data-mml-node="msup" transform="translate(2975.6,0)"><g data-mml-node="mi"><use data-c="1D450" xlink:href="#MJX-1-TEX-I-1D450"></use></g><g data-mml-node="mn" transform="translate(466,413) scale(0.707)"><use data-c="32" xlink:href="#MJX-1-TEX-N-32"></use></g></g></g></g></g></svg><svg data-labels="true" preserveAspectRatio="xMaxYMid" viewBox="1278 -817 1 1133.9"><g data-labels="true" transform="matrix(1 0 0 -1 0 0)"><g data-mml-node="mtd" id="mjx-eqn:mass-energy_relation" transform="translate(0,683)"><text data-id-align="true"></text><g data-idbox="true" transform="translate(0,-750)"><g data-mml-node="mtext"><use data-c="28" xlink:href="#MJX-1-TEX-N-28"></use><use data-c="31" xlink:href="#MJX-1-TEX-N-31" transform="translate(389,0)"></use><use data-c="29" xlink:href="#MJX-1-TEX-N-29" transform="translate(889,0)"></use></g></g></g></g></svg></g></g></g></g></svg></mjx-container> +<p>See equation <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="2.891ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 1278 1000" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="MJX-2-TEX-N-28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path><path id="MJX-2-TEX-N-31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path><path id="MJX-2-TEX-N-29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><a href="#mjx-eqn%3Amass-energy_relation"><g data-mml-node="mrow" class="MathJax_ref"><rect data-hitbox="true" fill="none" stroke="none" pointer-events="all" width="1278" height="1000" y="-250"></rect><g data-mml-node="mtext"><use data-c="28" xlink:href="#MJX-2-TEX-N-28"></use><use data-c="31" xlink:href="#MJX-2-TEX-N-31" transform="translate(389,0)"></use><use data-c="29" xlink:href="#MJX-2-TEX-N-29" transform="translate(889,0)"></use></g></g></a></g></g></svg></mjx-container>.</p> <style> mjx-container[jax="SVG"] { direction: ltr; 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 a7473e3..766e035 100644 --- a/packages/rehype-mathjax/test/fixture/equation-numbering-2-svg.html +++ b/packages/rehype-mathjax/test/fixture/equation-numbering-2-svg.html @@ -1,7 +1,7 @@ <p>Block math:</p> -<div class="math-display"><mjx-container class="MathJax" jax="SVG" display="true" width="full" style="min-width: 21.296ex;"><svg style="vertical-align: -0.717ex; min-width: 21.296ex;" xmlns="http://www.w3.org/2000/svg" width="100%" height="2.565ex" role="img" focusable="false" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="MJX-1-TEX-I-1D44E" d="M33 157Q33 258 109 349T280 441Q331 441 370 392Q386 422 416 422Q429 422 439 414T449 394Q449 381 412 234T374 68Q374 43 381 35T402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487Q506 153 506 144Q506 138 501 117T481 63T449 13Q436 0 417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157ZM351 328Q351 334 346 350T323 385T277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q217 26 254 59T298 110Q300 114 325 217T351 328Z"></path><path id="MJX-1-TEX-N-32" d="M109 429Q82 429 66 447T50 491Q50 562 103 614T235 666Q326 666 387 610T449 465Q449 422 429 383T381 315T301 241Q265 210 201 149L142 93L218 92Q375 92 385 97Q392 99 409 186V189H449V186Q448 183 436 95T421 3V0H50V19V31Q50 38 56 46T86 81Q115 113 136 137Q145 147 170 174T204 211T233 244T261 278T284 308T305 340T320 369T333 401T340 431T343 464Q343 527 309 573T212 619Q179 619 154 602T119 569T109 550Q109 549 114 549Q132 549 151 535T170 489Q170 464 154 447T109 429Z"></path><path id="MJX-1-TEX-N-2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"></path><path id="MJX-1-TEX-I-1D44F" d="M73 647Q73 657 77 670T89 683Q90 683 161 688T234 694Q246 694 246 685T212 542Q204 508 195 472T180 418L176 399Q176 396 182 402Q231 442 283 442Q345 442 383 396T422 280Q422 169 343 79T173 -11Q123 -11 82 27T40 150V159Q40 180 48 217T97 414Q147 611 147 623T109 637Q104 637 101 637H96Q86 637 83 637T76 640T73 647ZM336 325V331Q336 405 275 405Q258 405 240 397T207 376T181 352T163 330L157 322L136 236Q114 150 114 114Q114 66 138 42Q154 26 178 26Q211 26 245 58Q270 81 285 114T318 219Q336 291 336 325Z"></path><path id="MJX-1-TEX-N-3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path><path id="MJX-1-TEX-I-1D450" d="M34 159Q34 268 120 355T306 442Q362 442 394 418T427 355Q427 326 408 306T360 285Q341 285 330 295T319 325T330 359T352 380T366 386H367Q367 388 361 392T340 400T306 404Q276 404 249 390Q228 381 206 359Q162 315 142 235T121 119Q121 73 147 50Q169 26 205 26H209Q321 26 394 111Q403 121 406 121Q410 121 419 112T429 98T420 83T391 55T346 25T282 0T202 -11Q127 -11 81 37T34 159Z"></path><path id="MJX-1-TEX-N-28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path><path id="MJX-1-TEX-N-31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path><path id="MJX-1-TEX-N-29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(0.0181,-0.0181) translate(0, -817)"><g data-mml-node="math"><g data-mml-node="mtable" transform="translate(2078,0) translate(-2078,0)"><g transform="translate(0 817) matrix(1 0 0 -1 0 0) scale(55.25)"><svg data-table="true" preserveAspectRatio="xMidYMid" viewBox="2628.3 -817 1 1133.9"><g transform="matrix(1 0 0 -1 0 0)"><g data-mml-node="mlabeledtr" transform="translate(0,-67)"><g data-mml-node="mtd"><g data-mml-node="msup"><g data-mml-node="mi"><use data-c="1D44E" xlink:href="#MJX-1-TEX-I-1D44E"></use></g><g data-mml-node="mn" transform="translate(562,413) scale(0.707)"><use data-c="32" xlink:href="#MJX-1-TEX-N-32"></use></g></g><g data-mml-node="mo" transform="translate(1187.8,0)"><use data-c="2B" xlink:href="#MJX-1-TEX-N-2B"></use></g><g data-mml-node="msup" transform="translate(2188,0)"><g data-mml-node="mi"><use data-c="1D44F" xlink:href="#MJX-1-TEX-I-1D44F"></use></g><g data-mml-node="mn" transform="translate(462,413) scale(0.707)"><use data-c="32" xlink:href="#MJX-1-TEX-N-32"></use></g></g><g data-mml-node="mo" transform="translate(3331.3,0)"><use data-c="3D" xlink:href="#MJX-1-TEX-N-3D"></use></g><g data-mml-node="msup" transform="translate(4387.1,0)"><g data-mml-node="mi"><use data-c="1D450" xlink:href="#MJX-1-TEX-I-1D450"></use></g><g data-mml-node="mn" transform="translate(466,413) scale(0.707)"><use data-c="32" xlink:href="#MJX-1-TEX-N-32"></use></g></g></g></g></g></svg><svg data-labels="true" preserveAspectRatio="xMaxYMid" viewBox="1278 -817 1 1133.9"><g data-labels="true" transform="matrix(1 0 0 -1 0 0)"><g data-mml-node="mtd" id="mjx-eqn:pythagorean_theorem" transform="translate(0,683)"><text data-id-align="true"></text><g data-idbox="true" transform="translate(0,-750)"><g data-mml-node="mtext"><use data-c="28" xlink:href="#MJX-1-TEX-N-28"></use><use data-c="31" xlink:href="#MJX-1-TEX-N-31" transform="translate(389,0)"></use><use data-c="29" xlink:href="#MJX-1-TEX-N-29" transform="translate(889,0)"></use></g></g></g></g></svg></g></g></g></g></svg></mjx-container></div> -<p>See equation <span class="math-inline"><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="2.891ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 1278 1000" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="MJX-2-TEX-N-28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path><path id="MJX-2-TEX-N-31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path><path id="MJX-2-TEX-N-29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><a href="#mjx-eqn%3Apythagorean_theorem"><g data-mml-node="mrow" class="MathJax_ref"><rect data-hitbox="true" fill="none" stroke="none" pointer-events="all" width="1278" height="1000" y="-250"></rect><g data-mml-node="mtext"><use data-c="28" xlink:href="#MJX-2-TEX-N-28"></use><use data-c="31" xlink:href="#MJX-2-TEX-N-31" transform="translate(389,0)"></use><use data-c="29" xlink:href="#MJX-2-TEX-N-29" transform="translate(889,0)"></use></g></g></a></g></g></svg></mjx-container></span>.</p> -<p>Reference to an undefined equation <span class="math-inline"><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="4.964ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 2194 1000" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="MJX-3-TEX-N-28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path><path id="MJX-3-TEX-N-3F" d="M226 668Q190 668 162 656T124 632L114 621Q116 621 119 620T130 616T145 607T157 591T162 567Q162 544 147 529T109 514T71 528T55 566Q55 625 100 661T199 704Q201 704 210 704T224 705H228Q281 705 320 692T378 656T407 612T416 567Q416 503 361 462Q267 395 247 303Q242 279 242 241V224Q242 205 239 202T222 198T205 201T202 218V249Q204 320 220 371T255 445T292 491T315 537Q317 546 317 574V587Q317 604 315 615T304 640T277 661T226 668ZM162 61Q162 89 180 105T224 121Q247 119 264 104T281 61Q281 31 264 16T222 1Q197 1 180 16T162 61Z"></path><path id="MJX-3-TEX-N-29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><a href="#"><g data-mml-node="mrow" class="MathJax_ref"><rect data-hitbox="true" fill="none" stroke="none" pointer-events="all" width="2194" height="1000" y="-250"></rect><g data-mml-node="mtext"><use data-c="28" xlink:href="#MJX-3-TEX-N-28"></use><use data-c="3F" xlink:href="#MJX-3-TEX-N-3F" transform="translate(389,0)"></use><use data-c="3F" xlink:href="#MJX-3-TEX-N-3F" transform="translate(861,0)"></use><use data-c="3F" xlink:href="#MJX-3-TEX-N-3F" transform="translate(1333,0)"></use><use data-c="29" xlink:href="#MJX-3-TEX-N-29" transform="translate(1805,0)"></use></g></g></a></g></g></svg></mjx-container></span>.</p> +<mjx-container class="MathJax" jax="SVG" display="true" width="full" style="min-width: 21.296ex;"><svg style="vertical-align: -0.717ex; min-width: 21.296ex;" xmlns="http://www.w3.org/2000/svg" width="100%" height="2.565ex" role="img" focusable="false" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="MJX-1-TEX-I-1D44E" d="M33 157Q33 258 109 349T280 441Q331 441 370 392Q386 422 416 422Q429 422 439 414T449 394Q449 381 412 234T374 68Q374 43 381 35T402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487Q506 153 506 144Q506 138 501 117T481 63T449 13Q436 0 417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157ZM351 328Q351 334 346 350T323 385T277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q217 26 254 59T298 110Q300 114 325 217T351 328Z"></path><path id="MJX-1-TEX-N-32" d="M109 429Q82 429 66 447T50 491Q50 562 103 614T235 666Q326 666 387 610T449 465Q449 422 429 383T381 315T301 241Q265 210 201 149L142 93L218 92Q375 92 385 97Q392 99 409 186V189H449V186Q448 183 436 95T421 3V0H50V19V31Q50 38 56 46T86 81Q115 113 136 137Q145 147 170 174T204 211T233 244T261 278T284 308T305 340T320 369T333 401T340 431T343 464Q343 527 309 573T212 619Q179 619 154 602T119 569T109 550Q109 549 114 549Q132 549 151 535T170 489Q170 464 154 447T109 429Z"></path><path id="MJX-1-TEX-N-2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"></path><path id="MJX-1-TEX-I-1D44F" d="M73 647Q73 657 77 670T89 683Q90 683 161 688T234 694Q246 694 246 685T212 542Q204 508 195 472T180 418L176 399Q176 396 182 402Q231 442 283 442Q345 442 383 396T422 280Q422 169 343 79T173 -11Q123 -11 82 27T40 150V159Q40 180 48 217T97 414Q147 611 147 623T109 637Q104 637 101 637H96Q86 637 83 637T76 640T73 647ZM336 325V331Q336 405 275 405Q258 405 240 397T207 376T181 352T163 330L157 322L136 236Q114 150 114 114Q114 66 138 42Q154 26 178 26Q211 26 245 58Q270 81 285 114T318 219Q336 291 336 325Z"></path><path id="MJX-1-TEX-N-3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path><path id="MJX-1-TEX-I-1D450" d="M34 159Q34 268 120 355T306 442Q362 442 394 418T427 355Q427 326 408 306T360 285Q341 285 330 295T319 325T330 359T352 380T366 386H367Q367 388 361 392T340 400T306 404Q276 404 249 390Q228 381 206 359Q162 315 142 235T121 119Q121 73 147 50Q169 26 205 26H209Q321 26 394 111Q403 121 406 121Q410 121 419 112T429 98T420 83T391 55T346 25T282 0T202 -11Q127 -11 81 37T34 159Z"></path><path id="MJX-1-TEX-N-28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path><path id="MJX-1-TEX-N-31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path><path id="MJX-1-TEX-N-29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(0.0181,-0.0181) translate(0, -817)"><g data-mml-node="math"><g data-mml-node="mtable" transform="translate(2078,0) translate(-2078,0)"><g transform="translate(0 817) matrix(1 0 0 -1 0 0) scale(55.25)"><svg data-table="true" preserveAspectRatio="xMidYMid" viewBox="2628.3 -817 1 1133.9"><g transform="matrix(1 0 0 -1 0 0)"><g data-mml-node="mlabeledtr" transform="translate(0,-67)"><g data-mml-node="mtd"><g data-mml-node="msup"><g data-mml-node="mi"><use data-c="1D44E" xlink:href="#MJX-1-TEX-I-1D44E"></use></g><g data-mml-node="mn" transform="translate(562,413) scale(0.707)"><use data-c="32" xlink:href="#MJX-1-TEX-N-32"></use></g></g><g data-mml-node="mo" transform="translate(1187.8,0)"><use data-c="2B" xlink:href="#MJX-1-TEX-N-2B"></use></g><g data-mml-node="msup" transform="translate(2188,0)"><g data-mml-node="mi"><use data-c="1D44F" xlink:href="#MJX-1-TEX-I-1D44F"></use></g><g data-mml-node="mn" transform="translate(462,413) scale(0.707)"><use data-c="32" xlink:href="#MJX-1-TEX-N-32"></use></g></g><g data-mml-node="mo" transform="translate(3331.3,0)"><use data-c="3D" xlink:href="#MJX-1-TEX-N-3D"></use></g><g data-mml-node="msup" transform="translate(4387.1,0)"><g data-mml-node="mi"><use data-c="1D450" xlink:href="#MJX-1-TEX-I-1D450"></use></g><g data-mml-node="mn" transform="translate(466,413) scale(0.707)"><use data-c="32" xlink:href="#MJX-1-TEX-N-32"></use></g></g></g></g></g></svg><svg data-labels="true" preserveAspectRatio="xMaxYMid" viewBox="1278 -817 1 1133.9"><g data-labels="true" transform="matrix(1 0 0 -1 0 0)"><g data-mml-node="mtd" id="mjx-eqn:pythagorean_theorem" transform="translate(0,683)"><text data-id-align="true"></text><g data-idbox="true" transform="translate(0,-750)"><g data-mml-node="mtext"><use data-c="28" xlink:href="#MJX-1-TEX-N-28"></use><use data-c="31" xlink:href="#MJX-1-TEX-N-31" transform="translate(389,0)"></use><use data-c="29" xlink:href="#MJX-1-TEX-N-29" transform="translate(889,0)"></use></g></g></g></g></svg></g></g></g></g></svg></mjx-container> +<p>See equation <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="2.891ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 1278 1000" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="MJX-2-TEX-N-28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path><path id="MJX-2-TEX-N-31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path><path id="MJX-2-TEX-N-29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><a href="#mjx-eqn%3Apythagorean_theorem"><g data-mml-node="mrow" class="MathJax_ref"><rect data-hitbox="true" fill="none" stroke="none" pointer-events="all" width="1278" height="1000" y="-250"></rect><g data-mml-node="mtext"><use data-c="28" xlink:href="#MJX-2-TEX-N-28"></use><use data-c="31" xlink:href="#MJX-2-TEX-N-31" transform="translate(389,0)"></use><use data-c="29" xlink:href="#MJX-2-TEX-N-29" transform="translate(889,0)"></use></g></g></a></g></g></svg></mjx-container>.</p> +<p>Reference to an undefined equation <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="4.964ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 2194 1000" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="MJX-3-TEX-N-28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path><path id="MJX-3-TEX-N-3F" d="M226 668Q190 668 162 656T124 632L114 621Q116 621 119 620T130 616T145 607T157 591T162 567Q162 544 147 529T109 514T71 528T55 566Q55 625 100 661T199 704Q201 704 210 704T224 705H228Q281 705 320 692T378 656T407 612T416 567Q416 503 361 462Q267 395 247 303Q242 279 242 241V224Q242 205 239 202T222 198T205 201T202 218V249Q204 320 220 371T255 445T292 491T315 537Q317 546 317 574V587Q317 604 315 615T304 640T277 661T226 668ZM162 61Q162 89 180 105T224 121Q247 119 264 104T281 61Q281 31 264 16T222 1Q197 1 180 16T162 61Z"></path><path id="MJX-3-TEX-N-29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><a href="#"><g data-mml-node="mrow" class="MathJax_ref"><rect data-hitbox="true" fill="none" stroke="none" pointer-events="all" width="2194" height="1000" y="-250"></rect><g data-mml-node="mtext"><use data-c="28" xlink:href="#MJX-3-TEX-N-28"></use><use data-c="3F" xlink:href="#MJX-3-TEX-N-3F" transform="translate(389,0)"></use><use data-c="3F" xlink:href="#MJX-3-TEX-N-3F" transform="translate(861,0)"></use><use data-c="3F" xlink:href="#MJX-3-TEX-N-3F" transform="translate(1333,0)"></use><use data-c="29" xlink:href="#MJX-3-TEX-N-29" transform="translate(1805,0)"></use></g></g></a></g></g></svg></mjx-container>.</p> <style> mjx-container[jax="SVG"] { direction: ltr; diff --git a/packages/rehype-mathjax/test/fixture/markdown-code-fenced-svg.html b/packages/rehype-mathjax/test/fixture/markdown-code-fenced-svg.html new file mode 100644 index 0000000..84db67a --- /dev/null +++ b/packages/rehype-mathjax/test/fixture/markdown-code-fenced-svg.html @@ -0,0 +1,117 @@ +<mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -0.489ex;" xmlns="http://www.w3.org/2000/svg" width="1.229ex" height="1.486ex" role="img" focusable="false" viewBox="0 -441 543 657" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="MJX-1-TEX-I-1D6FE" d="M31 249Q11 249 11 258Q11 275 26 304T66 365T129 418T206 441Q233 441 239 440Q287 429 318 386T371 255Q385 195 385 170Q385 166 386 166L398 193Q418 244 443 300T486 391T508 430Q510 431 524 431H537Q543 425 543 422Q543 418 522 378T463 251T391 71Q385 55 378 6T357 -100Q341 -165 330 -190T303 -216Q286 -216 286 -188Q286 -138 340 32L346 51L347 69Q348 79 348 100Q348 257 291 317Q251 355 196 355Q148 355 108 329T51 260Q49 251 47 251Q45 249 31 249Z"></path></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><use data-c="1D6FE" xlink:href="#MJX-1-TEX-I-1D6FE"></use></g></g></g></svg></mjx-container><style> +mjx-container[jax="SVG"] { + direction: ltr; +} + +mjx-container[jax="SVG"] > svg { + overflow: visible; + min-height: 1px; + min-width: 1px; +} + +mjx-container[jax="SVG"] > svg a { + fill: blue; + stroke: blue; +} + +mjx-container[jax="SVG"][display="true"] { + display: block; + text-align: center; + margin: 1em 0; +} + +mjx-container[jax="SVG"][display="true"][width="full"] { + display: flex; +} + +mjx-container[jax="SVG"][justify="left"] { + text-align: left; +} + +mjx-container[jax="SVG"][justify="right"] { + text-align: right; +} + +g[data-mml-node="merror"] > g { + fill: red; + stroke: red; +} + +g[data-mml-node="merror"] > rect[data-background] { + fill: yellow; + stroke: none; +} + +g[data-mml-node="mtable"] > line[data-line], svg[data-table] > g > line[data-line] { + stroke-width: 70px; + fill: none; +} + +g[data-mml-node="mtable"] > rect[data-frame], svg[data-table] > g > rect[data-frame] { + stroke-width: 70px; + fill: none; +} + +g[data-mml-node="mtable"] > .mjx-dashed, svg[data-table] > g > .mjx-dashed { + stroke-dasharray: 140; +} + +g[data-mml-node="mtable"] > .mjx-dotted, svg[data-table] > g > .mjx-dotted { + stroke-linecap: round; + stroke-dasharray: 0,140; +} + +g[data-mml-node="mtable"] > g > svg { + overflow: visible; +} + +[jax="SVG"] mjx-tool { + display: inline-block; + position: relative; + width: 0; + height: 0; +} + +[jax="SVG"] mjx-tool > mjx-tip { + position: absolute; + top: 0; + left: 0; +} + +mjx-tool > mjx-tip { + display: inline-block; + padding: .2em; + border: 1px solid #888; + font-size: 70%; + background-color: #F8F8F8; + color: black; + box-shadow: 2px 2px 5px #AAAAAA; +} + +g[data-mml-node="maction"][data-toggle] { + cursor: pointer; +} + +mjx-status { + display: block; + position: fixed; + left: 1em; + bottom: 1em; + min-width: 25%; + padding: .2em .4em; + border: 1px solid #888; + font-size: 90%; + background-color: #F8F8F8; + color: black; +} + +foreignObject[data-mjx-xml] { + font-family: initial; + line-height: normal; + overflow: visible; +} + +mjx-container[jax="SVG"] path[data-c], mjx-container[jax="SVG"] use[data-c] { + stroke-width: 3; +} +</style> diff --git a/packages/rehype-mathjax/test/fixture/markdown-svg.html b/packages/rehype-mathjax/test/fixture/markdown-svg.html index 10a53c3..d45a34b 100644 --- a/packages/rehype-mathjax/test/fixture/markdown-svg.html +++ b/packages/rehype-mathjax/test/fixture/markdown-svg.html @@ -1,6 +1,6 @@ -<p>Inline math <code class="language-math math-inline"><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.025ex;" xmlns="http://www.w3.org/2000/svg" width="1.448ex" height="1.025ex" role="img" focusable="false" viewBox="0 -442 640 453" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="MJX-1-TEX-I-1D6FC" d="M34 156Q34 270 120 356T309 442Q379 442 421 402T478 304Q484 275 485 237V208Q534 282 560 374Q564 388 566 390T582 393Q603 393 603 385Q603 376 594 346T558 261T497 161L486 147L487 123Q489 67 495 47T514 26Q528 28 540 37T557 60Q559 67 562 68T577 70Q597 70 597 62Q597 56 591 43Q579 19 556 5T512 -10H505Q438 -10 414 62L411 69L400 61Q390 53 370 41T325 18T267 -2T203 -11Q124 -11 79 39T34 156ZM208 26Q257 26 306 47T379 90L403 112Q401 255 396 290Q382 405 304 405Q235 405 183 332Q156 292 139 224T121 120Q121 71 146 49T208 26Z"></path></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><use data-c="1D6FC" xlink:href="#MJX-1-TEX-I-1D6FC"></use></g></g></g></svg></mjx-container></code>.</p> +<p>Inline math <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.025ex;" xmlns="http://www.w3.org/2000/svg" width="1.448ex" height="1.025ex" role="img" focusable="false" viewBox="0 -442 640 453" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="MJX-1-TEX-I-1D6FC" d="M34 156Q34 270 120 356T309 442Q379 442 421 402T478 304Q484 275 485 237V208Q534 282 560 374Q564 388 566 390T582 393Q603 393 603 385Q603 376 594 346T558 261T497 161L486 147L487 123Q489 67 495 47T514 26Q528 28 540 37T557 60Q559 67 562 68T577 70Q597 70 597 62Q597 56 591 43Q579 19 556 5T512 -10H505Q438 -10 414 62L411 69L400 61Q390 53 370 41T325 18T267 -2T203 -11Q124 -11 79 39T34 156ZM208 26Q257 26 306 47T379 90L403 112Q401 255 396 290Q382 405 304 405Q235 405 183 332Q156 292 139 224T121 120Q121 71 146 49T208 26Z"></path></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><use data-c="1D6FC" xlink:href="#MJX-1-TEX-I-1D6FC"></use></g></g></g></svg></mjx-container>.</p> <p>Block math:</p> -<pre><code class="language-math math-display"><mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -0.489ex;" xmlns="http://www.w3.org/2000/svg" width="1.229ex" height="1.486ex" role="img" focusable="false" viewBox="0 -441 543 657" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="MJX-2-TEX-I-1D6FE" d="M31 249Q11 249 11 258Q11 275 26 304T66 365T129 418T206 441Q233 441 239 440Q287 429 318 386T371 255Q385 195 385 170Q385 166 386 166L398 193Q418 244 443 300T486 391T508 430Q510 431 524 431H537Q543 425 543 422Q543 418 522 378T463 251T391 71Q385 55 378 6T357 -100Q341 -165 330 -190T303 -216Q286 -216 286 -188Q286 -138 340 32L346 51L347 69Q348 79 348 100Q348 257 291 317Q251 355 196 355Q148 355 108 329T51 260Q49 251 47 251Q45 249 31 249Z"></path></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><use data-c="1D6FE" xlink:href="#MJX-2-TEX-I-1D6FE"></use></g></g></g></svg></mjx-container></code></pre><style> +<mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -0.489ex;" xmlns="http://www.w3.org/2000/svg" width="1.229ex" height="1.486ex" role="img" focusable="false" viewBox="0 -441 543 657" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="MJX-2-TEX-I-1D6FE" d="M31 249Q11 249 11 258Q11 275 26 304T66 365T129 418T206 441Q233 441 239 440Q287 429 318 386T371 255Q385 195 385 170Q385 166 386 166L398 193Q418 244 443 300T486 391T508 430Q510 431 524 431H537Q543 425 543 422Q543 418 522 378T463 251T391 71Q385 55 378 6T357 -100Q341 -165 330 -190T303 -216Q286 -216 286 -188Q286 -138 340 32L346 51L347 69Q348 79 348 100Q348 257 291 317Q251 355 196 355Q148 355 108 329T51 260Q49 251 47 251Q45 249 31 249Z"></path></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><use data-c="1D6FE" xlink:href="#MJX-2-TEX-I-1D6FE"></use></g></g></g></svg></mjx-container><style> mjx-container[jax="SVG"] { direction: ltr; } diff --git a/packages/rehype-mathjax/test/fixture/small-browser-delimiters.html b/packages/rehype-mathjax/test/fixture/small-browser-delimiters.html index 120e96b..d6cc6e9 100644 --- a/packages/rehype-mathjax/test/fixture/small-browser-delimiters.html +++ b/packages/rehype-mathjax/test/fixture/small-browser-delimiters.html @@ -1,3 +1,3 @@ -<p>Inline math <span class="math-inline">$\alpha$</span>.</p> +<p>Inline math $\alpha$.</p> <p>Block math:</p> -<div class="math-display">$$\gamma$$</div> +$$\gamma$$ diff --git a/packages/rehype-mathjax/test/fixture/small-browser.html b/packages/rehype-mathjax/test/fixture/small-browser.html index 7d859b4..917a871 100644 --- a/packages/rehype-mathjax/test/fixture/small-browser.html +++ b/packages/rehype-mathjax/test/fixture/small-browser.html @@ -1,3 +1,3 @@ -<p>Inline math <span class="math-inline">\(\alpha\)</span>.</p> +<p>Inline math \(\alpha\).</p> <p>Block math:</p> -<div class="math-display">\[\gamma\]</div> +\[\gamma\] diff --git a/packages/rehype-mathjax/test/fixture/small-chtml.html b/packages/rehype-mathjax/test/fixture/small-chtml.html index 213b0df..dd2e05c 100644 --- a/packages/rehype-mathjax/test/fixture/small-chtml.html +++ b/packages/rehype-mathjax/test/fixture/small-chtml.html @@ -1,6 +1,6 @@ -<p>Inline math <span class="math-inline"><mjx-container class="MathJax" jax="CHTML"><mjx-math class="MJX-TEX"><mjx-mi class="mjx-i"><mjx-c class="mjx-c1D6FC TEX-I"></mjx-c></mjx-mi></mjx-math></mjx-container></span>.</p> +<p>Inline math <mjx-container class="MathJax" jax="CHTML"><mjx-math class="MJX-TEX"><mjx-mi class="mjx-i"><mjx-c class="mjx-c1D6FC TEX-I"></mjx-c></mjx-mi></mjx-math></mjx-container>.</p> <p>Block math:</p> -<div class="math-display"><mjx-container class="MathJax" jax="CHTML" display="true"><mjx-math display="true" style="margin-left: 0px; margin-right: 0px;" class="MJX-TEX"><mjx-mi class="mjx-i"><mjx-c class="mjx-c1D6FE TEX-I"></mjx-c></mjx-mi></mjx-math></mjx-container></div> +<mjx-container class="MathJax" jax="CHTML" display="true"><mjx-math display="true" style="margin-left: 0px; margin-right: 0px;" class="MJX-TEX"><mjx-mi class="mjx-i"><mjx-c class="mjx-c1D6FE TEX-I"></mjx-c></mjx-mi></mjx-math></mjx-container> <style> mjx-container[jax="CHTML"] { line-height: 0; @@ -394,4 +394,4 @@ padding: 0.441em 0.543em 0.216em 0; content: "\3B3"; } -</style> \ No newline at end of file +</style> diff --git a/packages/rehype-mathjax/test/fixture/small-svg.html b/packages/rehype-mathjax/test/fixture/small-svg.html index b2d7d2b..1c10fdf 100644 --- a/packages/rehype-mathjax/test/fixture/small-svg.html +++ b/packages/rehype-mathjax/test/fixture/small-svg.html @@ -1,6 +1,6 @@ -<p>Inline math <span class="math-inline"><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.025ex;" xmlns="http://www.w3.org/2000/svg" width="1.448ex" height="1.025ex" role="img" focusable="false" viewBox="0 -442 640 453" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="MJX-1-TEX-I-1D6FC" d="M34 156Q34 270 120 356T309 442Q379 442 421 402T478 304Q484 275 485 237V208Q534 282 560 374Q564 388 566 390T582 393Q603 393 603 385Q603 376 594 346T558 261T497 161L486 147L487 123Q489 67 495 47T514 26Q528 28 540 37T557 60Q559 67 562 68T577 70Q597 70 597 62Q597 56 591 43Q579 19 556 5T512 -10H505Q438 -10 414 62L411 69L400 61Q390 53 370 41T325 18T267 -2T203 -11Q124 -11 79 39T34 156ZM208 26Q257 26 306 47T379 90L403 112Q401 255 396 290Q382 405 304 405Q235 405 183 332Q156 292 139 224T121 120Q121 71 146 49T208 26Z"></path></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><use data-c="1D6FC" xlink:href="#MJX-1-TEX-I-1D6FC"></use></g></g></g></svg></mjx-container></span>.</p> +<p>Inline math <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.025ex;" xmlns="http://www.w3.org/2000/svg" width="1.448ex" height="1.025ex" role="img" focusable="false" viewBox="0 -442 640 453" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="MJX-1-TEX-I-1D6FC" d="M34 156Q34 270 120 356T309 442Q379 442 421 402T478 304Q484 275 485 237V208Q534 282 560 374Q564 388 566 390T582 393Q603 393 603 385Q603 376 594 346T558 261T497 161L486 147L487 123Q489 67 495 47T514 26Q528 28 540 37T557 60Q559 67 562 68T577 70Q597 70 597 62Q597 56 591 43Q579 19 556 5T512 -10H505Q438 -10 414 62L411 69L400 61Q390 53 370 41T325 18T267 -2T203 -11Q124 -11 79 39T34 156ZM208 26Q257 26 306 47T379 90L403 112Q401 255 396 290Q382 405 304 405Q235 405 183 332Q156 292 139 224T121 120Q121 71 146 49T208 26Z"></path></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><use data-c="1D6FC" xlink:href="#MJX-1-TEX-I-1D6FC"></use></g></g></g></svg></mjx-container>.</p> <p>Block math:</p> -<div class="math-display"><mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -0.489ex;" xmlns="http://www.w3.org/2000/svg" width="1.229ex" height="1.486ex" role="img" focusable="false" viewBox="0 -441 543 657" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="MJX-2-TEX-I-1D6FE" d="M31 249Q11 249 11 258Q11 275 26 304T66 365T129 418T206 441Q233 441 239 440Q287 429 318 386T371 255Q385 195 385 170Q385 166 386 166L398 193Q418 244 443 300T486 391T508 430Q510 431 524 431H537Q543 425 543 422Q543 418 522 378T463 251T391 71Q385 55 378 6T357 -100Q341 -165 330 -190T303 -216Q286 -216 286 -188Q286 -138 340 32L346 51L347 69Q348 79 348 100Q348 257 291 317Q251 355 196 355Q148 355 108 329T51 260Q49 251 47 251Q45 249 31 249Z"></path></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><use data-c="1D6FE" xlink:href="#MJX-2-TEX-I-1D6FE"></use></g></g></g></svg></mjx-container></div> +<mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -0.489ex;" xmlns="http://www.w3.org/2000/svg" width="1.229ex" height="1.486ex" role="img" focusable="false" viewBox="0 -441 543 657" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="MJX-2-TEX-I-1D6FE" d="M31 249Q11 249 11 258Q11 275 26 304T66 365T129 418T206 441Q233 441 239 440Q287 429 318 386T371 255Q385 195 385 170Q385 166 386 166L398 193Q418 244 443 300T486 391T508 430Q510 431 524 431H537Q543 425 543 422Q543 418 522 378T463 251T391 71Q385 55 378 6T357 -100Q341 -165 330 -190T303 -216Q286 -216 286 -188Q286 -138 340 32L346 51L347 69Q348 79 348 100Q348 257 291 317Q251 355 196 355Q148 355 108 329T51 260Q49 251 47 251Q45 249 31 249Z"></path></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><use data-c="1D6FE" xlink:href="#MJX-2-TEX-I-1D6FE"></use></g></g></g></svg></mjx-container> <style> mjx-container[jax="SVG"] { direction: ltr; diff --git a/packages/rehype-mathjax/test/index.js b/packages/rehype-mathjax/test/index.js index dd5eb19..2905fc3 100644 --- a/packages/rehype-mathjax/test/index.js +++ b/packages/rehype-mathjax/test/index.js @@ -109,6 +109,23 @@ test('rehype-mathjax', async function (t) { ) }) + await t.test('should support markdown fenced code', async function () { + assert.deepEqual( + String( + await unified() + .use(remarkParse) + // @ts-expect-error: to do: remove when `remark-rehype` is released. + .use(remarkRehype) + .use(rehypeMathJaxSvg) + .use(rehypeStringify) + .process('```math\n\\gamma\n```') + ), + String( + await fs.readFile(new URL('markdown-code-fenced-svg.html', base)) + ).trim() + ) + }) + await t.test('should integrate with `remark-math`', async function () { assert.equal( String( diff --git a/packages/remark-math/lib/index.js b/packages/remark-math/lib/index.js index a2cc9a8..48ae82d 100644 --- a/packages/remark-math/lib/index.js +++ b/packages/remark-math/lib/index.js @@ -15,7 +15,7 @@ import {math} from 'micromark-extension-math' const emptyOptions = {} /** - * Plugin to support math. + * Add support for math. * * @param {Readonly<Options> | null | undefined} [options] * Configuration (optional). From dd3075a597d243671bf2bbb46400e3e0f3577078 Mon Sep 17 00:00:00 2001 From: Titus Wormer <tituswormer@gmail.com> Date: Tue, 19 Sep 2023 13:27:52 +0200 Subject: [PATCH 97/98] Refactor docs --- packages/rehype-katex/lib/index.js | 6 +- packages/rehype-katex/readme.md | 234 ++++++++------- packages/rehype-mathjax/lib/browser.js | 9 + packages/rehype-mathjax/lib/chtml.js | 9 + packages/rehype-mathjax/lib/create-plugin.js | 23 ++ packages/rehype-mathjax/lib/svg.js | 9 + packages/rehype-mathjax/readme.md | 281 ++++++++++--------- packages/remark-math/readme.md | 214 ++++++++------ readme.md | 103 ++++--- 9 files changed, 521 insertions(+), 367 deletions(-) diff --git a/packages/rehype-katex/lib/index.js b/packages/rehype-katex/lib/index.js index 8272e2d..1031db0 100644 --- a/packages/rehype-katex/lib/index.js +++ b/packages/rehype-katex/lib/index.js @@ -8,7 +8,7 @@ */ /** - * @typedef {Omit<KatexOptions, 'throwOnError'>} Options + * @typedef {Omit<KatexOptions, 'displayMode' | 'throwOnError'>} Options */ import {fromHtmlIsomorphic} from 'hast-util-from-html-isomorphic' @@ -22,8 +22,8 @@ const emptyOptions = {} const emptyClasses = [] /** - * Plugin to transform `<span class=math-inline>` and `<div class=math-display>` - * with KaTeX. + * Render elements with a `language-math` (or `math-display`, `math-inline`) + * class with KaTeX. * * @param {Readonly<Options> | null | undefined} [options] * Configuration (optional). diff --git a/packages/rehype-katex/readme.md b/packages/rehype-katex/readme.md index bf11238..b045a23 100644 --- a/packages/rehype-katex/readme.md +++ b/packages/rehype-katex/readme.md @@ -8,8 +8,8 @@ [![Backers][backers-badge]][collective] [![Chat][chat-badge]][chat] -**[rehype][]** plugin to render `<span class=math-inline>` and -`<div class=math-display>` with [KaTeX][]. +**[rehype][]** plugin to render elements with a `language-math` class with +[KaTeX][]. ## Contents @@ -19,8 +19,10 @@ * [Use](#use) * [API](#api) * [`unified().use(rehypeKatex[, options])`](#unifieduserehypekatex-options) + * [`Options`](#options) +* [Markdown](#markdown) +* [HTML](#html) * [CSS](#css) -* [Syntax tree](#syntax-tree) * [Types](#types) * [Compatibility](#compatibility) * [Security](#security) @@ -31,27 +33,21 @@ ## What is this? This package is a [unified][] ([rehype][]) plugin to render math. -You can combine it with [`remark-math`][remark-math] for math in markdown or add -`math-inline` and `math-display` classes in HTML. - -**unified** is a project that transforms content with abstract syntax trees -(ASTs). -**rehype** adds support for HTML to unified. -**hast** is the HTML AST that rehype uses. -This is a rehype plugin that transforms hast. +You can add classes to HTML elements, use fenced code in markdown, or combine +with [`remark-math`][remark-math] for a `$C$` syntax extension. ## When should I use this? This project is useful as it renders math with KaTeX at compile time, which means that there is no client side JavaScript needed. -A different plugin, [`rehype-mathjax`][rehype-mathjax], is similar but uses -[MathJax][] instead. +A different plugin, [`rehype-mathjax`][rehype-mathjax], does the same but with +[MathJax][]. ## Install -This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c). -In Node.js (version 12.20+, 14.14+, or 16.0+), install with [npm][]: +This package is [ESM only][esm]. +In Node.js (version 16+), install with [npm][]: ```sh npm install rehype-katex @@ -73,76 +69,117 @@ In browsers with [`esm.sh`][esmsh]: ## Use -Say we have the following file `example.html`: +Say our document `input.html` contains: ```html <p> - Lift(<code class="language-math math-inline">L</span>) can be determined by Lift Coefficient - (<code class="language-math math-inline">C_L</span>) like the following equation. + Lift(<code class="language-math">L</code>) can be determined by Lift Coefficient + (<code class="language-math">C_L</code>) like the following equation. </p> - -<div class="math math-display"> +<pre><code class="language-math"> L = \frac{1}{2} \rho v^2 S C_L -</div> +</code></pre> ``` -And our module `example.js` looks as follows: +…and our module `example.js` contains: ```js -import {read} from 'to-vfile' -import {unified} from 'unified' -import rehypeParse from 'rehype-parse' -import rehypeKatex from 'rehype-katex' import rehypeDocument from 'rehype-document' +import rehypeKatex from 'rehype-katex' +import rehypeParse from 'rehype-parse' import rehypeStringify from 'rehype-stringify' +import {read, write} from 'to-vfile' +import {unified} from 'unified' const file = await unified() .use(rehypeParse, {fragment: true}) - .use(rehypeKatex) .use(rehypeDocument, { - css: 'https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css' + // Get the latest one from: <https://katex.org/docs/browser>. + css: 'https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css' }) + .use(rehypeKatex) .use(rehypeStringify) - .process(await read('example.html')) + .process(await read('input.html')) -console.log(String(file)) +file.basename = 'output.html' +await write(file) ``` -Now running `node example.js` yields: +…then running `node example.js` creates an `output.html` with: ```html <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> -<title>example - - +input + +

- Lift() can be determined by Lift Coefficient - () like the following equation. + Lift() can be determined by Lift Coefficient + () like the following equation.

-
+ ``` +…open `output.html` in a browser to see the rendered math. + ## API This package exports no identifiers. -The default export is `rehypeKatex`. +The default export is [`rehypeKatex`][api-rehype-katex]. ### `unified().use(rehypeKatex[, options])` -Transform `` and `
` with -[KaTeX][]. +Render elements with a `language-math` (or `math-display`, `math-inline`) +class with [KaTeX][]. -##### `options` +###### Parameters + +* `options` ([`Options`][api-options]) + — configuration + +###### Returns + +Transform ([`Transformer`][unified-transformer]). + +### `Options` + +Configuration (TypeScript type). + +###### Type + +```ts +import {KatexOptions} from 'katex' + +type Options = Omit +``` + +See [*Options* on `katex.org`][katex-options] for more info. + +## Markdown + +This plugin supports the syntax extension enabled by +[`remark-math`][remark-math]. +It also supports math generated by using fenced code: + +````markdown +```math +C_L +``` +```` -Configuration (optional). -All options, except for `displayMode`, are passed to [KaTeX][katex-options]. +## HTML + +The content of any element with a `language-math`, `math-inline`, or +`math-display` class is transformed. +The elements are replaced by what KaTeX renders. +Either a `math-display` class or using `
` will
+result in “display” math: math that is a centered block on its own line.
 
 ## CSS
 
@@ -152,66 +189,63 @@ style it properly.
 At the time of writing, the last version is:
 
 ```html
-
+
+
 ```
 
-
-
-## Syntax tree
-
-This plugin transforms elements with a class name of either `math-inline` and/or
-`math-display`.
-
 ## Types
 
 This package is fully typed with [TypeScript][].
-An extra `Options` type is exported, which models the accepted options.
+It exports the additional type [`Options`][api-options].
 
 ## Compatibility
 
-Projects maintained by the unified collective are compatible with all maintained
+Projects maintained by the unified collective are compatible with maintained
 versions of Node.js.
-As of now, that is Node.js 12.20+, 14.14+, and 16.0+.
-Our projects sometimes work with older versions, but this is not guaranteed.
+
+When we cut a new major release, we drop support for unmaintained versions of
+Node.
+This means we try to keep the current release line, `rehype-katex@^7`,
+compatible with Node.js 16.
 
 This plugin works with unified version 6+ and rehype version 4+.
 
 ## Security
 
-Using `rehype-katex` should be safe assuming that you trust KaTeX.
-Any vulnerability in it could open you to a [cross-site scripting (XSS)][xss]
-attack.
-Always be wary of user input and use [`rehype-sanitize`][rehype-sanitize].
+Assuming you trust KaTeX, using `rehype-katex` is safe.
+A vulnerability in it could open you to a
+[cross-site scripting (XSS)][wiki-xss] attack.
+Be wary of user input and use [`rehype-sanitize`][rehype-sanitize].
 
-When you don’t trust user content but do trust KaTeX, you can allow the classes
-added by `remark-math` while disallowing anything else in the `rehype-sanitize`
-schema, and run `rehype-katex` afterwards.
-Like so:
+When you don’t trust user content but do trust KaTeX, run `rehype-katex`
+*after* `rehype-sanitize`:
 
 ```js
+import rehypeKatex from 'rehype-katex'
 import rehypeSanitize, {defaultSchema} from 'rehype-sanitize'
+import rehypeStringify from 'rehype-stringify'
+import remarkMath from 'remark-math'
+import remarkParse from 'remark-parse'
+import remarkRehype from 'remark-rehype'
+import {unified} from 'unified'
 
-const mathSanitizeSchema = {
-  ...defaultSchema,
-  attributes: {
-    ...defaultSchema.attributes,
-    div: [
-      ...defaultSchema.attributes.div,
-      ['className', 'math', 'math-display']
-    ],
-    span: [
-      ['className', 'math', 'math-inline']
-    ]
-  }
-}
-
-// …
-
-unified()
-  // …
-  .use(rehypeSanitize, mathSanitizeSchema)
+const file = await unified()
+  .use(remarkParse)
+  .use(remarkMath)
+  .use(remarkRehype)
+  .use(rehypeSanitize, {
+    ...defaultSchema,
+    attributes: {
+      ...defaultSchema.attributes,
+      // The `language-*` regex is allowed by default.
+      code: [['className', /^language-./, 'math-inline', 'math-display']]
+    }
+  })
   .use(rehypeKatex)
-  // …
+  .use(rehypeStringify)
+  .process('$C$')
+
+console.log(String(file))
 ```
 
 ## Related
@@ -255,9 +289,9 @@ abide by its terms.
 
 [downloads]: https://www.npmjs.com/package/rehype-katex
 
-[size-badge]: https://img.shields.io/bundlephobia/minzip/rehype-katex.svg
+[size-badge]: https://img.shields.io/bundlejs/size/rehype-katex
 
-[size]: https://bundlephobia.com/result?p=rehype-katex
+[size]: https://bundlejs.com/?q=rehype-katex
 
 [sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg
 
@@ -271,36 +305,44 @@ abide by its terms.
 
 [npm]: https://docs.npmjs.com/cli/install
 
+[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
+
 [esmsh]: https://esm.sh
 
 [health]: https://github.com/remarkjs/.github
 
-[contributing]: https://github.com/remarkjs/.github/blob/HEAD/contributing.md
+[contributing]: https://github.com/remarkjs/.github/blob/main/contributing.md
 
-[support]: https://github.com/remarkjs/.github/blob/HEAD/support.md
+[support]: https://github.com/remarkjs/.github/blob/main/support.md
 
-[coc]: https://github.com/remarkjs/.github/blob/HEAD/code-of-conduct.md
+[coc]: https://github.com/remarkjs/.github/blob/main/code-of-conduct.md
 
 [license]: https://github.com/remarkjs/remark-math/blob/main/license
 
 [author]: https://rokt33r.github.io
 
-[unified]: https://github.com/unifiedjs/unified
+[katex]: https://github.com/Khan/KaTeX
+
+[katex-options]: https://katex.org/docs/options.html
 
 [rehype]: https://github.com/rehypejs/rehype
 
-[xss]: https://en.wikipedia.org/wiki/Cross-site_scripting
+[rehype-sanitize]: https://github.com/rehypejs/rehype-sanitize
 
-[typescript]: https://www.typescriptlang.org
+[unified]: https://github.com/unifiedjs/unified
 
-[rehype-sanitize]: https://github.com/rehypejs/rehype-sanitize
+[unified-transformer]: https://github.com/unifiedjs/unified#transformer
 
-[katex]: https://github.com/Khan/KaTeX
+[typescript]: https://www.typescriptlang.org
 
-[katex-options]: https://katex.org/docs/options.html
+[wiki-xss]: https://en.wikipedia.org/wiki/Cross-site_scripting
 
 [mathjax]: https://www.mathjax.org
 
-[remark-math]: ../remark-math
+[remark-math]: ../remark-math/
+
+[rehype-mathjax]: ../rehype-mathjax/
+
+[api-options]: #options
 
-[rehype-mathjax]: ../rehype-mathjax
+[api-rehype-katex]: #unifieduserehypekatex-options
diff --git a/packages/rehype-mathjax/lib/browser.js b/packages/rehype-mathjax/lib/browser.js
index 91cdebd..2d1a52c 100644
--- a/packages/rehype-mathjax/lib/browser.js
+++ b/packages/rehype-mathjax/lib/browser.js
@@ -7,6 +7,15 @@ import {createPlugin} from './create-plugin.js'
 /** @type {Readonly} */
 const emptyTexOptions = {}
 
+/**
+ * Render elements with a `language-math` (or `math-display`, `math-inline`)
+ * class with MathJax with marker for MathJax in the browser.
+ *
+ * @param [options]
+ *   Configuration (optional).
+ * @returns
+ *   Transform.
+ */
 const rehypeMathJaxBrowser = createPlugin(function (options) {
   const tex = options.tex || emptyTexOptions
   const display = tex.displayMath || [['\\[', '\\]']]
diff --git a/packages/rehype-mathjax/lib/chtml.js b/packages/rehype-mathjax/lib/chtml.js
index 75e64a1..0201905 100644
--- a/packages/rehype-mathjax/lib/chtml.js
+++ b/packages/rehype-mathjax/lib/chtml.js
@@ -2,6 +2,15 @@ import {CHTML} from 'mathjax-full/js/output/chtml.js'
 import {createPlugin} from './create-plugin.js'
 import {createRenderer} from './create-renderer.js'
 
+/**
+ * Render elements with a `language-math` (or `math-display`, `math-inline`)
+ * class with MathJax using CHTML.
+ *
+ * @param options
+ *   Configuration (`options.chtml.fontURL` is required).
+ * @returns
+ *   Transform.
+ */
 const rehypeMathJaxCHtml = createPlugin(function (options) {
   if (!options.chtml || !options.chtml.fontURL) {
     throw new Error(
diff --git a/packages/rehype-mathjax/lib/create-plugin.js b/packages/rehype-mathjax/lib/create-plugin.js
index 9f8130e..fb72d27 100644
--- a/packages/rehype-mathjax/lib/create-plugin.js
+++ b/packages/rehype-mathjax/lib/create-plugin.js
@@ -62,6 +62,29 @@
  *
  * @typedef Options
  *   Configuration.
+ *
+ *   ###### Notes
+ *
+ *   When using `rehype-mathjax/browser`, only `options.tex.displayMath` and
+ *   `options.tex.inlineMath` are used.
+ *   That plugin will use the first delimiter pair in those fields to wrap
+ *   math.
+ *   Then you need to load MathJax yourself on the client and start it with the
+ *   same markers.
+ *   You can pass other options on the client.
+ *
+ *   When using `rehype-mathjax/chtml`, `options.chtml.fontURL` is required.
+ *   For example:
+ *
+ *   ```js
+ *     // …
+ *     .use(rehypeMathjaxChtml, {
+ *       chtml: {
+ *         fontURL: 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/output/chtml/fonts/woff-v2'
+ *       }
+ *     })
+ *     // …
+ *   ```
  * @property {Readonly | null | undefined} [chtml]
  *   Configuration for the output, when CHTML (optional).
  * @property {Readonly | null | undefined} [svg]
diff --git a/packages/rehype-mathjax/lib/svg.js b/packages/rehype-mathjax/lib/svg.js
index 9a95d6e..583ba4a 100644
--- a/packages/rehype-mathjax/lib/svg.js
+++ b/packages/rehype-mathjax/lib/svg.js
@@ -2,6 +2,15 @@ import {SVG} from 'mathjax-full/js/output/svg.js'
 import {createPlugin} from './create-plugin.js'
 import {createRenderer} from './create-renderer.js'
 
+/**
+ * Render elements with a `language-math` (or `math-display`, `math-inline`)
+ * class with MathJax using SVG.
+ *
+ * @param [options]
+ *   Configuration (optional).
+ * @returns
+ *   Transform.
+ */
 const rehypeMathJaxSvg = createPlugin(function (options) {
   // MathJax types do not allow `null`.
   return createRenderer(options, new SVG(options.svg || undefined))
diff --git a/packages/rehype-mathjax/readme.md b/packages/rehype-mathjax/readme.md
index 71e89c0..2ae13bc 100644
--- a/packages/rehype-mathjax/readme.md
+++ b/packages/rehype-mathjax/readme.md
@@ -8,8 +8,8 @@
 [![Backers][backers-badge]][collective]
 [![Chat][chat-badge]][chat]
 
-**[rehype][]** plugin to render `` and
-`
` with [MathJax][]. +**[rehype][]** plugin to render elements with a `language-math` class with +[MathJax][]. ## Contents @@ -18,9 +18,11 @@ * [Install](#install) * [Use](#use) * [API](#api) - * [`unified().use(rehypeMathjaxSvg[, options])`](#unifieduserehypemathjaxsvg-options) + * [`unified().use(rehypeMathjax[, options])`](#unifieduserehypemathjax-options) + * [`Options`](#options) +* [Markdown](#markdown) +* [HTML](#html) * [CSS](#css) -* [Syntax tree](#syntax-tree) * [Types](#types) * [Compatibility](#compatibility) * [Security](#security) @@ -31,27 +33,21 @@ ## What is this? This package is a [unified][] ([rehype][]) plugin to render math. -You can combine it with [`remark-math`][remark-math] for math in markdown or add -`math-inline` and `math-display` classes in HTML. - -**unified** is a project that transforms content with abstract syntax trees -(ASTs). -**rehype** adds support for HTML to unified. -**hast** is the HTML AST that rehype uses. -This is a rehype plugin that transforms hast. +You can add classes to HTML elements, use fenced code in markdown, or combine +with [`remark-math`][remark-math] for a `$C$` syntax extension. ## When should I use this? This project is useful as it renders math with MathJax at compile time, which means that there is no client side JavaScript needed. -A different plugin, [`rehype-katex`][rehype-katex], is similar but uses -[KaTeX][] instead. +A different plugin, [`rehype-katex`][rehype-katex], does the same but with +[KaTeX][]. ## Install -This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c). -In Node.js (version 12.20+, 14.14+, or 16.0+), install with [npm][]: +This package is [ESM only][esm]. +In Node.js (version 16+), install with [npm][]: ```sh npm install rehype-mathjax @@ -73,46 +69,45 @@ In browsers with [`esm.sh`][esmsh]: ## Use -Say we have the following file `example.html`: +Say our document `input.html` contains: ```html

- Lift(L) can be determined by Lift Coefficient - (C_L) like the following equation. + Lift(L) can be determined by Lift Coefficient + (C_L) like the following equation.

- -
+

   L = \frac{1}{2} \rho v^2 S C_L
-
+
``` -And our module `example.js` looks as follows: +…and our module `example.js` contains: ```js -import {read} from 'to-vfile' -import {unified} from 'unified' -import rehypeParse from 'rehype-parse' import rehypeMathjax from 'rehype-mathjax' +import rehypeParse from 'rehype-parse' import rehypeStringify from 'rehype-stringify' +import {read, write} from 'to-vfile' +import {unified} from 'unified' const file = await unified() .use(rehypeParse, {fragment: true}) .use(rehypeMathjax) .use(rehypeStringify) - .process(await read('example.html')) + .process(await read('input.html')) -console.log(String(file)) +file.basename = 'output.html' +await write(file) ``` -Now running `node example.js` yields: +…then running `node example.js` creates an `output.html` with: ```html

- Lift() can be determined by Lift Coefficient - () like the following equation. + Lift() can be determined by Lift Coefficient + () like the following equation.

- -
+ ``` -## API +…open `output.html` in a browser to see the rendered math. -This package exports no identifiers. -The default export is `rehypeMathjaxSvg`. +## API -### `unified().use(rehypeMathjaxSvg[, options])` +This package has an export map with several entries for plugins using different +strategies: -Transform `` and `
` with -[MathJax][]. +* `rehype-mathjax/browser` — browser (±1kb) +* `rehype-mathjax/chtml` — [CHTML][mathjax-chtml] (±154kb) +* `rehype-mathjax/svg` — [SVG][mathjax-svg] (±566kb) +* `rehype-mathjax` — same as SVG -This package includes three plugins, split out because MathJax targets have a -big memory and bundle size footprint: SVG, CHTML, and browser: +Each module exports the plugin [`rehypeMathjax`][api-rehype-mathjax] as +the default export. -###### SVG +### `unified().use(rehypeMathjax[, options])` -Render math as [SVG][mathjax-svg] -(`import rehypeMathjaxSvg from 'rehype-mathjax/svg.js'`, default). -About 566kb minzipped. +Render elements with a `language-math` (or `math-display`, `math-inline`) +class with [MathJax][]. -###### CHTML +###### Parameters -Render math as [CHTML][mathjax-chtml] -(`import rehypeMathjaxChtml from 'rehype-mathjax/chtml.js'`). -About 154kb minzipped. -Needs a `fontURL` to be passed. +* `options` ([`Options`][api-options], typically optional) + — configuration -###### Browser +###### Returns -Tiny wrapper that expects MathJax to do work client side -(`import rehypeMathjaxBrowser from 'rehype-mathjax/browser.js'`). -About 1kb minzipped. +Transform ([`Transformer`][unified-transformer]). -Uses `options.displayMath` (default: `['\\[', '\\]']`) for display math and -`options.inlineMath` (default: `['\\(', '\\)']`) for inline math. +### `Options` -You need to load MathJax on the client yourself and start it with corresponding -markers. -Options are not passed to MathJax: do that yourself on the client. +Configuration (TypeScript type). -#### `options` +###### Fields -All options, except when using the browser plugin, are passed to -[MathJax][mathjax-options]. +* `chtml` (`unknown`, optional) + — configuration for the output, when CHTML; + see [*CommonHTML Output Processor Options* on + `mathjax.org`][mathjax-chtml-options] +* `svg` (`unknown`, optional) + — configuration for the output, when SVG; + see [*SVG Output Processor Options* on + `mathjax.org`][mathjax-svg-options] +* `tex` (`unknown`, optional) + — configuration for the input TeX; + see [*TeX Input Processor Options* on + `mathjax.org`][mathjax-tex-options] -#### `options.tex` +###### Notes -These options are passed to the [TeX input processor][mathjax-tex-options]. -The browser plugin uses the first delimiter pair in `tex.displayMath` and -`tex.inlineMath` to instead wrap math. +When using `rehype-mathjax/browser`, only `options.tex.displayMath` and +`options.tex.inlineMath` are used. +That plugin will use the first delimiter pair in those fields to wrap +math. +Then you need to load MathJax yourself on the client and start it with the +same markers. +You can pass other options on the client. -#### `options.chtml` - -These options are passed to the [CommonHTML output -processor][mathjax-chtml-options]. -Passing `fontURL` is required! +When using `rehype-mathjax/chtml`, `options.chtml.fontURL` is required. For example: ```js @@ -188,69 +187,83 @@ For example: // … ``` -#### `options.svg` +## Markdown -These options are passed to the [SVG output processor][mathjax-svg-options]. +This plugin supports the syntax extension enabled by +[`remark-math`][remark-math]. +It also supports math generated by using fenced code: -## CSS +````markdown +```math +C_L +``` +```` -The HTML produced by MathJax does not require any extra CSS to render correctly. +## HTML + +The content of any element with a `language-math`, `math-inline`, or +`math-display` class is transformed. +The elements are replaced by what MathJax renders. +Either a `math-display` class or using `
` will
+result in “display” math: math that is a centered block on its own line.
 
-## Syntax tree
+## CSS
 
-This plugin transforms elements with a class name of either `math-inline` and/or
-`math-display`.
+The HTML produced by MathJax does not require any extra CSS to render correctly.
 
 ## Types
 
 This package is fully typed with [TypeScript][].
-An extra `Options` type is exported, which models the accepted options.
+It exports the additional type [`Options`][api-options].
 
 ## Compatibility
 
-Projects maintained by the unified collective are compatible with all maintained
+Projects maintained by the unified collective are compatible with maintained
 versions of Node.js.
-As of now, that is Node.js 12.20+, 14.14+, and 16.0+.
-Our projects sometimes work with older versions, but this is not guaranteed.
+
+When we cut a new major release, we drop support for unmaintained versions of
+Node.
+This means we try to keep the current release line, `rehype-mathjax@^5`,
+compatible with Node.js 16.
 
 This plugin works with unified version 6+ and rehype version 4+.
 
 ## Security
 
-Using `rehype-mathjax` should be safe assuming that you trust MathJax.
-Any vulnerability in it could open you to a [cross-site scripting (XSS)][xss]
-attack.
-Always be wary of user input and use [`rehype-sanitize`][rehype-sanitize].
+Assuming you trust MathJax, using `rehype-mathjax` is safe.
+A vulnerability in it could open you to a
+[cross-site scripting (XSS)][wiki-xss] attack.
+Be wary of user input and use [`rehype-sanitize`][rehype-sanitize].
 
-When you don’t trust user content but do trust MathKax, you can allow the
-classes added by `remark-math` while disallowing anything else in the
-`rehype-sanitize` schema, and run `rehype-katex` afterwards.
-Like so:
+When you don’t trust user content but do trust MathJax, run `rehype-mathjax`
+*after* `rehype-sanitize`:
 
 ```js
-import rehypeSanitize, {defaultSchema} from 'rehype-stringify'
-
-const mathSanitizeSchema = {
-  ...defaultSchema,
-  attributes: {
-    ...defaultSchema.attributes,
-    div: [
-      ...defaultSchema.attributes.div,
-      ['className', 'math', 'math-display']
-    ],
-    span: [
-      ['className', 'math', 'math-inline']
-    ]
-  }
-}
-
-// …
+import rehypeMathjax from 'rehype-mathjax'
+import rehypeSanitize, {defaultSchema} from 'rehype-sanitize'
+import rehypeStringify from 'rehype-stringify'
+import remarkMath from 'remark-math'
+import remarkParse from 'remark-parse'
+import remarkRehype from 'remark-rehype'
+import {unified} from 'unified'
 
-unified()
-  // …
-  .use(rehypeSanitize, mathSanitizeSchema)
+const file = await unified()
+  .use(remarkParse)
+  .use(remarkMath)
+  .use(remarkRehype)
+  .use(rehypeSanitize, {
+    ...defaultSchema,
+    attributes: {
+      ...defaultSchema.attributes,
+      // The `language-*` regex is allowed by default.
+      code: [['className', /^language-./, 'math-inline', 'math-display']]
+    }
+  })
   .use(rehypeMathjax)
-  // …
+  .use(rehypeStringify)
+  .process('$C$')
+
+console.log(String(file))
 ```
 
 ## Related
@@ -294,9 +307,9 @@ abide by its terms.
 
 [downloads]: https://www.npmjs.com/package/rehype-mathjax
 
-[size-badge]: https://img.shields.io/bundlephobia/minzip/rehype-mathjax.svg
+[size-badge]: https://img.shields.io/bundlejs/size/rehype-mathjax
 
-[size]: https://bundlephobia.com/result?p=rehype-mathjax
+[size]: https://bundlejs.com/?q=rehype-mathjax
 
 [sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg
 
@@ -310,46 +323,52 @@ abide by its terms.
 
 [npm]: https://docs.npmjs.com/cli/install
 
+[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
+
 [esmsh]: https://esm.sh
 
 [health]: https://github.com/remarkjs/.github
 
-[contributing]: https://github.com/remarkjs/.github/blob/HEAD/contributing.md
+[contributing]: https://github.com/remarkjs/.github/blob/main/contributing.md
 
-[support]: https://github.com/remarkjs/.github/blob/HEAD/support.md
+[support]: https://github.com/remarkjs/.github/blob/main/support.md
 
-[coc]: https://github.com/remarkjs/.github/blob/HEAD/code-of-conduct.md
+[coc]: https://github.com/remarkjs/.github/blob/main/code-of-conduct.md
 
 [license]: https://github.com/remarkjs/remark-math/blob/main/license
 
 [author]: https://rokt33r.github.io
 
-[rehype]: https://github.com/rehypejs/rehype
+[katex]: https://github.com/Khan/KaTeX
 
-[unified]: https://github.com/unifiedjs/unified
+[mathjax-svg]: http://docs.mathjax.org/en/latest/output/svg.html
 
-[xss]: https://en.wikipedia.org/wiki/Cross-site_scripting
+[mathjax-chtml]: http://docs.mathjax.org/en/latest/output/html.html
 
-[typescript]: https://www.typescriptlang.org
+[mathjax-tex-options]: http://docs.mathjax.org/en/latest/options/input/tex.html
 
-[rehype-sanitize]: https://github.com/rehypejs/rehype-sanitize
+[mathjax-svg-options]: http://docs.mathjax.org/en/latest/options/output/svg.html
 
-[mathjax]: https://mathjax.org/
+[mathjax-chtml-options]: http://docs.mathjax.org/en/latest/options/output/chtml.html
 
-[mathjax-options]: http://docs.mathjax.org/en/latest/options/
+[rehype]: https://github.com/rehypejs/rehype
 
-[mathjax-svg]: http://docs.mathjax.org/en/latest/output/svg.html
+[rehype-sanitize]: https://github.com/rehypejs/rehype-sanitize
 
-[mathjax-chtml]: http://docs.mathjax.org/en/latest/output/html.html
+[typescript]: https://www.typescriptlang.org
 
-[mathjax-tex-options]: http://docs.mathjax.org/en/latest/options/input/tex.html
+[unified]: https://github.com/unifiedjs/unified
 
-[mathjax-svg-options]: http://docs.mathjax.org/en/latest/options/output/svg.html
+[unified-transformer]: https://github.com/unifiedjs/unified#transformer
 
-[mathjax-chtml-options]: http://docs.mathjax.org/en/latest/options/output/chtml.html
+[wiki-xss]: https://en.wikipedia.org/wiki/Cross-site_scripting
 
-[katex]: https://github.com/Khan/KaTeX
+[mathjax]: https://mathjax.org/
+
+[remark-math]: ../remark-math/
+
+[rehype-katex]: ../rehype-katex/
 
-[remark-math]: ../remark-math
+[api-options]: #options
 
-[rehype-katex]: ../rehype-katex
+[api-rehype-mathjax]: #unifieduserehypemathjax-options
diff --git a/packages/remark-math/readme.md b/packages/remark-math/readme.md
index 35d4359..4081a57 100644
--- a/packages/remark-math/readme.md
+++ b/packages/remark-math/readme.md
@@ -18,8 +18,11 @@
 *   [Use](#use)
 *   [API](#api)
     *   [`unified().use(remarkMath[, options])`](#unifieduseremarkmath-options)
-*   [Syntax](#syntax)
+    *   [`Options`](#options)
+*   [Authoring](#authoring)
 *   [HTML](#html)
+*   [CSS](#css)
+*   [Syntax](#syntax)
 *   [Syntax tree](#syntax-tree)
 *   [Types](#types)
 *   [Compatibility](#compatibility)
@@ -30,16 +33,13 @@
 
 ## What is this?
 
-This package is a [unified][] ([remark][]) plugin to add support for math.
+This package is a [unified][] ([remark][]) plugin to add support for math
+syntax.
 You can use this to add support for parsing and serializing this syntax
 extension.
 
-**unified** is a project that transforms content with abstract syntax trees
-(ASTs).
-**remark** adds support for markdown to unified.
-**mdast** is the markdown AST that remark uses.
-**micromark** is the markdown parser we use.
-This is a remark plugin that adds support for the math syntax and AST to remark.
+As there is no spec for math in markdown, this extension follows how code
+(fenced and text) works in Commonmark, but uses dollars (`$`).
 
 ## When should I use this?
 
@@ -49,10 +49,23 @@ LaTeX equations are also quite hard.
 But this mechanism works well when you want authors, that have some LaTeX
 experience, to be able to embed rich diagrams of math in scientific text.
 
+If you *just* want to turn markdown into HTML (with maybe a few extensions such
+as math), we recommend [`micromark`][micromark] with
+[`micromark-extension-math`][micromark-extension-math] instead.
+If you don’t use plugins and want to access the syntax tree, you can use
+[`mdast-util-from-markdown`][mdast-util-from-markdown] with
+[`mdast-util-math`][mdast-util-math].
+
+This plugins adds [fields on nodes][mdast-util-to-hast-fields] so that the
+plugin responsible for turning markdown (mdast) into HTML (hast),
+[`remark-rehype`][remark-rehype], will turn text math (inline) into
+`` and flow math (block)
+into `
`. + ## Install -This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c). -In Node.js (version 12.20+, 14.14+, or 16.0+), install with [npm][]: +This package is [ESM only][esm]. +In Node.js (version 16+), install with [npm][]: ```sh npm install remark-math @@ -74,10 +87,10 @@ In browsers with [`esm.sh`][esmsh]: ## Use -Say we have the following file `example.md`: +Say our document `example.md` contains: ```markdown -Lift($L$) can be determined by Lift Coefficient ($C_L$) like the following +Lift($$L$$) can be determined by Lift Coefficient ($$C_L$$) like the following equation. $$ @@ -85,16 +98,16 @@ L = \frac{1}{2} \rho v^2 S C_L $$ ``` -And our module `example.js` looks as follows: +…and our module `example.js` contains: ```js -import {read} from 'to-vfile' -import {unified} from 'unified' -import remarkParse from 'remark-parse' -import remarkMath from 'remark-math' -import remarkRehype from 'remark-rehype' import rehypeKatex from 'rehype-katex' import rehypeStringify from 'rehype-stringify' +import remarkMath from 'remark-math' +import remarkParse from 'remark-parse' +import remarkRehype from 'remark-rehype' +import {read} from 'to-vfile' +import {unified} from 'unified' const file = await unified() .use(remarkParse) @@ -107,94 +120,108 @@ const file = await unified() console.log(String(file)) ``` -Now running `node example.js` yields: +…then running `node example.js` yields: ```html -

Lift() can be determined by Lift Coefficient () like the following equation.

-
+

Lift() like the following +equation.

+
``` ## API This package exports no identifiers. -The default export is `remarkMath`. +The default export is [`remarkMath`][api-remark-math]. ### `unified().use(remarkMath[, options])` -Plugin to support math. +Add support for math. -##### `options` +###### Parameters -Configuration (optional). +* `options` ([`Options`][api-options], optional) + — configuration -###### `options.singleDollarTextMath` +###### Returns -Whether to support math (text) with a single dollar (`boolean`, default: -`true`). -Single dollars work in Pandoc and many other places, but often interfere with -“normal” dollars in text. +Nothing (`undefined`). -If you turn this off, you can still use two or more dollars for text math. +### `Options` -## Syntax +Configuration (TypeScript type). -This plugin applies a micromark extensions to parse the syntax. -The syntax basically follows how code works in markdown, except that dollars (`$`) -are used instead of backticks (`` ` ``) and that 2 or more dollars instead of 3 -or more backticks is enough for blocks. +###### Fields -See its readme for parse details: +* `singleDollarTextMath` (`boolean`, default: `true`) + — whether to support text math (inline) with a single dollar. + Single dollars work in Pandoc and many other places, but often interfere + with “normal” dollars in text. + If you turn this off, you can still use two or more dollars for text math. -* [`micromark-extension-math`](https://github.com/micromark/micromark-extension-math#syntax) +## Authoring -> 👉 **Note**: `$math$` works similar to `` `code` ``. -> That means escapes don’t work inside math but you can use more dollars around -> the math instead: `$$\raisebox{0.25em}{$\frac a b$}$$` +When authoring markdown with math, keep in mind that math doesn’t work in most +places. +Notably, GitHub currently has a really weird crappy client-side regex-based +thing. +But on your own (math-heavy?) site it can be great! -> 👉 **Note**: Like code, the difference between “inline” and “block”, -> is in the line endings: -> -> ```markdown -> $$inline$$ -> -> $$ -> block -> $$ -> ``` +Instead of a syntax extension to markdown, you can also use fenced code with an +info string of `math`: + +````markdown +```math +L = \frac{1}{2} \rho v^2 S C_L +``` +```` ## HTML This plugin integrates with [`remark-rehype`][remark-rehype]. -When mdast (markdown AST) is turned into hast (the HTML AST) the math nodes -are turned into `` and `
` -elements. +When markdown (mdast) is turned into HTML (hast) the math nodes are turned +into `` and +`
` elements. -## Syntax tree +## CSS -This plugin applies one mdast utility to build and serialize the AST. -See its readme for the node types supported in the tree: +This package does not relate to CSS. +You can choose to render the math with KaTeX, MathJax, or something else, which +might need CSS. -* [`mdast-util-math`](https://github.com/syntax-tree/mdast-util-math#syntax-tree) +## Syntax + +See [*Syntax* in +`micromark-extension-math`](https://github.com/micromark/micromark-extension-math#syntax). + +## Syntax tree + +See [*Syntax tree* in +`mdast-util-math`](https://github.com/syntax-tree/mdast-util-math#syntax-tree). ## Types This package is fully typed with [TypeScript][]. -It exports an extra `Options` type which models the interface of the accepted -options. +It exports the additional type [`Options`][api-options]. -If you’re working with the syntax tree, make sure to import this plugin -somewhere in your types, as that registers the new node types in the tree. +If you’re working with the syntax tree, you can register the new node types +with `@types/mdast` by adding a reference: ```js -/** @typedef {import('remark-math')} */ +// Register math nodes in mdast: +/// import {visit} from 'unist-util-visit' -/** @type {import('unified').Plugin<[], import('mdast').Root>} */ -export default function myRemarkPlugin() { +function myRemarkPlugin() { + /** + * @param {import('mdast').Root} tree + * Tree. + * @returns {undefined} + * Nothing. + */ return function (tree) { - visit(tree, function(node) { - // `node` can now be one of the nodes for math. + visit(tree, function (node) { + console.log(node) // `node` can now be one of the math nodes. }) } } @@ -202,19 +229,22 @@ export default function myRemarkPlugin() { ## Compatibility -Projects maintained by the unified collective are compatible with all maintained +Projects maintained by the unified collective are compatible with maintained versions of Node.js. -As of now, that is Node.js 12.20+, 14.14+, and 16.0+. -Our projects sometimes work with older versions, but this is not guaranteed. + +When we cut a new major release, we drop support for unmaintained versions of +Node. +This means we try to keep the current release line, `remark-math@^6`, +compatible with Node.js 16. This plugin works with unified version 6+ and remark version 14+. The previous major (version 4) worked with remark 13. ## Security -Use of `remark-math` itself does not open you up to [cross-site scripting -(XSS)][xss] attacks. -Always be wary of user input and use [`rehype-sanitize`][rehype-sanitize]. +Use of `remark-math` does not involve **[rehype][]** ([hast][]) or user +content so there are no openings for [cross-site scripting (XSS)][wiki-xss] +attacks. ## Related @@ -226,7 +256,7 @@ Always be wary of user input and use [`rehype-sanitize`][rehype-sanitize]. * [`remark-directive`](https://github.com/remarkjs/remark-directive) — support directives * [`remark-mdx`](https://github.com/mdx-js/mdx/tree/main/packages/remark-mdx) - — support MDX (JSX, expressions, ESM) + — support MDX (ESM, JSX, expressions) ## Contribute @@ -256,9 +286,9 @@ abide by its terms. [downloads]: https://www.npmjs.com/package/remark-math -[size-badge]: https://img.shields.io/bundlephobia/minzip/remark-math.svg +[size-badge]: https://img.shields.io/bundlejs/size/remark-math -[size]: https://bundlephobia.com/result?p=remark-math +[size]: https://bundlejs.com/?q=remark-math [sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg @@ -272,28 +302,46 @@ abide by its terms. [npm]: https://docs.npmjs.com/cli/install +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [esmsh]: https://esm.sh [health]: https://github.com/remarkjs/.github -[contributing]: https://github.com/remarkjs/.github/blob/HEAD/contributing.md +[contributing]: https://github.com/remarkjs/.github/blob/main/contributing.md -[support]: https://github.com/remarkjs/.github/blob/HEAD/support.md +[support]: https://github.com/remarkjs/.github/blob/main/support.md -[coc]: https://github.com/remarkjs/.github/blob/HEAD/code-of-conduct.md +[coc]: https://github.com/remarkjs/.github/blob/main/code-of-conduct.md [license]: https://github.com/remarkjs/remark-math/blob/main/license [author]: https://rokt33r.github.io -[unified]: https://github.com/unifiedjs/unified +[hast]: https://github.com/syntax-tree/hast + +[mdast-util-from-markdown]: https://github.com/syntax-tree/mdast-util-from-markdown + +[mdast-util-math]: https://github.com/syntax-tree/mdast-util-math + +[mdast-util-to-hast-fields]: https://github.com/syntax-tree/mdast-util-to-hast#fields-on-nodes + +[micromark]: https://github.com/micromark/micromark + +[micromark-extension-math]: https://github.com/micromark/micromark-extension-math + +[rehype]: https://github.com/rehypejs/rehype [remark]: https://github.com/remarkjs/remark -[xss]: https://en.wikipedia.org/wiki/Cross-site_scripting +[remark-rehype]: https://github.com/remarkjs/remark-rehype [typescript]: https://www.typescriptlang.org -[remark-rehype]: https://github.com/remarkjs/remark-rehype +[unified]: https://github.com/unifiedjs/unified + +[wiki-xss]: https://en.wikipedia.org/wiki/Cross-site_scripting + +[api-options]: #options -[rehype-sanitize]: https://github.com/rehypejs/rehype-sanitize +[api-remark-math]: #unifieduseremarkmath-options diff --git a/readme.md b/readme.md index d2ea71f..d188a70 100644 --- a/readme.md +++ b/readme.md @@ -24,10 +24,10 @@ math in markdown and HTML. ## What is this? -This repository contains [unified][] ([remark][] and [rehype][]) plugins to add +This repository contains [unified][] ([rehype][] and [remark][]) plugins to add support for math. -You can use them to add support for parsing and serializing this syntax -extension and to render math with KaTeX or MathJax. +You can use them to add support for parsing and serializing a syntax extension +and to render math with KaTeX or MathJax. * [`remark-math`][remark-math] — remark plugin to support a math syntax in markdown @@ -36,13 +36,9 @@ extension and to render math with KaTeX or MathJax. * [`rehype-mathjax`][rehype-mathjax] — rehype plugin to render math in HTML with [MathJax][] -You typically use `remark-math` combined with either `rehype-katex` or -`rehype-mathjax`. - -**unified** is a project that transforms content with abstract syntax trees -(ASTs). -**remark** adds support for markdown and **rehype** adds support for HTML to -unified. +When dealing with markdown, you optionally use `remark-math`, or alternatively +use fenced code (` ```math `). +Then, you either use `rehype-katex` or `rehype-mathjax` to render math in HTML. ## When should I use this? @@ -50,8 +46,8 @@ This project is useful when you want to support LaTeX math. This mechanism works well when you want authors, that have some LaTeX experience, to be able to embed rich diagrams of math to scientific documentation. -The syntax of math in markdown does not work everywhere so it makes markdown -less portable. +The extra syntax extension supported by `remark-math` for math in markdown does +not work everywhere so it makes markdown less portable. This project is also useful as it renders math with KaTeX or MathJax at compile time, which means that there is no client side JavaScript needed. @@ -59,10 +55,10 @@ time, which means that there is no client side JavaScript needed. ### Example: KaTeX -Say we have the following file `example.md`: +Say our document `example.md` contains: ```markdown -Lift($L$) can be determined by Lift Coefficient ($C_L$) like the following +Lift($$L$$) can be determined by Lift Coefficient ($$C_L$$) like the following equation. $$ @@ -70,16 +66,16 @@ L = \frac{1}{2} \rho v^2 S C_L $$ ``` -And our module `example.js` looks as follows: +…and our module `example.js` contains: ```js -import {read} from 'to-vfile' -import {unified} from 'unified' -import remarkParse from 'remark-parse' -import remarkMath from 'remark-math' -import remarkRehype from 'remark-rehype' import rehypeKatex from 'rehype-katex' import rehypeStringify from 'rehype-stringify' +import remarkMath from 'remark-math' +import remarkParse from 'remark-parse' +import remarkRehype from 'remark-rehype' +import {read} from 'to-vfile' +import {unified} from 'unified' const file = await unified() .use(remarkParse) @@ -92,11 +88,12 @@ const file = await unified() console.log(String(file)) ``` -Now running `node example.js` yields: +…then running `node example.js` yields: ```html -

Lift() can be determined by Lift Coefficient () like the following equation.

-
+

Lift() like the following +equation.

+
``` > 👉 **Note**: KaTeX requires CSS to render correctly. @@ -104,29 +101,23 @@ Now running `node example.js` yields: > properly: > > ```html -> +> +> > ``` - - ### Example: MathJax Supporting either MathJax or KaTeX is very similar. Take the above KaTeX example and change: ```diff -@@ -3,7 +3,7 @@ import {unified} from 'unified' - import remarkParse from 'remark-parse' - import remarkMath from 'remark-math' - import remarkRehype from 'remark-rehype' +@@ -1,4 +1,4 @@ -import rehypeKatex from 'rehype-katex' +import rehypeMathjax from 'rehype-mathjax' import rehypeStringify from 'rehype-stringify' - -@@ -13,7 +13,7 @@ + import remarkMath from 'remark-math' + import remarkParse from 'remark-parse' +@@ -10,7 +10,7 @@ const file = await unified() .use(remarkParse) .use(remarkMath) .use(remarkRehype) @@ -136,21 +127,25 @@ Take the above KaTeX example and change: .process(await read('example.md')) ``` -Now running `node example.js` yields: +…then running `node example.js` yields: ```html -

Lift() can be determined by Lift Coefficient () like the following +

Lift() can be determined by Lift Coefficient () like the following equation.

-
- + ``` ## Security -Using `rehype-katex` or `rehype-mathjax` should be safe assuming that you trust -KaTeX and MathJax. -Any vulnerability in them could open you to a [cross-site scripting (XSS)][xss] -attack. +Assuming you trust KaTeX/MathJax, using `rehype-katex`/`rehype-mathjax` is +safe. +A vulnerability in them could open you to a +[cross-site scripting (XSS)][wiki-xss] attack. See their readmes for more info. ## Contribute @@ -181,9 +176,9 @@ abide by its terms. [downloads]: https://www.npmjs.com/package/remark-math -[size-badge]: https://img.shields.io/bundlephobia/minzip/remark-math.svg +[size-badge]: https://img.shields.io/bundlejs/size/remark-math -[size]: https://bundlephobia.com/result?p=remark-math +[size]: https://bundlejs.com/?q=remark-math [sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg @@ -207,20 +202,20 @@ abide by its terms. [author]: https://rokt33r.github.io -[unified]: https://github.com/unifiedjs/unified +[katex]: https://github.com/Khan/KaTeX -[remark]: https://github.com/remarkjs/remark +[mathjax]: https://mathjax.org/ -[rehype]: https://github.com/rehypejs/rehype +[unified]: https://github.com/unifiedjs/unified -[katex]: https://github.com/Khan/KaTeX +[rehype]: https://github.com/rehypejs/rehype -[mathjax]: https://mathjax.org/ +[remark]: https://github.com/remarkjs/remark -[xss]: https://en.wikipedia.org/wiki/Cross-site_scripting +[wiki-xss]: https://en.wikipedia.org/wiki/Cross-site_scripting -[remark-math]: ./packages/remark-math +[rehype-katex]: ./packages/rehype-katex/ -[rehype-katex]: ./packages/rehype-katex +[rehype-mathjax]: ./packages/rehype-mathjax/ -[rehype-mathjax]: ./packages/rehype-mathjax +[remark-math]: ./packages/remark-math/ From d5d0660b150810a535bbb07eac6cc96a4510aa24 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Tue, 19 Sep 2023 13:38:51 +0200 Subject: [PATCH 98/98] 6.0.0 --- packages/remark-math/package.json | 2 +- packages/remark-math/readme.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/remark-math/package.json b/packages/remark-math/package.json index c1a10c7..3fad47b 100644 --- a/packages/remark-math/package.json +++ b/packages/remark-math/package.json @@ -1,6 +1,6 @@ { "name": "remark-math", - "version": "5.1.1", + "version": "6.0.0", "description": "remark plugin to parse and stringify math", "license": "MIT", "keywords": [ diff --git a/packages/remark-math/readme.md b/packages/remark-math/readme.md index 4081a57..07504de 100644 --- a/packages/remark-math/readme.md +++ b/packages/remark-math/readme.md @@ -74,14 +74,14 @@ npm install remark-math In Deno with [`esm.sh`][esmsh]: ```js -import remarkMath from 'https://esm.sh/remark-math@5' +import remarkMath from 'https://esm.sh/remark-math@6' ``` In browsers with [`esm.sh`][esmsh]: ```html ```