execute method
Execute command on a single folder.
Called by ToolRunner for each folder during traversal.
Returns an ItemResult describing the outcome.
Implementation
@override
Future<ItemResult> execute(CommandContext context, CliArgs args) async {
final projectPath = context.path;
final verbose = args.verbose;
final checkOnly = _flag(args, 'check');
// Resolve the input targets for this project (step-3 precedence:
// positional args > buildkit.yaml section > build.yaml fallback).
final resolved = _resolveTargets(projectPath, args);
// Skip-success when the project opts out (no targets from any source).
if (resolved.targets.isEmpty) {
if (verbose) {
print(' ${context.relativePath}: no reflection targets, skipping');
}
return ItemResult.success(path: projectPath, name: context.name);
}
// Handle --list mode: report the project and stop.
if (args.listOnly) {
print(' ${context.relativePath}');
return ItemResult.success(path: projectPath, name: context.name);
}
try {
final result = await generateReflection(
projectRoot: projectPath,
targets: resolved.targets,
options: ReflectionGenerationOptions(
packageName: resolved.packageName,
outputExtension: resolved.outputExtension,
useAllCapabilities: resolved.useAllCapabilities,
allMode: resolved.allMode,
verbose: verbose,
noCache: _flag(args, 'no-cache'),
rebuildCache: _flag(args, 'rebuild-cache'),
showCacheStatus: _flag(args, 'show-cache-status'),
cacheOnlyPackages: _stringList(args.extraOptions['cache-only']),
checkOnly: checkOnly,
),
);
if (result.cacheStatusShown) {
return ItemResult.success(path: projectPath, name: context.name);
}
if (result.noFilesMatched) {
if (verbose) {
print(' ${context.relativePath}: no files matched targets');
}
return ItemResult.success(path: projectPath, name: context.name);
}
if (result.hasFailures) {
// A resolution/generation crash is a hard failure — surface it so the
// buildkit item is marked failed rather than silently "succeeded".
return ItemResult.failure(
path: projectPath,
name: context.name,
error: 'Reflection generation failed for ${result.failedCount} '
'file(s) (generated ${result.processedCount}, '
'skipped ${result.skippedCount})',
);
}
if (result.hasStaleOutputs) {
// The generated file no longer matches its source. Failing the item is
// the whole purpose of check mode: this drift is invisible to
// `dart analyze` and to the test suites, so the build is the only
// place it can be caught.
return ItemResult.failure(
path: projectPath,
name: context.name,
error: '${result.staleCount} generated file(s) are stale — '
'${result.staleFiles.join(', ')}. Regenerate and commit.',
);
}
if (checkOnly) {
return ItemResult.success(
path: projectPath,
name: context.name,
message: 'Up to date ${result.upToDateCount}, '
'skipped ${result.skippedCount}',
);
}
return ItemResult.success(
path: projectPath,
name: context.name,
message: 'Generated ${result.processedCount}, '
'skipped ${result.skippedCount}',
);
} catch (e, stack) {
stderr.writeln('Error processing $projectPath: $e');
if (verbose) stderr.writeln(stack);
return ItemResult.failure(
path: projectPath,
name: context.name,
error: '$e',
);
}
}