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
が利用されているのでビルドがかなり早いのでスクリプトの作成をとても快適に行うことができました。記事の中にサンプルを表示したい場合に使えそうです。