Notion Hugo Exporter は、Hugo の静的サイトで Notion をヘッドレスCMSとして利用するための OSS ツールです。私(dobassy)が個人で開発・公開しています。
- GitHub: dobassy/notion-hugo-exporter
- Docker Hub: dobassy/notion-hugo-exporter
なぜ作ったのか
Hugo で個人ブログを運営するにあたって、記事の執筆や構成管理をヘッドレスアーキテクチャで行いたいと考えていました。静的サイトジェネレーターの高速さと、CMS の執筆体験を両立させたかったのです。
最初に試したのは Contentful でした。ヘッドレスCMS としての機能は十分でしたが、UIが自分の好みに合わず、ブログの執筆体験が向上するとは感じられませんでした。結局のところ、CMS の管理画面で記事を書くよりも、使い慣れた Notion で書くほうがずっと快適だったのです。
そこで発想を転換しました。日常的に使っている Notion をそのまま CMS として利用できないか? Notion には API があり、データベース機能もある。Notion のページを Markdown に変換して Hugo に渡せば、執筆は Notion、公開は Hugo という理想的なワークフローが実現できるはずです。
こうして生まれたのが Notion Hugo Exporter です。Notion のデータベースに記事を書き、コマンド一発で Hugo 用の Markdown ファイルを生成する。画像のダウンロードや WebP 変換も自動で行い、面倒な作業を省きます。
バージョン
2022/12現在、バージョン 0.x の状態です。破壊的な仕様変更には最大限考慮しますが、破壊的変更が発生する可能性もあります。
提供機能
- Notion 上の指定するデータベースに作成されているページ単位でMarkdownファイルを生成します
- Notion API へのリクエストを軽減するためにローカルキャッシュを持っています
- Notion ページに含まれる画像が Amazon S3 に保存されている場合、有効期限付きのURLであり、このまま公開すると不都合があるため処理を停止します(この挙動を無効化することもできます)
- Notion ページに含まれる画像をダウンロードします。ダウンロードした画像は
staticディレクトリに保存して公開できるほか、独自に作成した Javascript の callback 処理をフックすることができます - ダウンロードした画像は、
.webpフォーマットに変換することができます - 記事の絶対パス (
Url) あるいは相対パス(Slug) に記入した文字列は kebab-case で変換するため、URLの作成が少し楽になりますUrlやSlugは Hugo の仕様による設定項目です
- その他いくつかの設定は Configuration を参照してください
Permission (Notion API)
Notion Hugo Exporter は Notion ページへの書き込みを行いません。よって、Read 権限の Token のみで動作します。
書き込み権限があればデータベースの初回セットアップが楽になりますが、裏返すとその時にしか主な活躍場面がありません。余計な書き込み権限を有することによる、不本意なデータ破壊を回避することを優先しています。
実行の前提条件
- Node を利用します (v18で開発していますが、一般的なライブラリしか利用していないので v12 くらいまでは動作すると思いますが未確認です)
- npm へは公開していません(まだ)
- Docker Image を提供しています
- 基本的にはローカル環境では直接実行しているため、初回構築時にしか試していません。Experimentalとします。
- dobassy/notion-hugo-exporter - Docker Image | Docker Hub
Quickstart
1. トークンの設定
export NOTION_TOKEN=
export NOTION_BLOG_DATABASE_ID=
NOTION_TOKEN: “my integrations” でトークンを作成しますNOTION_BLOG_DATABASE_ID: See this page (developers.notion.con) for how to find your database id.- データベースIDの確認方法は以下を参考にしてください。
https://www.notion.so/{workspace_name}/{database_id}?v={view_id}
2. Notion データベースプロパティの作成
プロパティは、以下の図で表示対象としている(Shown in table)プロパティがあれば動作します。その他、任意で非表示(Hidden in table)部分にあるプロパティのように Hugo のコンテンツで利用する値としてカスタマイズしたり、自身の管理メモとして任意の数のプロパティを追加できます。
3. コマンドの実行
ここでは Docker で動かす場合とします。
docker run -it --rm \
-e NOTION_TOKEN=$NOTION_TOKEN \
-e NOTION_BLOG_DATABASE_ID=$NOTION_BLOG_DATABASE_ID \
-v $(pwd)/notion-hugo.config.js:/work/notion-hugo.config.js \
-v $(pwd)/content/:/work/content/ \
-v $(pwd)/static/:/work/static/ \
-v $(pwd)/.notion-hugo-cache/:/work/.notion-hugo-cache/ \
dobassy/notion-hugo-exporter:latest
Mount four files or directories.
notion-hugo.config.js: いろいろな設定ファイル(後述)content/: Markdown コンテンツを出力する先static/: 画像をダウンロードする場合は static ディレクトリへ配置します.notion-hugo-cache/: Notion Hugo Exporter の管理用キャッシュデータです。仮に削除しても、全てのファイルを Notion API から取得し直すだけです。
ローカル環境で動かしたい場合は以下のようにビルドしてください。
git clone ...
npm install
npm link
Configuration
notion-hugo.config.js に設定可能なパラメタは以下の通りです。サンプルファイルがあるので参照してください。
module.exports = {
directory: string,
concurrency: number,
authorName: string,
s3ImageUrlWarningEnabled: boolean,
s3ImageUrlReplaceEnabled: boolean,
s3ImageConvertToWebpEnalbed: boolean,
useOriginalConverter: boolean,
saveAwsImageDirectory: null | string,
downloadImageCallback: null | func(),
customTransformerCallback: null | func(),
};
directory: (必須) コンテンツファイルを書き出すディレクトリconcurrency: 同時実行数。増やしすぎないほうがいいかもしれない。Defaults to5.authorName: 記事の author を強制で固定します。一人でサイト運営している時には都合がいいでしょうs3ImageUrlWarningEnabled: Defaults totrue. Amazon S3の画像が含まれている場合に警告を出してプログラムの動作を停止しますs3ImageUrlReplaceEnabled(Experimental): Defaults tofalse. Notion コンテンツに S3 URL が含まれている場合は、ダウンロード後にそれらをローカル パスに置き換えますs3ImageConvertToWebpEnalbed: Defaults tofalse. ダウンロードした画像を.webpへ変換しますuseOriginalConverter: Defaults tofalse. Markdown へ変換した後の行間の調整をしていますutcOffset: Defaults tonullas “Z”. Notion で設定した時刻が UTC だった場合に調整するものです。日本なら “+09:00”saveAwsImageDirectory: Defaults tonull. S3画像をダウンロードするディレクトリdownloadImageCallback: Defaults tonull. ダウンロードした画像に対して任意のコールバック処理を加えることができますfetchInterval: Server Mode で動かすときのインターバル。60秒より短くしても意味がないcustomTransformerCallback: Notion API のブロックに対する独自処理を追加
プロパティ
Notion データベースには以下のプロパティを設定してください。
| Property Name | Type | Required | Default value |
|---|---|---|---|
| isPublished | Boolean | ✅ | |
| Category | Select | ✅ | |
| Tags | multi_select | ✅ | |
| PublishedAt | date | ✅ | |
| UpdatedAt | date | ✅ | |
| Url | Text | ✅ (Either Url or Slug) | |
| Slug | Text | ✅ (Either Url or Slug) | |
| Description | Text | ✅ | |
| Image | Image (external url) | ✅ | |
| Section | Select | ✅ | |
| Author | Text or Select | ”Writer” | |
| isDraft | Boolean | false | |
| filepath | Text |
- これらプロパティは手動で作成してください
filepathproperty: ディレクトリとファイル名を記入することで、Markdownの書き出す場所を強制することができます
CustomTransformer の例
例えば以下のように、特定のURLに対して独自の Shortcode を適用することが可能です。
const customTransformerCallback = (n2m) => {
n2m.setCustomTransformer("bookmark", async (block) => {
const { bookmark } = block;
if (!bookmark?.url) return "";
return `${bookmark.url}`;
});
};
Watch mode (Server mode)
Notion ページのエクスポートには時間がかかるため、コマンドを 1 つずつ実行するのは面倒です。 --server オプションを使用して監視モードに入ることができます。
このモードでは、Notion の更新が定期的にチェックされ、ページが生成されます。 Hugo の hugo server コマンドと併用することで、リアルタイムプレビューのような使い方ができます。
Foreman や Hivemind などのプロセス マネージャーを利用すると便利です。 以下は、Procfile の構成方法の例です。
notion: notion-hugo -S
hugo: hugo server --ignoreCache --buildFuture 

