@sprouted/create-vibes
Version:
Create a new Vibes monorepo
247 lines • 9.49 kB
JavaScript
import * as path from 'path';
import * as fs from 'fs/promises';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import inquirer from 'inquirer';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
export class CreateAppCommand {
constructor(engine) {
this.engine = engine;
}
async execute(options) {
const finalOptions = await this.promptMissingOptions(options);
const templatePath = path.join(__dirname, '../../../templates/typescript/apps', finalOptions.type);
const targetPath = path.join(process.cwd(), 'apps', finalOptions.name);
// Ensure the apps directory exists
await fs.mkdir(path.join(process.cwd(), 'apps'), { recursive: true });
await this.engine.generateFromTemplate(templatePath, {
appName: finalOptions.name,
displayName: finalOptions.displayName,
appId: finalOptions.appId,
framework: finalOptions.framework,
includeRouter: finalOptions.includeRouter,
includeTailwind: finalOptions.includeTailwind,
includeNavigator: finalOptions.includeNavigator,
includeAuth: finalOptions.includeAuth,
includeApiClient: finalOptions.includeApiClient,
port: finalOptions.port,
includeDatabase: finalOptions.includeDatabase,
includeValidation: finalOptions.includeValidation,
includeSwagger: finalOptions.includeSwagger,
includeGraphQL: finalOptions.includeGraphQL,
includeAutoUpdater: finalOptions.includeAutoUpdater,
includeSystemTray: finalOptions.includeSystemTray,
includeMenuBar: finalOptions.includeMenuBar,
includeIpc: finalOptions.includeIpc,
includeDarkMode: finalOptions.includeDarkMode
}, {
outputPath: targetPath,
dryRun: false
});
console.log(`✨ Created ${finalOptions.type} app: ${finalOptions.name}`);
console.log(`📁 Location: ${targetPath}`);
console.log('\nNext steps:');
console.log(` cd apps/${finalOptions.name}`);
console.log(' pnpm install');
console.log(finalOptions.type === 'mobile' ? ' pnpm start' : ' pnpm dev');
}
async promptMissingOptions(options) {
const questions = [];
if (!options.name) {
questions.push({
type: 'input',
name: 'name',
message: 'App name:',
validate: (input) => {
if (!input.trim())
return 'App name is required';
if (!/^[a-z0-9-]+$/.test(input)) {
return 'App name must be lowercase alphanumeric with dashes';
}
return true;
}
});
}
if (options.type === 'web') {
if (options.framework === undefined) {
questions.push({
type: 'list',
name: 'framework',
message: 'Choose a framework:',
choices: [
{ name: 'Vite + React', value: 'vite-react' },
{ name: 'Next.js', value: 'nextjs' }
],
default: 'vite-react'
});
}
if (options.includeRouter === undefined) {
questions.push({
type: 'confirm',
name: 'includeRouter',
message: 'Include React Router?',
default: true
});
}
if (options.includeTailwind === undefined) {
questions.push({
type: 'confirm',
name: 'includeTailwind',
message: 'Include Tailwind CSS?',
default: false
});
}
}
if (options.type === 'mobile') {
if (options.displayName === undefined) {
questions.push({
type: 'input',
name: 'displayName',
message: 'Display name (shown to users):',
default: options.name || 'Vibes App'
});
}
if (options.includeNavigator === undefined) {
questions.push({
type: 'confirm',
name: 'includeNavigator',
message: 'Include React Navigation?',
default: true
});
}
if (options.includeAuth === undefined) {
questions.push({
type: 'confirm',
name: 'includeAuth',
message: 'Include authentication setup?',
default: false
});
}
if (options.includeApiClient === undefined) {
questions.push({
type: 'confirm',
name: 'includeApiClient',
message: 'Include API client?',
default: true
});
}
}
if (options.type === 'api') {
if (options.port === undefined) {
questions.push({
type: 'number',
name: 'port',
message: 'Port number:',
default: 3001
});
}
if (options.includeDatabase === undefined) {
questions.push({
type: 'confirm',
name: 'includeDatabase',
message: 'Include database (PostgreSQL with Prisma)?',
default: true
});
}
if (options.includeAuth === undefined) {
questions.push({
type: 'confirm',
name: 'includeAuth',
message: 'Include authentication (JWT)?',
default: true
});
}
if (options.includeValidation === undefined) {
questions.push({
type: 'confirm',
name: 'includeValidation',
message: 'Include request validation (Zod)?',
default: true
});
}
if (options.includeSwagger === undefined) {
questions.push({
type: 'confirm',
name: 'includeSwagger',
message: 'Include Swagger documentation?',
default: false
});
}
if (options.includeGraphQL === undefined) {
questions.push({
type: 'confirm',
name: 'includeGraphQL',
message: 'Include GraphQL support?',
default: false
});
}
}
if (options.type === 'desktop') {
if (options.displayName === undefined) {
questions.push({
type: 'input',
name: 'displayName',
message: 'Display name (shown to users):',
default: options.name || 'Vibes Desktop'
});
}
if (options.appId === undefined) {
questions.push({
type: 'input',
name: 'appId',
message: 'Application ID:',
default: `com.vibes.${options.name || 'desktop'}`
});
}
if (options.includeAutoUpdater === undefined) {
questions.push({
type: 'confirm',
name: 'includeAutoUpdater',
message: 'Include auto-updater?',
default: true
});
}
if (options.includeSystemTray === undefined) {
questions.push({
type: 'confirm',
name: 'includeSystemTray',
message: 'Include system tray support?',
default: false
});
}
if (options.includeMenuBar === undefined) {
questions.push({
type: 'confirm',
name: 'includeMenuBar',
message: 'Include custom menu bar?',
default: true
});
}
if (options.includeIpc === undefined) {
questions.push({
type: 'confirm',
name: 'includeIpc',
message: 'Include IPC communication examples?',
default: true
});
}
if (options.includeDarkMode === undefined) {
questions.push({
type: 'confirm',
name: 'includeDarkMode',
message: 'Include dark mode support?',
default: true
});
}
}
const answers = questions.length > 0
? await inquirer.prompt(questions)
: {};
return {
...options,
...answers
};
}
}
//# sourceMappingURL=create-app.js.map