import { stat } from "node:fs/promises"; import path from "node:path"; import { clientRootForRecipe, exists, fixturesRoot, loadRecipe, projectRoot, readJson, recipesRoot, workRoot, } from "./lib.mjs"; import { runWowExporter } from "./wow-export-tool.mjs"; async function resultMtime(output) { const resultFile = path.join(output, "automation-result.json"); return await exists(resultFile) ? (await stat(resultFile)).mtimeMs : 0; } async function checkedResult(slug, output, previousMtime) { const resultFile = path.join(output, "automation-result.json"); if (!await exists(resultFile)) return { status: "blocked", dungeonId: slug, blockers: [`wow.export produced no automation result at ${resultFile}.`] }; if ((await stat(resultFile)).mtimeMs <= previousMtime) return { status: "blocked", dungeonId: slug, blockers: [`wow.export did not refresh ${resultFile}; refusing to accept a stale result from an earlier run.`], }; const result = await readJson(resultFile); if (!result.ok) return { status: "blocked", dungeonId: slug, blockers: result.missingDependencies?.length ? result.missingDependencies.map((dependency) => `Missing ${dependency.type} dependency ${dependency.path}.`) : [result.error ?? "wow.export reported an unknown extraction failure."], }; return null; } export async function exportDungeonSource(slug) { const recipe = await loadRecipe(slug); const clientRoot = clientRootForRecipe(recipe); const output = path.join(workRoot, slug, "source-export"); const previousMtime = await resultMtime(output); await runWowExporter([ "--export-dungeon", "--recipe", path.join(recipesRoot, `${slug}.json`), "--client-root", clientRoot, "--output", output, ], { projectRoot }); const blocked = await checkedResult(slug, output, previousMtime); if (blocked) return blocked; return { status: "green", dungeonId: slug, output: path.relative(projectRoot, output).replace(/\\/g, "/") }; } export async function exportClientDiscoveryTables(slug) { const recipe = await loadRecipe(slug); const clientRoot = clientRootForRecipe(recipe); const output = path.join(workRoot, slug, "client-dbc"); const previousMtime = await resultMtime(output); await runWowExporter([ "--discover-dungeon", "--recipe", path.join(recipesRoot, `${slug}.json`), "--client-root", clientRoot, "--output", output, ], { projectRoot }); const blocked = await checkedResult(slug, output, previousMtime); if (blocked) throw new Error(blocked.blockers.join("\n")); return output; } export async function exportCreatureSource(slug) { const recipe = await loadRecipe(slug); const clientRoot = clientRootForRecipe(recipe); let selected = recipe.creatures?.length ? path.join(recipesRoot, `${slug}.json`) : null; for (const candidate of [ path.join(workRoot, slug, "creature-export.recipe.json"), path.join(fixturesRoot, slug, "creature-export.recipe.json"), ]) { if (await exists(candidate)) { selected = candidate; break; } } if (!selected) throw new Error(`${slug}: reviewed creature-export.recipe.json is missing.`); const selectedRecipe = await readJson(selected); const unresolved = (selectedRecipe.creatures ?? []).filter((creature) => ( creature.unresolvedComposite || !creature.displayId || !creature.modelPath )); if (unresolved.length) return { status: "blocked", dungeonId: slug, blockers: unresolved.map((creature) => `${creature.name ?? creature.slug} has an unresolved display/composite recipe.`), }; const output = path.join(workRoot, slug, "creature-source"); const previousMtime = await resultMtime(output); await runWowExporter([ "--export-creatures", "--recipe", selected, "--client-root", clientRoot, "--output", output, ], { projectRoot }); const blocked = await checkedResult(slug, output, previousMtime); if (blocked) return blocked; return { status: "green", dungeonId: slug, output: path.relative(projectRoot, output).replace(/\\/g, "/") }; }