コンテンツにスキップ

pw generate

Terminal window
pw generate [--code-only] [--debug] [--backend nethttp|fasthttp]

pw generatepw build から最後の手順を引いたもの です。バイナリは作りません。コンパイラが読める状態のツリーを残して止まります。

  1. テンプレート、SQL、ページツリー、カタログ、バインディングの呼び出し箇所を ソースの隣_pw_gen.go にコンパイルする
  2. Tailwind が有効なら、スタイルシートを最小化してビルドする
  3. アセットツリーdist/public に構築 する。圧縮サイドカーとマニフェストも含む
  4. project.main が開発専用パッケージに依存していれば拒否する

pw build がリンクの前に行うのと同じ内容、同じ順序です。pw build が「この コマンド+コンパイラ」として定義されているためで、両者がずれることはありません。

4 番目がコンパイラの隣ではなくここにあるのには理由があります。このコマンドは 自分が実行しないコンパイラにツリーを渡します。contrib/devidp — パスワードを 確認せずにユーザーをサインインさせる開発用の IdP — を配布バイナリから締め出す 検査は、渡したあとではなく渡す前に済んでいなければなりません。

オプション 効果
--code-only 手順 1 で止まる。スタイルシートもアセットツリーも書かない
--debug ビルド済みツリーにソースマップを残す
--backend 依存チェックが依存グラフを列挙するときの build tag を選ぶ

--target は provider 向けのパッケージングを選ぶもので、 pw build の役割です。ここでは受け付けません。

--debugpw build --debug と 同じようにソースマップを残します。ただしそのフラグのもう半分はこのコマンドの 持ち物ではありません。-ldflags は自分で書くコンパイラ行に属するので、ここで デバッグ用の成果物を作るなら pw generate --debug と、strip を指示しない go build の組み合わせになります。

--code-only が書くのは _pw_gen.go だけです。内側のループとエディタのタスク のためのフラグです。診断が指し示す先は生成された Go であり、テンプレートの エラーを見るために最小化済みスタイルシートを待つ時間は誰も払いたくありません。

その出力をコンパイラに渡さないでください。public.godist/publicgo:embed で名指ししているので、このフラグ付きで生成したツリーは、一度も 作られなかったディレクトリでコンパイルに失敗します。Tailwind を使っていれば スタイルシートも欠けますが、こちらはもっと静かに、もっと遅れて表面化します。 ページがスタイル無しで描画されるだけです。

手順 4 は変わらず走ります。このフラグが飛ばすのはファイルを書き出す手順だけ で、開発用 IdP をバイナリから締め出す検査の抜け道にはなりません。--debug は 併用を拒否します。ソースマップの置き場所であるツリーを、このフラグは作らない からです。

コンパイルを他のものが担当するとき

Section titled “コンパイルを他のものが担当するとき”

コンパイルを他のものが担当するのでなければ pw build を使ってください。担当が 移るのは次の 3 つの場合です。

TinyGo のビルド。 pw build は必ずホストの go でリンクするので、TinyGo の プロジェクトは生成してから自分のコンパイラを呼びます。

Terminal window
pw generate
tinygo build -scheduler=threads -o myapp ./cmd/myapp

制御したい go build 変わったターゲットへのクロスコンパイル、-ldflags の 指定、一つのツリーから複数のバイナリ。どれもコンパイラの行を自分で書く理由です。

Terminal window
pw generate
GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -o dist/myapp ./cmd/myapp

go build を握るイメージビルダー。 ko や Cloud Native Buildpacks は自分で コンパイルし、生成は代行してくれません。作業ツリーでこれを先に実行してから、 ビルダーを呼びます。

コンテナイメージではこのコマンドを Dockerfile.tinygo の中で使っています。そもそも Popcorn Web のビルドに ホストフェーズがある理由も、そちらにあります。

生成の範囲は用途ごとです。各用途は読んでよいディレクトリを列挙し、それ以外は 読みません。

[generate]
handlers = ["handlers"]
templates = ["handlers", "templates"]
queries = ["queries"]
config = ["cmd/myapp"]
pages = ["pages"]
dynamo = []
firestore = ["entities"]
用途 読むもの 生成するもの
handlers Go 中のルート登録、pw.Parse、レスポンス呼び出し リクエストバインディング、JSON コーデック、OpenAPI フラグメント
templates .pw.html 型付きレンダラ。ドキュメントシェルとエラーページを探す場所でもある
queries .pw.sql context ベースのクエリ関数
config Go 中の pw.RegisterConfig, pw.RegisterSubCommand 設定とサブコマンドのバインディング
pages ページツリーのルート 探索型ルーティングのルート登録とページパラメータ
dynamo dynamo タグ付き Go 型と .pw.dynamo レコードのコーデック、キー、型付き DynamoDB クエリ
firestore firestore タグ付き Go 型と .pw.firestore エンティティのコーデック、キー、Datastore mode の型付きクエリ

1 つのディレクトリが複数の用途に現れて構いません。ページテンプレートはそれを描画する ハンドラの隣に置くため、handlers は通常 handlerstemplates の両方に現れます。 列挙したディレクトリは再帰的に走査されるので、入れ子のパッケージを個別に書く必要は ありません。

以前からある handlerstemplatesqueriesconfig の 4 キーは必須で、既定値は ありません。pagesdynamofirestore は古いプロジェクトとの互換性のため省略できます。 それでも、その用途では何も生成しないという判断は空リストで明示するのが分かりやすい書き方です。

queries = [] # このプロジェクトに .pw.sql はない

この区別には意味があります。キーの書き忘れはエラーになり、[] は次に読む人に見える 判断として残ります。

自分の用途の外にあるソースはビルドを失敗させず、報告してスキップします。意図して置いた サンプルやフィクスチャがコードの隣にあっても構わないためです。

pw: samples/home.pw.html is outside generate.templates and is not generated from; list its directory to include it

Go のソースは報告しません。普通の Go コードはプロジェクト全体にあり、用途の外にある 呼び出し箇所は単にバインディングが生成されないだけだからです。以前のレイアウトが どの用途にも属さない場所に残した _pw_gen.go は報告します。もう再生成も削除も されないファイルだからです。

宣言ファイルに加えて、Go のソースから呼び出し箇所も読み取ります。

呼び出し 生成されるもの
pw.Parse[T] T のリクエストバインディング
pw.WriteAPI[T] T の JSON エンコーディング
pw.WriteStream[T] T のストリームエンコーディング
pw.RegisterConfig[T] T の設定バインディング
pw.RegisterSubCommand[T] T のサブコマンドのパース
pw.BadRequest などのエラーコンストラクタ ドキュメント化されるエラーレスポンス

同じ根拠から、パッケージごとに 1 つの OpenAPI 3.1 フラグメントも生成されます。 ビルド時に決定的にマージされるため、API 記述は別のアノテーション群ではなくコードに 追従します。

生成された Go はソースではなくビルド生成物です。

  • ファイル名は {ソース名}_pw_gen.go で、常にソースの隣に置かれる
  • スキャフォールドされた .gitignore によりバージョン管理から除外される
  • .vscode/settings.json によりエディタから隠される
  • アプリケーションのビルドのたびに作り直される

いつでも再生成できます。編集もコミットもしないでください。

pw generatedist/routes.json も書きます。アプリケーションが提供する全パターンが 1 つのファイルに入ります。Go で登録されたルート——それぞれが登録されたファイルと行付き ——と、ページツリーが生むルート。ページツリーとハンドラ登録が別々の場所に持っている URL 空間の、両方の半分です。

実行時には誰も読みません。ツールが解析をやり直さずに済むために存在します。 pw doctor が衝突するパターンを検査し、エディタ拡張が一覧に します。dist/manifest.json と同じくビルド生成物で、スキャフォールドされた .gitignore に含まれ、実行のたびに作り直されます。これより前に作られたプロジェクトには、無視行を 足す必要があります。

パターンがコンパイル時定数でない登録は読み取れないので、握り潰さずに未解決として記録 します。表を読むツールは、裏付けの取れないきれいなルート一覧を出すかわりに、その旨を 述べます。

エラーをテンプレートに向け直す

Section titled “エラーをテンプレートに向け直す”

生成ファイルは誰も書いていない出力なので、その中のエラーは、開いてもいなければ ブックマークもできないファイルを名指しします。popcornweb.tomlgenerate.line_directives がそれを直します。ジェネレータが Go の //line ディレクティブを書き、Go の位置情報を読むツールが一斉に追随します — コンパイラ、 go vet、デバッガ、gopls、そしてエディタ。

[generate]
line_directives = true

テンプレートの式の型エラーはこう出るようになります。

./queries/users.pw.sql:8: invalid operation: mismatched types untyped int and untyped string

users_pw_gen.go ではなく .pw.sql の行です。生成された .pw.sql 関数の中で panic した場合も、スタックフレームが .pw.sql を名指しします。

既定は無効で、無効のままにする理由が 2 つあります。

go test -cover と引き換えです。 有効にすると、カバレッジプロファイルは生成 ファイルのパスを保ったまま写像後の行番号を書くので、名指ししたファイルに存在しない行を 報告します。しかも go tool cover -html は文句を言わずに誤った行を塗り、exit 0 で 終わります。テンプレートの位置か、カバレッジか。両取りはできません。

.pw.html では半分だけです。 コンパイル時のエラーはどの方言でも写ります。実行時の スタックフレームが写るのは .pw.sql だけです。.pw.html はレンダープランにコンパイル され、それを共有ランタイムが歩くので、失敗したフレームは生成コードの中ではなくその ランタイムの中にあり、生成ファイルに付けたディレクティブでは動かせません。

この設定がフラグではなくプロジェクト単位なのは、生成出力が実行者によって変わっては ならないからです。pw check はツリーを新しい生成結果と 比較するので、あるマシンが渡してあるマシンが渡さないフラグがあると、全マシンで ドリフトが報告されます。

cmd/<name>/popcornweb_bootstrap_pw_gen.go は種類として例外です。ドキュメントシェルと 埋め込み公開アセットをバイナリにリンクするためのブランクインポートだけを含む生成 ファイルで、これによりハンドラがそれらを参照する必要がなくなります。どちらも存在しない 場合は自動的に削除されます。

dist/public は公開アセットが 1 つもないプロジェクトでも作られます。public.gogo:embed でそのディレクトリを名指ししており、コンパイラが読むのはツリーではなく ディレクティブだからです。

generate.api.cbor を有効にすると、生成されるすべてのバインダとライタが JSON に加えて application/cbor のリクエスト・レスポンスボディをネゴシエートするようになります。

[generate.api.cbor]
enabled = true

これが生成時・プロジェクト単位の設定なのは line_directives と同じ理由です。どの メディアタイプを受け付けるかはサービスの属性であり、出力が実行者によって変わっては なりません。隣に置ける 2 つのプロファイルキー reject_floatssorted_keys は生成 フィンガープリントの一部なので、どちらかを変えるとコーデックが再生成され、コミット するまで pw check に現れます。ワイヤ上で何が変わるか、 いつ切っておくべきかは CBOR ガイドが主題として扱います。

プロジェクトに document.pw.html はちょうど 1 つです。ツリーのどこかに 2 つ以上あると 次のように失敗します。

pw: multiple default documents: templates/document.pw.html, admin/document.pw.html

別のシェルは名前なしスロットを持つ通常のエクスポート済みコンポーネントとして書き、 ハンドラごとに pw.WriteHTMLChain で選択してください。 テンプレートを参照。

コンポーネントパッケージにはエントリポイントも public.go もドキュメントシェルもないので、手順 2 から 4 には対象がなく、生成された Go を書いたところでコマンドは終わります。--code-only と同じ結果ですが、選ぶのは project.kind であって、作者が覚えておくべきフラグではありません。

ビルド系のコマンドでパッケージプロジェクトが受け付けるのはこれだけです。 pw buildpw dev はどちらも 拒否します。動かすものがないからです。

生成コードが最新かを検証してから、生成してコンパイルします。

Terminal window
pw check
pw generate
go build ./cmd/myapp

pw check は何も書かず、古い出力があれば失敗します。 生成された Go は Git 管理外なので、リポジトリの差分では気づけません。

pw devpw build はどちらも 最初に生成します。直接呼ぶのは、コンパイルを自分で回すときです。