Migrer vers SvelteKit v2
Mettre à jour Sveltekit de la version 1 à la version 2 devrait en théorie se passer globalement
bien. Il y a quelques breaking changes à connaître, qui sont listés ci-dessous. Vous pouvez utiliser
npx sv migrate sveltekit-2
pour migrer certains de ces changements automatiquement.
Nous recommandons vivement de mettre à jour d’abord vers la dernière version en 1.x avant de passer à 2.0, afin de profiter des avertissements de dépréciation ciblés. Nous recommandons également de d’abord mettre à jour Svelte vers la version 4 : les dernières versions de SvelteKit 1.x le supportent, et SvelteKit 2.0 le nécessite.
Vous ne devez plus jeter manuellement redirect et error
Auparavant, vous deviez throw
les valeurs renvoyées par error(...)
et redirect(...)
vous-même.
Avec SvelteKit 2 ce n’est plus le cas — appeler les fonctions en question est suffisant.
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, "quelque chose s'est mal passé");
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.
svelte-migrate
fera ces changements automatiquement pour vous.
Si l’erreur ou la redirection est jetée dans un bloc try {...}
(indice : ne faites pas ça !), vous
pouvez les différencier des erreurs inattendues en utilisant les utilitaires
isHttpError
et isRedirect
importés
depuis @sveltejs/kit
.
path est requis lorsque vous définissez des cookies
Lorsque vous recevez un en-tête Set-Cookie
qui ne précise pas de path
, les navigateurs vont
définir le chemin du cookie en tant que
parent de la ressource en question. Ce comportement n’est pas particulièrement utile ni intuitif, et
entraîne souvent des bugs car le ou la développeuse s’attendait à ce que le cookie s’applique au
domaine en entier.
Avec SvelteKit 2.0, vous avez besoin de définir un path
lorsque vous appelez cookies.set(...)
,
cookies.delete(...)
ou cookies.serialize(...)
afin qu’il n’y ait pas d’ambiguïté. La plupart du
temps, vous voudrez probablement utiliser path: '/'
, mais vous pouvez le définir à la valeur qui
vous arrange, incluant des chemins relatifs — ''
signifiant “le chemin actuel”, '.'
signifiant
"le dossier actuel”.
/** @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
ajoutera des commentaires mettant en valeur les endroits qui ont besoin
d’ajustements.
Les promesses à la racine ne sont attendues
Avec SvelteKit version 1, si les propriétés à la racine de l’objet renvoyé par une fonction load
étaient des promesses, elles étaient automatiquement attendues avec await
. Avec l’introduction du
streaming ce comportement était devenu un peu bizarre car il
vous forçait à imbriquer les données que vouliez streamer à un niveau plus profond dans l’objet.
Avec la version 2, SvelteKit ne fait plus aucune différence entre les promesses à la racine et les
autres. Pour récupérer le comportement bloquant, utilisez await
explicitement (avec Promise.all
pour éviter les cascades, lorsque pertinent) :
// Si vous avez une seule promesse
/** @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 }
}
// SI vous avez plusieurs promesses
/** @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 };
}
Changements impactant goto(...)
goto(...)
n’accepte plus les URLs externes. Pour naviguer vers une URL externe, utilisez
window.location.href = url
. L’objet state
détermine désormais $page.state
et doit adhérer à
l’interface App.PageState
, si déclarée. Voir la section sur le shallow routing
pour plus d’informations.
Les chemins sont désormais relatifs par défaut
Avec SvelteKit 1, les occurences de %sveltekit.assets%
dans votre fichier app.html
étaient
remplacées par un chemin relatif par défaut (c-à-d .
ou ..
ou ../..
etc, en fonction du chemin
en train d’être rendu) lors du rendu côté serveur à moins que l’option de configuration
paths.relative
n’ait été explicitement définie à false
. Le même
comportement s’appliquait pour base
et assets
importés depuis $app/paths
, mais seulement si
l’option paths.relative
avait été définie à true
.
Cette incohérence est corrigée dans la version 2. Les chemins sont soit toujours relatifs, soit
toujours absolus, en fonction de la valeur de paths.relative
. Cette option
vaut true
par défaut car cela permet des applications plus portables : si la base
vaut autre
chose que ce que l’application attend (comme c’est le cas lorsqu’on visite l’application depuis
l’Internet Archive, par exemple), ou une valeur inconnue au moment de la
compilation (comme c’est le cas lorque vous déployez sur l’IPFS entre autres),
il y a alors moins de risques de casser des choses.
Les requêtes depuis le serveur ne sont plus traçables
Auparavant il était possible de tracer les URLs depuis les requêtes fetch
venant du serveur pour
permettre de ré-exécuter les fonctions load
. Ceci posait un problème de sécurité potentiel (faire
fuiter les URLs privées), et en conséquence ce comportement était activable avec le paramètre
dangerZone.trackServerFetches
, qui n’existe plus avec la version 2.
Les arguments de preloadCode doivent être préfixés avec base
SvelteKit expose deux fonctions, preloadCode
et
preloadData
, permettant de programmatiquement charger le code et
les données associées à un chemin particulier. Avec la version 1, il y avait une incohérence
subtile — le chemin passé à preloadCode
n’avait pas besoin d’être préfixé avec le chemin base
(si défini), alors que c’était le cas pour le chemin passé à preloadData
.
Ceci est corrigé avec SvelteKit 2 — dans les deux cas, le chemin devrait être préfixé avec base
si
cette valeur est définie.
De plus, preloadCode
prend désormais un seul argument plutôt n.
resolvePath a été supprimée
SvelteKit 1 possédait une fonction appelée resolvePath
vous permettant de résoudre un ID de route
(comme /blog/[slug]
) et un ensemble de paramètres (comme { slug: 'hello' }
) en tant que chemin.
Malheureusement la valeur de retour n’incluait pas le chemin base
, limitant son utilité dans les
situations où la valeur base
était définie.
En conséquence, SvelteKit 2 remplace resolvePath
par une fonction (un tout petit peu mieux nommée)
appelée resolveRoute
, qui est importée depuis $app/paths
et prend base
en compte.
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
remplacera la méthode pour vous ; toutefois, si vous préfixiez dans un second temps
le résultat avec base
, vous devrez supprimer ces préfixes vous-même.
Meilleure gestion des erreurs
SvelteKit 1 gère les erreurs de manière inconsistante. Certaines erreurs déclenchent le hook
handleError
mais il n’y aucun moyen acceptable de discriminer leur statut (par exemple, le seul
moyen de distinguer une 404 d’une 500 est de vérifier si event.route.id
est null
), tandis que
d’autres erreurs (comme les 405 des requêtes POST
vers des pages sans action) ne déclenchent pas
du tout handleError
, alors que cela devrait être le cas. Dans ce dernier cas, l’objet
$page.error
en résultant ne va pas correspondre au type App.Error
, si défini.
SvelteKit 2 règle ces problèmes en appelant le hook handleError
avec deux nouvelles propriétés :
status
et message
. Pour les erreurs jetées depuis votre code (ou depuis du code de librairie
exécuté depuis votre code), le statut sera 500
et le message sera Internal Error
. Alors que
error.message
peut éventuellement contenir des informations sensibles ne devant pas être exposées
aux utilisateurs et utilisatrices, message
ne pose pas de problème.
Les variables d’environnement dynamiques ne peuvent pas être utilisées lors du pré-rendu
Les modules $env/dynamic/public
et $env/dynamic/private
fournissent un accès aux variables
d’environnement d’exécution, tandis que les variables d’environnement de compilation sont
exposées par $env/static/public
et $env/static/private
.
Lors du pré-rendu avec SvelteKit 1, ces deux types de variables d’environnement sont les mêmes. En
conséquence, les pages pré-rendues utilisant les variables d’environnement “dynamiques” sont en
réalité des values intégrées lors de la compilation, ce qui est incorrect. Pire encore,
$env/dynamic/public
est rempli dans le navigateur avec ces valeurs périmées s’il se trouve que
l’utilisateur ou l’utilisatrice se trouve sur une page pré-rendue avant de naviguer vers des pages
rendues dynamiquement.
À cause de ce problème, les variables d’environnement dynamiques ne peuvent plus être lues lors du
pré-rendu avec SvelteKit 2 — vous devriez plutôt utiliser les modules static
. Si l’utilisateur ou
l’utilisatrice arrive sur une page pré-rendue, SvelteKit va requêter au serveur des valeurs à jour
pour $env/dynamic/public
(depuis un module /_app/env.js
, par défaut) plutôt que de les lire
depuis le HTML généré par le serveur.
form et data ont été supprimées des callbacks use:enhance
Si vous fournissez un callback à use:enhance
,
celui-ci sera exécuté avec un objet contenant différentes propriétés pratiques.
Avec SvelteKit 1, ces propriétés incluaient form
et data
. Ces dernières ont été dépréciées il y
a quelques temps en faveur de formElement
et formData
, et ont été supprimées complètement de
SvelteKit 2.
Les formulaires contenant des inputs de fichiers doivent utiliser multipart/form-data
Si un formulaire contient un élément <input type="file">
mais n’a pas pas d’attribut
enctype="multipart/form-data"
, les soumissions non faites avec JavaScript vont omettre le fichier.
SvelteKit 2 va jeter une erreur s’il rencontre un formulaire comme celui-là lors d’une soumission
use:enhance
pour assurer que vos formulaires fonctionnent correctement lorsque JavaScript n’est
pas disponible.
Le fichier tsconfig généré est plus strict
Auparavant, le fichier tsconfig.json
généré essayait au mieux de produire une configuration plus ou
moins valide lorsque votre tsconfig.json
incluait paths
ou baseUrl
. Avec SvelteKit 2, la
validation est plus stricte et vous avertira lorsque si vous utilisez paths
ou baseUrl
dans
votre tsconfig.json
. Ces paramètres sont utilisés pour générer des alias de chemin, et vous
devriez plutôt utiliser l’option de configuration alias
de votre fichier
svelte.config.js
, pour créer également un alias pour le bundler.
getRequest ne jette plus d’erreurs
Le module @sveltejs/kit/node
exporte des fonctions utilitaires à utiliser dans les environnements
Node, parmi lesquelles getRequest
, qui transforme un objet Node
ClientRequest
en un objet
Request
standard.
Avec SvelteKit 1, getRequest
pouvait jeter une erreur si l’en-tête Content-Length
dépassait la
limite de taille spécifiée. Avec SvelteKit 2, l’erreur ne sera pas jetée avant que le corps de la
requête (s’il existe) soit lu. Ceci permet d’obtenir de meilleurs diagnostics et du code plus
simple.
vitePreprocess n’est plus exporté depuis @sveltejs/kit/vite
Puisque @sveltejs/vite-plugin-svelte
est désormais une peer dependency, SvelteKit 2 ne ré-exporte
plus vitePreprocess
. Vous devriez l’importer directement depuis @sveltejs/vite-plugin-svelte
.
Dépendances requises mises à jour
SvelteKit 2 requiert Node 18.13
ou supérieur, et les versions de dépendances minimum suivantes :
svelte@4
vite@5
typescript@5
@sveltejs/vite-plugin-svelte@3
(cette dépendance doit désormais être unepeerDependency
de SvelteKit — c’était auparavant une dépendance directe)@sveltejs/adapter-cloudflare@3
(si vous utilisez ces adaptateurs)@sveltejs/adapter-cloudflare-workers@2
@sveltejs/adapter-netlify@3
@sveltejs/adapter-node@2
@sveltejs/adapter-static@3
@sveltejs/adapter-vercel@4
svelte-migrate
mettra à jour votre fichier package.json
pour vous.
Une conséquence de la mise à jour de TypeScript est que le fichier tsconfig.json
généré (celui que
votre propre tsconfig.json
étend) utilise désormais "moduleResolution": "bundler"
(ce qui est
recommandé par l’équipe TypeScript, puisque cela permet de résoudre les types de paquets avec une
map exports
dans votre fichier package.json
) et verbatimModuleSyntax
(qui remplace les options
existantes importsNotUsedAsValues
et preserveValueImports
— si vous les avez incluses dans votre
tsconfig.json
, supprimez-les. svelte-migrate
le fera pour vous).
SvelteKit 2.12 : $app/stores déprécié
SvelteKit 2.12 a apporté le module $app/state
basé sur l’API de runes de Svelte
5. $app/state
fournit tout ce que fournit $app/stores
mais avec
plus de flexibilité quant à où et quand vous vous en servez. Plus important encore, l’objet page
est désormais finement réactif, ce qui implique par ex. que les mises à jour de page.state
ne vont
pas invalider page.data
et vice-versa.
En conséquence, $app/stores
est déprécié et sera certainement supprimé dans SvelteKit 3. Nous
recommandons de vous mettre à jour vers Svelte 5, si vous ne
l’avez pas déjà fait, puis de migrer pour remplacer vos occurrences de $app/stores
. La plupart des
changements devraient être plutôt simples : remplacez l’import de $app/stores
par $app/state
et
supprimez les préfixes $
des endroits concernés.
<script>
import { page } from '$app/stores';
import { page } from '$app/state';
</script>
{$page.data}
{page.data}
Utilisez npx sv migrate app-state
pour migrer automatiquement la plupart de vos occurrences de
$app/stores
présentes dans vos composants .svelte
.
Modifier cette page sur Github llms.txt