devlog

go:embed するファイルの生成に go generate を活用した

2023-12-10

概要

  • Go には embed という機能があり、ビルドするときファイルを埋め込むことができます
  • embed で静的ファイル(HTML/CSS/JS)を埋め込む際に dist をどう管理するか悩みました
  • 解決策の1つとして、go generate を活用する方法を紹介します

背景

go:embed でフロントエンドアプリの dist を埋め込みたい時がありました。

次のようなディレクトリ構造です。

$ tree . ├── go.mod ├── go.sum ├── main.go └── web # web 配下はフロントエンドアプリ ├── node_modules ├── package.json ├── package-lock.json ├── src ├── dist └── main.go # dist を埋め込みたい

web というディレクトリ配下に package.json や src があります。

ここにフロントエンドアプリのソースが置かれ、同じ階層の web/dist へビルドした成果物が吐き出されます。 そして web/main.go で Goのバイナリに埋め込みます。

package web import ( "embed" ) //go:embed all:dist/* var Dist embed.FS // フロントエンドアプリの dist を格納

このとき次のような課題がありました。

課題: dist の管理方法

go:embed でファイルを埋め込むには、埋め込みたいファイルが存在している必要があります。
ここで言えば web/dist にファイルがないと embed で埋め込み出来ません。

... はい、当たり前です。
 
当たり前のことなのですが、ビルドする際に手順ができてしまいます。

  1. cd web && npm run build
  2. go build

この手順が「暗黙的だ」と課題を感じてました。
意識していないと npm run build の実行を忘れてしまう、ということです。

解決策

上記の課題の解決策として、ありがちなのはコマンドをまとめることでしょうか。
例えば、Makefile や go-task が有用だと思います。

それでも良いのですが、go generate を活用する方法もあるのに気づきました。

go generate で npm run build を実行する

go generate はコードを生成する機能です。 例えば OpenAPI や Protobuf からコードを生成する時使いますよね。

そんな go generate ですが embed する際も活用できそうだと気づきました。
つまり、埋め込むファイルの生成処理や取得処理を go generate で行う、ということです。

ここではフロントエンドアプリの dist を埋め込みたいので、次のような記述を追加します。

package web import ( "embed" ) // 追加 //go:generate npm install //go:generate npm run build //go:embed all:dist/* var Dist embed.FS

go generate からなるコメントを追加しました。
 
この状態で go generate ./... を実行するとフロントエンドアプリのビルド処理が行われ、 web/dist へビルド成果物が置かれます。

終わりに

結局のところビルド手順は減ってません。npm run build の代わりに go generate を実行する手順が加わっただけです。 しかし「Go のエコシステムの中に入れられた」と言いますか、より Go ライクな方法なのではないかと思ってます。

  • 作成日
    2023-12-10
  • 更新日
    2023-12-10