@ethernauta/cli
Advanced tools
| import { readFileSync, readdirSync, writeFileSync } from "node:fs"; | ||
| import { join, resolve } from "node:path"; | ||
| import { DescriptionSchema, to_selector } from "@ethernauta/abi"; | ||
| import { emit_file_basename_for, emit_name_for, generate } from "@ethernauta/abi/generator"; | ||
| import { Bytes4Schema } from "@ethernauta/core"; | ||
| import { bytes_to_hex } from "@ethernauta/utils"; | ||
| import { array, object, parse, string } from "valibot"; | ||
| //#region src/execute.ts | ||
| function selector_hex(signature) { | ||
| return parse(Bytes4Schema, bytes_to_hex(to_selector(signature))); | ||
| } | ||
| function parse_flags(args) { | ||
| let in_path; | ||
| let out_dir; | ||
| for (let i = 0; i < args.length; i++) { | ||
| const arg = args[i]; | ||
| if (arg === "--in") in_path = args[++i]; | ||
| else if (arg === "--out") out_dir = args[++i]; | ||
| } | ||
| if (!in_path || !out_dir) throw new Error("usage: ethernauta abi --in <path> --out <dir>"); | ||
| return { | ||
| in_path: resolve(process.cwd(), in_path), | ||
| out_dir: resolve(process.cwd(), out_dir) | ||
| }; | ||
| } | ||
| function load_abi(path) { | ||
| const raw = JSON.parse(readFileSync(path, "utf8")); | ||
| if (Array.isArray(raw)) return raw; | ||
| if (Array.isArray(raw.abi)) return raw.abi; | ||
| throw new Error(`expected an ABI JSON array or a foundry artifact with an \`abi\` array at ${path}`); | ||
| } | ||
| function signature_key(d) { | ||
| if (d.type !== "function") return d.type; | ||
| const param_types = d.inputs.map((i) => i.type).join(","); | ||
| return `${d.name}(${param_types})`; | ||
| } | ||
| function dedupe_by_signature(descriptions) { | ||
| const seen = /* @__PURE__ */ new Set(); | ||
| const out = []; | ||
| for (const d of descriptions) { | ||
| const key = signature_key(d); | ||
| if (seen.has(key)) continue; | ||
| seen.add(key); | ||
| out.push(d); | ||
| } | ||
| return out; | ||
| } | ||
| function write_barrel(out_dir, functions, generated_emit_names) { | ||
| const seen = /* @__PURE__ */ new Set(); | ||
| const lines = []; | ||
| for (const f of functions) { | ||
| if (f.type !== "function") continue; | ||
| const js_name = emit_name_for(f, functions); | ||
| if (seen.has(js_name)) continue; | ||
| if (!generated_emit_names.has(js_name)) continue; | ||
| seen.add(js_name); | ||
| const basename = emit_file_basename_for(f, functions); | ||
| lines.push(`export { ${js_name} } from "./${basename}"`); | ||
| } | ||
| writeFileSync(join(out_dir, "methods", "index.ts"), `${lines.join("\n")}\n`); | ||
| } | ||
| function is_generatable(d) { | ||
| if (d.type !== "function") return false; | ||
| return true; | ||
| } | ||
| function execute_abi(args) { | ||
| const { in_path, out_dir } = parse_flags(args); | ||
| const raw = load_abi(in_path); | ||
| const generatable = dedupe_by_signature(parse(array(DescriptionSchema), raw).filter((d) => d.type === "function")).filter(is_generatable); | ||
| const result = generate(generatable, out_dir); | ||
| write_barrel(out_dir, generatable, new Set(result.generated)); | ||
| console.log(`regenerated ${result.generated.length} methods into ${out_dir}/methods/`); | ||
| if (result.skipped.length > 0) { | ||
| console.warn(`skipped ${result.skipped.length} method(s):`); | ||
| for (const s of result.skipped) console.warn(` - ${s.name}: ${s.reason}`); | ||
| } | ||
| } | ||
| function parse_registry_flags(args) { | ||
| let in_dir; | ||
| let out_file; | ||
| for (let i = 0; i < args.length; i++) { | ||
| const arg = args[i]; | ||
| if (arg === "--in") in_dir = args[++i]; | ||
| else if (arg === "--out") out_file = args[++i]; | ||
| } | ||
| if (!in_dir || !out_file) throw new Error("usage: ethernauta registry --in <dir> --out <file>"); | ||
| return { | ||
| in_dir: resolve(process.cwd(), in_dir), | ||
| out_file: resolve(process.cwd(), out_file) | ||
| }; | ||
| } | ||
| function walk_abi_jsons(root) { | ||
| const found = []; | ||
| const stack = [root]; | ||
| while (stack.length > 0) { | ||
| const dir = stack.pop(); | ||
| for (const entry of readdirSync(dir, { withFileTypes: true })) { | ||
| const full = join(dir, entry.name); | ||
| if (entry.isDirectory()) stack.push(full); | ||
| else if (entry.name.endsWith(".abi.json")) found.push(full); | ||
| } | ||
| } | ||
| return found.sort(); | ||
| } | ||
| object({ | ||
| signature: string(), | ||
| name: string(), | ||
| types: array(string()), | ||
| param_names: array(string()), | ||
| source: string() | ||
| }); | ||
| function collect_entries(files, root) { | ||
| const out = /* @__PURE__ */ new Map(); | ||
| for (const file of files) { | ||
| const raw = load_abi(file); | ||
| const descriptions = parse(array(DescriptionSchema), raw); | ||
| for (const d of descriptions) { | ||
| if (d.type !== "function") continue; | ||
| const types = d.inputs.map((i) => i.type); | ||
| const param_names = d.inputs.map((i) => i.name); | ||
| const signature = `${d.name}(${types.join(",")})`; | ||
| const selector = selector_hex(signature); | ||
| const relative = file.startsWith(`${root}/`) ? file.slice(root.length + 1) : file; | ||
| const existing = out.get(selector); | ||
| if (existing) { | ||
| if (existing.signature !== signature) throw new Error(`selector collision ${selector}: '${existing.signature}' (${existing.source}) vs '${signature}' (${relative})`); | ||
| continue; | ||
| } | ||
| out.set(selector, { | ||
| signature, | ||
| name: d.name, | ||
| types, | ||
| param_names, | ||
| source: relative | ||
| }); | ||
| } | ||
| } | ||
| return out; | ||
| } | ||
| function format_registry(entries) { | ||
| return `// AUTO-GENERATED — do not edit. Run \`pnpm --filter @ethernauta/erc generate\`. | ||
| export const REGISTRY = { | ||
| ${Array.from(entries.entries()).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([selector, entry]) => { | ||
| const types = entry.types.map((t) => JSON.stringify(t)).join(", "); | ||
| const names = entry.param_names.map((n) => JSON.stringify(n)).join(", "); | ||
| return ` ${JSON.stringify(selector)}: { name: ${JSON.stringify(entry.name)}, signature: ${JSON.stringify(entry.signature)}, types: [${types}], param_names: [${names}] },`; | ||
| }).join("\n")} | ||
| } as const | ||
| export type RegistrySelector = keyof typeof REGISTRY | ||
| export type RegistryEntry = (typeof REGISTRY)[RegistrySelector] | ||
| `; | ||
| } | ||
| function execute_registry(args) { | ||
| const { in_dir, out_file } = parse_registry_flags(args); | ||
| const files = walk_abi_jsons(in_dir); | ||
| const entries = collect_entries(files, in_dir); | ||
| writeFileSync(out_file, format_registry(entries)); | ||
| console.log(`wrote ${entries.size} registry entries from ${files.length} ABI JSONs to ${out_file}`); | ||
| } | ||
| //#endregion | ||
| export { execute_registry as n, execute_abi as t }; | ||
| //# sourceMappingURL=execute-C3gnIegB.js.map |
| {"version":3,"file":"execute-C3gnIegB.js","names":[],"sources":["../src/execute.ts"],"sourcesContent":["import {\n readdirSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\"\nimport { join, resolve } from \"node:path\"\nimport {\n type Description,\n DescriptionSchema,\n to_selector,\n} from \"@ethernauta/abi\"\nimport {\n emit_file_basename_for,\n emit_name_for,\n generate,\n} from \"@ethernauta/abi/generator\"\nimport { type Bytes4, Bytes4Schema } from \"@ethernauta/core\"\nimport { bytes_to_hex } from \"@ethernauta/utils\"\nimport {\n array,\n type InferOutput,\n object,\n parse,\n string,\n} from \"valibot\"\n\nfunction selector_hex(signature: string): Bytes4 {\n return parse(\n Bytes4Schema,\n bytes_to_hex(to_selector(signature)),\n )\n}\n\nfunction parse_flags(args: string[]) {\n let in_path: string | undefined\n let out_dir: string | undefined\n for (let i = 0; i < args.length; i++) {\n const arg = args[i] as string\n if (arg === \"--in\") {\n in_path = args[++i]\n } else if (arg === \"--out\") {\n out_dir = args[++i]\n }\n }\n if (!in_path || !out_dir) {\n throw new Error(\n \"usage: ethernauta abi --in <path> --out <dir>\",\n )\n }\n return {\n in_path: resolve(process.cwd(), in_path),\n out_dir: resolve(process.cwd(), out_dir),\n }\n}\n\nfunction load_abi(path: string): unknown[] {\n const raw = JSON.parse(readFileSync(path, \"utf8\"))\n if (Array.isArray(raw)) return raw\n if (Array.isArray(raw.abi)) return raw.abi\n throw new Error(\n `expected an ABI JSON array or a foundry artifact with an \\`abi\\` array at ${path}`,\n )\n}\n\nfunction signature_key(d: Description): string {\n if (d.type !== \"function\") return d.type\n const param_types = d.inputs.map((i) => i.type).join(\",\")\n return `${d.name}(${param_types})`\n}\n\nfunction _snake_or_kebab(name: string): string {\n if (name.includes(\"_\")) return name\n return name\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .toLowerCase()\n}\n\nfunction dedupe_by_signature(\n descriptions: Description[],\n): Description[] {\n const seen = new Set<string>()\n const out: Description[] = []\n for (const d of descriptions) {\n const key = signature_key(d)\n if (seen.has(key)) continue\n seen.add(key)\n out.push(d)\n }\n return out\n}\n\nfunction write_barrel(\n out_dir: string,\n functions: Description[],\n generated_emit_names: Set<string>,\n): void {\n const seen = new Set<string>()\n const lines: string[] = []\n for (const f of functions) {\n if (f.type !== \"function\") continue\n const js_name = emit_name_for(f, functions)\n if (seen.has(js_name)) continue\n if (!generated_emit_names.has(js_name)) continue\n seen.add(js_name)\n const basename = emit_file_basename_for(f, functions)\n lines.push(`export { ${js_name} } from \"./${basename}\"`)\n }\n writeFileSync(\n join(out_dir, \"methods\", \"index.ts\"),\n `${lines.join(\"\\n\")}\\n`,\n )\n}\n\nfunction is_generatable(d: Description): boolean {\n if (d.type !== \"function\") return false\n return true\n}\n\nexport function execute_abi(args: string[]): void {\n const { in_path, out_dir } = parse_flags(args)\n const raw = load_abi(in_path)\n const descriptions = parse(array(DescriptionSchema), raw)\n const functions = dedupe_by_signature(\n descriptions.filter((d) => d.type === \"function\"),\n )\n const generatable = functions.filter(is_generatable)\n const result = generate(generatable, out_dir)\n write_barrel(\n out_dir,\n generatable,\n new Set(result.generated),\n )\n console.log(\n `regenerated ${result.generated.length} methods into ${out_dir}/methods/`,\n )\n if (result.skipped.length > 0) {\n console.warn(\n `skipped ${result.skipped.length} method(s):`,\n )\n for (const s of result.skipped) {\n console.warn(` - ${s.name}: ${s.reason}`)\n }\n }\n}\n\nfunction parse_registry_flags(args: string[]) {\n let in_dir: string | undefined\n let out_file: string | undefined\n for (let i = 0; i < args.length; i++) {\n const arg = args[i] as string\n if (arg === \"--in\") {\n in_dir = args[++i]\n } else if (arg === \"--out\") {\n out_file = args[++i]\n }\n }\n if (!in_dir || !out_file) {\n throw new Error(\n \"usage: ethernauta registry --in <dir> --out <file>\",\n )\n }\n return {\n in_dir: resolve(process.cwd(), in_dir),\n out_file: resolve(process.cwd(), out_file),\n }\n}\n\nfunction walk_abi_jsons(root: string): string[] {\n const found: string[] = []\n const stack = [root]\n while (stack.length > 0) {\n const dir = stack.pop() as string\n for (const entry of readdirSync(dir, {\n withFileTypes: true,\n })) {\n const full = join(dir, entry.name)\n if (entry.isDirectory()) {\n stack.push(full)\n } else if (entry.name.endsWith(\".abi.json\")) {\n found.push(full)\n }\n }\n }\n return found.sort()\n}\n\nconst RegistryEntrySchema = object({\n signature: string(),\n name: string(),\n types: array(string()),\n param_names: array(string()),\n source: string(),\n})\ntype RegistryEntry = InferOutput<typeof RegistryEntrySchema>\n\nfunction collect_entries(\n files: string[],\n root: string,\n): Map<Bytes4, RegistryEntry> {\n const out = new Map<Bytes4, RegistryEntry>()\n for (const file of files) {\n const raw = load_abi(file)\n const descriptions = parse(\n array(DescriptionSchema),\n raw,\n )\n for (const d of descriptions) {\n if (d.type !== \"function\") continue\n const types = d.inputs.map((i) => i.type)\n const param_names = d.inputs.map((i) => i.name)\n const signature = `${d.name}(${types.join(\",\")})`\n const selector = selector_hex(signature)\n const relative = file.startsWith(`${root}/`)\n ? file.slice(root.length + 1)\n : file\n const existing = out.get(selector)\n if (existing) {\n if (existing.signature !== signature) {\n throw new Error(\n `selector collision ${selector}: '${existing.signature}' (${existing.source}) vs '${signature}' (${relative})`,\n )\n }\n continue\n }\n out.set(selector, {\n signature,\n name: d.name,\n types,\n param_names,\n source: relative,\n })\n }\n }\n return out\n}\n\nfunction format_registry(\n entries: Map<Bytes4, RegistryEntry>,\n): string {\n const sorted = Array.from(entries.entries()).sort(\n ([a], [b]) => (a < b ? -1 : a > b ? 1 : 0),\n )\n const rows = sorted.map(([selector, entry]) => {\n const types = entry.types\n .map((t) => JSON.stringify(t))\n .join(\", \")\n const names = entry.param_names\n .map((n) => JSON.stringify(n))\n .join(\", \")\n return ` ${JSON.stringify(selector)}: { name: ${JSON.stringify(entry.name)}, signature: ${JSON.stringify(entry.signature)}, types: [${types}], param_names: [${names}] },`\n })\n return `// AUTO-GENERATED — do not edit. Run \\`pnpm --filter @ethernauta/erc generate\\`.\n\nexport const REGISTRY = {\n${rows.join(\"\\n\")}\n} as const\n\nexport type RegistrySelector = keyof typeof REGISTRY\nexport type RegistryEntry = (typeof REGISTRY)[RegistrySelector]\n`\n}\n\nexport function execute_registry(args: string[]): void {\n const { in_dir, out_file } = parse_registry_flags(args)\n const files = walk_abi_jsons(in_dir)\n const entries = collect_entries(files, in_dir)\n writeFileSync(out_file, format_registry(entries))\n console.log(\n `wrote ${entries.size} registry entries from ${files.length} ABI JSONs to ${out_file}`,\n )\n}\n"],"mappings":"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"} |
+1
-1
| #!/usr/bin/env node | ||
| import { n as execute_registry, t as execute_abi } from "./execute-by8xsAv7.js"; | ||
| import { n as execute_registry, t as execute_abi } from "./execute-C3gnIegB.js"; | ||
@@ -4,0 +4,0 @@ //#region src/bin.ts |
+1
-1
@@ -1,3 +0,3 @@ | ||
| import { n as execute_registry, t as execute_abi } from "./execute-by8xsAv7.js"; | ||
| import { n as execute_registry, t as execute_abi } from "./execute-C3gnIegB.js"; | ||
| export { execute_abi, execute_registry }; |
+6
-5
@@ -5,3 +5,3 @@ { | ||
| "type": "module", | ||
| "version": "0.0.44", | ||
| "version": "0.0.46", | ||
| "publishConfig": { | ||
@@ -23,5 +23,5 @@ "access": "public" | ||
| "dependencies": { | ||
| "@ethernauta/abi": "0.0.44", | ||
| "@ethernauta/core": "0.0.44", | ||
| "@ethernauta/utils": "0.0.44" | ||
| "@ethernauta/abi": "0.0.46", | ||
| "@ethernauta/core": "0.0.46", | ||
| "@ethernauta/utils": "0.0.46" | ||
| }, | ||
@@ -42,4 +42,5 @@ "peerDependencies": { | ||
| "typecheck": "tsc --noEmit", | ||
| "test:unit": "vitest" | ||
| "test:unit": "vitest", | ||
| "test:integration": "echo no integration tests" | ||
| } | ||
| } |
+4
-4
@@ -17,3 +17,3 @@ import { | ||
| } from "@ethernauta/abi/generator" | ||
| import { type Bytes4, bytes4Schema } from "@ethernauta/core" | ||
| import { type Bytes4, Bytes4Schema } from "@ethernauta/core" | ||
| import { bytes_to_hex } from "@ethernauta/utils" | ||
@@ -30,3 +30,3 @@ import { | ||
| return parse( | ||
| bytes4Schema, | ||
| Bytes4Schema, | ||
| bytes_to_hex(to_selector(signature)), | ||
@@ -189,3 +189,3 @@ ) | ||
| const registryEntrySchema = object({ | ||
| const RegistryEntrySchema = object({ | ||
| signature: string(), | ||
@@ -197,3 +197,3 @@ name: string(), | ||
| }) | ||
| type RegistryEntry = InferOutput<typeof registryEntrySchema> | ||
| type RegistryEntry = InferOutput<typeof RegistryEntrySchema> | ||
@@ -200,0 +200,0 @@ function collect_entries( |
| import { readFileSync, readdirSync, writeFileSync } from "node:fs"; | ||
| import { join, resolve } from "node:path"; | ||
| import { DescriptionSchema, to_selector } from "@ethernauta/abi"; | ||
| import { emit_file_basename_for, emit_name_for, generate } from "@ethernauta/abi/generator"; | ||
| import { bytes4Schema } from "@ethernauta/core"; | ||
| import { bytes_to_hex } from "@ethernauta/utils"; | ||
| import { array, object, parse, string } from "valibot"; | ||
| //#region src/execute.ts | ||
| function selector_hex(signature) { | ||
| return parse(bytes4Schema, bytes_to_hex(to_selector(signature))); | ||
| } | ||
| function parse_flags(args) { | ||
| let in_path; | ||
| let out_dir; | ||
| for (let i = 0; i < args.length; i++) { | ||
| const arg = args[i]; | ||
| if (arg === "--in") in_path = args[++i]; | ||
| else if (arg === "--out") out_dir = args[++i]; | ||
| } | ||
| if (!in_path || !out_dir) throw new Error("usage: ethernauta abi --in <path> --out <dir>"); | ||
| return { | ||
| in_path: resolve(process.cwd(), in_path), | ||
| out_dir: resolve(process.cwd(), out_dir) | ||
| }; | ||
| } | ||
| function load_abi(path) { | ||
| const raw = JSON.parse(readFileSync(path, "utf8")); | ||
| if (Array.isArray(raw)) return raw; | ||
| if (Array.isArray(raw.abi)) return raw.abi; | ||
| throw new Error(`expected an ABI JSON array or a foundry artifact with an \`abi\` array at ${path}`); | ||
| } | ||
| function signature_key(d) { | ||
| if (d.type !== "function") return d.type; | ||
| const param_types = d.inputs.map((i) => i.type).join(","); | ||
| return `${d.name}(${param_types})`; | ||
| } | ||
| function dedupe_by_signature(descriptions) { | ||
| const seen = /* @__PURE__ */ new Set(); | ||
| const out = []; | ||
| for (const d of descriptions) { | ||
| const key = signature_key(d); | ||
| if (seen.has(key)) continue; | ||
| seen.add(key); | ||
| out.push(d); | ||
| } | ||
| return out; | ||
| } | ||
| function write_barrel(out_dir, functions, generated_emit_names) { | ||
| const seen = /* @__PURE__ */ new Set(); | ||
| const lines = []; | ||
| for (const f of functions) { | ||
| if (f.type !== "function") continue; | ||
| const js_name = emit_name_for(f, functions); | ||
| if (seen.has(js_name)) continue; | ||
| if (!generated_emit_names.has(js_name)) continue; | ||
| seen.add(js_name); | ||
| const basename = emit_file_basename_for(f, functions); | ||
| lines.push(`export { ${js_name} } from "./${basename}"`); | ||
| } | ||
| writeFileSync(join(out_dir, "methods", "index.ts"), `${lines.join("\n")}\n`); | ||
| } | ||
| function is_generatable(d) { | ||
| if (d.type !== "function") return false; | ||
| return true; | ||
| } | ||
| function execute_abi(args) { | ||
| const { in_path, out_dir } = parse_flags(args); | ||
| const raw = load_abi(in_path); | ||
| const generatable = dedupe_by_signature(parse(array(DescriptionSchema), raw).filter((d) => d.type === "function")).filter(is_generatable); | ||
| const result = generate(generatable, out_dir); | ||
| write_barrel(out_dir, generatable, new Set(result.generated)); | ||
| console.log(`regenerated ${result.generated.length} methods into ${out_dir}/methods/`); | ||
| if (result.skipped.length > 0) { | ||
| console.warn(`skipped ${result.skipped.length} method(s):`); | ||
| for (const s of result.skipped) console.warn(` - ${s.name}: ${s.reason}`); | ||
| } | ||
| } | ||
| function parse_registry_flags(args) { | ||
| let in_dir; | ||
| let out_file; | ||
| for (let i = 0; i < args.length; i++) { | ||
| const arg = args[i]; | ||
| if (arg === "--in") in_dir = args[++i]; | ||
| else if (arg === "--out") out_file = args[++i]; | ||
| } | ||
| if (!in_dir || !out_file) throw new Error("usage: ethernauta registry --in <dir> --out <file>"); | ||
| return { | ||
| in_dir: resolve(process.cwd(), in_dir), | ||
| out_file: resolve(process.cwd(), out_file) | ||
| }; | ||
| } | ||
| function walk_abi_jsons(root) { | ||
| const found = []; | ||
| const stack = [root]; | ||
| while (stack.length > 0) { | ||
| const dir = stack.pop(); | ||
| for (const entry of readdirSync(dir, { withFileTypes: true })) { | ||
| const full = join(dir, entry.name); | ||
| if (entry.isDirectory()) stack.push(full); | ||
| else if (entry.name.endsWith(".abi.json")) found.push(full); | ||
| } | ||
| } | ||
| return found.sort(); | ||
| } | ||
| object({ | ||
| signature: string(), | ||
| name: string(), | ||
| types: array(string()), | ||
| param_names: array(string()), | ||
| source: string() | ||
| }); | ||
| function collect_entries(files, root) { | ||
| const out = /* @__PURE__ */ new Map(); | ||
| for (const file of files) { | ||
| const raw = load_abi(file); | ||
| const descriptions = parse(array(DescriptionSchema), raw); | ||
| for (const d of descriptions) { | ||
| if (d.type !== "function") continue; | ||
| const types = d.inputs.map((i) => i.type); | ||
| const param_names = d.inputs.map((i) => i.name); | ||
| const signature = `${d.name}(${types.join(",")})`; | ||
| const selector = selector_hex(signature); | ||
| const relative = file.startsWith(`${root}/`) ? file.slice(root.length + 1) : file; | ||
| const existing = out.get(selector); | ||
| if (existing) { | ||
| if (existing.signature !== signature) throw new Error(`selector collision ${selector}: '${existing.signature}' (${existing.source}) vs '${signature}' (${relative})`); | ||
| continue; | ||
| } | ||
| out.set(selector, { | ||
| signature, | ||
| name: d.name, | ||
| types, | ||
| param_names, | ||
| source: relative | ||
| }); | ||
| } | ||
| } | ||
| return out; | ||
| } | ||
| function format_registry(entries) { | ||
| return `// AUTO-GENERATED — do not edit. Run \`pnpm --filter @ethernauta/erc generate\`. | ||
| export const REGISTRY = { | ||
| ${Array.from(entries.entries()).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([selector, entry]) => { | ||
| const types = entry.types.map((t) => JSON.stringify(t)).join(", "); | ||
| const names = entry.param_names.map((n) => JSON.stringify(n)).join(", "); | ||
| return ` ${JSON.stringify(selector)}: { name: ${JSON.stringify(entry.name)}, signature: ${JSON.stringify(entry.signature)}, types: [${types}], param_names: [${names}] },`; | ||
| }).join("\n")} | ||
| } as const | ||
| export type RegistrySelector = keyof typeof REGISTRY | ||
| export type RegistryEntry = (typeof REGISTRY)[RegistrySelector] | ||
| `; | ||
| } | ||
| function execute_registry(args) { | ||
| const { in_dir, out_file } = parse_registry_flags(args); | ||
| const files = walk_abi_jsons(in_dir); | ||
| const entries = collect_entries(files, in_dir); | ||
| writeFileSync(out_file, format_registry(entries)); | ||
| console.log(`wrote ${entries.size} registry entries from ${files.length} ABI JSONs to ${out_file}`); | ||
| } | ||
| //#endregion | ||
| export { execute_registry as n, execute_abi as t }; | ||
| //# sourceMappingURL=execute-by8xsAv7.js.map |
| {"version":3,"file":"execute-by8xsAv7.js","names":[],"sources":["../src/execute.ts"],"sourcesContent":["import {\n readdirSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\"\nimport { join, resolve } from \"node:path\"\nimport {\n type Description,\n DescriptionSchema,\n to_selector,\n} from \"@ethernauta/abi\"\nimport {\n emit_file_basename_for,\n emit_name_for,\n generate,\n} from \"@ethernauta/abi/generator\"\nimport { type Bytes4, bytes4Schema } from \"@ethernauta/core\"\nimport { bytes_to_hex } from \"@ethernauta/utils\"\nimport {\n array,\n type InferOutput,\n object,\n parse,\n string,\n} from \"valibot\"\n\nfunction selector_hex(signature: string): Bytes4 {\n return parse(\n bytes4Schema,\n bytes_to_hex(to_selector(signature)),\n )\n}\n\nfunction parse_flags(args: string[]) {\n let in_path: string | undefined\n let out_dir: string | undefined\n for (let i = 0; i < args.length; i++) {\n const arg = args[i] as string\n if (arg === \"--in\") {\n in_path = args[++i]\n } else if (arg === \"--out\") {\n out_dir = args[++i]\n }\n }\n if (!in_path || !out_dir) {\n throw new Error(\n \"usage: ethernauta abi --in <path> --out <dir>\",\n )\n }\n return {\n in_path: resolve(process.cwd(), in_path),\n out_dir: resolve(process.cwd(), out_dir),\n }\n}\n\nfunction load_abi(path: string): unknown[] {\n const raw = JSON.parse(readFileSync(path, \"utf8\"))\n if (Array.isArray(raw)) return raw\n if (Array.isArray(raw.abi)) return raw.abi\n throw new Error(\n `expected an ABI JSON array or a foundry artifact with an \\`abi\\` array at ${path}`,\n )\n}\n\nfunction signature_key(d: Description): string {\n if (d.type !== \"function\") return d.type\n const param_types = d.inputs.map((i) => i.type).join(\",\")\n return `${d.name}(${param_types})`\n}\n\nfunction _snake_or_kebab(name: string): string {\n if (name.includes(\"_\")) return name\n return name\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .toLowerCase()\n}\n\nfunction dedupe_by_signature(\n descriptions: Description[],\n): Description[] {\n const seen = new Set<string>()\n const out: Description[] = []\n for (const d of descriptions) {\n const key = signature_key(d)\n if (seen.has(key)) continue\n seen.add(key)\n out.push(d)\n }\n return out\n}\n\nfunction write_barrel(\n out_dir: string,\n functions: Description[],\n generated_emit_names: Set<string>,\n): void {\n const seen = new Set<string>()\n const lines: string[] = []\n for (const f of functions) {\n if (f.type !== \"function\") continue\n const js_name = emit_name_for(f, functions)\n if (seen.has(js_name)) continue\n if (!generated_emit_names.has(js_name)) continue\n seen.add(js_name)\n const basename = emit_file_basename_for(f, functions)\n lines.push(`export { ${js_name} } from \"./${basename}\"`)\n }\n writeFileSync(\n join(out_dir, \"methods\", \"index.ts\"),\n `${lines.join(\"\\n\")}\\n`,\n )\n}\n\nfunction is_generatable(d: Description): boolean {\n if (d.type !== \"function\") return false\n return true\n}\n\nexport function execute_abi(args: string[]): void {\n const { in_path, out_dir } = parse_flags(args)\n const raw = load_abi(in_path)\n const descriptions = parse(array(DescriptionSchema), raw)\n const functions = dedupe_by_signature(\n descriptions.filter((d) => d.type === \"function\"),\n )\n const generatable = functions.filter(is_generatable)\n const result = generate(generatable, out_dir)\n write_barrel(\n out_dir,\n generatable,\n new Set(result.generated),\n )\n console.log(\n `regenerated ${result.generated.length} methods into ${out_dir}/methods/`,\n )\n if (result.skipped.length > 0) {\n console.warn(\n `skipped ${result.skipped.length} method(s):`,\n )\n for (const s of result.skipped) {\n console.warn(` - ${s.name}: ${s.reason}`)\n }\n }\n}\n\nfunction parse_registry_flags(args: string[]) {\n let in_dir: string | undefined\n let out_file: string | undefined\n for (let i = 0; i < args.length; i++) {\n const arg = args[i] as string\n if (arg === \"--in\") {\n in_dir = args[++i]\n } else if (arg === \"--out\") {\n out_file = args[++i]\n }\n }\n if (!in_dir || !out_file) {\n throw new Error(\n \"usage: ethernauta registry --in <dir> --out <file>\",\n )\n }\n return {\n in_dir: resolve(process.cwd(), in_dir),\n out_file: resolve(process.cwd(), out_file),\n }\n}\n\nfunction walk_abi_jsons(root: string): string[] {\n const found: string[] = []\n const stack = [root]\n while (stack.length > 0) {\n const dir = stack.pop() as string\n for (const entry of readdirSync(dir, {\n withFileTypes: true,\n })) {\n const full = join(dir, entry.name)\n if (entry.isDirectory()) {\n stack.push(full)\n } else if (entry.name.endsWith(\".abi.json\")) {\n found.push(full)\n }\n }\n }\n return found.sort()\n}\n\nconst registryEntrySchema = object({\n signature: string(),\n name: string(),\n types: array(string()),\n param_names: array(string()),\n source: string(),\n})\ntype RegistryEntry = InferOutput<typeof registryEntrySchema>\n\nfunction collect_entries(\n files: string[],\n root: string,\n): Map<Bytes4, RegistryEntry> {\n const out = new Map<Bytes4, RegistryEntry>()\n for (const file of files) {\n const raw = load_abi(file)\n const descriptions = parse(\n array(DescriptionSchema),\n raw,\n )\n for (const d of descriptions) {\n if (d.type !== \"function\") continue\n const types = d.inputs.map((i) => i.type)\n const param_names = d.inputs.map((i) => i.name)\n const signature = `${d.name}(${types.join(\",\")})`\n const selector = selector_hex(signature)\n const relative = file.startsWith(`${root}/`)\n ? file.slice(root.length + 1)\n : file\n const existing = out.get(selector)\n if (existing) {\n if (existing.signature !== signature) {\n throw new Error(\n `selector collision ${selector}: '${existing.signature}' (${existing.source}) vs '${signature}' (${relative})`,\n )\n }\n continue\n }\n out.set(selector, {\n signature,\n name: d.name,\n types,\n param_names,\n source: relative,\n })\n }\n }\n return out\n}\n\nfunction format_registry(\n entries: Map<Bytes4, RegistryEntry>,\n): string {\n const sorted = Array.from(entries.entries()).sort(\n ([a], [b]) => (a < b ? -1 : a > b ? 1 : 0),\n )\n const rows = sorted.map(([selector, entry]) => {\n const types = entry.types\n .map((t) => JSON.stringify(t))\n .join(\", \")\n const names = entry.param_names\n .map((n) => JSON.stringify(n))\n .join(\", \")\n return ` ${JSON.stringify(selector)}: { name: ${JSON.stringify(entry.name)}, signature: ${JSON.stringify(entry.signature)}, types: [${types}], param_names: [${names}] },`\n })\n return `// AUTO-GENERATED — do not edit. Run \\`pnpm --filter @ethernauta/erc generate\\`.\n\nexport const REGISTRY = {\n${rows.join(\"\\n\")}\n} as const\n\nexport type RegistrySelector = keyof typeof REGISTRY\nexport type RegistryEntry = (typeof REGISTRY)[RegistrySelector]\n`\n}\n\nexport function execute_registry(args: string[]): void {\n const { in_dir, out_file } = parse_registry_flags(args)\n const files = walk_abi_jsons(in_dir)\n const entries = collect_entries(files, in_dir)\n writeFileSync(out_file, format_registry(entries))\n console.log(\n `wrote ${entries.size} registry entries from ${files.length} ABI JSONs to ${out_file}`,\n )\n}\n"],"mappings":"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"} |
33997
0.16%+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
Updated
Updated
Updated