Tests. Autocomplete. Few Fixes. Mocks for Electrum Service. Template-to-Json parser. Fix global paths. Use IO Dependency injection for logging from cli. Additional commands in CLI.
This commit is contained in:
102
scripts/template-to-json.ts
Normal file
102
scripts/template-to-json.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
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();
|
||||
Reference in New Issue
Block a user