Hugoでnpmパッケージを利用する
Hugoでnpmパッケージを利用する方法について調べました。今回はreactを利用したjsxで作成したスクリプトを記事の中で利用してみました。
プロジェクトの初期化
まずは以下のコマンドでnpmプロジェクトの初期化を行います。
npm init次にreactとreact-domをプロジェクトにインストールします。
npm install --save react react-domディレクトリ構成について
この記事のマークダウンはcontentディレクトリ以下にあります。
assetsディレクトリ以下にcontentディレクトリから記事のマークダウンまでと同じ階層でディレクトリを作成しました。
このディレクトリにスクリプトを配置するルールにしました。
この記事の場合のディレクトリ構成は以下です。
- content/posts/blog
- hugo-use-npm-package
- index.mdこの記事のマークダウンファイルです
- assets/posts/blog
- hugo-use-npm-packageこの記事のアッセットを配置するディレクトリです
- tsconfig.json
- index.ts
- App.tsx
index.tsが読み込まれるスクリプトのエントリーポイントになります。
テンプレートの設定について
次にテンプレートにスクリプトをビルドして記事に読み込むための設定を追加します。
通常の記事ページで利用したいのでsingle.htmlに設定を追加しました。
<head>
...
{{- with resources.Get (path.Join .Page.File.Dir "index.ts") -}}
{{- with . | js.Build (dict "minify" true "target" "es6") | fingerprint -}}
<script src="{{ .Permalink | relURL }}" defer></script>
{{- end -}}
{{- end -}}
</head>リソースディレクトリからresources.Getで記事のマークダウンがあるディレクトリと同じ階層にあるindex.tsを取得します。
ファイルがある場合はjs.Buildでビルドして<script>タグを出力するようにしました。
Hugo Pipes can process JavaScript files with [ESBuild](https://github.com/evanw/esbuild).
js.Buildのtargetオプションを利用することでビルドして出力されるjavascriptの規格を指定することができます。
ただし、js.Buildが利用しているesbuildは現時点ではes6からes5への変換がまだサポートされていません。そのためtargetにはes6を指定しました。
Transforming ES6+ syntax to ES5 is not supported yet
読み込むスクリプトの作成
それでは記事に読み込むスクリプトを作成していきます。
まずは以下の内容でtypescriptの構成ファイルtsconfig.jsonを作成しました。tsconfig.jsonを作成することでエディタでの編集も快適に行えると思います。
{
"compilerOptions": {
"baseUrl": ".",
"module": "ES6",
"moduleResolution": "Node",
"target": "ES6",
"lib": ["ES6", "DOM"],
"jsx": "react-jsx"
}
}続いてreactのtsxで以下のスクリプトを作成しました。スライダーで設定した赤、緑、青の色を表示する簡単なプログラムです。
import * as React from 'react'
import { createRoot } from 'react-dom/client'
function App() {
const [r, setR] = React.useState('0')
const [g, setG] = React.useState('50')
const [b, setB] = React.useState('100')
const color = `rgb(${r}, ${g}, ${b})`
return (
<React.StrictMode>
<div style={{ backgroundColor: color }}>
<input
type="range"
max="255"
value={r}
onChange={(e) => setR(e.target.value)}
/>
<input
type="range"
max="255"
value={g}
onChange={(e) => setG(e.target.value)}
/>
<input
type="range"
max="255"
value={b}
onChange={(e) => setB(e.target.value)}
/>
</div>
<span>{color}</span>
</React.StrictMode>
)
}
export function render(node: HTMLElement) {
createRoot(node).render(<App />)
}次にエントリーポイントとなるindex.tsを作成しました。App.tsxを読み込んで記事の中にあるidがApp1のHTML要素にマウントするようにしています。
import { render } from './App'
window.addEventListener('DOMContentLoaded', () => {
render(document.getElementById('App1'))
})最後に記事のマークダウンの中にidがApp1の<div>タグを追加しました。
<div id="App1"></div>スタイルをあてて少し見た目を調整していますが実際の表示のサンプルは以下です。
お使いのブラウザがInternet Explorerの場合は構文エラーになるため表示されていないと思いますがご了承ください。
おわりに
Hugoでnpmパッケージを利用する方法について調べて、実際に作成したサンプルのスクリプトを記事の中に表示してみました。
esbuildが利用されているのでビルドがかなり早いのでスクリプトの作成をとても快適に行うことができました。記事の中にサンプルを表示したい場合に使えそうです。