run method

  1. @override
Future<int> run()
override

Runs this command.

The return value is wrapped in a Future if necessary and returned by CommandRunner.runCommand.

Implementation

@override
Future<int> run() async {
  if (argResults == null || argResults!.rest.isEmpty) {
    _logger.err('Error: Please specify the module name to add.');
    _logger.info('Usage: zuq add module <module_name>');
    _logger.info('Available modules: ${_modules.keys.join(', ')}');
    return 1;
  }

  final moduleName = argResults!.rest.first.toLowerCase();
  final module = _modules[moduleName];

  if (module == null) {
    _logger.err('Error: Module "$moduleName" is not recognized.');
    _logger.info('Available modules: ${_modules.keys.join(', ')}');
    return 1;
  }

  final projectConfig = _configParser.readConfig(fallbackProjectName: '');
  if (projectConfig == null) {
    _logger.err('Error: No zuq.yaml configuration found in this directory.');
    _logger.err('Make sure you are at the root of a zuq project.');
    return 1;
  }

  final projectPath = Directory.current.path;

  // Check if the requested module is already installed
  if (projectConfig.modules.contains(moduleName)) {
    _logger.info('Module "$moduleName" is already installed in this project. Skipping.');
    return 0;
  }

  // Build the dependency graph
  final resolver = DependencyResolver();
  for (final entry in _modules.entries) {
    resolver.addNode(entry.key, entry.value.dependencies);
  }

  // Resolve the installation list
  final List<String> installOrder;
  try {
    installOrder = resolver.resolve([moduleName], projectConfig.modules);
  } on CircularDependencyException catch (e) {
    _logger.err('Dependency resolution error: $e');
    return 1;
  }

  if (installOrder.isEmpty) {
    _logger.info('All dependencies for module "$moduleName" are already met.');
    return 0;
  }

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

  for (final modId in installOrder) {
    final mod = _modules[modId]!;
    final progress = _logger.progress('Installing $modId module...');

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

    try {
      await mod.install(context);
      progress.complete('$modId module files generated.');
    } catch (e) {
      progress.fail('Failed to generate files for $modId: $e');
      return 1;
    }

    // Install dependencies if registered by the module
    if (context.corePackages.isNotEmpty) {
      final coreInstallProgress = _logger.progress(
        'Installing packages for $modId: ${context.corePackages.join(', ')}',
      );
      final coreResult = await _processRunner.run('flutter', [
        'pub',
        'add',
        ...context.corePackages,
      ], workingDirectory: projectPath);

      if (coreResult.exitCode != 0) {
        coreInstallProgress.fail('Failed to install module packages.');
        return coreResult.exitCode;
      }
      coreInstallProgress.complete('Packages installed.');
    }

    if (context.devPackages.isNotEmpty) {
      final devInstallProgress = _logger.progress(
        'Installing dev packages for $modId: ${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 module dev packages.');
        return devResult.exitCode;
      }
      devInstallProgress.complete('Dev packages installed.');
    }

    // Record the installed module in zuq.yaml
    _updateYamlConfig(projectPath, modId);
  }

  _logger.success('Successfully added module $moduleName and its dependencies!');
  return 0;
}