ruch
Version:
Revolutionary React TypeScript CLI with hexagonal architecture & AI-powered development assistance. Create maintainable, scalable applications with domain-driven design and integrated AI tooling.
243 lines (219 loc) • 7.52 kB
text/typescript
import { Command } from 'commander';
import fs from 'fs-extra';
import type { DomainGenerationOptions } from './templates/domains';
import {
ConfigProvider,
ConsoleLogger,
DefaultPathUtils,
DomainFactory,
} from './services';
import { createDomain } from './commands/create';
import { deleteDomain } from './commands/delete';
import { listDomains } from './commands/list';
import { createHttpClient } from './commands/http-client';
import { initServiceContext, generateServiceContext, updateServiceContext } from './commands/context';
import { initMsw, generateMswHandlers, generateMswMocks, updateMsw, updateMswDomain } from './commands/msw';
import { createVisualizeCommand } from './commands/visualize';
import { generateAiGuide } from './commands/guide-ai';
import { generateDomainFiles } from './templates/domains';
// Initialize services
const config = ConfigProvider.getConfig();
const logger = new ConsoleLogger();
const pathUtils = new DefaultPathUtils(config);
const domainFactory = new DomainFactory({
logger,
pathUtils,
generateDomainFiles,
});
const program = new Command();
program
.name('ruch')
.description('Revolutionary React TypeScript CLI with hexagonal architecture & AI-powered development assistance')
.version('1.1.3');
// Create command
program
.command('create')
.description('Create a new domain with its hexagonal structure')
.argument('<domain>', 'Name of the domain to create')
.option('--with-ui', 'Include UI folder with React components', true)
.option('--without-ui', 'Exclude UI folder')
.option('--with-store', 'Include store folder with Zustand', true)
.option('--without-store', 'Exclude store folder')
.option('--with-queries', 'Include queries folder with React Query', true)
.option('--without-queries', 'Exclude queries folder')
.option('--with-api', 'Include API folder with Axios', true)
.option('--without-api', 'Exclude API folder')
.action(async (domainName: string, options) => {
try {
const domainOptions: DomainGenerationOptions = {
withUi: options.withoutUi ? false : options.withUi,
withStore: options.withoutStore ? false : options.withStore,
withQueries: options.withoutQueries ? false : options.withQueries,
withApi: options.withoutApi ? false : options.withApi,
};
await domainFactory.createDomain(domainName, domainOptions);
} catch (error) {
logger.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
process.exit(1);
}
});
// Delete command
program
.command('delete')
.description('Delete an existing domain')
.argument('<domain>', 'Name of the domain to delete')
.option('-f, --force', 'Force deletion without asking for confirmation', false)
.action(async (domainName: string, options) => {
try {
await deleteDomain(domainName, options.force, fs, logger);
} catch (error) {
logger.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
process.exit(1);
}
});
// List command
program
.command('list')
.description('List all existing domains')
.action(async () => {
try {
await listDomains(fs, logger);
} catch (error) {
logger.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
process.exit(1);
}
});
// Context command with subcommands
const contextCommand = program
.command('context')
.description('Manage React service context for dependency injection');
contextCommand
.command('init')
.description('Initialize empty service context')
.action(async () => {
try {
await initServiceContext(fs, logger);
} catch (error) {
logger.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
process.exit(1);
}
});
contextCommand
.command('generate')
.description('Generate service context with all existing domains')
.action(async () => {
try {
await generateServiceContext(fs, logger);
} catch (error) {
logger.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
process.exit(1);
}
});
contextCommand
.command('update')
.description('Update service context with missing domains')
.action(async () => {
try {
await updateServiceContext(fs, logger);
} catch (error) {
logger.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
process.exit(1);
}
});
// HTTP Client command
program
.command('http-client')
.description('Generate a default HTTP client configuration')
.action(async () => {
try {
await createHttpClient(fs, logger);
} catch (error) {
logger.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
process.exit(1);
}
});
// Guide AI command
program
.command('guide-ai')
.description('Generate AI-friendly documentation and configuration for the project architecture')
.option('--tools <tools>', 'Integrate with specific AI tools (cursor, copilot, windsurf, juni)', '')
.action(async (options: { tools?: string }) => {
try {
const tools = options.tools ? options.tools.split(',').map((t: string) => t.trim()) : [];
await generateAiGuide(logger, tools);
} catch (error) {
logger.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
process.exit(1);
}
});
// MSW command with subcommands
const mswCommand = program
.command('msw')
.description('Manage Mock Service Worker (MSW) configuration and handlers');
mswCommand
.command('init')
.description('Initialize MSW configuration in the project')
.action(async () => {
try {
await initMsw(fs, logger);
} catch (error) {
logger.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
process.exit(1);
}
});
mswCommand
.command('handlers')
.description('Generate MSW handlers for all domains')
.action(async () => {
try {
await generateMswHandlers(fs, logger);
} catch (error) {
logger.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
process.exit(1);
}
});
mswCommand
.command('mocks')
.description('Generate mock data for all domains')
.action(async () => {
try {
await generateMswMocks(fs, logger);
} catch (error) {
logger.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
process.exit(1);
}
});
mswCommand
.command('update')
.description('Update MSW configuration with missing handlers or for a specific domain')
.argument('[domain]', 'Specific domain to update (optional)')
.action(async (domain?: string) => {
try {
if (domain) {
await updateMswDomain(domain, fs, logger);
} else {
await updateMsw(fs, logger);
}
} catch (error) {
logger.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
process.exit(1);
}
});
// Add visualize command
program.addCommand(createVisualizeCommand());
// Display help if no command is provided
if (process.argv.length <= 2) {
program.help();
}
// Global error handling
process.on('uncaughtException', (error) => {
logger.error(`Unhandled error: ${error.message}`);
process.exit(1);
});
process.on('unhandledRejection', (reason) => {
logger.error(`Unhandled rejection: ${reason}`);
process.exit(1);
});
// Start the program
program.parse();