@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,29 @@
1
+ package run.granite.gradle.config
2
+
3
+ /**
4
+ * Dependency coordinate information for React Native and Hermes.
5
+ *
6
+ * @property reactVersion React Native version (e.g., "0.84.0-rc.4")
7
+ * @property hermesVersion Hermes Classic version (e.g., "0.15.0")
8
+ * @property hermesV1Version Hermes V1 version (e.g., "250829098.0.6")
9
+ * @property reactGroup React Native Maven group (default: com.facebook.react)
10
+ * @property hermesGroup Hermes Maven group (default: com.facebook.hermes)
11
+ */
12
+ data class DependencyCoordinates(
13
+ val reactVersion: String,
14
+ val hermesVersion: String,
15
+ val hermesV1Version: String,
16
+ val reactGroup: String = DEFAULT_REACT_GROUP,
17
+ val hermesGroup: String = DEFAULT_HERMES_GROUP,
18
+ ) {
19
+ companion object {
20
+ const val DEFAULT_REACT_GROUP = "com.facebook.react"
21
+ const val DEFAULT_HERMES_GROUP = "com.facebook.hermes"
22
+ }
23
+
24
+ /**
25
+ * Returns the Hermes version to use.
26
+ * Granite always uses Hermes V1.
27
+ */
28
+ fun getEffectiveHermesVersion(): String = if (hermesV1Version.isNotBlank()) hermesV1Version else hermesVersion
29
+ }
@@ -0,0 +1,101 @@
1
+ package run.granite.gradle.config
2
+
3
+ import org.gradle.api.Project
4
+ import run.granite.gradle.GraniteExtension
5
+ import java.io.File
6
+
7
+ /**
8
+ * Configurator for development server resource generation.
9
+ *
10
+ * Generates Android resources for React Native development server configuration:
11
+ * - res/values/strings.xml: Dev server host
12
+ * - res/values/integers.xml: Dev server port
13
+ *
14
+ * These resources are used by React Native's DevSupportManager for connecting
15
+ * to the Metro bundler during development.
16
+ */
17
+ class DevServerResourceConfigurator(
18
+ private val project: Project,
19
+ private val extension: GraniteExtension,
20
+ ) {
21
+
22
+ /**
23
+ * Generates dev server configuration resources for debug builds.
24
+ *
25
+ * Creates:
26
+ * - src/debug/res/values/strings.xml with react_native_dev_server_host
27
+ * - src/debug/res/values/integers.xml with react_native_dev_server_port
28
+ *
29
+ * Only generates resources if devServerHost and devServerPort are configured.
30
+ */
31
+ fun configure() {
32
+ val devServerHost = extension.devServerHost.orNull
33
+ val devServerPort = extension.devServerPort.orNull
34
+
35
+ if (devServerHost == null && devServerPort == null) {
36
+ project.logger.debug("Dev server configuration not set, skipping resource generation")
37
+ return
38
+ }
39
+
40
+ // Generate debug variant resources
41
+ val debugResDir = project.file("src/debug/res/values")
42
+ debugResDir.mkdirs()
43
+
44
+ if (devServerHost != null) {
45
+ generateStringsXml(debugResDir, devServerHost)
46
+ }
47
+
48
+ if (devServerPort != null) {
49
+ generateIntegersXml(debugResDir, devServerPort)
50
+ }
51
+
52
+ project.logger.lifecycle(
53
+ "Dev server resources generated: host=${devServerHost ?: "default"}, port=${devServerPort ?: "default"}",
54
+ )
55
+ }
56
+
57
+ /**
58
+ * Generates strings.xml with dev server host configuration.
59
+ */
60
+ private fun generateStringsXml(resDir: File, host: String) {
61
+ val stringsFile = File(resDir, "strings.xml")
62
+
63
+ val content = """
64
+ <?xml version="1.0" encoding="utf-8"?>
65
+ <resources>
66
+ <!-- React Native Metro bundler development server host -->
67
+ <string name="react_native_dev_server_host" translatable="false">${escapeXml(host)}</string>
68
+ </resources>
69
+ """.trimIndent()
70
+
71
+ stringsFile.writeText(content)
72
+ project.logger.debug("Generated ${stringsFile.absolutePath}")
73
+ }
74
+
75
+ /**
76
+ * Escapes special XML characters to prevent injection vulnerabilities.
77
+ */
78
+ private fun escapeXml(value: String): String = value.replace("&", "&amp;")
79
+ .replace("<", "&lt;")
80
+ .replace(">", "&gt;")
81
+ .replace("\"", "&quot;")
82
+ .replace("'", "&apos;")
83
+
84
+ /**
85
+ * Generates integers.xml with dev server port configuration.
86
+ */
87
+ private fun generateIntegersXml(resDir: File, port: Int) {
88
+ val integersFile = File(resDir, "integers.xml")
89
+
90
+ val content = """
91
+ <?xml version="1.0" encoding="utf-8"?>
92
+ <resources>
93
+ <!-- React Native Metro bundler development server port -->
94
+ <integer name="react_native_dev_server_port">$port</integer>
95
+ </resources>
96
+ """.trimIndent()
97
+
98
+ integersFile.writeText(content)
99
+ project.logger.debug("Generated ${integersFile.absolutePath}")
100
+ }
101
+ }
@@ -0,0 +1,160 @@
1
+ package run.granite.gradle.config
2
+
3
+ import com.android.build.api.variant.LibraryAndroidComponentsExtension
4
+ import com.android.build.api.variant.Variant
5
+ import org.gradle.api.Project
6
+ import run.granite.gradle.GraniteExtension
7
+
8
+ /**
9
+ * Configures JNI library packaging options for React Native native modules.
10
+ *
11
+ * This configurator sets up pickFirst rules to handle duplicate .so files
12
+ * from various React Native dependencies. Based on React Native Gradle Plugin's
13
+ * NdkConfiguratorUtils.configureJsEnginePackagingOptions and configureNewArchPackagingOptions.
14
+ *
15
+ * Key responsibilities:
16
+ * - Configure Hermes .so file packaging (libhermes.so, libhermestooling.so)
17
+ * - Configure fbjni and c++_shared library packaging
18
+ * - Configure React Native core library packaging (libreactnative.so, libjsi.so)
19
+ * - Exclude JSC libraries when Hermes is enabled
20
+ *
21
+ * Without proper packaging configuration, duplicate .so files from multiple AARs
22
+ * will cause build failures or runtime crashes.
23
+ */
24
+ class JniPackagingConfigurator(
25
+ private val project: Project,
26
+ private val extension: GraniteExtension,
27
+ ) {
28
+
29
+ /**
30
+ * Configures JNI packaging options for all build variants.
31
+ *
32
+ * Must be called during plugin configuration (typically in apply() or afterEvaluate).
33
+ */
34
+ fun configure(androidComponents: LibraryAndroidComponentsExtension) {
35
+ project.logger.lifecycle("Configuring JNI packaging options for React Native")
36
+
37
+ // Configure packaging for each variant
38
+ androidComponents.onVariants { variant ->
39
+ val hermesEnabled = true // Hermes is always enabled
40
+ val newArchEnabled = true // New Architecture is mandatory
41
+
42
+ project.logger.debug(
43
+ "Configuring packaging for variant ${variant.name}: " +
44
+ "hermesEnabled=$hermesEnabled (mandatory), newArchEnabled=$newArchEnabled (mandatory)",
45
+ )
46
+
47
+ // Configure JS engine packaging (Hermes only)
48
+ configureJsEnginePackaging(variant, hermesEnabled)
49
+
50
+ // Configure New Architecture packaging
51
+ configureNewArchPackaging(variant, newArchEnabled)
52
+
53
+ project.logger.debug("Packaging configuration complete for variant ${variant.name}")
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Configures JavaScript engine .so file packaging.
59
+ *
60
+ * Based on NdkConfiguratorUtils.configureJsEnginePackagingOptions.
61
+ *
62
+ * When Hermes is enabled:
63
+ * - Exclude JSC libraries (libjsc.so, libjsctooling.so)
64
+ * - Include Hermes libraries (libhermes.so, libhermestooling.so) as pickFirst
65
+ *
66
+ * When JSC is enabled:
67
+ * - Exclude Hermes libraries
68
+ * - Include JSC libraries as pickFirst
69
+ *
70
+ * @param variant The build variant being configured
71
+ * @param hermesEnabled Whether Hermes is enabled for this build
72
+ */
73
+ private fun configureJsEnginePackaging(
74
+ variant: com.android.build.api.variant.Variant,
75
+ hermesEnabled: Boolean,
76
+ ) {
77
+ if (hermesEnabled) {
78
+ // Hermes enabled: exclude JSC, include Hermes
79
+ variant.packaging.jniLibs.excludes.addAll(
80
+ listOf(
81
+ "**/libjsc.so",
82
+ "**/libjsctooling.so",
83
+ ),
84
+ )
85
+ variant.packaging.jniLibs.pickFirsts.addAll(
86
+ listOf(
87
+ "**/libhermes.so",
88
+ "**/libhermestooling.so",
89
+ ),
90
+ )
91
+
92
+ project.logger.debug("Configured Hermes packaging: excluding JSC, including Hermes")
93
+ } else {
94
+ // JSC enabled (not supported by Granite, but keep for compatibility)
95
+ variant.packaging.jniLibs.excludes.addAll(
96
+ listOf(
97
+ "**/libhermes.so",
98
+ "**/libhermestooling.so",
99
+ ),
100
+ )
101
+ variant.packaging.jniLibs.pickFirsts.addAll(
102
+ listOf(
103
+ "**/libjsc.so",
104
+ "**/libjsctooling.so",
105
+ ),
106
+ )
107
+
108
+ project.logger.warn(
109
+ "JSC packaging configured, but Granite plugin only supports Hermes. " +
110
+ "This configuration may cause runtime errors.",
111
+ )
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Configures New Architecture .so file packaging.
117
+ *
118
+ * Based on NdkConfiguratorUtils.configureNewArchPackagingOptions.
119
+ *
120
+ * For Old Architecture:
121
+ * - pickFirst for fbjni and c++_shared (commonly duplicated)
122
+ *
123
+ * For New Architecture:
124
+ * - pickFirst for fbjni, c++_shared, libreactnative.so, libjsi.so
125
+ *
126
+ * @param variant The build variant being configured
127
+ * @param newArchEnabled Whether New Architecture is enabled
128
+ */
129
+ private fun configureNewArchPackaging(
130
+ variant: com.android.build.api.variant.Variant,
131
+ newArchEnabled: Boolean,
132
+ ) {
133
+ if (!newArchEnabled) {
134
+ // Old Architecture: basic pickFirst for common duplicates
135
+ variant.packaging.jniLibs.pickFirsts.addAll(
136
+ listOf(
137
+ "**/libfbjni.so",
138
+ "**/libc++_shared.so",
139
+ ),
140
+ )
141
+
142
+ project.logger.debug("Configured Old Architecture packaging: basic pickFirsts")
143
+ } else {
144
+ // New Architecture: additional React Native core libraries
145
+ variant.packaging.jniLibs.pickFirsts.addAll(
146
+ listOf(
147
+ // FBJNI provided via prefab
148
+ "**/libfbjni.so",
149
+ // React Native prefab libraries
150
+ "**/libreactnative.so",
151
+ "**/libjsi.so",
152
+ // C++ standard library
153
+ "**/libc++_shared.so",
154
+ ),
155
+ )
156
+
157
+ project.logger.debug("Configured New Architecture packaging: extended pickFirsts")
158
+ }
159
+ }
160
+ }
@@ -0,0 +1,135 @@
1
+ package run.granite.gradle.config
2
+
3
+ import com.android.build.gradle.LibraryExtension
4
+ import org.gradle.api.Project
5
+ import run.granite.gradle.GraniteExtension
6
+ import java.io.File
7
+
8
+ /**
9
+ * Configures Android NDK and CMake build settings for React Native native modules.
10
+ *
11
+ * This configurator:
12
+ * - Enables Prefab for consuming native library dependencies
13
+ * - Configures externalNativeBuild with CMake
14
+ * - Configures Android ABIs
15
+ * - Sets up CMake arguments for React Native
16
+ *
17
+ * Native libraries (.so files) are built by CMake and packaged in the AAR.
18
+ * App modules can consume these libraries via Prefab without additional configuration.
19
+ */
20
+ class NdkConfigurator(
21
+ private val project: Project,
22
+ private val extension: GraniteExtension,
23
+ private val androidExtension: LibraryExtension,
24
+ ) {
25
+
26
+ /**
27
+ * Configures all NDK and CMake settings.
28
+ */
29
+ fun configure() {
30
+ project.logger.lifecycle("Configuring NDK and CMake for React Native native modules")
31
+
32
+ // Enable Prefab
33
+ configurePrefab()
34
+
35
+ // Configure externalNativeBuild with CMake
36
+ configureCMake()
37
+
38
+ // Configure ABIs
39
+ configureAbis()
40
+
41
+ project.logger.lifecycle("NDK configuration complete")
42
+ }
43
+
44
+ /**
45
+ * Enables Prefab for packaging native libraries.
46
+ *
47
+ * Prefab allows the AAR to export native libraries (.so files) that can be
48
+ * consumed by app modules without manual configuration.
49
+ *
50
+ * Implementation details.
51
+ */
52
+ private fun configurePrefab() {
53
+ androidExtension.buildFeatures.prefab = true
54
+
55
+ project.logger.debug("Prefab enabled for native library packaging")
56
+ }
57
+
58
+ /**
59
+ * Configures externalNativeBuild with CMake.
60
+ *
61
+ * Sets up CMake configuration for building React Native and custom native modules.
62
+ *
63
+ * Implementation details.
64
+ */
65
+ private fun configureCMake() {
66
+ val cmakeListsFile = project.file("CMakeLists.txt")
67
+
68
+ if (!cmakeListsFile.exists()) {
69
+ project.logger.warn(
70
+ """
71
+ |CMakeLists.txt not found: ${cmakeListsFile.absolutePath}
72
+ |
73
+ |Native module builds will be skipped. If your library has C++ native modules,
74
+ |create a CMakeLists.txt file at the project root.
75
+ """.trimMargin(),
76
+ )
77
+ return
78
+ }
79
+
80
+ androidExtension.externalNativeBuild.cmake {
81
+ path = cmakeListsFile
82
+
83
+ // CMake version (compatible with React Native 0.81+)
84
+ version = "3.22.1"
85
+ }
86
+
87
+ androidExtension.defaultConfig.externalNativeBuild.cmake {
88
+ // Add CMake arguments for React Native
89
+ val codegenJniDir = project.layout.buildDirectory
90
+ .dir("generated/codegen/jni")
91
+ .get().asFile.absolutePath
92
+
93
+ val reactNativeDir = extension.getReactNativeDirResolved().absolutePath
94
+ val nodeModulesDir = extension.getNodeModulesDirResolved().absolutePath
95
+ // For RN 0.71-0.75, use ReactAndroid; for RN 0.76+, use android
96
+ val reactAndroidDir = "$reactNativeDir/ReactAndroid"
97
+ val projectBuildDir = project.layout.buildDirectory.get().asFile.absolutePath
98
+ val projectRootDir = project.rootDir.absolutePath
99
+
100
+ arguments(
101
+ "-DREACT_NATIVE_DIR=$reactNativeDir",
102
+ "-DREACT_ANDROID_DIR=$reactAndroidDir",
103
+ "-DNODE_MODULES_DIR=$nodeModulesDir",
104
+ "-DCODEGEN_JNI_DIR=$codegenJniDir",
105
+ "-DPROJECT_BUILD_DIR=$projectBuildDir",
106
+ "-DPROJECT_ROOT_DIR=$projectRootDir",
107
+ "-DANDROID_STL=c++_shared",
108
+ )
109
+
110
+ project.logger.debug("CMake configured with React Native arguments")
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Configures Android ABIs (architectures) to build.
116
+ *
117
+ * By default, builds for all ABIs. Users can customize via GraniteExtension.
118
+ *
119
+ * Implementation details.
120
+ */
121
+ private fun configureAbis() {
122
+ val abis = extension.nativeArchitectures.get()
123
+
124
+ if (abis.isEmpty()) {
125
+ project.logger.warn("No ABIs configured - using default (all ABIs)")
126
+ return
127
+ }
128
+
129
+ androidExtension.defaultConfig.ndk {
130
+ abiFilters.addAll(abis)
131
+ }
132
+
133
+ project.logger.lifecycle("Configured ABIs: ${abis.joinToString(", ")}")
134
+ }
135
+ }
@@ -0,0 +1,148 @@
1
+ package run.granite.gradle.config
2
+
3
+ import org.gradle.api.Project
4
+ import run.granite.gradle.GraniteExtension
5
+ import java.io.File
6
+
7
+ /**
8
+ * Configures Maven repositories for React Native dependencies.
9
+ *
10
+ * This configurator automatically adds required repositories:
11
+ * - Maven Central (for React Native, Hermes, SoLoader)
12
+ * - Google Maven (for Android dependencies)
13
+ * - React Native Maven repository (node_modules/react-native/android)
14
+ * - JSC/Hermes Maven repository (for JavaScript engines)
15
+ * - Project-specific repositories (if configured)
16
+ */
17
+ class RepositoryConfigurator(
18
+ private val project: Project,
19
+ private val extension: GraniteExtension,
20
+ ) {
21
+
22
+ /**
23
+ * Configures all required Maven repositories.
24
+ *
25
+ * Called during plugin application to ensure repositories are available
26
+ * before dependency resolution.
27
+ *
28
+ * Adds repositories to all projects (including root) so that third-party modules
29
+ * can resolve react-native dependencies.
30
+ */
31
+ fun configure() {
32
+ project.logger.lifecycle("Configuring Maven repositories for React Native")
33
+
34
+ val reactNativeDir = extension.getReactNativeDirResolved()
35
+ val nodeModulesDir = extension.getNodeModulesDirResolved()
36
+
37
+ // Add repositories to all projects (official RN plugin approach)
38
+ project.rootProject.allprojects.forEach { eachProject ->
39
+ // Add standard repositories
40
+ addStandardRepositories(eachProject)
41
+
42
+ // Add React Native Maven repository
43
+ addReactNativeMavenRepository(eachProject, reactNativeDir)
44
+
45
+ // Add Android Maven repository from node_modules
46
+ addAndroidMavenRepository(eachProject, nodeModulesDir)
47
+
48
+ // Add JSC Maven repository (even though we use Hermes, some RN components may reference it)
49
+ addJscMavenRepository(eachProject, nodeModulesDir)
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Adds standard Maven repositories (Google, Maven Central).
55
+ */
56
+ private fun addStandardRepositories(targetProject: Project) {
57
+ targetProject.repositories.apply {
58
+ // Google Maven for Android dependencies
59
+ if (findByName("Google") == null) {
60
+ google()
61
+ project.logger.debug("[${targetProject.name}] Added repository: Google Maven")
62
+ }
63
+
64
+ // Maven Central for React Native and Hermes
65
+ if (findByName("MavenRepo") == null) {
66
+ mavenCentral()
67
+ project.logger.debug("[${targetProject.name}] Added repository: Maven Central")
68
+ }
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Adds React Native Maven repository from node_modules.
74
+ *
75
+ * This repository contains React Native Android artifacts.
76
+ */
77
+ private fun addReactNativeMavenRepository(targetProject: Project, reactNativeDir: File) {
78
+ val reactNativeAndroidDir = reactNativeDir.resolve("ReactAndroid")
79
+
80
+ if (!reactNativeAndroidDir.exists()) {
81
+ project.logger.warn(
82
+ "React Native Android directory not found: ${reactNativeAndroidDir.absolutePath}",
83
+ )
84
+ return
85
+ }
86
+
87
+ // Prevent duplicate additions
88
+ if (targetProject.repositories.findByName("ReactNative") != null) {
89
+ return
90
+ }
91
+
92
+ targetProject.repositories.maven {
93
+ name = "ReactNative"
94
+ setUrl(reactNativeAndroidDir)
95
+ }
96
+
97
+ project.logger.debug("[${targetProject.name}] Added repository: React Native Maven (${reactNativeAndroidDir.absolutePath})")
98
+ }
99
+
100
+ /**
101
+ * Adds Android Maven repository from node_modules.
102
+ *
103
+ * This repository contains prebuilt React Native Android artifacts.
104
+ */
105
+ private fun addAndroidMavenRepository(targetProject: Project, nodeModulesDir: File) {
106
+ val androidMavenDir = nodeModulesDir.resolve("react-native/android")
107
+
108
+ if (androidMavenDir.exists()) {
109
+ // Prevent duplicate additions
110
+ if (targetProject.repositories.findByName("ReactNativeAndroid") != null) {
111
+ return
112
+ }
113
+
114
+ targetProject.repositories.maven {
115
+ name = "ReactNativeAndroid"
116
+ setUrl(androidMavenDir)
117
+ }
118
+
119
+ project.logger.debug("[${targetProject.name}] Added repository: React Native Android Maven (${androidMavenDir.absolutePath})")
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Adds JSC Maven repository from node_modules.
125
+ *
126
+ * Even though Hermes is required, some React Native components may reference JSC.
127
+ */
128
+ private fun addJscMavenRepository(targetProject: Project, nodeModulesDir: File) {
129
+ val jscAndroidDir = nodeModulesDir.resolve("jsc-android/dist")
130
+
131
+ if (jscAndroidDir.exists()) {
132
+ // Prevent duplicate additions
133
+ if (targetProject.repositories.findByName("JSC") != null) {
134
+ return
135
+ }
136
+
137
+ targetProject.repositories.maven {
138
+ name = "JSC"
139
+ setUrl(jscAndroidDir)
140
+ }
141
+
142
+ project.logger.debug("[${targetProject.name}] Added repository: JSC Maven (${jscAndroidDir.absolutePath})")
143
+ } else {
144
+ // JSC may not be installed if using Hermes exclusively
145
+ project.logger.debug("[${targetProject.name}] JSC Maven repository not found (not required for Hermes builds)")
146
+ }
147
+ }
148
+ }
@@ -0,0 +1,56 @@
1
+ package run.granite.gradle.config
2
+
3
+ import com.android.build.gradle.LibraryExtension
4
+ import org.gradle.api.Project
5
+ import run.granite.gradle.GraniteExtension
6
+
7
+ /**
8
+ * Configurator for Android resource packaging options.
9
+ *
10
+ * Configures:
11
+ * - Bundle compression settings (aaptOptions.noCompress)
12
+ * - Resource merging behavior
13
+ * - Asset packaging optimizations
14
+ */
15
+ class ResourceConfigurator(
16
+ private val project: Project,
17
+ private val extension: GraniteExtension,
18
+ ) {
19
+
20
+ /**
21
+ * Configures Android resource packaging options.
22
+ *
23
+ * Sets up:
24
+ * 1. Bundle compression control based on extension settings
25
+ * 2. No-compress patterns for JavaScript bundles
26
+ */
27
+ fun configure(androidExtension: LibraryExtension) {
28
+ configureAaptOptions(androidExtension)
29
+ project.logger.debug("Resource packaging configured")
30
+ }
31
+
32
+ /**
33
+ * Configures AAPT options for bundle compression.
34
+ *
35
+ * If bundle compression is disabled, adds bundle file patterns to noCompress list
36
+ * to prevent APK/AAR packaging from compressing already-compressed Hermes bytecode.
37
+ */
38
+ private fun configureAaptOptions(androidExtension: LibraryExtension) {
39
+ androidExtension.packaging {
40
+ resources {
41
+ // Don't compress JavaScript bundles if they're already gzip compressed
42
+ if (!extension.bundleCompressionEnabled.get()) {
43
+ excludes.add("**/*.bundle")
44
+ excludes.add("**/*.hbc")
45
+ }
46
+
47
+ // Always exclude .map files from compression
48
+ excludes.add("**/*.map")
49
+ }
50
+ }
51
+
52
+ project.logger.lifecycle(
53
+ "Bundle compression: ${if (extension.bundleCompressionEnabled.get()) "enabled" else "disabled"}",
54
+ )
55
+ }
56
+ }