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

Blog

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

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を利用して実装する検索画面も組込みきます。 検索画面のためのソースコードを別リポジトリで管理する必要がなくなるので、便利だなと感じます。