HugoテーマのカスタマイズでTypeScriptとReactを統合する方法

本記事では、静的サイトジェネレーターHugoのテーマを自作する際に、TypeScriptとReactを導入する方法について解説します。Hugo自体の基本的な設定については、markdownで記事を作成して無料ブログをつくる[Github Pages+Hugo] を参照してください。

1. Hugoテーマの初期化

hugo new theme コマンドを使用すると、テーマのひな形を簡単に作成できます。

hugo new theme tomatohugo

このコマンドを実行すると、tomatohugo という名前のディレクトリが作成され、その中にテーマ開発に必要な基本的なファイルとディレクトリ構造が生成されます。

➜  tomatohugo git:(main) tree .
.
├── LICENSE
├── README.md
├── archetypes/
│   └── default.md
├── assets/
│   ├── css/
│   │   └── main.css
│   └── js/
│       └── main.js
├── content/
│   ├── _index.md
│   └── posts/
│       ├── _index.md
│       ├── post-1.md
│       ├── post-2.md
│       └── post-3/
│           ├── bryce-canyon.jpg
│           └── index.md
├── data/
├── hugo.toml
├── i18n/
├── layouts/
│   ├── _default/
│   │   ├── baseof.html
│   │   ├── home.html
│   │   ├── list.html
│   │   └── single.html
│   └── partials/
│       ├── footer.html
│       ├── head/
│       │   ├── css.html
│       │   └── js.html
│       ├── head.html
│       ├── header.html
│       ├── menu.html
│       └── terms.html
├── static/
│   └── favicon.ico
└── theme.toml

15 directories, 25 files
  • LICENSE: テーマのライセンス情報。
  • README.md: テーマの説明や使用方法。
  • archetypes/: コンテンツ作成時のテンプレート。
  • assets/: CSSやJavaScriptなどのフロントエンドリソース。Hugo Pipesで処理されます。
  • content/: サンプルのコンテンツ。
  • data/: テーマで使用するデータファイル。
  • hugo.toml: サイト全体の設定ファイル(Hugo 0.112.0以降)。
  • i18n/: 多言語対応のための翻訳ファイル。
  • layouts/: HTMLテンプレートファイル。
  • static/: 静的ファイル(画像、ファビコンなど)。ビルド時にそのままコピーされます。
  • theme.toml: テーマの設定を定義するファイル。

初期化が完了したら、hugo server コマンドを使用してローカルサーバーを起動し、シンプルなテーマが正しく動作しているか確認できます。

hugo server

初期化時のコミット例: https://github.com/yuhi-sa/tomatohugo/commit/695eed912b903388faf16a1791b852697811d348

2. TypeScriptの導入

テーマ内でTypeScriptを使用するために、Node.jsの環境をセットアップし、WebpackとTypeScriptコンパイラを設定します。

  1. package.json の初期化: テーマディレクトリのルートで npm init コマンドを実行し、package.json ファイルを作成します。

npm init -y ```

  1. TypeScriptとts-loaderのインストール: TypeScriptコンパイラと、WebpackでTypeScriptファイルを処理するためのローダーをインストールします。

npm install –save-dev typescript ts-loader webpack webpack-cli ```

  1. tsconfig.json の作成: TypeScriptの設定ファイル tsconfig.json を作成します。

{ “compilerOptions”: { “target”: “es5”, // 出力するJavaScriptのバージョン “module”: “commonjs”, // モジュールシステム “outDir”: “./assets/js/”, // コンパイルされたJSファイルの出力先 “strict”: true // 厳格な型チェックを有効にする }, “include”: [ “./assets/ts/**/*.ts” // コンパイル対象のTypeScriptファイル ], “exclude”: [ “node_modules” // 除外するディレクトリ ] } ```

  1. TypeScriptファイルの作成: ./assets/ts/ ディレクトリ内にTypeScriptファイル(例: main.ts)を作成します。

const message: string = “Hello, TypeScript!”; console.log(message); ```

  1. webpack.config.js の作成: Webpackの設定ファイルを作成します。Webpackは、JavaScriptアプリケーションのためのモジュールバンドラーであり、TypeScriptファイルをバンドルするために使用します。

const path = require(‘path’);

module.exports = { mode: ‘development’, // または ‘production’ entry: ‘./assets/ts/main.ts’, // エントリポイント module: { rules: [ { test: /\.ts$/, // .tsファイルを対象とする use: ’ts-loader’, // ts-loaderを使用 exclude: /node_modules/, }, ], }, resolve: { extensions: [’.ts’, ‘.js’], // インポート時に解決する拡張子 }, output: { filename: ‘bundle.js’, // 出力ファイル名 path: path.resolve(__dirname, ‘assets/js’), // 出力ディレクトリ }, }; ```

  1. ビルドの実行: Webpackを実行してTypeScriptファイルをJavaScriptにコンパイルし、バンドルします。

npx webpack ``` これにより、./assets/js/bundle.js が作成されます。

  1. Hugoテンプレートへの組み込み: 生成されたJavaScriptファイルをHugoのテンプレートに組み込むために、layouts/partials/head/js.html などの適切な場所に以下のようなコードを追加します。

{{- with resources.Get “js/bundle.js” }}

{{- end }} ``` TypeScript導入時のPR例: https://github.com/yuhi-sa/tomatohugo/pull/1

3. Reactの導入

TypeScript環境にReactを追加します。

  1. React関連ライブラリのインストール: React本体とDOM操作ライブラリ、およびそれらの型定義ファイルをインストールします。

npm install –save react react-dom npm install –save-dev @types/react @types/react-dom ```

  1. Reactコンポーネントの記述: ReactではJSX(JavaScript XML)と呼ばれる構文を使用してコンポーネントを記述するため、TypeScriptファイルの拡張子を assets/ts/main.tsx に変更します。

import React from ‘react’; import { createRoot } from ‘react-dom/client’; // React 18以降の推奨API

const App = () => { return (

Hello, React!

); };

// HTML内のマウントポイントとなる要素を取得 const appElement = document.getElementById(‘app-root’); // 例: idを’app-root’とする if (appElement) { const root = createRoot(appElement); // createRootでルートを作成 root.render(); // コンポーネントをレンダリング } ``` 注意: React 18以降では ReactDOM.render の代わりに createRoot を使用することが推奨されています。HTML側のマウントポイントのIDも app-root などに変更してください。

  1. Hugoテンプレートへの組み込み: Reactコンポーネントを呼び出すためのHTML要素を、適切なHugoのテンプレートファイル(例: layouts/_default/single.htmllayouts/_default/home.html)に追加します。

{{- partial “head/js.html” . -}} ```

  1. Webpack設定の更新: Reactコンポーネント(.tsxファイル)をバンドルするために、Webpackの設定を更新します。

const path = require(‘path’);

module.exports = { mode: ‘development’, entry: ‘./assets/ts/main.tsx’, // エントリポイントを.tsxに変更 module: { rules: [ { test: /\.tsx?$/, // .tsまたは.tsxファイルを対象とする use: ’ts-loader’, exclude: /node_modules/, }, ], }, resolve: { extensions: [’.ts’, ‘.tsx’, ‘.js’], // 拡張子に.tsxを追加 }, output: { filename: ‘bundle.js’, path: path.resolve(__dirname, ‘assets/js’), }, }; ```

  1. 動作確認: 動作確認を行います。 ローカルサーバーを起動し、「Hello, React!」が表示されることを確認します.

hugo server ``` React導入時のPR例: https://github.com/yuhi-sa/tomatohugo/pull/2