From 8aecd7c0a06d614c6b8cebef71f8ddf3ae7936d1 Mon Sep 17 00:00:00 2001 From: willbot Date: Tue, 18 Aug 2026 09:24:49 +0200 Subject: [PATCH] refactor(cli-engine): call every presentation, with no defensive calls left MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `Presentations` requires all four fields, so materializePresentation had no business calling two of them with `?.()`. Those existed to tolerate @prisma/orm-toolchain built against engine 0.0.9, where three of the four were optional — a consumer working around a producer it shares an owner with. The producer was fixed instead (prisma/prisma#30004) and this repo now pins 8.0.0-rc.2, which declares all four. Verified by removing the calls and running tests/orm-mount.test.ts against the pinned package: five pass, including the human-mode case, which is the one the shim was protecting. A command that omits a presentation now fails loudly rather than silently publishing an empty surface. Co-Authored-By: Claude Opus 5 Signed-off-by: willbot Signed-off-by: Will Madden --- .drive/projects/prisma-cli-v8/deferred.md | 2 ++ .../src/execution/command-context.ts | 34 ++++++++----------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/.drive/projects/prisma-cli-v8/deferred.md b/.drive/projects/prisma-cli-v8/deferred.md index bbc23c33..038bad6b 100644 --- a/.drive/projects/prisma-cli-v8/deferred.md +++ b/.drive/projects/prisma-cli-v8/deferred.md @@ -97,6 +97,8 @@ composer can use it. ## The ORM family does not work through the assembled binary (found 2026-08-13, writing the e2e happy paths) +**Resolved 2026-08-17 (the presentations half):** `@prisma/orm-toolchain@8.0.0-rc.2` declares all four presentations, prisma-cli pins it, and the engine's two defensive `?.()` calls are gone. The `orm init` config mismatch below is untouched and still live. + Running the shipped `prisma` binary against a scratch directory, rather than the ORM family through the test harness, turns up three things. The first is a defect a user hits on their first command. - **`prisma orm init` scaffolds a project the `prisma` binary cannot read.** It writes `prisma-next.config.ts` — the standalone `prisma-next` bin's config file — and then fails its own last step, `Emit the contract`, with exit 5 and `Config is not a defineConfig result`. Nine files are already on disk at that point. Running any ORM command afterwards fails again, differently: the mounted family reads its configuration from an `orm` section of `prisma.config.ts` (`ormConfigSection`, `packages/1-framework/3-tooling/cli/src/orm/config-section.ts` in prisma/prisma), so it reports `CLI.CONFIG_SECTION_INVALID` and `CONFIG.FILE_NOT_FOUND` — "The orm config section is absent, so prisma-next.config.ts was never evaluated." So `prisma orm init && prisma contract emit` cannot work, and the two config surfaces have different shapes: the section nests the whole config under `orm`, while the scaffolded file exports a `defineConfig` result. Which side moves is the ORM's call; that it is broken today is not in question. diff --git a/packages/cli-engine/src/execution/command-context.ts b/packages/cli-engine/src/execution/command-context.ts index 1062b8d9..591bd20b 100644 --- a/packages/cli-engine/src/execution/command-context.ts +++ b/packages/cli-engine/src/execution/command-context.ts @@ -55,24 +55,18 @@ export function makeUi(colorEnabled: boolean, stderr: OutputStream): Ui { /** Materializes ONLY the active format's presentation functions, at the * return site: human → human + stdout + next; json → json + next. * - * `stdout` and `next` may be absent at runtime, so both are called with - * `?.()`. `Presentations` requires all four, so no command compiled - * against this engine can omit one — but `@prisma/orm-toolchain` is - * built against engine `0.0.9`, where three of the four were optional, - * and its published commands took that up: `migration list` declares - * `human` and `json` and neither of the others. Calling them - * unconditionally makes it exit 2 — `stdout` in human mode, `next` in - * both. + * Every one is called unconditionally, because `Presentations` requires + * all four and nothing reaches here that did not satisfy that type. * - * `json` is called unconditionally, and stays that way: a missing json - * presentation is the defect this change removes, and every ORM command - * already declares one. - * - * This is version skew in our own code, not a foreign contract. The fix - * is in prisma/prisma: declare the missing presentations in the ORM - * commands and build orm-toolchain against this engine, where the type - * refuses to compile without them. Delete both `?.()` when that - * version is pinned here. */ + * Two of these calls were briefly written `?.()`, to tolerate + * `@prisma/orm-toolchain` built against engine `0.0.9`, where three of + * the four were optional and its commands took that up. That was a + * consumer working around a producer it shares an owner with. The + * producer was fixed instead (prisma/prisma#30004), and this repo pins + * a version that declares all four, so the defensive calls are gone. + * Anything that omits one now fails loudly rather than silently + * publishing an empty surface — which is the whole point of requiring + * them. */ function materializePresentation( state: RunState, ui: Ui, @@ -83,14 +77,14 @@ function materializePresentation( human: [], stdout: [], json: presentations.json(), - next: presentations.next?.() ?? [], + next: presentations.next(), }; } return { human: presentations.human(ui), - stdout: presentations.stdout?.() ?? [], + stdout: presentations.stdout(), json: undefined, - next: presentations.next?.() ?? [], + next: presentations.next(), }; }