Skip to content

Commit

Permalink
Add support for rush (#22)
Browse files Browse the repository at this point in the history
* ADd support for rush

* Add tests

* Create rush-ws-2024-0-29-16-13-31.md
  • Loading branch information
timotheeguerin authored Jan 29, 2024
1 parent 242040a commit 86246ee
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 39 deletions.
5 changes: 5 additions & 0 deletions .changeset/rush-ws-2024-0-29-16-13-31.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@chronus/chronus": patch
---

Add support for rush.js
3 changes: 2 additions & 1 deletion packages/chronus/src/workspace-manager/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./npm.js";
export * from "./pnpm.js";
export { createPnpmWorkspaceManager } from "./pnpm.js";
export { createRushWorkspaceManager } from "./rush.js";
43 changes: 5 additions & 38 deletions packages/chronus/src/workspace-manager/pnpm.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
import { load } from "js-yaml";
import {
ChronusError,
isDefined,
isPathAccessible,
joinPaths,
lookup,
resolvePath,
type ChronusHost,
} from "../utils/index.js";
import { ChronusError, isPathAccessible, joinPaths, lookup, type ChronusHost } from "../utils/index.js";
import type { Package, Workspace, WorkspaceManager } from "./types.js";
import { findPackagesFromPattern } from "./utils.js";

const workspaceFileName = "pnpm-workspace.yaml";
interface PnpmWorkspaceConfig {
Expand All @@ -23,18 +16,18 @@ export function createPnpmWorkspaceManager(host: ChronusHost): WorkspaceManager
return isPathAccessible(host, path);
});
if (root === undefined) {
throw new Error(`Cannot find pnpm-workspace.yaml in a parent folder to ${dir}`);
throw new ChronusError(`Cannot find ${workspaceFileName} in a parent folder to ${dir}`);
}
const workspaceFilePath = joinPaths(root, workspaceFileName);

const file = await host.readFile(workspaceFilePath);
const config: PnpmWorkspaceConfig = load(file.content) as any;

if (config.packages === undefined) {
throw new ChronusError("packages entry missing in pnpm-workspace.yaml");
throw new ChronusError(`packages entry missing in ${workspaceFileName}`);
}
if (Array.isArray(config.packages) === false) {
throw new ChronusError("packages is not an array in pnpm-workspace.yaml");
throw new ChronusError(`packages is not an array in ${workspaceFileName}`);
}
const packages: Package[] = (
await Promise.all(config.packages.map((pattern) => findPackagesFromPattern(host, root, pattern)))
Expand All @@ -46,29 +39,3 @@ export function createPnpmWorkspaceManager(host: ChronusHost): WorkspaceManager
},
};
}

export async function findPackagesFromPattern(host: ChronusHost, root: string, pattern: string): Promise<Package[]> {
const packageRoots = await host.glob(pattern, {
baseDir: root,
onlyDirectories: true,
});

const packages = await Promise.all(packageRoots.map((x) => tryLoadNodePackage(host, root, x)));
return packages.filter(isDefined);
}

async function tryLoadNodePackage(host: ChronusHost, root: string, relativePath: string): Promise<Package | undefined> {
const pkgJsonPath = resolvePath(root, relativePath, "package.json");
if (await isPathAccessible(host, pkgJsonPath)) {
const file = await host.readFile(pkgJsonPath);
const pkgJson = JSON.parse(file.content);
return {
name: pkgJson.name,
version: pkgJson.version,
relativePath: relativePath,
manifest: pkgJson,
};
} else {
return undefined;
}
}
62 changes: 62 additions & 0 deletions packages/chronus/src/workspace-manager/rush.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { dump } from "js-yaml";
import { beforeEach, describe, expect, it } from "vitest";
import { createTestHost, type TestHost } from "../testing/test-host.js";
import { createRushWorkspaceManager } from "./rush.js";
import type { WorkspaceManager } from "./types.js";

describe("rush", () => {
let host: TestHost;
let rush: WorkspaceManager;
beforeEach(async () => {
host = createTestHost({});

rush = createRushWorkspaceManager(host.host);
});

it("finds 0 packages when workspace has none", async () => {
host.addFile(
"proj/rush.json",
dump({
projects: [],
}),
);
const workspace = await rush.load("proj");
expect(workspace.packages).toEqual([]);
});

it("finds all packages", async () => {
host.addFile(
"proj/rush.json",
dump({
projects: [
{
packageName: "pkg-a",
projectFolder: "packages/pkg-a",
shouldPublish: true,
},
{
packageName: "pkg-b",
projectFolder: "packages/pkg-b",
shouldPublish: true,
},
],
}),
);
host.addFile("proj/packages/pkg-a/package.json", JSON.stringify({ name: "pkg-a", version: "1.0.0" }));
host.addFile("proj/packages/pkg-b/package.json", JSON.stringify({ name: "pkg-b", version: "1.2.0" }));
const workspace = await rush.load("proj");
expect(workspace.packages).toHaveLength(2);
expect(workspace.packages[0]).toEqual({
name: "pkg-a",
version: "1.0.0",
relativePath: "packages/pkg-a",
manifest: { name: "pkg-a", version: "1.0.0" },
});
expect(workspace.packages[1]).toEqual({
name: "pkg-b",
version: "1.2.0",
relativePath: "packages/pkg-b",
manifest: { name: "pkg-b", version: "1.2.0" },
});
});
});
48 changes: 48 additions & 0 deletions packages/chronus/src/workspace-manager/rush.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { load } from "js-yaml";
import { ChronusError, isDefined, isPathAccessible, joinPaths, lookup, type ChronusHost } from "../utils/index.js";
import type { Package, Workspace, WorkspaceManager } from "./types.js";
import { tryLoadNodePackage } from "./utils.js";

const workspaceFileName = "rush.json";

interface RushJson {
readonly projects: RushProject[];
}

interface RushProject {
readonly packageName: string;
readonly projectFolder: string;
readonly shouldPublish?: boolean;
}

export function createRushWorkspaceManager(host: ChronusHost): WorkspaceManager {
return {
async load(dir: string): Promise<Workspace> {
const root = await lookup(dir, (current) => {
const path = joinPaths(current, workspaceFileName);
return isPathAccessible(host, path);
});
if (root === undefined) {
throw new ChronusError(`Cannot find ${workspaceFileName} in a parent folder to ${dir}`);
}
const workspaceFilePath = joinPaths(root, workspaceFileName);

const file = await host.readFile(workspaceFilePath);
const config: RushJson = load(file.content) as any;

if (config.projects === undefined) {
throw new ChronusError(`projects entry missing in ${workspaceFileName}`);
}
if (Array.isArray(config.projects) === false) {
throw new ChronusError(`projects is not an array in ${workspaceFileName}`);
}
const packages: Package[] = (
await Promise.all(config.projects.map((pattern) => tryLoadNodePackage(host, root, pattern.projectFolder)))
).filter(isDefined);
return {
path: root,
packages,
};
},
};
}
34 changes: 34 additions & 0 deletions packages/chronus/src/workspace-manager/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { isPathAccessible } from "../utils/fs-utils.js";
import type { ChronusHost } from "../utils/host.js";
import { isDefined, resolvePath } from "../utils/index.js";
import type { Package } from "./types.js";

export async function findPackagesFromPattern(host: ChronusHost, root: string, pattern: string): Promise<Package[]> {
const packageRoots = await host.glob(pattern, {
baseDir: root,
onlyDirectories: true,
});

const packages = await Promise.all(packageRoots.map((x) => tryLoadNodePackage(host, root, x)));
return packages.filter(isDefined);
}

export async function tryLoadNodePackage(
host: ChronusHost,
root: string,
relativePath: string,
): Promise<Package | undefined> {
const pkgJsonPath = resolvePath(root, relativePath, "package.json");
if (await isPathAccessible(host, pkgJsonPath)) {
const file = await host.readFile(pkgJsonPath);
const pkgJson = JSON.parse(file.content);
return {
name: pkgJson.name,
version: pkgJson.version,
relativePath: relativePath,
manifest: pkgJson,
};
} else {
return undefined;
}
}

0 comments on commit 86246ee

Please sign in to comment.