From 63ef94ac2d0beed96a38c0e211dc11456f85cb16 Mon Sep 17 00:00:00 2001 From: Yuki Hattori Date: Wed, 14 Jul 2021 21:52:15 +0900 Subject: [PATCH 1/7] Add math global directive to choose preferred lib --- src/math/math.ts | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/math/math.ts b/src/math/math.ts index d4f18a16..b44c36f1 100644 --- a/src/math/math.ts +++ b/src/math/math.ts @@ -1,21 +1,23 @@ import marpitPlugin from '@marp-team/marpit/plugin' +import { Marp } from '../marp' import { getMathContext, setMathContext } from './context' import * as katex from './katex' import * as mathjax from './mathjax' +export type MathPreferredLibrary = 'katex' | 'mathjax' + export interface MathOptionsInterface { - lib?: 'katex' | 'mathjax' + lib?: MathPreferredLibrary katexOption?: Record katexFontPath?: string | false } -export type MathOptions = - | boolean - | MathOptionsInterface['lib'] - | MathOptionsInterface +export type MathOptions = boolean | MathPreferredLibrary | MathOptionsInterface export const markdown = marpitPlugin((md) => { - const opts: MathOptions | undefined = md.marpit.options.math + const marp: Marp = md.marpit + const opts: MathOptions | undefined = marp.options.math + if (!opts) return const parsedOpts = @@ -23,6 +25,14 @@ export const markdown = marpitPlugin((md) => { ? { lib: typeof opts === 'string' ? opts : undefined } : opts + // Define `math` global directive to choose preferred library + Object.defineProperty(marp.customDirectives.global, 'math', { + value: (math: unknown): { math?: MathPreferredLibrary } => { + if (math === 'katex' || math === 'mathjax') return { math } + return {} + }, + }) + // Initialize const { parse, parseInline } = md @@ -86,13 +96,20 @@ export const markdown = marpitPlugin((md) => { ) // Renderer - if (parsedOpts.lib === 'mathjax') { - md.renderer.rules.marp_math_inline = mathjax.inline(md.marpit) - md.renderer.rules.marp_math_block = mathjax.block(md.marpit) - } else { - md.renderer.rules.marp_math_inline = katex.inline(md.marpit) - md.renderer.rules.marp_math_block = katex.block(md.marpit) + const getPreferredLibrary = () => { + const prefferedByDirective: MathPreferredLibrary | undefined = (marp as any) + .lastGlobalDirectives.math + + // TODO: Change the default math library from `katex` to `mathjax` in the next major version + const preferredLibrary = prefferedByDirective ?? parsedOpts.lib ?? 'katex' + return preferredLibrary === 'mathjax' ? mathjax : katex } + + const getRenderer = (type: 'inline' | 'block') => (tokens: any, idx: any) => + getPreferredLibrary()[type](md.marpit)(tokens, idx) + + md.renderer.rules.marp_math_inline = getRenderer('inline') + md.renderer.rules.marp_math_block = getRenderer('block') }) export const css = (marpit: any): string | null => { @@ -104,6 +121,8 @@ export const css = (marpit: any): string | null => { return katex.css(options.katexFontPath) } +// --- + function isValidDelim(state, pos = state.pos) { const ret = { openable: true, closable: true } const { posMax, src } = state From 6a91f3807e45927cbc20e7351522ae8a95e94faa Mon Sep 17 00:00:00 2001 From: Yuki Hattori Date: Fri, 16 Jul 2021 22:34:30 +0900 Subject: [PATCH 2/7] Disable fitting if switched math library from katex to mathjax --- src/fitting/fitting.ts | 15 ++++++++++----- src/math/math.ts | 40 +++++++++++++++++++++++++++++----------- test/marp.ts | 24 ++++++++++++++++++++++++ test/math/math.ts | 11 +++++++++++ 4 files changed, 74 insertions(+), 16 deletions(-) diff --git a/src/fitting/fitting.ts b/src/fitting/fitting.ts index f3ec14d7..f6598c71 100644 --- a/src/fitting/fitting.ts +++ b/src/fitting/fitting.ts @@ -1,5 +1,6 @@ import marpitPlugin from '@marp-team/marpit/plugin' import { Marp } from '../marp' +import { getMathContext } from '../math/context' import { attr, code, math, svgContentAttr, svgContentWrapAttr } from './data' import fittingCSS from './fitting.scss' @@ -98,15 +99,19 @@ function fittingHeader(md): void { : '' } -function fittingMathBlock(md): void { +function fittingKaTeXMathBlock(md): void { const { marp_math_block } = md.renderer.rules - if (!marp_math_block || marp_math_block.scaled) return + if (!marp_math_block) return md.renderer.rules.marp_math_block = (...args) => { - // Rendered math block is wrapped by `

` tag in math plugin + // Rendered math block is wrapped by `

` tag in KaTeX const rendered: string = marp_math_block(...args) - if (isEnabledAutoScaling(md.marpit, 'math')) { + const { + options: { lib }, + } = getMathContext(md.marpit) + + if (lib === 'katex' && isEnabledAutoScaling(md.marpit, 'math')) { const katex = rendered.slice(3, -4) if (md.marpit.options.inlineSVG) { @@ -125,5 +130,5 @@ function fittingMathBlock(md): void { } export const markdown = marpitPlugin((md) => { - md.use(fittingHeader).use(fittingCode).use(fittingMathBlock) + md.use(fittingHeader).use(fittingCode).use(fittingKaTeXMathBlock) }) diff --git a/src/math/math.ts b/src/math/math.ts index b44c36f1..75053ed8 100644 --- a/src/math/math.ts +++ b/src/math/math.ts @@ -37,9 +37,9 @@ export const markdown = marpitPlugin((md) => { const { parse, parseInline } = md const initializeMathContext = () => { - if (getMathContext(md.marpit).processing) return false + if (getMathContext(marp).processing) return false - setMathContext(md.marpit, () => ({ + setMathContext(marp, () => ({ enabled: false, options: parsedOpts, processing: true, @@ -62,7 +62,7 @@ export const markdown = marpitPlugin((md) => { return func.apply(this, args) } finally { if (initialized) { - setMathContext(md.marpit, (ctx) => ({ ...ctx, processing: false })) + setMathContext(marp, (ctx) => ({ ...ctx, processing: false })) } } } @@ -72,7 +72,7 @@ export const markdown = marpitPlugin((md) => { md.parseInline = parseWithMath(parseInline) const enableMath = () => - setMathContext(md.marpit, (ctx) => ({ ...ctx, enabled: true })) + setMathContext(marp, (ctx) => ({ ...ctx, enabled: true })) // Inline md.inline.ruler.after('escape', 'marp_math_inline', (state, silent) => { @@ -96,17 +96,35 @@ export const markdown = marpitPlugin((md) => { ) // Renderer - const getPreferredLibrary = () => { - const prefferedByDirective: MathPreferredLibrary | undefined = (marp as any) - .lastGlobalDirectives.math + md.core.ruler.after( + 'marpit_directives_global_parse', + 'marp_math_directive', + () => { + const { enabled } = getMathContext(marp) + if (!enabled) return + + const preffered: MathPreferredLibrary | undefined = (marp as any) + .lastGlobalDirectives.math + + setMathContext(marp, (ctx) => ({ + ...ctx, + options: { + ...ctx.options, + + // TODO: Change the default math library from `katex` to `mathjax` in the next major version + lib: preffered ?? parsedOpts.lib ?? 'katex', + }, + })) + } + ) - // TODO: Change the default math library from `katex` to `mathjax` in the next major version - const preferredLibrary = prefferedByDirective ?? parsedOpts.lib ?? 'katex' - return preferredLibrary === 'mathjax' ? mathjax : katex + const getPreferredLibrary = () => { + const { options } = getMathContext(marp) + return options.lib === 'mathjax' ? mathjax : katex } const getRenderer = (type: 'inline' | 'block') => (tokens: any, idx: any) => - getPreferredLibrary()[type](md.marpit)(tokens, idx) + getPreferredLibrary()[type](marp)(tokens, idx) md.renderer.rules.marp_math_inline = getRenderer('inline') md.renderer.rules.marp_math_block = getRenderer('block') diff --git a/test/marp.ts b/test/marp.ts index 74a5ce14..8e70f233 100644 --- a/test/marp.ts +++ b/test/marp.ts @@ -502,6 +502,30 @@ describe('Marp', () => { expect(notDefined).not.toBe(plain) }) + describe('math global directive', () => { + it('allows to switch rendering library from katex to mathjax', () => { + const instance = marp({ math: 'katex' }) + const { html } = instance.render( + `\n\n${inline}\n\n${block}` + ) + const $ = cheerio.load(html) + + expect($('.MathJax')).toHaveLength(2) + expect($('.katex')).not.toHaveLength(2) + }) + + it('allows to switch rendering library from mathjax to katex', () => { + const instance = marp({ math: 'mathjax' }) + const { html } = instance.render( + `\n\n${inline}\n\n${block}` + ) + const $ = cheerio.load(html) + + expect($('.MathJax')).not.toHaveLength(2) + expect($('.katex')).toHaveLength(2) + }) + }) + describe('when math typesetting syntax is not using', () => { it('does not inject MathJax css', () => expect( diff --git a/test/math/math.ts b/test/math/math.ts index 8a488989..d8a5217c 100644 --- a/test/math/math.ts +++ b/test/math/math.ts @@ -14,6 +14,17 @@ describe('markdown-it math plugin', () => { const md = new MarkdownIt() md.marpit = { options: { math: true } } + + // Mock Marpit instance + md.use(() => { + Object.assign(md.marpit, { + customDirectives: { global: {} }, + lastGlobalDirectives: {}, + }) + md.core.ruler.push('marpit_directives_global_parse', () => { + // no ops + }) + }) md.use(mathPlugin) it('renders simple inline math', () => { From 19302eb4d1ebab7de306d8f5618176cd81dfdb9c Mon Sep 17 00:00:00 2001 From: Yuki Hattori Date: Fri, 16 Jul 2021 22:37:27 +0900 Subject: [PATCH 3/7] Test rendered CSS in math plugin --- test/marp.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/marp.ts b/test/marp.ts index 8e70f233..cec6dfe7 100644 --- a/test/marp.ts +++ b/test/marp.ts @@ -505,24 +505,30 @@ describe('Marp', () => { describe('math global directive', () => { it('allows to switch rendering library from katex to mathjax', () => { const instance = marp({ math: 'katex' }) - const { html } = instance.render( + const { html, css } = instance.render( `\n\n${inline}\n\n${block}` ) const $ = cheerio.load(html) expect($('.MathJax')).toHaveLength(2) expect($('.katex')).not.toHaveLength(2) + + expect(css).toContain('mjx-container') + expect(css).not.toContain('.katex') }) it('allows to switch rendering library from mathjax to katex', () => { const instance = marp({ math: 'mathjax' }) - const { html } = instance.render( + const { html, css } = instance.render( `\n\n${inline}\n\n${block}` ) const $ = cheerio.load(html) expect($('.MathJax')).not.toHaveLength(2) expect($('.katex')).toHaveLength(2) + + expect(css).not.toContain('mjx-container') + expect(css).toContain('.katex') }) }) From 44b7a89f1169be6ba1637e2cac46b4e63241a85a Mon Sep 17 00:00:00 2001 From: Yuki Hattori Date: Sat, 17 Jul 2021 13:49:24 +0900 Subject: [PATCH 4/7] Add test case of math global directive with unknown keyword --- test/marp.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/marp.ts b/test/marp.ts index cec6dfe7..d4fcfb34 100644 --- a/test/marp.ts +++ b/test/marp.ts @@ -530,6 +530,23 @@ describe('Marp', () => { expect(css).not.toContain('mjx-container') expect(css).toContain('.katex') }) + + it('ignores if defined unknown keyword', () => { + const katex = marp({ math: 'katex' }) + + for (const keyword of ['unknown', 'false', 'true']) { + const katexRendered = katex.render( + `\n\n${inline}` + ) + const $katex = cheerio.load(katexRendered.html) + + expect($katex('.MathJax')).not.toHaveLength(1) + expect($katex('.katex')).toHaveLength(1) + + expect(katexRendered.css).not.toContain('mjx-container') + expect(katexRendered.css).toContain('.katex') + } + }) }) describe('when math typesetting syntax is not using', () => { From 9afe8298b19a72cc4deaa57fb2a85a44928c87f0 Mon Sep 17 00:00:00 2001 From: Yuki Hattori Date: Mon, 19 Jul 2021 00:12:35 +0900 Subject: [PATCH 5/7] Update README.md --- README.md | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9453fedd..e6499f65 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,32 @@ $$ You can choose a library for math from [KaTeX](https://khan.github.io/KaTeX/) and [MathJax](https://www.mathjax.org/) in [the constructor option](#constructor-options). By default, we prefer KaTeX for compatibility and performance, but MathJax has better syntax support than KaTeX. +#### `math` global directive + +Through `math` global directive, Marp Core is supporting to declare math library that will be used within current Markdown. + +Set **`katex`** or **`mathjax`** in the `math` global directive like this: + +```markdown +--- +# Declare to use MathJax in this Markdown +math: mathjax +--- + +$$ +\begin{align} +x &= 1+1 \tag{1} \\ + &= 2 +\end{align} +$$ +``` + +If not declared, Marp Core will use the default library to render math (KaTeX in v2). + +We may change the default in the future and would break existing slides, so recommend to declare the library whenever to use math typesetting. + +> :warning: The declaration of math library is given priority over [`math` JS constructor option](#math-constructor-option), but you cannot turn on again via `math` global directive if disabled math typesetting by the constructor. + ### Auto-scaling features Auto-scaling is available only if enabled [Marpit's `inlineSVG` mode](https://github.com/marp-team/marpit#inline-svg-slide-experimental) and defined [`@auto-scaling` metadata][metadata] in an using theme CSS. @@ -253,9 +279,11 @@ Setting about emoji conversions. > **For developers:** When you setting `unicode` option as `true`, Markdown parser will convert Unicode emoji into tokens internally. The rendering result is same as in `false`. -### `math`: _`boolean` | `"katex"` | `"mathjax"` | `object`_ +### `math`: _`boolean` | `"katex"` | `"mathjax"` | `object`_ + +Enable or disable [math typesetting](#math-typesetting) syntax and [`math` global directive](#math-global-directive). -Enable or disable [math typesetting](#math-typesetting) syntax. You can choose a library for math by passing **`katex`** (default) or **`mathjax`**, and modify more settings by passing an object of sub-options. +You can choose the default library for math by passing **`"katex"`** (default) or **`"mathjax"`**, and modify more settings by passing an object of sub-options. - **`lib`**: _`"katex"` | `"mathjax"`_ - Choose a library for math typesetting. _(`katex` by default)_ From c860e2cf0bb854ce84e92ca096c874c508a2ef51 Mon Sep 17 00:00:00 2001 From: Yuki Hattori Date: Mon, 19 Jul 2021 00:19:21 +0900 Subject: [PATCH 6/7] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e6499f65..e2c9f978 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ $$ -You can choose a library for math from [KaTeX](https://khan.github.io/KaTeX/) and [MathJax](https://www.mathjax.org/) in [the constructor option](#constructor-options). By default, we prefer KaTeX for compatibility and performance, but MathJax has better syntax support than KaTeX. +You can choose using library for math from [KaTeX](https://khan.github.io/KaTeX/) and [MathJax](https://www.mathjax.org/) in [`math` global directive](#math-global-directive) (or [JS constructor option](#math-constructor-option)). By default, we prefer KaTeX for compatibility and performance, but MathJax has better rendering and syntax support than KaTeX. #### `math` global directive @@ -286,13 +286,13 @@ Enable or disable [math typesetting](#math-typesetting) syntax and [`math` globa You can choose the default library for math by passing **`"katex"`** (default) or **`"mathjax"`**, and modify more settings by passing an object of sub-options. - **`lib`**: _`"katex"` | `"mathjax"`_ - - Choose a library for math typesetting. _(`katex` by default)_ + - Choose the default library for math typesetting. _(`katex` by default)_ * **`katexOption`**: _`object`_ - - The options passing to KaTeX. Please refer to [KaTeX document](https://khan.github.io/KaTeX/docs/options.html). + - Options that will be passed to KaTeX. Please refer to [KaTeX document](https://khan.github.io/KaTeX/docs/options.html). - **`katexFontPath`**: _`string` | `false`_ - - By default, marp-core will use [online web-font resources through jsDelivr CDN](https://cdn.jsdelivr.net/npm/katex@latest/dist/fonts/). You have to set path to fonts directory if you want to use local resources. If you set `false`, we will not manipulate the path (Use KaTeX's original path: `fonts/KaTeX_***-***.woff2`). + - By default, Marp Core will use [online web-font resources through jsDelivr CDN](https://cdn.jsdelivr.net/npm/katex@latest/dist/fonts/). You have to set path to fonts directory if you want to use local resources. If you set `false`, we will not manipulate the path (Use KaTeX's original path: `fonts/KaTeX_***-***.woff2`). ### `minifyCSS`: _`boolean`_ From 91796dd311838bbb41ac83700f79db3b852473d8 Mon Sep 17 00:00:00 2001 From: Yuki Hattori Date: Mon, 19 Jul 2021 00:25:49 +0900 Subject: [PATCH 7/7] [ci skip] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index da2e1d98..1ff15aec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Added + +- `math` global directive for switching math typesetting library in current Markdown ([#243](https://github.com/marp-team/marp-core/issues/243), [#246](https://github.com/marp-team/marp-core/pull/246)) + ### Changed - Upgrade dependent packages to the latest version ([#241](https://github.com/marp-team/marp-core/pull/241))