remarkjs/remark-math

`rehype-katex` does not catch non-parse errors

FindDefinition opened this issue · 7 comments

Initial checklist

Affected packages and versions

5.1.1

Link to runnable example

https://codesandbox.io/s/markdown-editor-with-katex-forked-x63zo4

Steps to reproduce

use invalid latex code with rehypeKatex and react-markdown:

\begin{split}
A & = \frac{\pi r^2}{2} \\
 & = \frac{1}{2} \pi r^2
\end{{split}}

Expected behavior

invalid latex code is displayed with red color
here is a fixed version of rehypeKatex that has expected behavior:

/**
 * @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'
import {toText} from 'hast-util-to-text'
import {unified} from 'unified'
import rehypeParse from 'rehype-parse'

const assign = Object.assign

const parseHtml = unified().use(rehypeParse, {fragment: true})

const source = 'rehype-katex'

/**
 * Plugin to transform `<span class=math-inline>` and `<div class=math-display>`
 * with KaTeX.
 *
 * @type {import('unified').Plugin<[Options?]|void[], Root>}
 */
export default function rehypeKatex(options) {
  const settings = options || {}
  const throwOnError = settings.throwOnError || false

  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')

      if (!inline && !displayMode) {
        return
      }
      const value = toText(element, {whitespace: 'pre'})

      /** @type {string | null} */
      let result = null

      try {
        result = katex.renderToString(
          value,
          assign({}, settings, {displayMode, throwOnError: true})
        )
      } catch (error_) {
        const error = /** @type {Error} */ (error_)
        if (error instanceof katex.ParseError) {
            const fn = throwOnError ? 'fail' : 'message'
            const origin = [source, error.name.toLowerCase()].join(':')
    
            file[fn](error.message, element.position, origin)
            result = katex.renderToString(
              value,
              assign({}, settings, {
                displayMode,
                throwOnError: false,
                strict: 'ignore'
              })
            )    
        }else{
          result = `<div style="color: red">${value}</div>`
        }
      }
      // @ts-expect-error: assume no `doctypes` in KaTeX result.
      element.children = removePosition(parseHtml.parse(result), true).children
    })
  }
}

Actual behavior

element crash.

Runtime

No response

Package manager

No response

OS

No response

Build and bundle tools

No response

Hi! This was closed. Team: If this was fixed, please add phase/solved. Otherwise, please add one of the no/* labels.

@wooorm ?????? I know the error boundry, if I use rehype-mathjax, the syntax error is handled inside mathjax to tell users that math block is wrong WITHOUT breaking render of legal markdown code, but it's impossible to implement this in rehype-katex via error boundry. this error will break whole react-markdown renderer.

wooorm commented

I don’t know what you want.
Your codesandbox is broken.
Your code sandbox does not use react-markdown or rehype-katex.
If there is an error, show a stack trace.
Your issue title, “invalid latex code breaks whole react-markdown”, is incorrect: set throwOnError: false

@wooorm thanks for reply. This bug should be a katex bug, not rehype-katex. I will open a issue in katex.
Here is error image. the codesandbox is my mistake, should be fixed now.
image

Hi! This was marked as ready to be worked on! Note that while this is ready to be worked on, nothing is said about priority: it may take a while for this to be solved.

Is this something you can and want to work on?

Team: please use the area/* (to describe the scope of the change), platform/* (if this is related to a specific one), and semver/* and type/* labels to annotate this. If this is first-timers friendly, add good first issue and if this could use help, add help wanted.