├── .remarkignore ├── .npmrc ├── lerna.json ├── .prettierignore ├── packages ├── rehype-mathjax │ ├── index.js │ ├── test │ │ ├── fixture │ │ │ ├── double.html │ │ │ ├── markdown.md │ │ │ ├── none.html │ │ │ ├── none-svg.html │ │ │ ├── small.html │ │ │ ├── small-browser.html │ │ │ ├── small-browser-delimiters.html │ │ │ ├── document.html │ │ │ ├── double-svg.html │ │ │ ├── document-svg.html │ │ │ ├── small-svg.html │ │ │ ├── markdown-svg.html │ │ │ └── small-chtml.html │ │ └── index.js │ ├── lib │ │ ├── adaptor.browser.js │ │ ├── output-svg.js │ │ ├── adaptor.js │ │ ├── input.js │ │ ├── output-chtml.js │ │ ├── renderer.js │ │ └── core.js │ ├── svg.js │ ├── chtml.js │ ├── browser.js │ ├── package.json │ └── readme.md ├── remark-math │ ├── index.js │ ├── util.js │ ├── package.json │ ├── inline.js │ ├── block.js │ ├── readme.md │ └── test.js ├── remark-html-katex │ ├── package.json │ ├── index.js │ ├── test.js │ └── readme.md └── rehype-katex │ ├── package.json │ ├── index.js │ ├── test.js │ └── readme.md ├── screenshot.png ├── .gitignore ├── .travis.yml ├── .editorconfig ├── license ├── package.json └── readme.md /.remarkignore: -------------------------------------------------------------------------------- 1 | test/ 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "independent" 3 | } 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | *.html 3 | *.json 4 | *.md 5 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./svg') 2 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyme5/remark-math/main/screenshot.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.log 3 | .nyc_output/ 4 | coverage/ 5 | node_modules/ 6 | package-lock.json 7 | yarn.lock 8 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/test/fixture/double.html: -------------------------------------------------------------------------------- 1 |

Double math \alpha.

2 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/test/fixture/markdown.md: -------------------------------------------------------------------------------- 1 | Inline math $\alpha$. 2 | 3 | Block math: 4 | 5 | $$ 6 | \gamma 7 | $$ 8 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/lib/adaptor.browser.js: -------------------------------------------------------------------------------- 1 | module.exports = require('mathjax-full/js/adaptors/browserAdaptor').browserAdaptor 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - lts/dubnium 4 | - node 5 | after_script: bash <(curl -s https://codecov.io/bash) 6 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/test/fixture/none.html: -------------------------------------------------------------------------------- 1 |

No math

2 |
3 | 4 |
5 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/test/fixture/none-svg.html: -------------------------------------------------------------------------------- 1 |

No math

2 |
3 | 4 |
5 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/test/fixture/small.html: -------------------------------------------------------------------------------- 1 |

Inline math \alpha.

2 |

Block math:

3 |
\gamma
4 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/test/fixture/small-browser.html: -------------------------------------------------------------------------------- 1 |

Inline math \(\alpha\).

2 |

Block math:

3 |
\[\gamma\]
4 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/test/fixture/small-browser-delimiters.html: -------------------------------------------------------------------------------- 1 |

Inline math $\alpha$.

2 |

Block math:

3 |
$$\gamma$$
4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/lib/output-svg.js: -------------------------------------------------------------------------------- 1 | const Svg = require('mathjax-full/js/output/svg').SVG 2 | 3 | module.exports = createOutput 4 | 5 | function createOutput(options) { 6 | return new Svg(options) 7 | } 8 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/lib/adaptor.js: -------------------------------------------------------------------------------- 1 | const JsDom = require('jsdom').JSDOM 2 | const adaptor = require('mathjax-full/js/adaptors/jsdomAdaptor').jsdomAdaptor 3 | 4 | module.exports = createAdaptor 5 | 6 | function createAdaptor() { 7 | return adaptor(JsDom) 8 | } 9 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/lib/input.js: -------------------------------------------------------------------------------- 1 | const Tex = require('mathjax-full/js/input/tex').TeX 2 | const packages = require('mathjax-full/js/input/tex/AllPackages').AllPackages 3 | 4 | module.exports = createInput 5 | 6 | function createInput() { 7 | return new Tex({packages: packages}) 8 | } 9 | -------------------------------------------------------------------------------- /packages/remark-math/index.js: -------------------------------------------------------------------------------- 1 | const inlinePlugin = require('./inline') 2 | const blockPlugin = require('./block') 3 | 4 | module.exports = math 5 | 6 | function math(options) { 7 | var settings = options || {} 8 | blockPlugin.call(this, settings) 9 | inlinePlugin.call(this, settings) 10 | } 11 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/test/fixture/document.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Math! 6 | 7 | 8 | 9 |

Hello, \alpha!

10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/remark-math/util.js: -------------------------------------------------------------------------------- 1 | exports.isRemarkParser = isRemarkParser 2 | exports.isRemarkCompiler = isRemarkCompiler 3 | 4 | function isRemarkParser(parser) { 5 | return Boolean(parser && parser.prototype && parser.prototype.blockTokenizers) 6 | } 7 | 8 | function isRemarkCompiler(compiler) { 9 | return Boolean(compiler && compiler.prototype && compiler.prototype.visitors) 10 | } 11 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/svg.js: -------------------------------------------------------------------------------- 1 | const createInput = require('./lib/input') 2 | const createOutput = require('./lib/output-svg') 3 | const createRenderer = require('./lib/renderer') 4 | const createPlugin = require('./lib/core') 5 | 6 | module.exports = createPlugin('rehypeMathJaxSvg', renderSvg) 7 | 8 | function renderSvg(options) { 9 | return createRenderer(createInput(), createOutput(options)) 10 | } 11 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/chtml.js: -------------------------------------------------------------------------------- 1 | const createInput = require('./lib/input') 2 | const createOutput = require('./lib/output-chtml') 3 | const createRenderer = require('./lib/renderer') 4 | const createPlugin = require('./lib/core') 5 | 6 | module.exports = createPlugin('rehypeMathJaxCHtml', renderCHtml) 7 | 8 | function renderCHtml(options) { 9 | return createRenderer(createInput(), createOutput(options)) 10 | } 11 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/lib/output-chtml.js: -------------------------------------------------------------------------------- 1 | const CHtml = require('mathjax-full/js/output/chtml').CHTML 2 | 3 | module.exports = createOutput 4 | 5 | function createOutput(options) { 6 | if (!options || !options.fontURL) { 7 | throw new Error( 8 | 'rehype-mathjax: missing `fontURL` in options, which must be set to a URL to reach MathJaX fonts' 9 | ) 10 | } 11 | 12 | return new CHtml(options) 13 | } 14 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/browser.js: -------------------------------------------------------------------------------- 1 | const createPlugin = require('./lib/core') 2 | 3 | module.exports = createPlugin('rehypeMathJaxBrowser', renderBrowser) 4 | 5 | function renderBrowser(options) { 6 | const settings = options || {} 7 | const display = settings.displayMath || ['\\[', '\\]'] 8 | const inline = settings.inlineMath || ['\\(', '\\)'] 9 | 10 | return {render: render} 11 | 12 | function render(node, renderOptions) { 13 | const delimiters = renderOptions.display ? display : inline 14 | node.children.unshift({type: 'text', value: delimiters[0]}) 15 | node.children.push({type: 'text', value: delimiters[1]}) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/lib/renderer.js: -------------------------------------------------------------------------------- 1 | const mathjax = require('mathjax-full/js/mathjax').mathjax 2 | const register = require('mathjax-full/js/handlers/html').RegisterHTMLHandler 3 | const fromDom = require('hast-util-from-dom') 4 | const toText = require('hast-util-to-text') 5 | const createAdaptor = require('./adaptor') 6 | 7 | module.exports = renderer 8 | 9 | function renderer(input, output) { 10 | const adaptor = createAdaptor() 11 | register(adaptor) 12 | 13 | const doc = mathjax.document('', {InputJax: input, OutputJax: output}) 14 | 15 | return {render: render, styleSheet: styleSheet} 16 | 17 | function render(node, options) { 18 | node.children = [fromDom(doc.convert(toText(node), options))] 19 | } 20 | 21 | function styleSheet() { 22 | const value = adaptor.textContent(output.styleSheet(doc)) 23 | 24 | return { 25 | type: 'element', 26 | tagName: 'style', 27 | properties: {}, 28 | children: [{type: 'text', value: value}] 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/remark-math/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "remark-math", 3 | "version": "2.0.1", 4 | "description": "remark plugin to parse and stringify math", 5 | "license": "MIT", 6 | "keywords": [ 7 | "unified", 8 | "remark", 9 | "remark-plugin", 10 | "plugin", 11 | "mdast", 12 | "markdown", 13 | "math", 14 | "katex", 15 | "latex", 16 | "tex" 17 | ], 18 | "repository": "https://github.com/remarkjs/remark-math/tree/main/packages/remark-math", 19 | "bugs": "https://github.com/remarkjs/remark-math/issues", 20 | "funding": { 21 | "type": "opencollective", 22 | "url": "https://opencollective.com/unified" 23 | }, 24 | "author": "Junyoung Choi (https://rokt33r.github.io)", 25 | "contributors": [ 26 | "Junyoung Choi (https://rokt33r.github.io)", 27 | "Titus Wormer (https://wooorm.com)" 28 | ], 29 | "files": [ 30 | "index.js", 31 | "block.js", 32 | "inline.js", 33 | "util.js" 34 | ], 35 | "main": "index.js", 36 | "dependencies": {}, 37 | "devDependencies": {}, 38 | "xo": false 39 | } 40 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2017 Junyoung Choi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/lib/core.js: -------------------------------------------------------------------------------- 1 | const visit = require('unist-util-visit') 2 | 3 | module.exports = createPlugin 4 | 5 | function createPlugin(displayName, createRenderer) { 6 | attacher.displayName = displayName 7 | 8 | return attacher 9 | 10 | function attacher(options) { 11 | const renderer = createRenderer(options) 12 | 13 | transform.displayName = displayName + 'Transform' 14 | 15 | return transform 16 | 17 | function transform(tree) { 18 | let context = tree 19 | let found = false 20 | 21 | visit(tree, 'element', onelement) 22 | 23 | if (found && renderer.styleSheet) { 24 | context.children.push(renderer.styleSheet()) 25 | } 26 | 27 | function onelement(node) { 28 | const classes = node.properties.className || [] 29 | const inline = classes.includes('math-inline') 30 | const display = classes.includes('math-display') 31 | 32 | if (node.tagName === 'head') { 33 | context = node 34 | } 35 | 36 | if (!inline && !display) { 37 | return 38 | } 39 | 40 | found = true 41 | renderer.render(node, {display: display}) 42 | 43 | return visit.SKIP 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/remark-html-katex/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "remark-html-katex", 3 | "version": "2.1.0", 4 | "description": "remark plugin to transform inline and block math with KaTeX", 5 | "license": "MIT", 6 | "keywords": [ 7 | "unified", 8 | "remark", 9 | "remark-plugin", 10 | "plugin", 11 | "mdast", 12 | "markdown", 13 | "html", 14 | "math", 15 | "katex", 16 | "latex", 17 | "tex" 18 | ], 19 | "repository": "https://github.com/remarkjs/remark-math/tree/main/packages/remark-html-katex", 20 | "bugs": "https://github.com/remarkjs/remark-math/issues", 21 | "funding": { 22 | "type": "opencollective", 23 | "url": "https://opencollective.com/unified" 24 | }, 25 | "author": "Junyoung Choi (https://rokt33r.github.io)", 26 | "contributors": [ 27 | "Junyoung Choi (https://rokt33r.github.io)", 28 | "Titus Wormer (https://wooorm.com)" 29 | ], 30 | "files": [ 31 | "index.js" 32 | ], 33 | "main": "index.js", 34 | "dependencies": { 35 | "katex": "^0.12.0", 36 | "rehype-parse": "^6.0.0", 37 | "unified": "^9.0.0", 38 | "unist-util-visit": "^2.0.0" 39 | }, 40 | "devDependencies": {}, 41 | "xo": false 42 | } 43 | -------------------------------------------------------------------------------- /packages/rehype-katex/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rehype-katex", 3 | "version": "3.1.0", 4 | "description": "rehype plugin to transform inline and block math with KaTeX", 5 | "license": "MIT", 6 | "keywords": [ 7 | "unified", 8 | "remark", 9 | "rehype", 10 | "rehype-plugin", 11 | "plugin", 12 | "mdast", 13 | "markdown", 14 | "hast", 15 | "html", 16 | "math", 17 | "katex", 18 | "latex", 19 | "tex" 20 | ], 21 | "repository": "https://github.com/remarkjs/remark-math/tree/main/packages/rehype-katex", 22 | "bugs": "https://github.com/remarkjs/remark-math/issues", 23 | "funding": { 24 | "type": "opencollective", 25 | "url": "https://opencollective.com/unified" 26 | }, 27 | "author": "Junyoung Choi (https://rokt33r.github.io)", 28 | "contributors": [ 29 | "Junyoung Choi (https://rokt33r.github.io)", 30 | "Titus Wormer (https://wooorm.com)" 31 | ], 32 | "files": [ 33 | "index.js" 34 | ], 35 | "main": "index.js", 36 | "dependencies": { 37 | "hast-util-to-text": "^2.0.0", 38 | "katex": "^0.12.0", 39 | "rehype-parse": "^6.0.0", 40 | "unified": "^9.0.0", 41 | "unist-util-visit": "^2.0.0" 42 | }, 43 | "devDependencies": {}, 44 | "xo": false 45 | } 46 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rehype-mathjax", 3 | "version": "2.0.0", 4 | "description": "rehype plugin to transform inline and block math with MathJax", 5 | "license": "MIT", 6 | "keywords": [ 7 | "unified", 8 | "remark", 9 | "rehype", 10 | "rehype-plugin", 11 | "plugin", 12 | "mdast", 13 | "markdown", 14 | "hast", 15 | "html", 16 | "math", 17 | "mathjax", 18 | "latex", 19 | "tex" 20 | ], 21 | "repository": "https://github.com/remarkjs/remark-math/tree/main/packages/rehype-mathjax", 22 | "bugs": "https://github.com/remarkjs/remark-math/issues", 23 | "funding": { 24 | "type": "opencollective", 25 | "url": "https://opencollective.com/unified" 26 | }, 27 | "browser": { 28 | "./lib/adaptor.js": "./lib/adaptor.browser.js" 29 | }, 30 | "author": "TANIGUCHI Masaya (https://docs.casa)", 31 | "contributors": [ 32 | "TANIGUCHI Masaya (https://docs.casa)", 33 | "Titus Wormer (https://wooorm.com)" 34 | ], 35 | "files": [ 36 | "lib/", 37 | "browser.js", 38 | "chtml.js", 39 | "svg.js", 40 | "index.js" 41 | ], 42 | "main": "index.js", 43 | "dependencies": { 44 | "hast-util-from-dom": "^2.0.0", 45 | "hast-util-to-text": "^2.0.0", 46 | "jsdom": "^16.0.0", 47 | "mathjax-full": "^3.0.0", 48 | "unist-util-visit": "^2.0.0" 49 | }, 50 | "devDependencies": {}, 51 | "xo": false 52 | } 53 | -------------------------------------------------------------------------------- /packages/remark-html-katex/index.js: -------------------------------------------------------------------------------- 1 | const visit = require('unist-util-visit') 2 | const katex = require('katex').renderToString 3 | const unified = require('unified') 4 | const parse = require('rehype-parse') 5 | 6 | module.exports = htmlKatex 7 | 8 | const assign = Object.assign 9 | 10 | const parseHtml = unified().use(parse, {fragment: true, position: false}) 11 | 12 | const source = 'remark-html-katex' 13 | 14 | function htmlKatex(options) { 15 | const settings = options || {} 16 | const throwOnError = settings.throwOnError || false 17 | 18 | return transform 19 | 20 | function transform(tree, file) { 21 | visit(tree, ['inlineMath', 'math'], onmath) 22 | 23 | function onmath(node) { 24 | const displayMode = node.type === 'math' 25 | let result 26 | 27 | try { 28 | result = katex( 29 | node.value, 30 | assign({}, settings, {displayMode: displayMode, throwOnError: true}) 31 | ) 32 | } catch (error) { 33 | const fn = throwOnError ? 'fail' : 'message' 34 | const origin = [source, error.name.toLowerCase()].join(':') 35 | 36 | file[fn](error.message, node.position, origin) 37 | 38 | result = katex( 39 | node.value, 40 | assign({}, settings, { 41 | displayMode: displayMode, 42 | throwOnError: false, 43 | strict: 'ignore' 44 | }) 45 | ) 46 | } 47 | 48 | node.data.hChildren = parseHtml.parse(result).children 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/rehype-katex/index.js: -------------------------------------------------------------------------------- 1 | const visit = require('unist-util-visit') 2 | const katex = require('katex').renderToString 3 | const unified = require('unified') 4 | const parse = require('rehype-parse') 5 | const toText = require('hast-util-to-text') 6 | 7 | module.exports = rehypeKatex 8 | 9 | const assign = Object.assign 10 | 11 | const parseHtml = unified().use(parse, {fragment: true, position: false}) 12 | 13 | const source = 'rehype-katex' 14 | 15 | function rehypeKatex(options) { 16 | const settings = options || {} 17 | const throwOnError = settings.throwOnError || false 18 | 19 | return transformMath 20 | 21 | function transformMath(tree, file) { 22 | visit(tree, 'element', onelement) 23 | 24 | function onelement(element) { 25 | const classes = element.properties.className || [] 26 | const inline = classes.includes('math-inline') 27 | const displayMode = classes.includes('math-display') 28 | 29 | if (!inline && !displayMode) { 30 | return 31 | } 32 | 33 | const value = toText(element) 34 | 35 | let result 36 | 37 | try { 38 | result = katex( 39 | value, 40 | assign({}, settings, {displayMode: displayMode, throwOnError: true}) 41 | ) 42 | } catch (error) { 43 | const fn = throwOnError ? 'fail' : 'message' 44 | const origin = [source, error.name.toLowerCase()].join(':') 45 | 46 | file[fn](error.message, element.position, origin) 47 | 48 | result = katex( 49 | value, 50 | assign({}, settings, { 51 | displayMode: displayMode, 52 | throwOnError: false, 53 | strict: 'ignore' 54 | }) 55 | ) 56 | } 57 | 58 | element.children = parseHtml.parse(result).children 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "remark-math", 3 | "private": true, 4 | "repository": "https://github.com/remarkjs/remark-math", 5 | "bugs": "https://github.com/remarkjs/remark-math/issues", 6 | "author": "Junyoung Choi (https://rokt33r.github.io)", 7 | "contributors": [ 8 | "Junyoung Choi (https://rokt33r.github.io)", 9 | "Titus Wormer (https://wooorm.com)", 10 | "Victor Felder ", 11 | "Xiaoru Li ", 12 | "John Jeng ", 13 | "Daniel Perez Alvarez ", 14 | "TANIGUCHI Masaya " 15 | ], 16 | "dependencies": {}, 17 | "devDependencies": { 18 | "lerna": "^3.0.0", 19 | "nyc": "^15.0.0", 20 | "prettier": "^2.0.0", 21 | "rehype-parse": "^7.0.0", 22 | "rehype-stringify": "^8.0.0", 23 | "remark-cli": "^8.0.0", 24 | "remark-html": "^12.0.0", 25 | "remark-parse": "^8.0.0", 26 | "remark-preset-wooorm": "^7.0.0", 27 | "remark-rehype": "^7.0.0", 28 | "remark-stringify": "^8.0.0", 29 | "tape": "^5.0.0", 30 | "unified": "^9.0.0", 31 | "unist-builder": "^2.0.0", 32 | "xo": "^0.32.0" 33 | }, 34 | "scripts": { 35 | "postinstall": "lerna bootstrap --no-ci", 36 | "format": "remark . -qfo && prettier . --write && xo --fix", 37 | "test-api": "tape \"packages/*/test.js\" \"packages/*/test/**/*.js\"", 38 | "test-coverage": "nyc --reporter lcov tape \"packages/*/test.js\" \"packages/*/test/**/*.js\"", 39 | "test": "npm run format && npm run test-coverage" 40 | }, 41 | "nyc": { 42 | "check-coverage": true, 43 | "lines": 100, 44 | "functions": 100, 45 | "branches": 100 46 | }, 47 | "prettier": { 48 | "tabWidth": 2, 49 | "useTabs": false, 50 | "singleQuote": true, 51 | "bracketSpacing": false, 52 | "semi": false, 53 | "trailingComma": "none" 54 | }, 55 | "xo": { 56 | "prettier": true, 57 | "esnext": false, 58 | "rules": { 59 | "unicorn/no-fn-reference-in-iterator": "off", 60 | "unicorn/string-content": "off", 61 | "new-cap": "off", 62 | "complexity": "off", 63 | "no-eq-null": "off", 64 | "eqeqeq": [ 65 | 2, 66 | "allow-null" 67 | ] 68 | } 69 | }, 70 | "remarkConfig": { 71 | "plugins": [ 72 | "preset-wooorm" 73 | ] 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/test/fixture/double-svg.html: -------------------------------------------------------------------------------- 1 |

Double math .

2 | 117 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/test/fixture/document-svg.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Math! 4 | 5 | 120 | 121 |

Hello, !

122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/test/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const test = require('tape') 3 | const vfile = require('to-vfile') 4 | const unified = require('unified') 5 | const parseMarkdown = require('remark-parse') 6 | const remark2rehype = require('remark-rehype') 7 | const parseHtml = require('rehype-parse') 8 | const stringify = require('rehype-stringify') 9 | const math = require('../../remark-math') 10 | const svg = require('..') 11 | const chtml = require('../chtml') 12 | const browser = require('../browser') 13 | 14 | const fixtures = path.join(__dirname, 'fixture') 15 | 16 | test('rehype-mathjax', function (t) { 17 | t.equal( 18 | unified() 19 | .use(parseHtml, {fragment: true}) 20 | .use(svg) 21 | .use(stringify) 22 | .processSync(vfile.readSync({dirname: fixtures, basename: 'small.html'})) 23 | .toString(), 24 | String( 25 | vfile.readSync({dirname: fixtures, basename: 'small-svg.html'}) 26 | ).trim(), 27 | 'should render SVG' 28 | ) 29 | 30 | t.throws( 31 | function () { 32 | unified().use(chtml).freeze() 33 | }, 34 | /rehype-mathjax: missing `fontURL` in options/, 35 | 'should crash for CHTML w/o `fontURL`' 36 | ) 37 | 38 | t.equal( 39 | unified() 40 | .use(parseHtml, {fragment: true}) 41 | .use(chtml, {fontURL: 'place/to/fonts'}) 42 | .use(stringify) 43 | .processSync(vfile.readSync({dirname: fixtures, basename: 'small.html'})) 44 | .toString(), 45 | String( 46 | vfile.readSync({dirname: fixtures, basename: 'small-chtml.html'}) 47 | ).trim(), 48 | 'should render CHTML' 49 | ) 50 | 51 | t.equal( 52 | unified() 53 | .use(parseHtml, {fragment: true}) 54 | .use(browser) 55 | .use(stringify) 56 | .processSync(vfile.readSync({dirname: fixtures, basename: 'small.html'})) 57 | .toString(), 58 | String(vfile.readSync({dirname: fixtures, basename: 'small-browser.html'})), 59 | 'should render browser' 60 | ) 61 | 62 | t.equal( 63 | unified() 64 | .use(parseMarkdown) 65 | .use(math) 66 | .use(remark2rehype) 67 | .use(svg) 68 | .use(stringify) 69 | .processSync(vfile.readSync({dirname: fixtures, basename: 'markdown.md'})) 70 | .toString(), 71 | String( 72 | vfile.readSync({dirname: fixtures, basename: 'markdown-svg.html'}) 73 | ).trim(), 74 | 'should integrate with `remark-math`' 75 | ) 76 | 77 | t.equal( 78 | unified() 79 | .use(parseHtml, {fragment: true}) 80 | .use(svg) 81 | .use(stringify) 82 | .processSync(vfile.readSync({dirname: fixtures, basename: 'double.html'})) 83 | .toString(), 84 | String( 85 | vfile.readSync({dirname: fixtures, basename: 'double-svg.html'}) 86 | ).trim(), 87 | 'should transform `.math-inline.math-display`' 88 | ) 89 | 90 | t.equal( 91 | unified() 92 | .use(parseHtml, {fragment: true}) 93 | .use(svg) 94 | .use(stringify) 95 | .processSync(vfile.readSync({dirname: fixtures, basename: 'none.html'})) 96 | .toString(), 97 | String(vfile.readSync({dirname: fixtures, basename: 'none-svg.html'})), 98 | 'should transform documents without math' 99 | ) 100 | 101 | t.equal( 102 | unified() 103 | .use(parseHtml) 104 | .use(svg) 105 | .use(stringify) 106 | .processSync( 107 | vfile.readSync({dirname: fixtures, basename: 'document.html'}) 108 | ) 109 | .toString(), 110 | String( 111 | vfile.readSync({dirname: fixtures, basename: 'document-svg.html'}) 112 | ).trim(), 113 | 'should transform complete documents' 114 | ) 115 | 116 | t.equal( 117 | unified() 118 | .use(parseHtml, {fragment: true}) 119 | .use(browser, {inlineMath: ['$', '$'], displayMath: ['$$', '$$']}) 120 | .use(stringify) 121 | .processSync(vfile.readSync({dirname: fixtures, basename: 'small.html'})) 122 | .toString(), 123 | String( 124 | vfile.readSync({ 125 | dirname: fixtures, 126 | basename: 'small-browser-delimiters.html' 127 | }) 128 | ), 129 | 'should support custom `inlineMath` and `displayMath` delimiters for browser' 130 | ) 131 | 132 | t.end() 133 | }) 134 | -------------------------------------------------------------------------------- /packages/remark-html-katex/test.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const katex = require('katex') 3 | const unified = require('unified') 4 | const parseMarkdown = require('remark-parse') 5 | const parseHtml = require('rehype-parse') 6 | const stringify = require('rehype-stringify') 7 | const html = require('remark-html') 8 | const math = require('../remark-math') 9 | const htmlKatex = require('.') 10 | 11 | test('remark-html-katex', function (t) { 12 | t.deepEqual( 13 | unified() 14 | .use(parseMarkdown, {position: false}) 15 | .use(math) 16 | .use(htmlKatex) 17 | .use(html) 18 | .processSync( 19 | [ 20 | 'Inline math $\\alpha$.', 21 | '', 22 | 'Block math:', 23 | '', 24 | '$$', 25 | '\\gamma', 26 | '$$' 27 | ].join('\n') 28 | ) 29 | .toString(), 30 | unified() 31 | .use(parseHtml, {fragment: true, position: false}) 32 | .use(stringify) 33 | .processSync( 34 | [ 35 | '

Inline math ' + 36 | katex.renderToString('\\alpha') + 37 | '.

', 38 | '

Block math:

', 39 | '
' + 40 | katex.renderToString('\\gamma', {displayMode: true}) + 41 | '
', 42 | '' 43 | ].join('\n') 44 | ) 45 | .toString(), 46 | 'should transform math with katex' 47 | ) 48 | 49 | const macros = {'\\RR': '\\mathbb{R}'} 50 | 51 | t.deepEqual( 52 | unified() 53 | .use(parseMarkdown, {position: false}) 54 | .use(math) 55 | .use(htmlKatex, {macros: macros}) 56 | .use(html) 57 | .processSync('$\\RR$') 58 | .toString(), 59 | unified() 60 | .use(parseHtml, {fragment: true, position: false}) 61 | .use(stringify) 62 | .processSync( 63 | '

' + 64 | katex.renderToString('\\RR', {macros: macros}) + 65 | '

\n' 66 | ) 67 | .toString(), 68 | 'should support `macros`' 69 | ) 70 | 71 | t.deepEqual( 72 | unified() 73 | .use(parseMarkdown, {position: false}) 74 | .use(math) 75 | .use(htmlKatex, {errorColor: 'orange'}) 76 | .use(html) 77 | .processSync('$\\alpa$') 78 | .toString(), 79 | unified() 80 | .use(parseHtml, {fragment: true, position: false}) 81 | .use(stringify) 82 | .processSync( 83 | '

' + 84 | katex.renderToString('\\alpa', { 85 | throwOnError: false, 86 | errorColor: 'orange' 87 | }) + 88 | '

\n' 89 | ) 90 | .toString(), 91 | 'should support `errorColor`' 92 | ) 93 | 94 | t.deepLooseEqual( 95 | unified() 96 | .use(parseMarkdown) 97 | .use(math) 98 | .use(htmlKatex) 99 | .use(html) 100 | .processSync('Lorem\n$\\alpa$') 101 | .messages.map(String), 102 | [ 103 | '2:1-2:8: KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲' 104 | ], 105 | 'should create a message for errors' 106 | ) 107 | 108 | try { 109 | unified() 110 | .use(parseMarkdown) 111 | .use(math) 112 | .use(htmlKatex, {throwOnError: true}) 113 | .use(html) 114 | .processSync('Lorem\n$\\alpa$') 115 | } catch (error) { 116 | t.equal( 117 | error.message, 118 | 'KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲', 119 | 'should throw an error if `throwOnError: true`' 120 | ) 121 | } 122 | 123 | t.deepEqual( 124 | unified() 125 | .use(parseMarkdown, {position: false}) 126 | .use(math) 127 | .use(htmlKatex, {errorColor: 'orange', strict: 'ignore'}) 128 | .use(html) 129 | .processSync('$ê&$') 130 | .toString(), 131 | unified() 132 | .use(parseHtml, {fragment: true, position: false}) 133 | .use(stringify) 134 | .processSync( 135 | '

ê&

\n' 136 | ) 137 | .toString(), 138 | 'should support `strict: ignore`' 139 | ) 140 | 141 | t.end() 142 | }) 143 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/test/fixture/small-svg.html: -------------------------------------------------------------------------------- 1 |

Inline math .

2 |

Block math:

3 |
4 | 119 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/test/fixture/markdown-svg.html: -------------------------------------------------------------------------------- 1 |

Inline math .

2 |

Block math:

3 |
118 | -------------------------------------------------------------------------------- /packages/remark-math/inline.js: -------------------------------------------------------------------------------- 1 | var util = require('./util') 2 | 3 | module.exports = mathInline 4 | 5 | const tab = 9 // '\t' 6 | const space = 32 // ' ' 7 | const dollarSign = 36 // '$' 8 | const digit0 = 48 // '0' 9 | const digit9 = 57 // '9' 10 | const backslash = 92 // '\\' 11 | 12 | const classList = ['math', 'math-inline'] 13 | const mathDisplay = 'math-display' 14 | 15 | function mathInline(options) { 16 | const parser = this.Parser 17 | const compiler = this.Compiler 18 | 19 | if (util.isRemarkParser(parser)) { 20 | attachParser(parser, options) 21 | } 22 | 23 | if (util.isRemarkCompiler(compiler)) { 24 | attachCompiler(compiler, options) 25 | } 26 | } 27 | 28 | function attachParser(parser, options) { 29 | const proto = parser.prototype 30 | const inlineMethods = proto.inlineMethods 31 | 32 | mathInlineTokenizer.locator = locator 33 | 34 | proto.inlineTokenizers.math = mathInlineTokenizer 35 | 36 | inlineMethods.splice(inlineMethods.indexOf('text'), 0, 'math') 37 | 38 | function locator(value, fromIndex) { 39 | return value.indexOf('$', fromIndex) 40 | } 41 | 42 | function mathInlineTokenizer(eat, value, silent) { 43 | const length = value.length 44 | let double = false 45 | let escaped = false 46 | let index = 0 47 | let previous 48 | let code 49 | let next 50 | let contentStart 51 | let contentEnd 52 | let valueEnd 53 | let content 54 | 55 | if (value.charCodeAt(index) === backslash) { 56 | escaped = true 57 | index++ 58 | } 59 | 60 | if (value.charCodeAt(index) !== dollarSign) { 61 | return 62 | } 63 | 64 | index++ 65 | 66 | // Support escaped dollars. 67 | if (escaped) { 68 | /* istanbul ignore if - never used (yet) */ 69 | if (silent) { 70 | return true 71 | } 72 | 73 | return eat(value.slice(0, index))({type: 'text', value: '$'}) 74 | } 75 | 76 | if (value.charCodeAt(index) === dollarSign) { 77 | double = true 78 | index++ 79 | } 80 | 81 | next = value.charCodeAt(index) 82 | 83 | // Opening fence cannot be followed by a space or a tab. 84 | if (next === space || next === tab) { 85 | return 86 | } 87 | 88 | contentStart = index 89 | 90 | while (index < length) { 91 | code = next 92 | next = value.charCodeAt(index + 1) 93 | 94 | if (code === dollarSign) { 95 | previous = value.charCodeAt(index - 1) 96 | 97 | // Closing fence cannot be preceded by a space or a tab, or followed by 98 | // a digit. 99 | // If a double marker was used to open, the closing fence must consist 100 | // of two dollars as well. 101 | if ( 102 | previous !== space && 103 | previous !== tab && 104 | // eslint-disable-next-line no-self-compare 105 | (next !== next || next < digit0 || next > digit9) && 106 | (!double || next === dollarSign) 107 | ) { 108 | contentEnd = index - 1 109 | 110 | index++ 111 | 112 | if (double) { 113 | index++ 114 | } 115 | 116 | valueEnd = index 117 | break 118 | } 119 | } else if (code === backslash) { 120 | index++ 121 | next = value.charCodeAt(index + 1) 122 | } 123 | 124 | index++ 125 | } 126 | 127 | if (valueEnd === undefined) { 128 | return 129 | } 130 | 131 | /* istanbul ignore if - never used (yet) */ 132 | if (silent) { 133 | return true 134 | } 135 | 136 | content = value.slice(contentStart, contentEnd + 1) 137 | 138 | return eat(value.slice(0, valueEnd))({ 139 | type: 'inlineMath', 140 | value: content, 141 | data: { 142 | hName: 'span', 143 | hProperties: { 144 | className: classList.concat( 145 | double && options.inlineMathDouble ? [mathDisplay] : [] 146 | ) 147 | }, 148 | hChildren: [{type: 'text', value: content}] 149 | } 150 | }) 151 | } 152 | } 153 | 154 | function attachCompiler(compiler) { 155 | const proto = compiler.prototype 156 | 157 | proto.visitors.inlineMath = compileInlineMath 158 | 159 | function compileInlineMath(node) { 160 | let fence = '$' 161 | const classes = 162 | (node.data && node.data.hProperties && node.data.hProperties.className) || 163 | [] 164 | 165 | if (classes.includes(mathDisplay)) { 166 | fence = '$$' 167 | } 168 | 169 | return fence + node.value + fence 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /packages/remark-math/block.js: -------------------------------------------------------------------------------- 1 | const util = require('./util') 2 | 3 | module.exports = mathBlock 4 | 5 | const lineFeed = 10 // '\n' 6 | const space = 32 // ' ' 7 | const dollarSign = 36 // '$' 8 | 9 | const lineFeedChar = '\n' 10 | const dollarSignChar = '$' 11 | 12 | const minFenceCount = 2 13 | 14 | const classList = ['math', 'math-display'] 15 | 16 | function mathBlock() { 17 | const parser = this.Parser 18 | const compiler = this.Compiler 19 | 20 | if (util.isRemarkParser(parser)) { 21 | attachParser(parser) 22 | } 23 | 24 | if (util.isRemarkCompiler(compiler)) { 25 | attachCompiler(compiler) 26 | } 27 | } 28 | 29 | function attachParser(parser) { 30 | const proto = parser.prototype 31 | const blockMethods = proto.blockMethods 32 | const interruptParagraph = proto.interruptParagraph 33 | const interruptList = proto.interruptList 34 | const interruptBlockquote = proto.interruptBlockquote 35 | 36 | proto.blockTokenizers.math = mathBlockTokenizer 37 | 38 | blockMethods.splice(blockMethods.indexOf('fencedCode') + 1, 0, 'math') 39 | 40 | // Inject math to interrupt rules 41 | interruptParagraph.splice(interruptParagraph.indexOf('fencedCode') + 1, 0, [ 42 | 'math' 43 | ]) 44 | interruptList.splice(interruptList.indexOf('fencedCode') + 1, 0, ['math']) 45 | interruptBlockquote.splice(interruptBlockquote.indexOf('fencedCode') + 1, 0, [ 46 | 'math' 47 | ]) 48 | 49 | function mathBlockTokenizer(eat, value, silent) { 50 | var length = value.length 51 | var index = 0 52 | let code 53 | let content 54 | let lineEnd 55 | let lineIndex 56 | let openingFenceIndentSize 57 | let openingFenceSize 58 | let openingFenceContentStart 59 | let closingFence 60 | let closingFenceSize 61 | let lineContentStart 62 | let lineContentEnd 63 | 64 | // Skip initial spacing. 65 | while (index < length && value.charCodeAt(index) === space) { 66 | index++ 67 | } 68 | 69 | openingFenceIndentSize = index 70 | 71 | // Skip the fence. 72 | while (index < length && value.charCodeAt(index) === dollarSign) { 73 | index++ 74 | } 75 | 76 | openingFenceSize = index - openingFenceIndentSize 77 | 78 | // Exit if there is not enough of a fence. 79 | if (openingFenceSize < minFenceCount) { 80 | return 81 | } 82 | 83 | // Skip spacing after the fence. 84 | while (index < length && value.charCodeAt(index) === space) { 85 | index++ 86 | } 87 | 88 | openingFenceContentStart = index 89 | 90 | // Eat everything after the fence. 91 | while (index < length) { 92 | code = value.charCodeAt(index) 93 | 94 | // We don’t allow dollar signs here, as that could interfere with inline 95 | // math. 96 | if (code === dollarSign) { 97 | return 98 | } 99 | 100 | if (code === lineFeed) { 101 | break 102 | } 103 | 104 | index++ 105 | } 106 | 107 | if (value.charCodeAt(index) !== lineFeed) { 108 | return 109 | } 110 | 111 | if (silent) { 112 | return true 113 | } 114 | 115 | content = [] 116 | 117 | if (openingFenceContentStart !== index) { 118 | content.push(value.slice(openingFenceContentStart, index)) 119 | } 120 | 121 | index++ 122 | lineEnd = value.indexOf(lineFeedChar, index + 1) 123 | lineEnd = lineEnd === -1 ? length : lineEnd 124 | 125 | while (index < length) { 126 | closingFence = false 127 | lineContentStart = index 128 | lineContentEnd = lineEnd 129 | lineIndex = lineEnd 130 | closingFenceSize = 0 131 | 132 | // First, let’s see if this is a valid closing fence. 133 | // Skip trailing white space 134 | while ( 135 | lineIndex > lineContentStart && 136 | value.charCodeAt(lineIndex - 1) === space 137 | ) { 138 | lineIndex-- 139 | } 140 | 141 | // Skip the fence. 142 | while ( 143 | lineIndex > lineContentStart && 144 | value.charCodeAt(lineIndex - 1) === dollarSign 145 | ) { 146 | closingFenceSize++ 147 | lineIndex-- 148 | } 149 | 150 | // Check if this is a valid closing fence line. 151 | if ( 152 | openingFenceSize <= closingFenceSize && 153 | value.indexOf(dollarSignChar, lineContentStart) === lineIndex 154 | ) { 155 | closingFence = true 156 | lineContentEnd = lineIndex 157 | } 158 | 159 | // Sweet, next, we need to trim the line. 160 | // Skip initial spacing. 161 | while ( 162 | lineContentStart <= lineContentEnd && 163 | lineContentStart - index < openingFenceIndentSize && 164 | value.charCodeAt(lineContentStart) === space 165 | ) { 166 | lineContentStart++ 167 | } 168 | 169 | // If this is a closing fence, skip final spacing. 170 | if (closingFence) { 171 | while ( 172 | lineContentEnd > lineContentStart && 173 | value.charCodeAt(lineContentEnd - 1) === space 174 | ) { 175 | lineContentEnd-- 176 | } 177 | } 178 | 179 | // If this is a content line, or if there is content before the fence: 180 | if (!closingFence || lineContentStart !== lineContentEnd) { 181 | content.push(value.slice(lineContentStart, lineContentEnd)) 182 | } 183 | 184 | if (closingFence) { 185 | break 186 | } 187 | 188 | index = lineEnd + 1 189 | lineEnd = value.indexOf(lineFeedChar, index + 1) 190 | lineEnd = lineEnd === -1 ? length : lineEnd 191 | } 192 | 193 | content = content.join('\n') 194 | 195 | return eat(value.slice(0, lineEnd))({ 196 | type: 'math', 197 | value: content, 198 | data: { 199 | hName: 'div', 200 | hProperties: {className: classList.concat()}, 201 | hChildren: [{type: 'text', value: content}] 202 | } 203 | }) 204 | } 205 | } 206 | 207 | function attachCompiler(compiler) { 208 | const proto = compiler.prototype 209 | 210 | proto.visitors.math = compileBlockMath 211 | 212 | function compileBlockMath(node) { 213 | return '$$\n' + node.value + '\n$$' 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /packages/rehype-katex/test.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const katex = require('katex') 3 | const unified = require('unified') 4 | const parseMarkdown = require('remark-parse') 5 | const remark2rehype = require('remark-rehype') 6 | const parseHtml = require('rehype-parse') 7 | const stringify = require('rehype-stringify') 8 | const math = require('../remark-math') 9 | const rehypeKatex = require('.') 10 | 11 | test('rehype-katex', function (t) { 12 | t.deepEqual( 13 | unified() 14 | .use(parseHtml, {fragment: true, position: false}) 15 | .use(rehypeKatex) 16 | .use(stringify) 17 | .processSync( 18 | [ 19 | '

Inline math \\alpha.

', 20 | '

Block math:

', 21 | '
\\gamma
' 22 | ].join('\n') 23 | ) 24 | .toString(), 25 | unified() 26 | .use(parseHtml, {fragment: true, position: false}) 27 | .use(stringify) 28 | .processSync( 29 | [ 30 | '

Inline math ' + 31 | katex.renderToString('\\alpha') + 32 | '.

', 33 | '

Block math:

', 34 | '
' + 35 | katex.renderToString('\\gamma', {displayMode: true}) + 36 | '
' 37 | ].join('\n') 38 | ) 39 | .toString(), 40 | 'should transform math with katex' 41 | ) 42 | 43 | t.deepEqual( 44 | unified() 45 | .use(parseMarkdown, {position: false}) 46 | .use(math) 47 | .use(remark2rehype) 48 | .use(rehypeKatex) 49 | .use(stringify) 50 | .processSync( 51 | [ 52 | 'Inline math $\\alpha$.', 53 | '', 54 | 'Block math:', 55 | '', 56 | '$$', 57 | '\\gamma', 58 | '$$' 59 | ].join('\n') 60 | ) 61 | .toString(), 62 | unified() 63 | .use(parseHtml, {fragment: true, position: false}) 64 | .use(stringify) 65 | .processSync( 66 | [ 67 | '

Inline math ' + 68 | katex.renderToString('\\alpha') + 69 | '.

', 70 | '

Block math:

', 71 | '
' + 72 | katex.renderToString('\\gamma', {displayMode: true}) + 73 | '
' 74 | ].join('\n') 75 | ) 76 | .toString(), 77 | 'should integrate with `remark-math`' 78 | ) 79 | 80 | t.deepEqual( 81 | unified() 82 | .use(parseHtml, {fragment: true, position: false}) 83 | .use(rehypeKatex) 84 | .use(stringify) 85 | .processSync( 86 | '

Double math \\alpha.

' 87 | ) 88 | .toString(), 89 | unified() 90 | .use(parseHtml, {fragment: true, position: false}) 91 | .use(stringify) 92 | .processSync( 93 | '

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

' 96 | ) 97 | .toString(), 98 | 'should transform `.math-inline.math-display` math with `displayMode: true`' 99 | ) 100 | 101 | const macros = {'\\RR': '\\mathbb{R}'} 102 | 103 | t.deepEqual( 104 | unified() 105 | .use(parseHtml, {fragment: true, position: false}) 106 | .use(rehypeKatex, {macros: macros}) 107 | .use(stringify) 108 | .processSync('\\RR') 109 | .toString(), 110 | unified() 111 | .use(parseHtml, {fragment: true, position: false}) 112 | .use(stringify) 113 | .processSync( 114 | '' + 115 | katex.renderToString('\\RR', {macros: macros}) + 116 | '' 117 | ) 118 | .toString(), 119 | 'should support `macros`' 120 | ) 121 | 122 | t.deepEqual( 123 | unified() 124 | .use(parseHtml, {fragment: true, position: false}) 125 | .use(rehypeKatex, {errorColor: 'orange'}) 126 | .use(stringify) 127 | .processSync('\\alpa') 128 | .toString(), 129 | unified() 130 | .use(parseHtml, {fragment: true, position: false}) 131 | .use(stringify) 132 | .processSync( 133 | '' + 134 | katex.renderToString('\\alpa', { 135 | throwOnError: false, 136 | errorColor: 'orange' 137 | }) + 138 | '' 139 | ) 140 | .toString(), 141 | 'should support `errorColor`' 142 | ) 143 | 144 | t.deepEqual( 145 | unified() 146 | .use(parseHtml, {fragment: true}) 147 | .use(rehypeKatex) 148 | .use(stringify) 149 | .processSync( 150 | '

Lorem

\n

\\alpa

' 151 | ) 152 | .messages.map(String), 153 | [ 154 | '2:4-2:42: KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲' 155 | ], 156 | 'should create a message for errors' 157 | ) 158 | 159 | try { 160 | unified() 161 | .use(parseHtml, {fragment: true}) 162 | .use(rehypeKatex, {throwOnError: true}) 163 | .use(stringify) 164 | .processSync( 165 | '

Lorem

\n

\\alpa

' 166 | ) 167 | } catch (error) { 168 | t.equal( 169 | error.message, 170 | 'KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲', 171 | 'should throw an error if `throwOnError: true`' 172 | ) 173 | } 174 | 175 | t.deepEqual( 176 | unified() 177 | .use(parseHtml, {fragment: true, position: false}) 178 | .use(rehypeKatex, {errorColor: 'orange', strict: 'ignore'}) 179 | .use(stringify) 180 | .processSync('ê&') 181 | .toString(), 182 | unified() 183 | .use(parseHtml, {fragment: true, position: false}) 184 | .use(stringify) 185 | .processSync( 186 | 'ê&' 187 | ) 188 | .toString(), 189 | 'should support `strict: ignore`' 190 | ) 191 | 192 | t.end() 193 | }) 194 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/readme.md: -------------------------------------------------------------------------------- 1 | # rehype-mathjax 2 | 3 | [![Build][build-badge]][build] 4 | [![Coverage][coverage-badge]][coverage] 5 | [![Downloads][downloads-badge]][downloads] 6 | [![Size][size-badge]][size] 7 | [![Sponsors][sponsors-badge]][collective] 8 | [![Backers][backers-badge]][collective] 9 | [![Chat][chat-badge]][chat] 10 | 11 | [**rehype**][rehype] plugin to transform `` and 12 | `
` with [MathJax][]. 13 | 14 | ## Install 15 | 16 | [npm][]: 17 | 18 | ```sh 19 | npm install rehype-mathjax 20 | ``` 21 | 22 | ## Use 23 | 24 | Say we have the following file, `example.html`: 25 | 26 | ```html 27 |

28 | Lift(L) can be determined by Lift Coefficient 29 | (C_L) like the following equation. 30 |

31 | 32 |
33 | L = \frac{1}{2} \rho v^2 S C_L 34 |
35 | ``` 36 | 37 | And our script, `example.js`, looks as follows: 38 | 39 | ```js 40 | const vfile = require('to-vfile') 41 | const unified = require('unified') 42 | const parse = require('rehype-parse') 43 | const mathjax = require('rehype-mathjax') 44 | const stringify = require('rehype-stringify') 45 | 46 | unified() 47 | .use(parse, {fragment: true}) 48 | .use(mathjax) 49 | .use(stringify) 50 | .process(vfile.readSync('example.html'), function (err, file) { 51 | if (err) throw err 52 | console.log(String(file)) 53 | }) 54 | ``` 55 | 56 | Now, running `node example` yields: 57 | 58 | ```html 59 |

60 | Lift() can be determined by Lift Coefficient 61 | () like the following equation. 62 |

63 | 64 |
65 | 76 | ``` 77 | 78 | ## API 79 | 80 | ### `rehype().use(rehypeMathJax[, options])` 81 | 82 | Transform `` and `
` with 83 | [MathJax][]. 84 | 85 | This package includes three plugins, split out because MathJax targets have a 86 | big memory and size footprint: SVG, CHTML, and browser: 87 | 88 | ###### SVG 89 | 90 | Render math as [SVG][mathjax-svg] (`require('rehype-mathjax/svg')`, default). 91 | About 566kb minzipped. 92 | 93 | ###### CHTML 94 | 95 | Render math as [CHTML][mathjax-chtml] (`require('rehype-mathjax/chtml')`). 96 | About 154kb minzipped. 97 | Needs a `fontURL` to be passed. 98 | 99 | ###### Browser 100 | 101 | Tiny wrapper to render MathJax client-side (`require('rehype-mathjax/browser')`). 102 | About 1kb minzipped. 103 | 104 | Uses `options.displayMath` (default: `['\\[', '\\]']`) for display, and 105 | `options.inlineMath` (default: `['\\(', '\\)']`) for inline math. 106 | 107 | You need to load MathJax on the client yourself and start it with corresponding 108 | markers. 109 | Options are not passed to MathJax: do that yourself on the client. 110 | 111 | #### `options` 112 | 113 | All options, except when using the browser plugin, are passed to 114 | [MathJax][mathjax-options]. 115 | 116 | ## Security 117 | 118 | Use of `rehype-mathjax` renders user content with [MathJax][], so any 119 | vulnerability in MathJax can open you to a [cross-site scripting (XSS)][xss] 120 | attack. 121 | 122 | Always be wary of user input and use [`rehype-sanitize`][rehype-sanitize]. 123 | 124 | ## Contribute 125 | 126 | See [`contributing.md`][contributing] in [`remarkjs/.github`][health] for ways 127 | to get started. 128 | See [`support.md`][support] for ways to get help. 129 | 130 | This project has a [code of conduct][coc]. 131 | By interacting with this repository, organization, or community you agree to 132 | abide by its terms. 133 | 134 | ## License 135 | 136 | [MIT][license] © [TANIGUCHI Masaya][author] 137 | 138 | 139 | 140 | [build-badge]: https://img.shields.io/travis/remarkjs/remark-math/main.svg 141 | 142 | [build]: https://travis-ci.org/remarkjs/remark-math 143 | 144 | [coverage-badge]: https://img.shields.io/codecov/c/github/remarkjs/remark-math.svg 145 | 146 | [coverage]: https://codecov.io/github/remarkjs/remark-math 147 | 148 | [downloads-badge]: https://img.shields.io/npm/dm/rehype-mathjax.svg 149 | 150 | [downloads]: https://www.npmjs.com/package/rehype-mathjax 151 | 152 | [size-badge]: https://img.shields.io/bundlephobia/minzip/rehype-mathjax.svg 153 | 154 | [size]: https://bundlephobia.com/result?p=rehype-mathjax 155 | 156 | [sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg 157 | 158 | [backers-badge]: https://opencollective.com/unified/backers/badge.svg 159 | 160 | [collective]: https://opencollective.com/unified 161 | 162 | [chat-badge]: https://img.shields.io/badge/chat-spectrum-7b16ff.svg 163 | 164 | [chat]: https://spectrum.chat/unified/remark 165 | 166 | [npm]: https://docs.npmjs.com/cli/install 167 | 168 | [health]: https://github.com/remarkjs/.github 169 | 170 | [contributing]: https://github.com/remarkjs/.github/blob/HEAD/contributing.md 171 | 172 | [support]: https://github.com/remarkjs/.github/blob/HEAD/support.md 173 | 174 | [coc]: https://github.com/remarkjs/.github/blob/HEAD/code-of-conduct.md 175 | 176 | [license]: https://github.com/remarkjs/remark-math/blob/main/license 177 | 178 | [author]: https://rokt33r.github.io 179 | 180 | [rehype]: https://github.com/rehypejs/rehype 181 | 182 | [xss]: https://en.wikipedia.org/wiki/Cross-site_scripting 183 | 184 | [rehype-sanitize]: https://github.com/rehypejs/rehype-sanitize 185 | 186 | [mathjax]: https://mathjax.org/ 187 | 188 | [mathjax-options]: http://docs.mathjax.org/en/latest/options/ 189 | 190 | [mathjax-svg]: http://docs.mathjax.org/en/latest/output/svg.html 191 | 192 | [mathjax-chtml]: http://docs.mathjax.org/en/latest/output/html.html 193 | -------------------------------------------------------------------------------- /packages/rehype-katex/readme.md: -------------------------------------------------------------------------------- 1 | # rehype-katex 2 | 3 | [![Build][build-badge]][build] 4 | [![Coverage][coverage-badge]][coverage] 5 | [![Downloads][downloads-badge]][downloads] 6 | [![Size][size-badge]][size] 7 | [![Sponsors][sponsors-badge]][collective] 8 | [![Backers][backers-badge]][collective] 9 | [![Chat][chat-badge]][chat] 10 | 11 | [**rehype**][rehype] plugin to transform `` and 12 | `
` with [KaTeX][]. 13 | 14 | ## Install 15 | 16 | [npm][]: 17 | 18 | ```sh 19 | npm install rehype-katex 20 | ``` 21 | 22 | ## Use 23 | 24 | Say we have the following file, `example.html`: 25 | 26 | ```html 27 |

28 | Lift(L) can be determined by Lift Coefficient 29 | (C_L) like the following equation. 30 |

31 | 32 |
33 | L = \frac{1}{2} \rho v^2 S C_L 34 |
35 | ``` 36 | 37 | And our script, `example.js`, looks as follows: 38 | 39 | ```js 40 | const vfile = require('to-vfile') 41 | const unified = require('unified') 42 | const parse = require('rehype-parse') 43 | const katex = require('rehype-katex') 44 | const stringify = require('rehype-stringify') 45 | 46 | unified() 47 | .use(parse, {fragment: true}) 48 | .use(katex) 49 | .use(stringify) 50 | .process(vfile.readSync('example.html'), function(err, file) { 51 | if (err) throw err 52 | console.log(String(file)) 53 | }) 54 | ``` 55 | 56 | Now, running `node example` yields: 57 | 58 | ```html 59 |

60 | Lift(LL) can be determined by Lift Coefficient 61 | (CLC_L) like the following equation. 62 |

63 | 64 |
L=12ρv2SCL 65 | L = \frac{1}{2} \rho v^2 S C_L 66 |
67 | ``` 68 | 69 | ## API 70 | 71 | ### `rehype().use(katex[, options])` 72 | 73 | Transform `` and `
` with 74 | [KaTeX][]. 75 | 76 | #### `options` 77 | 78 | ##### `options.throwOnError` 79 | 80 | Throw if a KaTeX parse error occurs (`boolean`, default: `false`). 81 | See [KaTeX options][katex-options]. 82 | 83 | ##### `options.<*>` 84 | 85 | All other options, except for `displayMode`, are passed to 86 | [KaTeX][katex-options]. 87 | 88 | ## Security 89 | 90 | Use of `rehype-katex` renders user content with [KaTeX][], so any vulnerability 91 | in KaTeX can open you to a [cross-site scripting (XSS)][xss] attack. 92 | 93 | Always be wary of user input and use [`rehype-sanitize`][rehype-sanitize]. 94 | 95 | ## Contribute 96 | 97 | See [`contributing.md`][contributing] in [`remarkjs/.github`][health] for ways 98 | to get started. 99 | See [`support.md`][support] for ways to get help. 100 | 101 | This project has a [code of conduct][coc]. 102 | By interacting with this repository, organization, or community you agree to 103 | abide by its terms. 104 | 105 | ## License 106 | 107 | [MIT][license] © [Junyoung Choi][author] 108 | 109 | 110 | 111 | [build-badge]: https://img.shields.io/travis/remarkjs/remark-math/main.svg 112 | 113 | [build]: https://travis-ci.org/remarkjs/remark-math 114 | 115 | [coverage-badge]: https://img.shields.io/codecov/c/github/remarkjs/remark-math.svg 116 | 117 | [coverage]: https://codecov.io/github/remarkjs/remark-math 118 | 119 | [downloads-badge]: https://img.shields.io/npm/dm/rehype-katex.svg 120 | 121 | [downloads]: https://www.npmjs.com/package/rehype-katex 122 | 123 | [size-badge]: https://img.shields.io/bundlephobia/minzip/rehype-katex.svg 124 | 125 | [size]: https://bundlephobia.com/result?p=rehype-katex 126 | 127 | [sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg 128 | 129 | [backers-badge]: https://opencollective.com/unified/backers/badge.svg 130 | 131 | [collective]: https://opencollective.com/unified 132 | 133 | [chat-badge]: https://img.shields.io/badge/chat-spectrum-7b16ff.svg 134 | 135 | [chat]: https://spectrum.chat/unified/remark 136 | 137 | [npm]: https://docs.npmjs.com/cli/install 138 | 139 | [health]: https://github.com/remarkjs/.github 140 | 141 | [contributing]: https://github.com/remarkjs/.github/blob/HEAD/contributing.md 142 | 143 | [support]: https://github.com/remarkjs/.github/blob/HEAD/support.md 144 | 145 | [coc]: https://github.com/remarkjs/.github/blob/HEAD/code-of-conduct.md 146 | 147 | [license]: https://github.com/remarkjs/remark-math/blob/main/license 148 | 149 | [author]: https://rokt33r.github.io 150 | 151 | [rehype]: https://github.com/rehypejs/rehype 152 | 153 | [xss]: https://en.wikipedia.org/wiki/Cross-site_scripting 154 | 155 | [rehype-sanitize]: https://github.com/rehypejs/rehype-sanitize 156 | 157 | [katex]: https://github.com/Khan/KaTeX 158 | 159 | [katex-options]: https://katex.org/docs/options.html 160 | -------------------------------------------------------------------------------- /packages/remark-math/readme.md: -------------------------------------------------------------------------------- 1 | # remark-math 2 | 3 | [![Build][build-badge]][build] 4 | [![Coverage][coverage-badge]][coverage] 5 | [![Downloads][downloads-badge]][downloads] 6 | [![Size][size-badge]][size] 7 | [![Sponsors][sponsors-badge]][collective] 8 | [![Backers][backers-badge]][collective] 9 | [![Chat][chat-badge]][chat] 10 | 11 | [**remark**][remark] plugin to parse and stringify math. 12 | 13 | ## Install 14 | 15 | [npm][]: 16 | 17 | ```sh 18 | npm install remark-math 19 | ``` 20 | 21 | ## Use 22 | 23 | Say we have the following file, `example.md`: 24 | 25 | ```markdown 26 | Lift($L$) can be determined by Lift Coefficient ($C_L$) like the following equation. 27 | 28 | $$ 29 | L = \frac{1}{2} \rho v^2 S C_L 30 | $$ 31 | ``` 32 | 33 | And our script, `example.js`, looks as follows: 34 | 35 | ```js 36 | const vfile = require('to-vfile') 37 | const unified = require('unified') 38 | const markdown = require('remark-parse') 39 | const math = require('remark-math') 40 | const remark2rehype = require('remark-rehype') 41 | const katex = require('rehype-katex') 42 | const stringify = require('rehype-stringify') 43 | 44 | unified() 45 | .use(markdown) 46 | .use(math) 47 | .use(remark2rehype) 48 | .use(katex) 49 | .use(stringify) 50 | .process(vfile.readSync('example.md'), function(err, file) { 51 | if (err) throw err 52 | console.log(String(file)) 53 | }) 54 | ``` 55 | 56 | Now, running `node example` yields: 57 | 58 | ```html 59 |

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

60 |
L=12ρv2SCLL = \frac{1}{2} \rho v^2 S C_L
61 | ``` 62 | 63 | ## API 64 | 65 | ### `remark().use(math[, options])` 66 | 67 | Parse and stringify math. 68 | 69 | Get’s useful when combined with [`rehype-katex`][rehype-katex] or 70 | [`remark-html-katex`][remark-html-katex]. 71 | 72 | You can also support only inline, or online block math, by importing them 73 | directly: 74 | 75 | ```js 76 | const mathInline = require('remark-math/inline') 77 | 78 | // … 79 | 80 | unified() 81 | // … 82 | .use(mathInline) 83 | // … 84 | ``` 85 | 86 | #### `options` 87 | 88 | ##### `options.inlineMathDouble` 89 | 90 | Add an extra `math-display` class to inline `$$` math (default: `false`). 91 | 92 | #### Notes 93 | 94 | ##### Escaping 95 | 96 | You can escape dollar signs with a back slash (`\`): 97 | 98 | ```markdown 99 | \$\alpha\$ 100 | 101 | $\alpha\$$ 102 | 103 | $$ 104 | \beta\$ 105 | $$ 106 | ``` 107 | 108 | ## Security 109 | 110 | Use of `remark-math` itself doesn’t open you up to [cross-site scripting 111 | (XSS)][xss] attacks. 112 | 113 | Always be wary of user input and use [`rehype-sanitize`][rehype-sanitize]. 114 | 115 | ## Contribute 116 | 117 | See [`contributing.md`][contributing] in [`remarkjs/.github`][health] for ways 118 | to get started. 119 | See [`support.md`][support] for ways to get help. 120 | 121 | This project has a [code of conduct][coc]. 122 | By interacting with this repository, organization, or community you agree to 123 | abide by its terms. 124 | 125 | ## License 126 | 127 | [MIT][license] © [Junyoung Choi][author] 128 | 129 | 130 | 131 | [build-badge]: https://img.shields.io/travis/remarkjs/remark-math/main.svg 132 | 133 | [build]: https://travis-ci.org/remarkjs/remark-math 134 | 135 | [coverage-badge]: https://img.shields.io/codecov/c/github/remarkjs/remark-math.svg 136 | 137 | [coverage]: https://codecov.io/github/remarkjs/remark-math 138 | 139 | [downloads-badge]: https://img.shields.io/npm/dm/remark-math.svg 140 | 141 | [downloads]: https://www.npmjs.com/package/remark-math 142 | 143 | [size-badge]: https://img.shields.io/bundlephobia/minzip/remark-math.svg 144 | 145 | [size]: https://bundlephobia.com/result?p=remark-math 146 | 147 | [sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg 148 | 149 | [backers-badge]: https://opencollective.com/unified/backers/badge.svg 150 | 151 | [collective]: https://opencollective.com/unified 152 | 153 | [chat-badge]: https://img.shields.io/badge/chat-spectrum-7b16ff.svg 154 | 155 | [chat]: https://spectrum.chat/unified/remark 156 | 157 | [npm]: https://docs.npmjs.com/cli/install 158 | 159 | [health]: https://github.com/remarkjs/.github 160 | 161 | [contributing]: https://github.com/remarkjs/.github/blob/HEAD/contributing.md 162 | 163 | [support]: https://github.com/remarkjs/.github/blob/HEAD/support.md 164 | 165 | [coc]: https://github.com/remarkjs/.github/blob/HEAD/code-of-conduct.md 166 | 167 | [license]: https://github.com/remarkjs/remark-math/blob/main/license 168 | 169 | [author]: https://rokt33r.github.io 170 | 171 | [remark]: https://github.com/remarkjs/remark 172 | 173 | [xss]: https://en.wikipedia.org/wiki/Cross-site_scripting 174 | 175 | [rehype-katex]: https://github.com/remarkjs/remark-math/tree/main/packages/rehype-katex 176 | 177 | [remark-html-katex]: https://github.com/remarkjs/remark-math/tree/main/packages/remark-html-katex 178 | 179 | [rehype-sanitize]: https://github.com/rehypejs/rehype-sanitize 180 | -------------------------------------------------------------------------------- /packages/rehype-mathjax/test/fixture/small-chtml.html: -------------------------------------------------------------------------------- 1 |

Inline math .

2 |

Block math:

3 |
4 | 397 | -------------------------------------------------------------------------------- /packages/remark-html-katex/readme.md: -------------------------------------------------------------------------------- 1 | # remark-html-katex 2 | 3 | [![Build][build-badge]][build] 4 | [![Coverage][coverage-badge]][coverage] 5 | [![Downloads][downloads-badge]][downloads] 6 | [![Size][size-badge]][size] 7 | [![Sponsors][sponsors-badge]][collective] 8 | [![Backers][backers-badge]][collective] 9 | [![Chat][chat-badge]][chat] 10 | 11 | [**remark**][remark] plugin to transform `inlineMath` and `math` nodes with 12 | [KaTeX][] for [`remark-html`][remark-html]. 13 | 14 | > This package integrates with [`remark-html`][remark-html]. 15 | > It’s better to work with [**rehype**][rehype], which is specifically made 16 | > for HTML, and to use [`rehype-katex`][rehype-katex] instead of this package. 17 | 18 | ## Install 19 | 20 | [npm][]: 21 | 22 | ```sh 23 | npm install remark-html-katex 24 | ``` 25 | 26 | ## Use 27 | 28 | Say we have the following file, `example.md`: 29 | 30 | ```html 31 | Lift($L$) can be determined by Lift Coefficient ($C_L$) like the following equation. 32 | 33 | $$ 34 | L = \frac{1}{2} \rho v^2 S C_L 35 | $$ 36 | ``` 37 | 38 | And our script, `example.js`, looks as follows: 39 | 40 | ```js 41 | const vfile = require('to-vfile') 42 | const unified = require('unified') 43 | const markdown = require('remark-parse') 44 | const math = require('remark-math') 45 | const htmlKatex = require('remark-html-katex') 46 | const html = require('remark-html') 47 | 48 | unified() 49 | .use(markdown) 50 | .use(math) 51 | .use(htmlKatex) 52 | .use(html) 53 | .process(vfile.readSync('example.md'), function(err, file) { 54 | if (err) throw err 55 | console.log(String(file)) 56 | }) 57 | ``` 58 | 59 | Now, running `node example` yields: 60 | 61 | ```html 62 |

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

63 |
L=12ρv2SCLL = \frac{1}{2} \rho v^2 S C_L
64 | ``` 65 | 66 | ## API 67 | 68 | ### `remark().use(htmlKatex[, options])` 69 | 70 | Transform `inlineMath` and `math` nodes with [KaTeX][] for 71 | [`remark-html`][remark-html]. 72 | 73 | #### `options` 74 | 75 | ##### `options.throwOnError` 76 | 77 | Throw if a KaTeX parse error occurs (`boolean`, default: `false`). 78 | See [KaTeX options][katex-options]. 79 | 80 | ##### `options.<*>` 81 | 82 | All other options, except for `displayMode`, are passed to 83 | [KaTeX][katex-options]. 84 | 85 | ## Security 86 | 87 | Use of `remark-html-katex` renders user content with [KaTeX][], so any 88 | vulnerability in KaTeX can open you to a [cross-site scripting (XSS)][xss] 89 | attack. 90 | 91 | Always be wary of user input and use the [`sanitize`][remark-html-sanitize] 92 | option of `remark-html`. 93 | 94 | ## Contribute 95 | 96 | See [`contributing.md`][contributing] in [`remarkjs/.github`][health] for ways 97 | to get started. 98 | See [`support.md`][support] for ways to get help. 99 | 100 | This project has a [code of conduct][coc]. 101 | By interacting with this repository, organization, or community you agree to 102 | abide by its terms. 103 | 104 | ## License 105 | 106 | [MIT][license] © [Junyoung Choi][author] 107 | 108 | 109 | 110 | [build-badge]: https://img.shields.io/travis/remarkjs/remark-math/main.svg 111 | 112 | [build]: https://travis-ci.org/remarkjs/remark-math 113 | 114 | [coverage-badge]: https://img.shields.io/codecov/c/github/remarkjs/remark-math.svg 115 | 116 | [coverage]: https://codecov.io/github/remarkjs/remark-math 117 | 118 | [downloads-badge]: https://img.shields.io/npm/dm/remark-html-katex.svg 119 | 120 | [downloads]: https://www.npmjs.com/package/remark-html-katex 121 | 122 | [size-badge]: https://img.shields.io/bundlephobia/minzip/remark-html-katex.svg 123 | 124 | [size]: https://bundlephobia.com/result?p=remark-html-katex 125 | 126 | [sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg 127 | 128 | [backers-badge]: https://opencollective.com/unified/backers/badge.svg 129 | 130 | [collective]: https://opencollective.com/unified 131 | 132 | [chat-badge]: https://img.shields.io/badge/chat-spectrum-7b16ff.svg 133 | 134 | [chat]: https://spectrum.chat/unified/remark 135 | 136 | [npm]: https://docs.npmjs.com/cli/install 137 | 138 | [health]: https://github.com/remarkjs/.github 139 | 140 | [contributing]: https://github.com/remarkjs/.github/blob/HEAD/contributing.md 141 | 142 | [support]: https://github.com/remarkjs/.github/blob/HEAD/support.md 143 | 144 | [coc]: https://github.com/remarkjs/.github/blob/HEAD/code-of-conduct.md 145 | 146 | [license]: https://github.com/remarkjs/remark-math/blob/main/license 147 | 148 | [author]: https://rokt33r.github.io 149 | 150 | [remark]: https://github.com/remarkjs/remark 151 | 152 | [remark-html]: https://github.com/remarkjs/remark-html 153 | 154 | [remark-html-sanitize]: https://github.com/remarkjs/remark-html#optionssanitize 155 | 156 | [rehype]: https://github.com/rehypejs/rehype 157 | 158 | [xss]: https://en.wikipedia.org/wiki/Cross-site_scripting 159 | 160 | [katex]: https://github.com/Khan/KaTeX 161 | 162 | [katex-options]: https://katex.org/docs/options.html 163 | 164 | [rehype-katex]: https://github.com/remarkjs/remark-math/tree/main/packages/rehype-katex 165 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # remark-math 2 | 3 | [![Build][build-badge]][build] 4 | [![Coverage][coverage-badge]][coverage] 5 | [![Downloads][downloads-badge]][downloads] 6 | [![Size][size-badge]][size] 7 | [![Sponsors][sponsors-badge]][collective] 8 | [![Backers][backers-badge]][collective] 9 | [![Chat][chat-badge]][chat] 10 | 11 | [**remark**][remark] and [**rehype**][rehype] plugins to support math! 12 | 13 | ## Install 14 | 15 | [npm][]: 16 | 17 | ```sh 18 | npm install remark-math rehype-katex 19 | ``` 20 | 21 | ## Use 22 | 23 | Say we have the following file, `example.md`: 24 | 25 | ```markdown 26 | Lift($L$) can be determined by Lift Coefficient ($C_L$) like the following equation. 27 | 28 | $$ 29 | L = \frac{1}{2} \rho v^2 S C_L 30 | $$ 31 | ``` 32 | 33 | And our script, `example.js`, looks as follows: 34 | 35 | ```js 36 | const vfile = require('to-vfile') 37 | const unified = require('unified') 38 | const markdown = require('remark-parse') 39 | const math = require('remark-math') 40 | const remark2rehype = require('remark-rehype') 41 | const katex = require('rehype-katex') 42 | const stringify = require('rehype-stringify') 43 | 44 | unified() 45 | .use(markdown) 46 | .use(math) 47 | .use(remark2rehype) 48 | .use(katex) 49 | .use(stringify) 50 | .process(vfile.readSync('example.md'), function(err, file) { 51 | if (err) throw err 52 | console.log(String(file)) 53 | }) 54 | ``` 55 | 56 | Now, running `node example` yields: 57 | 58 | ```html 59 |

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

60 |
L=12ρv2SCLL = \frac{1}{2} \rho v^2 S C_L
61 | ``` 62 | 63 | Wow, that’s a lot! 64 | But in a browser, that looks something like this: 65 | 66 | ![][screenshot] 67 | 68 | > Note: you should also use `katex.css` somewhere on the page to style math 69 | > properly: 70 | > 71 | > ```html 72 | > 73 | > ``` 74 | 75 | ## Packages 76 | 77 | This repo houses four packages: 78 | 79 | * [`remark-math`][remark-math] 80 | — Parses `$` as `inlineMath` and `$$` as `math` nodes 81 | * [`rehype-katex`][rehype-katex] 82 | — Transforms math nodes with [KaTeX][] 83 | (✨ recommended) 84 | * [`rehype-mathjax`][rehype-mathjax] 85 | — Transforms math nodes with [MathJax][] 86 | (✨ recommended) 87 | * [`remark-html-katex`][remark-html-katex] 88 | — Transforms math nodes with [KaTeX][] for [`remark-html`][remark-html] 89 | (discouraged) 90 | 91 | See their readmes for more information. 92 | 93 | ## Security 94 | 95 | Use of `rehype-katex` or `remark-html-katex` renders user content with 96 | [KaTeX][], so any vulnerability in KaTeX can open you to a 97 | [cross-site scripting (XSS)][xss] attack. 98 | 99 | Always be wary of user input and use [`rehype-sanitize`][rehype-sanitize]. 100 | 101 | ## Related 102 | 103 | * [`remark-github`][remark-github] 104 | — Auto-link references like in GitHub issues, PRs, and comments 105 | * [`remark-frontmatter`][remark-frontmatter] 106 | — Support frontmatter (YAML, TOML, and more) 107 | * [`remark-footnotes`][remark-footnotes] 108 | — Support footnotes 109 | * [`remark-breaks`][remark-breaks] 110 | – Support hard breaks without needing spaces (like on issues) 111 | 112 | ## Contribute 113 | 114 | See [`contributing.md`][contributing] in [`remarkjs/.github`][health] for ways 115 | to get started. 116 | See [`support.md`][support] for ways to get help. 117 | 118 | This project has a [code of conduct][coc]. 119 | By interacting with this repository, organization, or community you agree to 120 | abide by its terms. 121 | 122 | ## License 123 | 124 | [MIT][license] © [Junyoung Choi][author] 125 | 126 | 127 | 128 | [build-badge]: https://img.shields.io/travis/remarkjs/remark-math/main.svg 129 | 130 | [build]: https://travis-ci.org/remarkjs/remark-math 131 | 132 | [coverage-badge]: https://img.shields.io/codecov/c/github/remarkjs/remark-math.svg 133 | 134 | [coverage]: https://codecov.io/github/remarkjs/remark-math 135 | 136 | [downloads-badge]: https://img.shields.io/npm/dm/remark-math.svg 137 | 138 | [downloads]: https://www.npmjs.com/package/remark-math 139 | 140 | [size-badge]: https://img.shields.io/bundlephobia/minzip/remark-math.svg 141 | 142 | [size]: https://bundlephobia.com/result?p=remark-math 143 | 144 | [sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg 145 | 146 | [backers-badge]: https://opencollective.com/unified/backers/badge.svg 147 | 148 | [collective]: https://opencollective.com/unified 149 | 150 | [chat-badge]: https://img.shields.io/badge/chat-spectrum-7b16ff.svg 151 | 152 | [chat]: https://spectrum.chat/unified/remark 153 | 154 | [npm]: https://docs.npmjs.com/cli/install 155 | 156 | [health]: https://github.com/remarkjs/.github 157 | 158 | [contributing]: https://github.com/remarkjs/.github/blob/HEAD/contributing.md 159 | 160 | [support]: https://github.com/remarkjs/.github/blob/HEAD/support.md 161 | 162 | [coc]: https://github.com/remarkjs/.github/blob/HEAD/code-of-conduct.md 163 | 164 | [license]: license 165 | 166 | [author]: https://rokt33r.github.io 167 | 168 | [remark]: https://github.com/remarkjs/remark 169 | 170 | [remark-html]: https://github.com/remarkjs/remark-html 171 | 172 | [remark-github]: https://github.com/remarkjs/remark-github 173 | 174 | [remark-frontmatter]: https://github.com/remarkjs/remark-frontmatter 175 | 176 | [remark-footnotes]: https://github.com/remarkjs/remark-footnotes 177 | 178 | [remark-breaks]: https://github.com/remarkjs/remark-breaks 179 | 180 | [rehype]: https://github.com/rehypejs/rehype 181 | 182 | [rehype-sanitize]: https://github.com/rehypejs/rehype-sanitize 183 | 184 | [katex]: https://github.com/Khan/KaTeX 185 | 186 | [mathjax]: https://mathjax.org/ 187 | 188 | [xss]: https://en.wikipedia.org/wiki/Cross-site_scripting 189 | 190 | [remark-math]: ./packages/remark-math 191 | 192 | [rehype-katex]: ./packages/rehype-katex 193 | 194 | [rehype-mathjax]: ./packages/rehype-mathjax 195 | 196 | [remark-html-katex]: ./packages/remark-html-katex 197 | 198 | [screenshot]: screenshot.png 199 | -------------------------------------------------------------------------------- /packages/remark-math/test.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const unified = require('unified') 3 | const parse = require('remark-parse') 4 | const remark2rehype = require('remark-rehype') 5 | const rehypeStringify = require('rehype-stringify') 6 | const stringify = require('remark-stringify') 7 | const u = require('unist-builder') 8 | const math = require('.') 9 | 10 | test('remark-math', function (t) { 11 | const toHtml = unified() 12 | .use(parse) 13 | .use(math, {inlineMathDouble: true}) 14 | .use(remark2rehype) 15 | .use(rehypeStringify) 16 | 17 | t.deepEqual( 18 | unified() 19 | .use(parse, {position: false}) 20 | .use(math) 21 | .parse('Math $\\alpha$\n\n$$\n\\beta+\\gamma\n$$'), 22 | u('root', [ 23 | u('paragraph', [ 24 | u('text', 'Math '), 25 | u( 26 | 'inlineMath', 27 | { 28 | data: { 29 | hName: 'span', 30 | hProperties: {className: ['math', 'math-inline']}, 31 | hChildren: [u('text', '\\alpha')] 32 | } 33 | }, 34 | '\\alpha' 35 | ) 36 | ]), 37 | u( 38 | 'math', 39 | { 40 | data: { 41 | hName: 'div', 42 | hProperties: {className: ['math', 'math-display']}, 43 | hChildren: [u('text', '\\beta+\\gamma')] 44 | } 45 | }, 46 | '\\beta+\\gamma' 47 | ) 48 | ]), 49 | 'should parse inline and block math' 50 | ) 51 | 52 | t.deepEqual( 53 | unified().use(parse, {position: false}).use(math).parse('\\$\\alpha$'), 54 | u('root', [u('paragraph', [u('text', '$'), u('text', '\\alpha$')])]), 55 | 'should ignore an escaped opening dollar sign' 56 | ) 57 | 58 | t.deepEqual( 59 | unified().use(parse, {position: false}).use(math).parse('$\\alpha\\$'), 60 | u('root', [u('paragraph', [u('text', '$\\alpha'), u('text', '$')])]), 61 | 'should ignore an escaped closing dollar sign' 62 | ) 63 | 64 | t.deepEqual( 65 | unified().use(parse, {position: false}).use(math).parse('\\$\\alpha$'), 66 | u('root', [u('paragraph', [u('text', '$'), u('text', '\\alpha$')])]), 67 | 'should ignore an escaped opening dollar sign' 68 | ) 69 | t.deepEqual( 70 | unified().use(parse, {position: false}).use(math).parse('$\\alpha\\$'), 71 | u('root', [u('paragraph', [u('text', '$\\alpha'), u('text', '$')])]), 72 | 'should ignore an escaped closing dollar sign' 73 | ) 74 | 75 | t.deepEqual( 76 | unified().use(parse, {position: false}).use(math).parse('\\\\$\\alpha$'), 77 | u('root', [ 78 | u('paragraph', [ 79 | u('text', '\\'), 80 | u( 81 | 'inlineMath', 82 | { 83 | data: { 84 | hName: 'span', 85 | hProperties: {className: ['math', 'math-inline']}, 86 | hChildren: [u('text', '\\alpha')] 87 | } 88 | }, 89 | '\\alpha' 90 | ) 91 | ]) 92 | ]), 93 | 'should support a escaped escape before a dollar sign' 94 | ) 95 | 96 | t.deepEqual( 97 | unified().use(parse, {position: false}).use(math).parse('`$`\\alpha$'), 98 | u('root', [u('paragraph', [u('inlineCode', '$'), u('text', '\\alpha$')])]), 99 | 'should ignore dollar signs in inline code (#1)' 100 | ) 101 | 102 | t.deepEqual( 103 | unified().use(parse, {position: false}).use(math).parse('$\\alpha`$`'), 104 | u('root', [ 105 | u('paragraph', [ 106 | u( 107 | 'inlineMath', 108 | { 109 | data: { 110 | hName: 'span', 111 | hProperties: {className: ['math', 'math-inline']}, 112 | hChildren: [u('text', '\\alpha`')] 113 | } 114 | }, 115 | '\\alpha`' 116 | ), 117 | u('text', '`') 118 | ]) 119 | ]), 120 | 'should allow backticks in math' 121 | ) 122 | 123 | t.deepEqual( 124 | unified().use(parse, {position: false}).use(math).parse('$`\\alpha`$'), 125 | u('root', [ 126 | u('paragraph', [ 127 | u( 128 | 'inlineMath', 129 | { 130 | data: { 131 | hName: 'span', 132 | hProperties: {className: ['math', 'math-inline']}, 133 | hChildren: [u('text', '`\\alpha`')] 134 | } 135 | }, 136 | '`\\alpha`' 137 | ) 138 | ]) 139 | ]), 140 | 'should support backticks in inline math' 141 | ) 142 | 143 | t.deepEqual( 144 | unified().use(parse, {position: false}).use(math).parse('$\\alpha\\$$'), 145 | u('root', [ 146 | u('paragraph', [ 147 | u( 148 | 'inlineMath', 149 | { 150 | data: { 151 | hName: 'span', 152 | hProperties: {className: ['math', 'math-inline']}, 153 | hChildren: [u('text', '\\alpha\\$')] 154 | } 155 | }, 156 | '\\alpha\\$' 157 | ) 158 | ]) 159 | ]), 160 | 'should support a super factorial in inline math' 161 | ) 162 | 163 | t.deepEqual( 164 | unified() 165 | .use(parse, {position: false}) 166 | .use(math) 167 | .parse('$$\n\\alpha\\$\n$$'), 168 | u('root', [ 169 | u( 170 | 'math', 171 | { 172 | data: { 173 | hName: 'div', 174 | hProperties: {className: ['math', 'math-display']}, 175 | hChildren: [u('text', '\\alpha\\$')] 176 | } 177 | }, 178 | '\\alpha\\$' 179 | ) 180 | ]), 181 | 'should support a super factorial in block math' 182 | ) 183 | 184 | t.deepEqual( 185 | unified() 186 | .use(parse, {position: false}) 187 | .use(math) 188 | .parse('tango\n$$\n\\alpha\n$$'), 189 | u('root', [ 190 | u('paragraph', [u('text', 'tango')]), 191 | u( 192 | 'math', 193 | { 194 | data: { 195 | hName: 'div', 196 | hProperties: {className: ['math', 'math-display']}, 197 | hChildren: [u('text', '\\alpha')] 198 | } 199 | }, 200 | '\\alpha' 201 | ) 202 | ]), 203 | 'should support a math block right after a paragraph' 204 | ) 205 | 206 | t.deepEqual( 207 | unified().use(parse, {position: false}).use(math).parse('$$\\alpha$$'), 208 | u('root', [ 209 | u('paragraph', [ 210 | u( 211 | 'inlineMath', 212 | { 213 | data: { 214 | hName: 'span', 215 | hProperties: {className: ['math', 'math-inline']}, 216 | hChildren: [u('text', '\\alpha')] 217 | } 218 | }, 219 | '\\alpha' 220 | ) 221 | ]) 222 | ]), 223 | 'should support inline math with double dollars' 224 | ) 225 | 226 | t.deepEqual( 227 | unified() 228 | .use(parse, {position: false}) 229 | .use(math) 230 | .parse('$$$\n\\alpha\n$$$'), 231 | u('root', [ 232 | u( 233 | 'math', 234 | { 235 | data: { 236 | hName: 'div', 237 | hProperties: {className: ['math', 'math-display']}, 238 | hChildren: [u('text', '\\alpha')] 239 | } 240 | }, 241 | '\\alpha' 242 | ) 243 | ]), 244 | 'should support block math with triple dollars' 245 | ) 246 | 247 | t.deepEqual( 248 | unified() 249 | .use(parse, {position: false}) 250 | .use(math) 251 | .parse(' $$\n \\alpha\n $$'), 252 | u('root', [ 253 | u( 254 | 'math', 255 | { 256 | data: { 257 | hName: 'div', 258 | hProperties: {className: ['math', 'math-display']}, 259 | hChildren: [u('text', ' \\alpha')] 260 | } 261 | }, 262 | ' \\alpha' 263 | ) 264 | ]), 265 | 'should support indented block math' 266 | ) 267 | 268 | t.deepEqual( 269 | unified() 270 | .use(parse, {position: false}) 271 | .use(stringify) 272 | .use(math) 273 | .processSync('Math $\\alpha$\n\n$$\n\\beta+\\gamma\n$$\n') 274 | .toString(), 275 | 'Math $\\alpha$\n\n$$\n\\beta+\\gamma\n$$\n', 276 | 'should stringify inline and block math' 277 | ) 278 | 279 | t.deepEqual( 280 | unified() 281 | .use(parse, {position: false}) 282 | .use(stringify) 283 | .use(math) 284 | .processSync('> $$\n> \\alpha\\beta\n> $$\n') 285 | .toString(), 286 | '> $$\n> \\alpha\\beta\n> $$\n', 287 | 'should stringify math in a blockquote' 288 | ) 289 | 290 | t.deepEqual( 291 | String(toHtml.processSync('$$just two dollars')), 292 | '

$$just two dollars

', 293 | 'should not support an opening fence without newline' 294 | ) 295 | t.deepEqual( 296 | String(toHtml.processSync('$$ must\n\\alpha\n$$')), 297 | '
must\n\\alpha
', 298 | 'should include values after the opening fence (except for spacing #1)' 299 | ) 300 | t.deepEqual( 301 | String(toHtml.processSync('$$ \n\\alpha\n$$')), 302 | '
\\alpha
', 303 | 'should include values after the opening fence (except for spacing #2)' 304 | ) 305 | t.deepEqual( 306 | String(toHtml.processSync('$$\n\\alpha\nmust $$')), 307 | '
\\alpha\nmust
', 308 | 'should include values before the closing fence (except for spacing #1)' 309 | ) 310 | t.deepEqual( 311 | String(toHtml.processSync('$$\n\\alpha\n $$')), 312 | '
\\alpha
', 313 | 'should include values before the closing fence (except for spacing #2)' 314 | ) 315 | t.deepEqual( 316 | String(toHtml.processSync('$$\n\\alpha$$ ')), 317 | '
\\alpha
', 318 | 'should exclude spacing after the closing fence' 319 | ) 320 | 321 | t.deepEqual( 322 | unified() 323 | .use(parse, {position: false}) 324 | .use(math) 325 | .parse('$$\n\\alpha\n$$\n```\nbravo\n```\n'), 326 | u('root', [ 327 | u( 328 | 'math', 329 | { 330 | data: { 331 | hName: 'div', 332 | hProperties: {className: ['math', 'math-display']}, 333 | hChildren: [u('text', '\\alpha')] 334 | } 335 | }, 336 | '\\alpha' 337 | ), 338 | u('code', {lang: null, meta: null}, 'bravo') 339 | ]), 340 | 'should not affect the next block' 341 | ) 342 | 343 | t.deepEqual( 344 | unified() 345 | .use(parse, {position: false}) 346 | .use(math, {inlineMathDouble: true}) 347 | .parse('$$\\alpha$$'), 348 | u('root', [ 349 | u('paragraph', [ 350 | u( 351 | 'inlineMath', 352 | { 353 | data: { 354 | hName: 'span', 355 | hProperties: {className: ['math', 'math-inline', 'math-display']}, 356 | hChildren: [u('text', '\\alpha')] 357 | } 358 | }, 359 | '\\alpha' 360 | ) 361 | ]) 362 | ]), 363 | 'should add a `math-display` class to inline math with double dollars if `inlineMathDouble: true`' 364 | ) 365 | 366 | t.deepEqual( 367 | unified() 368 | .use(stringify) 369 | .use(math) 370 | .stringify( 371 | u('root', [ 372 | u('paragraph', [u('text', 'Math '), u('inlineMath', '\\alpha')]), 373 | u('math', '\\beta+\\gamma') 374 | ]) 375 | ) 376 | .toString(), 377 | 'Math $\\alpha$\n\n$$\n\\beta+\\gamma\n$$\n', 378 | 'should stringify a tree' 379 | ) 380 | 381 | t.deepEqual( 382 | unified() 383 | .use(parse, {position: false}) 384 | .use(stringify) 385 | .use(math) 386 | .processSync('$$\\alpha$$') 387 | .toString(), 388 | '$\\alpha$\n', 389 | 'should stringify inline math with double dollars using one dollar by default' 390 | ) 391 | 392 | t.deepEqual( 393 | unified() 394 | .use(parse, {position: false}) 395 | .use(stringify) 396 | .use(math, {inlineMathDouble: true}) 397 | .processSync('$$\\alpha$$') 398 | .toString(), 399 | '$$\\alpha$$\n', 400 | 'should stringify inline math with double dollars using one dollar if `inlineMathDouble: true`' 401 | ) 402 | 403 | t.deepEqual( 404 | String(toHtml.processSync('$1+1 = 2$')), 405 | '

1+1 = 2

', 406 | 'markdown-it-katex#01' 407 | ) 408 | t.deepEqual( 409 | String(toHtml.processSync('$$1+1 = 2$$')), 410 | '

1+1 = 2

', 411 | 'markdown-it-katex#02' 412 | ) 413 | t.deepEqual( 414 | String(toHtml.processSync('foo$1+1 = 2$bar')), 415 | '

foo1+1 = 2bar

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

foo-1+1 = 2bar

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

aaa $$ bbb

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

aaa $5.99 bbb

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

foo $1+1

\n

= 2$ bar

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

foo 1 *i* 1 bar

', 441 | 'markdown-it-katex#08: inline math with apparent markup should not be processed' 442 | ) 443 | t.deepEqual( 444 | String(toHtml.processSync(' $$\n 1+1 = 2\n $$')), 445 | '
1+1 = 2
', 446 | 'markdown-it-katex#09: block math can be indented up to 3 spaces' 447 | ) 448 | t.deepEqual( 449 | String(toHtml.processSync(' $$\n 1+1 = 2\n $$')), 450 | '
$$\n1+1 = 2\n$$\n
', 451 | 'markdown-it-katex#10: …but 4 means a code block' 452 | ) 453 | t.deepEqual( 454 | String(toHtml.processSync('foo $1 + 1\n= 2$ bar')), 455 | '

foo 1 + 1\n= 2 bar

', 456 | 'markdown-it-katex#11: multiline inline math' 457 | ) 458 | t.deepEqual( 459 | String(toHtml.processSync('$$\n\n 1\n+ 1\n\n= 2\n\n$$')), 460 | '
\n 1\n+ 1\n\n= 2\n\n
', 461 | 'markdown-it-katex#12: multiline display math' 462 | ) 463 | t.deepEqual( 464 | String(toHtml.processSync('$n$-th order')), 465 | '

n-th order

', 466 | 'markdown-it-katex#13: text can immediately follow inline math' 467 | ) 468 | t.deepEqual( 469 | String(toHtml.processSync('$$\n1+1 = 2')), 470 | '
1+1 = 2
', 471 | 'markdown-it-katex#14: display math self-closes at the end of document' 472 | ) 473 | t.deepEqual( 474 | String(toHtml.processSync('* $1+1 = 2$\n* $$\n 1+1 = 2\n $$')), 475 | '
    \n
  • 1+1 = 2
  • \n
  • 1+1 = 2
  • \n
', 476 | 'markdown-it-katex#15: display and inline math can appear in lists' 477 | ) 478 | t.deepEqual( 479 | String(toHtml.processSync('$$1+1 = 2$$')), 480 | '

1+1 = 2

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

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

', 492 | 'markdown-it-katex#18: escaped delimiters should not render math' 493 | ) 494 | t.deepEqual( 495 | String( 496 | toHtml.processSync('Thus, $20,000 and USD$30,000 won’t parse as math.') 497 | ), 498 | '

Thus, $20,000 and USD$30,000 won’t parse as math.

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

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

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

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

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

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

', 517 | 'markdown-it-katex#23: escaped delimiters in math mode' 518 | ) 519 | t.deepEqual( 520 | String( 521 | toHtml.processSync( 522 | 'Weird-o: $\\displaystyle{\\begin{pmatrix} \\$ & 1\\\\\\$ \\end{pmatrix}}$.' 523 | ) 524 | ), 525 | '

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

', 526 | 'markdown-it-katex#24: multiple escaped delimiters in math module' 527 | ) 528 | 529 | t.end() 530 | }) 531 | --------------------------------------------------------------------------------