Nuxt+PrismaORM ビルド時のエラー対処 [未解決]

## はじめに
Vue.jsのフルスタックフレームワークである[Nuxt](https://nuxt.com/)で、Node.js用のORMである[Prisma ORM](https://www.prisma.io/)を使って、アプリを作成していました。
`npm run dev`で立ち上がる開発環境を使って開発し、本番環境へデプロイしようと`npm run build`を実行したところ、エラーが発生してビルドできませんでした。
最終的にビルドに成功することはなかったものの進捗はあったので、同じような状況になった方の参考になるかもしれないと思い、知見を共有します。

## 自分の環境
Windows
node : v18.18.0
npm : 10.9.0
### `package.json`より
関係なさそうなパッケージは除いています。
|パッケージ名|バージョン|備考|
|--|--|--|
|nuxt|^3.16.2||
|vue|^3.5.13||
|vue-router|^4.5.1||
|@prisma/nuxt|^0.3.0|最終的にアンインストールした|
|@prisma/client|^6.7.0|後から追加した|
|prisma|^6.7.0|後から追加した|

## `@prisma/nuxt`を使用しての試行錯誤 -> 断念
### (`@prisma/nuxt`のインストール)


`@prisma/nuxt`は、NuxtでPrismaを使う際に推奨される、設定の簡素化などをしてくれるモジュールらしいです。

https://nuxt.com/modules/prisma

https://www.prisma.io/docs/orm/more/help-and-troubleshooting/prisma-nuxt-module

上記サイトの通り、
```shell
npx nuxi@latest module add @prisma/nuxt
```
で`@prisma/nuxt`を追加します。

### エラー1 : Nuxt Build Error
`npm run build`を実行したところ、次のようなエラーが発生しました。

```shell
> npm run build

ERROR  ✗ Build failed in 2.13s                                                00:00:00  

[nuxi 00:00:00]  ERROR  Nuxt Build Error: [commonjs--resolver] Invalid module ".prisma/client/index-browser" is not a valid package name imported from C:\ProjectRoot\node_modules\
file: C:/ProjectRoot/node_modules/@prisma/client/index-browser.js
```

### 解決策1 -> 進んだ?
調べると、Viteのaliasによって解決できるかもしれないという情報がありました。

https://www.prisma.io/docs/orm/more/help-and-troubleshooting/prisma-nuxt-module#resolving-typeerror-failed-to-resolve-module-specifier-prismaclientindex-browser

上記サイトに倣い、`nuxt.config.ts`に次の内容を追記しました。
```diff_typescript:nuxt.config.ts
    vite: {
+       resolve: {
+            alias: {
+                ".prisma/client/index-browser": "./node_modules/.prisma/client/index-browser.js",
+            },
+        },
    },
```

### エラー2 : TypeError & Invalid module
再び`npm run build`を実行すると先程のエラーは消え、解決かと思いきや、今度は次のようないくつかのエラーが発生しました。

```shell
> npm run build

[nitro 00:00:00]  ERROR  TypeError [ERR_INVALID_MODULE_SPECIFIER]: Invalid module ".prisma" is not a valid package name imported from C:\ProjectRoot\node_modules\@prisma\client\default.js

[00:00:00]  ERROR  Invalid module ".prisma" is not a valid package name imported from C:\ProjectRoot\node_modules\@prisma\client\default.js

[00:00:00]  ERROR  Invalid module ".prisma" is not a valid package name imported from C:\ProjectRoot\node_modules\@prisma\client\default.js
```

### 解決策2 -> 失敗
Viteのaliasを、下記のようにさらに追加してみましたが、解決しませんでした。
```diff_typescript:nuxt.config.ts
    vite: {
        resolve: {
            alias: {
                ".prisma/client/index-browser": "./node_modules/.prisma/client/index-browser.js",
+                ".prisma": "./node_modules/@prisma/client/default.js",
            },
        },
    },
```

### 解決策2.5 -> 失敗
また、他の記事を参考に次のようにも書き換えてみましたが、解決しませんでした。

https://zenn.dev/ryoohno/articles/12b68d3ecbe863

```diff:prisma/schema.prisma
generator client {
-  provider = "prisma-client-js"
+  provider = "prisma-client"
  output   = "../generated/prisma"
}
```
```diff_typescript:lib/prisma.ts
- import { PrismaClient } from '@prisma/client'
+ import { PrismaClient } from "~/generated/prisma/client";
```
```shell
npx prisma generate
```

### 断念
`@prisma/nuxt`を使用しては、ビルドできないという判断に至りました。

`@prisma/nuxt`をアンインストールして、ここまでの変更を戻しました。

<details><summary>ここまでの変更を戻す</summary>

```diff_typescript:nuxt.config.ts
-    modules: ["@prisma/nuxt"],
+    modules: [],
    vite: {
-        resolve: {
-            alias: {
-                ".prisma/client/index-browser": "./node_modules/.prisma/client/index-browser.js",
-                ".prisma": "./node_modules/@prisma/client/default.js",
-            },
-        },
    },
```

```diff:prisma/schema.prisma
generator client {
-  provider = "prisma-client"
+  provider = "prisma-client-js"
  output   = "../generated/prisma"
}
```
```diff_typescript:lib/prisma.ts
- import { PrismaClient } from "~/generated/prisma/client";
+ import { PrismaClient } from '@prisma/client'
```
```shell
npm uninstall @prisma/nuxt
```

</details>

## `prisma` `@prisma/client`へ切り替えて試行錯誤 -> 一瞬だけ成功 -> 失敗
### `prisma` `@prisma/client`のインストール
Nuxt用のパッケージ`@prisma/nuxt`ではなく、`prisma` `@prisma/client`を使えば解決できるかもしれないと思い、次のようにインストールしました。
```shell
npm install @prisma/client@latest prisma@latest
```
```diff_json:package.json
  "scripts": {
-    "build": "nuxt build",
+    "build": "prisma generate && nuxt build",
    "dev": "nuxt dev --host",
    "generate": "nuxt generate",
    "preview": "nuxt preview",
    "postinstall": "nuxt prepare"
  },
```

### エラー1 : TypeError & Invalid module
`npm run build`を実行すると、さっきも出たエラーが再発しました。

```shell
> npm run build

[nitro 00:00:00]  ERROR  TypeError [ERR_INVALID_MODULE_SPECIFIER]: Invalid module ".prisma" is not a valid package name imported from C:\ProjectRoot\node_modules\@prisma\client\default.js

[00:00:00]  ERROR  Invalid module ".prisma" is not a valid package name imported from C:\ProjectRoot\node_modules\@prisma\client\default.js

[00:00:00]  ERROR  Invalid module ".prisma" is not a valid package name imported from C:\ProjectRoot\node_modules\@prisma\client\default.js
```
### 解決策1 -> 前進
そこで、再び下記の記事を参考に次のようにも書き換えてみました。

https://zenn.dev/ryoohno/articles/12b68d3ecbe863

```diff:prisma/schema.prisma
generator client {
-  provider = "prisma-client-js"
+  provider = "prisma-client"
  output   = "../generated/prisma"
}
```
```diff_typescript:lib/prisma.ts
- import { PrismaClient } from '@prisma/client'
+ import { PrismaClient } from "~/generated/prisma/client";
```

すると今度はビルドに成功しました。
```shell
> npm run build

✔ Nuxt Nitro server built
✔ You can preview this build using node .output/server/index.mjs
```

### エラー2 : SyntaxError
ビルドに成功し、一件落着かと思いきや、`node .output/server/index.mjs`で本番環境を立ち上げたところ、再びエラーが発生しました。
本番環境は立ち上がるのですが、サーバーサイドでPrismaを使う処理を走らせたときに、次のようなエラーが出て処理ができませんでした。

```shell
> node .output/server/index.mjs

Listening on http://[::]:3000

[request error] [unhandled] [POST] http://[::1]:3000/api/your-endpoint
 SyntaxError: Identifier 'process' has already been declared
    at ESMLoader.moduleStrategy (node:internal/modules/esm/translators:119:18)
    at ESMLoader.moduleProvider (node:internal/modules/esm/loader:468:14) {
  cause: SyntaxError: Identifier 'process' has already been declared
      at ESMLoader.moduleStrategy (node:internal/modules/esm/translators:119:18)
      at ESMLoader.moduleProvider (node:internal/modules/esm/loader:468:14),
  statusCode: 500,
  fatal: false,
  unhandled: true,
  statusMessage: undefined,
  data: undefined
}
```
### 解決策2 -> ~~前進~~

何を参考にしたかがわからなくなったのですが、次のように変更したところ、`SyntaxError`は解決しました。
```diff_typescript:lib/prisma.ts
- import { PrismaClient } from "~/generated/prisma/client";
+ import { PrismaClient } from "~/generated/prisma";
```

### エラー3 : ReferenceError
今度は別のエラーが発生しました。
```shell
> node .output/server/index.mjs

Listening on http://[::]:3000

[request error] [unhandled] [POST] http://[::1]:3000/api/your-endpoint
 ReferenceError: __dirname is not defined in ES module scope
    at file:///C:/ProjectRoot/.output/server/chunks/_/prisma.mjs:831:16
    at ModuleJob.run (node:internal/modules/esm/module_job:194:25) {
  cause: ReferenceError: __dirname is not defined in ES module scope
      at file:///C:/ProjectRoot/.output/server/chunks/_/prisma.mjs:831:16
      at ModuleJob.run (node:internal/modules/esm/module_job:194:25),
  statusCode: 500,
  fatal: false,
  unhandled: true,
  statusMessage: undefined,
  data: undefined
}
```

### 解決策3 -> ~~解決~~
エラーをよく見てみると、`__dirname`が存在しないみたいです。
下記サイトなどを参考に、エラーの該当ファイル`.output/server/chunks/_/prisma.mjs`に、追記をすることで解決しました。

https://flaviocopes.com/fix-dirname-not-defined-es-module-scope/

830行目あたりにエラー要因のコードがあったので、その近辺に追記しました。
```diff_javascript:C:/ProjectRoot/.output/server/chunks/_/prisma.mjs
var M = k(require$$3); k(wi()); N("prisma:engines");
+ import { fileURLToPath } from 'url'
+ // import path from 'path' //pathはimport済みだったので不要でしたが、環境によってはここで定義する必要があるかもしれません
+ const __filename = fileURLToPath(import.meta.url)
+ const __dirname = path.dirname(__filename)
function fs() {
  return M.default.join(__dirname, "../");
}
M.default.join(__dirname, "../query-engine-darwin");
```

これで、無事に動くようになりました!
```shell
> node .output/server/index.mjs

Listening on http://[::]:3000
```

### 解決策3.5 -> 失敗
解決策3の方法では、ビルド毎にファイルの書き換えが必要なため、あまり望ましくありません。
そこで、よりよい方法はないかと色々と模索してみましたが、どれもうまくいきませんでした。

https://www.prisma.io/docs/orm/prisma-schema/overview/generators#field-reference-1

```diff:prisma/schema.prisma
generator client {
  provider               = "prisma-client"
  output                 = "../generated/prisma"
+  runtime                = "nodejs"
+  moduleFormat           = "esm"
+  generatedFileExtension = "ts"
+  importFileExtension    = "ts"
}
```

https://github.com/prisma/prisma/issues/14484

https://github.com/prisma/prisma/issues/20702

https://github.com/prisma/prisma/issues/26897

### エラー4 : SyntaxError
不要なファイルを消すためにパッケージの再インストールなどをしていたら、先程のエラー2が再発するようになりました。
Gitを使ってコード管理をしているので、不用意に必要なファイルを削除してしまったということもなさそうなのです。
```shell
> node .output/server/index.mjs

Listening on http://[::]:3000

[request error] [unhandled] [POST] http://[::1]:3000/api/your-endpoint
 SyntaxError: Identifier 'process' has already been declared
    at ESMLoader.moduleStrategy (node:internal/modules/esm/translators:119:18)
    at ESMLoader.moduleProvider (node:internal/modules/esm/loader:468:14) {
  cause: SyntaxError: Identifier 'process' has already been declared
      at ESMLoader.moduleStrategy (node:internal/modules/esm/translators:119:18)
      at ESMLoader.moduleProvider (node:internal/modules/esm/loader:468:14),
  statusCode: 500,
  fatal: false,
  unhandled: true,
  statusMessage: undefined,
  data: undefined
}
```

### 解決策4 -> 失敗

解決策2の方法はすでに試していますが、それでも治りませんでした。
色々と調べて、エラーを吐いているであろうファイルを直接書き換えたりも試してみましたが、うまくいかず、結局未解決となりました。

## おまけ
### エラー : crypto
バックエンドでcryptoというパッケージを使っていて、
開発環境では`crypto.randomUUID()`というように使えていたのですが、本番環境ではそこがエラーを吐きました。

```shell
> node .output/server/index.mjs

Listening on http://[::]:3000

 ReferenceError: crypto is not defined
    at Object.handler (file:///C:/ProjectRoot/.output/server/chunks/routes/api/register.post.mjs:76:16)
    at async Object.handler (file:///C:/ProjectRoot/.output/server/chunks/_/nitro.mjs:1967:19)
    at async Server.toNodeHandle (file:///C:/ProjectRoot/.output/server/chunks/_/nitro.mjs:2238:7) {
  cause: ReferenceError: crypto is not defined
      at Object.handler (file:///C:/ProjectRoot/server/chunks/routes/api/register.post.mjs:76:16)
      at async Object.handler (file:///C:/ProjectRoot/.output/server/chunks/_/nitro.mjs:1967:19)
      at async Server.toNodeHandle (file:///C:/ProjectRoot/.output/server/chunks/_/nitro.mjs:2238:7),
  statusCode: 500,
  fatal: false,
  unhandled: true,
  statusMessage: undefined,
  data: undefined
}
```

### 解決策
ちゃんとimportしてあげれば、問題なく動きました。
```diff_typescript
+ import { randomUUID } from "crypto";

- const uuid = crypto.randomUUID()
+ const uuid = randomUUID()
```
https://developer.mozilla.org/ja/docs/Web/API/Crypto


## 終わりに

なぜか、一度は正しく動いたのですが、最終的には解決できませんでした。
今回作っていたプロジェクトでは、[Prisma ORM](https://www.prisma.io/)を使うのを諦め、[Drizzle ORM](https://orm.drizzle.team/)へ移行しました。

参考文献はところどころに貼ってますが、実際には[Perplexity](https://www.perplexity.ai/)なども併用しています。