#!/usr/bin/env node import { input } from "@inquirer/prompts"; import select from "@inquirer/select"; import chalk from "chalk"; import childProcess from "child_process"; import { Command } from "commander"; import { execa, execaCommand } from "execa"; import fs from "fs"; import open from "open"; import ora from "ora"; import path from "path"; const program = new Command(); const log = console.log; const repoUrl = "https://github.com/fal-ai/fal-nextjs-template.git"; const green = chalk.green; const purple = chalk.hex("#6e40c9"); async function main() { const spinner = ora({ text: "Creating codebase", }); try { const kebabRegez = /^([a-z]+)(-[a-z0-9]+)*$/; program .name("The fal.ai App Generator") .description("Generate full stack AI apps integrated with fal.ai"); program.parse(process.argv); const args = program.args; let appName = args[0]; if (!appName || !kebabRegez.test(args[0])) { appName = await input({ message: "Enter your app name", default: "model-playground", validate: (d) => { if (!kebabRegez.test(d)) { return "please enter your app name in the format of my-app-name"; } return true; }, }); } const hasFalEnv = await select({ message: "Do you have a fal.ai API key?", choices: [ { name: "Yes", value: true, }, { name: "No", value: false, }, ], }); if (!hasFalEnv) { await open("https://www.fal.ai/dashboard"); } const fal_api_key = await input({ message: "Fal AI API Key" }); const envs = ` # environment, either PRODUCTION or DEVELOPMENT ENVIRONMENT="PRODUCTION" # FAL AI API Key FAL_KEY="${fal_api_key}" `; log(`\nInitializing project. \n`); spinner.start(); await execa("git", ["clone", repoUrl, appName]); let packageJson = fs.readFileSync(`${appName}/package.json`, "utf8"); const packageObj = JSON.parse(packageJson); packageObj.name = appName; packageJson = JSON.stringify(packageObj, null, 2); fs.writeFileSync(`${appName}/package.json`, packageJson); fs.writeFileSync(`${appName}/.env.local`, envs); process.chdir(path.join(process.cwd(), appName)); await execa("rm", ["-rf", ".git"]); await execa("git", ["init"]); spinner.text = ""; let startCommand = ""; if (isBunInstalled()) { spinner.text = "Installing dependencies"; await execaCommand("bun install").pipeStdout(process.stdout); spinner.text = ""; startCommand = "bun dev"; console.log("\n"); } else if (isYarnInstalled()) { await execaCommand("yarn").pipeStdout(process.stdout); startCommand = "yarn dev"; } else { spinner.text = "Installing dependencies"; await execa("npm", ["install", "--verbose"]).pipeStdout(process.stdout); spinner.text = ""; startCommand = "npm run dev"; } spinner.stop(); await execa("git", ["add", "."]); await execa("git", ["commit", "-m", "Initial commit"]); process.chdir("../"); log( `${green.bold("Success!")} Created ${purple.bold( appName, )} at ${process.cwd()} \n`, ); log( `To get started, change into the new directory and run ${chalk.cyan( startCommand, )}\n`, ); } catch (err) { log("\n"); if (err.exitCode == 128) { log("Error: directory already exists."); } spinner.stop(); } } main(); function isYarnInstalled() { try { childProcess.execSync("yarn --version"); return true; } catch { return false; } } function isBunInstalled() { try { childProcess.execSync("bun --version"); return true; } catch (err) { return false; } }