@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,578 @@
1
+ # Granite Gradle Plugin
2
+
3
+ **Granite** is a Gradle plugin that enables full React Native features in Android Library modules (AAR). The official `com.facebook.react` plugin only provides complete functionality for Application modules—Granite solves this limitation.
4
+
5
+ [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
6
+ [![Gradle Plugin Portal](https://img.shields.io/badge/Gradle-Plugin-brightgreen)](https://plugins.gradle.org/plugin/run.granite.library)
7
+
8
+ ---
9
+
10
+ ## Overview
11
+
12
+ Granite Gradle Plugin brings React Native to Android Library modules with full support for:
13
+ - **TurboModules & Fabric** (New Architecture required)
14
+ - **Autolinking** for native module discovery
15
+ - **JavaScript bundling** with Hermes bytecode compilation
16
+ - **AAR packaging** with bundled assets and native libraries
17
+ - **Multiple ReactHost instances** for modular architectures
18
+
19
+ **Key Use Cases:**
20
+ - Package React Native features as reusable AAR libraries
21
+ - Multi-module architecture with isolated React Native screens
22
+ - Share React Native components across multiple Android apps
23
+
24
+ ---
25
+
26
+ ## Feature Comparison
27
+
28
+ | Feature | com.facebook.react | run.granite.library |
29
+ |---------|-------------------|---------------------|
30
+ | Application module support | ✅ Yes | ❌ No (library only) |
31
+ | Library module support | ❌ No | ✅ Yes |
32
+ | TurboModules codegen | ✅ Yes | ✅ Yes |
33
+ | Fabric component codegen | ✅ Yes | ✅ Yes |
34
+ | Autolinking | ✅ Yes | ✅ Yes |
35
+ | Hermes | ✅ Yes | ✅ Yes (required) |
36
+ | JSC (JavaScriptCore) | ✅ Yes | ❌ No |
37
+ | JS bundling | ✅ Yes | ✅ Yes |
38
+ | AAR packaging | ❌ No | ✅ Yes |
39
+ | Multiple ReactHost | ❌ No | ✅ Yes |
40
+ | Old Architecture | ✅ Yes | ❌ No |
41
+ | New Architecture | ✅ Yes | ✅ Yes (required) |
42
+
43
+ **Summary:** Granite is specialized for library modules and requires New Architecture + Hermes.
44
+
45
+ ---
46
+
47
+ ## Installation
48
+
49
+ ### Quick Start
50
+
51
+ **Step 1: Apply the plugin**
52
+
53
+ **Kotlin DSL** (`build.gradle.kts`):
54
+ ```kotlin
55
+ plugins {
56
+ id("com.android.library")
57
+ id("run.granite.library") version "1.0.0"
58
+ }
59
+
60
+ granite {
61
+ entryFile.set("src/main/js/index.js")
62
+ }
63
+ ```
64
+
65
+ **Groovy DSL** (`build.gradle`):
66
+ ```groovy
67
+ plugins {
68
+ id 'com.android.library'
69
+ id 'run.granite.library' version '1.0.0'
70
+ }
71
+
72
+ granite {
73
+ entryFile = "src/main/js/index.js"
74
+ }
75
+ ```
76
+
77
+ **Step 2: Root project plugin** (optional, for monorepo compatibility)
78
+
79
+ ```kotlin
80
+ // root build.gradle.kts
81
+ plugins {
82
+ id("run.granite.rootproject") version "1.0.0"
83
+ }
84
+ ```
85
+
86
+ **Step 3: Build**
87
+
88
+ ```bash
89
+ ./gradlew :your-library:assembleDebug
90
+ ```
91
+
92
+ ### Using with npm (includeBuild)
93
+
94
+ For standard npm/node_modules setups, use `includeBuild` to resolve the plugin:
95
+
96
+ ```kotlin
97
+ // settings.gradle.kts
98
+ pluginManagement {
99
+ includeBuild("../node_modules/@granite-js/screen/gradle-plugin")
100
+ }
101
+ ```
102
+
103
+ ### Yarn PnP Users
104
+
105
+ If your project uses Yarn PnP (`nodeLinker: pnp`), you must unplug the screen package
106
+ before Gradle can resolve the plugin:
107
+
108
+ ```bash
109
+ yarn unplug @granite-js/screen
110
+ ```
111
+
112
+ Then reference the unplugged path:
113
+
114
+ ```kotlin
115
+ // settings.gradle.kts
116
+ pluginManagement {
117
+ includeBuild(".yarn/unplugged/@granite-js-screen-npm-VERSION-HASH/node_modules/@granite-js/screen/gradle-plugin")
118
+ }
119
+ ```
120
+
121
+ ---
122
+
123
+ ## Native Module Autolinking (Critical)
124
+
125
+ Granite lacks a Settings Plugin, so native module projects must be registered in `settings.gradle.kts`. **Without this, autolinking will fail** and native modules won't be linked.
126
+
127
+ ### Option 1: Use Official React Native Settings Plugin (Recommended)
128
+
129
+ ```kotlin
130
+ // settings.gradle.kts
131
+ pluginManagement {
132
+ repositories {
133
+ mavenCentral()
134
+ google()
135
+ }
136
+ }
137
+
138
+ plugins {
139
+ id("com.facebook.react.settings") version "0.84.0"
140
+ }
141
+
142
+ configure<com.facebook.react.ReactSettingsExtension> {
143
+ autolinkLibrariesFromCommand()
144
+ }
145
+ ```
146
+
147
+ **Why this works:** The official settings plugin registers all native module subprojects by executing `npx react-native config`, making them discoverable by Granite's autolinking task.
148
+
149
+ ### Option 2: Manual Registration
150
+
151
+ ```kotlin
152
+ // settings.gradle.kts
153
+ include(":react-native-gesture-handler")
154
+ project(":react-native-gesture-handler").projectDir =
155
+ file("node_modules/react-native-gesture-handler/android")
156
+
157
+ include(":react-native-reanimated")
158
+ project(":react-native-reanimated").projectDir =
159
+ file("node_modules/react-native-reanimated/android")
160
+ ```
161
+
162
+ **What happens without registration:**
163
+
164
+ When `graniteAutolinking` runs, it calls `project.findProject(projectPath)` for each native module. If the subproject isn't registered in `settings.gradle.kts`, this returns `null` and the module is skipped with a warning:
165
+
166
+ ```
167
+ [WARN] Native module ':react-native-gesture-handler' not found in project. Skipping autolinking.
168
+ ```
169
+
170
+ ---
171
+
172
+ ## PrivateReactExtension Compatibility
173
+
174
+ In monorepo environments where both Granite and the official React Native Gradle Plugin coexist, Granite ensures compatibility through reflection-based handling of `PrivateReactExtension`:
175
+
176
+ **How it works:**
177
+
178
+ 1. **Pre-creation:** Granite uses `Class.forName("com.facebook.react.internal.PrivateReactExtension")` to create the extension early
179
+ 2. **Path configuration:** Sets correct root paths for React Native and node_modules
180
+ 3. **Graceful coexistence:** If the extension already exists (created by official plugin), Granite only configures the root property
181
+ 4. **Safe degradation:** If `ClassNotFoundException` occurs, logs a warning and continues
182
+
183
+ **Important:** `PrivateReactExtension` is a React Native internal API and may change between major versions. Verify compatibility when upgrading React Native.
184
+
185
+ ```kotlin
186
+ // Internal implementation (informational only)
187
+ try {
188
+ val extensionClass = Class.forName("com.facebook.react.internal.PrivateReactExtension")
189
+ // Configure root paths...
190
+ } catch (e: ClassNotFoundException) {
191
+ logger.warn("PrivateReactExtension not found. Monorepo compatibility disabled.")
192
+ }
193
+ ```
194
+
195
+ ---
196
+
197
+ ## Configuration Reference
198
+
199
+ ### Library Module Plugin (`granite { }`)
200
+
201
+ Configure the plugin using the `granite` DSL block:
202
+
203
+ ```kotlin
204
+ granite {
205
+ // JavaScript entry point
206
+ entryFile.set("src/main/js/index.js")
207
+
208
+ // Bundle output name
209
+ bundleAssetName.set("index.android.bundle")
210
+
211
+ // Enable/disable bundle compression
212
+ bundleCompressionEnabled.set(true)
213
+
214
+ // Target ABIs (production: arm64-v8a, armeabi-v7a only)
215
+ nativeArchitectures.set(listOf("arm64-v8a", "armeabi-v7a"))
216
+
217
+ // React Native directory (auto-detected)
218
+ reactNativeDir.set(rootProject.file("node_modules/react-native"))
219
+
220
+ // Node modules directory (auto-detected)
221
+ nodeModulesDir.set(rootProject.file("node_modules"))
222
+
223
+ // Dev server configuration (optional)
224
+ devServerHost.set("localhost")
225
+ devServerPort.set(8081)
226
+ }
227
+ ```
228
+
229
+ ### DSL Property Reference
230
+
231
+ | Property | Type | Default | Description |
232
+ |----------|------|---------|-------------|
233
+ | `entryFile` | `Property<String>` | `"src/main/js/index.js"` | JavaScript entry file path |
234
+ | `bundleAssetName` | `Property<String>` | `"index.android.bundle"` | Bundle file name in AAR |
235
+ | `reactNativeDir` | `Property<File>` | `rootProject.file("node_modules/react-native")` | React Native installation directory |
236
+ | `nodeModulesDir` | `Property<File>` | `rootProject.file("node_modules")` | Node modules directory |
237
+ | `bundleCompressionEnabled` | `Property<Boolean>` | `true` | Enable bundle compression |
238
+ | `nativeArchitectures` | `ListProperty<String>` | `["armeabi-v7a", "arm64-v8a", "x86", "x86_64"]` | Android ABIs to build |
239
+ | `reactNativeVersion` | `Property<String>` | Auto-detected from `node_modules` | React Native version |
240
+ | `devServerHost` | `Property<String>` | `null` | Metro dev server host (development only) |
241
+ | `devServerPort` | `Property<Int>` | `null` | Metro dev server port (development only) |
242
+
243
+ **Production-optimized configuration:**
244
+
245
+ ```kotlin
246
+ granite {
247
+ entryFile.set("src/main/js/index.js")
248
+ bundleCompressionEnabled.set(true)
249
+ // Only include 64-bit and 32-bit ARM (most devices)
250
+ nativeArchitectures.set(listOf("arm64-v8a", "armeabi-v7a"))
251
+ }
252
+ ```
253
+
254
+ ### Root Project Plugin (`graniteRoot { }`)
255
+
256
+ Apply to the root project for monorepo dependency management:
257
+
258
+ ```kotlin
259
+ // root build.gradle.kts
260
+ plugins {
261
+ id("run.granite.rootproject") version "1.0.0"
262
+ }
263
+ ```
264
+
265
+ **What it does:**
266
+ - Configures dependency substitution for React Native libraries
267
+ - Sets up `PrivateReactExtension` for path resolution
268
+ - Ensures consistent React Native versions across modules
269
+
270
+ ---
271
+
272
+ ## Gradle Tasks
273
+
274
+ Granite provides 5 main task groups:
275
+
276
+ ### 1. Autolinking Tasks
277
+
278
+ **`graniteAutolinking`**
279
+ - Discovers native modules in `node_modules/`
280
+ - Generates `PackageList.java` (Java native module registry)
281
+ - Generates `autolinking.cpp/h` (C++ JNI registration)
282
+ - Generates `Android-autolinking.cmake` (CMake build configuration)
283
+ - **Output:** `build/generated/autolinking/`
284
+ - **Runs:** Before `preBuild`
285
+
286
+ ### 2. Codegen Tasks
287
+
288
+ **`graniteCodegenSchema`**
289
+ - Scans JavaScript specs for TurboModule/Fabric definitions
290
+ - Generates unified schema JSON
291
+ - **Output:** `build/generated/codegen/schema.json`
292
+
293
+ **`graniteCodegenArtifacts`**
294
+ - Generates Java interfaces and C++ implementations
295
+ - Creates TurboModule and Fabric component bindings
296
+ - **Output:** `build/generated/codegen/java/` and `jni/`
297
+ - **Runs:** Before Kotlin/Java compilation
298
+
299
+ ### 3. Bundling Tasks
300
+
301
+ **`graniteBundleDebug`** / **`graniteBundleRelease`**
302
+ - Runs Metro bundler to create JavaScript bundle
303
+ - Compiles to Hermes bytecode (`.hbc`)
304
+ - Applies minification and optimization (Release only)
305
+ - **Output:** `build/generated/assets/{variant}/index.android.bundle`
306
+
307
+ ### 4. Packaging Tasks
308
+
309
+ **`granitePackageAssetsDebug`** / **`granitePackageAssetsRelease`**
310
+ - Copies bundle to AAR asset directory
311
+ - Packages images and drawable resources
312
+ - **Output:** `src/{variant}/assets/`
313
+
314
+ ### 5. Task Execution Flow
315
+
316
+ ```
317
+ npm install
318
+
319
+ graniteAutolinking → preBuild → compileKotlin
320
+ ↓ ↓
321
+ graniteCodegenSchema externalNativeBuild (CMake)
322
+ ↓ ↓
323
+ graniteCodegenArtifacts ────────────┘
324
+
325
+ AAR Creation
326
+
327
+ graniteBundleRelease → granitePackageAssets
328
+ ```
329
+
330
+ **Manual bundle workflow:**
331
+
332
+ ```bash
333
+ # Generate JavaScript bundle with Hermes compilation
334
+ ./gradlew graniteBundleRelease
335
+
336
+ # Package assets into AAR
337
+ ./gradlew granitePackageAssetsRelease
338
+
339
+ # Build final AAR
340
+ ./gradlew bundleReleaseAar
341
+ ```
342
+
343
+ **One-liner for CI/CD:**
344
+
345
+ ```bash
346
+ ./gradlew graniteBundleRelease granitePackageAssetsRelease bundleReleaseAar
347
+ ```
348
+
349
+ ---
350
+
351
+ ## Version Compatibility
352
+
353
+ | Component | Minimum Version | Tested Version |
354
+ |-----------|----------------|----------------|
355
+ | React Native | 0.84.0 | 0.84.0+ |
356
+ | Android Gradle Plugin | 8.1.0 | 8.6.1 |
357
+ | Gradle | 8.0 | 8.9 |
358
+ | JDK | 17 | 17 |
359
+ | Kotlin | 1.9.0 | 2.1.21 |
360
+ | SoLoader | 0.12.1 | 0.12.1 |
361
+ | Hermes | (bundled with RN) | v96 |
362
+
363
+ **Required settings:**
364
+
365
+ ```properties
366
+ # gradle.properties
367
+ newArchEnabled=true
368
+ ```
369
+
370
+ ---
371
+
372
+ ## Architecture
373
+
374
+ ### Generated Code Structure
375
+
376
+ ```
377
+ build/
378
+ ├── generated/
379
+ │ ├── autolinking/
380
+ │ │ └── src/main/
381
+ │ │ ├── java/com/facebook/react/
382
+ │ │ │ └── PackageList.java # Java TurboModule registry
383
+ │ │ └── jni/
384
+ │ │ ├── autolinking.cpp # C++ JNI registration
385
+ │ │ ├── autolinking.h
386
+ │ │ └── Android-autolinking.cmake # CMake configuration
387
+ │ ├── codegen/
388
+ │ │ ├── schema.json # Unified TurboModule/Fabric schema
389
+ │ │ ├── java/ # Generated Java interfaces
390
+ │ │ └── jni/ # Generated C++ implementations
391
+ │ └── assets/
392
+ │ ├── debug/index.android.bundle # Debug JS bundle
393
+ │ └── release/index.android.bundle.hbc # Hermes bytecode
394
+ └── outputs/
395
+ └── aar/
396
+ └── your-library-release.aar # Final AAR with bundled assets
397
+ ```
398
+
399
+ ### AAR Contents
400
+
401
+ ```
402
+ your-library.aar
403
+ ├── jni/ # Native libraries
404
+ │ ├── arm64-v8a/
405
+ │ │ └── libreactnative.so # React Native runtime (64-bit ARM)
406
+ │ └── armeabi-v7a/
407
+ │ └── libreactnative.so # React Native runtime (32-bit ARM)
408
+ ├── assets/
409
+ │ └── index.android.bundle # Hermes bytecode bundle
410
+ ├── res/
411
+ │ └── drawable/ # Image assets from JS bundle
412
+ └── classes.jar # Compiled Kotlin/Java code
413
+ ```
414
+
415
+ ---
416
+
417
+ ## Troubleshooting
418
+
419
+ ### Common Issues
420
+
421
+ **Issue: Native module not linked**
422
+
423
+ ```
424
+ [WARN] Native module ':react-native-gesture-handler' not found in project. Skipping autolinking.
425
+ ```
426
+
427
+ **Solution:** Register native modules in `settings.gradle.kts` (see [Native Module Autolinking](#native-module-autolinking-critical))
428
+
429
+ ---
430
+
431
+ **Issue: `Unresolved reference: PackageList`**
432
+
433
+ ```
434
+ e: Unresolved reference: PackageList
435
+ ```
436
+
437
+ **Solution:** Run autolinking manually:
438
+ ```bash
439
+ ./gradlew graniteAutolinking
440
+ ls -la build/generated/autolinking/src/main/java/
441
+ ```
442
+
443
+ ---
444
+
445
+ **Issue: JavaScript entry file not found**
446
+
447
+ ```
448
+ JavaScript entry file not found: /path/to/index.js
449
+ ```
450
+
451
+ **Solution:** Ensure `entryFile` points to an existing file:
452
+ ```kotlin
453
+ granite {
454
+ entryFile.set("src/main/js/index.js") // Must exist!
455
+ }
456
+ ```
457
+
458
+ ---
459
+
460
+ **Issue: React Native not found**
461
+
462
+ ```
463
+ React Native directory not found: /path/to/node_modules/react-native
464
+ ```
465
+
466
+ **Solution:** Install dependencies:
467
+ ```bash
468
+ npm install # or yarn install
469
+ ```
470
+
471
+ ---
472
+
473
+ **Issue: Hermes compilation fails**
474
+
475
+ ```
476
+ Failed to compile bundle to Hermes bytecode
477
+ ```
478
+
479
+ **Solution:** Verify Hermes is enabled and check JavaScript syntax:
480
+ ```bash
481
+ # Test Metro bundler directly
482
+ npx react-native start --reset-cache
483
+ ```
484
+
485
+ ---
486
+
487
+ ### Debug Commands
488
+
489
+ ```bash
490
+ # View full stack traces
491
+ ./gradlew build --stacktrace
492
+
493
+ # Verbose logging
494
+ ./gradlew build --info
495
+
496
+ # Debug logging
497
+ ./gradlew build --debug
498
+
499
+ # Dependency tree
500
+ ./gradlew :your-library:dependencies
501
+
502
+ # Task execution order
503
+ ./gradlew :your-library:assembleDebug --dry-run
504
+ ```
505
+
506
+ ---
507
+
508
+ ## FAQ
509
+
510
+ **Q: Why is New Architecture required?**
511
+
512
+ A: Granite is optimized for multi-module architectures and multiple ReactHost scenarios. New Architecture's TurboModules and Fabric provide module isolation and type safety necessary for safe library module usage. Old Architecture lacks these guarantees.
513
+
514
+ ---
515
+
516
+ **Q: Can I use JSC instead of Hermes?**
517
+
518
+ A: No. Granite only supports Hermes. Hermes is optimized for React Native with faster startup, smaller bundle size, and lower memory usage. Hermes bytecode also provides source code protection.
519
+
520
+ ---
521
+
522
+ **Q: Can I use Granite in multiple library modules?**
523
+
524
+ A: Yes! Each library module must have a unique Android namespace. Each module gets its own ReactHost and codegen artifacts.
525
+
526
+ ```kotlin
527
+ // module-a/build.gradle.kts
528
+ android { namespace = "com.example.module.a" }
529
+
530
+ // module-b/build.gradle.kts
531
+ android { namespace = "com.example.module.b" }
532
+ ```
533
+
534
+ ---
535
+
536
+ **Q: Can I use Granite in application modules?**
537
+
538
+ A: No. Granite is library-module only. For application modules, use the official `com.facebook.react` plugin.
539
+
540
+ ---
541
+
542
+ **Q: Should I commit generated code to Git?**
543
+
544
+ A: No. All files in `build/` are auto-generated during build. Add `build/` to `.gitignore`.
545
+
546
+ ---
547
+
548
+ **Q: What's the recommended production configuration?**
549
+
550
+ A:
551
+ ```kotlin
552
+ granite {
553
+ entryFile.set("src/main/js/index.js")
554
+ bundleCompressionEnabled.set(true)
555
+ nativeArchitectures.set(listOf("arm64-v8a", "armeabi-v7a"))
556
+ }
557
+ ```
558
+
559
+ Exclude x86/x86_64 to reduce AAR size. Enable compression. Hermes is always enabled.
560
+
561
+ ---
562
+
563
+ ## License
564
+
565
+ Apache License 2.0
566
+
567
+ ---
568
+
569
+ ## Contributing
570
+
571
+ Contributions are welcome!
572
+
573
+ - **Issues:** [GitHub Issues](https://github.com/toss/granite/issues)
574
+ - **Discussions:** [GitHub Discussions](https://github.com/toss/granite/discussions)
575
+
576
+ ---
577
+
578
+ **Repository:** [https://github.com/toss/granite](https://github.com/toss/granite)
@@ -0,0 +1,97 @@
1
+ plugins {
2
+ `kotlin-dsl`
3
+ `java-gradle-plugin`
4
+ alias(libs.plugins.spotless)
5
+ }
6
+
7
+ java {
8
+ sourceCompatibility = JavaVersion.VERSION_17
9
+ targetCompatibility = JavaVersion.VERSION_17
10
+ }
11
+
12
+ kotlin {
13
+ jvmToolchain(17)
14
+ }
15
+
16
+ repositories {
17
+ google()
18
+ mavenCentral()
19
+ gradlePluginPortal()
20
+ }
21
+
22
+ spotless {
23
+ kotlin {
24
+ target("**/*.kt")
25
+ targetExclude("**/build/**/*.kt")
26
+ ktlint()
27
+ .editorConfigOverride(
28
+ mapOf(
29
+ "indent_size" to "2",
30
+ "continuation_indent_size" to "2",
31
+ "ktlint_standard_function-naming" to "disabled",
32
+ "ktlint_standard_no-wildcard-imports" to "disabled",
33
+ ),
34
+ )
35
+ trimTrailingWhitespace()
36
+ endWithNewline()
37
+ }
38
+ kotlinGradle {
39
+ target("**/*.gradle.kts")
40
+ targetExclude("**/build/**/*.gradle.kts")
41
+ ktlint()
42
+ .editorConfigOverride(
43
+ mapOf(
44
+ "indent_size" to "2",
45
+ "continuation_indent_size" to "2",
46
+ "ktlint_standard_function-naming" to "disabled",
47
+ "ktlint_standard_no-wildcard-imports" to "disabled",
48
+ ),
49
+ )
50
+ trimTrailingWhitespace()
51
+ endWithNewline()
52
+ }
53
+ }
54
+
55
+ dependencies {
56
+ // Gradle Plugin API
57
+ implementation(gradleApi())
58
+
59
+ // Kotlin standard library
60
+ implementation(kotlin("stdlib"))
61
+
62
+ // Android Gradle Plugin (for library module configuration)
63
+ compileOnly(libs.android.gradle.plugin)
64
+
65
+ // JSON parsing for package.json and configuration files
66
+ implementation(libs.gson)
67
+
68
+ // Testing
69
+ testImplementation(libs.junit.jupiter)
70
+ testImplementation(kotlin("test"))
71
+ testImplementation(gradleTestKit())
72
+ testImplementation(libs.android.gradle.plugin)
73
+ testImplementation(libs.assertj)
74
+ testImplementation(libs.junit.jupiter.api)
75
+ testImplementation(libs.junit.jupiter.params)
76
+ }
77
+
78
+ gradlePlugin {
79
+ plugins {
80
+ create("granitePlugin") {
81
+ id = "run.granite.library"
82
+ implementationClass = "run.granite.gradle.GranitePlugin"
83
+ displayName = "Granite Gradle Plugin"
84
+ description = "Gradle plugin for packaging React Native functionality in Android library modules"
85
+ }
86
+ create("graniteRootPlugin") {
87
+ id = "run.granite.rootproject"
88
+ implementationClass = "run.granite.gradle.GraniteRootProjectPlugin"
89
+ displayName = "Granite Root Project Plugin"
90
+ description = "Gradle plugin for configuring React Native dependency substitution at the root project level"
91
+ }
92
+ }
93
+ }
94
+
95
+ tasks.test {
96
+ useJUnitPlatform()
97
+ }
@@ -0,0 +1,17 @@
1
+ [versions]
2
+ agp = "8.13.2"
3
+ gson = "2.13.2"
4
+ junit = "5.8.2"
5
+ assertj = "3.24.2"
6
+ spotless = "8.1.0"
7
+
8
+ [libraries]
9
+ android-gradle-plugin = { module = "com.android.tools.build:gradle", version.ref = "agp" }
10
+ gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
11
+ junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
12
+ junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit" }
13
+ junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "junit" }
14
+ assertj = { module = "org.assertj:assertj-core", version.ref = "assertj" }
15
+
16
+ [plugins]
17
+ spotless = { id = "com.diffplug.spotless", version.ref = "spotless" }
@@ -0,0 +1,7 @@
1
+ distributionBase=GRADLE_USER_HOME
2
+ distributionPath=wrapper/dists
3
+ distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
4
+ networkTimeout=10000
5
+ validateDistributionUrl=false
6
+ zipStoreBase=GRADLE_USER_HOME
7
+ zipStorePath=wrapper/dists
@@ -0,0 +1,12 @@
1
+ # Gradle Plugin Properties
2
+ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
3
+ org.gradle.parallel=true
4
+ org.gradle.caching=true
5
+ org.gradle.configuration-cache=true
6
+
7
+ # Kotlin
8
+ kotlin.code.style=official
9
+
10
+ # Plugin Version
11
+ version=0.1.34
12
+ group=run.granite