🔗

SvelteKitでパッチを当てた外部のコンポーネントを読むまで

2024/05/17に公開

背景

こちらの、svelte-material-uiというパッケージに送ったPull requestを作るに当たって[1]、修正したバージョンを手元のSvelteKitのプロジェクトで試そうとしたところ、思いのほか苦労したのでやったことを共有します。記憶とコマンドのログなどを元に書くので、ちょっと間違っている箇所があるかも知れませんが、参考になれば幸いです。

利用した主なソフトウェア

  • Node.js: 20.11.0
  • SvelteKit: 1.27.2
  • Svelte: 4.2.2
  • Vite: 4.5.2
  • OS: WSL 2上の Debian 11.9

うまくいった方法

結論から言うと、こちらの記事を参考に対象のコンポーネントを含むパッケージをnpm linkし、その上で対象のコンポーネントをビルドすれば🆗です。

例として、今回実際に手を加えたsvelte-material-uiにおけるSelectというコンポーネントを修正したとしましょう。

1. パッチを当てるパッケージをサブモジュールに

まずは修正したコンポーネントを利用するプロジェクトのディレクトリー(ここではsvelte-kit-projectという名前にしておきます)に行き、パッチを当てたパッケージを含むリポジトリー、即ち今回の例ではsvelte-material-uiのリポジトリーのフォークをgit submodule addします。

$ cd ~/path/to/your/svelte-kit-project
$ git submodule add https://github.com/igrep/svelte-material-ui

npm linkの仕様上、実際のところgit submoduleを使う必要は愚か、svelte-kit-projectディレクトリー以下に置く必要すらないのですが、Gitリポジトリーとしてでしか取得できないパッケージを利用する際は、次の理由によりgit submodule addで追加するのをおすすめします:

  • 使用しているバージョンをGitのコミット単位で固定できるので、特に変更したバージョンの利用が長期化した場合において安全
  • プロジェクトで試した結果に基づき、パッケージに更に変更を加えた場合、そのままgit commitgit pushでフォークにフィードバックできる

必要なら、ここでgit submodule addしたリポジトリーのブランチを、当てたいパッチを含むものに切り替えたりしておきましょう:

$ cd svelte-material-ui
$ git checkout -t origin/your-patched-branch

2. パッチを当てたパッケージをビルド

svelte-material-uiに含まれる各パッケージは、ビルドしないと使えません。今回修正したいのはSelectコンポーネントを含む@smui/selectパッケージなので、packages/selectディレクトリーに移動してnpm run buildします:

# svelte-material-ui ディレクトリーから
$ cd packages/select
$ npm run build

3. npm linkで利用するプロジェクトから参照する

続いて、npm linkコマンドを使って、svelte-kit-projectnode_modulesにビルドしたパッケージへのシンボリックリンクを張ることで、svelte-kit-projectの依存関係として使えるようにします。冒頭でも触れたこちらの記事を参考に、まずは@smui/selectパッケージをグローバルなパッケージとしてnpm linkします:

# svelte-material-ui/packages/select ディレクトリーから
$ sudo npm link

ℹ️手元の環境ではnpmのグローバルなパッケージはroot権限がないとアクセスできない場所にインストールされていたので、sudoをつけました。

それができたら、いよいよ修正した@smui/selectを利用するプロジェクト、本稿で言うところのsvelte-kit-projectnpm linkしましょう:

# svelte-material-ui/packages/select ディレクトリーから
# svelte-kit-project ディレクトリーに戻る
$ cd ../../..

# 念のためバージョンも指定しておく
$ npm link @smui/select@7.0.0-beta.18

ℹ️npm linkのドキュメントを改めて読んだところ、実際のところ、svelte-kit-project側で直接npm link svelte-material-ui/packages/selectと書くだけでよいみたいですが、今回私が参考にして試した手順では上記の通り一旦グローバルなパッケージとしてインストールしたのでその通りに書きました。

4. npm installする

「ここまでの手順でsvelte-kit-projectnode_modulesにはnpm linkした@smui/selectが見えているんだから、これで十分だろう」と思ってそのままviteを実行してみましたが、これだけではどうも変更が反映されないようでした。あれこれ触ってみたところ、どうやら修正した@smui/selectを使うsvelte-kit-projectにおいて、再度npm installしなければらなかったようです:

# svelte-kit-project ディレクトリーから
$ npm install

5. vite.config.ts を編集する

前の手順を終えて再度viteを起動すれば、svelte-material-uiディレクトリーにチェックアウトしたバージョンのSelectコンポーネントが利用されるようになっている... はずが、viteが次のエラーを出すようになってしまいました:

Failed to load url /svelte-material-ui/packages/select/dist/index.js (resolved id: /home/user/path/to/your/svelte-kit-project/svelte-material-ui/packages/select/dist/index.js) in /home/user/path/to/your/svelte-kit-project/src/lib/Program.svelte. Does the file exist?
Failed to load url /svelte-material-ui/packages/select/dist/index.js (resolved id: /home/user/path/to/your/svelte-kit-project/svelte-material-ui/packages/select/dist/index.js) in /home/user/path/to/your/svelte-kit-project/src/lib/Program.svelte. Does the file exist? (x2)
Error: Not found: /svelte-material-ui/packages/select/dist/index.js
    at resolve (/home/user/path/to/your/svelte-kit-project/node_modules/@sveltejs/kit/src/runtime/server/respond.js:483:13)
    at resolve (/home/user/path/to/your/svelte-kit-project/node_modules/@sveltejs/kit/src/runtime/server/respond.js:277:5)
    at #options.hooks.handle (/home/user/path/to/your/svelte-kit-project/node_modules/@sveltejs/kit/src/runtime/server/index.js:49:56)
    at Module.respond (/home/user/path/to/your/svelte-kit-project/node_modules/@sveltejs/kit/src/runtime/server/respond.js:274:40)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
The request url "/home/user/path/to/your/svelte-kit-project/svelte-material-ui/packages/select/dist/index.js" is outside of Vite serving allow list.

- /home/user/path/to/your/svelte-kit-project/src/lib

... 省略 ...

Refer to docs https://vitejs.dev/config/server-options.html#server-fs-allow for configurations and more details.

最後に書かれているRefer to docs https://vitejs.dev/config/server-options.html#server-fs-allow for configurations and more details.に従い、次のようにvite.config.tsserver.fs.allowという設定を加えれば解決できました:

vite.config.ts
export default defineConfig({
  // ... 省略 ...
  server: {
    fs: {
      allow: ['./svelte-material-ui/'],
    },
  },
});

多分、セキュリティーのために意図しないファイルを開発用サーバーから参照できないようにしているんでしょう。その際シンボリックリンクの参照先で判断するようになっているみたいですね。

うまくいかなかった方法

参考までに、うまくいかなかった方法とその際発生したエラーについても、覚えている範囲で共有いたします。

package.json の file: プロトコルを使う

従来、私はnpmにリリースされていないバージョンのパッケージを用いるときは、npm linkではなくこのfile:プロトコルを使った方法を使っていました。

やり方は簡単です。修正したバージョンを使用するプロジェクトのpackage.jsonを編集して、dependenciesあるいはdevDependenciesにおける、該当のパッケージのバージョンが書かれた箇所を、file:<該当のパッケージへのパス>に置き換えるだけです(「うまくいった方法」と同様、予め該当のパスにgit submodule addしている前提です)。今回編集した@smui/selectはSvelteのライブラリーなので、devDependenciesを編集しましょう:

{
  "name": "svelte-kit-project",
  "devDependencies": {
    "@smui/select": "file:./svelte-material-ui/packages/select",
    // ... 省略 ...
  },
  // ... 省略 ...
}

この状態で@smui/selectをビルドした上でnpm installし、viteを起動したところ、次のようなエラーに遭遇してしまいました:

[plugin:vite:import-analysis] Missing "./src/Select.svelte" specifier in "@smui/select" package

エラーメッセージで検索してもハッキリわからず、状況から考えて「sveltekit local dependencies」などといった単語で検索したところ、こちらのissueが見つかりました。いくつかワークアラウンドが提案されていたので試してみましたがいずれもうまくいかず(あるいは本当に正しく適用できているかもわからず)、Svelte JapanのDiscordで質問し、頂いた答えをきっかけにあれこれ調べているうちにnpm linkの存在を思い出し、解決に至りました😌。

脚注
  1. (余談)執筆時点で、svelte-material-uiの作者はSvelte 5に対応したsvelte-material-ui v8への対応に集中しているようなので、多分マージしていただくのは時間がかかるでしょう。 ↩︎

GitHubで編集を提案

Discussion