npmDependencies in the manifest, installed once for every plugin
replaces the per-plugin package.json from earlier tonight. the owner's call, and the reason is one i had not weighed: sharp is ~50 MB of platform binaries and the obvious dependency for anything touching images, so a second and third plugin wanting it would have meant a second and third copy with no version they could converge on. so every plugin's `npmDependencies` merge into ONE generated plugins/package.json, installed into plugins/node_modules/. bun resolves node_modules up the tree, so that directory serves every plugin and none of the platform's own code. ranges intersect rather than alternate. two plugins asking for ^0.33.5 and ^0.33.9 produce "^0.33.5 ^0.33.9", which npm and bun read as AND — the resolved version must satisfy both. `||` would have meant "either", which is the silent version of this failure: one plugin quietly getting a version the other cannot import. incompatible requirements now fail the install with bun's own message, and both askers are named in the steps either way. generated from the plugins ON DISK, like generatePluginSchemas, and never edited — so a package cannot appear there without a plugin having declared it. what this gives up, knowingly: a committed lockfile. ranges are the whole point and a range resolves per machine, so plugin packages are resolved at install time like osDependencies are, and unlike the platform's own. CLAUDE.md is emphatic about frozen lockfiles and 2026-08-04, so it is named in the manifest type rather than left to be discovered. the generated plugins/package.json and lockfile are gitignored — /plugins/*/ only ignores directories, so files at that level needed naming explicitly.
This commit is contained in:
@@ -3,8 +3,6 @@ import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { PLUGINS_DIR } from './discover';
|
||||
import { pullPlugin } from './update';
|
||||
import { discoverPlugins } from './discover';
|
||||
import { npmManifestFor, npmWouldBlockUpdates } from './npm-deps';
|
||||
|
||||
// The REFUSALS are what this test is for. A pull that works is easy to believe; a pull that quietly
|
||||
// discarded somebody's uncommitted work is the failure that matters, and every plugin directory on a
|
||||
@@ -55,19 +53,3 @@ describe('pullPlugin refuses rather than guesses', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('npm packages and the Update button', () => {
|
||||
// The trap: `pullPlugin` refuses on ANY uncommitted change, and `node_modules/` that nothing ignores
|
||||
// is one. So a plugin shipping `package.json` without ignoring it installs perfectly once and can then
|
||||
// never be updated — the button says "N uncommitted changes" forever, about a directory the platform
|
||||
// itself created. Every plugin that exists today ignores it, which is luck rather than a rule.
|
||||
test('atelier — the only plugin with packages — ignores node_modules', async () => {
|
||||
const { plugins } = await discoverPlugins();
|
||||
const withPackages = plugins.filter((p) => npmManifestFor(p));
|
||||
|
||||
// Not asserting that atelier specifically is installed — a checkout without it is legitimate.
|
||||
for (const plugin of withPackages) {
|
||||
expect(await npmWouldBlockUpdates(plugin), `${plugin.appName} would block its own updates`).toBe(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user