Sapper のプロジェクトを SvelteKit にマイグレーションするか
マイグレーションするやつ↓
type:module
package.json - type: module
を追加する
{
+ "type": "module",
}
dependencies
package.json - express
または polka
を dependencies
から取り除く。
また sirv
や compression
などのミドルウェアも同様。
npm uninstall polka sirc compression
# TypeScript only
npm uninstall @types/polka @types/compression --dev
dev-dependencies
package.json sapper
を取り除いて @sveltejs/kit
に置き換える。
npm uninstall sapper --dev
npm install @sveltejs/kit --dev
package.json - npm-scripts
npm-scripts を sapper
から svelte-kit
を使うように置き換える。
Sapper
では sapper build
・ sapper export
のようにコマンドでビルドを切り替えていたが SvelteKit
では adapter と呼ばれるものを切り替えてビルドする。
"scripts": {
- "dev": "sapper dev",
- "build": "npm run build:tailwind && sapper build --legacy",
- "export": "npm run build:tailwind && sapper export --legacy",
- "start": "node __sapper__/build",
+ "dev": "svelte-kit dev",
+ "build": "npm run build:tailwind && svelte-kit build",
+ "start": "node build",
}
設定ファイル
webpack.config.js
または rollup.config.js
は svelte.config.js に置き換える
rm rollup.config.js
touch svelte.config.js
以下の通りに記述する
import adapter from '@sveltejs/adapter-static';
import preprocess from 'svelte-preprocess';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://github.com/sveltejs/svelte-preprocess
// for more information about preprocessors
preprocess: preprocess(),
kit: {
adapter: adapter(),
// hydrate the <div id="svelte"> element in src/app.html
target: '#svelte'
}
};
export default config;
rollup.config.js
には env
の設定を記述していたが、 SvelteKit
では Vite
により .env
ファイルが読み込まれるので設定ファイルに記述する必要はなさそう。
設定ファイル - adapter
https://zenn.dev/link/comments/e7ba4182f189bd で述べたとおり、ビルドを切り替えるために adapter
を設定する。
sapper export
と同等の adapter
は @sveltejs/adapter-staticを使用するらしい。
npm install @sveltejs/adapter-static --dev
+ import adapter from '@sveltejs/adapter-static';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// options passed to svelte.compile (https://svelte.dev/docs#svelte_compile)
compilerOptions: null,
// an array of file extensions that should be treated as Svelte components
extensions: ['.svelte'],
kit: {
- adapter: null,
+ adapter: adapter(),
src/client.js
sapper.start(...)
の前に記述しているロジックがある場合には __layout.svelte
の onMount
コールバックの中に処理を移す必要がある。
src/server.js
このファイルに記述したロジックは hooks module に処理を移す。
src/service-worker.js
sapper/service-worker
から import していたものを $service-worker
に置き換える。
以下の変更点が存在する
-
shell
→build
-
routes
→ 削除
- import { timestamp, files, shell } from '@sapper/service-worker';
+ import { timestamp, files, build } from '@sapper/service-worker';
src/template.html
src/template.html
は src/app.html
にリネームする。
mv src/template.html src/app.html
以下の記述をは削除する。
%sapper.base%
%sapper.scripts%
%sapper.styles%
以下の記述は置き換える
-
%sapper.head%
→%svelte.head%
-
%sapper.html%
→%svelte.body%
<div id="sapper">
は不要になった。特定の要素にマウントしたい場合には svelte.config.js
ファイルの target
に記述する・
src/node_modules
Sapper
では内部ライブラリは src/node_modules
に配置していたがこれは Vite
では機能しないので src/lib
に置き換える。
特に使用 src/node-modules
にファイルを配置していないのであればそのまま削除して良さそう。
rm -rf src/node_modules
レイアウトファイル
以下のファイルをそれぞれリネームする。
-
_layout.svelte
→__layout.svelte
-
_error.svelte
→_error.svelte
mv src/routes/_layout.svelte src/routes/__layout.svelte
mv src/routes/_error.svelte src/routes/__error.svelte
$app/navigation
@sapper/app
からインポートしている goto
,prefetch
,prefetchRoutes
は $app/navigation からインポートするように置き換える。
<script lang="ts">
- import { goto } from '@sapper/app'
+ import { goto } from '$app/navigation'
Preload
preload
は Sapper
において Next.js
の getInitialProps
や Nuxt.js
の asyncData
と同等の機能。
まずはじめに preload
は load にリネームする。
<script context="module" lang="ts">
- export async function preload({ params }) {
+ export async function load({ params }) {
const res = await this.fetch(`blog/${params.slug}.json`)
const data = await res.json()
if (res.status === 200) {
const { post, contents } = data
return { post, contents }
} else {
const { message } = data
this.error(res.status, message)
}
}
</script>
TypeScript
<script context="module" lang="ts">
import { load } from '@sveltejs/kit'
export const load = async ({ params }) {
}
Pleload - APIの変更
preload
は page
と session
の 2つの引数を受け取っていたが、これらは1つの引数に統合されることになった。
また、this.fetch
は fetch
を引数から受け取るように変更になった。
<script context="module" lang="ts">
- export async function load({ params }) {
+ export async function load({ params, fetch }) {
const page = Number(params.page)
- const res = await this.fetch(`tags/${params.slug}/page/${page}.json`)
+ const res = await fetch(`tags/${params.slug}/page/${page}.json`)
const data = await res.json()
if (res.status === 200) {
const { tag } = data
return { tag }
} else {
const { message } = data
this.error(res.status, message)
}
}
</script>
また、preload
ではオブジェクトをそのまま返却していたが load
では props
のプロパティとして返却するようになった。
<script context="module" lang="ts">
export async function load({ params, fetch }) {
const page = Number(params.page)
const res = await fetch(`tags/${params.slug}/page/${page}.json`)
const data = await res.json()
if (res.status === 200) {
const { tag } = data
- return { tag }
+ return { props: tag }
} else {
const { message } = data
this.error(res.status, message)
}
}
</script>
なお load
が何も return
しない場合には 404 が返される。
インターフェイス
// Declaration types for Loading
// * declarations that are not exported are for internal use
export interface LoadInput<
PageParams extends Record<string, string> = Record<string, string>,
Stuff extends Record<string, any> = Record<string, any>,
Session = any
> {
url: URL;
params: PageParams;
fetch(info: RequestInfo, init?: RequestInit): Promise<Response>;
session: Session;
stuff: Stuff;
}
export interface LoadOutput<
Props extends Record<string, any> = Record<string, any>,
Stuff extends Record<string, any> = Record<string, any>
> {
status?: number;
error?: string | Error;
redirect?: string;
props?: Props;
stuff?: Stuff;
maxage?: number;
}
query
は url.searchParams
になってた
this
の廃止
Preload - load
内では this
は使われなくなった。
this.error
,this.redirect
は代わりに return
するオブジェクトのプロパティとして指定するようになった。
<script context="module" lang="ts">
export async function load({ params, fetch }) {
const page = Number(params.page)
const res = await fetch(`tags/${params.slug}/page/${page}.json`)
const data = await res.json()
if (res.status === 200) {
const { tag } = data
return { props: tag }
} else {
const { message } = data
- this.error(res.status, message)
+ return {
+ status: res.status,
+ error: new Error(message)
}
}
</script>
<script context="module">
export function load() {
- return this.redirect(302, 'blog')
+ return {
+ status: 302,
+ redirect: 'blog',
+ }
}
</script>
Stores
Sapper
では page
,session
などのは @sapper/app
から stores
をインポートしてそれを関数呼び出しして取り出していた。
import { stores } from '@sapper/app';
const { preloading, page, session } = stores();
SvelteKit
では $app/stores
から直接インポートするようになった。また preloading
は navigating
に置き換えられた。
import { getStores, navigating, page, session } from '$app/stores';
const stores = getStores()
Routing
正規表現ベースのルーティングは廃止になった。代わりにFallthrough routesを使う
<a>
タグ
-
rel="prefetch"
orsapper:prefetch
→sveltekit:prefetch
-
sapper:noscroll
→sveltekit:noscroll
とりあえずここまでで動かしてみる
npm run dev
> TODO@0.0.1 dev
> svelte-kit dev
Malformed svelte.config.js
Error [ERR_REQUIRE_ESM]: require() of ES Module /sapper-blog-app/svelte.config.js from /sapper-blog-app/@root not supported.
Instead change the require of svelte.config.js in /sapper-blog-app/@root to a dynamic import() which is available in all CommonJS modules.
require
を使うとだめ?
@sveltejs/kit
と @sveltejs/adapter-static
のバージョンを next
に固定したら動いた。
npm i -D @sveltejs/kit@next @sveltejs/adapter-static@next
postcss.config.js
や tailwind.config.js
のような Commonjs として扱うファイルは拡張子を .cjs
に変換する必要がある
sapper-google-analytics
が動かないので一旦消す
.gitignore
に以下を追加する必要があった
/.svelte-kit
また別のエラーが
Cannot read properties of undefined (reading 'end')
TypeError: Cannot read properties of undefined (reading 'end')
at get (/sapper-blog-app/src/routes/blog/index.json.ts:8:10)
at async render_endpoint (file:///sapper-blog-app/node_modules/@sveltejs/kit/dist/ssr.js:94:19)
at async resolve (file:///sapper-blog-app/node_modules/@sveltejs/kit/dist/ssr.js:1792:10)
at async respond (file:///sapper-blog-app/node_modules/@sveltejs/kit/dist/ssr.js:1769:10)
at async fetch (file:///sapper-blog-app/node_modules/@sveltejs/kit/dist/ssr.js:980:23)
at async load (index.svelte:3:22)
at async load_node (file:///sapper-blog-app/node_modules/@sveltejs/kit/dist/ssr.js:1114:12)
at async respond$1 (file:///sapper-blog-app/node_modules/@sveltejs/kit/dist/ssr.js:1321:15)
at async render_page (file:///sapper-blog-app/node_modules/@sveltejs/kit/dist/ssr.js:1507:19)
at async resolve (file:///sapper-blog-app/node_modules/@sveltejs/kit/dist/ssr.js:1793:10)
Unexpected token T in JSON at position 0
SyntaxError: Unexpected token T in JSON at position 0
at JSON.parse (<anonymous>)
at Proxy.<anonymous> (file:///sapper-blog-app/node_modules/@sveltejs/kit/dist/ssr.js:1077:22)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async load (index.svelte:4:28)
at async load_node (file:///sapper-blog-app/node_modules/@sveltejs/kit/dist/ssr.js:1114:12)
at async respond$1 (file:///sapper-blog-app/node_modules/@sveltejs/kit/dist/ssr.js:1321:15)
at async render_page (file:///sapper-blog-app/node_modules/@sveltejs/kit/dist/ssr.js:1507:19)
at async resolve (file:///sapper-blog-app/node_modules/@sveltejs/kit/dist/ssr.js:1793:10)
at async respond (file:///sapper-blog-app/node_modules/@sveltejs/kit/dist/ssr.js:1769:10)
at async file:///sapper-blog-app/node_modules/@sveltejs/kit/dist/chunks/index.js:4374:24
Cannot read properties of undefined (reading 'message')
TypeError: Cannot read properties of undefined (reading 'message')
at __error.svelte:37:17
at Object.$$render (/sapper-blog-app/node_modules/svelte/internal/index.js:1702:22)
at Object.default (root.svelte:43:47)
at eval (/src/routes/__layout.svelte:71:80)
at Object.$$render (/sapper-blog-app/node_modules/svelte/internal/index.js:1702:22)
at root.svelte:37:45
at $$render (/sapper-blog-app/node_modules/svelte/internal/index.js:1702:22)
at Object.render (/sapper-blog-app/node_modules/svelte/internal/index.js:1710:26)
at render_response (file:///sapper-blog-app/node_modules/@sveltejs/kit/dist/ssr.js:605:28)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
Cannot read properties of undefined (reading 'message')
TypeError: Cannot read properties of undefined (reading 'message')
at __error.svelte:37:17
at Object.$$render (/sapper-blog-app/node_modules/svelte/internal/index.js:1702:22)
at Object.default (root.svelte:43:47)
at eval (/src/routes/__layout.svelte:71:80)
at Object.$$render (/sapper-blog-app/node_modules/svelte/internal/index.js:1702:22)
at root.svelte:37:45
at $$render (/sapper-blog-app/node_modules/svelte/internal/index.js:1702:22)
at Object.render (/sapper-blog-app/node_modules/svelte/internal/index.js:1710:26)
at render_response (file:///sapper-blog-app/node_modules/@sveltejs/kit/dist/ssr.js:605:28)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
Cannot read properties of undefined (reading 'end')
server routes も修正する必要がありそう。
import type { ServerResponse } from 'http'
import type { Request } from 'polka'
import RepositoryFactory, { POST } from '../../repositories/RepositoryFactory'
const PostRepository = RepositoryFactory[POST]
export async function get(req: Request, res: ServerResponse) {
const posts = await PostRepository.get({})
res.end(JSON.stringify({ posts }))
}
endpoints が server routes
と同等っぽい?
endpoints
に書き換えたらこんな感じ
import type { RequestHandler } from '@sveltejs/kit'
import RepositoryFactory, { POST } from '../../repositories/RepositoryFactory'
const PostRepository = RepositoryFactory[POST]
export const get: RequestHandler = async (req) => {
const posts = await PostRepository.get({})
return {
body: {
posts
}
}
}
ページ表示できた!
$page.path has been replaced by $page.url.pathname
stores
の page
が変わっている
- $: url = `${protocol}://${$page.host}${$page.path}`
+ $: url = `${protocol}://${$page.url.host}${$page.url.pathname}`
fetch
のパスの指定方法が変わってる
export async function load({ params, fetch }) {
- const res = await fetch(`blog/${params.slug}.json`)
+ const res = await fetch(`/blog/${params.slug}.json`)
process.env
の値が取得できていない
公開する環境変数は VITE
をプレフィックスにする必要がある。
その後 import.meta.env
から値を取得する。
<style>
タグ内で環境変数を利用するとビルド時に問題があるようなので lib/variables.ts
にまとめて定義する方法が紹介されている
export const variables = {
basePath: import.meta.env.VITE_PUBLIC_BASE_PATH
};
<script lang="ts">
import { variables } from '$lib/variables';
</script>
<div>basePath: {variables.basePath}</div>
参考↓
global.d.ts
ファイルを作成して以下のように sveltekit
の型を効かせる
/// <reference types="@sveltejs/kit" />
__layout.svelte
の segment
が undefined
を返す
$app/stores
から page
をインポートして page.url.pathname
が同等
Vercel のビルドコマンドも npm run export
から npm run build
に修正した
デプロイ完了🎉