Svelte Kit and Prisma is a go-to stack for me these days. I like its great ergonomics in frontend and less cost of cognitive load from context switches between server sides.
Users of Svelte Kit, Nuxt or other Vite based frameworks haven't been able to import PrismaClient or other exports in the way the documentation suggests1.
which leads you to see:
import Prisma, { PrismaClient } from "@prisma/client";
^^^^^^^^^^^^
SyntaxError: Named export 'PrismaClient' not found. The requested module '@prisma/client' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:
import pkg from '@prisma/client';
const { PrismaClient } = pkg;
While there's several workarounds known in the community, they still have some shortcomings like inability to import Enum values or Prisma
object for SQL templates nicely.
For the context, when you use Prisma, what you use is not something in @prisma/client
, but a code locally generated prisma generate command, which in turn @prisma/client
would import inside. Since Prisma need to provide you a custom made API that reflects your schema, it wouldn't able to come as like an ordinary package.
As a result, @prisma/client
contains just a line of code or two, to import the custom-made client supposedly generated at the time of deployment. It's really small piece of code:
const prisma = require('.prisma/client/index')
module.exports = prisma
Since this glues the application code and generated code, I would call this as Prisma's glue code.
This works fine for CJS code to require. Similarly, the code that would be transpiled by TypeScript or other bundlers can work with this, since those tools take care at compile time.
The problem is ES modules, who cannot recognize any exports other than default exports.
Svelte Kit (as far as I know also Nuxt or other Vite based frameworks, depending on configs) emits ES modules as their build artifacts, unlike Next that emits CJS bundles, and encounters this problem.
But why?
When an ES module tries to import
named exports from other CJS modules, Node recognizes only the exports that explicitly defiend in specific syntax. They would be detected before they get executed with static analysis 2
For better compatibility with existing usage in the JS ecosystem, Node.js in addition attempts to determine the CommonJS named exports of every imported CommonJS module to provide them as separate ES module exports using a static analysis process.
The detection of named exports is based on common syntax patterns but does not always correctly detect named exports.
Because of this, Prisma's glue code effectively reexports just default export from perspective of Node, while TS compiler somehow recognized the named exports. This causes a weird situation where they would work fine in dev server, but fails with the error message like this once they have been bundled for production, as ES modules with static import
s.
Fortunately, Node doesn't detect only export.name = value
. It seems to support more involved syntax, like 3:
module.exports = {
...require('.prisma/client/index'),
}
Which I sent as a pull request to Prisma. I'm not sure when or even if it would be accepted, I assume you would be able to use this hook in package.json
for the time being. Assuming above glue code replacement (JS snippet) saved as patches/prisma-fixed-glue.js
, add this:
"build": "cp patches/prisma-fixed-glue.js node_modules/@prisma/client/index.js && svelte-kit build",
(I use pnpm which no longer supports lifecycle scripts in default, but you can put prebuild
as long as you enabled it or use npm)
Top comments (2)
It's also possible to achieve a goal with the following code:
Well that's no longer necessary because my PR is merged in Prisma 3.14.