create-rainbow-app
Version:
A Module to make DAPP easily and quickly
390 lines (346 loc) • 14.1 kB
JavaScript
#!/usr/bin/env node
const { execSync } = require("child_process");
const fs = require("fs");
const path = require("path");
/**
* Create Rainbow App - A production-ready Web3 dApp template generator
* with Next.js, TypeScript, RainbowKit, Wagmi, viem & ShadCN/UI
*/
(async () => {
try {
// Dynamically import chalk
const chalk = (await import("chalk")).default;
// Display welcome banner
displayWelcomeBanner(chalk);
// Validate project name
const projectName = validateProjectName(process.argv[2], chalk);
const targetPath = path.join(process.cwd(), projectName);
// Determine package manager to use (bun or yarn)
const packageManager = determinePackageManager(chalk);
// Create Next.js app
createNextApp(projectName, packageManager, chalk);
// Change directory to the target path
process.chdir(targetPath);
// Set up Web3 configuration
setupWeb3Config(chalk);
// Install dependencies
installDependencies(packageManager, chalk);
// Configure UI components
setupShadcnUI(packageManager, chalk);
// Update starter template files
updateTemplateFiles(chalk);
// Display success message
displaySuccessMessage(projectName, packageManager, chalk);
} catch (error) {
console.error(`\n❌ Error: ${error.message}`);
process.exit(1);
}
})();
/**
* Displays a colorful welcome banner
* @param {Object} chalk - Chalk instance for colored output
*/
function displayWelcomeBanner(chalk) {
console.clear();
console.log(
chalk.cyan.bold(`
██╗ ██╗███████╗██████╗ ██████╗ ██████╗ █████╗ ██████╗ ██████╗
██║ ██║██╔════╝██╔══██╗╚════██╗ ██╔══██╗██╔══██╗██╔══██╗██╔══██╗
██║ █╗ ██║█████╗ ██████╔╝ █████╔╝ ██║ ██║███████║██████╔╝██████╔╝
██║███╗██║██╔══╝ ██╔══██╗ ╚═══██╗ ██║ ██║██╔══██║██╔═══╝ ██╔═══╝
╚███╔███╔╝███████╗██████╔╝██████╔╝ ██████╔╝██║ ██║██║ ██║
╚══╝╚══╝ ╚══════╝╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝
`),
);
console.log(
chalk.blue.bold(
" 🌈 Production-ready Web3 dApp template with Next.js, TypeScript, RainbowKit, Wagmi, viem & ShadCN/UI!",
),
);
console.log(
chalk.gray(
" ─────────────────────────────────────────────────────────────────────────────────────\n",
),
);
}
/**
* Validates the project name from command line arguments
* @param {string} name - Project name from command line
* @param {Object} chalk - Chalk instance for colored output
* @returns {string} Validated project name
*/
function validateProjectName(name, chalk) {
if (!name) {
throw new Error(chalk.red("Please provide a project name."));
}
return name;
}
/**
* Determines which package manager to use (bun or yarn)
* @param {Object} chalk - Chalk instance for colored output
* @returns {string} Package manager to use ('bun' or 'yarn')
*/
function determinePackageManager(chalk) {
try {
execSync("bun --version", { stdio: "ignore" });
return "bun";
} catch (bunError) {
try {
execSync("yarn --version", { stdio: "ignore" });
return "yarn";
} catch (yarnError) {
console.error(
chalk.red(
"Neither Bun nor Yarn is installed. One of these is required to run this script.",
),
);
console.log(
chalk.yellow("Install Bun from:"),
chalk.blue("https://bun.sh/"),
);
console.log(
chalk.yellow("Install Yarn from:"),
chalk.blue("https://yarnpkg.com/getting-started/install"),
);
process.exit(1);
}
}
}
/**
* Creates a new Next.js application with the specified configuration
* @param {string} projectName - Name of the project
* @param {string} packageManager - Package manager to use
* @param {Object} chalk - Chalk instance for colored output
*/
function createNextApp(projectName, packageManager, chalk) {
console.log(
chalk.blue("Creating Next.js app with TypeScript and Tailwind CSS..."),
);
const createNextCommand =
packageManager === "bun"
? `bunx create-next-app@latest ${projectName} --typescript --tailwind --eslint --src-dir --app=false --import-alias="@/* --disable-git"`
: `npx create-next-app@latest ${projectName} --typescript --tailwind --eslint --src-dir --app=false --import-alias="@/* --disable-git"`;
try {
execSync(createNextCommand, { stdio: "inherit" });
} catch (error) {
throw new Error(`Failed to create Next.js app: ${error.message}`);
}
}
/**
* Sets up Web3 configuration files
* @param {Object} chalk - Chalk instance for colored output
*/
function setupWeb3Config(chalk) {
try {
// Create wagmi.ts file in src folder
console.log(chalk.blue("Creating wagmi.ts configuration..."));
const wagmiContent = `import { getDefaultConfig } from "@rainbow-me/rainbowkit";
import { mainnet, polygon, optimism, arbitrum, base } from "viem/chains";
export const config = getDefaultConfig({
appName: "My RainbowKit App",
projectId: "YOUR_PROJECT_ID",
chains: [mainnet, polygon, optimism, arbitrum, base],
ssr: true, // If your dApp uses server side rendering (SSR)
});`;
fs.writeFileSync(path.join("src", "wagmi.ts"), wagmiContent);
// Create ABI folder and demo.json
console.log(chalk.blue("Creating ABI demo.json..."));
const abiDir = path.join("src", "ABI");
fs.mkdirSync(abiDir, { recursive: true });
const demoAbi = [
{
inputs: [
{
internalType: "string",
name: "_greeting",
type: "string",
},
],
name: "setGreeting",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [],
name: "greeting",
outputs: [
{
internalType: "string",
name: "",
type: "string",
},
],
stateMutability: "view",
type: "function",
},
];
fs.writeFileSync(
path.join(abiDir, "demo.json"),
JSON.stringify(demoAbi, null, 2),
);
} catch (error) {
throw new Error(`Failed to setup Web3 configuration: ${error.message}`);
}
}
/**
* Installs required dependencies
* @param {string} packageManager - Package manager to use
* @param {Object} chalk - Chalk instance for colored output
*/
function installDependencies(packageManager, chalk) {
try {
console.log(chalk.blue("Installing packages..."));
const installCommand =
packageManager === "bun"
? "bun add @rainbow-me/rainbowkit wagmi viem@2.x @tanstack/react-query"
: "yarn add @rainbow-me/rainbowkit wagmi viem@2.x @tanstack/react-query";
execSync(installCommand, { stdio: "inherit" });
} catch (error) {
throw new Error(`Failed to install dependencies: ${error.message}`);
}
}
/**
* Sets up shadcn/ui components
* @param {string} packageManager - Package manager to use
* @param {Object} chalk - Chalk instance for colored output
*/
function setupShadcnUI(packageManager, chalk) {
try {
console.log(chalk.blue("Initializing shadcn/ui..."));
const shadcnCommand =
packageManager === "bun"
? "bunx shadcn@latest init"
: "npx shadcn@latest init";
execSync(shadcnCommand, { stdio: "inherit" });
const shadcnCommandInstall =
packageManager === "bun"
? "bunx shadcn@latest add button"
: "npx shadcn@latest add button";
execSync(shadcnCommandInstall, { stdio: "inherit" });
} catch (error) {
throw new Error(`Failed to setup shadcn/ui: ${error.message}`);
}
}
/**
* Updates template files with custom content
* @param {Object} chalk - Chalk instance for colored output
*/
function updateTemplateFiles(chalk) {
try {
// Update _app.tsx with providers
console.log(chalk.blue("Setting up providers in _app.tsx..."));
const appContent = `import '@/styles/globals.css';
import '@rainbow-me/rainbowkit/styles.css';
import type { AppProps } from 'next/app';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { WagmiProvider } from 'wagmi';
import { RainbowKitProvider } from '@rainbow-me/rainbowkit';
import { config } from '@/wagmi';
const queryClient = new QueryClient();
export default function App({ Component, pageProps }: AppProps) {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<RainbowKitProvider>
<Component {...pageProps} />
</RainbowKitProvider>
</QueryClientProvider>
</WagmiProvider>
);
}`;
fs.writeFileSync(path.join("src", "pages", "_app.tsx"), appContent);
// Update index.tsx with custom content
console.log(chalk.blue("Updating index.tsx with custom content..."));
const indexContent = `import { ConnectButton } from "@rainbow-me/rainbowkit";
import { Geist, Geist_Mono } from "next/font/google";
import Link from "next/link";
import { Button } from "@/components/ui/button";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export default function Home() {
return (
<div
className={\`\${geistSans.className} \${geistMono.className} grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]\`}
>
<main className="flex flex-col gap-10 row-start-2 items-center sm:items-start">
<div className="flex flex-col items-center gap-4">
<h1 className="text-4xl font-bold text-center">
🎒 Web3 Dapp Template
</h1>
<p className="text-lg text-center text-gray-600 dark:text-gray-400">
Web3 dApp template with Next.js, TypeScript, Tailwind CSS & shadcn/ui
</p>
</div>
<div className="flex flex-col items-center justify-center gap-6 w-full">
<ConnectButton />
<div className="w-full p-6 border border-gray-200 dark:border-gray-800 rounded-lg">
<h2 className="text-xl font-semibold mb-4 text-center">
🚀 Get Started
</h2>
<ol className="list-inside list-decimal text-sm space-y-2 font-[family-name:var(--font-geist-mono)]">
<li>
Edit{" "}
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
src/pages/index.tsx
</code>
</li>
<li>
Update your project ID in{" "}
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
src/wagmi.ts
</code>
</li>
<li>Add shadcn/ui components as needed</li>
<li>Build your Web3 application!</li>
</ol>
</div>
</div>
<div className="flex gap-4 items-center flex-col sm:flex-row">
<Link
href="https://rainbowkit.com/docs"
target="_blank"
rel="noopener noreferrer"
>
<Button variant="secondary">📚 RainbowKit Docs</Button>
</Link>
<Link
href="https://ui.shadcn.com"
target="_blank"
rel="noopener noreferrer"
>
<Button variant="outline">🎨 shadcn/ui</Button>
</Link>
</div>
</main>
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center text-sm text-gray-500">
<span>Built with ❤️ using Web3 Dapp Template</span>
</footer>
</div>
);
}`;
fs.writeFileSync(path.join("src", "pages", "index.tsx"), indexContent);
} catch (error) {
throw new Error(`Failed to update template files: ${error.message}`);
}
}
/**
* Displays success message with next steps
* @param {string} projectName - Name of the project
* @param {string} packageManager - Package manager used
* @param {Object} chalk - Chalk instance for colored output
*/
function displaySuccessMessage(projectName, packageManager, chalk) {
console.log(chalk.green(`\n✅ Project ${projectName} is ready!`));
console.log(chalk.yellow(`To start working on your project, run:`));
console.log(chalk.cyan(`\t cd ${projectName}`));
console.log(
chalk.cyan(`\t ${packageManager === "bun" ? "bun dev" : "yarn dev"}`),
);
}