Open7
Svelte (5) のパッケージングに関する雑記
基本的には公式ドキュメントを読めば大体わかる。というか、 $ npx sv create
で Svelte library
テンプレートを選んでおけば後は書くだけなので、補足や気になった点を書いていく。
コンポーネントの公開
例えば svelte の UI ライブラリなどを作成する場合、 package.json
の exports
を以下のように設定する ( 単純な例として一つのコンポーネントのみ公開 )。
package.json
{
// 省略
"exports": {
".": {
"svelte": "./index.svelte",
"types": "./index.svelte.d.ts"
}
}
}
- 型情報を提供するには型定義ファイルが必要で、
tsc
では svelte コンポーネントを扱えないためツールを使う必要がある(後述) -
.svelte.ts
(.svelte.js
) の中ではrune
が利用できるが、これらのファイルは"svelte"
キーでなく"default"
などでも公開は可能。- ただし、 svelte のコンパイラを通さないと
rune
が参照できずに$state is undefined
のようなエラーが起きるため"svelte"
キーでよいように思われる [1]
- ただし、 svelte のコンパイラを通さないと
-
svelte 環境以外でも使える関数を作るような場合が思い当たるが、そういった場合は先に svelte でコンパイルして配布するのでは?という気もする ↩︎
@sveltejs/package
@sveltejs/package の svelte-package
コマンドで以下の処理が行われる
- svelte component のプリプロセス [1]
- TypeScript のトランスパイル
- 型定義ファイルの生成
例として、以下のコンポーネントに svelte-package
を実行した結果をのせる
Hello.svelte
<!--
@component
Hello My Component!
- foo
- bar
-->
<script lang="ts">
let { name }: {
/** your name */
name: string
} = $props()
</script>
<p>Hello { name }</p>
結果の型定義ファイル:
Hello.svelte.d.ts
type $$ComponentProps = {
/** your name */
name: string;
};
/**
* Hello My Component!
*
* - foo
* - bar
*/
declare const Hello: import("svelte").Component<$$ComponentProps, {}, "">;
type Hello = ReturnType<typeof Hello>;
export default Hello;
- コンポーネント内の ts は変換されない模様( svelte5 では コンパイラが直接 ts を解釈するため )
- 若干複雑な記述で、手書きでコンポーネントの型定義をするのは想定されていない感がある
-
プリプロセスでは
<style lang="sass">
を生のcss に変換するなどしているらしいが、これはパッケージとして配布後でもできるのでは? vite では標準で変換しているけど他のツールでは同じとは限らないから、そういったケースへの対応? ↩︎
テスト
大きく以下の手法がある
-
rune
を使う関数などのユニットテスト (vitest) - コンポーネントのテスト (vitest + @testing-library/svelte )
- e2e テスト ( Playwright 等)
個人的には基本方針としてはユニットテストが書けるならユニットテスト書いた方がいい派なのだが、ユニットテスト・コンポーネントテストのための設定がいまいち煩雑というかよくわからない感があり、svelte では e2e で書いても良いような気がしている。
上の issue にあるように jsdom が無いと $effect が起動しないなど
ユニットテストする場合
- 基本的な設定に関しては ドキュメント通りで、
Svelte library
テンプレートを選べば設定済みになっているので省略 - 上述の issue にあるように
$effect
をテストするにはenvironment: "jsdom"
が必要(詳細は不明)
コード例
example.svelte.spec.ts
import { flushSync } from 'svelte';
import { expect, test } from 'vitest';
test('Effect', () => {
let count = $state(0);
let double = $state(100);
const cleanup = $effect.root(() => {
$effect(() => {
// 通常はこのような場合 $derived を使うが、 $effect の簡単な動作例として以下のように書く
double = count * 2;
});
});
expect(double).toEqual(100);
flushSync(); // effect が実行されるのを待つ
expect(double).toEqual(0);
count = 1
flushSync();
expect(double).toEqual(2);
cleanup();
});
- テストファイルでも
rune
の利用にはファイル名が.svelte.ts
or.svelte.js
である必要があるが、テストの場合.svelte.spec.ts
でも.spec.svelte.ts
でも良い模様- テスト対象を
*.spec.ts
で指定したりするので前者の方が無難か
- テスト対象を
-
$effect.root
は$effect
を管理するためのスコープを新たに作成する関数で、主にコンポーネント外部で$effect
を作成したい時に使う。通常はコンポーネント初期化時にスコープが作成されるので$effect.root
を使う必要がない- コンポーネント初期化時に作成されるスコープはコンポーネント破棄のタイミングで自動的にクリーンアップが実行されるが、
$effect.root
で作成したスコープは 手動でクリーンアップを行う必要がある -
$effect
をコンポーネント外で定義された関数の中で呼び出すことは問題ないが、その関数の呼び出しはスコープ内で行う必要がある。
- コンポーネント初期化時に作成されるスコープはコンポーネント破棄のタイミングで自動的にクリーンアップが実行されるが、
その他メモ
-
npm pack
で tarball を作成 &npm install foo-1.0.0.tgz
で配布物のテストができる
コンポーネントテストをする場合
- こちらも設定はドキュメント通りでOK
- 記事によっては svelte5 では
@testing-library/svelte/svelte5
を使うように書いてあるものもあるが、 @testing-library/svelte@5.2.0 でルートのエントリーポイントが svelte5 に切り替わったので今は@testing-library/svelte
をインポートすれば良い
ログインするとコメントできます