@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.
- package/CHANGELOG.md +7 -0
- package/GraniteScreen.podspec +25 -0
- package/LICENSE +202 -0
- package/android/CMakeLists.txt +62 -0
- package/android/build.gradle +63 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/BundleEvaluator.cpp +27 -0
- package/android/src/main/cpp/BundleEvaluator.h +17 -0
- package/android/src/main/cpp/onLoad.cpp +6 -0
- package/android/src/main/kotlin/run/granite/BundleEvaluator.kt +50 -0
- package/android/src/main/kotlin/run/granite/BundleLoader.kt +40 -0
- package/android/src/main/kotlin/run/granite/DefaultBundleLoader.kt +20 -0
- package/android/src/main/kotlin/run/granite/DefaultErrorView.kt +58 -0
- package/android/src/main/kotlin/run/granite/DefaultLoadingView.kt +51 -0
- package/android/src/main/kotlin/run/granite/GraniteReactDelegate.kt +76 -0
- package/android/src/main/kotlin/run/granite/GraniteReactDelegateImpl.kt +448 -0
- package/android/src/main/kotlin/run/granite/GraniteReactHost.kt +113 -0
- package/android/src/main/kotlin/run/granite/ReactHostFactory.kt +106 -0
- package/gradle-plugin/LICENSE +201 -0
- package/gradle-plugin/README.md +578 -0
- package/gradle-plugin/build.gradle.kts +97 -0
- package/gradle-plugin/gradle/libs.versions.toml +17 -0
- package/gradle-plugin/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/gradle-plugin/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/gradle-plugin/gradle.properties +12 -0
- package/gradle-plugin/gradlew +248 -0
- package/gradle-plugin/gradlew.bat +93 -0
- package/gradle-plugin/settings.gradle.kts +1 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/GraniteExtension.kt +225 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/GranitePlugin.kt +784 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/GraniteRootExtension.kt +107 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/GraniteRootProjectPlugin.kt +290 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/BuildConfigConfigurator.kt +69 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/DependencyConfigurator.kt +232 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/DependencyCoordinates.kt +29 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/DevServerResourceConfigurator.kt +101 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/JniPackagingConfigurator.kt +160 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/NdkConfigurator.kt +135 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/RepositoryConfigurator.kt +148 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/ResourceConfigurator.kt +56 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/generators/CMakeGenerator.kt +105 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/generators/CppAutolinkingGenerator.kt +152 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/generators/EntryPointGenerator.kt +100 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/AndroidDependencyConfig.kt +23 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/AutolinkingConfig.kt +89 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/CMakeEntry.kt +47 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/NativeModule.kt +177 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/AssetPackagingTask.kt +194 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/AutolinkingTask.kt +431 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/BundleTask.kt +275 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/CodegenArtifactsTask.kt +218 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/CodegenSchemaTask.kt +186 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/AutolinkingParser.kt +128 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/ConflictDetector.kt +121 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/JdkValidator.kt +73 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/NodeExecutableFinder.kt +43 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/ReactNativeVersionReader.kt +329 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/TaskDependencyValidator.kt +198 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/GraniteExtensionTest.kt +191 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/GranitePluginTest.kt +156 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/GraniteRootProjectPluginTest.kt +87 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/BuildConfigConfiguratorTest.kt +115 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/DependencyConfiguratorTest.kt +338 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/DevServerResourceConfiguratorTest.kt +205 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/ResourceConfiguratorTest.kt +131 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/fixtures/NativeModuleFixtures.kt +67 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/generators/CMakeGeneratorTest.kt +71 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/generators/CppAutolinkingGeneratorTest.kt +344 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/generators/EntryPointGeneratorTest.kt +40 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/models/AutolinkingConfigTest.kt +350 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/models/CMakeEntryTest.kt +200 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/models/NativeModuleTest.kt +562 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/AssetPackagingTaskTest.kt +318 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/AutolinkingTaskTest.kt +89 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/BundleTaskTest.kt +68 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/CodegenTasksTest.kt +410 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/AutolinkingParserTest.kt +335 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/ConflictDetectorTest.kt +75 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/JdkValidatorTest.kt +88 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/ReactNativeVersionReaderTest.kt +585 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/TaskDependencyValidatorTest.kt +123 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/TaskTestUtils.kt +88 -0
- package/gradle-plugin/src/test/resources/fixtures/sample-rn-config.json +45 -0
- package/ios/BundleLoader/BundleEvaluator.h +16 -0
- package/ios/BundleLoader/BundleEvaluator.mm +76 -0
- package/ios/BundleLoader/BundleLoadable.swift +91 -0
- package/ios/GraniteBundleLoaderTypes.swift +7 -0
- package/ios/GraniteScreen.h +12 -0
- package/ios/ReactNativeHosting/DefaultViews.swift +138 -0
- package/ios/ReactNativeHosting/GraniteDefaultModuleProvider.h +24 -0
- package/ios/ReactNativeHosting/GraniteDefaultModuleProvider.mm +22 -0
- package/ios/ReactNativeHosting/GraniteHostingHelper.swift +103 -0
- package/ios/ReactNativeHosting/GraniteNativeFactory.swift +35 -0
- package/ios/ReactNativeHosting/GraniteNativeFactoryDelegateImpl.swift +30 -0
- package/ios/ReactNativeHosting/GraniteNativeFactoryImpl.swift +24 -0
- package/ios/ReactNativeHosting/GraniteReactHost.swift +39 -0
- package/ios/ReactNativeHosting/GraniteScreen-Bridging-Header.h +12 -0
- package/package.json +59 -0
- 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)
|
|
6
|
+
[](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" }
|
|
Binary file
|
|
@@ -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
|