@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,73 @@
|
|
|
1
|
+
package run.granite.gradle.utils
|
|
2
|
+
|
|
3
|
+
import org.gradle.api.JavaVersion
|
|
4
|
+
import org.gradle.api.Project
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Validates JDK version requirements for Granite plugin.
|
|
8
|
+
*
|
|
9
|
+
* The plugin requires JDK 17 or higher due to:
|
|
10
|
+
* - Modern Gradle features requiring Java 17
|
|
11
|
+
* - React Native Gradle plugin compatibility
|
|
12
|
+
* - Android Gradle Plugin 8.x requirements
|
|
13
|
+
*/
|
|
14
|
+
object JdkValidator {
|
|
15
|
+
|
|
16
|
+
private val MINIMUM_JDK_VERSION = JavaVersion.VERSION_17
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Validates that the current JDK meets minimum version requirements.
|
|
20
|
+
*
|
|
21
|
+
* @param project The Gradle project
|
|
22
|
+
* @throws IllegalStateException if JDK version is below minimum
|
|
23
|
+
*/
|
|
24
|
+
fun validate(project: Project) {
|
|
25
|
+
val currentJavaVersion = JavaVersion.current()
|
|
26
|
+
|
|
27
|
+
if (currentJavaVersion < MINIMUM_JDK_VERSION) {
|
|
28
|
+
error(
|
|
29
|
+
"""
|
|
30
|
+
|Granite plugin requires JDK 17 or higher.
|
|
31
|
+
|
|
|
32
|
+
|Current JDK version: $currentJavaVersion
|
|
33
|
+
|Required JDK version: $MINIMUM_JDK_VERSION or higher
|
|
34
|
+
|
|
|
35
|
+
|Solutions:
|
|
36
|
+
| 1. Update your JDK installation to version 17 or higher
|
|
37
|
+
| 2. Configure Gradle to use JDK 17+:
|
|
38
|
+
| - Set JAVA_HOME environment variable to JDK 17+ path
|
|
39
|
+
| - Or configure in gradle.properties:
|
|
40
|
+
| org.gradle.java.home=/path/to/jdk-17
|
|
41
|
+
| 3. Use Gradle toolchain to automatically download JDK 17:
|
|
42
|
+
| Add to build.gradle.kts:
|
|
43
|
+
| java {
|
|
44
|
+
| toolchain {
|
|
45
|
+
| languageVersion.set(JavaLanguageVersion.of(17))
|
|
46
|
+
| }
|
|
47
|
+
| }
|
|
48
|
+
|
|
|
49
|
+
|Project: ${project.path}
|
|
50
|
+
""".trimMargin(),
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
project.logger.lifecycle("Granite plugin: JDK validation passed (using JDK $currentJavaVersion)")
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Gets the current JDK version.
|
|
59
|
+
*/
|
|
60
|
+
fun getCurrentVersion(): JavaVersion = JavaVersion.current()
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Gets the minimum required JDK version.
|
|
64
|
+
*/
|
|
65
|
+
fun getMinimumVersion(): JavaVersion = MINIMUM_JDK_VERSION
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Checks if the current JDK meets minimum requirements without throwing an error.
|
|
69
|
+
*
|
|
70
|
+
* @return true if current JDK >= minimum version, false otherwise
|
|
71
|
+
*/
|
|
72
|
+
fun isCompatible(): Boolean = JavaVersion.current() >= MINIMUM_JDK_VERSION
|
|
73
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
package run.granite.gradle.utils
|
|
2
|
+
|
|
3
|
+
import java.io.File
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Utility for locating the Node.js executable.
|
|
7
|
+
*
|
|
8
|
+
* Searches the PATH environment variable to find the Node.js executable.
|
|
9
|
+
* Looks for node.exe on Windows and node on other platforms.
|
|
10
|
+
*/
|
|
11
|
+
object NodeExecutableFinder {
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Finds and returns the Node.js executable.
|
|
15
|
+
*
|
|
16
|
+
* Search order:
|
|
17
|
+
* 1. Scan directories listed in the PATH environment variable for the node executable
|
|
18
|
+
* 2. If not found in PATH, return just the node name so the system can resolve it (fallback)
|
|
19
|
+
*
|
|
20
|
+
* @return a [File] pointing to the Node.js executable
|
|
21
|
+
*/
|
|
22
|
+
fun findNodeExecutable(): File {
|
|
23
|
+
val nodeName = if (System.getProperty("os.name").startsWith("Windows")) {
|
|
24
|
+
"node.exe"
|
|
25
|
+
} else {
|
|
26
|
+
"node"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Search for the node executable in PATH directories
|
|
30
|
+
val pathEnv = System.getenv("PATH") ?: ""
|
|
31
|
+
val pathDirs = pathEnv.split(File.pathSeparator)
|
|
32
|
+
|
|
33
|
+
for (dir in pathDirs) {
|
|
34
|
+
val nodeFile = File(dir, nodeName)
|
|
35
|
+
if (nodeFile.exists() && nodeFile.canExecute()) {
|
|
36
|
+
return nodeFile
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Fallback: return just the node name and let the system resolve it
|
|
41
|
+
return File(nodeName)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
package run.granite.gradle.utils
|
|
2
|
+
|
|
3
|
+
import com.google.gson.Gson
|
|
4
|
+
import com.google.gson.JsonObject
|
|
5
|
+
import org.gradle.api.Project
|
|
6
|
+
import run.granite.gradle.config.DependencyCoordinates
|
|
7
|
+
import java.io.File
|
|
8
|
+
import java.util.Properties
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Reads and resolves React Native version information.
|
|
12
|
+
*
|
|
13
|
+
* This utility reads version information from:
|
|
14
|
+
* - node_modules/react-native/package.json
|
|
15
|
+
* - User-specified reactNativeDir in GraniteExtension
|
|
16
|
+
*
|
|
17
|
+
* Version resolution is used to:
|
|
18
|
+
* - Configure compatible dependency versions (hermes-android, soloader, etc.)
|
|
19
|
+
* - Locate React Native codegen and bundler tools
|
|
20
|
+
* - Validate minimum version requirements
|
|
21
|
+
*/
|
|
22
|
+
object ReactNativeVersionReader {
|
|
23
|
+
|
|
24
|
+
private const val MINIMUM_RN_VERSION = "0.81.0"
|
|
25
|
+
private const val INTERNAL_VERSION_NAME = "VERSION_NAME"
|
|
26
|
+
private const val INTERNAL_PUBLISHING_GROUP = "react.internal.publishingGroup"
|
|
27
|
+
private const val DEFAULT_INTERNAL_PUBLISHING_GROUP = "com.facebook.react"
|
|
28
|
+
|
|
29
|
+
// Hermes version property keys
|
|
30
|
+
private const val INTERNAL_HERMES_VERSION_NAME = "HERMES_VERSION_NAME"
|
|
31
|
+
private const val INTERNAL_HERMES_V1_VERSION_NAME = "HERMES_V1_VERSION_NAME"
|
|
32
|
+
private const val INTERNAL_HERMES_PUBLISHING_GROUP = "react.internal.hermesPublishingGroup"
|
|
33
|
+
private const val DEFAULT_HERMES_PUBLISHING_GROUP = "com.facebook.hermes"
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Reads React Native version from the specified directory.
|
|
37
|
+
*
|
|
38
|
+
* @param reactNativeDir The React Native installation directory
|
|
39
|
+
* @return The version string (e.g., "0.81.6")
|
|
40
|
+
* @throws IllegalStateException if version cannot be read
|
|
41
|
+
*/
|
|
42
|
+
fun readVersion(reactNativeDir: File): String {
|
|
43
|
+
val packageJsonFile = reactNativeDir.resolve("package.json")
|
|
44
|
+
|
|
45
|
+
if (!packageJsonFile.exists()) {
|
|
46
|
+
error(
|
|
47
|
+
"""
|
|
48
|
+
|React Native package.json not found.
|
|
49
|
+
|
|
|
50
|
+
|Expected location: ${packageJsonFile.absolutePath}
|
|
51
|
+
|React Native directory: ${reactNativeDir.absolutePath}
|
|
52
|
+
|
|
|
53
|
+
|Solutions:
|
|
54
|
+
| 1. Run 'npm install' or 'yarn install' to install React Native
|
|
55
|
+
| 2. Verify React Native is listed in your package.json dependencies
|
|
56
|
+
| 3. Configure the React Native directory in your build.gradle.kts:
|
|
57
|
+
| granite {
|
|
58
|
+
| reactNativeDir.set(file("path/to/node_modules/react-native"))
|
|
59
|
+
| }
|
|
60
|
+
""".trimMargin(),
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return try {
|
|
65
|
+
val packageJson = Gson().fromJson(packageJsonFile.readText(), JsonObject::class.java)
|
|
66
|
+
val version = packageJson.get("version")?.asString
|
|
67
|
+
?: error("No 'version' field found in ${packageJsonFile.absolutePath}")
|
|
68
|
+
|
|
69
|
+
version
|
|
70
|
+
} catch (e: Exception) {
|
|
71
|
+
error(
|
|
72
|
+
"""
|
|
73
|
+
|Failed to read React Native version from package.json.
|
|
74
|
+
|
|
|
75
|
+
|File: ${packageJsonFile.absolutePath}
|
|
76
|
+
|Error: ${e.message}
|
|
77
|
+
|
|
|
78
|
+
|Verify that the file is valid JSON and contains a 'version' field.
|
|
79
|
+
""".trimMargin(),
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Reads React Native version from the project's node_modules.
|
|
86
|
+
*
|
|
87
|
+
* @param project The Gradle project
|
|
88
|
+
* @return The version string
|
|
89
|
+
*/
|
|
90
|
+
fun readVersion(project: Project): String {
|
|
91
|
+
val reactNativeDir = project.rootProject.file("node_modules/react-native")
|
|
92
|
+
return readVersion(reactNativeDir)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Reads React Native version and Maven group ID.
|
|
97
|
+
*
|
|
98
|
+
* Reads gradle.properties first, falls back to package.json if not found.
|
|
99
|
+
* Nightly builds automatically have the -SNAPSHOT suffix added.
|
|
100
|
+
*
|
|
101
|
+
* @param reactNativeDir React Native installation directory
|
|
102
|
+
* @return Pair<version, groupId> (e.g., "0.81.6" to "com.facebook.react")
|
|
103
|
+
* @throws IllegalStateException if version cannot be read
|
|
104
|
+
*/
|
|
105
|
+
fun readVersionAndGroup(reactNativeDir: File): Pair<String, String> {
|
|
106
|
+
val gradlePropertiesFile = reactNativeDir.resolve("ReactAndroid/gradle.properties")
|
|
107
|
+
|
|
108
|
+
// Read gradle.properties first if it exists
|
|
109
|
+
if (gradlePropertiesFile.exists()) {
|
|
110
|
+
try {
|
|
111
|
+
val properties = Properties().apply {
|
|
112
|
+
gradlePropertiesFile.inputStream().use { load(it) }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
val versionStringFromFile = (properties[INTERNAL_VERSION_NAME] as? String).orEmpty()
|
|
116
|
+
|
|
117
|
+
// Add -SNAPSHOT suffix for nightly builds
|
|
118
|
+
val versionString = if (versionStringFromFile.startsWith("0.0.0") ||
|
|
119
|
+
"-nightly-" in versionStringFromFile
|
|
120
|
+
) {
|
|
121
|
+
"$versionStringFromFile-SNAPSHOT"
|
|
122
|
+
} else {
|
|
123
|
+
versionStringFromFile
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
val groupString = properties[INTERNAL_PUBLISHING_GROUP] as? String
|
|
127
|
+
?: DEFAULT_INTERNAL_PUBLISHING_GROUP
|
|
128
|
+
|
|
129
|
+
if (versionString.isEmpty()) {
|
|
130
|
+
// Error if VERSION_NAME is empty and no package.json exists
|
|
131
|
+
val packageJsonFile = reactNativeDir.resolve("package.json")
|
|
132
|
+
if (!packageJsonFile.exists()) {
|
|
133
|
+
error(
|
|
134
|
+
"""
|
|
135
|
+
|React Native gradle.properties does not contain VERSION_NAME.
|
|
136
|
+
|
|
|
137
|
+
|File: ${gradlePropertiesFile.absolutePath}
|
|
138
|
+
|
|
|
139
|
+
|This might indicate a corrupted React Native installation.
|
|
140
|
+
""".trimMargin(),
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
// Fallback to package.json if it exists
|
|
144
|
+
val version = readVersion(reactNativeDir)
|
|
145
|
+
return version to DEFAULT_INTERNAL_PUBLISHING_GROUP
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return versionString to groupString
|
|
149
|
+
} catch (e: IllegalStateException) {
|
|
150
|
+
// Re-throw explicitly thrown errors
|
|
151
|
+
throw e
|
|
152
|
+
} catch (e: Exception) {
|
|
153
|
+
// Fallback to package.json if gradle.properties read fails
|
|
154
|
+
val version = readVersion(reactNativeDir)
|
|
155
|
+
return version to DEFAULT_INTERNAL_PUBLISHING_GROUP
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Use package.json if gradle.properties doesn't exist (legacy approach)
|
|
160
|
+
val version = readVersion(reactNativeDir)
|
|
161
|
+
return version to DEFAULT_INTERNAL_PUBLISHING_GROUP
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Reads complete coordinate information for React Native and Hermes.
|
|
166
|
+
*
|
|
167
|
+
* - ReactAndroid/gradle.properties: RN version, React/Hermes groups
|
|
168
|
+
* - sdks/hermes-engine/version.properties: Hermes version, Hermes V1 version
|
|
169
|
+
*
|
|
170
|
+
* @param reactNativeDir React Native installation directory
|
|
171
|
+
* @return DependencyCoordinates object
|
|
172
|
+
* @throws IllegalStateException if version cannot be read
|
|
173
|
+
*/
|
|
174
|
+
fun readCoordinates(reactNativeDir: File): DependencyCoordinates {
|
|
175
|
+
val gradlePropertiesFile = reactNativeDir.resolve("ReactAndroid/gradle.properties")
|
|
176
|
+
val hermesVersionFile = reactNativeDir.resolve("sdks/hermes-engine/version.properties")
|
|
177
|
+
|
|
178
|
+
// Read React Native version and groups
|
|
179
|
+
val (reactVersion, reactGroup, hermesGroup) = readReactProperties(gradlePropertiesFile, reactNativeDir)
|
|
180
|
+
|
|
181
|
+
// Read Hermes versions
|
|
182
|
+
val (hermesVersion, hermesV1Version) = readHermesProperties(hermesVersionFile, reactVersion)
|
|
183
|
+
|
|
184
|
+
return DependencyCoordinates(
|
|
185
|
+
reactVersion = reactVersion,
|
|
186
|
+
hermesVersion = hermesVersion,
|
|
187
|
+
hermesV1Version = hermesV1Version,
|
|
188
|
+
reactGroup = reactGroup,
|
|
189
|
+
hermesGroup = hermesGroup,
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private fun readReactProperties(gradlePropertiesFile: File, reactNativeDir: File): Triple<String, String, String> {
|
|
194
|
+
if (!gradlePropertiesFile.exists()) {
|
|
195
|
+
// Fallback to package.json if gradle.properties doesn't exist
|
|
196
|
+
val version = readVersion(reactNativeDir)
|
|
197
|
+
return Triple(version, DEFAULT_INTERNAL_PUBLISHING_GROUP, DEFAULT_HERMES_PUBLISHING_GROUP)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
val properties = Properties().apply {
|
|
201
|
+
gradlePropertiesFile.inputStream().use { load(it) }
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
val versionStringFromFile = (properties[INTERNAL_VERSION_NAME] as? String).orEmpty()
|
|
205
|
+
val versionString = if (versionStringFromFile.startsWith("0.0.0") ||
|
|
206
|
+
"-nightly-" in versionStringFromFile
|
|
207
|
+
) {
|
|
208
|
+
"$versionStringFromFile-SNAPSHOT"
|
|
209
|
+
} else {
|
|
210
|
+
versionStringFromFile
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (versionString.isEmpty()) {
|
|
214
|
+
error("React Native gradle.properties does not contain VERSION_NAME.")
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
val reactGroup = properties[INTERNAL_PUBLISHING_GROUP] as? String
|
|
218
|
+
?: DEFAULT_INTERNAL_PUBLISHING_GROUP
|
|
219
|
+
val hermesGroup = properties[INTERNAL_HERMES_PUBLISHING_GROUP] as? String
|
|
220
|
+
?: DEFAULT_HERMES_PUBLISHING_GROUP
|
|
221
|
+
|
|
222
|
+
return Triple(versionString, reactGroup, hermesGroup)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
private fun readHermesProperties(hermesVersionFile: File, fallbackVersion: String): Pair<String, String> {
|
|
226
|
+
if (!hermesVersionFile.exists()) {
|
|
227
|
+
// Fallback to RN version if Hermes version file doesn't exist (RN 0.83 or lower compatibility)
|
|
228
|
+
return fallbackVersion to fallbackVersion
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
val properties = Properties().apply {
|
|
232
|
+
hermesVersionFile.inputStream().use { load(it) }
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
val hermesVersionFromFile = (properties[INTERNAL_HERMES_VERSION_NAME] as? String).orEmpty()
|
|
236
|
+
val hermesVersion = if (hermesVersionFromFile.startsWith("0.0.0") ||
|
|
237
|
+
"-commitly-" in hermesVersionFromFile
|
|
238
|
+
) {
|
|
239
|
+
"$hermesVersionFromFile-SNAPSHOT"
|
|
240
|
+
} else {
|
|
241
|
+
hermesVersionFromFile
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
val hermesV1Version = (properties[INTERNAL_HERMES_V1_VERSION_NAME] as? String).orEmpty()
|
|
245
|
+
|
|
246
|
+
// Fallback if both are empty
|
|
247
|
+
return if (hermesVersion.isEmpty() && hermesV1Version.isEmpty()) {
|
|
248
|
+
fallbackVersion to fallbackVersion
|
|
249
|
+
} else {
|
|
250
|
+
hermesVersion to hermesV1Version
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Validates that the React Native version meets minimum requirements.
|
|
256
|
+
*
|
|
257
|
+
* @param version The React Native version string
|
|
258
|
+
* @throws IllegalStateException if version is below minimum
|
|
259
|
+
*/
|
|
260
|
+
fun validateVersion(version: String) {
|
|
261
|
+
if (!isVersionCompatible(version, MINIMUM_RN_VERSION)) {
|
|
262
|
+
error(
|
|
263
|
+
"""
|
|
264
|
+
|React Native version $version is not supported.
|
|
265
|
+
|
|
|
266
|
+
|Current version: $version
|
|
267
|
+
|Minimum required version: $MINIMUM_RN_VERSION
|
|
268
|
+
|
|
|
269
|
+
|Solution: Update React Native to version $MINIMUM_RN_VERSION or higher:
|
|
270
|
+
| npm install react-native@^$MINIMUM_RN_VERSION
|
|
271
|
+
| # or
|
|
272
|
+
| yarn add react-native@^$MINIMUM_RN_VERSION
|
|
273
|
+
""".trimMargin(),
|
|
274
|
+
)
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Compares two semantic version strings.
|
|
280
|
+
*
|
|
281
|
+
* @param current The current version
|
|
282
|
+
* @param minimum The minimum required version
|
|
283
|
+
* @return true if current >= minimum, false otherwise
|
|
284
|
+
*/
|
|
285
|
+
private fun isVersionCompatible(current: String, minimum: String): Boolean {
|
|
286
|
+
// Parse semantic versions (major.minor.patch)
|
|
287
|
+
val currentParts = parseVersion(current)
|
|
288
|
+
val minimumParts = parseVersion(minimum)
|
|
289
|
+
|
|
290
|
+
// Compare major.minor.patch
|
|
291
|
+
for (i in 0 until 3) {
|
|
292
|
+
val currentPart = currentParts.getOrNull(i) ?: 0
|
|
293
|
+
val minimumPart = minimumParts.getOrNull(i) ?: 0
|
|
294
|
+
|
|
295
|
+
when {
|
|
296
|
+
currentPart > minimumPart -> return true
|
|
297
|
+
currentPart < minimumPart -> return false
|
|
298
|
+
// Equal, continue to next part
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return true // Versions are equal
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Parses a semantic version string into major, minor, patch components.
|
|
307
|
+
*
|
|
308
|
+
* Supports version formats:
|
|
309
|
+
* - X.Y.Z (stable)
|
|
310
|
+
* - X.Y.Z-rc.N (release candidate)
|
|
311
|
+
* - X.Y.Z-SNAPSHOT (snapshot)
|
|
312
|
+
* - X.Y.Z-nightly-YYYYMMDD (nightly)
|
|
313
|
+
*
|
|
314
|
+
* @param version The version string (e.g., "0.81.6" or "0.81.6-rc.0")
|
|
315
|
+
* @return List of [major, minor, patch] as integers
|
|
316
|
+
*/
|
|
317
|
+
private fun parseVersion(version: String): List<Int> {
|
|
318
|
+
// Remove pre-release suffix (e.g., "-rc.0", "-SNAPSHOT", "-nightly-20240101")
|
|
319
|
+
val versionCore = version.split("-")[0]
|
|
320
|
+
|
|
321
|
+
return versionCore.split(".")
|
|
322
|
+
.mapNotNull { it.toIntOrNull() }
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Gets the minimum supported React Native version.
|
|
327
|
+
*/
|
|
328
|
+
fun getMinimumVersion(): String = MINIMUM_RN_VERSION
|
|
329
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
package run.granite.gradle.utils
|
|
2
|
+
|
|
3
|
+
import org.gradle.api.Project
|
|
4
|
+
import org.gradle.api.Task
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Validates task dependency structure for Granite plugin.
|
|
8
|
+
*
|
|
9
|
+
* Ensures that:
|
|
10
|
+
* - Codegen tasks complete before compilation tasks
|
|
11
|
+
* - Autolinking completes before codegen
|
|
12
|
+
* - Bundle tasks complete before per-variant packaging tasks
|
|
13
|
+
* - All tasks have proper input/output relationships
|
|
14
|
+
*
|
|
15
|
+
* This validator prevents build failures caused by incorrect task ordering.
|
|
16
|
+
*/
|
|
17
|
+
object TaskDependencyValidator {
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Validates that autolinking completes before codegen.
|
|
21
|
+
*
|
|
22
|
+
* @param project The Gradle project
|
|
23
|
+
* @param autolinkingTask The autolinking task
|
|
24
|
+
* @param codegenTask The codegen task
|
|
25
|
+
*/
|
|
26
|
+
fun validateAutolinkingBeforeCodegen(
|
|
27
|
+
project: Project,
|
|
28
|
+
autolinkingTask: Task,
|
|
29
|
+
codegenTask: Task,
|
|
30
|
+
) {
|
|
31
|
+
if (!codegenTask.dependsOn.contains(autolinkingTask) &&
|
|
32
|
+
!codegenTask.mustRunAfter.getDependencies(codegenTask).contains(autolinkingTask)
|
|
33
|
+
) {
|
|
34
|
+
project.logger.warn(
|
|
35
|
+
"""
|
|
36
|
+
|⚠️ Task dependency warning: Codegen task '${codegenTask.name}' should depend on
|
|
37
|
+
|autolinking task '${autolinkingTask.name}'.
|
|
38
|
+
|
|
|
39
|
+
|This may cause build failures if codegen runs before autolinking completes.
|
|
40
|
+
""".trimMargin(),
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Validates that codegen completes before compilation.
|
|
47
|
+
*
|
|
48
|
+
* @param project The Gradle project
|
|
49
|
+
* @param codegenTask The codegen task
|
|
50
|
+
* @param compileTask The compilation task
|
|
51
|
+
*/
|
|
52
|
+
fun validateCodegenBeforeCompilation(
|
|
53
|
+
project: Project,
|
|
54
|
+
codegenTask: Task,
|
|
55
|
+
compileTask: Task,
|
|
56
|
+
) {
|
|
57
|
+
if (!compileTask.dependsOn.contains(codegenTask) &&
|
|
58
|
+
!compileTask.mustRunAfter.getDependencies(compileTask).contains(codegenTask)
|
|
59
|
+
) {
|
|
60
|
+
project.logger.warn(
|
|
61
|
+
"""
|
|
62
|
+
|⚠️ Task dependency warning: Compilation task '${compileTask.name}' should depend on
|
|
63
|
+
|codegen task '${codegenTask.name}'.
|
|
64
|
+
|
|
|
65
|
+
|This may cause compilation errors if codegen outputs are not available.
|
|
66
|
+
""".trimMargin(),
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Validates that bundle tasks complete before packaging tasks.
|
|
73
|
+
*
|
|
74
|
+
* @param project The Gradle project
|
|
75
|
+
* @param bundleTask The bundle task
|
|
76
|
+
* @param packageTask The packaging task
|
|
77
|
+
*/
|
|
78
|
+
fun validateBundleBeforePackaging(
|
|
79
|
+
project: Project,
|
|
80
|
+
bundleTask: Task,
|
|
81
|
+
packageTask: Task,
|
|
82
|
+
) {
|
|
83
|
+
if (!packageTask.dependsOn.contains(bundleTask) &&
|
|
84
|
+
!packageTask.mustRunAfter.getDependencies(packageTask).contains(bundleTask)
|
|
85
|
+
) {
|
|
86
|
+
project.logger.warn(
|
|
87
|
+
"""
|
|
88
|
+
|⚠️ Task dependency warning: Packaging task '${packageTask.name}' should depend on
|
|
89
|
+
|bundle task '${bundleTask.name}'.
|
|
90
|
+
|
|
|
91
|
+
|This may cause missing bundle assets in the final AAR.
|
|
92
|
+
""".trimMargin(),
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Validates task input/output relationships.
|
|
99
|
+
*
|
|
100
|
+
* Ensures that task outputs are properly declared and consumed by dependent tasks.
|
|
101
|
+
*
|
|
102
|
+
* @param project The Gradle project
|
|
103
|
+
* @param task The task to validate
|
|
104
|
+
*/
|
|
105
|
+
fun validateTaskInputsOutputs(project: Project, task: Task) {
|
|
106
|
+
val hasInputs = task.inputs.hasInputs
|
|
107
|
+
val hasOutputs = task.outputs.files.isEmpty.not()
|
|
108
|
+
|
|
109
|
+
if (!hasInputs && !hasOutputs) {
|
|
110
|
+
project.logger.warn(
|
|
111
|
+
"""
|
|
112
|
+
|⚠️ Task configuration warning: Task '${task.name}' has no declared inputs or outputs.
|
|
113
|
+
|
|
|
114
|
+
|This prevents Gradle's up-to-date checking and caching from working correctly.
|
|
115
|
+
|Consider declaring task inputs and outputs for better build performance.
|
|
116
|
+
""".trimMargin(),
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Validates that all required tasks are registered.
|
|
123
|
+
*
|
|
124
|
+
* @param project The Gradle project
|
|
125
|
+
* @param requiredTaskNames List of task names that must be registered
|
|
126
|
+
* @return List of missing task names
|
|
127
|
+
*/
|
|
128
|
+
fun validateRequiredTasks(project: Project, requiredTaskNames: List<String>): List<String> {
|
|
129
|
+
val missingTasks = mutableListOf<String>()
|
|
130
|
+
|
|
131
|
+
for (taskName in requiredTaskNames) {
|
|
132
|
+
if (project.tasks.findByName(taskName) == null) {
|
|
133
|
+
missingTasks.add(taskName)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (missingTasks.isNotEmpty()) {
|
|
138
|
+
project.logger.warn(
|
|
139
|
+
"""
|
|
140
|
+
|⚠️ Task registration warning: Required tasks are missing:
|
|
141
|
+
|${missingTasks.joinToString("\n") { " - $it" }}
|
|
142
|
+
|
|
|
143
|
+
|This may indicate incomplete plugin configuration.
|
|
144
|
+
""".trimMargin(),
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return missingTasks
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Validates that per-variant tasks only run after shared infrastructure tasks.
|
|
153
|
+
*
|
|
154
|
+
* Ensures codegen and autolinking (shared) complete before variant-specific bundling.
|
|
155
|
+
*
|
|
156
|
+
* @param project The Gradle project
|
|
157
|
+
* @param infrastructureTask Shared infrastructure task (codegen, autolinking)
|
|
158
|
+
* @param variantTask Per-variant task (bundle, package)
|
|
159
|
+
*/
|
|
160
|
+
fun validateInfrastructureBeforeVariant(
|
|
161
|
+
project: Project,
|
|
162
|
+
infrastructureTask: Task,
|
|
163
|
+
variantTask: Task,
|
|
164
|
+
) {
|
|
165
|
+
if (!variantTask.dependsOn.contains(infrastructureTask) &&
|
|
166
|
+
!variantTask.mustRunAfter.getDependencies(variantTask).contains(infrastructureTask)
|
|
167
|
+
) {
|
|
168
|
+
project.logger.debug(
|
|
169
|
+
"""
|
|
170
|
+
|Task dependency info: Variant task '${variantTask.name}' should typically depend on
|
|
171
|
+
|infrastructure task '${infrastructureTask.name}'.
|
|
172
|
+
""".trimMargin(),
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Logs task dependency graph for debugging.
|
|
179
|
+
*
|
|
180
|
+
* @param project The Gradle project
|
|
181
|
+
* @param task The task to log dependencies for
|
|
182
|
+
*/
|
|
183
|
+
fun logTaskDependencies(project: Project, task: Task) {
|
|
184
|
+
if (project.logger.isDebugEnabled) {
|
|
185
|
+
val dependencies = task.dependsOn.joinToString(", ") { it.toString() }
|
|
186
|
+
val mustRunAfter = task.mustRunAfter.getDependencies(task)
|
|
187
|
+
.joinToString(", ") { it.name }
|
|
188
|
+
|
|
189
|
+
project.logger.debug(
|
|
190
|
+
"""
|
|
191
|
+
|Task: ${task.name}
|
|
192
|
+
| Dependencies: $dependencies
|
|
193
|
+
| Must run after: $mustRunAfter
|
|
194
|
+
""".trimMargin(),
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|