Release Healer Man 0.1.22
This commit is contained in:
1 parent
f6f2375145
commit
7f6001723a
144 files changed
+54107
-690
No files matched your search
@@ -0,0 +1,125 @@
|
||||
import { spawn as spawnChild } from "node:child_process";
|
||||
import { stat } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
|
||||
export const WOW_EXPORT_AUTOMATION_VERSION = "0.2.19";
|
||||
export const WOW_EXPORT_REQUIRED_FLAGS = Object.freeze([
|
||||
"--disable-auto-update",
|
||||
"--discover-dungeon",
|
||||
"--export-dungeon",
|
||||
"--export-creatures",
|
||||
]);
|
||||
|
||||
export function defaultWowExportExecutable(projectRoot) {
|
||||
return path.resolve(
|
||||
projectRoot,
|
||||
`../wow335a/dungeon-export-work/tools/wow.export-${WOW_EXPORT_AUTOMATION_VERSION}/wow.export.exe`,
|
||||
);
|
||||
}
|
||||
|
||||
async function inspectCandidate(file, statFile) {
|
||||
try {
|
||||
const info = await statFile(file);
|
||||
return info.isFile()
|
||||
? { usable: true, reason: null }
|
||||
: { usable: false, reason: "path exists but is not a file" };
|
||||
} catch (error) {
|
||||
if (error?.code === "ENOENT") return { usable: false, reason: "file does not exist" };
|
||||
return { usable: false, reason: `cannot inspect file (${error?.code ?? error?.message ?? "unknown error"})` };
|
||||
}
|
||||
}
|
||||
|
||||
function unavailableMessage(checks, configured) {
|
||||
const checked = checks.map(({ label, file, reason }) => ` - ${label}: ${file} (${reason})`).join("\n");
|
||||
const selection = configured
|
||||
? "WOW_EXPORT_BIN is set, so the default location was not used. Correct or unset that variable."
|
||||
: "Restore the patched portable bundle at the default location, or set WOW_EXPORT_BIN to its exact executable path.";
|
||||
return [
|
||||
"HealerMan's patched wow.export automation bridge is unavailable.",
|
||||
`Required build: wow.export ${WOW_EXPORT_AUTOMATION_VERSION} with these automation flags: ${WOW_EXPORT_REQUIRED_FLAGS.join(", ")}.`,
|
||||
"Checked:",
|
||||
checked,
|
||||
selection,
|
||||
"A stock GUI-only wow.export executable is not a drop-in replacement unless it includes the HealerMan automation patch.",
|
||||
"No export was started and no placeholder assets were created.",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
export class WowExportUnavailableError extends Error {
|
||||
constructor(checks, configured) {
|
||||
super(unavailableMessage(checks, configured));
|
||||
this.name = "WowExportUnavailableError";
|
||||
this.code = "WOW_EXPORT_AUTOMATION_UNAVAILABLE";
|
||||
this.checks = checks;
|
||||
this.requiredVersion = WOW_EXPORT_AUTOMATION_VERSION;
|
||||
this.requiredFlags = [...WOW_EXPORT_REQUIRED_FLAGS];
|
||||
}
|
||||
}
|
||||
|
||||
export async function resolveWowExportExecutable({
|
||||
projectRoot,
|
||||
environment = process.env,
|
||||
statFile = stat,
|
||||
} = {}) {
|
||||
if (!projectRoot) throw new TypeError("resolveWowExportExecutable requires projectRoot.");
|
||||
const configuredValue = environment.WOW_EXPORT_BIN?.trim();
|
||||
const candidates = configuredValue
|
||||
? [{ label: "WOW_EXPORT_BIN", file: path.resolve(projectRoot, configuredValue) }]
|
||||
: [{ label: "default portable path", file: defaultWowExportExecutable(projectRoot) }];
|
||||
const checks = [];
|
||||
for (const candidate of candidates) {
|
||||
const inspection = await inspectCandidate(candidate.file, statFile);
|
||||
if (inspection.usable) return candidate.file;
|
||||
checks.push({ ...candidate, reason: inspection.reason });
|
||||
}
|
||||
throw new WowExportUnavailableError(checks, Boolean(configuredValue));
|
||||
}
|
||||
|
||||
function exporterFailureMessage(executable, argumentsList, detail) {
|
||||
const operation = argumentsList.find((argument) => argument.startsWith("--")) ?? "unknown operation";
|
||||
return [
|
||||
`wow.export automation failed during ${operation}: ${detail}`,
|
||||
`Executable: ${executable}`,
|
||||
`Required automation flags: ${WOW_EXPORT_REQUIRED_FLAGS.join(", ")}.`,
|
||||
"Verify that WOW_EXPORT_BIN points to the HealerMan-patched build, not the stock GUI-only release.",
|
||||
"No placeholder assets were created.",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
export async function runWowExporter(argumentsList, {
|
||||
projectRoot,
|
||||
environment = process.env,
|
||||
statFile = stat,
|
||||
spawn = spawnChild,
|
||||
} = {}) {
|
||||
const executable = await resolveWowExportExecutable({ projectRoot, environment, statFile });
|
||||
await new Promise((resolve, reject) => {
|
||||
const child = spawn(executable, ["--disable-auto-update", ...argumentsList], {
|
||||
cwd: path.dirname(executable),
|
||||
stdio: "inherit",
|
||||
windowsHide: true,
|
||||
});
|
||||
let settled = false;
|
||||
const fail = (error) => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
reject(error);
|
||||
};
|
||||
child.once("error", (error) => fail(new Error(exporterFailureMessage(
|
||||
executable,
|
||||
argumentsList,
|
||||
`process could not start (${error?.code ?? error?.message ?? "unknown error"})`,
|
||||
))));
|
||||
child.once("exit", (code, signal) => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
if (code === 0) resolve();
|
||||
else reject(new Error(exporterFailureMessage(
|
||||
executable,
|
||||
argumentsList,
|
||||
signal ? `process exited after signal ${signal}` : `process exited with code ${code}`,
|
||||
)));
|
||||
});
|
||||
});
|
||||
return executable;
|
||||
}
|
||||
Reference in new issue
Block a user