2024-02-21 20:12:09 -08:00

155 lines
3.7 KiB
JavaScript

#!/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;
}
}