Hugoプロジェクトをpnpmモノレポ化してブログにツールを組み込む
ブログに組み込む小さなツールの開発をHugoのjs.Buildからpnpmのモノレポ構成とViteを使った開発環境に移行してみました。
きっかけ
ブログでjs.Build
などHugoの機能を利用してちょっとしたツールのページを作成していたのですが、ちょっとしたサンプルなどではいいのですが少し規模が大きくなると、あまり開発体験は良いものではありませんでした。
せっかくならViteで開発したいのですが、ブログとは別プロジェクトにするのも管理が面倒です。
Hugoはnpmパッケージのhugo-extended
を利用しているので、pnpmのモノレポ構成にして、両方をうまく管理できないかと試してみました。
Hugo Extendedをどの環境でも同じバージョンを同じ手順でインストールするのにnpmパッケージのhugo-extendedが便利でしたので、それを利用する手順をまとめました。
プロジェクト構成の変更
ディレクトリ構成について
ディレクトリ構成は以下にしました。
project-root/
├── blog/ # Hugoブログ
│ ├── content/
│ ├── layouts/
│ ├── static/
│ ├── assets/
│ ├── config.toml
│ └── package.json
├── tools/ # 各種ツール
│ ├── tool-01/ # ツール1
│ │ ├── src/
│ │ └── package.json
│ └── tool-02/ # ツール2
│ ├── src/
│ └── package.json
├── package.json
└── pnpm-workspace.yaml
pnpmワークスペースの設定
まずプロジェクトのルートでpnpm-workspace.yaml
を作成し以下を設定しました。
packages:
- blog
- tools/*
ルートのpackage.json
には以下を追加して、pnpm run build:tools
でサブパッケージのツールをビルドするようにしました。
pnpm build
ではツールのビルドを行ってからHugoでブログのビルドを行うようにしています。
{
"scripts": {
"dev": "pnpm --filter blog dev",
"build": "pnpm run build:tools && pnpm run build:blog",
"build:blog": "pnpm --filter blog build",
"build:tools": "pnpm --filter @tools/* build"
}
}
A workspace is a directory that contains multiple packages.
ツールのサブパッケージ化
Viteプロジェクトの作成
以前に作成したPDFメタデータ編集ツールをサブパッケージに移行します。
まずはViteプロジェクトを作成します。
pnpm create vite tools/pdf-meta-editor
package.json
のname
には@tools/pdf-meta-editor
を設定しました。
{
"name": "@tools/pdf-meta-editor",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint ."
}
}
これでプロジェクトのルートでサブパッケージをフィルタしてbuild
やdev
での起動ができるようになります。
# ビルド
pnpm --filter @tools/pdf-meta-editor build
# 開発モードでの起動
pnpm --filter @tools/pdf-meta-editor dev
# @tools/* をビルド
pnpm run build:tools
Next Generation Frontend Tooling
Vite設定の調整
Viteの設定を行うvite.config.ts
で出力先を調整しました。
export default defineConfig(({ command }) => ({
base: `/${name.substring(1)}`,
build: {
outDir: `../../blog/static/${name.substring(1)}`,
emptyOutDir: true,
manifest: true,
rollupOptions: {
input: command === 'build' ? 'src/main.tsx' : undefined,
},
},
}))
ポイントはbase
にツールを配置するサブパスを指定することと、outDir
でHugoのstatic
ディレクトリに出力することと、manifest: true
でViteのマニフェストファイルを生成することです。
Hugoでのツール読み込み設定
テンプレートの作成
Hugoのテンプレートでは、Viteのmanifest.json
を読んでファイルを動的に読み込む仕組みを作りました。
{{- with os.ReadFile (path.Join hugo.WorkingDir "static" (printf "%s.vite/manifest.json" .RelPermalink)) }}
{{- $manifest := . | unmarshal -}}
{{- with index $manifest "src/main.tsx" }}
{{- range .css }}
<link rel="stylesheet" href="{{ . }}">
{{- end }}
<script type="module" defer src="{{ .file }}"></script>
{{- end }}
{{- end }}
この設定により、Viteでビルドされたファイルを自動的に読み込むことができます。
記事ページの設定
ツールの記事のマークダウンファイルではフロントマターでlayout: tools
を指定することで、上記のテンプレートが利用されツールのページを作成することができます。
---
title: PDF metadata editor
description: 'Tools to edit PDF metadata on the web browser.'
layout: tools
date: 2022-11-29
---
ビルド成果物をGit管理外に設定
Viteでビルドしたファイルはblog/static
に出力されますが、これらはGitで管理する必要がないため.gitignore
を設定しました。
tools/
この設定により、ツールのビルド成果物がGitにコミットされることを防げます。
おわりに
モノレポ化したことでViteなどでツールの開発ができるようになったのが大きな改善でした。
今後ツールを追加するときもツールごとに好きな開発ツールを使って開発ができます。ブログとツールを同じリポジトリで管理できて、それぞれ独立して開発できるのがちょうど良いバランスかなと思います。