👍

Remix Vite で DuckDB を使うときに @mapbox/node-pre-gyp のエラーを出ないようにする方法

2024/02/07に公開

結論

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:
      86return 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 を設定することで、エラーが発生しないようにする、ということで対応できました。

サンプルリポジトリ

上記の設定をしたコードをここにおいておきました。
https://github.com/coji/remix-vite-duckdb

所感

Remix Vite にすることで HMR が速くなり、とても快適だったのですが、DuckDB のエラーが出るようになってつらかったのです。今回、簡単な対応策を見つけられて嬉しいです。 vite の事前バンドルの仕組みもをちゃんと読む機会になってよかった。

@mapbox/node-pre-gyp のこの問題については、issue もちらほらあって、特に electron 勢がお困りのようでした。「メンテナンスもほとんどされないこのパッケージを、使わないようにしてもらえると嬉しい」、みたいなコメントもあり、わかる〜と思いました。でも、C++の nodejs 拡張モジュールを入れるの、結構たいへんそうなので、なんか良い alternative があればいいのですが。

Discussion