## 背景 [[Awesome Nav for MkDocs]]は[[MkDocs]]の[[ナビゲーション (MkDocs)|ナビゲーション]]を[[YAML]]で定義することができるプラグイン。 `.nav.yml` ```yaml nav: - hoge.md - dir: - huga.md - エイリアス: title.md ``` ただ、[[Obsidian]]で管理する場合は[[Markdown]]の[[内部リンク]]箇条書きで表現できると嬉しいことがある。 `nav.md` ```markdown nav: - [[hoge]] - dir: - [[huga]] - [[title|エイリアス]] ``` 具体的なメリット。 - デッドリンクにすぐ気付ける - [[MkDocs]]のビルドよりも早く気付ける - 参照しているノートのタイトルが変わったときに変更が不要 - 自動で連動更新されるから - [[Obsidian]]からもナビゲーションメニューを見て関連ノートにジャンプしやすい - [[🦉Another Quick Switcher]] の [[Outgoing link search]] が便利 - [[Live Preview]]での表示が直感的 ## 環境 | 対象 | バージョン | | -------------------------- | ----- | | [[MkDocs]] | 1.6.1 | | [[Awesome Nav for MkDocs]] | 3.2.0 | | [[mkdocs-gen-files]] | 0.5.0 | ## やってみる (非推奨な方法) [[Awesome Nav for MkDocs]]では対応していないため、[[hooks (MkDocs)|hooks]]を使って以下の処理を実装する。 1. `nav.md` を読み込む 2. 1を[[Awesome Nav for MkDocs]]が対応している[[YAML]]に変換する 3. 2を `.nav.yml` として吐き出す 4. [[Awesome Nav for MkDocs]] に `.nav.yml` を処理させる `mkdocs.yml` に以下を追加。 ```yaml hooks: - markdown_nav.py plugins: - awesome-nav ``` `markdown_nav.py` ```python import re from pathlib import Path WIKI_LINK = re.compile(r"\[\[([^\]]+)\]\]") def transform_nav(text: str) -> str: def repl(m: re.Match) -> str: inner = m.group(1) target, alias = inner.split("|", 1) if "|" in inner else (inner, None) path = f"{target}.md" return f"{alias}: {path}" if alias else path return WIKI_LINK.sub(repl, text) def on_config(config): docs_dir = Path(config["docs_dir"]) src = docs_dir / "nav.md" dst_filename = ".nav.yml" dst = docs_dir / dst_filename dst_next_text = transform_nav(src.read_text()) dst_current_text = dst.read_text() if dst.exists() else "" # WARN: 常に書き込むと無限にon_configが呼ばれる if dst_next_text != dst_current_text: dst.write_text(dst_next_text) return config ``` これで `nav.md` を更新すると、[[Live Reloading (MkDocs)|Live Reloading]]の際に `.nav.yml` が生成される。 ただ、`.nav.yml` が生成されたときに再び[[Live Reloading (MkDocs)|Live Reloading]]が発生し、無限ループに陥るためif文でガードしている。 ```python # WARN: 常に書き込むと無限にon_configが呼ばれる if dst_next_text != dst_current_text: dst.write_text(dst_next_text) ``` ## ビルドを1回にできないか? [[mkdocs-macros-plugin]] などを使って少し時間のかかる処理をしているとき、毎回2回のビルドが発生することは望ましくない。対処方法がないかを調べる。 ### hotreloadの対象から `.nav.yml` を外せないか? [[MkDocs]]ではそういうのは認めてなさそう。主張は分からなくもないけど...。 <div class="link-card-v2"> <div class="link-card-v2-site"> <img class="link-card-v2-site-icon" src="https://github.githubassets.com/favicons/favicon.svg" /> <span class="link-card-v2-site-name">GitHub</span> </div> <div class="link-card-v2-title"> `server.watch()`: ignore files or directories in watch list ? · Issue #2247 · mkdocs/mkdocs </div> <div class="link-card-v2-content"> In the on_serve() method, I am having an unexpected functional issue with the server.watch(): in some cases wher ... </div> <img class="link-card-v2-image" src="https://opengraph.githubassets.com/f8f3a51d04c862e543a5f64a4ebab4574b047ae8c316b33d0c34fbb2cac1e0c4/mkdocs/mkdocs/issues/2247" /> <a href="https://github.com/mkdocs/mkdocs/issues/2247"></a> </div> ### 別アプローチの調査 似たような境遇の人を発見。 <div class="link-card-v2"> <div class="link-card-v2-site"> <img class="link-card-v2-site-icon" src="https://github.githubassets.com/favicons/favicon.svg" /> <span class="link-card-v2-site-name">GitHub</span> </div> <div class="link-card-v2-title"> Don't trigger rebuild for given files / directories that are being watched? · Issue #3433 · mkdocs/mkdocs </div> <div class="link-card-v2-content"> This is basically the same as #2247. However, that was closed with the argument being that mkdocs is not in cont ... </div> <img class="link-card-v2-image" src="https://opengraph.githubassets.com/aea08f1fcdc3140943cb3bb21f078244db8f8b2e15e53ed46c2e74ff48c96b1d/mkdocs/mkdocs/issues/3433" /> <a href="https://github.com/mkdocs/mkdocs/issues/3433"></a> </div> [[mkdocs-gen-files]]を使って仮想的な `.nav.yml` を生成すればいけそうな気がする。[[Awesome Nav for MkDocs]]も対応してそう。 <div class="link-card-v2"> <div class="link-card-v2-site"> <img class="link-card-v2-site-icon" src="https://github.githubassets.com/favicons/favicon.svg" /> <span class="link-card-v2-site-name">GitHub</span> </div> <div class="link-card-v2-title"> Handle plugin generated content and config by thejcannon · Pull Request #79 · lukasgeiter/mkdocs-awesome-nav </div> <div class="link-card-v2-content"> This change allows mkdocs-awesome-pages-plugin to play nicely with mkdocs-gen-files, specifically with generated ... </div> <img class="link-card-v2-image" src="https://opengraph.githubassets.com/23aee7d53423d7783dcae9670fece901b156148d1668d9d1146fa6c157bbdda2/lukasgeiter/mkdocs-awesome-nav/pull/79" /> <a href="https://github.com/lukasgeiter/mkdocs-awesome-nav/pull/79"></a> </div> ## 仮想的な `.nav.yml` を生成するアプローチ > [[ナビゲーションをMarkdownファイルで設定 (MkDocs)|ナビゲーションをMarkdownファイルで設定]] ```console pip install mkdocs-gen-files ``` `mkdocs.yml` に追加。 ```yaml plugins: - gen-files: scripts: - gen_nav.py ``` `gen_nav.py` を作成。 ```python import re from pathlib import Path import mkdocs_gen_files WIKI_LINK = re.compile(r"\[\[([^\]]+)\]\]") def transform_nav(text: str) -> str: def repl(m: re.Match) -> str: inner = m.group(1) target, alias = inner.split("|", 1) if "|" in inner else (inner, None) path = f"{target}.md" return f"{alias}: {path}" if alias else path return WIKI_LINK.sub(repl, text) def main(): src = Path(mkdocs_gen_files.config["docs_dir"]) / "nav.md" dst_filename = ".nav.yml" with mkdocs_gen_files.open(dst_filename, "w") as f: print(transform_nav(src.read_text()), file=f) main() ``` これで `docs/nav.md` を変更すれば、変更がナビゲーションメニューに反映される。 ## まとめ [[#仮想的な `.nav.yml` を生成するアプローチ]] だけを実施すればOK。