Simple notes from Migrating to PlusKit
swyx
“PlusKit” is my affectionate name for the Great SvelteKit Redesign of 2022. I was a little apprehensive at the amount of work and learning curve there would be, but it took about 1 hour all told (for admittedly a very simple sveltekit app like swyxkit).
You can see the final PR here: sw-yx#88/
What follows are just raw notes for “simple” sveltekit users like me. I want to stress that this is an incomplete list - however, I guess that if a “simple” usecase required these refactors, then they are the essential ones that most people will run into.
Run the migrate script
npx svelte-migrate routes
This is pretty straightforward - the migration script moves your code around and sometimes indiscriminately - in some cases you may have to move back some code (by uncommenting it back in) and in others you may have to refactor return signatures and imports. I found all the @migration
hints to give enough warning - I solely looked at the @migration
hints and that was sufficient.
Follow the instructions, but you’ll want to upgrade your sveltekit version as well: npm i @sveltejs/kit@1.0.0-next.423
.
The 4 kinds of refactors
Static imports
Since we no longer have <script context="module">
, if you did some static imports in a layout/page component, you’ll need to just move that into a <script>
:
// src/routes/__layout.svelte -> src/routes/+layout.svelte
- <script context="module">
- import { MY_TWITTER_HANDLE, MY_YOUTUBE, REPO_URL, SITE_TITLE } from '$lib/siteConfig';
- </script>
<script>
import '../tailwind.css';
import Nav from '../components/Nav.svelte';
+ import { MY_TWITTER_HANDLE, MY_YOUTUBE, REPO_URL, SITE_TITLE } from '$lib/siteConfig';
</script>
This was fairly easy.
Special page values
Now that we don’t have load()
in a page, we dont have access to url
, status
, or error
from load()
. Pull them from the page
store instead:
// src/routes/__error.svelte -> src/routes/+error.svelte
- <script context="module">
- /** @type {import('@sveltejs/kit').ErrorLoad} */
- export function load({ url, error, status }) {
- return {
- props: { error, status, url }
- };
- }
- </script>
<script>
- export let url;
- export let status;
- export let error;
+ import { page } from '$app/stores';
- let message = offline ? 'Find the internet and try again' : error?.message;
+ let message = offline ? 'Find the internet and try again' : $page.error?.message;
- let title = offline ? 'Offline' : status;
- if (status === 404) {
+ let title = offline ? 'Offline' : $page.status;
+ if ($page.status === 404) {
title = 'Page not found :(';
message = 'Sorry! If you think this URL is broken, please let me know!';
}
</script>
- {#if status === 404}
- <p class="">There is no post at the slug <code>{url.pathname}</code>.</p>
- <p><a href={'/ideas/?filter=' + url.pathname.slice(1)}>Try searching for it here!</a></p>
+ {#if $page.status === 404}
+ <p class="">There is no post at the slug <code>{$page.url.pathname}</code>.</p>
+ <p><a href={'/ideas/?filter=' + $page.url.pathname.slice(1)}>Try searching for it here!</a></p>
...
You’ll want to look up what you can get from the Page store but otherwise it’s also pretty straightforward.
Refactor return values
I think this took the most time for me. There a few refactors to do:
- Refactoring error values
- Refactoring return values and headers for pages
- Refactoring return values and headers for API routes
See migration docs.
Refactoring Errors:
// before: page.svelte
- if (res.status > 400) {
- return {
- status: res.status,
- error: await res.text()
- };
- }
// after: +page.js
+ import { error } from '@sveltejs/kit';
// ...
+ if (res.status > 400) {
+ throw error(res.status, await res.text())
+ }
Refactoring return values and headers (using the new setHeaders
api):
// src/routes/[slug]/+page.js
- export async function load({ params, fetch }) {
- const slug = params.slug;
- let res = null;
- res = await fetch(`/api/blog/${slug}.json`);
- return {
- props: {
- json: await res.json(),
- slug,
- REPO_URL
- },
- cache: {
- maxage: 60 // 1 minute
- }
- };
+ export async function load({ params, fetch, setHeaders }) {
+ const slug = params.slug;
+ let res = null;
+ res = await fetch(`/api/blog/${slug}.json`);
+ setHeaders({
+ 'cache-control': 'public, max-age=60'
+ });
+ return {
+ json: await res.json(),
+ slug,
+ REPO_URL
+ }
This refactor looks slightly different for API routes:
// src/routes/api/blog/[slug].json
- try {
- data = await getContent(slug);
- return {
- body: JSON.stringify(data),
- headers: {
- 'Cache-Control': `max-age=0, s-maxage=${60}` // 1 minute.. for now
- }
- };
- } catch (err) {
- return {
- status: 404,
- body: err.message
- };
- }
+ import { error } from '@sveltejs/kit';
// ...
+ data = await getContent(slug).catch(err => error(404, err.message));
+ return new Response(JSON.stringify(data), {
+ headers: {
+ 'Cache-Control': `max-age=0, s-maxage=${60}`
+ }
+ });
SvelteKit has a little json
helper for the new Response(JSON.stringify
stuff, so the last part simplifies to:
import { error, json } from '@sveltejs/kit';
// ...
data = await getContent(slug).catch(err => error(404, err.message));
return json(data, {
headers: {
'Cache-Control': `max-age=0, s-maxage=${60}`
}
});
data
props
Per docs, we now no longer have individual export let
declarations for a +page.svelte
- everything comes into export let data
and the types are supposed to flow from its corresponding +page.js
:
+ /** @type {import('./$types').PageData} */
+ export let data;
/** @type {import('$lib/types').ContentItem} */
- export let json; // warning: if you try to destructure content here, make sure to make it reactive, or your page content will not update when your user navigates
+ $: json = data.json;
(in practice I found PageData
to still be any
here but I am assuming those are growing pains)
Done
There are more smaller changes to handle (I dont use stuff
, I don’t use session
, I dont have a +page.server.js
, etc), but the above was all that affected me and so I guess will be the major ones to take care of for most people, hence me writing this up. All told, about 400 lines of code affected for me.
Once again, you can see the final PR here: sw-yx#88/