Files
xo-cli/scripts/template-to-json.ts

103 lines
3.1 KiB
TypeScript

import fs from "node:fs/promises";
import path from "node:path";
import { pathToFileURL } from "node:url";
/**
* This just convers the <template>.ts file to a <template>.json file.
* Im fairly sure there is a util in the engine or engine-packages for this, but I decided to just keep it as simple as possible because I didn't feel like digging around for it.
*
* Usage:
* tsx scripts/template-to-json.ts ../templates/source/p2pkh.ts ./p2pkh.json p2pkhTemplate
*/
/**
* Prints usage to stderr and exits with a non-zero code.
*/
function printUsageAndExit(): never {
console.error(
[
"Usage: tsx scripts/template-to-json.ts <input.ts> <output.json> [exportName]",
"",
"Loads a TypeScript module, picks one exported value, and writes JSON.stringify to the output path.",
"If exportName is omitted: uses default export, or the only non-function export if there is exactly one.",
"",
"Example:",
" tsx scripts/template-to-json.ts ../templates/source/p2pkh.ts ./p2pkh.json p2pkhTemplate",
].join("\n"),
);
process.exit(1);
}
/**
* Collects runtime export keys whose values are not functions (typical for data/template objects).
*/
function listDataExportKeys(mod: Record<string, unknown>): string[] {
return Object.keys(mod).filter((key) => {
if (key === "__esModule") {
return false;
}
const value = mod[key];
return typeof value !== "function";
});
}
/**
* Resolves which export to serialize: explicit name, default, or a single unambiguous data export.
*/
function resolveExportedValue(
mod: Record<string, unknown>,
exportName: string | undefined,
): unknown {
if (exportName !== undefined) {
if (!(exportName in mod)) {
const keys = listDataExportKeys(mod);
console.error(
`Export "${exportName}" not found. Available data exports: ${keys.length ? keys.join(", ") : "(none)"}`,
);
process.exit(1);
}
return mod[exportName];
}
if ("default" in mod && mod.default !== undefined) {
return mod.default;
}
const keys = listDataExportKeys(mod);
if (keys.length === 1) {
return mod[keys[0]!];
}
if (keys.length === 0) {
console.error("No suitable exports found (need default or a non-function export).");
} else {
console.error(
`Multiple data exports found; pass exportName. Candidates: ${keys.join(", ")}`,
);
}
process.exit(1);
}
async function main(): Promise<void> {
const args = process.argv.slice(2);
if (args.length < 2) {
printUsageAndExit();
}
const [inputRel, outputRel, exportName] = args;
const inputPath = path.resolve(process.cwd(), inputRel!);
const outputPath = path.resolve(process.cwd(), outputRel!);
/** Dynamic import needs a file URL so Windows paths and ESM resolution behave. */
const fileUrl = pathToFileURL(inputPath).href;
const mod = (await import(fileUrl)) as Record<string, unknown>;
const value = resolveExportedValue(mod, exportName);
const json = `${JSON.stringify(value, null, 2)}\n`;
await fs.mkdir(path.dirname(outputPath), { recursive: true });
await fs.writeFile(outputPath, json, "utf8");
console.log(`Wrote ${outputPath}`);
}
await main();