create-accessnode
Version:
CLI for setting up AccessNode projects. Clones AccessNode repo, prompts for chains & contracts, creates node.config.ts.
134 lines (129 loc) • 4.25 kB
JavaScript
// bin/cli.ts
import fs from "fs";
import path from "path";
import prompts from "prompts";
import pico from "picocolors";
import prettier from "prettier";
import { execa } from "execa";
// src/chain.ts
var CHAINS = [
{
id: "base",
name: "Base",
config: {
chainId: 8453,
transport: "http(process.env.ACCESSNODE_RPC_URL_8453)",
maxRequestsPerSecond: 15
}
},
{
id: "base-sepolia",
name: "Base Sepolia",
config: {
chainId: 84532,
transport: "http(process.env.ACCESSNODE_RPC_URL_84532)",
maxRequestsPerSecond: 15
}
}
];
// bin/cli.ts
var REPO_URL = "https://github.com/accesstimeio/accessnode";
async function main() {
const args = process.argv.slice(2);
const cliProjectName = args.find((arg) => arg.startsWith("--project-name="));
let projectName = cliProjectName?.split("=")[1];
if (!projectName) {
const res = await prompts({
type: "text",
name: "name",
message: "Enter your project name:",
initial: "accessnode-app"
});
projectName = res.name.trim();
}
const projectPath = path.resolve(process.cwd(), projectName);
console.log(pico.blue("\u23F3 Cloning repository..."));
await execa("git", ["clone", REPO_URL, projectName]);
const { selectedChains } = await prompts({
type: "multiselect",
name: "selectedChains",
message: "Select chains to support:",
choices: CHAINS.map((c) => ({ title: c.name, value: c.id })),
min: 1
});
const contractNetworkConfig = {};
for (const chainId of selectedChains) {
const { addresses } = await prompts({
type: "text",
name: "addresses",
message: `Enter contract addresses for ${chainId} (comma-separated):`
});
const addressList = addresses.split(",").map((a) => a.trim().toLowerCase()).filter(Boolean);
const { startBlock } = await prompts({
type: "number",
name: "startBlock",
message: `Enter start block for ${chainId}:`
});
contractNetworkConfig[chainId] = {
address: addressList,
startBlock
};
}
const selectedNetworkConfigs = Object.fromEntries(
selectedChains.map((id) => {
const chain = CHAINS.find((c) => c.id === id);
return [id, chain.config];
})
);
const rawConfig = `
import { createNodeConfig } from "./src/types";
import { http } from "viem";
export default createNodeConfig({
networks: ${JSON.stringify(selectedNetworkConfigs, null, 2).replaceAll(
/"http\((.*?)\)"/g,
(_, env) => `http(${env})`
)},
contracts: {
AccessTime: {
network: ${JSON.stringify(contractNetworkConfig, null, 2)}
}
}
});
`;
const formattedConfig = await prettier.format(rawConfig, {
parser: "typescript"
});
fs.writeFileSync(path.join(projectPath, "node.config.ts"), formattedConfig);
console.log();
console.log(pico.green("\u2705 Project created at:"), pico.bold(projectPath));
console.log(pico.green("\u2705 Configuration written to node.config.ts"));
console.log();
console.log(pico.blue("\u{1F527} Resetting git history..."));
fs.rmSync(path.join(projectPath, ".git"), { recursive: true, force: true });
await execa("git", ["init"], { cwd: projectPath });
await execa("git", ["add", "."], { cwd: projectPath });
await execa("git", ["commit", "-m", "chore: update config"], { cwd: projectPath });
console.log(pico.green("\u2705 Git repository initialized."));
console.log(pico.blue("\u{1F50D} Checking pnpm..."));
let pnpmInstalled = true;
try {
await execa("pnpm", ["--version"]);
} catch {
pnpmInstalled = false;
}
if (!pnpmInstalled) {
console.log(pico.yellow("\u{1F4E6} pnpm not found. Installing..."));
await execa("npm", ["install", "-g", "pnpm"]);
console.log(pico.green("\u2705 pnpm installed globally."));
}
console.log(pico.blue("\u{1F4E6} Installing dependencies with pnpm..."));
await execa("pnpm", ["install"], { cwd: projectPath });
console.log(pico.green("\u2705 Dependencies installed."));
console.log();
console.log(pico.green("\u{1F680} Setup complete! Happy hacking!"));
}
main().catch((err) => {
console.error(pico.red("\u274C Error:"), err.message);
process.exit(1);
});