pathpidaをSapper(Svelte)で使ってみた
こんにちは!プロダクト開発部の町田です!
私事ですが、沖縄から東京への引っ越しが完了してやっと一息つきました。
デスク周りを整理するいい機会なので、新居では FlexiSpot のスタンディングデスクを新調し、4K の縦画面モニターも新しく配置しました。
ほんの少しでも開発がしやすくなるといいな〜と思ってる今日この頃です。
今回の記事ですが、フロントエンドフレームワークの Sapper(Svelte)で pathpida を使ってみた話をしていきたいと思います。
pathpida とは
pathpida は Next.js や Nuxt.js や Sapper の内部リンクを取得するためのライブラリです。
概要
各フレームワークのディレクトリによるルーティング規約に最適化されており、型安全に内部リンクを取得することができます。
pathpida を Sapper で使ってみる
前述にもある通り、今回は Sapper(Svelte)で pathpida を使っていきたいと思います。
Sapper(Svelte)は既にインストール済みの前提で進めます。
下記が、執筆時点でのバージョンです。
svelte: 0.28.0
pathpida: 0.17.0
インストール
npm-run-all もインストールします。
npm-run-all を導入することで、シンプルな記述で npm-scripts を順次・並列実行できます。
特に依存してるわけではないのですが、入れておくと便利です。
- npm
npm install pathpida npm-run-all --save-dev
- yarn
yarn add pathpida npm-run-all --dev
開発時に pathpida が動くように設定します
{
"scripts": {
"dev": "run-p dev:*",
"dev:sapper": "sapper dev",
"dev:path": "pathpida --ignorePath .gitignore --watch",
"build": "pathpida --ignorePath .gitignore && sapper build --legacy",
"export": "pathpida --ignorePath .gitignore && sapper export --legacy"
// ...other
}
}
以上です。
pathpida は設定なしに動作します。
あとは開発サーバーを立ち上げるだけで内部リンク取得に必要なオブジェクトが生成されます。
特に複雑な設定など要らず簡単に設定できました。
型安全に内部リンクの取得
以下のように/posts/1
に遷移するリンクがあります。
<script>
import { pagesPath } from "$path"
</script>
<a href="posts/1">記事1</a>
上の例だと、url の/posts/1
が内部リンクとして適してるかどうかは型安全に判定できません。
もしroutes/posts/[id]/index.svelte
というファイルが存在しなければページの遷移に失敗してしまいます。
このような内部リンクを型安全に取得し、利用できるようにしてくれるのが pathpida です。
routes が以下のようなディレクトリの場合
routes/index.svelte
routes/about.svelte
routes/posts/index.svelte
routes/posts/[id]/index.svelte
以下の TypeScript のファイルが生成されます
/* eslint-disable */
// prettier-ignore
export const pagesPath = {
about: {
$url: (url?: { hash?: string }) => `/about${url?.hash ? `#${url.hash}` : ''}`
},
posts: {
_id: (id: string | number) => ({
$url: (url?: { hash?: string }) => `/posts/${id}${url?.hash ? `#${url.hash}` : ''}`
}),
$url: (url?: { hash?: string }) => `/posts${url?.hash ? `#${url.hash}` : ''}`
},
$url: (url?: { hash?: string }) => `/${url?.hash ? `#${url.hash}` : ''}`
}
// prettier-ignore
export type PagesPath = typeof pagesPath
svelte で使うイメージはこういう感じです
<script context="module" lang="ts">
import { pagesPath } from "$path"
</script>
<a href="{pagesPath.posts._id(1).$url()}">記事1</a>
型安全に取得したオブジェクトでページ遷移ができました。
型安全なので、オブジェクトから取得できないリンクはエラーが出て気づけたり、型推論によってリンクの候補が出てくるので、候補から取得したいリンクを選択するだけでよい点も便利ですね。
オブジェクトから取得できないリンクを指定した場合
型推論でリンク候補の表示
静的ファイルのパスを型安全に利用する
--enableStatic
オプションを追加してください。
{
"scripts": {
"dev": "run-p dev:*",
"dev:sapper": "sapper dev",
"dev:path": "pathpida --ignorePath .gitignore --enableStatic --watch",
"build": "pathpida --ignorePath .gitignore --enableStatic && sapper build --legacy",
"export": "pathpida --ignorePath .gitignore --enableStatic && sapper export --legacy"
// ...other
}
}
static ディレクトリが以下のような場合
static/favicon.png
static/manifest.json
static/posts/post_1.json
static/posts/post_2.json
$path.ts に以下の オブジェクトが生成されます
// prettier-ignore
export const staticPath = {
favicon_png: '/favicon.png',
manifest_json: '/manifest.json',
posts: {
post_1_json: '/posts/post_1.json',
post_2_json: '/posts/post_2.json'
}
} as const
// prettier-ignore
export type StaticPath = typeof staticPath
pagesPath
の時と同様に自動生成されたstaticPath
を利用して静的ファイルのパスを型安全に取得できます。
クエリパラメータを指定する
pathpida が生成する URL にクエリパラメータを含めたい場合は svelte コンポーネントからQuery
orOptionalQuery
を export すると、あとは pathpida がよしなに組み込んでくれます。
クエリパラメータが必須なページの場合はQuery
、必須でない場合はOptionalQuery
で使い分けます
以下がposts
ページにクエリパラメータを指定する場合の例です。
Query Type を export してposts
のリンクを pathpida で取得する際にクエリパラメータの指定を行います。
<script context="module" lang="ts">
import { pagesPath } from "$path"
export type Query = {
page: number;
limit: number;
}
</script>
svelte で使うイメージはこういう感じです
<script context="module" lang="ts">
import { pagesPath } from "$path"
</script>
<a href="{pagesPath.posts.$url({ query: { page: 1, limit: 10 } })}">記事一覧</a>
また、ドキュメントにもありますが、reference types をクエリパラメータの型に含める際は、絶対パスを利用した dynamic import を利用してください。
TypeScript の制限により、.svelte ファイルから export された型は import できないからです。
公式にある方法とは異なりますが、私は tsconfig.json の設定を変更して path のエイリアスを登録して使う方が好みです
{
"extends": "@tsconfig/svelte/tsconfig.json",
"compilerOptions": {
"lib": ["DOM", "ES2017", "WebWorker"],
"types": ["svelte", "node"],
"baseUrl": "./",
"paths": { "@routes/*": ["src/routes/*"] }
},
"include": ["src/**/*", "src/node_modules/**/*"],
"exclude": ["node_modules/*", "__sapper__/*", "static/*"]
}
export type Page = number;
export type Limit = number;
<script context="module" lang="ts">
import { pagesPath } from "$path"
export type Query = {
page: import('@routes/posts').Page;
limit: import('@routes/posts').Limit
}
</script>
ここらは各々の気に入った方法でコーディングするといいと思います。
まとめ
pathpida を使うことで静的ファイルのパスや内部リンクを型安全に取得し、ページ遷移のリンクに利用することができました!
個人的にはうっかりしたリンクのタイプミスをなくせるのがすごく助かってます。
プロジェクト内には、文字列で書かれたページ遷移のリンクがまだ複数あるので、少しずつ pathpida で取得したリンクに置き換えていこうと思います。
皆さんも是非携わっているプロジェクトに pathpida を導入してみてください!
おわりに
クラッソーネでは一緒に働くクルーを大募集してます!
プロダクトをより良いものにしたいきたいエンジニア、常に改善を続けていきたいエンジニアの皆さんからのご連絡をお待ちしております。
少しでも興味を持っていただけた方、ぜひ一度カジュアルにお話ししましょう!