From 23914b6741409ed7436cc37a960309615a68990f Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 18 Sep 2023 17:08:32 +0200 Subject: [PATCH] Refactor to move code to `lib/` --- packages/rehype-katex/index.js | 115 +----------------------- packages/rehype-katex/lib/index.js | 116 +++++++++++++++++++++++++ packages/rehype-katex/package.json | 1 + packages/rehype-mathjax/browser.js | 22 +---- packages/rehype-mathjax/chtml.js | 16 +--- packages/rehype-mathjax/index.js | 2 +- packages/rehype-mathjax/lib/browser.js | 24 +++++ packages/rehype-mathjax/lib/chtml.js | 15 ++++ packages/rehype-mathjax/lib/svg.js | 10 +++ packages/rehype-mathjax/svg.js | 11 +-- packages/remark-math/index.js | 41 +-------- packages/remark-math/lib/index.js | 42 +++++++++ 12 files changed, 216 insertions(+), 199 deletions(-) create mode 100644 packages/rehype-katex/lib/index.js create mode 100644 packages/rehype-mathjax/lib/browser.js create mode 100644 packages/rehype-mathjax/lib/chtml.js create mode 100644 packages/rehype-mathjax/lib/svg.js create mode 100644 packages/remark-math/lib/index.js diff --git a/packages/rehype-katex/index.js b/packages/rehype-katex/index.js index d60ab01..a49a278 100644 --- a/packages/rehype-katex/index.js +++ b/packages/rehype-katex/index.js @@ -1,116 +1,5 @@ /** - * @typedef {import('hast').ElementContent} ElementContent - * @typedef {import('hast').Root} Root - * - * @typedef {import('katex').KatexOptions} KatexOptions - * - * @typedef {import('vfile').VFile} VFile + * @typedef {import('./lib/index.js').Options} Options */ -/** - * @typedef {Omit} Options - */ - -import {fromHtmlIsomorphic} from 'hast-util-from-html-isomorphic' -import {toText} from 'hast-util-to-text' -import katex from 'katex' -import {visit} from 'unist-util-visit' - -/** @type {Readonly} */ -const emptyOptions = {} -/** @type {ReadonlyArray} */ -const emptyClasses = [] - -/** - * Plugin to transform `` and `
` - * with KaTeX. - * - * @param {Readonly | null | undefined} [options] - * Configuration (optional). - * @returns - * Transform. - */ -export default function rehypeKatex(options) { - const settings = options || emptyOptions - - /** - * Transform. - * - * @param {Root} tree - * Tree. - * @param {VFile} file - * File. - * @returns {undefined} - * Nothing. - */ - return function (tree, file) { - visit(tree, 'element', function (element, _, parent) { - const classes = Array.isArray(element.properties.className) - ? element.properties.className - : emptyClasses - const inline = classes.includes('math-inline') - const displayMode = classes.includes('math-display') - - if (!inline && !displayMode) { - return - } - - const value = toText(element, {whitespace: 'pre'}) - - /** @type {string} */ - let result - - try { - result = katex.renderToString(value, { - ...settings, - displayMode, - throwOnError: true - }) - } catch (error) { - const cause = /** @type {Error} */ (error) - const ruleId = cause.name.toLowerCase() - - file.message('Could not render math with KaTeX', { - /* c8 ignore next -- verbose to test */ - ancestors: parent ? [parent, element] : [element], - cause, - place: element.position, - ruleId, - source: 'rehype-katex' - }) - - // KaTeX can handle `ParseError` itself, but not others. - if (ruleId === 'parseerror') { - result = katex.renderToString(value, { - ...settings, - displayMode, - strict: 'ignore', - throwOnError: false - }) - } - // Generate similar markup if this is an other error. - // See: . - else { - element.children = [ - { - type: 'element', - tagName: 'span', - properties: { - className: ['katex-error'], - style: 'color:' + (settings.errorColor || '#cc0000'), - title: String(error) - }, - children: [{type: 'text', value}] - } - ] - return - } - } - - const root = fromHtmlIsomorphic(result, {fragment: true}) - // Cast because there will not be `doctypes` in KaTeX result. - const content = /** @type {Array} */ (root.children) - element.children = content - }) - } -} +export {default} from './lib/index.js' diff --git a/packages/rehype-katex/lib/index.js b/packages/rehype-katex/lib/index.js new file mode 100644 index 0000000..d60ab01 --- /dev/null +++ b/packages/rehype-katex/lib/index.js @@ -0,0 +1,116 @@ +/** + * @typedef {import('hast').ElementContent} ElementContent + * @typedef {import('hast').Root} Root + * + * @typedef {import('katex').KatexOptions} KatexOptions + * + * @typedef {import('vfile').VFile} VFile + */ + +/** + * @typedef {Omit} Options + */ + +import {fromHtmlIsomorphic} from 'hast-util-from-html-isomorphic' +import {toText} from 'hast-util-to-text' +import katex from 'katex' +import {visit} from 'unist-util-visit' + +/** @type {Readonly} */ +const emptyOptions = {} +/** @type {ReadonlyArray} */ +const emptyClasses = [] + +/** + * Plugin to transform `` and `
` + * with KaTeX. + * + * @param {Readonly | null | undefined} [options] + * Configuration (optional). + * @returns + * Transform. + */ +export default function rehypeKatex(options) { + const settings = options || emptyOptions + + /** + * Transform. + * + * @param {Root} tree + * Tree. + * @param {VFile} file + * File. + * @returns {undefined} + * Nothing. + */ + return function (tree, file) { + visit(tree, 'element', function (element, _, parent) { + const classes = Array.isArray(element.properties.className) + ? element.properties.className + : emptyClasses + const inline = classes.includes('math-inline') + const displayMode = classes.includes('math-display') + + if (!inline && !displayMode) { + return + } + + const value = toText(element, {whitespace: 'pre'}) + + /** @type {string} */ + let result + + try { + result = katex.renderToString(value, { + ...settings, + displayMode, + throwOnError: true + }) + } catch (error) { + const cause = /** @type {Error} */ (error) + const ruleId = cause.name.toLowerCase() + + file.message('Could not render math with KaTeX', { + /* c8 ignore next -- verbose to test */ + ancestors: parent ? [parent, element] : [element], + cause, + place: element.position, + ruleId, + source: 'rehype-katex' + }) + + // KaTeX can handle `ParseError` itself, but not others. + if (ruleId === 'parseerror') { + result = katex.renderToString(value, { + ...settings, + displayMode, + strict: 'ignore', + throwOnError: false + }) + } + // Generate similar markup if this is an other error. + // See: . + else { + element.children = [ + { + type: 'element', + tagName: 'span', + properties: { + className: ['katex-error'], + style: 'color:' + (settings.errorColor || '#cc0000'), + title: String(error) + }, + children: [{type: 'text', value}] + } + ] + return + } + } + + const root = fromHtmlIsomorphic(result, {fragment: true}) + // Cast because there will not be `doctypes` in KaTeX result. + const content = /** @type {Array} */ (root.children) + element.children = content + }) + } +} diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index d030eb7..55299a8 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -34,6 +34,7 @@ "main": "index.js", "types": "index.d.ts", "files": [ + "lib/", "index.d.ts", "index.js" ], diff --git a/packages/rehype-mathjax/browser.js b/packages/rehype-mathjax/browser.js index 6481d87..8937594 100644 --- a/packages/rehype-mathjax/browser.js +++ b/packages/rehype-mathjax/browser.js @@ -1,25 +1,5 @@ /** * @typedef {import('./lib/create-plugin.js').Options} Options - * @typedef {import('./lib/create-plugin.js').InputTexOptions} InputTexOptions */ -import {createPlugin} from './lib/create-plugin.js' - -/** @type {Readonly} */ -const emptyTexOptions = {} - -const rehypeMathJaxBrowser = createPlugin(function (options) { - const tex = options.tex || emptyTexOptions - const display = tex.displayMath || [['\\[', '\\]']] - const inline = tex.inlineMath || [['\\(', '\\)']] - - return { - render(node, options) { - const delimiters = (options.display ? display : inline)[0] - node.children.unshift({type: 'text', value: delimiters[0]}) - node.children.push({type: 'text', value: delimiters[1]}) - } - } -}) - -export default rehypeMathJaxBrowser +export {default} from './lib/browser.js' diff --git a/packages/rehype-mathjax/chtml.js b/packages/rehype-mathjax/chtml.js index 75946ad..8845600 100644 --- a/packages/rehype-mathjax/chtml.js +++ b/packages/rehype-mathjax/chtml.js @@ -2,18 +2,4 @@ * @typedef {import('./lib/create-plugin.js').Options} Options */ -import {CHTML} from 'mathjax-full/js/output/chtml.js' -import {createPlugin} from './lib/create-plugin.js' -import {createRenderer} from './lib/create-renderer.js' - -const rehypeMathJaxCHtml = createPlugin(function (options) { - if (!options.chtml || !options.chtml.fontURL) { - throw new Error( - 'rehype-mathjax: missing `fontURL` in options, which must be set to a URL to reach MathJaX fonts' - ) - } - - return createRenderer(options, new CHTML(options.chtml)) -}) - -export default rehypeMathJaxCHtml +export {default} from './lib/chtml.js' diff --git a/packages/rehype-mathjax/index.js b/packages/rehype-mathjax/index.js index 05dedc2..9c3f749 100644 --- a/packages/rehype-mathjax/index.js +++ b/packages/rehype-mathjax/index.js @@ -2,4 +2,4 @@ * @typedef {import('./lib/create-plugin.js').Options} Options */ -export {default} from './svg.js' +export {default} from './lib/svg.js' diff --git a/packages/rehype-mathjax/lib/browser.js b/packages/rehype-mathjax/lib/browser.js new file mode 100644 index 0000000..09b25ae --- /dev/null +++ b/packages/rehype-mathjax/lib/browser.js @@ -0,0 +1,24 @@ +/** + * @typedef {import('./create-plugin.js').InputTexOptions} InputTexOptions + */ + +import {createPlugin} from './create-plugin.js' + +/** @type {Readonly} */ +const emptyTexOptions = {} + +const rehypeMathJaxBrowser = createPlugin(function (options) { + const tex = options.tex || emptyTexOptions + const display = tex.displayMath || [['\\[', '\\]']] + const inline = tex.inlineMath || [['\\(', '\\)']] + + return { + render(node, options) { + const delimiters = (options.display ? display : inline)[0] + node.children.unshift({type: 'text', value: delimiters[0]}) + node.children.push({type: 'text', value: delimiters[1]}) + } + } +}) + +export default rehypeMathJaxBrowser diff --git a/packages/rehype-mathjax/lib/chtml.js b/packages/rehype-mathjax/lib/chtml.js new file mode 100644 index 0000000..75e64a1 --- /dev/null +++ b/packages/rehype-mathjax/lib/chtml.js @@ -0,0 +1,15 @@ +import {CHTML} from 'mathjax-full/js/output/chtml.js' +import {createPlugin} from './create-plugin.js' +import {createRenderer} from './create-renderer.js' + +const rehypeMathJaxCHtml = createPlugin(function (options) { + if (!options.chtml || !options.chtml.fontURL) { + throw new Error( + 'rehype-mathjax: missing `fontURL` in options, which must be set to a URL to reach MathJaX fonts' + ) + } + + return createRenderer(options, new CHTML(options.chtml)) +}) + +export default rehypeMathJaxCHtml diff --git a/packages/rehype-mathjax/lib/svg.js b/packages/rehype-mathjax/lib/svg.js new file mode 100644 index 0000000..9a95d6e --- /dev/null +++ b/packages/rehype-mathjax/lib/svg.js @@ -0,0 +1,10 @@ +import {SVG} from 'mathjax-full/js/output/svg.js' +import {createPlugin} from './create-plugin.js' +import {createRenderer} from './create-renderer.js' + +const rehypeMathJaxSvg = createPlugin(function (options) { + // MathJax types do not allow `null`. + return createRenderer(options, new SVG(options.svg || undefined)) +}) + +export default rehypeMathJaxSvg diff --git a/packages/rehype-mathjax/svg.js b/packages/rehype-mathjax/svg.js index 27e6fb2..9c3f749 100644 --- a/packages/rehype-mathjax/svg.js +++ b/packages/rehype-mathjax/svg.js @@ -2,13 +2,4 @@ * @typedef {import('./lib/create-plugin.js').Options} Options */ -import {SVG} from 'mathjax-full/js/output/svg.js' -import {createPlugin} from './lib/create-plugin.js' -import {createRenderer} from './lib/create-renderer.js' - -const rehypeMathJaxSvg = createPlugin(function (options) { - // `mathjax-types` do not allow `null`. - return createRenderer(options, new SVG(options.svg || undefined)) -}) - -export default rehypeMathJaxSvg +export {default} from './lib/svg.js' diff --git a/packages/remark-math/index.js b/packages/remark-math/index.js index a2cc9a8..a49a278 100644 --- a/packages/remark-math/index.js +++ b/packages/remark-math/index.js @@ -1,42 +1,5 @@ -/// -/// -/// - /** - * @typedef {import('mdast').Root} Root - * @typedef {import('mdast-util-math').ToOptions} Options - * @typedef {import('unified').Processor} Processor + * @typedef {import('./lib/index.js').Options} Options */ -import {mathFromMarkdown, mathToMarkdown} from 'mdast-util-math' -import {math} from 'micromark-extension-math' - -/** @type {Readonly} */ -const emptyOptions = {} - -/** - * Plugin to support math. - * - * @param {Readonly | null | undefined} [options] - * Configuration (optional). - * @returns {undefined} - * Nothing. - */ -export default function remarkMath(options) { - // @ts-expect-error: TS is wrong about `this`. - // eslint-disable-next-line unicorn/no-this-assignment - const self = /** @type {Processor} */ (this) - const settings = options || emptyOptions - const data = self.data() - - const micromarkExtensions = - data.micromarkExtensions || (data.micromarkExtensions = []) - const fromMarkdownExtensions = - data.fromMarkdownExtensions || (data.fromMarkdownExtensions = []) - const toMarkdownExtensions = - data.toMarkdownExtensions || (data.toMarkdownExtensions = []) - - micromarkExtensions.push(math(settings)) - fromMarkdownExtensions.push(mathFromMarkdown()) - toMarkdownExtensions.push(mathToMarkdown(settings)) -} +export {default} from './lib/index.js' diff --git a/packages/remark-math/lib/index.js b/packages/remark-math/lib/index.js new file mode 100644 index 0000000..a2cc9a8 --- /dev/null +++ b/packages/remark-math/lib/index.js @@ -0,0 +1,42 @@ +/// +/// +/// + +/** + * @typedef {import('mdast').Root} Root + * @typedef {import('mdast-util-math').ToOptions} Options + * @typedef {import('unified').Processor} Processor + */ + +import {mathFromMarkdown, mathToMarkdown} from 'mdast-util-math' +import {math} from 'micromark-extension-math' + +/** @type {Readonly} */ +const emptyOptions = {} + +/** + * Plugin to support math. + * + * @param {Readonly | null | undefined} [options] + * Configuration (optional). + * @returns {undefined} + * Nothing. + */ +export default function remarkMath(options) { + // @ts-expect-error: TS is wrong about `this`. + // eslint-disable-next-line unicorn/no-this-assignment + const self = /** @type {Processor} */ (this) + const settings = options || emptyOptions + const data = self.data() + + const micromarkExtensions = + data.micromarkExtensions || (data.micromarkExtensions = []) + const fromMarkdownExtensions = + data.fromMarkdownExtensions || (data.fromMarkdownExtensions = []) + const toMarkdownExtensions = + data.toMarkdownExtensions || (data.toMarkdownExtensions = []) + + micromarkExtensions.push(math(settings)) + fromMarkdownExtensions.push(mathFromMarkdown()) + toMarkdownExtensions.push(mathToMarkdown(settings)) +}