はみ出るMarkdownのテーブルを改善!Rehypeプラグイン実装

はみ出るMarkdownのテーブルを改善!Rehypeプラグイン実装
記事内に商品プロモーションを含む場合があります

Astro 産のブログで MySQL の実行計画の表を書いていたときの話…

テーブルを使いたく Markdown でいつも通りテーブルを書き、実行計画の表を完成させました

実行計画

| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 1 | SIMPLE | question |  | ref | PRIMARY,user_id,user_id_created | user_id_created | 5 | const | 3 | 100 | Backward index scan; Using index |
| 1 | SIMPLE | answers |  | ref | question_id | question_id | 5 | question.id | 3 | 100 |  |

しかし、完成した表を見てみるとテーブル要素が親要素よりも大きくなってしまったことで UI が崩れてしまっていました…

テーブルが親要素からはみ出ている画像

本記事ではそんな課題を解決するために Rehype プラグインを作成しました

Rehypeとは

Rehype とは HTMLを unist を拡張した hast と呼ばれる抽象構文木として扱うプラグインのエコシステムのことです

unist や hast などについて詳しくは別記事で触れているのでそちらをご覧ください!

課題にどう対応するか

Astro では unified で Markdown から HTML への変換を実現しています

先ほど記事の上部で書いた以下の Markdown 記法を用いると…

実行計画

| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 1 | SIMPLE | question |  | ref | PRIMARY,user_id,user_id_created | user_id_created | 5 | const | 3 | 100 | Backward index scan; Using index |
| 1 | SIMPLE | answers |  | ref | question_id | question_id | 5 | question.id | 3 | 100 |  |

以下のような HTML に変換されて表示されます


<table>
  <thead>
    <tr>
      <th>id</th>
      <th>select_type</th>
      <th>table</th>
      <th>partitions</th>
      <th>type</th>
      <th>possible_keys</th>
      <th>key</th>
      <th>key_len</th>
      <th>ref</th>
      <th>rows</th>
      <th>filtered</th>
      <th>Extra</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>SIMPLE</td>
      <td>question</td>
      <td></td>
      <td>ref</td>
      <td>PRIMARY,user_id,user_id_created</td>
      ...
    </tr>
  </tbody>
</table>

本来はこの table タグの親要素に overflow-x: auto; のスタイルを当てるだけでスクロールできるようになります

そしてブログの場合はこの table タグの親要素は article タグになることが多いと思います

しかし、その場合 article タグ内のコンテンツ全体がスクロール可能になってしまうため、親要素にスタイルを当てることができません

その課題に対応するために、今回は table タグを overflow-x: auto; のスタイルが当たった親タグで囲う Rehype プラグインを作成しました

Rehypeプラグインを実装する

実行計画

| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 1 | SIMPLE | question |  | ref | PRIMARY,user_id,user_id_created | user_id_created | 5 | const | 3 | 100 | Backward index scan; Using index |
| 1 | SIMPLE | answers |  | ref | question_id | question_id | 5 | question.id | 3 | 100 |  |

上記のような Markdown が mdast に変換され mdast が hast に変換されることで、以下のような抽象構文木として表現されます


{
  type: 'element',
  tagName: 'table',
  properties: {},
  children: [
    {
      type: 'element',
      tagName: 'thead',
      properties: {},
      children: [Array],
      position: [Object]
    },
    {
      type: 'element',
      tagName: 'tbody',
      properties: {},
      children: [Array],
      position: [Object]
    }
  ],
  ...
}

今回作成したプラグインは、上記の hast が HTML に変換される前に overflow-x: auto; のスタイルが当たったdivタグを親要素として追加することで、table タグをスクローラブルにしています

プラグインの実装自体は以下のようになっています

src/plugins/rehypeTableWrap.js

import { h } from 'hastscript'
import { visit } from 'unist-util-visit'

export function rehypeTableWrap() {
  return function (tree) {
    visit(tree, 'element', (node, index, parent) => {
      if (node.tagName === 'table') {
        const wrapper = h('div', { class: 'table-wrapper' }, [node])
        parent.children[index] = wrapper
      }
    })
  }
}
component.scss

.table-wrapper {
  overflow-x: auto;
}
  • hastscript は hast をコードで生成したいときに使えるパッケージです
  • unist-util-visit は unist をウォークしたい時に使うパッケージです

rehypeTableWrap プラグインでは tagName が table の hast の場合、hastscript で table-wrapper クラスが付与された div タグを生成して、その子要素として元の table タグの hast を代入し、その要素を元の要素を上書きする処理をしています

wrapper 変数は以下のような hast になっています


{
  type: 'element',
  tagName: 'div',
  properties: { className: [ 'table-wrapper' ] },
  children: [
    {
      type: 'element',
      tagName: 'table',
      properties: {},
      children: [Array],
      position: [Object]
    }
  ]
}

これを HTML に変換すると、overflow-x: auto; のスタイルが当たった div タグで元の table タグを囲ってあげることができます

Astroでプラグインを導入する

Astro でプラグインを導入するには astro.config.mjs でプラグインの読み込みを行う必要があります

今回作成したのは Rehype のプラグインなので markdown.rehypePlugins オプションにプラグインを指定してあげたら導入は完了です

astro.config.mjs

import { rehypeTableWrap } from './src/plugins/rehypeTableWrap'

export default defineConfig({
  markdown: {
    rehypePlugins: [
      rehypeTableWrap,
    ],
  },
});

あとは自分好みでスタイルを当てたらスクロールできるテーブルの完成です!

テーブルがスクロールできるようになった画像

Recommend
こんな記事も読まれています!