run method
Implementation
Future<void> run(List<String> arguments) async {
// Handle help and version flags
if (arguments.isNotEmpty) {
final firstArg = arguments[0].toLowerCase();
if (firstArg == '--help' || firstArg == '-h' || firstArg == 'help') {
printUsage();
return;
}
if (firstArg == '--version' || firstArg == '-v' || firstArg == 'version') {
printVersion();
return;
}
}
if (arguments.isEmpty) {
printUsage();
return;
}
final firstArg = arguments[0];
final firstArgLower = firstArg.toLowerCase();
// Reserved commands that cannot be feature names
final reservedCommands = [
'list',
'tree',
'init',
'remove',
'delete',
'del',
'rm',
'rename',
'mv',
'ren',
'clean',
'config',
'setup',
'health',
];
// Check if it's a reserved command first (case-insensitive)
if (reservedCommands.contains(firstArgLower)) {
if (firstArgLower == 'list') {
await _listFeatures();
return;
}
if (firstArgLower == 'tree') {
await TreeManager.showTree();
return;
}
if (firstArgLower == 'health') {
await HealthManager.checkHealth();
return;
}
if (firstArgLower == 'init') {
// Check if app name is provided as second argument
String? appName;
if (arguments.length > 1) {
appName = arguments[1];
// Validate app name (lowercase, underscores only, no spaces or special characters)
if (!RegExp(r'^[a-z][a-z0-9_]*$').hasMatch(appName)) {
Logger.error('Invalid app name: $appName');
Logger.info('App name must be lowercase, with underscores only, no spaces or special characters.');
return;
}
}
await InitManager.initialize(appName: appName);
return;
}
// For other reserved commands, continue to their specific handlers below
// They will be handled with case-insensitive checks
}
// Continue with original case for feature name
final featureName = firstArg;
// Handle remove command (case-insensitive)
if (firstArgLower == 'remove' || firstArgLower == 'delete' || firstArgLower == 'del' || firstArgLower == 'rm') {
if (arguments.length < 2) {
Logger.error('Please specify a feature name to delete.');
Logger.info('Usage: flarch delete <FeatureName>');
return;
}
final featureToRemove = arguments[1];
await _removeFeature(featureToRemove);
return;
}
// Handle rename command (case-insensitive)
if (firstArgLower == 'rename' || firstArgLower == 'mv' || firstArgLower == 'ren') {
if (arguments.length < 2) {
Logger.error('Please specify a feature name to rename.');
Logger.info('Usage: flarch rename <OldFeatureName> [NewFeatureName]');
Logger.info(' Examples:');
Logger.info(' flarch rename old_feature new_feature');
Logger.info(' flarch rename old_feature (interactive mode)');
return;
}
final oldFeatureName = arguments[1];
final newFeatureName = arguments.length > 2 ? arguments[2] : null;
await FeatureRenamer.renameFeature(oldFeatureName, newFeatureName: newFeatureName);
return;
}
// Handle clean command (case-insensitive)
if (firstArgLower == 'clean') {
if (arguments.length < 2) {
Logger.error('Please specify what to clean.');
Logger.info('Usage: flarch clean pubspec');
Logger.info('Available options: pubspec (cleans pubspec.yaml comments)');
return;
}
final cleanTarget = arguments[1].toLowerCase();
if (cleanTarget == 'pubspec' || cleanTarget == 'pubspec.yaml') {
await PubspecCleaner.cleanPubspec();
} else {
Logger.error('Unknown clean target: $cleanTarget');
Logger.info('Available options: pubspec');
}
return;
}
// Handle config command (case-insensitive)
if (firstArgLower == 'config') {
if (arguments.length < 2) {
Logger.error('Please specify what to configure.');
Logger.info(
'Usage: flarch config assets | flarch config main | flarch config theme | flarch config router | flarch config storage | flarch config package [id] | flarch config name [name]');
Logger.info('Available options:');
Logger.info(' assets - Setup assets folder structure');
Logger.info(' main - Clean main.dart and create app.dart');
Logger.info(' theme - Setup theme configuration with light/dark themes');
Logger.info(' router - Setup GoRouter configuration');
Logger.info(' storage - Setup local storage (Hive, SharedPreferences, ObjectBox, Isar, Drift)');
Logger.info(' package [id] - Replace application package ID');
Logger.info(' Examples:');
Logger.info(' flarch config package com.newpackage.app');
Logger.info(' flarch config package --interactive');
Logger.info(' flarch config package com.newpackage.app --backup');
Logger.info(' name [name] - Change app display name');
Logger.info(' Examples:');
Logger.info(' flarch config name "My New App"');
Logger.info(' flarch config name --interactive');
Logger.info(' flarch config name "My New App" --backup');
return;
}
final configTarget = arguments[1].toLowerCase();
if (configTarget == 'assets') {
await AssetConfigManager.setupAssets();
} else if (configTarget == 'main') {
await MainConfigManager.configureMain();
} else if (configTarget == 'theme') {
await ThemeConfigManager.configureTheme();
} else if (configTarget == 'router') {
await RouterConfigManager.configureRouter();
} else if (configTarget == 'storage' || configTarget == 'local storage') {
await StorageConfigManager.configureStorage();
} else if (configTarget == 'package') {
// Parse package command options
bool interactive = arguments.contains('--interactive') || arguments.contains('-i');
bool createBackup = arguments.contains('--backup') || arguments.contains('-b');
bool dryRun = arguments.contains('--dry-run') || arguments.contains('--preview');
// Get package ID from arguments (skip flags)
String? newPackageId;
for (int i = 2; i < arguments.length; i++) {
final arg = arguments[i];
if (!arg.startsWith('--') && arg != '-i' && arg != '-b') {
newPackageId = arg;
break;
}
}
await PackageIdManager.replacePackageId(
newPackageId: newPackageId,
interactive: interactive || newPackageId == null,
createBackup: createBackup,
dryRun: dryRun,
);
} else if (configTarget == 'name') {
// Parse app name command options
bool interactive = arguments.contains('--interactive') || arguments.contains('-i');
bool createBackup = arguments.contains('--backup') || arguments.contains('-b');
bool dryRun = arguments.contains('--dry-run') || arguments.contains('--preview');
// Get app name from arguments (skip flags)
// App name might have spaces, so we need to handle quoted strings
String? newAppName;
for (int i = 2; i < arguments.length; i++) {
final arg = arguments[i];
if (!arg.startsWith('--') && arg != '-i' && arg != '-b') {
// If it starts with a quote, collect until we find the closing quote
if (arg.startsWith('"') || arg.startsWith("'")) {
final quote = arg[0];
String collected = arg.substring(1); // Remove opening quote
// If it ends with the same quote, we're done
if (collected.endsWith(quote)) {
newAppName = collected.substring(0, collected.length - 1);
break;
}
// Otherwise, collect more arguments until we find the closing quote
for (int j = i + 1; j < arguments.length; j++) {
collected += ' ${arguments[j]}';
if (arguments[j].endsWith(quote)) {
newAppName = collected.substring(0, collected.length - 1);
break;
}
}
if (newAppName != null) break;
} else {
newAppName = arg;
break;
}
}
}
await AppNameManager.replaceAppName(
newAppName: newAppName,
interactive: interactive || newAppName == null,
createBackup: createBackup,
dryRun: dryRun,
);
} else {
Logger.error('Unknown config target: $configTarget');
Logger.info('Available options: assets, main, theme, router, storage, package, name');
}
return;
}
// Handle setup command (alias for config, case-insensitive)
if (firstArgLower == 'setup') {
if (arguments.length < 2) {
Logger.error('Please specify what to setup.');
Logger.info('Usage: flarch setup assets | flarch setup package [new.package.id]');
Logger.info('Available options:');
Logger.info(' assets - Setup assets folder structure');
Logger.info(' package [id] - Replace application package ID');
return;
}
final setupTarget = arguments[1].toLowerCase();
if (setupTarget == 'assets') {
await AssetConfigManager.setupAssets();
} else if (setupTarget == 'package') {
// Parse package command options
bool interactive = arguments.contains('--interactive') || arguments.contains('-i');
bool createBackup = arguments.contains('--backup') || arguments.contains('-b');
bool dryRun = arguments.contains('--dry-run') || arguments.contains('--preview');
// Get package ID from arguments (skip flags)
String? newPackageId;
for (int i = 2; i < arguments.length; i++) {
final arg = arguments[i];
if (!arg.startsWith('--') && arg != '-i' && arg != '-b') {
newPackageId = arg;
break;
}
}
await PackageIdManager.replacePackageId(
newPackageId: newPackageId,
interactive: interactive || newPackageId == null,
createBackup: createBackup,
dryRun: dryRun,
);
} else if (setupTarget == 'name') {
// Parse app name command options
bool interactive = arguments.contains('--interactive') || arguments.contains('-i');
bool createBackup = arguments.contains('--backup') || arguments.contains('-b');
bool dryRun = arguments.contains('--dry-run') || arguments.contains('--preview');
// Get app name from arguments (skip flags)
String? newAppName;
for (int i = 2; i < arguments.length; i++) {
final arg = arguments[i];
if (!arg.startsWith('--') && arg != '-i' && arg != '-b') {
// Handle quoted strings for app names with spaces
if (arg.startsWith('"') || arg.startsWith("'")) {
final quote = arg[0];
String collected = arg.substring(1);
if (collected.endsWith(quote)) {
newAppName = collected.substring(0, collected.length - 1);
break;
}
for (int j = i + 1; j < arguments.length; j++) {
collected += ' ${arguments[j]}';
if (arguments[j].endsWith(quote)) {
newAppName = collected.substring(0, collected.length - 1);
break;
}
}
if (newAppName != null) break;
} else {
newAppName = arg;
break;
}
}
}
await AppNameManager.replaceAppName(
newAppName: newAppName,
interactive: interactive || newAppName == null,
createBackup: createBackup,
dryRun: dryRun,
);
} else {
Logger.error('Unknown setup target: $setupTarget');
Logger.info('Available options: assets, package, name');
}
return;
}
if (arguments.length > 1) {
if (arguments.contains('-sm')) {
await _handleOption(arguments, featureName);
return;
}
if (_featureExists(featureName)) {
await _handleOption(arguments, featureName);
return;
}
}
if (_featureExists(featureName) && arguments.length == 1) {
Logger.error('Feature "$featureName" already exists.');
return;
}
if (arguments.length > 1 && ['-mvc', '-mvvm', '-clean'].contains(arguments[1])) {
final architecture = arguments[1].substring(1);
final stateManagement = await _promptForStateManagement();
final customClassName = stateManagement != null ? _promptForCustomClassName() : null;
await GetItManager.manageGetIt(featureName, stateManagement, architecture);
_createFeatureStructure(featureName, stateManagement, customClassName, architecture);
Logger.success('Feature "$featureName" created successfully with $architecture architecture');
return;
}
await _createFeatureWithPrompt(featureName);
}