import fsPromises from "fs/promises"; import path from "path"; import { fileURLToPath } from "url"; import { glob } from "glob"; import { parse as parseYaml } from "yaml"; import z from "zod"; import packageJson from "../../../../package.json"; export const getPackageVersion = () => packageJson.version; export const getDependenciesAsync = async (): Promise => { const pathNames = await glob("**/package.json", { ignore: "**/node_modules/**", cwd: "../../", absolute: true, }); const packageContents = await Promise.all(pathNames.map(async (path) => await fsPromises.readFile(path, "utf-8"))); const packageDependencies = packageContents .map((packageContent) => (JSON.parse(packageContent) as PackageJson).dependencies) .filter((dependencies) => dependencies !== undefined); const catalog = await parseDependencyCatalogsAsync(); let dependencies = {}; for (const dependenciesOfPackage of packageDependencies) { const resolvedDependencies = Object.entries(dependenciesOfPackage).map(([name, version]) => { const catalogVersion = catalog.get(name); return [name, catalogVersion ?? version] as const; }); dependencies = { ...dependencies, ...Object.fromEntries(resolvedDependencies) }; } return dependencies; }; type DependencyCatalog = Map; const parseDependencyCatalogsAsync = async (): Promise => { const currentDir = path.dirname(fileURLToPath(import.meta.url)); const workspaceConfigPath = path.join(currentDir, "..", "..", "..", "..", "pnpm-workspace.yaml"); try { const workspaceConfigContent = await fsPromises.readFile(workspaceConfigPath, "utf-8"); const workspaceConfig: unknown = parseYaml(workspaceConfigContent); const parseResult = workspaceConfigCatalogSchema.parse(workspaceConfig); return new Map(Object.entries(parseResult.catalog ?? {})); } catch (error) { // The workspace config is absent in Docker (only the runner stage is shipped), // so a missing file is expected. Surface any other failure (e.g. malformed YAML). if ((error as NodeJS.ErrnoException).code === "ENOENT") { return new Map(); } throw error; } }; const workspaceConfigCatalogSchema = z.object({ catalog: z.record(z.string(), z.string()).optional(), }); export type PackageJsonDependencies = Record; interface PackageJson { dependencies: PackageJsonDependencies | undefined; }