Skip to main content

ディレクトリ階層の表示用に List を使いたい

やりたいこと/方針

  • やりたいこと: ファイル/フォルダの階層リストを Markdown 上で表示したい
  • 方針: リストの bullet を消す カスタムディレクティブだけ作って、ファイル/フォルダのアイコンは絵文字(📝/📁)で表現

Markdown 上で このように書いたら、

md
:::no-bullets
- 📁 src/
- 📁 components/
- 📄 Header.tsx
- 📄 Footer.tsx
- 📄 index.ts
:::

こう出るということ

  • 📁 src/
    • 📁 components/
      • 📄 Header.tsx
      • 📄 Footer.tsx
    • 📄 index.ts

::: ... ::: という記法、tips, info 等での使用例しか知らなかったから、どうして「カスタムディレクティブ」という呼び名かと思ったら、こうやってカスタマイズした記法を定義するための仕組みだから、ということね。

検討時の経緯:ある日の脳内会議
  • 👴 : ファイル名/フォルダ名を list に書いたら、bullet のところ
  • 🤖1 : できなくはないんだけど、、
  • 🤖2 : カスタマイズ最小の方針にしているのでは?
  • 🤖3 : ファイルかどうかは自動判別させないで、明示的に差し込んだ方が可読性が良いかと。特に前提知識ない人には...
  • 🤖2 : だいたい作り込んだら、またバグるでしょう。で自動判別されなくてイライラするのが目に浮かぶよ。
  • 🤖2 : ちょっと考えたら Makefile とか .git とか引っかかるし。ちゃんとパターン洗い出したの?
  • 🤖1 : あと、<li> タグの bullet は同じ階層で別のものにできないから、bullet は消すだけかな
  • 🤖1 : これで我慢なさい(bullet消すだけディレクティブ)
  • 👴 : うぐぅ...

...

  • 🤖??? : UI設計と一緒です。削ぎ落とした先に本当に必要なものがあるのです

...

  • 👴 : 落ち着いて考えたら CSS で完結しない?
  • 🤖2 : それしたら、通常のリストまで bullet 無しになってしまうでしょうよ。思いつきで発言しないでくれる?
  • 👴 : うぐぅ...

実装

  • plugin に Custom Directive の interface を作成し
  • docusaurus.config.ts から読み込み
  • css に「bulletなしリスト」クラスを追加 (Custom Directive で生成された HTMLで指定しているクラス)

plugin

src/plugins/remark-no-bullets.ts
import { Plugin } from 'unified'
import { visit } from 'unist-util-visit'
import { Node } from 'unist'

interface DirectiveNode extends Node {
name: string
type: 'containerDirective'
children: Node[]
}

interface ListNode extends Node {
type: 'list'
data?: {
hProperties?: Record<string, unknown>
}
}

const remarkNoBullets: Plugin = () => {
return (tree) => {
visit(tree, 'containerDirective', (node: DirectiveNode) => {
if (node.name !== 'no-bullets') return

// node の data を削除(<div>化しない)
delete node['data']

// 中の最初の list ノードに class を付与
const targetList = node.children.find((child): child is ListNode => child.type === 'list')
if (targetList) {
targetList.data ??= {}
targetList.data.hProperties ??= {}
targetList.data.hProperties.className = ['no-bullets']
}
})
}
}

export default remarkNoBullets

docusurus.config.ts

ts
import remarkNoBullets from './src/plugins/remark-no-bullets'
...
export default {
// ...
presets: [
[
'classic',
{
docs: {
remarkPlugins: [
remarkNoBullets, // ← ★ これを追加
],
},
},
],
],
}

css

custom.css
ul.no-bullets, ul.no-bullets li {
list-style: none;
padding-left: 0.2em;
}