概要
- Go には embed という機能があり、ビルドするときファイルを埋め込むことができます
- embed は全てのファイルを埋め込んでくれる訳ではありません
- デフォルトで . や _ で始まるファイルを無視します
- all をつけると全てのファイルを埋め込みます
経緯
Next.js の dist を go:embed で埋め込んだところ「一部のファイルが見つからない」という事象が見受けられました。具体的には次のようなコードです。
package main import ( "embed" "fmt" "strings" "github.com/gofiber/fiber/v2" ) //go:embed dist/* var Dist embed.FS func main() { app := fiber.New() app.Get("/*", func(c *fiber.Ctx) error { requestPath := c.Path() // like `/` path := fmt.Sprintf("dist%s", requestPath) // like `dist/` if strings.HasSuffix(path, "/") { path += "index.html" } f, err := admin.Dist.ReadFile(path) if err != nil { return err } // 省略: content-type 付与処理など return c.SendString(string(f)) }) app.Listen(":3000") }
Dist と言う変数に Next.js の dist を埋め込んでいます。 HTTPでアクセスがあると Dist からファイルを探し、それをそのまま返す処理を書いてます。
go run してウェブブラウザで確認したところ、なぜか一部のファイルが読み込まれない、という事象が起きました。
調査を進めると、、まず
- Next.js は _ で始まるファイルを生成する。例: _buildManifest.js
- _ で始まるファイルが見つからない
- Dist にも見つからない
という事が分かりました。
原因
GitHubでIssueを探したところ、golang/go#43854 というIssueを見つけました。
確認したところ上記の挙動は go:embed の仕様のようです。
embedパッケージのドキュメントにも記載がありました。
If a pattern names a directory, all files in the subtree rooted at that directory are embedded (recursively), except that files with names beginning with ‘.’ or ‘_’ are excluded
. や _ で始まるファイルは除外される、とのことです。
対策
all を付けます
//go:embed all:dist/* var Dist embed.FS
これもドキュメントに記載がありました。
If a pattern begins with the prefix ‘all:’, then the rule for walking directories is changed to include those files beginning with ‘.’ or ‘_’.
終わりに
なかなか気付きにくい挙動だと思います。
が、例えば .git や .env を意図せず埋め込んでしまうのは避けたいので、多少合理的ではあるかなあ、と感じました。