103 lines
4.8 KiB
JavaScript
103 lines
4.8 KiB
JavaScript
// @vitest-environment node
|
|
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { afterEach, describe, test } from "vitest";
|
|
import { prunePnpmRuntimeClosure } from "./prune-pnpm-runtime-closure.mjs";
|
|
|
|
const fixtures = [];
|
|
|
|
afterEach(() => {
|
|
for (const fixture of fixtures.splice(0)) fs.rmSync(fixture, { force: true, recursive: true });
|
|
});
|
|
|
|
describe("pnpm runtime closure pruning", () => {
|
|
test("keeps reachable native packages while removing an excluded optional peer toolchain and its orphans", async () => {
|
|
const fixture = createFixture();
|
|
const nodeModulesPath = path.join(fixture, "node_modules");
|
|
const client = addPackage(nodeModulesPath, "@prisma+client@7.8.0_peer", "@prisma/client");
|
|
const prisma = addPackage(nodeModulesPath, "prisma@7.8.0_peer", "prisma");
|
|
const prismaDev = addPackage(nodeModulesPath, "@prisma+dev@0.24.3", "@prisma/dev");
|
|
const pglite = addPackage(nodeModulesPath, "@electric-sql+pglite@0.4.1", "@electric-sql/pglite");
|
|
const typescript = addPackage(nodeModulesPath, "typescript@5.9.3", "typescript");
|
|
const sharp = addPackage(nodeModulesPath, "sharp@0.35.2", "sharp");
|
|
const sharpBinary = addPackage(nodeModulesPath, "@img+sharp-linux-x64@0.35.2", "@img/sharp-linux-x64");
|
|
const unrelatedOrphan = addPackage(nodeModulesPath, "orphan@1.0.0", "orphan");
|
|
|
|
linkPackage(nodeModulesPath, "@prisma/client", client.packagePath);
|
|
linkPackage(nodeModulesPath, "prisma", prisma.packagePath);
|
|
linkPackage(nodeModulesPath, "typescript", typescript.packagePath);
|
|
linkPackage(nodeModulesPath, "sharp", sharp.packagePath);
|
|
linkPackage(path.join(client.entryPath, "node_modules"), "prisma", prisma.packagePath);
|
|
linkPackage(path.join(prisma.entryPath, "node_modules"), "@prisma/dev", prismaDev.packagePath);
|
|
linkPackage(path.join(prismaDev.entryPath, "node_modules"), "@electric-sql/pglite", pglite.packagePath);
|
|
linkPackage(path.join(sharp.entryPath, "node_modules"), "@img/sharp-linux-x64", sharpBinary.packagePath);
|
|
linkPackage(path.join(nodeModulesPath, ".bin"), "prisma", path.join(prisma.packagePath, "index.js"));
|
|
|
|
const result = await prunePnpmRuntimeClosure({
|
|
excludedPackageNames: ["prisma", "typescript"],
|
|
nodeModulesPath,
|
|
});
|
|
|
|
assert.deepEqual(
|
|
result.removedEntries,
|
|
[
|
|
"@electric-sql+pglite@0.4.1",
|
|
"@prisma+dev@0.24.3",
|
|
"orphan@1.0.0",
|
|
"prisma@7.8.0_peer",
|
|
"typescript@5.9.3",
|
|
].sort()
|
|
);
|
|
assert.ok(result.bytesRemoved > 0);
|
|
assert.ok(result.filesRemoved > 0);
|
|
assert.ok(result.brokenLinksRemoved > 0);
|
|
assert.ok(fs.existsSync(client.packagePath));
|
|
assert.ok(fs.existsSync(sharp.packagePath));
|
|
assert.ok(fs.existsSync(sharpBinary.packagePath));
|
|
assert.ok(!fs.existsSync(prisma.packagePath));
|
|
assert.ok(!fs.existsSync(prismaDev.packagePath));
|
|
assert.ok(!fs.existsSync(pglite.packagePath));
|
|
assert.ok(!fs.existsSync(typescript.packagePath));
|
|
assert.ok(!fs.existsSync(unrelatedOrphan.packagePath));
|
|
assert.doesNotThrow(() => fs.realpathSync(path.join(nodeModulesPath, "@prisma/client")));
|
|
assert.doesNotThrow(() => fs.realpathSync(path.join(nodeModulesPath, "sharp")));
|
|
assert.doesNotThrow(() => fs.realpathSync(path.join(sharp.entryPath, "node_modules/@img/sharp-linux-x64")));
|
|
assert.ok(!fs.existsSync(path.join(nodeModulesPath, ".bin/prisma")));
|
|
});
|
|
|
|
test("fails closed instead of deleting a store with no reachable runtime roots", async () => {
|
|
const fixture = createFixture();
|
|
const nodeModulesPath = path.join(fixture, "node_modules");
|
|
const orphan = addPackage(nodeModulesPath, "orphan@1.0.0", "orphan");
|
|
|
|
await assert.rejects(
|
|
() => prunePnpmRuntimeClosure({ excludedPackageNames: [], nodeModulesPath }),
|
|
/refusing to prune 1 pnpm entries without a reachable runtime root/
|
|
);
|
|
assert.ok(fs.existsSync(orphan.packagePath));
|
|
});
|
|
});
|
|
|
|
function createFixture() {
|
|
const fixture = fs.mkdtempSync(path.join(os.tmpdir(), "pnpm-runtime-prune-"));
|
|
fixtures.push(fixture);
|
|
fs.mkdirSync(path.join(fixture, "node_modules/.pnpm"), { recursive: true });
|
|
return fixture;
|
|
}
|
|
|
|
function addPackage(nodeModulesPath, entryName, packageName) {
|
|
const entryPath = path.join(nodeModulesPath, ".pnpm", entryName);
|
|
const packagePath = path.join(entryPath, "node_modules", ...packageName.split("/"));
|
|
fs.mkdirSync(packagePath, { recursive: true });
|
|
fs.writeFileSync(path.join(packagePath, "index.js"), `module.exports = ${JSON.stringify(packageName)};\n`);
|
|
return { entryPath, packagePath };
|
|
}
|
|
|
|
function linkPackage(nodeModulesPath, packageName, target) {
|
|
const linkPath = path.join(nodeModulesPath, ...packageName.split("/"));
|
|
fs.mkdirSync(path.dirname(linkPath), { recursive: true });
|
|
fs.symlinkSync(path.relative(path.dirname(linkPath), target), linkPath);
|
|
}
|