generate method

Future<int> generate({
  1. required String projectName,
  2. required String stateManagement,
  3. required String router,
  4. String preset = 'default',
  5. List<String> platforms = const [],
})

Implementation

Future<int> generate({
  required String projectName,
  required String stateManagement,
  required String router,
  String preset = 'default',
  List<String> platforms = const [],
}) async {
  _logger.info('Scaffolding project ${lightCyan.wrap(projectName)}...');

  // - Run flutter create
  final createProgress = _logger.progress('Running "flutter create"');

  final List<String> args = ['create'];
  if (platforms.isNotEmpty) {
    args.add('--platforms=${platforms.join(',')}');
  }
  args.add(projectName);

  final createResult = await _processRunner.run('flutter', args);

  if (createResult.exitCode != 0) {
    print('Error Scaffolding project.');
    print(createResult.stderr);
    return createResult.exitCode;
  }

  createProgress.complete('Flutter project created.');

  final projectPath = p.join(Directory.current.path, projectName);
  final libPath = p.join(projectPath, 'lib');

  // Build the dependency graph using DependencyResolver
  final resolver = DependencyResolver();
  final Map<String, Module> allModules = {
    'networking': NetworkingModule(),
    'routing': RoutingModule(),
    'storage': StorageModule(),
    'theme': ThemeModule(),
    'l10n': L10nModule(),
    'analytics': AnalyticsModule(),
  };

  for (final entry in allModules.entries) {
    resolver.addNode(entry.key, entry.value.dependencies);
  }

  final List<String> initialModules;
  switch (preset.toLowerCase()) {
    case 'fintech':
      initialModules = ['routing', 'networking', 'storage', 'theme'];
      break;
    case 'ecommerce':
      initialModules = ['routing', 'networking', 'storage', 'analytics', 'theme'];
      break;
    case 'default':
    default:
      initialModules = ['routing', 'theme'];
      break;
  }

  final List<String> installOrder;
  try {
    installOrder = resolver.resolve(initialModules, []);
  } on CircularDependencyException catch (e) {
    _logger.err('Dependency resolution error: $e');
    return 1;
  }

  _logger.info('Resolved default modules installation order: ${installOrder.join(' -> ')}');

  // - Create a local zuq.yaml inside the new project for future commands
  final configProgress = _logger.progress('Creating project config file');

  final projectConfigFile = File(p.join(projectPath, 'zuq.yaml'));
  final modulesBlock = installOrder.map((m) => '  - $m').join('\n');
  projectConfigFile.writeAsStringSync('''
name: $projectName
state_management: $stateManagement
router: $router
preset: $preset
modules:
$modulesBlock
''');

  configProgress.complete('zuq.yaml created inside the project.');

  // - Clean default main.dart
  final defaultMain = File(p.join(libPath, 'main.dart'));

  if (defaultMain.existsSync()) {
    defaultMain.deleteSync();
  }
  // - Generate Directory Structure
  final structProgress = _logger.progress(
    'Generating base directory structure',
  );
  await _directoryGenerator.generate(libPath);
  structProgress.complete('Base directory structure generated.');

  // - Generate Core Project Scaffold from brick
  final scaffoldProgress = _logger.progress(
    'Generating core project scaffold',
  );
  try {
    await _templateGenerator.generateProjectCore(
      projectPath: projectPath,
      projectName: projectName,
      isRiverpod: stateManagement == 'riverpod',
      isBloc: stateManagement == 'bloc',
      isProvider: stateManagement == 'provider',
      isNone: stateManagement == 'none',
      isGoRouter: router == 'go_router',
      isAutoRoute: router == 'auto_route',
    );
    scaffoldProgress.complete('Core project scaffold generated.');
  } catch (e) {
    scaffoldProgress.fail('Failed to generate core project scaffold: $e');
    return 1;
  }

  final context = ProjectContext(
    projectName: projectName,
    projectPath: projectPath,
    stateManagement: stateManagement,
    router: router,
  );

  // Add state management packages based on selection
  if (stateManagement == 'riverpod') {
    context.corePackages.add('flutter_riverpod');
  } else if (stateManagement == 'bloc') {
    context.corePackages.addAll(['flutter_bloc', 'equatable']);
  } else if (stateManagement == 'provider') {
    context.corePackages.add('provider');
  }

  // Process resolved modules in topological order
  for (final modId in installOrder) {
    final module = allModules[modId]!;
    final moduleProgress = _logger.progress(
      'Processing ${module.id} module...',
    );
    await module.install(context);
    moduleProgress.complete('${module.id} module processed.');
  }

  if (context.corePackages.isNotEmpty) {
    final coreInstallProgress = _logger.progress(
      'Installing core packages: ${context.corePackages.join(', ')}',
    );
    final coreResult = await _processRunner.run('flutter', [
      'pub',
      'add',
      ...context.corePackages,
    ], workingDirectory: projectPath);
    if (coreResult.exitCode != 0) {
      coreInstallProgress.fail('Failed to install core packages.');
      return coreResult.exitCode;
    }
    coreInstallProgress.complete('Core packages installed.');
  }

  // - Install dev packages if needed
  if (context.devPackages.isNotEmpty) {
    final devInstallProgress = _logger.progress(
      'Installing dev packages: ${context.devPackages.join(', ')}',
    );
    final devResult = await _processRunner.run('flutter', [
      'pub',
      'add',
      '--dev',
      ...context.devPackages,
    ], workingDirectory: projectPath);

    if (devResult.exitCode != 0) {
      devInstallProgress.fail('Failed to install dev packages.');
      return devResult.exitCode;
    }
    devInstallProgress.complete('Dev packages installed.');
  }

  _logger.success('\nSuccessfully created project $projectName!');
  _logger.info(
    'Navigate to project directory: ${lightCyan.wrap('cd $projectName')}',
  );
  _logger.info(
    'Run ${lightGreen.wrap('flutter run')} to start the application.',
  );

  return 0;
}