⛩️

イケてるGo版JSX,templを知っているか!?(feat. neovimでの設定)

に公開

Tutorial

https://templ.guide/

templはGolangで書かれたHTML出力用のDSLです。
あらかじめ次のコマンドでtemplを導入しておきましょう。

$ go install github.com/a-h/templ/cmd/templ@latest

では構文を確認してみましょう。

package main
import "fmt"

templ diaryCard(emoji string, title string, content string, date string, idx int) {
	{{ path := fmt.Sprintf("/view/item?idx=%d", idx) }}
	<div
		class="bg-white rounded-lg shadow p-4 hover:shadow-md transition"
		hx-trigger="click"
		hx-get={ path }
		hx-target="#detail-panel"
		@click="open=true"
	>
		<div class="flex items-center mb-2">
			<span class="text-3xl mr-2">{ emoji }</span>
			<h2 class="text-xl font-semibold truncate">{ title }</h2>
		</div>
		<p class="text-sm text-gray-500 mb-1">{ date }</p>
		<p class="text-gray-700 line-clamp-3">
			{ content }
		</p>
	</div>
}

このような感じです。htmxとtailwindcssとalpine.jsを使っているので,ごちゃごちゃしていますが、Golang版JSXという雰囲気ですね。今、これをdiary_card.templとして保存します。
保存先のフォルダで

$ templ generate

と実行すると、diary_card_templ.goというファイルが生成されます。
あとは

package main

import (
	"log"
	"net/http"
)

func main() {
	http.HandleFunc("/diary", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		diaryCard("📝","日記タイトル","これは日記の本文です。","2025-01-01",0).Render(r.Context(), w)
	})

	log.Fatal(http.ListenAndServe(":8080", nil))
}

のように書き、サーバーを実行し、http://localhost:8080/diaryにアクセスすると、先ほどのtemplファイルにdiaryCard関数の引数が埋め込まれた状態で返却されます。

Golang標準のhtml/templateと比較すると

  • Golangにコンパイルされるのでテンプレートファイルを読み込む処理を書かなくてよい
  • 関数に変換されるので型安全で、ビルド前にエラーが把握できる
  • ネストなども簡単にできる
  • forやif文なども実行できる

という特徴があります。まさにGolang版JSXという感じですね。
また、tinygoでも実行できるらしいのでWASMにコンパイルしてCloudflare Workersにデプロイなんてこともできるらしいです。

というわけでtemplの開発環境を整えてみましょう!

Neovimでのセットアップ

templはLSP含め開発リソースがかなり充実しているので、Neovimでも簡単にセットアップできます。
https://templ.guide/developer-tools/ide-support

1. シンタックスハイライト

デフォルトだとtemplがシンタックスハイライトされません。nvim-treesitter/nvim-treesitterを入れたうえで:TSInstall templでセットアップできます。これでtemplファイルを開くとシンタックスハイライトされるようになるはずです。

2. LSPのインストール

mason.nvimを使っている場合簡単に入れられます。:Masonで起動してtemplというそのまんまの名前のLSPを入れるだけです。

3. LSPのセットアップ

mason-lspconfig.nvimはバージョン2.0.0以降でsetup_handlerが使えなくなりました。これはNeovim 0.11で本体にLSP関連の機能が追加されたことに起因するそうです。
https://zenn.dev/kawarimidoll/articles/b202e546bca344
https://zenn.dev/vim_jp/articles/migrate-nvim-lspconfig-v0_11

自分の場合、上記記事を概ね真似て

require('mason').setup() -- mason.nvimの読み込み
-- setup_handlerの代わり.LSPサーバーを設定する
vim.lsp.config('*', {
  capabilities = require('cmp_nvim_lsp').default_capabilities(),
})

--[[
 **ここで各lspの設定を行う**
]]

-- このsetupにより内部的にvim.lsp.enableが実行されLSPサーバーがロードされる。
require("mason-lspconfig").setup {}

という感じにしています。。
あとは:checkhealth vim.lsptemplがロード可能になっているのを確認できれば問題ないです。(templファイル開きながらのほうが確認しやすいかもです。)

必要に応じて、html-lspなどもインストールすると良いと思います。
なお、場合によっては拡張子の設定が必要な場合があるので、例えばhtml-lspの場合は

-- For templ
vim.lsp.config['html'] = {
  filetypes = {'html','templ'}
}

のようにtemplファイルでもhtml-lspが起動するようしてやります。tailwindcsshtmxでも同様です。

4. 閉じタグの自動補完(オプショナル)

vscodeには開きタグを入力すると対応する閉じタグを自動で記述する機能があります。
個人的にはこれもneovimで使えると嬉しいのでwindwp/nvim-ts-autotagを入れます。

setup関数を呼ぶだけで動いてくれます。

require('nvim-ts-autotag').setup()

なお、templについてはプラグイン側で対応しているため、treesitterのパーサーが入っていればそれ以外にtemplに特化した設定は必要なさそうです。

5. AIコーディング(オプショナル)

設定とは少し違いますが、昨今流行りのAIコーディングだとモデルによってはtemplを知らなかったり、存在は知っていても正確なコーディングを行ってくれない場合があります。一応、GeminiとGPTは読解ができそうなのですが、In-Context Learningに基づいた、正確なコーディングを行うためのリソースとして、公式がllm.mdを用意してくれています。CopilotなどでこれをURLからの読み込み機能を使って読ませてあげれば、性能が上がるかもしれません。ただし、非常に長いファイルなのである程度コンテキストが長いモデルが必要があるのと、それ相応にAPIクレジットを消費する点には注意した方がいいかもしれません。

Discussion