SvelteKit v2への移行
SvelteKitバージョン1からバージョン2へのアップグレードは、ほとんどシームレスに行われるはずです。ただし、いくつかの破壊的変更があることに注意してください。それらはここにリストされています。`npx sv migrate sveltekit-2`を使用して、これらの変更の一部を自動的に移行することができます。
ターゲットを絞った非推奨警告を利用できるように、バージョン2.0にアップグレードする前に、最新の1.xバージョンにアップグレードすることを強くお勧めします。また、Svelte 4へのアップデートを先に実行することをお勧めします。SvelteKit 1.xのそれ以降のバージョンはSvelte 4をサポートしており、SvelteKit 2.0ではSvelte 4が必要です。
redirectとerrorは、もはやユーザーがスローしません
以前は、`error(...)`と`redirect(...)`から返された値を自分で`throw`する必要がありました。SvelteKit 2では、これは不要になり、関数を呼び出すだけで十分です。
import { function error(status: number, body: App.Error): never (+1 overload)
Throws an error with a HTTP status code and an optional message.
When called during request handling, this will cause SvelteKit to
return an error response without invoking handleError
.
Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.
error } from '@sveltejs/kit'
// ...
throw error(500, 'something went wrong');
function error(status: number, body?: {
message: string;
} extends App.Error ? App.Error | string | undefined : never): never (+1 overload)
Throws an error with a HTTP status code and an optional message.
When called during request handling, this will cause SvelteKit to
return an error response without invoking handleError
.
Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.
error(500, 'something went wrong');
`svelte-migrate`がこれらの変更を自動的に行います。
エラーまたはリダイレクトが`try {...}`ブロック内でスローされた場合(ヒント:これを行わないでください!)、`isHttpError`と`isRedirect`(`@sveltejs/kit`からインポート)を使用して、予期しないエラーと区別することができます。
pathはCookieを設定する際に必須です
`path`を指定しない`Set-Cookie`ヘッダーを受け取ると、ブラウザはCookieのパスを対象リソースの親に設定します。この動作は特に役立つものではなく、直感的ではなく、開発者がCookieがドメイン全体に適用されると期待していたため、バグが発生することがよくあります。
SvelteKit 2.0以降、`cookies.set(...)`、`cookies.delete(...)`、`cookies.serialize(...)`を呼び出す際に`path`を設定する必要があります。あいまいさがなくなります。ほとんどの場合、`path: '/'`を使用する必要があるでしょうが、相対パスを含む好きなように設定できます。`''`は「現在のパス」、`'.'`は「現在のディレクトリ」を意味します。
/** @type {import('./$types').PageServerLoad} */
export function function load({ cookies }: {
cookies: any;
}): {
response: any;
}
load({ cookies: any
cookies }) {
cookies: any
cookies.set(const name: void
name, value, { path: string
path: '/' });
return { response: any
response }
}
`svelte-migrate`は、調整が必要な場所を強調表示するコメントを追加します。
トップレベルのプロミスはもはやawaitされません
SvelteKitバージョン1では、`load`関数の戻り値オブジェクトのトップレベルのプロパティがプロミスだった場合、自動的にawaitされました。ストリーミングの導入により、この動作はやや扱いにくくなりました。ストリーミングされたデータを1レベル深くネストする必要があるためです。
バージョン2以降、SvelteKitはトップレベルのプロミスとトップレベル以外プロミスを区別しなくなりました。ブロッキング動作を戻すには、`await`を使用します(適切な場合は、ウォーターフォールを防ぐために`Promise.all`を使用します)。
// If you have a single promise
/** @type {import('./$types').PageServerLoad} */
export async function function load(event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>): MaybePromise<void | Record<string, any>>
load({ fetch: {
(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>;
}
fetch
is equivalent to the native fetch
web API, with a few additional features:
- It can be used to make credentialed requests on the server, as it inherits the
cookie
and authorization
headers for the page request.
- It can make relative requests on the server (ordinarily,
fetch
requires a URL with an origin when used in a server context).
- Internal requests (e.g. for
+server.js
routes) go directly to the handler function when running on the server, without the overhead of an HTTP call.
- During server-side rendering, the response will be captured and inlined into the rendered HTML by hooking into the
text
and json
methods of the Response
object. Note that headers will not be serialized, unless explicitly included via filterSerializedResponseHeaders
- During hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request.
You can learn more about making credentialed requests with cookies here
fetch }) {
const const response: any
response = await fetch: (input: string | URL | globalThis.Request, init?: RequestInit) => Promise<Response> (+1 overload)
fetch(const url: string
url).Promise<Response>.then<any, never>(onfulfilled?: ((value: Response) => any) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<any>
Attaches callbacks for the resolution and/or rejection of the Promise.
then(r: Response
r => r: Response
r.Body.json(): Promise<any>
json());
return { response: any
response }
}
// If you have multiple promises
/** @type {import('./$types').PageServerLoad} */
export async function function load(event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>): MaybePromise<void | Record<string, any>>
load({ fetch: {
(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>;
}
fetch
is equivalent to the native fetch
web API, with a few additional features:
- It can be used to make credentialed requests on the server, as it inherits the
cookie
and authorization
headers for the page request.
- It can make relative requests on the server (ordinarily,
fetch
requires a URL with an origin when used in a server context).
- Internal requests (e.g. for
+server.js
routes) go directly to the handler function when running on the server, without the overhead of an HTTP call.
- During server-side rendering, the response will be captured and inlined into the rendered HTML by hooking into the
text
and json
methods of the Response
object. Note that headers will not be serialized, unless explicitly included via filterSerializedResponseHeaders
- During hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request.
You can learn more about making credentialed requests with cookies here
fetch }) {
const a = fetch(url1).then(r => r.json());
const b = fetch(url2).then(r => r.json());
const [const a: any
a, const b: any
b] = await var Promise: PromiseConstructor
Represents the completion of an asynchronous operation
Promise.PromiseConstructor.all<[Promise<any>, Promise<any>]>(values: [Promise<any>, Promise<any>]): Promise<[any, any]> (+1 overload)
Creates a Promise that is resolved with an array of results when all of the provided Promises
resolve, or rejected when any Promise is rejected.
all([
fetch: (input: string | URL | globalThis.Request, init?: RequestInit) => Promise<Response> (+1 overload)
fetch(const url1: string
url1).Promise<Response>.then<any, never>(onfulfilled?: ((value: Response) => any) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<any>
Attaches callbacks for the resolution and/or rejection of the Promise.
then(r: Response
r => r: Response
r.Body.json(): Promise<any>
json()),
fetch: (input: string | URL | globalThis.Request, init?: RequestInit) => Promise<Response> (+1 overload)
fetch(const url2: string
url2).Promise<Response>.then<any, never>(onfulfilled?: ((value: Response) => any) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<any>
Attaches callbacks for the resolution and/or rejection of the Promise.
then(r: Response
r => r: Response
r.Body.json(): Promise<any>
json()),
]);
return { a: any
a, b: any
b };
}
goto(...)の変更
`goto(...)`は外部URLを受け付けなくなりました。外部URLに移動するには、`window.location.href = url`を使用します。`state`オブジェクトは現在`$page.state`を決定し、宣言されている場合、`App.PageState`インターフェースに準拠する必要があります。シャロールーティングの詳細を参照してください。
パスはデフォルトで相対パスになりました
SvelteKit 1では、`app.html`内の`%sveltekit.assets%`は、`paths.relative`設定オプションが明示的に`false`に設定されていない限り、サーバーサイドレンダリング中にデフォルトで相対パス(つまり、`.`、`..`、`../..`など、レンダリングされるパスに応じて)に置き換えられました。`$app/paths`からインポートされた`base`と`assets`についても同様でしたが、`paths.relative`オプションが明示的に`true`に設定されている場合のみでした。
この矛盾はバージョン2で修正されました。パスは、`paths.relative`の値に応じて、常に相対パスか絶対パスのいずれかになります。これは`true`をデフォルトとしています。これは、より移植性の高いアプリにつながるためです。`base`がアプリが期待するものとは異なる場合(たとえば、インターネットアーカイブで表示する場合)、またはビルド時に不明な場合(IPFSなどにデプロイする場合)、壊れる可能性のあるものが少なくなります。
サーバーフェッチは追跡できなくなりました
以前は、load関数を再実行するために、サーバー上の`fetch`からのURLを追跡することができました。これは潜在的なセキュリティリスク(プライベートURLの漏洩)をもたらすため、`dangerZone.trackServerFetches`設定の背後にあるものでしたが、現在は削除されています。
preloadCodeの引数は`base`で始める必要があります
SvelteKitは、特定のパスに関連付けられたコードとデータのプログラムによる読み込みのために、`preloadCode`と`preloadData`という2つの関数を公開しています。バージョン1では、微妙な矛盾がありました。`preloadCode`に渡されたパスは、`base`パス(設定されている場合)で始める必要はありませんでしたが、`preloadData`に渡されたパスは始める必要がありました。
これはSvelteKit 2で修正されました。どちらの場合も、設定されている場合は、パスを`base`で始める必要があります。
さらに、`preloadCode`は、*n*個の引数ではなく、1つの引数を取るようになりました。
resolvePathは削除されました
SvelteKit 1には、ルートID(` /blog/[slug]`など)とパラメータのセット(`{ slug: 'hello' }`など)をパス名に解決できる`resolvePath`という関数が含まれていました。残念ながら、戻り値には`base`パスが含まれていなかったため、`base`が設定されている場合の有用性が制限されていました。
そのため、SvelteKit 2では`resolvePath`を(少し名前が良くなった)`resolveRoute`関数に置き換えました。これは`$app/paths`からインポートされ、`base`を考慮します。
import { resolvePath } from '@sveltejs/kit';
import { base } from '$app/paths';
import { function resolveRoute(id: string, params: Record<string, string | undefined>): string
Populate a route ID with params to resolve a pathname.
resolveRoute } from '$app/paths';
const path = base + resolvePath('/blog/[slug]', { slug });
const const path: string
path = function resolveRoute(id: string, params: Record<string, string | undefined>): string
Populate a route ID with params to resolve a pathname.
resolveRoute('/blog/[slug]', { slug: any
slug });
`svelte-migrate`はメソッドの置換を自動的に行いますが、後で結果に`base`をプリペンドする場合は、それを自分で削除する必要があります。
エラー処理の改善
SvelteKit 1では、エラーは一貫性のない方法で処理されます。一部のエラーは`handleError`フックをトリガーしますが、そのステータスを判別する良い方法はありません(たとえば、404と500を区別する唯一の方法は、`event.route.id`が`null`かどうかを確認することです)。一方、他のエラー(`POST`リクエストに対してアクションのないページへの405エラーなど)は、`handleError`をまったくトリガーしません。しかし、トリガーする必要があります。後者の場合、結果の`$page.error`は、指定されている場合、`App.Error`型とは異なります。
SvelteKit 2は、2つの新しいプロパティである`status`と`message`を使用して`handleError`フックを呼び出すことで、これをクリーンアップします。ユーザーのコード(またはユーザーのコードによって呼び出されるライブラリコード)からスローされたエラーの場合、ステータスは`500`になり、メッセージは`Internal Error`になります。`error.message`には、ユーザーに公開すべきではない機密情報が含まれる可能性がありますが、`message`は安全です。
動的環境変数はプリレンダリング中に使用できません
`$env/dynamic/public`と`$env/dynamic/private`モジュールは、`$env/static/public`と`$env/static/private`によって公開されるビルド時の環境変数とは対照的に、実行時の環境変数へのアクセスを提供します。
SvelteKit 1のプリレンダリング中、これらは同じものです。「動的」環境変数を使用するプリレンダリングされたページは、実際にはビルド時の値を「焼き込んでいる」ことになり、これは正しくありません。さらに悪いことに、ユーザーが動的にレンダリングされたページに移動する前にプリレンダリングされたページにアクセスした場合、`$env/dynamic/public`はこれらの古い値でブラウザに設定されます。
このため、SvelteKit 2では、動的環境変数はプリレンダリング中に読み取ることができなくなりました。代わりに`static`モジュールを使用する必要があります。ユーザーがプリレンダリングされたページにアクセスした場合、SvelteKitはサーバーから`$env/dynamic/public`の最新値を要求します(デフォルトでは`_env.js`というモジュールから。これは`config.kit.env.publicModule`で設定できます)。サーバーレンダリングされたHTMLから読み取るのではなく。
formとdataはuse:enhanceコールバックから削除されました
`use:enhance`にコールバックを提供する場合、さまざまな便利なプロパティを含むオブジェクトを使用して呼び出されます。
SvelteKit 1では、`form`と`data`プロパティが含まれていました。これらは以前、`formElement`と`formData`に非推奨となり、SvelteKit 2では完全に削除されました。
ファイル入力を含むフォームはmultipart/form-dataを使用する必要があります
フォームに``が含まれているが、`enctype="multipart/form-data"`属性がない場合、JavaScriptを使用しない送信ではファイルが省略されます。SvelteKit 2では、`use:enhance`送信中にこのようなフォームが検出されるとエラーがスローされ、JavaScriptを使用しない場合でもフォームが正しく動作するように保証されます。
生成されるtsconfig.jsonはより厳格になりました
以前は、生成された`tsconfig.json`は、`tsconfig.json`に`paths`または`baseUrl`が含まれている場合でも、ある程度有効な設定を生成しようとしました。SvelteKit 2では、検証がより厳格になり、`tsconfig.json`で`paths`または`baseUrl`を使用すると警告が表示されます。これらの設定はパスエイリアスの生成に使用され、バンドラに対応するエイリアスを作成するためにも、代わりに`svelte.config.js`の`alias`設定オプションを使用する必要があります。
getRequestはエラーをスローしなくなりました
`@sveltejs/kit/node`モジュールは、Node環境で使用するためのヘルパー関数をエクスポートしており、その中にはNode `ClientRequest`を標準的な`Request`オブジェクトに変換する`getRequest`が含まれています。
SvelteKit 1では、`Content-Length`ヘッダーが指定されたサイズ制限を超えた場合、`getRequest`はエラーをスローすることがありました。SvelteKit 2では、リクエストボディ(存在する場合)の読み取り時までエラーはスローされません。これにより、より優れた診断と簡素化されたコードが可能になります。
vitePreprocessは@sveltejs/kit/viteからエクスポートされなくなりました
`@sveltejs/vite-plugin-svelte`がピア依存関係になったため、SvelteKit 2では`vitePreprocess`を再エクスポートしなくなりました。`@sveltejs/vite-plugin-svelte`から直接インポートする必要があります。
依存関係の要件が更新されました
SvelteKit 2では、Node `18.13`以降、および以下の最小依存関係バージョンが必要です。
svelte@4
vite@5
typescript@5
- `@sveltejs/vite-plugin-svelte@3` (これは、SvelteKitの`peerDependency`として必要になりました—以前は直接依存していました)
- `@sveltejs/adapter-cloudflare@3` (これらのアダプターを使用している場合)
@sveltejs/adapter-cloudflare-workers@2
@sveltejs/adapter-netlify@3
@sveltejs/adapter-node@2
@sveltejs/adapter-static@3
@sveltejs/adapter-vercel@4
`svelte-migrate`が`package.json`を更新します。
TypeScriptのアップグレードの一環として、生成された`tsconfig.json`(`tsconfig.json`が拡張するファイル)は、` "moduleResolution": "bundler"`(TypeScriptチームが推奨するもので、`package.json`に`exports`マップを持つパッケージから型を適切に解決します)と`verbatimModuleSyntax`(既存の`importsNotUsedAsValues`と`preserveValueImports`フラグを置き換えます—`tsconfig.json`にそれらがある場合は削除してください。`svelte-migrate`がこれを行います)を使用するようになりました。