Skip to content

fix: use dynamic import() to load Prisma client (fixes #22)#32

Open
tywenk wants to merge 2 commits intocodepunkt:mainfrom
tywenk:use-dynamic-import-for-client
Open

fix: use dynamic import() to load Prisma client (fixes #22)#32
tywenk wants to merge 2 commits intocodepunkt:mainfrom
tywenk:use-dynamic-import-for-client

Conversation

@tywenk
Copy link
Copy Markdown

@tywenk tywenk commented Apr 21, 2026

Fixes #22.

Problem

The current implementation uses createRequire(import.meta.url) to synchronously require() the Prisma client at options.clientPath:

const require = createRequire(import.meta.url);
const { PrismaClient } = require(options.clientPath);

This is incompatible with Prisma 7's prisma-client generator, which outputs plain TypeScript (ESM) files that Node's native CJS require() cannot load. Under Vitest, those TS files are transformed by Vite — but the plugin's createRequire-based require() bypasses Vite's loader entirely, so resolution fails with either Cannot find module (when pointed at an extensionless path) or ERR_MODULE_NOT_FOUND on transitive imports (when pointed at the .ts file).

Fix

Switch to await import(options.clientPath):

  • Under Vitest, clientPath pointed at the generated client.ts (or client.js) is handled by Vite's transform pipeline, which resolves TypeScript/ESM transparently.
  • Under plain Node, any ESM-compatible specifier works — package names (e.g. @prisma/client), file:// URLs, or .js/.mjs paths.

Before dispatching the dynamic import, clientPath is normalized so that relative (./foo, ../foo) and absolute filesystem paths are resolved against the consuming project's process.cwd() and converted to file:// URLs. Bare module specifiers and pre-built file:// URLs pass through unchanged. This means users can pass a natural project-relative path — clientPath: "./src/generated/prisma/client.ts" — without manually constructing a file:// URL, and absolute paths work correctly on Windows (where ESM import() otherwise rejects bare drive paths).

createContext becomes async; the only caller (environment.setup in src/index.ts) is already async and just needs an await.

Tested

  • Unit tests (11 tests across src/contest.test.ts and src/index.test.ts) all pass with 100% coverage preserved. Added a regression test that passes an absolute file:// URL as clientPath to cover the Prisma 7 path and the non-path-like branch of the resolver.

  • Verified end-to-end against Prisma 7's prisma-client generator in a downstream project: 792 integration tests pass. With the new normalization, the config can be as simple as:

    // vitest.config.ts
    environmentOptions: {
      "prisma-postgres": {
        clientPath: "./src/generated/prisma/client.ts",
      },
    }

    A manually constructed file:// URL via pathToFileURL(path.resolve(...)) still works too.

    generator client {
      provider            = "prisma-client"
      output              = "../src/generated/prisma"
      runtime             = "nodejs"
      moduleFormat        = "esm"
      importFileExtension = "ts"
    }

Breaking change?

Technically yes — createContext now returns a Promise. No public users of createContext directly are documented in the README; consumers interact only through the Vitest environment, which is updated here. Happy to add a note to CHANGELOG if desired.

Summary by CodeRabbit

  • Refactor
    • Context initialization is now asynchronous to support dynamic module loading and improve startup reliability.
  • Bug Fixes
    • Improved handling of relative and absolute client module paths, including file:// URLs, for more predictable module resolution.
  • Tests
    • Added coverage for absolute file:// client paths and full setup/teardown flow to prevent regressions.

The previous implementation used createRequire(import.meta.url) to
synchronously require() the Prisma client at options.clientPath.

This is incompatible with Prisma 7's `prisma-client` generator, which
outputs plain TypeScript (ESM) files that cannot be loaded by Node's
native CJS require().

Switch to await import(options.clientPath) so that:

- Under Vitest, clientPath can point at the generated TS source
  directly (e.g. a file:// URL to `client.ts`), and Vite's transform
  pipeline handles the TypeScript/ESM resolution.
- Under plain Node, any ESM-compatible specifier continues to work
  (package names, file:// URLs, .js/.mjs paths).

`createContext` becomes async; callers must `await` it. The only
caller is `environment.setup`, which is already async.

Fixes codepunkt#22
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 21, 2026

Note

.coderabbit.yaml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key(s) in object: 'review'
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
📝 Walkthrough

Walkthrough

createContext now performs a dynamic ESM import for the Prisma client and is async; callers and tests were updated to await context creation and to accept file://-style clientPath specifiers.

Changes

Cohort / File(s) Summary
Context loading (async ESM import)
src/context.ts
Replaced CommonJS createRequire(...).require(clientPath) with await import(clientImportPath); normalize relative/absolute paths to file:// URLs; createContext is now async.
Initialization flow
src/index.ts
Now awaits createContext(options) during environment setup so setup/teardown use the asynchronously created context.
Tests / helpers updated for async context & clientPath forms
src/contest.test.ts, src/index.test.ts
makeContext updated to await createContext(...) and return appropriate Promise typed tuple; tests switched clientPath from ../test/prisma-client-stub.js to ./test/prisma-client-stub.js and added a case using an absolute file:// URL to verify import and full setup/teardown.

Sequence Diagram(s)

sequenceDiagram
  participant Test as Test Runner
  participant Env as Environment (index)
  participant Context as createContext
  participant Client as Prisma Client (module)
  participant Mock as PrismaPgMock

  Test->>Env: environment.setup(options with clientPath)
  Env->>Context: await createContext(options)
  Context->>Client: import(clientImportPath)
  Client-->>Context: PrismaClient class/module
  Context->>Mock: instantiate/initialize with connectionString
  Context-->>Env: context (with setup/teardown)
  Env-->>Test: setup complete
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped from require to import with glee,
No more stuck on .ts for me,
file:// paths and async cheer,
Context comes ready — setup’s near! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR successfully addresses all coding requirements from issue #22: replacing createRequire()/require() with dynamic import(), adding path normalization for file:// URLs, making createContext async, and including a test case for file:// URL paths.
Out of Scope Changes check ✅ Passed All changes are directly related to issue #22 requirements: converting to dynamic import, normalizing paths, making createContext async, updating callers, and adding test coverage for file:// URLs.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the primary change: replacing synchronous CommonJS require() with dynamic ESM import() for loading the Prisma client, which is the core fix for issue #22.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/contest.test.ts (1)

23-24: Add a regression test case for file:// client paths to ensure Prisma 7/Vite compatibility.

The current test only covers a relative path. Adding a small test using pathToFileURL(...).href would verify that absolute file URLs work correctly as clientPath values.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/contest.test.ts` around lines 23 - 24, Add a regression test that passes
an absolute file:// URL as clientPath to createContext to cover Prisma 7/Vite
compatibility; specifically, in the test file where createContext is invoked
with clientPath '../test/prisma-client-stub.js', add a new test that constructs
a file URL via pathToFileURL(somePath).href and supplies that string as
clientPath, then assert the same behaviors as the relative-path case so both
relative and file:// absolute clientPath values are validated.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/context.ts`:
- Line 31: The dynamic import using options.clientPath should first convert
relative ("./" or "../") and absolute filesystem paths to file:// URLs so they
resolve from the consuming project's CWD instead of src/context.ts; leave bare
module specifiers (like "@prisma/client") and existing file:// URLs unchanged.
Update the import site where you do const { PrismaClient } = await
import(options.clientPath); to: detect if options.clientPath starts with
"file://" or is a bare specifier (no path separators), otherwise resolve with
path.resolve(process.cwd(), options.clientPath) and convert to a URL via
url.pathToFileURL(...).href, then call import(resolvedPath). Ensure path and url
utilities are imported/available in the module.

---

Nitpick comments:
In `@src/contest.test.ts`:
- Around line 23-24: Add a regression test that passes an absolute file:// URL
as clientPath to createContext to cover Prisma 7/Vite compatibility;
specifically, in the test file where createContext is invoked with clientPath
'../test/prisma-client-stub.js', add a new test that constructs a file URL via
pathToFileURL(somePath).href and supplies that string as clientPath, then assert
the same behaviors as the relative-path case so both relative and file://
absolute clientPath values are validated.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d70f8669-ff17-41d4-9fdd-5af4c6df5fc7

📥 Commits

Reviewing files that changed from the base of the PR and between fc5b11e and 9ca03c4.

📒 Files selected for processing (3)
  • src/contest.test.ts
  • src/context.ts
  • src/index.ts

Comment thread src/context.ts Outdated
Normalize relative and absolute filesystem clientPath values to file://
URLs before dynamic import so they resolve from the project root instead
of this module's install location. Bare specifiers and existing file://
URLs pass through unchanged. Adds a regression test covering the file://
URL path used by the Prisma 7 setup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tywenk tywenk changed the title Use dynamic import() to load Prisma client (fixes #22) fix: use dynamic import() to load Prisma client (fixes #22) Apr 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

clientPath incompatible with Prisma 7's prisma-client generator (TypeScript output)

1 participant