Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/guides/running-locally.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ needed; local dev never talks to the platform.

## Bring it up

Local Postgres requires `@prisma/dev` in the devDependencies of the project where
you run Composer (the root package for a monorepo). Prisma 8 does not include this
runtime; installing `prisma` alone is not enough. No emulator dependency is needed
for cloud deployment or a local app without Postgres resources.

Composer resolves `@prisma/dev` from the app first. For compatibility with
installations that supply it through `prisma`, it also checks relative to the
CLI's exported `prisma/package.json`; no JavaScript root export is required.

```sh
prisma-composer dev module.ts
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ describe('a bogus prismaDevModulePath', () => {
// `request to the local dev emulator failed (${status}): ${body}` —
// assert the status explicitly, not just that SOME error was thrown.
expect(message).toContain('(500)');
expect(message).toContain('local dev needs @prisma/dev');
expect(message).toContain('add "@prisma/dev" to the devDependencies');
expect(message).toContain(bogusPath);
// No OTHER filesystem path leaks — e.g. a dynamic `import()`
// failure's own message routinely names a SECOND path (the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const ALREADY_RUNNING_POLLS = 24;
const ALREADY_RUNNING_POLL_MS = 500;

const NOT_INSTALLED_MESSAGE =
'local dev needs @prisma/dev for its local Postgres emulator — add "prisma" to your app\'s devDependencies.';
'local dev needs @prisma/dev for its local Postgres emulator — add "@prisma/dev" to the devDependencies of the project where you run Composer.';

// The behavior contract's no-value-logging rule, applied to embedded
// diagnostics too (spec's diagnostics rule) — masks a connection URL's
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,6 @@ import * as os from 'node:os';
import * as path from 'node:path';
import { resolvePrismaDevModulePath } from '../postgres.ts';

/**
* `resolvePrismaDevModulePath`'s two-step resolution (local-dev spec § 4,
* REVISED — operator review of #162): resolve `@prisma/dev` directly from
* the app's own node_modules first; on failure, resolve `prisma` (which
* apps typically depend on, and which carries `@prisma/dev` as its own
* dependency) and resolve `@prisma/dev` from there; both failing throws the
* pinned error.
*/
describe('resolvePrismaDevModulePath', () => {
let cwd: string;

Expand Down Expand Up @@ -43,8 +35,12 @@ describe('resolvePrismaDevModulePath', () => {
expect(resolved).toBe(path.join(cwd, 'node_modules', '@prisma', 'dev', 'index.js'));
});

test("falls back to resolving @prisma/dev from prisma's own dependency tree", () => {
test('resolves an installed @prisma/dev through a CLI with no root export', () => {
writeModule(path.join(cwd, 'node_modules'), 'prisma');
fs.writeFileSync(
path.join(cwd, 'node_modules', 'prisma', 'package.json'),
JSON.stringify({ name: 'prisma', exports: { './package.json': './package.json' } }),
);
writeModule(path.join(cwd, 'node_modules', 'prisma', 'node_modules'), '@prisma/dev');

const resolved = resolvePrismaDevModulePath(cwd);
Expand All @@ -56,15 +52,19 @@ describe('resolvePrismaDevModulePath', () => {

test('neither @prisma/dev nor prisma installed throws the pinned error', () => {
expect(() => resolvePrismaDevModulePath(cwd)).toThrow(
'local dev needs @prisma/dev for its local Postgres emulator — add "prisma" to your app\'s devDependencies.',
'local dev needs @prisma/dev for its local Postgres emulator — add "@prisma/dev" to the devDependencies of the project where you run Composer.',
);
});

test('prisma installed but without @prisma/dev throws the pinned error', () => {
writeModule(path.join(cwd, 'node_modules'), 'prisma');
fs.writeFileSync(
path.join(cwd, 'node_modules', 'prisma', 'package.json'),
JSON.stringify({ name: 'prisma', exports: { './package.json': './package.json' } }),
);

expect(() => resolvePrismaDevModulePath(cwd)).toThrow(
'local dev needs @prisma/dev for its local Postgres emulator — add "prisma" to your app\'s devDependencies.',
'local dev needs @prisma/dev for its local Postgres emulator — add "@prisma/dev" to the devDependencies of the project where you run Composer.',
);
});
});
14 changes: 7 additions & 7 deletions packages/1-prisma-cloud/0-lowering/local-target/src/postgres.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,17 @@ function databaseIdOfInput(value: unknown): string | undefined {

function noPrismaDevError(): Error {
return new Error(
'local dev needs @prisma/dev for its local Postgres emulator — add "prisma" to your app\'s devDependencies.',
'local dev needs @prisma/dev for its local Postgres emulator — add "@prisma/dev" to the devDependencies of the project where you run Composer.',
);
}

/**
* Two-step resolution, pinned (local-dev spec § 4): (1) resolve
* `@prisma/dev` directly from the app's own `node_modules`; (2) on failure,
* resolve `prisma` (the CLI apps typically depend on, which itself carries
* `@prisma/dev`) and resolve `@prisma/dev` from THERE. The daemon imports
* the returned path dynamically, so the app stays in charge of its own
* Prisma version. `cwd` is the one place a local provider legitimately
* resolve `prisma/package.json` and check that installation for `@prisma/dev`
* (older CLI releases carry it; Prisma 8 does not). The daemon imports the
* returned path dynamically, so the app owns its local Postgres runtime.
* `cwd` is the one place a local provider legitimately
* reads `process.cwd()` — finding the app's own installed version is
* inherently cwd-relative.
*/
Expand All @@ -54,8 +54,8 @@ export function resolvePrismaDevModulePath(cwd: string): string {
// fall through to the prisma-CLI-relative resolution
}
try {
const prismaEntry = appRequire.resolve('prisma');
return createRequire(prismaEntry).resolve('@prisma/dev');
const prismaManifest = appRequire.resolve('prisma/package.json');
return createRequire(prismaManifest).resolve('@prisma/dev');
} catch {
throw noPrismaDevError();
}
Expand Down
7 changes: 7 additions & 0 deletions skills/prisma-composer-core-concepts/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,13 @@ that surprise:
service it calls.
5. Windows isn't supported yet.

Local Postgres needs `@prisma/dev` in the devDependencies of the project where
Composer runs (the root package in a monorepo). Prisma 8 does not ship it.
Composer resolves the app's copy first, then checks relative to the exported
`prisma/package.json` for installations that supply it there. If neither resolves,
add `@prisma/dev`, not another copy of `prisma`; leave database bindings unchanged.
Cloud deployment and local apps without Postgres do not need this runtime.

## Testing is an environment seam

A test is just another environment: one where you decide what `load()` and
Expand Down
Loading