import { execFileSync } from "node:child_process"; import { readFileSync, readdirSync, statSync } from "node:fs"; import { relative, resolve } from "node:path"; function runPnpm(args) { if (process.platform === "win32") { execFileSync(process.env.ComSpec ?? "cmd.exe", ["/d", "/s", "/c", `pnpm ${args.join(" ")}`], { stdio: "inherit", }); return; } execFileSync("pnpm", args, { stdio: "inherit" }); } export function buildApiContracts() { runPnpm(["--filter", "@dada/asset-release-manifest", "build"]); runPnpm(["--filter", "@dada/shared-contracts", "build"]); runPnpm(["--filter", "@dada/api", "build"]); } export async function createOpenApiDocument() { const moduleUrl = new URL(`../../apps/api/dist/app.js?build=${Date.now()}`, import.meta.url); const { createApp } = await import(moduleUrl.href); const app = await createApp(); await app.ready(); const document = app.swagger(); await app.close(); return sortObject(document); } export function formattedJson(value) { return `${JSON.stringify(sortObject(value), null, 2)}\n`; } export function readTree(root) { const files = new Map(); function visit(path) { for (const name of readdirSync(path)) { const child = resolve(path, name); if (statSync(child).isDirectory()) visit(child); else files.set(relative(root, child).replaceAll("\\", "/"), readFileSync(child, "utf8")); } } visit(root); return files; } export function compareTrees(expectedRoot, actualRoot) { const expected = readTree(expectedRoot); const actual = readTree(actualRoot); const names = new Set([...expected.keys(), ...actual.keys()]); return [...names] .sort() .filter((name) => expected.get(name) !== actual.get(name)) .map((name) => ({ actual: actual.has(name), expected: expected.has(name), file: name, })); } function sortObject(value) { if (Array.isArray(value)) return value.map(sortObject); if (!value || typeof value !== "object") return value; return Object.fromEntries( Object.entries(value) .sort(([left], [right]) => left.localeCompare(right)) .map(([key, child]) => [key, sortObject(child)]), ); }