isolate-package
Version:
Isolate monorepo packages to form a self-contained deployable unit
214 lines (181 loc) • 6.15 kB
text/typescript
import fs from "fs-extra";
import os from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { getInternalPackageNames } from "./get-internal-package-names";
vi.mock("~/lib/logger", () => ({
useLogger: () => ({
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
}),
setLogLevel: vi.fn(),
}));
const packageManagerResult = {
name: "pnpm",
version: "9.0.0",
majorVersion: 9,
};
const mockDetectPackageManager = vi.fn(
(_workspaceRootDir: string) => packageManagerResult,
);
vi.mock("~/lib/package-manager", () => ({
usePackageManager: () => packageManagerResult,
detectPackageManager: (workspaceRootDir: string) =>
mockDetectPackageManager(workspaceRootDir),
}));
/**
* Sets up a minimal workspace file structure with a target package and
* workspace packages so that getInternalPackageNames can resolve them.
*/
async function createWorkspace(
rootDir: string,
{
targetDeps = {} as Record<string, string>,
targetDevDeps = {} as Record<string, string>,
packages = [] as {
name: string;
dir: string;
deps?: Record<string, string>;
}[],
},
) {
const packagesDir = path.join(rootDir, "packages");
const targetDir = path.join(packagesDir, "target");
/** Write pnpm-workspace.yaml so the registry can find packages */
await fs.writeFile(
path.join(rootDir, "pnpm-workspace.yaml"),
"packages:\n - packages/*\n",
);
/** Write a pnpm lockfile so package manager detection works */
await fs.writeFile(
path.join(rootDir, "pnpm-lock.yaml"),
"lockfileVersion: '9.0'\n",
);
/** Write target package manifest */
await fs.ensureDir(targetDir);
await fs.writeJson(path.join(targetDir, "package.json"), {
name: "@test/target",
version: "0.0.0",
dependencies: targetDeps,
devDependencies: targetDevDeps,
});
/** Write workspace package manifests */
for (const pkg of packages) {
const pkgDir = path.join(packagesDir, pkg.dir);
await fs.ensureDir(pkgDir);
await fs.writeJson(path.join(pkgDir, "package.json"), {
name: pkg.name,
version: "0.0.0",
dependencies: pkg.deps ?? {},
});
}
return targetDir;
}
describe("getInternalPackageNames", () => {
let tempDir: string;
let originalCwd: string;
beforeEach(async () => {
vi.clearAllMocks();
originalCwd = process.cwd();
tempDir = await fs.mkdtemp(
path.join(os.tmpdir(), "isolate-get-internal-test-"),
);
});
afterEach(async () => {
process.chdir(originalCwd);
await fs.remove(tempDir);
});
it("returns internal package names from the target manifest", async () => {
const targetDir = await createWorkspace(tempDir, {
targetDeps: { "@test/shared": "0.0.0", lodash: "^4.0.0" },
packages: [{ name: "@test/shared", dir: "shared" }],
});
process.chdir(targetDir);
const result = await getInternalPackageNames({ workspaceRoot: "../.." });
expect(result).toEqual(["@test/shared"]);
expect(mockDetectPackageManager).toHaveBeenCalled();
});
it("excludes devDependencies by default", async () => {
const targetDir = await createWorkspace(tempDir, {
targetDeps: { "@test/shared": "0.0.0" },
targetDevDeps: { "@test/dev-tool": "0.0.0" },
packages: [
{ name: "@test/shared", dir: "shared" },
{ name: "@test/dev-tool", dir: "dev-tool" },
],
});
process.chdir(targetDir);
const result = await getInternalPackageNames({ workspaceRoot: "../.." });
expect(result).toEqual(["@test/shared"]);
});
it("includes devDependencies when configured", async () => {
const targetDir = await createWorkspace(tempDir, {
targetDeps: { "@test/shared": "0.0.0" },
targetDevDeps: { "@test/dev-tool": "0.0.0" },
packages: [
{ name: "@test/shared", dir: "shared" },
{ name: "@test/dev-tool", dir: "dev-tool" },
],
});
process.chdir(targetDir);
const result = await getInternalPackageNames({
workspaceRoot: "../..",
includeDevDependencies: true,
});
expect(result).toEqual(
expect.arrayContaining(["@test/shared", "@test/dev-tool"]),
);
expect(result).toHaveLength(2);
});
it("resolves recursive internal dependencies", async () => {
const targetDir = await createWorkspace(tempDir, {
targetDeps: { "@test/app-utils": "0.0.0" },
packages: [
{
name: "@test/app-utils",
dir: "app-utils",
deps: { "@test/core": "0.0.0" },
},
{ name: "@test/core", dir: "core" },
],
});
process.chdir(targetDir);
const result = await getInternalPackageNames({ workspaceRoot: "../.." });
expect(result).toEqual(
expect.arrayContaining(["@test/app-utils", "@test/core"]),
);
expect(result).toHaveLength(2);
});
it("returns an empty array when there are no internal dependencies", async () => {
const targetDir = await createWorkspace(tempDir, {
targetDeps: { lodash: "^4.0.0", express: "^4.0.0" },
packages: [{ name: "@test/unrelated", dir: "unrelated" }],
});
process.chdir(targetDir);
const result = await getInternalPackageNames({ workspaceRoot: "../.." });
expect(result).toEqual([]);
});
it("loads config from isolate.config.json when no config is passed", async () => {
const targetDir = await createWorkspace(tempDir, {
targetDeps: { "@test/shared": "0.0.0" },
targetDevDeps: { "@test/dev-tool": "0.0.0" },
packages: [
{ name: "@test/shared", dir: "shared" },
{ name: "@test/dev-tool", dir: "dev-tool" },
],
});
/** Write config file with includeDevDependencies enabled */
await fs.writeJson(path.join(targetDir, "isolate.config.json"), {
workspaceRoot: "../..",
includeDevDependencies: true,
});
process.chdir(targetDir);
const result = await getInternalPackageNames();
expect(result).toEqual(
expect.arrayContaining(["@test/shared", "@test/dev-tool"]),
);
expect(result).toHaveLength(2);
});
});