From 537cc29c3e6b69dcb8b8fe679107e53de3783d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Sun, 30 Aug 2026 11:20:25 +0100 Subject: [PATCH] a package with no root entry point is not a broken install MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `unresolvableDependencies` asked `Bun.resolveSync(name, pluginDir)` of every declared package. That is the right question for almost all of them and unanswerable for a few. `@tiptap/pm` declares no `main`, no `module`, and an `exports` map with no "." — it exists only as subpaths. You import `@tiptap/pm/state`, never `@tiptap/pm`. Asked to resolve the bare name Bun correctly says it cannot, and this read that as a failed install. It cost a deploy. fofos-admin declares `@tiptap/pm` because tiptap needs it present; the install was perfect — the directory, its dist and thirteen prosemirror packages all there — and the plugin was reported as failed on every host that has it. The strong check stays first, because it is the one that catches a package which installed but cannot be loaded on this machine — `sharp` resolving to a platform-specific binary is the shape it was written for. Only a package that fails it is asked the weaker question: is it there. Present-but-unimportable still fails, since it never reaches the second line. Verified on the host rather than reasoned about: of the five packages fofos-admin declares, four resolve and @tiptap/pm alone does not, and it is present. Co-Authored-By: Claude Opus 5 (1M context) --- src/servers/plugins/npm-deps.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/servers/plugins/npm-deps.ts b/src/servers/plugins/npm-deps.ts index 6f1abfea..7d474bc2 100644 --- a/src/servers/plugins/npm-deps.ts +++ b/src/servers/plugins/npm-deps.ts @@ -181,12 +181,31 @@ const body = { export function unresolvableDependencies(plugin: DiscoveredPlugin): string[] { const names = Object.keys(plugin.manifest.npmDependencies ?? {}); return names.filter((name) => { + // The strong check: it imports. This is what catches a package that installed but cannot be loaded + // on this machine — `sharp` resolving to a platform-specific binary is the shape it was written for. try { Bun.resolveSync(name, plugin.dir); return false; } catch { - return true; + // fall through } + + /* + Some packages have NO root entry point, and that is not a fault. + + `@tiptap/pm` declares no `main`, no `module`, and an `exports` map with no `"."` — it exists only as + subpaths, and you import `@tiptap/pm/state`, never `@tiptap/pm`. Asked to resolve the bare name, + Bun correctly says it cannot, and the check above read that as a broken install. + + It cost a deploy: fofos-admin declares `@tiptap/pm` because tiptap needs it present, the install + was perfect — the directory, its dist and thirteen prosemirror packages all there — and the plugin + was reported as failed on every host that had it. + + So a package that does not resolve is asked the weaker question instead: is it THERE. Present but + unimportable still fails, because the strong check ran first and only a package with no root entry + reaches this line. + */ + return !existsSync(join(PLUGINS_DIR, 'node_modules', name, 'package.json')); }); }