run method
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!.rest.isEmpty) {
stderr.writeln('\x1B[31mError:\x1B[0m Please specify a component name.');
stderr.writeln('');
stderr.writeln('Usage: flai add <component>');
stderr.writeln(
'Run \x1B[36mflai list\x1B[0m to see available components.',
);
return 1;
}
final componentName = argResults!.rest.first;
final dryRun = argResults!['dry-run'] as bool;
final cwd = Directory.current.path;
// 1. Check the component exists in the registry.
final brick = BrickRegistry.lookup(componentName);
if (brick == null) {
stderr.writeln(
'\x1B[31mError:\x1B[0m Unknown component "$componentName".',
);
stderr.writeln(
'Run \x1B[36mflai list\x1B[0m to see available components.',
);
return 1;
}
// 2. Check that the project is initialised.
final configManager = FlaiConfigManager(projectRoot: cwd);
if (!configManager.exists) {
stderr.writeln(
'\x1B[31mError:\x1B[0m No flai.yaml found. '
'Run \x1B[36mflai init\x1B[0m first.',
);
return 1;
}
final config = configManager.read();
final alreadyInstalled = config.installed.toSet();
// 3. Resolve the full dependency graph.
const resolver = DependencyResolver();
final installOrder = resolver.resolve(
componentName,
alreadyInstalled: alreadyInstalled,
);
if (installOrder.isEmpty) {
stdout.writeln(
'\x1B[32m\u2713\x1B[0m $componentName is already installed.',
);
return 0;
}
// 4. Collect pub dependencies.
final pubDeps = resolver.collectPubDependencies(installOrder);
// 5. Display the installation plan.
stdout.writeln('');
stdout.writeln('\x1B[1mInstallation plan:\x1B[0m');
for (final name in installOrder) {
final info = BrickRegistry.lookup(name)!;
stdout.writeln(' \x1B[36m+\x1B[0m $name — ${info.description}');
}
if (pubDeps.isNotEmpty) {
stdout.writeln('');
stdout.writeln('\x1B[1mPub dependencies to add:\x1B[0m');
for (final dep in pubDeps) {
stdout.writeln(' \x1B[36m+\x1B[0m $dep');
}
}
stdout.writeln('');
if (dryRun) {
stdout.writeln('\x1B[33m(dry run — no changes made)\x1B[0m');
return 0;
}
// Derive the output_dir variable from config.
// config.outputDir is like "lib/flai" — the brick var is just "flai".
final outputDirVar =
config.outputDir.startsWith('lib/')
? config.outputDir.substring(4)
: config.outputDir;
// 6. Install each component using Mason.
for (final name in installOrder) {
final brickPath = _resolveBrickPath(name);
if (brickPath == null) {
stderr.writeln(
'\x1B[33m!\x1B[0m Brick not found for $name — skipping.',
);
continue;
}
stdout.writeln('\x1B[36m>\x1B[0m Installing $name...');
try {
final generator = await MasonGenerator.fromBrick(Brick.path(brickPath));
final target = DirectoryGeneratorTarget(Directory(cwd));
final files = await generator.generate(
target,
vars: {'output_dir': outputDirVar},
fileConflictResolution: FileConflictResolution.overwrite,
);
for (final file in files) {
stdout.writeln(' \x1B[32m\u2713\x1B[0m ${file.path}');
}
} on Exception catch (e) {
stderr.writeln('\x1B[31mError:\x1B[0m Failed to install $name: $e');
continue;
}
}
// 7. Add pub.dev dependencies to the project pubspec.yaml.
if (pubDeps.isNotEmpty) {
_addPubDependencies(cwd, pubDeps);
}
// 8. Update flai.yaml.
configManager.markInstalled(installOrder);
stdout.writeln('');
stdout.writeln(
'\x1B[32m\u2713 Successfully installed $componentName!\x1B[0m',
);
if (pubDeps.isNotEmpty) {
stdout.writeln('');
stdout.writeln(
'Run \x1B[36mflutter pub get\x1B[0m to fetch new dependencies.',
);
}
stdout.writeln('');
return 0;
}