HUGO v0.74 で追加された JS Bundler を試す

July 20, 2020

概要

HUGO の v0.74.0 にて、ESBuildが組み込まれ、 HUGO 上で JavaScript/JSX や TypeScript/TSX のバンドルが行えるようになりました。

HUGO の公式ドキュメントにはまだ記載されてませんが、Document JS.Build #1171を参考にこの記事では、JS Budler を利用する方法を説明します。

2020/07/21 追記 この機能についてのドキュメントが公開されました。 JavaScript Building

環境

  • HUGO v0.74.2
  • Node.js v12.18.1

基本

バンドル対象のリソースは、テーマディレクトリ、またはサイトディレクトリの assets の下に置きます。

  • サイトディレクトリ内のテンプレート内で利用する場合

    site/
    ├── ...
    ├── assets/
    │   └── js/
    │       └── index.ts
    ├── ...
  • テーマディレクトリ内のテンプレート内で利用する場合

    site
    ├── ...
    ├── themes
    │     └── THEME/
    │           ├── ...
    │           ├── assets
    │           │    └── js
    │           │    └── index.ts
    │           ├── ...
    ├── ...

TypeScript のビルド&バンドル

TypeScript をビルド&バンドルしたいテンプレートファイルに、次のように記述します。

{{ with resources.Get "js/index.ts" }}
  {{ $js := resources.Get . | js.Build }}
  <script src="{{ $js.Permalink }}"></script>
{{ end }}
  • resources.Get のあとに、対象のファイルを assets 以下からパスを指定します。
  • ビルドしたファイルのパス(ここでは、$js.Permalink)を<script>タグで指定します。

HUGO でビルドすると、js ファイルと次のような HTML が生成されます。

<!-- ...略... -->
<script src="http://localhost:1313/js/index.js"></script>
<!-- ...略... -->

生成されるファイル名は、バンドルする対象のファイル名と同じです。名前を変更するには、js.Build に渡すオプションとして targetPath を指定します。

{{ with resources.Get "js/index.ts" }}
  {{ $js := resources.Get . | js.Build (dict "targetPath" "js/main.js") }}
  <script src="{{ $js.Permalink }}"></script>
{{ end }}

ファイルを Minify するには、js.Build で渡すオプションに "minify" true を渡すか、resources.Minify にパイプで渡します。 参考:Asset minification

  • "minify" true を渡す例(オプションが冗長になってきたので、別変数に切り出し)

    {{ with resources.Get "js/index.ts" }}
      {{ $option := dict "targetPath" "js/main.js" "minify" true }}
      {{ $js := resources.Get . | js.Build $option }}
      <script src="{{ $js.Permalink }}"></script>
    {{ end }}

React のビルド&バンドル

node_modules からのインポートをサポートしているので、React アプリケーションもバンドルできます。 今回は、HUGO v0.74.2 で追加された defines を利用します。

次のような React アプリケーションをバンドルすることを考えます(参考: Components and Props)。

import * as React from "react";
import * as ReactDOM from "react-dom";

function Welcome(props) {
  return <p>Hello, {props.name}</p>;
}

function App() {
  return (
    <>
      <Welcome name="Sara" />
      <Welcome name="Cahal" />
      <Welcome name="Edite" />
    </>
  );
}

ReactDOM.render(
  <App />,
  document.getElementById("react")
);

npm install で利用するモジュールをインストールします。

package.json および node_modules はバンドルするテンプレートディレクトリに配置する必要があります。 たとえば、テーマディレクトリ内のテンプレートで利用する場合には、テンプレートディレクトリの下に配置します。

npm init -y
npm install --save react react-dom

バンドルしたいテンプレートファイルに、次のように記述します。

{{ with resources.Get "js/App.jsx" }}
  <div id="react"></div>
  {{ $options := dict "targetPath" "js/app.js" "minify" true "defines" (dict "process.env.NODE_ENV" "\"development\"") }}
  {{ $js := resources.Get . | js.Build $options }}
  <script src="{{ $js.Permalink }}"></script>
{{ end }}

HUGO でビルドすると、js ファイルと次のような HTML が生成されます。

<!-- ...略... -->
<div id="react"></div>
<script src="http://localhost:1313/js/app.min.js"></script>
<!-- ...略... -->

React を HUGO でバンドルできると、Algolia の React InstantSearch を利用して実装する検索画面も組込みきます。 検索画面のためのソースコードを別リポジトリで管理する必要がなくなるので、便利だなと感じます。