[Kit]: Anchor drizzle-orm package probes to the project directory - #6172
Open
SayantanDutt wants to merge 1 commit into
Open
[Kit]: Anchor drizzle-orm package probes to the project directory#6172SayantanDutt wants to merge 1 commit into
SayantanDutt wants to merge 1 commit into
Conversation
SayantanDutt
force-pushed
the
fix/6156-package-probe-resolution
branch
from
August 24, 2026 06:37
af4d481 to
67e3ba4
Compare
drizzle-kit's package probes in utils.ts resolved drizzle-orm relative to bin.cjs's own realpath rather than the project directory, breaking detection under pnpm's global virtual store and similar symlink layouts. Anchor resolution via createRequire(process.cwd()) instead. Addresses part of drizzle-team#6156, does not fully resolve it. See PR description for the remaining scope. Signed-off-by: Sayantan <duttasayantan257@gmail.com>
SayantanDutt
force-pushed
the
fix/6156-package-probe-resolution
branch
from
August 24, 2026 06:56
67e3ba4 to
b63485e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This addresses part of #6156. I'm deliberately not using a closing keyword here, since this fixes the reported symptom but not the whole problem. See the known limitation section below.
Problem
Drizzle Kit reports:
even though drizzle-orm is installed correctly. The original report was on nub, and I could also reproduce it with pnpm when the global virtual store is enabled.
Root cause
The probes in
drizzle-kit/src/cli/utils.tsdetect packages with a bare dynamic import:I traced this down, and the real problem is where that specifier gets resolved from, not which loader does the resolving.
With pnpm's global virtual store enabled,
node_modules/drizzle-kitends up as a symlink into a store outside the project:Node resolves the CLI's realpath, so
bin.cjsactually executes from inside that store. Both ESM and CJS resolve a bare specifier by walkingnode_modulesupward from the importing module's own location, and from inside the store, that walk never reaches the user's project.I measured this directly from inside the store, same cwd, same specifier:
await import('drizzle-orm/version')require('drizzle-orm/version')createRequire(process.cwd()).resolve(...)The identical file placed inside the project resolves fine via plain
import(), so this is purely about the CLI's own location relative to the project.I want to flag two corrections to the original issue's diagnosis, both of which I verified directly:
require()fails identically toimport()in this layout. Swapping loaders alone doesn't fix anything.require()succeed, because NODE_PATH feedsmodule.globalPaths, which only the CJS resolver consults. But I could reproduce the failure with NODE_PATH unset, so it isn't what's actually breaking here.The fix works because
createRequire(process.cwd())anchors resolution to the user's project directory, independent of where the CLI binary physically lives. That anchor is the part doing the work, not CJS, not NODE_PATH.The fix
I added one helper in
drizzle-kit/src/cli/utils.tsand routed the probes through it:A few notes on why I built it this way:
import()stays the fast path. The fallback only runs after it fails, so layouts that already worked are untouched.require.resolvegives an absolute path that honors package exports subpaths, and importing that resolved path keeps ESM-only packages loadable, which a directrequire()wouldn't allow (ERR_REQUIRE_ESM).process.cwd()since that's the actual fix. It also sidestepsimport.meta.url/__filename, neither of which works across both bundles drizzle-kit ships (import.meta.urlbreaks the CJS build,__filenamebreaks the ESM one).import()error when neither strategy resolves, so a genuinely missing package still reports as missing instead of a confusing fallback error.I updated all six probe functions that had this bug:
ormVersionGt,checkPackage,assertPackages,assertEitherPackage,assertOrmCoreVersion,ormCoreVersions.I kept the scope confined to this one file. I didn't touch any snapshot, diffing, or dialect logic.
This fixes the specific probe failure that was reported, drizzle-kit falsely claiming drizzle-orm isn't installed, and I verified that against a real pnpm
enableGlobalVirtualStorelayout (details below).It doesn't make drizzle-kit work end to end in that layout, though. After this fix,
drizzle-kit generategets past the version check and then fails further downstream:drizzle-orm is marked external in the build, so the shipped bundle resolves it at roughly 63 other call sites, about 33
require("drizzle-orm…")calls (11 bare drizzle-orm, plus /casing, /relations, and the dialect cores) and 30import("drizzle-orm/…")calls (/version, /relations, /pg-core, and the driver + migrator pairs). None of those are cwd-anchored, so each one still resolves from bin.cjs's own location and the failure just moves to the next unguarded site.Fixing that bundle-wide is a materially bigger change than this PR, and I see two reasonable approaches that differ a lot in blast radius: an esbuild banner installing a cwd-anchored resolver for the whole bundle, or routing every external drizzle-orm resolution through one shared helper. I didn't want to guess at which one you'd prefer, since it depends on build and packaging constraints and on whether these layouts are even meant to be supported. Worth mentioning the same anchor question applies to this PR too:
process.cwd()might need to become the config file's directory instead, for monorepos or invocation from a subdirectory.Because of all this, I think #6156 should stay open after this merges.
Tests
I added
drizzle-kit/tests/cli-package-resolution.test.tswith 3 cases that exercise the real probes in a child process:import()fails while the probe resolves it and the module actually loads.I confirmed these are real regression tests by reverting the fix, cases 1 and 2 fail (expected false to be true) and case 3 still passes.
One caveat worth flagging on the tests: they exercise the fallback mechanism via NODE_PATH, since that's the easiest way to make a package invisible to
import()in-process. They don't reproduce the actual realpath-anchoring scenario that triggers #6156, that required a full pnpm install and I only verified it manually (below). Happy to strengthen this if you'd want the real trigger covered in CI.Verification
I ran this end to end against a real pnpm global virtual store (pnpm 11.23.0; worth noting
enable-global-virtual-storein .npmrc gets silently ignored, it only takes effect via pnpm-workspace.yaml):Please install latest version of drizzle-orm, exit 1 (reproduces #6156 verbatim)[✓] Your SQL migration file ➜ migrations/0000_….sqlThe control run confirms the fix doesn't regress anything in standard layouts.
Static checks:
tsc -p tsconfig.build.json --noEmit→ clean, exit 0.Same 10 failing files in both runs. Those failures are environmental on my machine, unrelated to this change: Docker wasn't running (
connect ENOENT //./pipe/docker_engine, needed by the MySQL/SingleStore/Gel suites) and better-sqlite3's native binding never compiled (no MSVC toolchain here). The +3 passing tests are the new ones I added.Notes for reviewers
generateprints the error but still exits 0 and writes no migration SQL, the only output is an emptymigrations/metadirectory. Anything that gates on exit code reads that as success. That's independent of this PR and of the layout that triggered it, a misleading success code is a problem regardless of the underlying cause. I didn't fix it here, but happy to open a separate issue if that's useful.