FN-6410: fix standalone pi runtime deps
Keep published CLI standalone installs from omitting the pi runtime packages.\n\n- Remove pi runtime packages from optional peer declarations so npm and pnpm install them as required dependencies.\n- Add a package manifest regression test for source and prepack-transformed publish manifests.\n- Add a patch changeset for the published CLI package.\n\nFiles changed:\n .changeset/fn-6410-standalone-pi-dependency.md | 5 +++\n packages/cli/package.json | 8 ----\n packages/cli/src/__tests__/package-config.test.ts | 47 +++++++++++++++++++++++\n 3 files changed, 52 insertions(+), 8 deletions(-) Fusion-Task-Id: FN-6410 Fusion-Task-Lineage: 579c8a91-a021-4b36-94d7-e4651ce241cb
This commit is contained in:
5
.changeset/fn-6410-standalone-pi-dependency.md
Normal file
5
.changeset/fn-6410-standalone-pi-dependency.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix standalone installs of the published CLI crashing with `ERR_MODULE_NOT_FOUND` for `@earendil-works/pi-coding-agent`. `@earendil-works/pi-coding-agent` and `@earendil-works/pi-ai` are now plain required dependencies instead of also being optional peers, so clean npm and pnpm installs resolve the pi runtime packages.
|
||||||
@@ -74,17 +74,9 @@
|
|||||||
"ws": "^8.18.0"
|
"ws": "^8.18.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@earendil-works/pi-ai": "*",
|
|
||||||
"@earendil-works/pi-coding-agent": "*",
|
|
||||||
"typebox": "*"
|
"typebox": "*"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@earendil-works/pi-ai": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"@earendil-works/pi-coding-agent": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"typebox": {
|
"typebox": {
|
||||||
"optional": true
|
"optional": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { readFileSync } from "node:fs";
|
|||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { builtinModules } from "node:module";
|
import { builtinModules } from "node:module";
|
||||||
import { parse } from "yaml";
|
import { parse } from "yaml";
|
||||||
|
import { applyPrepackTransform } from "../../scripts/prepare-publish-manifest.mjs";
|
||||||
|
|
||||||
const workspaceRoot = join(__dirname, "..", "..", "..", "..");
|
const workspaceRoot = join(__dirname, "..", "..", "..", "..");
|
||||||
|
|
||||||
@@ -32,6 +33,43 @@ function hasProjectArg(script: string | undefined, project: string): boolean {
|
|||||||
return parts.some((part, index) => part === "--project" && parts[index + 1] === project);
|
return parts.some((part, index) => part === "--project" && parts[index + 1] === project);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function assertRuntimeDepsAreNotOptionalPeers(pkg: any, label: string): void {
|
||||||
|
const dependencies = pkg.dependencies ?? {};
|
||||||
|
const peerDependencies = pkg.peerDependencies ?? {};
|
||||||
|
const peerDependenciesMeta = pkg.peerDependenciesMeta ?? {};
|
||||||
|
|
||||||
|
for (const dependencyName of Object.keys(dependencies)) {
|
||||||
|
expect(
|
||||||
|
peerDependenciesMeta[dependencyName]?.optional,
|
||||||
|
`${label}: runtime dependency "${dependencyName}" must not also be an optional peer; npm/pnpm may omit it from clean standalone installs.`,
|
||||||
|
).not.toBe(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const dependencyName of ["@earendil-works/pi-coding-agent", "@earendil-works/pi-ai"]) {
|
||||||
|
expect(dependencies, `${label}: ${dependencyName} must remain a required runtime dependency`).toHaveProperty(
|
||||||
|
dependencyName,
|
||||||
|
"^0.79.1",
|
||||||
|
);
|
||||||
|
expect(peerDependencies, `${label}: ${dependencyName} must not be a peer dependency`).not.toHaveProperty(
|
||||||
|
dependencyName,
|
||||||
|
);
|
||||||
|
expect(peerDependenciesMeta, `${label}: ${dependencyName} must not have peer metadata`).not.toHaveProperty(
|
||||||
|
dependencyName,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(dependencies, `${label}: typebox must not be promoted into runtime dependencies`).not.toHaveProperty(
|
||||||
|
"typebox",
|
||||||
|
);
|
||||||
|
expect(peerDependencies, `${label}: typebox remains the optional peer control`).toHaveProperty(
|
||||||
|
"typebox",
|
||||||
|
"*",
|
||||||
|
);
|
||||||
|
expect(peerDependenciesMeta.typebox, `${label}: typebox remains optional peer metadata`).toEqual({
|
||||||
|
optional: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
describe("CLI package.json publishing config", () => {
|
describe("CLI package.json publishing config", () => {
|
||||||
const pkg = loadPackageJson("cli");
|
const pkg = loadPackageJson("cli");
|
||||||
const prepackScript = loadCliPrepackScript();
|
const prepackScript = loadCliPrepackScript();
|
||||||
@@ -93,6 +131,15 @@ describe("CLI package.json publishing config", () => {
|
|||||||
expect(deps).toContain("ioredis");
|
expect(deps).toContain("ioredis");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FNXC:Packaging 2026-06-13-16:36:
|
||||||
|
* Standalone npm/pnpm installs may omit a package when the published manifest declares it as both a runtime dependency and an optional peer. Keep the pi runtime packages as plain dependencies so dist/bin.js and dist/extension.js can resolve their static imports outside the monorepo, while leaving typebox as the optional-peer control because Fusion does not import it at runtime.
|
||||||
|
*/
|
||||||
|
it("does not declare runtime dependencies as optional peers in source or published manifests", () => {
|
||||||
|
assertRuntimeDepsAreNotOptionalPeers(pkg, "source manifest");
|
||||||
|
assertRuntimeDepsAreNotOptionalPeers(applyPrepackTransform(pkg), "published manifest");
|
||||||
|
});
|
||||||
|
|
||||||
it("defines test:docs-index as a single-file docs README index lane", () => {
|
it("defines test:docs-index as a single-file docs README index lane", () => {
|
||||||
const script = pkg.scripts?.["test:docs-index"];
|
const script = pkg.scripts?.["test:docs-index"];
|
||||||
const parts = script?.trim().split(/\s+/) ?? [];
|
const parts = script?.trim().split(/\s+/) ?? [];
|
||||||
|
|||||||
Reference in New Issue
Block a user