Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

prisma client could not be resolved #19036

Open
7 tasks done
dhruvkaushik305 opened this issue Dec 22, 2024 · 16 comments
Open
7 tasks done

prisma client could not be resolved #19036

dhruvkaushik305 opened this issue Dec 22, 2024 · 16 comments
Labels
has workaround p2-edge-case Bug, but has workaround or limited in scope (priority)

Comments

@dhruvkaushik305
Copy link

Describe the bug

I started with a default vercel template for react router v7, deployed it on vercel, everything works. Now when i add prisma and start the build, vite says ".prisma/client/default" is imported by ".prisma/client/default?commonjs-external", but could not be resolved – treating it as an external dependency. and deployment crashes with an internal server error.
I had already raised this issue on react-router but they closed it saying that this isn't a react router bug

Reproduction

https://github.com/dhruvkaushik305/testing-prisma.git

Steps to reproduce

run npm run build and the cli shows that error message

System Info

System:
    OS: Linux 6.1 Debian GNU/Linux 12 (bookworm) 12 (bookworm)
    CPU: (8) x64 Intel(R) Core(TM) i5-10210U CPU @ 1.60GHz
    Memory: 14.32 GB / 19.20 GB
    Container: Yes
    Shell: 5.9 - /usr/bin/zsh
  Binaries:
    Node: 22.12.0 - ~/.nvm/versions/node/v22.12.0/bin/node
    npm: 10.9.0 - ~/.nvm/versions/node/v22.12.0/bin/npm
    pnpm: 9.14.4 - ~/.nvm/versions/node/v22.12.0/bin/pnpm
    bun: 1.1.38 - ~/.bun/bin/bun
  Browsers:
    Brave Browser: 131.1.73.101

Used Package Manager

npm

Logs

These are the server logs on vercel

TypeError [ERR_INVALID_MODULE_SPECIFIER]: Invalid module ".prisma/client/default" is not a valid package name imported from /var/task/assets/server-build-Bi4c-HXU.js
    at parsePackageName (node:internal/modules/esm/resolve:786:11)
    at packageResolve (node:internal/modules/esm/resolve:809:5)
    at moduleResolve (node:internal/modules/esm/resolve:931:18)
    at moduleResolveWithNodePath (node:internal/modules/esm/resolve:1173:14)
    at defaultResolve (node:internal/modules/esm/resolve:1216:79)
    at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:542:12)
    at ModuleLoader.resolve (node:internal/modules/esm/loader:510:25)
    at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:239:38)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:96:40)
    at link (node:internal/modules/esm/module_job:95:36)

Validations

@sapphi-red
Copy link
Member

More simple reproduction: https://stackblitz.com/edit/github-jxnpwg49?file=server%2Fapp.ts,vite.config.ts,package.json&terminal=build (needs to run locally as prisma does not work in stackblitz)

It seems prisma generates node_modules/.prisma/client and imports that from node_modules/@prisma/client/default.js by require('.prisma/client/default').
While require('.prisma/client/default') works, Vite does not support that because it's not supported for imports (i.e. import '.prisma/client/default' errors with ERR_INVALID_MODULE_SPECIFIER (spec). Technically, Vite should allow that to be resolved for require, but I think it's better to be avoided and the default generated location should be changed on prisma side.

For a workaround, you can change the location.
https://www.prisma.io/docs/orm/prisma-client/setup-and-configuration/generating-prisma-client#the-location-of-prisma-client
For example, update schema.prisma to include the following code:

generator client {
  provider = "prisma-client-js"
  output = "../node_modules/@prisma/client-generated"
}

and update the import to use @prisma/client-generated (instead of @prisma/client).

@hi-ogawa
Copy link
Collaborator

hi-ogawa commented Dec 23, 2024

Not sure this is actionable for us either. Just to note, this repro is on Vite 5, but there is a prisma related issue with Vite 6 as well in remix-run/react-router#12610.
(Commented at the same time before seeing sapphi's reply 😄)

@sapphi-red sapphi-red added has workaround p2-edge-case Bug, but has workaround or limited in scope (priority) labels Dec 23, 2024
@dhruvkaushik305
Copy link
Author

More simple reproduction: https://stackblitz.com/edit/github-jxnpwg49?file=server%2Fapp.ts,vite.config.ts,package.json&terminal=build (needs to run locally as prisma does not work in stackblitz)

It seems prisma generates node_modules/.prisma/client and imports that from node_modules/@prisma/client/default.js by require('.prisma/client/default'). While require('.prisma/client/default') works, Vite does not support that because it's not supported for imports (i.e. import '.prisma/client/default' errors with ERR_INVALID_MODULE_SPECIFIER (spec). Technically, Vite should allow that to be resolved for require, but I think it's better to be avoided and the default generated location should be changed on prisma side.

For a workaround, you can change the location. https://www.prisma.io/docs/orm/prisma-client/setup-and-configuration/generating-prisma-client#the-location-of-prisma-client For example, update schema.prisma to include the following code:

generator client {
  provider = "prisma-client-js"
  output = "../node_modules/@prisma/client-generated"
}

and update the import to use @prisma/client-generated (instead of @prisma/client).

I changed the output as

generator client {
  provider = "prisma-client-js"
  output   = "../node_modules/@prisma/client-generated"
}

and also changed the import as import { PrismaClient } from "@prisma/client-generated";

While building vite said this

node_modules/@prisma/client-generated/runtime/library.js (64:923): Use of eval in "node_modules/@prisma/client-generated/runtime/library.js" is strongly discouraged as it poses security risks and may cause issues with minification.

and the server logs on vercel have this error

ReferenceError: __dirname is not defined in ES module scope
This file is being treated as an ES module because it has a '.js' file extension and '/var/task/package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.
at file:///var/task/assets/server-build-CVaE3q-I.js:23421:16
at ModuleJob.run (node:internal/modules/esm/module_job:234:25)
at async ModuleLoader.import (node:internal/modules/esm/loader:473:24)
at async requestHandler (file:///var/task/assets/app-QdoKnqb0.js:11639:44)
at async file:///var/task/assets/app-QdoKnqb0.js:12588:23

Is there something that i am missing?

@silvenon
Copy link

silvenon commented Dec 27, 2024

This issue is unrelated to remix-run/react-router#12610, in there React Router misconfigures Vite v6, so it fails to resolve @prisma/client. Also, as the simplified StackBlitz example implies, this issue is unrelated to React Router entirely.

I tried various combinations of solutions, but Vite refuses to bundle the generated .prisma/client. I also tried generating the client outside of node_modules and importing the client directly, which helped with bundling to a certain degree, but for some reason left some require() statements in the build. I'm not sure why Vite didn't bundle that, those were top-level require()s 🤷‍♂️ I guess it has trouble with bundling CJS packages that aren't listed in package.json.

The only thing that seems to have worked was patching both @prisma/client and the generated client with an ESM build. I only used small demo code to check, so I'm not sure how it would work in practice I meticulously converted all of necessary modules into ESM, so Prisma adding an ESM build would definitely make the problem go away without a need for workarounds.

Btw, prisma/prisma#21094 seems related to this issue.

@silvenon
Copy link

I found that rebasing all require(".prisma/client/...") to be relative rather than absolute in combination with replacing all __dirname with import.meta.dirname seems to do the trick in the StackBlitz demo, so try it in your React Router app:

import { defineConfig } from "vite";

export default defineConfig({
  plugins: [
    {
      name: "prisma;rebase",
      enforce: "pre",
      transform(code, id) {
        if (id.includes("@prisma/client")) {
          return code.replaceAll(
            /require\((['"])\.prisma\/client/g,
            "require($1../../.prisma/client",
          );
        }
      },
    },
    {
      name: "prisma:dirname",
      transform(code, id) {
        if (id.includes("@prisma/client") || id.includes(".prisma/client")) {
          return code.replaceAll("__dirname", "import.meta.dirname");
        }
      },
    },
  ],
  build: {
    ssr: "./server/app.ts",
  },
  ssr: {
    noExternal: true,
  },
});

The question now is whether this is something that Vite is willing to fix, or whether I should submit a request for ESM build to Prisma with this use case as an example, or both.

@dhruvkaushik305
Copy link
Author

@silvenon apologies for the delay, i implemented this plugin and it did work during the local build, i did not see any weird errors/warnings. I deployed this change to vercel and things aren't working there yet. This was the error message in the logs

Error: PrismaClient is unable to run in this browser environment, or has been bundled for the browser (running in Node.js).
If this is unexpected, please open an issue: https://pris.ly/prisma-prisma-bug-report
at Object.get (file:///var/task/assets/server-build-DmvRm3sn.js:23630:17)
at loader (file:///var/task/assets/server-build-DmvRm3sn.js:23664:34)
at callRouteHandler (file:///var/task/assets/app-Ch0vjp69.js:11173:22)
at commonRoute.loader (file:///var/task/assets/app-Ch0vjp69.js:11281:25)
at actualHandler (file:///var/task/assets/app-Ch0vjp69.js:6073:14)
at file:///var/task/assets/app-Ch0vjp69.js:6084:91
at runHandler (file:///var/task/assets/app-Ch0vjp69.js:6089:7)
at callLoaderOrAction (file:///var/task/assets/app-Ch0vjp69.js:6134:22)
at Object.resolve3 [as resolve] (file:///var/task/assets/app-Ch0vjp69.js:6029:27)
at file:///var/task/assets/app-Ch0vjp69.js:6012:64

@silvenon
Copy link

For some reason importing .prisma/client matches its browser export instead of node in Vite v5. In Vite v6 the correct export is matched.

This is not a React Router issue because the same issue is present in the StackBlitz demo without React Router as well.

You can work around it by forcing the /index import in the prisma:rebase plugin:

return code.replaceAll(
  /require\((['"])\.prisma\/client\/default/g,
  "require($1../../.prisma/client/index"
);

Also, I recommend moving the Prisma plugins before the React Router plugin, to be on the safe side.

@dhruvkaushik305
Copy link
Author

this is how i set up the plugins

plugins: [{
    name: "prisma:rebase",
    enforce: "pre",
    transform(code, id) {
      if (id.includes("@prisma/client")) {
        return code.replaceAll(
          /require\((['"])\.prisma\/client\/default/g,
          "require($1../../.prisma/client/index"
        );
      }
    },
  },
  {
    name: "prisma:dirname",
    transform(code, id) {
      if (id.includes("@prisma/client") || id.includes(".prisma/client")) {
        return code.replaceAll("__dirname", "import.meta.dirname");
      }
    },
  },reactRouter(), tsconfigPaths(),],

while building it locally it said node_modules/@prisma/client/runtime/library.js (64:923): Use of eval in "node_modules/@prisma/client/runtime/library.js" is strongly discouraged as it poses security risks and may cause issues with minification. and after pushing it to vercel, this is the server log

SyntaxError: Cannot use 'import.meta' outside a module
    at om (file:///var/task/assets/server-build-BdzsStJ4.js:26479:68)
    at async el (file:///var/task/assets/server-build-BdzsStJ4.js:26472:49)
    at async Object.loadLibrary (file:///var/task/assets/server-build-BdzsStJ4.js:27015:27)
    at async _r.loadEngine (file:///var/task/assets/server-build-BdzsStJ4.js:27124:54)
    at async _r.instantiateLibrary (file:///var/task/assets/server-build-BdzsStJ4.js:27103:68)
    at async _r.start (file:///var/task/assets/server-build-BdzsStJ4.js:27159:9)
    at async _r.request (file:///var/task/assets/server-build-BdzsStJ4.js:27200:7)
    at async Object.singleLoader (file:///var/task/assets/server-build-BdzsStJ4.js:27559:111)
    at async qn.request (file:///var/task/assets/server-build-BdzsStJ4.js:27571:14)
    at async l2 (file:///var/task/assets/server-build-BdzsStJ4.js:27966:17) {
  clientVersion: '6.1.0'
}

@silvenon
Copy link

silvenon commented Dec 30, 2024

The error is misleading, it is a module, because as soon as I don't use import.meta.dirname it starts complaining about __filename, and if I get rid of that then it starts complaining about using exports because it IS a module.

So this is out of my league, sorry. I don't know where the root of these problems is, whether it's Vite's CommonJS interoperability, or Prisma's CJS-only exports (prisma/prisma#21858), or both.

@silvenon
Copy link

silvenon commented Dec 30, 2024

For quick debugging in your project you don't have to actually run the thing on Vercel, I ran:

node .vercel/output/functions/index.func/assets/server-build-BcQwqtm4.js

and got the same error. That way you can iterate on your solution more quickly.

@silvenon
Copy link

silvenon commented Dec 30, 2024

And considering Prisma's very slow movement towards ESM I recomend to both of us that we try out Drizzle, if that's a viable option for you.

@dhruvkaushik305
Copy link
Author

For quick debugging in your project you don't have to actually run the thing on Vercel, I ran:

node .vercel/output/functions/index.func/assets/server-build-BcQwqtm4.js

and got the same error. That way you can iterate on your solution more quickly.

Wow that's really helpful, thank you so much, I really appreciate your help and efforts ❤️

@silvenon
Copy link

You're welcome, it was an interesting problem to try to solve, too bad it didn't work out, though. I'll try to isolate the issue and report it separately to Vite.

@silvenon
Copy link

silvenon commented Dec 30, 2024

What you can do to help is include bundling as a detail in your title because that's important. That way people won't think that Vite is generally failing to resolve Prisma. Some examples:

  • "unable to bundle Prisma for serverless functions"
  • "generated .prisma/client remains externalized despite enabling ssr.noExternal"

Or leave as-is, your choice.

@silvenon
Copy link

silvenon commented Jan 1, 2025

@dhruvkaushik305

  • generate the client as @prisma/client-generated as instructed initially via output in schema, this way the plugin you won't have to deal with rebasing the path to .prisma/client
  • replace imports to @prisma/client with @prisma/client-generated
  • replace __dirname/__filename with import.meta.dirname/import.meta.filename
  • replace eval("__dirname") with just import.meta.dirname, no eval() call, this was the one that was causing the error!
    • eval("__dirname") works, but eval("import.meta.dirname") doesn't, and I see no need for it
{
  name: "prisma:build",
  apply: "build",
  config() {
    return {
      define: {
        __dirname: "import.meta.dirname",
        __filename: "import.meta.filename",
      },
    };
  },
  transform(code, id) {
    if (id.includes("@prisma/client-generated")) {
      return code.replace('eval("__dirname")', "import.meta.dirname");
    }
  },
},

This is the only plugin needed now.

Possibly the only remaining problem that you might have during runtime is missing Prisma binary, for example node_modules/@prisma/client-generated/libquery_engine-darwin-arm64.dylib.node. Figure out which one you need for Vercel and you could either add a buildEnd() hook to the prisma:build plugin where you copy over the binary, or extend your .vercel/prepare.js script. I think it must be located directly in the index.func directory, outside of index.func/assets.

If you're still interested in pursuing this problem, let me know how it goes.

@silvenon
Copy link

silvenon commented Jan 1, 2025

@sapphi-red I don't think there's anything that Vite is doing wrong here. Unless you have plans to support imports to .prisma/client.

It's funny how far I went only to figure out that the workaround you initially recommended was the best way 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
has workaround p2-edge-case Bug, but has workaround or limited in scope (priority)
Projects
None yet
Development

No branches or pull requests

4 participants