nebula/libs/es-client/scripts/gen-protobufs.mjs

172 lines
4.5 KiB
JavaScript
Raw Permalink Normal View History

2025-01-07 14:16:58 -05:00
// @ts-check
/**
* This script generates the src/protobufs directory from the proto files in the
* repos specified in `REPOS`. It uses `buf` to generate TS files from the proto
* files, and then generates an `index.ts` file to re-export the generated code.
*/
import { spawnSync } from "child_process";
import degit from "degit";
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
import { globSync } from "glob";
import { capitalize } from "lodash-es";
import { dirname, join } from "path";
import { fileURLToPath } from "url";
/**
* @typedef Repo
* @type {object}
* @property {string} repo - Git repo and branch to clone
* @property {string[]} paths - Paths to proto files relative to the repo root
*/
/**
* TODO: Add more repos here when necessary.
* @type {Repo[]}
*/
const REPOS = [
{
repo: "cosmos/cosmos-sdk#v0.50.5",
paths: ["proto"],
},
{
repo: "cosmos/ics23#master",
paths: ["proto"],
},
{
repo: "cosmos/ibc-go#main",
paths: ["proto"],
},
{
repo: "onsonr/sonr#main",
paths: ["proto"],
},
{
repo: "CosmWasm/wasmd#main",
paths: ["proto"],
},
{
repo: "osmosis-labs/osmosis#main",
paths: ["proto"],
},
{
repo: "evmos/ethermint#main",
paths: ["proto"],
},
{
repo: "nomic-io/nomic#develop",
paths: ["src/babylon/proto"],
},
];
const __dirname = dirname(fileURLToPath(import.meta.url));
const PROTOBUFS_DIR = join(__dirname, "..", "src", "protobufs");
const TMP_DIR = join(PROTOBUFS_DIR, ".tmp");
/** Generates a unique dirname from `repo` to use in `TMP_DIR`. */
const id = (/** @type {string} */ repo) => repo.replace(/[#/]/g, "-");
console.log("Initialising directories...");
{
rmSync(PROTOBUFS_DIR, { recursive: true, force: true });
rmSync(TMP_DIR, { recursive: true, force: true });
mkdirSync(PROTOBUFS_DIR);
mkdirSync(TMP_DIR);
}
console.log("Cloning required repos...");
{
await Promise.all(
REPOS.map(({ repo }) => degit(repo).clone(join(TMP_DIR, id(repo))))
);
}
console.log("Generating TS files from proto files...");
{
for (const { repo, paths } of REPOS) {
for (const path of paths) {
spawnSync(
"pnpm",
[
"buf",
"generate",
join(TMP_DIR, id(repo), path),
"--output",
join(
PROTOBUFS_DIR,
repo.startsWith("dymensionxyz") ? "dymension" : ""
),
],
{
cwd: process.cwd(),
stdio: "inherit",
}
);
}
console.log(`✔️ [${repo}]`);
}
}
console.log("Generating src/index.ts file and renaming exports...");
{
const LAST_SEGMENT_REGEX = /[^/]+$/;
const EXPORTED_NAME_REGEX = /^export \w+ (\w+) /gm;
let contents =
"/** This file is generated by gen-protobufs.mjs. Do not edit. */\n\n";
/**
* Builds the `src/proto/index.ts` file to re-export generated code.
* A prefix is added to the exported names to avoid name collisions.
* The prefix is the names of the directories in `proto` leading up
* to the directory of the exported code, concatenated in PascalCase.
* For example, if the exported code is in `proto/foo/bar/goo.ts`, the
* prefix will be `FooBar`.
* @param {string} dir
*/
function generateIndexExports(dir) {
const files = globSync(join(dir, "*"));
if (files.length === 0) {
return;
}
const prefixName = dir
.replace(PROTOBUFS_DIR + "/", "")
.split("/")
.map((name) =>
// convert all names to PascalCase
name.split(/[-_]/).map(capitalize).join("")
)
.join("");
for (const file of files) {
const fileName = file.match(LAST_SEGMENT_REGEX)?.[0];
if (!fileName) {
console.error("Could not find name for", file);
continue;
}
if (!fileName.endsWith(".ts")) {
continue;
}
const code = readFileSync(file, "utf8");
contents += `export {\n`;
for (const match of code.matchAll(EXPORTED_NAME_REGEX)) {
const exportedName = match[1];
contents += ` ${exportedName} as ${prefixName + exportedName},\n`;
}
const exportedFile = file
.replace(PROTOBUFS_DIR + "/", "")
.replace(".ts", ".js");
contents += `} from "./${exportedFile}";\n`;
}
for (const file of files) {
generateIndexExports(file);
}
}
generateIndexExports(PROTOBUFS_DIR);
writeFileSync(join(PROTOBUFS_DIR, "index.ts"), contents);
}
console.log("Cleaning up...");
{
rmSync(TMP_DIR, { recursive: true, force: true });
}
console.log("Proto generation completed successfully!");