handle method
Execute the command. Returns the process exit code.
Implementation
@override
Future<int> handle(ArtisanContext ctx) async {
// 1. Read all CLI option values — no defaults live in this method.
final root = ctx.input.option('root') as String? ?? '.';
final outputDir = ctx.input.option('output') as String? ?? 'public';
String teamId = ctx.input.option('team-id') as String? ?? '';
String bundleId = ctx.input.option('bundle-id') as String? ?? '';
String packageName = ctx.input.option('package-name') as String? ?? '';
List<String> fingerprints =
ctx.input.option('sha256-fingerprints') as List<String>? ?? [];
List<String> paths = ctx.input.option('paths') as List<String>? ?? [];
// 2. Resolve paths for the operation.
final projectRoot = getProjectRoot();
final effectiveRoot = root == '.' ? projectRoot : root;
final outputPath = '$effectiveRoot/$outputDir';
// 3. Try to read lib/config/deeplink.dart.
final configFile = File('$effectiveRoot/lib/config/deeplink.dart');
if (configFile.existsSync()) {
final content = configFile.readAsStringSync();
final config = parseDeeplinkConfig(content);
// 4. Merge: CLI flags override config values.
if (teamId.isEmpty) {
teamId = config['teamId'] as String? ?? '';
}
if (bundleId.isEmpty) {
bundleId = config['bundleId'] as String? ?? '';
}
if (packageName.isEmpty) {
packageName = config['packageName'] as String? ?? '';
}
if (fingerprints.isEmpty) {
fingerprints = config['fingerprints'] as List<String>? ?? [];
}
// Only use config paths if CLI didn't explicitly provide paths.
// `paths` has a default value `['/*']` in `configure()`. We only override it
// if it equals the default AND config has paths.
if (paths.length == 1 &&
paths.first == '/*' &&
(config['paths'] as List<String>? ?? []).isNotEmpty) {
paths = config['paths'] as List<String>;
}
}
ctx.output.info('Generating deep link files in $outputPath...');
// 5. Detect iOS platform via PlatformHelper — inform user about AASA placement.
if (PlatformHelper.hasPlatform(effectiveRoot, 'ios')) {
ctx.output.info(
'iOS platform detected — upload apple-app-site-association to your web server root.',
);
}
// 6. Build and write apple-app-site-association (iOS Universal Links).
if (teamId.isNotEmpty && bundleId.isNotEmpty) {
final aasaData = buildAppleAppSiteAssociation(teamId, bundleId, paths);
JsonEditor.writeJson('$outputPath/apple-app-site-association', aasaData);
ctx.output.success('Created $outputPath/apple-app-site-association');
} else {
ctx.output.warning(
'Skipping apple-app-site-association — provide --team-id and --bundle-id.',
);
}
// 7. Build and write assetlinks.json (Android App Links).
if (packageName.isNotEmpty && fingerprints.isNotEmpty) {
final assetLinksData = buildAssetLinks(packageName, fingerprints);
JsonEditor.writeJson('$outputPath/assetlinks.json', assetLinksData);
ctx.output.success('Created $outputPath/assetlinks.json');
} else {
ctx.output.warning(
'Skipping assetlinks.json — provide --package-name and --sha256-fingerprints.',
);
}
return 0;
}