diff --git a/packages/rehype-katex/index.js b/packages/rehype-katex/index.js index 7f0fe78..d60ab01 100644 --- a/packages/rehype-katex/index.js +++ b/packages/rehype-katex/index.js @@ -2,11 +2,15 @@ * @typedef {import('hast').ElementContent} ElementContent * @typedef {import('hast').Root} Root * - * @typedef {import('katex').KatexOptions} Options + * @typedef {import('katex').KatexOptions} KatexOptions * * @typedef {import('vfile').VFile} VFile */ +/** + * @typedef {Omit} Options + */ + import {fromHtmlIsomorphic} from 'hast-util-from-html-isomorphic' import {toText} from 'hast-util-to-text' import katex from 'katex' @@ -28,7 +32,6 @@ const emptyClasses = [] */ export default function rehypeKatex(options) { const settings = options || emptyOptions - const throwOnError = settings.throwOnError || false /** * Transform. @@ -41,7 +44,7 @@ export default function rehypeKatex(options) { * Nothing. */ return function (tree, file) { - visit(tree, 'element', function (element) { + visit(tree, 'element', function (element, _, parent) { const classes = Array.isArray(element.properties.className) ? element.properties.className : emptyClasses @@ -64,16 +67,30 @@ export default function rehypeKatex(options) { throwOnError: true }) } catch (error) { - const exception = /** @type {Error} */ (error) - const fn = throwOnError ? 'fail' : 'message' - const origin = ['rehype-katex', exception.name.toLowerCase()].join(':') + const cause = /** @type {Error} */ (error) + const ruleId = cause.name.toLowerCase() - file[fn](exception.message, element.position, origin) + file.message('Could not render math with KaTeX', { + /* c8 ignore next -- verbose to test */ + ancestors: parent ? [parent, element] : [element], + cause, + place: element.position, + ruleId, + source: 'rehype-katex' + }) // KaTeX can handle `ParseError` itself, but not others. + if (ruleId === 'parseerror') { + result = katex.renderToString(value, { + ...settings, + displayMode, + strict: 'ignore', + throwOnError: false + }) + } // Generate similar markup if this is an other error. // See: . - if (exception.name !== 'ParseError') { + else { element.children = [ { type: 'element', @@ -88,13 +105,6 @@ export default function rehypeKatex(options) { ] return } - - result = katex.renderToString(value, { - ...settings, - displayMode, - strict: 'ignore', - throwOnError: false - }) } const root = fromHtmlIsomorphic(result, {fragment: true}) diff --git a/packages/rehype-katex/readme.md b/packages/rehype-katex/readme.md index 845398a..bf11238 100644 --- a/packages/rehype-katex/readme.md +++ b/packages/rehype-katex/readme.md @@ -144,11 +144,6 @@ Transform `` and `
` with Configuration (optional). All options, except for `displayMode`, are passed to [KaTeX][katex-options]. -###### `options.throwOnError` - -Throw if a KaTeX parse error occurs (`boolean`, default: `false`). -See [KaTeX options][katex-options]. - ## CSS The HTML produced by KaTeX requires CSS to render correctly. diff --git a/packages/rehype-katex/test.js b/packages/rehype-katex/test.js index 9c5f40b..f7bbb08 100644 --- a/packages/rehype-katex/test.js +++ b/packages/rehype-katex/test.js @@ -142,15 +142,15 @@ test('rehype-katex', async function (t) { ) }) - await t.test('should support `errorColor`', async function () { + await t.test('should handle errors, support `errorColor`', async function () { + const file = await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeKatex, {errorColor: 'orange'}) + .use(rehypeStringify) + .process('\\alpa') + assert.deepEqual( - String( - await unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex, {errorColor: 'orange'}) - .use(rehypeStringify) - .process('\\alpa') - ), + String(file), String( await unified() .use(rehypeParse, {fragment: true}) @@ -165,41 +165,37 @@ test('rehype-katex', async function (t) { ) ) ) - }) - - await t.test('should create a message for errors', async function () { - const file = await unified() - .use(rehypeParse, {fragment: true}) - .use(rehypeKatex) - .use(rehypeStringify) - .process('

Lorem

\n

\\alpa

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

Lorem

\n

\\alpa

' - ) - /* c8 ignore next 2 -- some c8 bug. */ - assert.fail() - } catch (error) { - assert.match( - String(error), - /KaTeX parse error: Undefined control sequence: \\alpa at position 1: \\̲a̲l̲p̲a̲/ - ) + assert.equal(file.messages.length, 1) + const message = file.messages[0] + assert(message) + assert(message.cause) + assert(message.ancestors) + assert.match( + String(message.cause), + /KaTeX parse error: Undefined control sequence/ + ) + assert.equal(message.ancestors.length, 2) + assert.deepEqual( + {...file.messages[0], cause: undefined, ancestors: []}, + { + ancestors: [], + cause: undefined, + column: 1, + fatal: false, + line: 1, + message: 'Could not render math with KaTeX', + name: '1:1-1:39', + place: { + start: {column: 1, line: 1, offset: 0}, + end: {column: 39, line: 1, offset: 38} + }, + reason: 'Could not render math with KaTeX', + ruleId: 'parseerror', + source: 'rehype-katex' } - } - ) + ) + }) await t.test('should support `strict: ignore`', async function () { assert.deepEqual( diff --git a/tsconfig.json b/tsconfig.json index 617fe63..4ade2fc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,7 @@ "declaration": true, "emitDeclarationOnly": true, "exactOptionalPropertyTypes": true, - "lib": ["es2020"], + "lib": ["es2022"], "module": "node16", // To do: remove when `mathjax-full` types are fixed. "skipLibCheck": true,