1年ぶりになります.今年もよろしくお願いします.
最近は紅茶コーディネータになるべく日々精進しています1.
TL;DR
About
ブログを書こうと思い立って,以前構築したブログを久々に確認した.
今つかってるパソコンにブログのソースコードを持ってきていなかったので,npm install
すると脆弱性の嵐.
$npm i
removed 22 packages, changed 14 packages, and audited 1090 packages in 8s
33 vulnerabilities (7 low, 15 moderate, 11 high)
To address issues that do not require attention, run:
npm audit fix
To address all issues, run:
npm audit fix --force
Run `npm audit` for details.
また,ブログシステムのベースに使っているVuePressが0.xから1.8までバージョンアップしていたので,これは追従しよう.
しかし早々に更新を断念. デザインをフルスクラッチでやるためにVuePressのソースをeject
していたため,追従をすべて手でやる必要があった.
ブログ自体のメンテにはそこまでコストをかけてられないので,今度はeject
しなくてもデザインを触れるライブラリを選定することにした.
- 開発言語自体にはこだわりはないが,型システムはほしい
- マークダウンで書ける
- サーバ管理は避けたいので,GitHub Pagesに載せられるもの
を探した.最終的には次の2つから選んだ. 結局相当のyak shavingが発生した.
Gatsby.js
Gatsby.jsはReact.jsをベースにしたリッチなCMSフレームワーク.バックエンドとはGraphQLで連携し,コンテンツの取得はcontentful+Markdownだったりと選定の自由度が高い.
SGのプロセスについてもAPIの呼び出し回数は全ページに対して1回で済む(ようなquery buildをする必要はある)ようなので,productionでの利用をするならGatsby.jsを選ぶと思う.
Next.js
Next.jsはGatsby.jsと比べると薄いフレームワークで,ビルドやルーティング等ベースとなる機能の提供がメインになっている. Static Generationの仕組みはあるが,Gatsby.jsほどの最適化は行われていない感じ.
したがってCMSとして使うためにはそれ相当の機能の実装が必要となるため,コストは高い面白そう.
Artifacts
これができた.
Architecture
Next.jsのgetStaticProps
とgetStaticPaths
を用いて一覧・詳細ページをSG.
コンテンツはMarkdownで記述し,remarkを用いてHTMLを生成している.
Next.js側でもMDX(Markdown+React Component)を描画できる仕組みがあったけど,内部で呼ばれているコンパイラに手を加えるのに難儀したため,情報の多いremarkを選んだ.
(amdxはマジですごいと思う)
remarkはMarkdownを入力として,指定した形式での出力を行うプロセッサで,fig.1のようにParser
,Transformer
やCompiler
といったプラグインを処理に挟むことができる.
受け取ったMarkdownはmdast2という形式のastに変換され,プラグインはmdastを受取りmdastを返すよう要求される.
| ........................ process ........................... |
| .......... parse ... | ... run ... | ... stringify ..........|
+--------+ +----------+
Input ->- | Parser | ->- Syntax Tree ->- | Compiler | ->- Output
+--------+ | +----------+
X
|
+--------------+
| Transformers |
+--------------+
fig.1 remarkの変換プロセス. 図はベースとなるunifiedから. [出典]
このブログではそこそこプラグインを使っていて,中には自作したものもある3.
const markdownToHtml = async (markdown: string): Promise<string> => {
const result = await remark()
.use(gfm) // remark-gfm
.use(math) // remark-math
.use(emoji) // remark-emoji
.use(container) // 自作 | VuePressで使っていたCustom Container記法に対応
.use(caption) // 自作 | Markdown中の画像に対して通し番号+figcaptionを付与
.use(prism) // remark-prism
.use(externalLink) // remark-external-links
.use(slug) // remark-slug
.use(headings, { behavior: "wrap" }) // remark-autolink-headings
.use(footnotes) // remark-footnotes
.use(remark2rehype) // remark-rehype | mdastからhast形式へ変換
.use(katex) // rehype-katex | remark-mathで変換したmarkdownでの数式をkatex記法へ変換
.use(stringify) // rehype-stringify
.process(markdown);
return result.toString();
};
fig.2 プラグイン一覧
remark plugin
実際作ってみると結構簡単に実装できる. READMEにあるサンプルだと型情報がなかったりで結構辛いけど,fig.1とguideが参考になる.
- remark-container
もともと使っていたVuePressでは,Custom Containersと呼ばれるMarkdownの拡張記法が使える4.
::: info Custom Title
Custom Container Body
:::
fig.3 これがこうじゃ
Custom Container Body
remark版を探すといくつか見つかったけど,どれも動かなかったので作ることにした.
実装は正規表現でdirectiveにマッチした行のastを書き換えるもので,単純なため対応してないケースもある(directive内のcode blockとかnodeが分割されるケース). これらの対応はremark-directiveを用いて後々やりたい.
- remark-image-caption
img要素のようなself-closing tagについては::before
等の疑似要素が使えないため,例えばMarkdownで記述した画像のタイトルをCSSのみで抽出・表示するといったことができない.
画像に対してcaptionを付与するプラグインは動作するいくつか見つかったけど,画像に対して連番を振ってくれるようなものは見当たらなかった.
![alt text](image.png "image title")
今回作成したプラグインでは,画像を<figure>
タグで囲んでタイトルを<figcaption>
に設定するようにした.
ただ,この手法は例えばtableのようなMarkdown記法でタイトルを付与できない要素に対しては使えないため,画像やコード,テーブル等の直下のem
やstrong
をcaptionとみなすといったやり方に移行すると思う.
Commit Log
各記事の編集履歴を表示するCommitsコンポーネント(↓のCommits).
やっていることは単純で,getStaticProps
呼び出し時に該当するファイルに対してのGit履歴を取得しているだけ.
Table of Contents
現在のページにあるheadingsから目次を構築するコンポーネント(→の目次.表示されてない人は1200px以上の画面で見てね).
これは単に実装したことがなかったから作った5. 原理は単純で,現在のスクロール位置を取得して,通り過ぎたheadingのうち最も近いものをhighlightしている. 最初はmarkdownを与えてheadingを抽出するやり方にしていたけど,markdown外にあるCommitsやComments等,そのページにあるheadingを動的に抽出する方針に切り替えた.
Comments
GitHub Issueと連携したコメントコンポーネント(↓のComments). utterancesというサービスを利用した. ブログからコメントするにはGitHub AccountでのAuthが必要だけど,Issuesに直接コメントすることもできる.
これから
フリーランスとしてのお仕事も現在は募集しておらず,自身の身の振り方については少なくとも今年いっぱいは考える予定. とりあえずプライベートではアルゴリズムやデータ構造等再度やっていこうと思う.
紅茶コーディネータについては年初からの受講なので,順調に行けば今年の秋ころには取れるかなあ,という感じ. ミーハーなので今年のセカンドフラッシュを楽しみにしてたりする.
Footnotes
-
余談だけど茶葉のテイスティングカップ,料理の際に調味料を混ぜる小さい容器に最適すぎる ↩
-
mdastはunistという規格に従っていて,他にはHTMLのunist表記であるhastなどがある. またmdastのプロセッサ実装(remark)があるように,hastのプロセッサにはrehypeがある. ↩
-
remark内部で使っているライブラリの移行が2020後半にあって,npmに上がっているプラグインの中にも動作しないものが少なくない. 実際動作するプラグインの中には(This plugin is made for the new parser in remark (micromark, see remarkjs/remark#536).)のように注釈があったりする. ↩
-
今回UIコンポーネントライブラリは一切使わなかった.
コストとは↩
Comments