@granite-js/screen 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/GraniteScreen.podspec +25 -0
  3. package/LICENSE +202 -0
  4. package/android/CMakeLists.txt +62 -0
  5. package/android/build.gradle +63 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/cpp/BundleEvaluator.cpp +27 -0
  8. package/android/src/main/cpp/BundleEvaluator.h +17 -0
  9. package/android/src/main/cpp/onLoad.cpp +6 -0
  10. package/android/src/main/kotlin/run/granite/BundleEvaluator.kt +50 -0
  11. package/android/src/main/kotlin/run/granite/BundleLoader.kt +40 -0
  12. package/android/src/main/kotlin/run/granite/DefaultBundleLoader.kt +20 -0
  13. package/android/src/main/kotlin/run/granite/DefaultErrorView.kt +58 -0
  14. package/android/src/main/kotlin/run/granite/DefaultLoadingView.kt +51 -0
  15. package/android/src/main/kotlin/run/granite/GraniteReactDelegate.kt +76 -0
  16. package/android/src/main/kotlin/run/granite/GraniteReactDelegateImpl.kt +448 -0
  17. package/android/src/main/kotlin/run/granite/GraniteReactHost.kt +113 -0
  18. package/android/src/main/kotlin/run/granite/ReactHostFactory.kt +106 -0
  19. package/gradle-plugin/LICENSE +201 -0
  20. package/gradle-plugin/README.md +578 -0
  21. package/gradle-plugin/build.gradle.kts +97 -0
  22. package/gradle-plugin/gradle/libs.versions.toml +17 -0
  23. package/gradle-plugin/gradle/wrapper/gradle-wrapper.jar +0 -0
  24. package/gradle-plugin/gradle/wrapper/gradle-wrapper.properties +7 -0
  25. package/gradle-plugin/gradle.properties +12 -0
  26. package/gradle-plugin/gradlew +248 -0
  27. package/gradle-plugin/gradlew.bat +93 -0
  28. package/gradle-plugin/settings.gradle.kts +1 -0
  29. package/gradle-plugin/src/main/kotlin/run/granite/gradle/GraniteExtension.kt +225 -0
  30. package/gradle-plugin/src/main/kotlin/run/granite/gradle/GranitePlugin.kt +784 -0
  31. package/gradle-plugin/src/main/kotlin/run/granite/gradle/GraniteRootExtension.kt +107 -0
  32. package/gradle-plugin/src/main/kotlin/run/granite/gradle/GraniteRootProjectPlugin.kt +290 -0
  33. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/BuildConfigConfigurator.kt +69 -0
  34. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/DependencyConfigurator.kt +232 -0
  35. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/DependencyCoordinates.kt +29 -0
  36. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/DevServerResourceConfigurator.kt +101 -0
  37. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/JniPackagingConfigurator.kt +160 -0
  38. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/NdkConfigurator.kt +135 -0
  39. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/RepositoryConfigurator.kt +148 -0
  40. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/ResourceConfigurator.kt +56 -0
  41. package/gradle-plugin/src/main/kotlin/run/granite/gradle/generators/CMakeGenerator.kt +105 -0
  42. package/gradle-plugin/src/main/kotlin/run/granite/gradle/generators/CppAutolinkingGenerator.kt +152 -0
  43. package/gradle-plugin/src/main/kotlin/run/granite/gradle/generators/EntryPointGenerator.kt +100 -0
  44. package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/AndroidDependencyConfig.kt +23 -0
  45. package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/AutolinkingConfig.kt +89 -0
  46. package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/CMakeEntry.kt +47 -0
  47. package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/NativeModule.kt +177 -0
  48. package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/AssetPackagingTask.kt +194 -0
  49. package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/AutolinkingTask.kt +431 -0
  50. package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/BundleTask.kt +275 -0
  51. package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/CodegenArtifactsTask.kt +218 -0
  52. package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/CodegenSchemaTask.kt +186 -0
  53. package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/AutolinkingParser.kt +128 -0
  54. package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/ConflictDetector.kt +121 -0
  55. package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/JdkValidator.kt +73 -0
  56. package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/NodeExecutableFinder.kt +43 -0
  57. package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/ReactNativeVersionReader.kt +329 -0
  58. package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/TaskDependencyValidator.kt +198 -0
  59. package/gradle-plugin/src/test/kotlin/run/granite/gradle/GraniteExtensionTest.kt +191 -0
  60. package/gradle-plugin/src/test/kotlin/run/granite/gradle/GranitePluginTest.kt +156 -0
  61. package/gradle-plugin/src/test/kotlin/run/granite/gradle/GraniteRootProjectPluginTest.kt +87 -0
  62. package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/BuildConfigConfiguratorTest.kt +115 -0
  63. package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/DependencyConfiguratorTest.kt +338 -0
  64. package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/DevServerResourceConfiguratorTest.kt +205 -0
  65. package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/ResourceConfiguratorTest.kt +131 -0
  66. package/gradle-plugin/src/test/kotlin/run/granite/gradle/fixtures/NativeModuleFixtures.kt +67 -0
  67. package/gradle-plugin/src/test/kotlin/run/granite/gradle/generators/CMakeGeneratorTest.kt +71 -0
  68. package/gradle-plugin/src/test/kotlin/run/granite/gradle/generators/CppAutolinkingGeneratorTest.kt +344 -0
  69. package/gradle-plugin/src/test/kotlin/run/granite/gradle/generators/EntryPointGeneratorTest.kt +40 -0
  70. package/gradle-plugin/src/test/kotlin/run/granite/gradle/models/AutolinkingConfigTest.kt +350 -0
  71. package/gradle-plugin/src/test/kotlin/run/granite/gradle/models/CMakeEntryTest.kt +200 -0
  72. package/gradle-plugin/src/test/kotlin/run/granite/gradle/models/NativeModuleTest.kt +562 -0
  73. package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/AssetPackagingTaskTest.kt +318 -0
  74. package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/AutolinkingTaskTest.kt +89 -0
  75. package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/BundleTaskTest.kt +68 -0
  76. package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/CodegenTasksTest.kt +410 -0
  77. package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/AutolinkingParserTest.kt +335 -0
  78. package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/ConflictDetectorTest.kt +75 -0
  79. package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/JdkValidatorTest.kt +88 -0
  80. package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/ReactNativeVersionReaderTest.kt +585 -0
  81. package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/TaskDependencyValidatorTest.kt +123 -0
  82. package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/TaskTestUtils.kt +88 -0
  83. package/gradle-plugin/src/test/resources/fixtures/sample-rn-config.json +45 -0
  84. package/ios/BundleLoader/BundleEvaluator.h +16 -0
  85. package/ios/BundleLoader/BundleEvaluator.mm +76 -0
  86. package/ios/BundleLoader/BundleLoadable.swift +91 -0
  87. package/ios/GraniteBundleLoaderTypes.swift +7 -0
  88. package/ios/GraniteScreen.h +12 -0
  89. package/ios/ReactNativeHosting/DefaultViews.swift +138 -0
  90. package/ios/ReactNativeHosting/GraniteDefaultModuleProvider.h +24 -0
  91. package/ios/ReactNativeHosting/GraniteDefaultModuleProvider.mm +22 -0
  92. package/ios/ReactNativeHosting/GraniteHostingHelper.swift +103 -0
  93. package/ios/ReactNativeHosting/GraniteNativeFactory.swift +35 -0
  94. package/ios/ReactNativeHosting/GraniteNativeFactoryDelegateImpl.swift +30 -0
  95. package/ios/ReactNativeHosting/GraniteNativeFactoryImpl.swift +24 -0
  96. package/ios/ReactNativeHosting/GraniteReactHost.swift +39 -0
  97. package/ios/ReactNativeHosting/GraniteScreen-Bridging-Header.h +12 -0
  98. package/package.json +59 -0
  99. package/react-native.config.js +8 -0
@@ -0,0 +1,105 @@
1
+ package run.granite.gradle.generators
2
+
3
+ import run.granite.gradle.models.NativeModule
4
+ import java.io.File
5
+ import java.util.logging.Logger
6
+
7
+ /**
8
+ * Generates Android-autolinking.cmake configuration file for React Native native modules.
9
+ *
10
+ * This generator creates a CMake configuration file that:
11
+ * - Adds subdirectories for each native module
12
+ * - Sets REACTNATIVE_MERGED_SO=true for React Native 0.76+
13
+ * - Sanitizes paths (escape spaces, remove CMakeLists.txt)
14
+ * - Creates unique build directory names ({libraryName}_autolinked_build)
15
+ * - Handles both standard and cxxModule CMakeLists.txt
16
+ * - Generates AUTOLINKED_LIBRARIES list with react_codegen_* prefixes
17
+ */
18
+ object CMakeGenerator {
19
+ private val logger = Logger.getLogger(CMakeGenerator::class.java.name)
20
+
21
+ /**
22
+ * Generates Android-autolinking.cmake file content.
23
+ *
24
+ * @param modules List of all native modules
25
+ * @param projectRoot Root directory of the project for path resolution
26
+ * @return Generated CMake code as string
27
+ */
28
+ fun generate(modules: List<NativeModule>, projectRoot: File): String {
29
+ // Filter out brick-module as it uses a special codegen pattern
30
+ val cmakeModules = modules.filter { it.hasCMakeConfiguration }
31
+
32
+ if (cmakeModules.isEmpty()) {
33
+ // Skip CMake generation if no modules with native code
34
+ return generateEmptyFile()
35
+ }
36
+
37
+ // Collect all CMake entries from modules
38
+ val allEntries = cmakeModules.flatMap { it.cmakeEntries() }
39
+
40
+ // Filter entries to only include directories that actually exist
41
+ val existingEntries = allEntries.filter { entry ->
42
+ val sanitizedPath = entry.sanitizedPath(projectRoot)
43
+ File(sanitizedPath).exists().also { exists ->
44
+ if (!exists) {
45
+ logger.warning("Skipping non-existent CMake directory: $sanitizedPath")
46
+ }
47
+ }
48
+ }
49
+
50
+ val allLibraryTargets = existingEntries.flatMap { it.libraryTargets }
51
+
52
+ return buildString {
53
+ appendLine("# Generated by Granite Gradle Plugin - DO NOT EDIT")
54
+ appendLine("# This file is automatically generated during the build process")
55
+ appendLine()
56
+
57
+ // Set REACTNATIVE_MERGED_SO=true for React Native 0.76+
58
+ appendLine("# Enable merged shared object model for React Native 0.76+")
59
+ appendLine("set(REACTNATIVE_MERGED_SO true)")
60
+ appendLine()
61
+
62
+ if (existingEntries.isNotEmpty()) {
63
+ appendLine("# Add native module directories")
64
+ existingEntries.forEach { entry ->
65
+ appendLine(entry.toCMakeCommand(projectRoot))
66
+ }
67
+ appendLine()
68
+ }
69
+
70
+ // Initialize empty AUTOLINKED_LIBRARIES and conditionally append targets
71
+ // This ensures clean works even when codegen directories don't exist
72
+ appendLine("# Autolinked library targets (conditionally added based on existence)")
73
+ appendLine("set(AUTOLINKED_LIBRARIES \"\")")
74
+ existingEntries.forEach { entry ->
75
+ entry.libraryTargets.forEach { target ->
76
+ appendLine("if(TARGET $target)")
77
+ appendLine(" list(APPEND AUTOLINKED_LIBRARIES $target)")
78
+ appendLine("endif()")
79
+ }
80
+ }
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Generates an empty CMake file with explanatory comment.
86
+ */
87
+ private fun generateEmptyFile(): String = buildString {
88
+ appendLine("# Generated by Granite Gradle Plugin - DO NOT EDIT")
89
+ appendLine("# This file is automatically generated during the build process")
90
+ appendLine()
91
+ appendLine("# No native modules with CMake configuration found")
92
+ appendLine("set(REACTNATIVE_MERGED_SO true)")
93
+ appendLine("set(AUTOLINKED_LIBRARIES)")
94
+ }
95
+
96
+ /**
97
+ * Writes Android-autolinking.cmake to the specified output directory.
98
+ */
99
+ fun generateToFile(modules: List<NativeModule>, projectRoot: File, outputDir: File) {
100
+ val content = generate(modules, projectRoot)
101
+ val outputFile = File(outputDir, "Android-autolinking.cmake")
102
+ outputFile.parentFile.mkdirs()
103
+ outputFile.writeText(content)
104
+ }
105
+ }
@@ -0,0 +1,152 @@
1
+ package run.granite.gradle.generators
2
+
3
+ import run.granite.gradle.models.NativeModule
4
+ import java.io.File
5
+
6
+ /**
7
+ * Generates autolinking.cpp with concrete implementations of autolinking functions.
8
+ *
9
+ * This generator creates C++ code that:
10
+ * - Implements autolinking_ModuleProvider for Java TurboModules
11
+ * - Implements autolinking_cxxModuleProvider for C++ TurboModules
12
+ * - Includes proper headers for C++ TurboModules
13
+ * - Returns nullptr when no match is found (early-return pattern)
14
+ * - Sanitizes C++ identifiers
15
+ */
16
+ object CppAutolinkingGenerator {
17
+ /**
18
+ * Generates autolinking.cpp file content.
19
+ *
20
+ * @param modules List of all native modules
21
+ * @return Generated C++ code as string
22
+ */
23
+ fun generate(modules: List<NativeModule>): String {
24
+ val cxxModules = modules.filter { it.hasCxxImplementation }
25
+ val javaModules = modules.filter { it.hasJavaImplementation }
26
+ val fabricLibraries =
27
+ modules
28
+ .filter { it.hasFabricComponents }
29
+ .mapNotNull { module ->
30
+ val libraryName = module.libraryName ?: return@mapNotNull null
31
+ libraryName to module.componentDescriptors
32
+ }
33
+ .groupBy({ it.first }, { it.second })
34
+ .mapValues { (_, descriptorLists) -> descriptorLists.flatten().distinct() }
35
+ .toSortedMap()
36
+
37
+ return buildString {
38
+ appendLine("// Generated by Granite Gradle Plugin - DO NOT EDIT")
39
+ appendLine("// This file is automatically generated during the build process")
40
+ appendLine()
41
+ appendLine("#include <autolinking.h>")
42
+ appendLine()
43
+
44
+ // C++ TurboModule headers
45
+ if (cxxModules.isNotEmpty()) {
46
+ appendLine("// C++ TurboModule headers")
47
+ cxxModules.forEach { module ->
48
+ module.cxxModuleHeaderName?.let { headerName ->
49
+ appendLine("#include <$headerName.h>")
50
+ }
51
+ }
52
+ appendLine()
53
+ }
54
+
55
+ // Fabric component descriptor headers
56
+ if (fabricLibraries.isNotEmpty()) {
57
+ appendLine("// Fabric component descriptor headers")
58
+ fabricLibraries.forEach { (libraryName, componentDescriptors) ->
59
+ appendLine("#if defined(__has_include) && __has_include(<react/renderer/components/$libraryName/ComponentDescriptors.h>)")
60
+ appendLine("#include <react/renderer/components/$libraryName/ComponentDescriptors.h>")
61
+ appendLine("#endif")
62
+ componentDescriptors.forEach { descriptor ->
63
+ appendLine("#if defined(__has_include) && __has_include(<react/renderer/components/$libraryName/$descriptor.h>)")
64
+ appendLine("#include <react/renderer/components/$libraryName/$descriptor.h>")
65
+ appendLine("#endif")
66
+ }
67
+ }
68
+ appendLine()
69
+ }
70
+
71
+ appendLine("namespace facebook::react {")
72
+ appendLine()
73
+
74
+ // autolinking_registerProviders function
75
+ appendLine("void autolinking_registerProviders(")
76
+ appendLine(" std::shared_ptr<const ComponentDescriptorProviderRegistry> registry) {")
77
+ if (fabricLibraries.isNotEmpty()) {
78
+ appendLine(" // Register Fabric component descriptors from autolinked libraries")
79
+ fabricLibraries.forEach { (libraryName, componentDescriptors) ->
80
+ appendLine("#if defined(__has_include) && __has_include(<react/renderer/components/$libraryName/ComponentDescriptors.h>)")
81
+ appendLine(" ${libraryName}_registerComponentDescriptorsFromCodegen(registry);")
82
+ appendLine("#endif")
83
+ componentDescriptors.forEach { descriptor ->
84
+ appendLine("#if defined(__has_include) && __has_include(<react/renderer/components/$libraryName/$descriptor.h>)")
85
+ appendLine(" registry->add(concreteComponentDescriptorProvider<$descriptor>());")
86
+ appendLine("#endif")
87
+ }
88
+ }
89
+ } else {
90
+ appendLine(" // No autolinked Fabric components found")
91
+ }
92
+ appendLine("}")
93
+ appendLine()
94
+
95
+ // autolinking_cxxModuleProvider function
96
+ appendLine("std::shared_ptr<TurboModule> autolinking_cxxModuleProvider(")
97
+ appendLine(" const std::string& moduleName,")
98
+ appendLine(" const std::shared_ptr<CallInvoker>& jsInvoker) {")
99
+ if (cxxModules.isNotEmpty()) {
100
+ appendLine(" // Check C++ TurboModules (early-return pattern)")
101
+ cxxModules.forEach { module ->
102
+ module.cxxModuleHeaderName?.let { headerName ->
103
+ appendLine(" if (moduleName == $headerName::kModuleName) {")
104
+ appendLine(" return std::make_shared<$headerName>(jsInvoker);")
105
+ appendLine(" }")
106
+ }
107
+ }
108
+ } else {
109
+ appendLine(" // No autolinked C++ TurboModules found")
110
+ }
111
+ appendLine()
112
+ appendLine(" return nullptr;")
113
+ appendLine("}")
114
+ appendLine()
115
+
116
+ // autolinking_ModuleProvider function
117
+ appendLine("std::shared_ptr<TurboModule> autolinking_ModuleProvider(")
118
+ appendLine(" const std::string& moduleName,")
119
+ appendLine(" const JavaTurboModule::InitParams& params) {")
120
+ if (javaModules.isNotEmpty()) {
121
+ appendLine(" // Delegate to Java TurboModule providers (early-return pattern)")
122
+ javaModules.forEach { module ->
123
+ module.libraryName?.let { libraryName ->
124
+ val sanitized = module.sanitizedName()
125
+ appendLine(" auto module_$sanitized = ${libraryName}_ModuleProvider(moduleName, params);")
126
+ appendLine(" if (module_$sanitized != nullptr) {")
127
+ appendLine(" return module_$sanitized;")
128
+ appendLine(" }")
129
+ }
130
+ }
131
+ } else {
132
+ appendLine(" // No autolinked Java TurboModules found")
133
+ }
134
+ appendLine()
135
+ appendLine(" return nullptr;")
136
+ appendLine("}")
137
+ appendLine()
138
+
139
+ appendLine("} // namespace facebook::react")
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Writes autolinking.cpp to the specified output directory.
145
+ */
146
+ fun generateToFile(modules: List<NativeModule>, outputDir: File) {
147
+ val content = generate(modules)
148
+ val outputFile = File(outputDir, "autolinking.cpp")
149
+ outputFile.parentFile.mkdirs()
150
+ outputFile.writeText(content)
151
+ }
152
+ }
@@ -0,0 +1,100 @@
1
+ package run.granite.gradle.generators
2
+
3
+ import java.io.File
4
+
5
+ /**
6
+ * Generates ReactNativeApplicationEntryPoint.java for SoLoader initialization
7
+ * and New Architecture loading.
8
+ *
9
+ * This generator creates Java code that:
10
+ * - Provides ReactNativeApplicationEntryPoint class with static loadReactNative method
11
+ * - Initializes SoLoader with OpenSourceMergedSoMapping
12
+ * - Conditionally loads New Architecture components
13
+ * - Conditionally enables edge-to-edge UI
14
+ * - Places generated code in com.facebook.react package
15
+ * - Throws RuntimeException on SoLoader failure
16
+ */
17
+ object EntryPointGenerator {
18
+ /**
19
+ * Generates ReactNativeApplicationEntryPoint.java file content.
20
+ *
21
+ * @param packageName The Android package name from project configuration
22
+ * @return Generated Java code as string
23
+ * @throws IllegalStateException if packageName is null
24
+ */
25
+ fun generate(packageName: String?): String {
26
+ // Validate package name is present
27
+ requireNotNull(packageName) {
28
+ "Android package name not found in react-native config. " +
29
+ "Ensure project has valid android configuration."
30
+ }
31
+
32
+ return buildString {
33
+ appendLine("// Generated by Granite Gradle Plugin - DO NOT EDIT")
34
+ appendLine("// This file is automatically generated during the build process")
35
+ appendLine()
36
+ appendLine("package com.facebook.react;")
37
+ appendLine()
38
+ appendLine("import android.content.Context;")
39
+ appendLine("import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;")
40
+ appendLine("import com.facebook.react.soloader.OpenSourceMergedSoMapping;")
41
+ appendLine("import com.facebook.react.views.view.WindowUtilKt;")
42
+ appendLine("import com.facebook.soloader.SoLoader;")
43
+ appendLine()
44
+ appendLine("/**")
45
+ appendLine(" * ReactNativeApplicationEntryPoint provides static initialization for React Native runtime.")
46
+ appendLine(" * This class handles SoLoader initialization and New Architecture component loading.")
47
+ appendLine(" */")
48
+ appendLine("public class ReactNativeApplicationEntryPoint {")
49
+ appendLine()
50
+ appendLine(" /**")
51
+ appendLine(" * Loads the React Native runtime with proper SoLoader and New Architecture initialization.")
52
+ appendLine(" *")
53
+ appendLine(" * @param context Application context for SoLoader initialization")
54
+ appendLine(" * @throws RuntimeException if SoLoader initialization fails")
55
+ appendLine(" */")
56
+ appendLine(" public static void loadReactNative(Context context) {")
57
+ appendLine(" // Initialize SoLoader with merged shared object mapping")
58
+ appendLine(" try {")
59
+ appendLine(" SoLoader.init(context, OpenSourceMergedSoMapping.INSTANCE);")
60
+ appendLine(" } catch (Exception e) {")
61
+ appendLine(" throw new RuntimeException(\"Failed to initialize SoLoader\", e);")
62
+ appendLine(" }")
63
+ appendLine()
64
+ appendLine(" // Load New Architecture components if enabled")
65
+ appendLine(" // if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {")
66
+ appendLine(" DefaultNewArchitectureEntryPoint.load();")
67
+ appendLine(" WindowUtilKt.setEdgeToEdgeFeatureFlagOn();")
68
+ appendLine(" // }")
69
+ appendLine()
70
+ appendLine(" // Enable edge-to-edge UI if configured")
71
+ appendLine(" // if (BuildConfig.IS_EDGE_TO_EDGE_ENABLED) {")
72
+ appendLine(" // Edge-to-edge feature flag is set in BuildConfig")
73
+ appendLine(" // }")
74
+ appendLine(" }")
75
+ appendLine()
76
+ appendLine(" private ReactNativeApplicationEntryPoint() {")
77
+ appendLine(" // Utility class - prevent instantiation")
78
+ appendLine(" }")
79
+ appendLine("}")
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Writes ReactNativeApplicationEntryPoint.java to the specified output directory.
85
+ *
86
+ * @param packageName Android package name from project configuration
87
+ * @param outputDir Output directory for Java source files
88
+ * @throws IllegalStateException if packageName is null
89
+ */
90
+ fun generateToFile(packageName: String?, outputDir: File) {
91
+ val content = generate(packageName)
92
+
93
+ // Create package directory structure: com/facebook/react/
94
+ val packageDir = File(outputDir, "src/main/java/com/facebook/react")
95
+ packageDir.mkdirs()
96
+
97
+ val outputFile = File(packageDir, "ReactNativeApplicationEntryPoint.java")
98
+ outputFile.writeText(content)
99
+ }
100
+ }
@@ -0,0 +1,23 @@
1
+ package run.granite.gradle.models
2
+
3
+ /**
4
+ * Complete Android-specific configuration for a native module.
5
+ * Maps directly to the "platforms.android" JSON structure from react-native config.
6
+ *
7
+ * All fields are nullable as they may be absent in the JSON configuration.
8
+ * Missing fields are treated identically to null fields.
9
+ */
10
+ data class AndroidDependencyConfig(
11
+ val sourceDir: String?,
12
+ val packageImportPath: String?,
13
+ val packageInstance: String?,
14
+ val dependencyConfiguration: String?,
15
+ val buildTypes: List<String>?,
16
+ val libraryName: String?,
17
+ val componentDescriptors: List<String>?,
18
+ val cmakeListsPath: String?,
19
+ val cxxModuleCMakeListsPath: String?,
20
+ val cxxModuleCMakeListsModuleName: String?,
21
+ val cxxModuleHeaderName: String?,
22
+ val isPureCxxDependency: Boolean?,
23
+ )
@@ -0,0 +1,89 @@
1
+ package run.granite.gradle.models
2
+
3
+ /**
4
+ * Root configuration object parsed from `react-native config` JSON output.
5
+ *
6
+ * Provides filtered views of dependencies for different code generation purposes:
7
+ * - androidDependencies(): All modules with Android native code
8
+ * - javaModules(): Modules with Java TurboModule implementations
9
+ * - cxxModules(): Modules with C++ TurboModule implementations
10
+ * - fabricModules(): Modules with Fabric components
11
+ * - cmakeModules(): Modules with CMake configuration
12
+ */
13
+ data class AutolinkingConfig(
14
+ val project: ProjectInfo,
15
+ val dependencies: Map<String, DependencyConfig>,
16
+ ) {
17
+ /**
18
+ * Filters dependencies to only those with Android native code.
19
+ * Excludes JavaScript-only packages.
20
+ * Reads each module's package.json to check for includesGeneratedCode flag.
21
+ */
22
+ fun androidDependencies(): List<NativeModule> = dependencies.mapNotNull { (name, config) ->
23
+ config.platforms?.android?.let { androidConfig ->
24
+ NativeModule.from(name, androidConfig, config.root)
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Returns modules that have Java TurboModule implementations.
30
+ * Identified by non-null packageImportPath and packageInstance.
31
+ */
32
+ fun javaModules(): List<NativeModule> = androidDependencies().filter { it.hasJavaImplementation }
33
+
34
+ /**
35
+ * Returns modules that have C++ TurboModule implementations.
36
+ * Identified by non-null cxxModuleHeaderName.
37
+ */
38
+ fun cxxModules(): List<NativeModule> = androidDependencies().filter { it.hasCxxImplementation }
39
+
40
+ /**
41
+ * Returns modules that have Fabric components.
42
+ * Identified by non-empty componentDescriptors array.
43
+ */
44
+ fun fabricModules(): List<NativeModule> = androidDependencies().filter { it.hasFabricComponents }
45
+
46
+ /**
47
+ * Returns modules that have CMake configuration.
48
+ * Identified by non-null cmakeListsPath or cxxModuleCMakeListsPath.
49
+ */
50
+ fun cmakeModules(): List<NativeModule> = androidDependencies().filter { it.hasCMakeConfiguration }
51
+ }
52
+
53
+ /**
54
+ * Metadata about the React Native project itself.
55
+ * Used for entry point generation.
56
+ */
57
+ data class ProjectInfo(
58
+ val name: String?,
59
+ val version: String?,
60
+ val ios: Map<String, Any>?,
61
+ val android: AndroidProjectConfig?,
62
+ )
63
+
64
+ /**
65
+ * Android project configuration extracted from react-native config.
66
+ * packageName is required for entry point generation.
67
+ */
68
+ data class AndroidProjectConfig(
69
+ val sourceDir: String?,
70
+ val manifestPath: String?,
71
+ val packageName: String?,
72
+ )
73
+
74
+ /**
75
+ * Top-level configuration for a single dependency.
76
+ */
77
+ data class DependencyConfig(
78
+ val name: String,
79
+ val root: String,
80
+ val platforms: PlatformConfig?,
81
+ )
82
+
83
+ /**
84
+ * Platform-specific configuration container.
85
+ */
86
+ data class PlatformConfig(
87
+ val ios: Map<String, Any>?,
88
+ val android: AndroidDependencyConfig?,
89
+ )
@@ -0,0 +1,47 @@
1
+ package run.granite.gradle.models
2
+
3
+ import java.io.File
4
+
5
+ /**
6
+ * Represents a single add_subdirectory entry in Android-autolinking.cmake.
7
+ *
8
+ * Each CMake entry includes:
9
+ * - sourcePath: Path to the directory containing CMakeLists.txt
10
+ * - buildDirName: Unique build directory name for this module
11
+ * - libraryTargets: List of CMake library targets to be linked
12
+ */
13
+ data class CMakeEntry(
14
+ val sourcePath: String,
15
+ val buildDirName: String,
16
+ val libraryTargets: List<String>,
17
+ ) {
18
+ /**
19
+ * Sanitizes the source path for CMake.
20
+ * - Removes trailing /CMakeLists.txt or \CMakeLists.txt
21
+ * - Converts to absolute path if relative
22
+ * - Replaces backslashes with forward slashes
23
+ */
24
+ fun sanitizedPath(moduleRoot: File): String {
25
+ var path = sourcePath.removeSuffix("/CMakeLists.txt")
26
+ .removeSuffix("\\CMakeLists.txt")
27
+
28
+ val file = File(path)
29
+ if (!file.isAbsolute) {
30
+ path = File(moduleRoot, path).canonicalPath
31
+ }
32
+
33
+ return path.replace('\\', '/')
34
+ }
35
+
36
+ /**
37
+ * Generates the add_subdirectory command line with existence check.
38
+ * Wraps with if(EXISTS ...) to prevent CMake errors during clean
39
+ * when codegen directories don't exist yet.
40
+ */
41
+ fun toCMakeCommand(moduleRoot: File): String {
42
+ val sanitized = sanitizedPath(moduleRoot)
43
+ return """if(EXISTS "$sanitized")
44
+ add_subdirectory("$sanitized" $buildDirName)
45
+ endif()"""
46
+ }
47
+ }