Closed13

SvelteKitのソースコードを読んでForm Actionsの処理を浅く把握する

kazokmrkazokmr

Severクラス。これをインスタンス化する。

https://github.com/sveltejs/kit/blob/master/packages/kit/src/runtime/server/index.js

例えば、vite dev コマンドを実行した場合はこちらのプログラム経由でserverインスタンスが起動する

https://github.com/sveltejs/kit/blob/master/packages/kit/src/exports/vite/dev/index.js

さらに遡ると vite コマンドを実行すると 下記が呼び出され vite と sveltekit のconfigをロードし、さらに dev によって上記のファイルが呼び出されている

https://github.com/sveltejs/kit/blob/master/packages/kit/src/exports/vite/index.js

kazokmrkazokmr

vite devでServerを起動するときに SvelteKitのRouterのパス群を探索し、manifestの routesプロパティにセットする。

https://github.com/sveltejs/kit/blob/master/packages/kit/src/core/sync/create_manifest_data/index.js

標準的な処理としては、svelteの拡張子 .svelte と モジュールの拡張子 .ts | .js ファイルを検出し、さらに +page.svelte, +layout.svelte, +error.svelte および +server.ts(js) , +page.server.ts(js), +page.ts(js) を検出する。
その上で、.svelte は component route として収集、.ts | .js は server route または universal route として収集する

探索するパスのルートディレクトリは、svelte.config.js の kit.files.routes で設定する。
未設定の場合のデフォルトは src/routesが指定される

https://kit.svelte.dev/docs/configuration#files

kazokmrkazokmr

route情報の登録では まずComponentかどうか(.svelte ページ)を判定する。Componentは名称で error / layout / leaf (pageのこと?) に分類し自分の階層、相対PATHなどの情報をもつ。layout や leaf の場合は親となる layoutのidなども登録する
componentではなくモジュールの場合(.ts|.js) も同様で layout / leaf / endpoint に分離し、相対PATHを管理する
この処理を routesディレクトリ内のディレクトリとファイルに対して再帰的に実施する。

全てのファイルの設定が終わったあと、 layoutモジュールとerrorモジュールについては対応する componentのパスを登録する (pageモジュールについては行っていない)

また leafについては pageと判定し、関連するlayoutの情報を格納する

kazokmrkazokmr

この後、vite/dev/index.js に戻った後、rootの情報をベースに component, server, universal の区分を元にファイルをロードして Node情報として Manifestに追加する

kazokmrkazokmr

リクエストを受信すると、severインスタンスのrepond関数が呼ばれる。その中でさらに repond関数を呼ぶ

https://github.com/sveltejs/kit/blob/master/packages/kit/src/runtime/server/respond.js

respond関数では リクエストパスを元にRequest Eventと route情報を取得し、resolve関数を呼ぶ。
resolve関数では、routeが pageなら、pageモジュールの render_page関数を実行する

https://github.com/sveltejs/kit/blob/master/packages/kit/src/runtime/server/page/index.js

routeがendpointなら endpointモジュールの render_endpoint関数を実行する

https://github.com/sveltejs/kit/blob/master/packages/kit/src/runtime/server/endpoint.js

以後は、render_page関数を確認する

kazokmrkazokmr

上記以外に リクエストパスの最後が /__data.json の場合に呼び出される render_data関数があるのだが、このようなリクエストが実行される条件が不明なのだが、処理としては +page.ts の load関数を呼び出しているようにも見える。
また、 render_page関数は 同 actions関数を呼び出して処理をしているようにも見えるので、リクエストを呼び出した時点で 何らかの振り分けが行われている可能性がある

kazokmrkazokmr

render_page関数ではまず、request メソッドが POST の場合URLに対応する action処理を実行し、ActionResultを取得する。
画面を表示するだけの時のメソッドは GETになるのでアクション処理は呼ばれず、次のデータロードが実行される

https://github.com/sveltejs/kit/blob/master/packages/kit/src/runtime/server/page/index.js

データロードでは、まずリクエストのパスの最後に /__data.json をつけ、load_server_data関数と load_data関数を実行し画面の表示に必要なデータを取得する。

どちらの関数でデータを取得するかは モジュールが sever か universal かで異なる。

https://github.com/sveltejs/kit/blob/master/packages/kit/src/runtime/server/page/load_data.js

この後、render_response で、取得したデータおよび action実行時は actionResultも含めページのレンダリングを実行する

kazokmrkazokmr

アクション処理で返される情報(actionResult)はrender_responseの中で、form_valueとしてdeseriarizeして埋め込まれている模様

kazokmrkazokmr

render関数の戻り値は、Response型で content-typeは "text/html" なのでhtmlが返される。
これが正しいとするなら、クライアント側でJavascriptが有効で use:enhanceを設定していると レスポンスのhtmlをそのまま使わずに何らかの解釈をしてページを書き換えているような動きだと推測する。

kazokmrkazokmr

ActionResultは、Response の text (bodyの内容をtext形式で表現したもの)を JSON形式でparseしている

export function deserialize(result) {
	const parsed = JSON.parse(result);
	if (parsed.data) {
		parsed.data = devalue.parse(parsed.data);
	}
	return parsed;
}
kazokmrkazokmr

formsモジュールの enhance関数を見てみる

https://github.com/sveltejs/kit/blob/master/packages/kit/src/runtime/app/forms.js

formタグに use:enhance をつけている場合、submitイベントをこの関数で実行するようになっている。
この関数内では、actionに対してfetchで次のようなリクエストを送信している

const response = await fetch(action, {
  method: 'POST',
  headers: {
    accept: 'application/json',
    'x-sveltekit-action': 'true'
  },
  cache: 'no-store',
  body: form_data,
  signal: controller.signal
});

headerに accept: application/json がついているため、 server側の page.indexでは、is_action_json_request と判定され、handle_action_json_request 関数でJsonオブジェクトを返すだけでレンダリングは行われていない模様

https://github.com/sveltejs/kit/blob/master/packages/kit/src/runtime/server/page/actions.js

このときに返すJSONは

return action_json({
  type: 'success',
  status: data ? 200 : 204,
  // @ts-expect-error see comment above
  data: stringify_action_response(data, /** @type {string} */ (event.route.id))
});

stringify_action_response() では、Jsonオブジェクトである dataに対して devalue.strigify(data) を実施している。

kazokmrkazokmr

SvelteKit-Superforms の enhance は 前述の$apps/formsのenhanceを呼び出す時に 第2引数の submit関数に独自の処理を追加し、そのコールバック関数として validationResponse関数を呼び出してイベント処理と applyActionを実行している

https://superforms.rocks/

このスクラップは2023/06/15にクローズされました