vivliostyle / vfm

⬇️ Open and extendable Markdown syntax and toolchain.
https://vivliostyle.github.io/vfm/#/vfm
Other
69 stars 12 forks source link

spec: math をデフォルト有効とする #93

Closed akabekobeko closed 3 years ago

akabekobeko commented 3 years ago

Goals

37 対応により数式以外で $ が含まれる文があっても $5 などには反応しなくなったので math をデフォルト有効にしてもよいのでは?と #30 にあった。

@akabekobeko @spring-raining (https://github.com/vivliostyle/vivliostyle-cli/pull/182#issuecomment-829985914 に関連して)

mathをデフォルトで有効にするほうがよいのでないかと思いはじめています。

mathをデフォルトで無効にしたのは $...$ が意図せず数式として扱われてしまう問題があるだろうということでしたが、pandoc と同様の $...$ を数式扱いする条件が実装されたので、その問題はだいぶ解消しています。

ただし数式が使われていないHTMLでもMathJaxがロードされたり、その処理がされるのは効率的ではないので、実際にMarkdownに $...$ または $$...$$ の数式が出現した場合にかぎり <script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.9/MathJax.js?config=TeX-MML-AM_CHTML"></script>data-math-typeset="true" を出力するのがよいと思います。

これを踏まえて対応を検討する。

Prior Art

なし

Discussion

30 に書いた私の見解を転載。

ざっと調べたメモ。

インターフェース変更して VFile を受け渡せるか、それともグローバル変数などの代替手段にするかを継続調査 & 検討する。

akabekobeko commented 3 years ago

調査メモ。

akabekobeko commented 3 years ago

math.ts スコープの変数で解決を試みる。

  1. let MATH_PROCESSED = false を定義
  2. 冪等性のために mdast 冒頭で必ず MATH_PROCESSED = false を設定
  3. handlerinlineMath または displayMath が処理されたら MATH_PROCESSED = true にする、冗長になるが true にしかならないので許容する
  4. hastMATH_PROCESSED = true の場合のみタグ処理をして MATH_PROCESSED = false を設定
    • handlerhast はセット想定なので、これらだけ実行されても矛盾しないよう念の為に初期値へ戻す
    • mdast もセットと考えるなら不要ではあるが
akabekobeko commented 3 years ago

ファイル スコープ変数で実際に数式を処理した場合のみ <script><body> 属性の出力実行を切り替えられたので、ドキュメント修正なども含めて PR を出す。

akabekobeko commented 3 years ago

デフォルトが有効となったため VFM の CLI オプションを --math から --disable-math に変更しなければならない。

akabekobeko commented 3 years ago

CLI オプションを変更して npm linknpm pack で実験してみたが、なぜかビルドされたものは --disable-math とならず古い --math のままである。cli.ts の変更が反映されていない。これが解決できないとリリースできないので継続調査する。

akabekobeko commented 3 years ago

VFM のプロジェクトは npm-scripts に prepare を指定しないため npm linknpm pack だけではビルドされず、事前に npm build を実行しなくてはならない。release-it 経由のリリースだと設定は見当たらないがこれを実行してくれるようで、問題となっていなかったようだ。前も CLI のテストで同じ問題を踏んだ気がする。

npm build 後に npm link して $x = y$ だけ記述した sample.md を用意して

$ vfm ./sample.md
<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.9/MathJax.js?config=TeX-MML-AM_CHTML"></script>
  </head>
  <body data-math-typeset="true">
    <p><span class="math inline">\(x = y\)</span></p>
  </body>
</html>

$ vfm ./sample.md --disable-math
<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>
    <p>$x = y$</p>
  </body>
</html>

をそれぞれ確認した。また数式のないものについて

$ vfm ./sample.md 
<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>
    <p>sample</p>
  </body>
</html>

のように既定で数式有効でも <script><body> の属性が出力されないことを確認した。

CLI オプション変更を確認したのでドキュメント整備してから PR する。

akabekobeko commented 3 years ago

94 に新たな要望あり。以下、転載。

@akabekobeko すみません、body 要素に data-math-typeset="true" を指定するのは間違いだったことに気がつきました。 これは数式を直接囲む要素に指定しなくてはならないものでした。

TeX数式を含むHTMLサンプル https://github.com/jagat-xpub/cosmology の index.html の body 要素に data-math-typeset="true" を指定して、Vivliostyle Viewer で組版結果を確認すると、ページの組版処理が正常に行われなくなってしまいました。(改ページされず1ページ目でページがオーバーフローする、など)

data-math-typeset="true" をbody 要素にではなく、数式を囲む span 要素に出力するように変更をお願いします。例:

<span class="math inline" data-math-typeset="true">\(...\)</span>
<span class="math display" data-math-typeset="true">$$...$$</span>

もうひとつお願いです。 MathML 数式がある場合にも <script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.9/MathJax.js?config=TeX-MML-AM_CHTML"></script> の出力が必要です。

MathML 数式が使われているかどうかは <math> タグの有無で判定できるかと思います。例:

- MathML: <math><mi>x</mi><mo>=</mo><mi>y</mi></math>.

AsciiMath 対応については、今回は見合わせて、またの検討でよいかと思います。

akabekobeko commented 3 years ago

data-math-typeset="true" の以降は実装して PR に含めた。<math> については以下の課題あり。

  1. VFM として math: false の場合でも処理するのか?
  2. <math> タグの検出方法

1 は #94 にて村上さんに問い合わせ中。2 についてまずは rehype の handler で math をフックできるか試したがダメだった。hast で visit によりハンドリングする場合、<script> 出力用の head 検出とどのように組み合わせるか検討する必要あり。visit が非同期ならば同期しなければならない。hast 的に async/await 使えるのだろうか。などを調べる。

akabekobeko commented 3 years ago

syntax-tree/hast-util-select で同期的にタグ検索できる。ただし最新版の v5.0.0 は Node.js v12 が下限となったことを踏まえてかネイティブ ESM となっているため Jest が解析できない。

 FAIL  tests/math.test.ts
  ● Test suite failed to run

    Jest encountered an unexpected token

    This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.

    By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

    Here's what you can do:
     • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/en/ecmascript-modules for how to enable it.
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/en/configuration.html

これを回避するため直前の v4.0.2 を入れて math.tshast にて

const mathTag = select('math', tree);
console.log(mathTag);

を実行したら検出できた。なお matchtree ではなく node 対象なので select を利用しなければならない。この関数には querySelector と対応しており同様のセレクターを指定可能。selectAllquerySelectorAll に対応している。

akabekobeko commented 3 years ago

94 に村上さんから返信あり。要約すると <math> 検出による <script> 出力は VFM が math: true の場合のみ実行するほうがよいとのこと。math: false やそのために CLI へ --disable-math が指定された場合は明示的な数式の無効化が期待されるため処理すべきではない。

<math> 検出は math.ts の mdast や handler ではなくユーザーが Markdown へ直に書いたものを対象とするため、hast で VFM として math: true なのか判定しなければならない。設計変更が必要そう。

akabekobeko commented 3 years ago

と思ったが index.ts にて hastMathmath: true 時のみとしているため、新規の判定追加はしなくてよい。

akabekobeko commented 3 years ago

ことから inlineMathdisplayMath についても select で検索するようにすればファイル スコープ変数を無くせそう。設計的にも「状態たる変数を回避できる」のは冪等性の観点から望ましいことなので試す。

akabekobeko commented 3 years ago

以下のように判定することでファイル スコープ変数を回避できた。PR #94 へ反映済み。

if (!(select('[data-math-typeset="true"]', tree) || select('math', tree))) {
  return;
}

mdast/handler を経て生成された hast を判定するという場面は今後もありそうなので、手段として覚えておく。

akabekobeko commented 3 years ago

94 を merge して VFM 1.0.0-alpha.21 をリリースしたので close する。