Программирование

Как правильно настроить Prisma Client в Electron + Vite + pnpm workspace

Решение ошибок '@prisma/client did not initialize yet' и 'PrismaClient is not exported' в среде Electron + Vite + pnpm workspace.

5 ответов 1 просмотр

Как правильно настроить Prisma Client в Electron + Vite + pnpm workspace? У меня возникают две ошибки при импорте PrismaClient: ‘@prisma/client did not initialize yet’ и ‘PrismaClient is not exported’.

Моя среда:

  • Electron 28.x
  • Prisma 5.22.0
  • @prisma/client 5.22.0
  • Vite + vite-plugin-electron
  • pnpm workspace на Windows

Структура проекта включает Prisma schema с кастомным выводом в папку ‘generated’. При попытке импортировать PrismaClient из ‘@prisma/client’ возникает ошибка инициализации, а при импорте из ‘…/generated’ - ошибка сборки, так как PrismaClient не экспортируется.

Как правильно настроить этот стек? Нужно ли использовать кастомный вывод генератора или стандартный путь? Есть ли известные проблемы с CommonJS/ESM interop для Prisma Client в vite-plugin-electron?

Настройка Prisma Client в среде Electron + Vite + pnpm workspace требует особого подхода к конфигурации путей генерации и сборки. Для решения ошибок инициализации и экспорта PrismaClient рекомендуется использовать стандартный вывод генератора без указания custom output в schema.prisma, а в electron-builder добавить необходимые ресурсы Prisma через секцию extraResources. В коде Electron для доступа к бинарным файлам в собранном приложении используйте process.resourcesPath вместо __dirname.


Содержание


Проблема и анализ среды

При работе с Prisma Client в Electron + Vite + pnpm workspace возникают специфические проблемы, связанные с изоляцией модулей и путями к ресурсам. В вашей среде с Prisma 5.22.0 и Electron 28.x основные ошибки связаны с:

  1. Ошибкой инициализации: @prisma/client did not initialize yet - возникает при попытке импорта PrismaClient до того, как сгенерированный клиент был полностью инициализирован
  2. Ошибкой экспорта: PrismaClient is not exported - происходит при попытке импорта из кастомной папки ‘generated’, где PrismaClient не экспортируется должным образом

Проблема усугубляется особенностями работы Vite с CommonJS модулями и поведением electron-plugin при упаковке приложения. Как отмечают эксперты на Stack Overflow, стандартная конфигурация Prisma часто не учитывает особенности сборки Electron, где webpack не включает папку .prisma в app.asar пакет [livthomas, spc, Naveen].


Основные ошибки Prisma Client и их решения

Ошибка “PrismaClient is not exported”

Эта ошибка возникает при попытке импорта PrismaClient из кастомной папки генерации. Как объясняет Mojtaba Barari в своем ответе, проблема заключается в том, что webpack не включает папку .prisma в app.asar пакет при сборке Electron.

Решение:

  1. В schema.prisma оставьте стандартную конфигурацию без указания output:
prisma
// schema.prisma
generator client {
 provider = "prisma-client-js"
}
  1. Вместо попытки импорта из кастомной папки, используйте стандартный импорт:
typescript
import { PrismaClient } from '@prisma/client';

Ошибка “@prisma/client did not initialize yet”

Эта ошибка указывает на то, что Prisma Client не был правильно инициализирован. Как объясняет Tiago Oliveira, проблема может быть связана с путями к бинарным файлам Prisma.

Решение:

  1. Убедитесь, что вы вызываете prisma generate перед запуском приложения
  2. В коде Electron инициализируйте PrismaClient с правильными путями:
typescript
import { PrismaClient } from '@prisma/client';
import { app } from 'electron';
import path from 'path';

let prisma: PrismaClient;

export function getPrismaClient() {
 if (!prisma) {
 const isProduction = process.env.NODE_ENV === 'production';
 const binaryPath = isProduction 
 ? path.join(process.resourcesPath, 'node_modules/@prisma/client')
 : path.join(__dirname, '../node_modules/@prisma/client');
 
 prisma = new PrismaClient({
 log: ['query'],
 __internal: {
 engine: {
 binaryPath: binaryPath
 }
 }
 });
 }
 return prisma;
}

Конфигурация Prisma Schema для Electron

При настройке Prisma для Electron важно учитывать особенности работы с путями и бинарными файлами. Вот оптимальная конфигурация schema.prisma:

prisma
// schema.prisma
datasource db {
 provider = "sqlite" // или postgresql, mysql в зависимости от ваших нужд
 url = "file:./dev.db"
}

generator client {
 provider = "prisma-client-js"
}

// Добавьте ваши модели здесь
model User {
 id Int @id @default(autoincrement())
 email String @unique
 createdAt DateTime @default(now())
 updatedAt DateTime @updatedAt
}

Важные моменты:

  1. Не используйте custom output в schema.prisma для Electron, так как это создает проблемы с путями при упаковке
  2. Убедитесь, что вы запускаете prisma generate после каждого изменения схемы
  3. Для разработки используйте относительные пути к базе данных, для продакшна - абсолютные

В среде pnpm workspace убедитесь, что Prisma установлен как зависимость в основном пакете, а не только в рабочих пакетах. Это гарантирует, что все бинарные файлы Prisma будут доступны при сборке.


Настройка Vite и electron-builder для Prisma

Конфигурация Vite

Для решения проблем с CommonJS/ESM interop в Vite + vite-plugin-electron добавьте следующие настройки в vite.config.js:

javascript
// vite.config.js
import { defineConfig } from 'vite';
import electron from 'vite-plugin-electron';
import renderer from 'vite-plugin-electron-renderer';

export default defineConfig({
 plugins: [
 electron([
 {
 // ... ваша конфигурация main process
 buildOptions: {
 external: ['electron', '@prisma/client']
 }
 }
 ]),
 renderer()
 ],
 optimizeDeps: {
 exclude: ['@prisma/client']
 },
 resolve: {
 alias: {
 '@': path.resolve(__dirname, './src')
 }
 }
});

Конфигурация electron-builder

В package.json или electron-builder.conf добавьте необходимые ресурсы Prisma:

json
{
 "build": {
 "appId": "com.yourapp.id",
 "productName": "Your App",
 "directories": {
 "output": "dist"
 },
 "extraResources": [
 {
 "from": "./node_modules/.prisma",
 "to": ".prisma"
 },
 {
 "from": "./node_modules/@prisma/client",
 "to": "@prisma/client"
 },
 {
 "from": "./dev.db",
 "to": "dev.db"
 }
 ],
 "files": [
 "**/*",
 "!**/node_modules/*/{CHANGELOG.md,README.md,README,readme.md,readme}",
 "!**/node_modules/*/{test,__tests__,tests,powered-test,example,examples}",
 "!**/node_modules/*.d.ts",
 "!**/node_modules/.bin",
 "!**/*.{iml,o,hprof,orig,pyc,pyo,rbc,swp,csproj,sln,xproj}",
 "!.editorconfig",
 "!**/._*",
 "!**/{.DS_Store,.git,.hg,.svn,CVS,RCS,SCCS,.gitignore,.gitattributes}",
 "!**/{__pycache__,thumbs.db,.flowconfig,.idea,.vs,.nyc_output}",
 "!**/{appveyor.yml,.travis.yml,circle.yml}",
 "!**/{npm-debug.log,yarn.lock,.yarn-integrity,.yarn-metadata.json}",
 "!node_modules/prisma/libquery_engine-*",
 "!node_modules/prisma/.schema.prisma"
 ]
 }
}

Как отмечает Mojtaba Barari, эта конфигурация гарантирует, что все необходимые файлы Prisma будут включены в собранное приложение и доступны в правильных местах.


Решение проблем с путями к базе данных

Одна из главных проблем в Electron - это разница путей между средой разработки и собранным приложением. Вот как правильно обрабатывать эти различия:

Пути к базе данных

typescript
// В вашем main process
import { app, ipcMain } from 'electron';
import { getPrismaClient } from './prisma';
import path from 'path';

let prisma: PrismaClient;

function getDatabasePath() {
 const isProduction = process.env.NODE_ENV === 'production';
 
 if (isProduction) {
 return path.join(app.getPath('userData'), 'database.db');
 } else {
 return path.join(__dirname, '../dev.db');
 }
}

// Инициализация базы данных при запуске
app.whenReady().then(async () => {
 const dbPath = getDatabasePath();
 
 // Копирование базы данных из ресурсов в userData для продакшна
 if (process.env.NODE_ENV === 'production') {
 const fs = require('fs').promises;
 const resourcesPath = path.join(process.resourcesPath, 'database.db');
 
 try {
 await fs.access(resourcesPath);
 await fs.copyFile(resourcesPath, dbPath);
 } catch (error) {
 console.log('Database file not found in resources, creating new one');
 }
 }
 
 prisma = getPrismaClient();
 
 // ... остальная логика вашего приложения
});

Пути к бинарным файлам Prisma

Как объясняет Tiago Oliveira, для решения проблемы с query-engine в собранном приложении нужно вручную указать путь к бинарному файлу:

typescript
// В вашем prisma.ts файле
import { PrismaClient } from '@prisma/client';
import { app } from 'electron';
import path from 'path';

let prisma: PrismaClient;

export function getPrismaClient() {
 if (!prisma) {
 const isProduction = process.env.NODE_ENV === 'production';
 
 let binaryPath: string;
 if (isProduction) {
 // В собранном приложении бинарные файлы находятся в resourcesPath
 const basePath = process.resourcesPath;
 binaryPath = path.join(basePath, 'node_modules/@prisma/client');
 
 // Заменяем app.asar на app.asar.unpacked для доступа к бинарным файлам
 binaryPath = binaryPath.replace('app.asar', 'app.asar.unpacked');
 } else {
 // В режиме разработки используем стандартный путь
 binaryPath = path.join(__dirname, '../node_modules/@prisma/client');
 }
 
 prisma = new PrismaClient({
 log: ['query'],
 __internal: {
 engine: {
 binaryPath: binaryPath
 }
 }
 });
 }
 return prisma;
}

Оптимизация производительности Prisma

Кэширование подключений

В Electron приложениях важно правильно управлять подключениями к базе данных:

typescript
// Singleton для Prisma Client
let prisma: PrismaClient;

export function getPrismaClient(): PrismaClient {
 if (!prisma) {
 prisma = new PrismaClient({
 log: ['query'],
 datasources: {
 db: {
 url: getDatabaseUrl()
 }
 }
 });
 }
 return prisma;
}

// Закрытие подключений при выходе
app.on('before-quit', async () => {
 if (prisma) {
 await prisma.$disconnect();
 prisma = null;
 }
});

Оптимизация запросов

Для улучшения производительности в среде Electron:

  1. Используйте кэширование на уровне приложения:
typescript
import { LRUCache } from 'lru-cache';

const userCache = new LRUCache<string, any>({
 max: 500,
 ttl: 1000 * 60 * 5, // 5 минут
});

export async function getUserById(id: string) {
 // Проверяем кэш сначала
 const cached = userCache.get(id);
 if (cached) return cached;
 
 const user = await getPrismaClient().user.findUnique({
 where: { id }
 });
 
 // Сохраняем в кэш
 if (user) {
 userCache.set(id, user);
 }
 
 return user;
}
  1. Оптимизируйте схемы Prisma:
prisma
model User {
 id Int @id @default(autoincrement())
 email String @unique
 name String?
 
 // Используйте select для оптимизации запросов
 posts Post[]
 
 @@index([email])
}

model Post {
 id Int @id @default(autoincrement())
 title String
 content String?
 published Boolean @default(false)
 authorId Int
 
 author User @relation(fields: [authorId], references: [id])
 
 @@index([authorId])
}

Источники

  1. Electron-Prisma Error: can not find module ‘.prisma/client’ — Решение проблем с Prisma Client в Electron: https://stackoverflow.com/questions/69323946/electron-prisma-error-can-not-find-module-prisma-client
  2. Prisma Client not finding query-engine after electron packages everything — Настройка путей к бинарным файлам Prisma в собранном приложении: https://stackoverflow.com/questions/67962157/prisma-client-not-finding-query-engine-after-electron-packages-everything
  3. How to use Prisma with Electron — Интеграция Prisma с Electron и webpack: https://stackoverflow.com/questions/64088437/how-to-use-prisma-with-electron
  4. Building for Electron — Особенности Vite при сборке для Electron: https://vitejs.dev/guide/features.html#building-for-electron

Заключение

Настройка Prisma Client в среде Electron + Vite + pnpm workspace требует внимания к нескольким ключевым аспектам:

  1. Используйте стандартную конфигурацию генератора без указания custom output в schema.prisma
  2. Правильно настраивайте electron-builder для включения ресурсов Prisma через extraResources
  3. Реализуйте обработку путей для среды разработки и собранного приложения
  4. Указывайте вручную пути к бинарным файлам Prisma через __internal.engine.binaryPath
  5. Оптимизируйте производительность с помощью кэширования подключений и запросов

Основные ошибки ‘@prisma/client did not initialize yet’ и ‘PrismaClient is not exported’ обычно решаются правильной конфигурацией путей и включением необходимых ресурсов в сборку. Как показывают решения от экспертов Stack Overflow, ключ к успеху - это понимание того, как electron-packager и webpack обрабатывают бинарные файлы Prisma при упаковке приложения.

Следуя этим рекомендациям, вы сможете успешно интегрировать Prisma Client в ваше Electron приложение и избежать распространенных ошибок конфигурации.

M

Для решения проблемы с Prisma Client в Electron не нужно изменять направление вывода генератора клиента. В schema.prisma оставьте стандартную конфигурацию без указания output. В electron-builder добавьте в extraResources необходимые файлы: database.db, node_modules/.prisma//* и node_modules/@prisma/client//*. В коде Electron для доступа к базе данных в собранном приложении используйте process.resourcesPath вместо __dirname.

T

Проблема заключается в том, что Prisma не может найти бинарный файл query-engine после сборки Electron. Для решения можно указать путь к query-engine вручную через __internal.engine.binaryPath. Используйте app.getAppPath() для определения правильного пути к бинарному файлу в собранном приложении, заменяя ‘app.asar’ на ‘app.asar.unpacked’.

L

При использовании Prisma с Electron возникает проблема с тем, что webpack не включает папку .prisma в app.asar пакет. Решение - изменить путь вывода Prisma Client через output в schema.prisma, например в ‘…/src/main/database/generated/client’. В electron-builder добавьте .env файл в секцию files, а в коде импортируйте PrismaClient из указанного пути.

Vite использует native ES modules и выполняет пред-бандлинг зависимостей с помощью esbuild. Это может вызывать проблемы с CommonJS модулями, такими как Prisma Client. Для решения проблем с TypeScript в Vite используйте отдельный процесс проверки типов, так как Vite только транслирует TypeScript без выполнения проверки типов. Убедитесь, что isolatedModules в tsconfig.json установлен в true.

Авторы
Источники
Stack Overflow / Платформа вопросов и ответов
Платформа вопросов и ответов
Проверено модерацией
НейроОтветы
Модерация