linkNativeImplStarters function

Set<String> linkNativeImplStarters({
  1. String baseDir = '.',
})

Writes missing Swift and Kotlin impl classes. Returns the Apple platforms (ios/macos) that received a new file (their SwiftPM sources need a resync).

Implementation

Set<String> linkNativeImplStarters({String baseDir = '.'}) {
  final generated = p.join(baseDir, 'lib', 'src', 'generated');
  final touched = <String>{};
  for (final platform in ['ios', 'macos']) {
    final classes = Directory(p.join(baseDir, platform, 'Classes'));
    if (!classes.existsSync()) continue;
    final sources = classes.listSync().whereType<File>().where((f) => f.path.endsWith('.swift')).toList();
    final plugin = sources.where((f) => f.path.endsWith('Plugin.swift')).firstOrNull;
    if (plugin == null) continue;
    final existing = sources.map((f) => f.readAsStringSync()).join('\n');
    for (final m in RegExp(r'(\w+)Registry\.register\((\w+)\(').allMatches(plugin.readAsStringSync())) {
      final (module, cls) = (m.group(1)!, m.group(2)!);
      if (RegExp('class\\s+$cls\\b').hasMatch(existing)) continue;
      final members = _protocolMembers(Directory(p.join(generated, 'swift')), 'public protocol Hybrid${module}Protocol: AnyObject {', '.swift');
      if (members == null) continue;
      File(p.join(classes.path, '$cls.swift')).writeAsStringSync(_swiftStarter(module, cls, members));
      touched.add(platform);
    }
  }

  final ktRoot = Directory(p.join(baseDir, 'android', 'src', 'main', 'kotlin'));
  if (ktRoot.existsSync()) {
    final kts = ktRoot.listSync(recursive: true).whereType<File>().where((f) => f.path.endsWith('.kt')).toList();
    final plugin = kts.where((f) => f.path.endsWith('Plugin.kt')).firstOrNull;
    if (plugin != null) {
      final pluginSrc = plugin.readAsStringSync();
      final pkg = RegExp(r'^package\s+([\w.]+)', multiLine: true).firstMatch(pluginSrc)?.group(1);
      final existing = kts.map((f) => f.readAsStringSync()).join('\n');
      for (final m in RegExp(r'(\w+)JniBridge\.registerFactory\(\{\s*(\w+)\(').allMatches(pluginSrc)) {
        final (module, cls) = (m.group(1)!, m.group(2)!);
        if (pkg == null || RegExp('class\\s+$cls\\b').hasMatch(existing)) continue;
        final genFile = _fileDeclaring(Directory(p.join(generated, 'kotlin')), 'interface Hybrid${module}Spec {', '.kt');
        final members = genFile == null ? null : _blockMembers(genFile.readAsStringSync(), 'interface Hybrid${module}Spec {');
        if (members == null) continue;
        final imports = RegExp(r'^import .+$', multiLine: true).allMatches(genFile!.readAsStringSync()).map((i) => i.group(0)!).toSet();
        final genPkg = RegExp(r'^package\s+([\w.]+)', multiLine: true).firstMatch(genFile.readAsStringSync())!.group(1)!;
        File(p.join(plugin.parent.path, '$cls.kt')).writeAsStringSync(_kotlinStarter(pkg, genPkg, imports, module, cls, members));
      }
    }
  }
  return touched;
}