Remix Vite で DuckDB を使うときに @mapbox/node-pre-gyp のエラーを出ないようにする方法
結論
optimizeDeps.exclude の設定を vite.config.ts に追加することで pnpm dev
でもエラーが出なくなります。
import { unstable_vitePlugin as remix } from '@remix-run/dev'
import { defineConfig } from 'vite'
import tsconfigPaths from 'vite-tsconfig-paths'
export default defineConfig({
plugins: [remix(), tsconfigPaths()],
+ optimizeDeps: { exclude: ['@mapbox/node-pre-gyp'] },
})
症状
vite で動かしている Remix アプリで duckdb モジュールを追加して、pnpm dev
を実行すると、ページを参照する都度以下のようなエラーがコンソールに表示されてしまいます。duckdb 自体は正常に動作するものの、コンソールに毎回大量のエラーがでるので非常に開発者体験が悪くなってしまいます。
$ pnpm dev
> remix-vite-duckdb@ dev /Users/coji/progs/spike/remix/remix-vite-duckdb
> remix vite:dev
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h + enter to show help
✘ [ERROR] No loader is configured for ".html" files: node_modules/.pnpm/@mapbox+node-pre-gyp@1.0.11/node_modules/@mapbox/node-pre-gyp/lib/util/nw-pre-gyp/index.html
node_modules/.pnpm/@mapbox+node-pre-gyp@1.0.11/node_modules/@mapbox/node-pre-gyp/lib/node-pre-gyp.js:86:21:
86 │ return require('./' + command)(self, argvx, callback);
╵ ~~~~~~~~~~~~~~
✘ [ERROR] Could not resolve "mock-aws-s3"
node_modules/.pnpm/@mapbox+node-pre-gyp@1.0.11/node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:43:28:
43 │ const AWSMock = require('mock-aws-s3');
╵ ~~~~~~~~~~~~~
You can mark the path "mock-aws-s3" as external to exclude it from the bundle,
which will remove this error and leave the unresolved path in the bundle. You can
also surround this "require" call with a try/catch block to handle this failure at
run-time instead of bundle-time.
✘ [ERROR] Could not resolve "aws-sdk"
node_modules/.pnpm/@mapbox+node-pre-gyp@1.0.11/node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:76:22:
76 │ const AWS = require('aws-sdk');
╵ ~~~~~~~~~
You can mark the path "aws-sdk" as external to exclude it from the bundle, which
will remove this error and leave the unresolved path in the bundle. You can also
surround this "require" call with a try/catch block to handle this failure at
run-time instead of bundle-time.
✘ [ERROR] Could not resolve "nock"
node_modules/.pnpm/@mapbox+node-pre-gyp@1.0.11/node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:112:23:
112 │ const nock = require('nock');
╵ ~~~~~~
You can mark the path "nock" as external to exclude it from the bundle, which will
remove this error and leave the unresolved path in the bundle. You can also
surround this "require" call with a try/catch block to handle this failure at
run-time instead of bundle-time.
8:31:25 PM [vite] error while updating dependencies:
Error: Build failed with 4 errors:
node_modules/.pnpm/@mapbox+node-pre-gyp@1.0.11/node_modules/@mapbox/node-pre-gyp/lib/node-pre-gyp.js:86:21: ERROR: No loader is configured for ".html" files: node_modules/.pnpm/@mapbox+node-pre-gyp@1.0.11/node_modules/@mapbox/node-pre-gyp/lib/util/nw-pre-gyp/index.html
node_modules/.pnpm/@mapbox+node-pre-gyp@1.0.11/node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:43:28: ERROR: Could not resolve "mock-aws-s3"
node_modules/.pnpm/@mapbox+node-pre-gyp@1.0.11/node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:76:22: ERROR: Could not resolve "aws-sdk"
node_modules/.pnpm/@mapbox+node-pre-gyp@1.0.11/node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:112:23: ERROR: Could not resolve "nock"
at failureErrorWithLog (/Users/coji/progs/spike/remix/remix-vite-duckdb/node_modules/.pnpm/esbuild@0.19.12/node_modules/esbuild/lib/main.js:1651:15)
at /Users/coji/progs/spike/remix/remix-vite-duckdb/node_modules/.pnpm/esbuild@0.19.12/node_modules/esbuild/lib/main.js:1059:25
at /Users/coji/progs/spike/remix/remix-vite-duckdb/node_modules/.pnpm/esbuild@0.19.12/node_modules/esbuild/lib/main.js:1527:9
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
原因
DuckDBはOLAP特化した埋め込みRDBMSです。Vectorized query engine を搭載していて超高速に集計ができるのが特徴で、C++ で開発されています。
そのため、DuckDB の Node Binding ではインストール時に duckdb を nodejs の拡張モジュールとしてインストールするのですが、そのための環境設定ユーティリティとして @mapbox/node-pre-gyp を使っているようです。
そして、このモジュールに対して vite が開発環境での体験最適化のために行っている依存関係の事前バンドリングによって duckdb モジュールを事前バンドルする際に、合わせて @mapbox/node-pre-gyp
が事前バンドルでコードが実行されるようなのです。
そのときに @mapbox/node-pre-gyp
が CLI コマンドごとに別jsファイルに切り出して動的import(require)している処理が走りそこでエラーがでています。また、本来不要なのでインストールもされていない mock-aws-s3
, aws-sdk
, nock
それぞれのモジュールも require しようとして、エラーが発生し、コンソールにそのエラーが表示されるようです。
そして事前バンドルはされない状態となるので、ページビューのたびにエラーが大量表示される、ということになるようでした。
対策
vite の設定に optimizedDeps.exclude というのがあり、事前バンドルから除外するモジュールを設定できるので、そこに @mapbox/node-pre-gyp
を設定することで、エラーが発生しないようにする、ということで対応できました。
サンプルリポジトリ
上記の設定をしたコードをここにおいておきました。
所感
Remix Vite にすることで HMR が速くなり、とても快適だったのですが、DuckDB のエラーが出るようになってつらかったのです。今回、簡単な対応策を見つけられて嬉しいです。 vite の事前バンドルの仕組みもをちゃんと読む機会になってよかった。
@mapbox/node-pre-gyp
のこの問題については、issue もちらほらあって、特に electron 勢がお困りのようでした。「メンテナンスもほとんどされないこのパッケージを、使わないようにしてもらえると嬉しい」、みたいなコメントもあり、わかる〜と思いました。でも、C++の nodejs 拡張モジュールを入れるの、結構たいへんそうなので、なんか良い alternative があればいいのですが。
Discussion