@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,177 @@
1
+ package run.granite.gradle.models
2
+
3
+ /**
4
+ * Enriched model representing a React Native native module with computed properties.
5
+ * This is the primary model used by code generators.
6
+ *
7
+ * Constructed from AndroidDependencyConfig with validation:
8
+ * - packageImportPath and packageInstance must both be present or both be null
9
+ */
10
+ data class NativeModule(
11
+ val name: String,
12
+ val packageImportPath: String?,
13
+ val packageInstance: String?,
14
+ val dependencyConfiguration: String?,
15
+ val buildTypes: List<String>,
16
+ val libraryName: String?,
17
+ val componentDescriptors: List<String>,
18
+ val cmakeListsPath: String?,
19
+ val cxxModuleCMakeListsPath: String?,
20
+ val cxxModuleCMakeListsModuleName: String?,
21
+ val cxxModuleHeaderName: String?,
22
+ val isPureCxxDependency: Boolean,
23
+ /**
24
+ * True if the module's package.json has codegenConfig.includesGeneratedCode: true.
25
+ * Modules with this flag use their own codegen system (e.g., brick-codegen)
26
+ * and should be excluded from react-native-codegen based autolinking.
27
+ */
28
+ val includesGeneratedCode: Boolean = false,
29
+ ) {
30
+ init {
31
+ // Validate packageImportPath and packageInstance pairing
32
+ val hasImportPath = packageImportPath != null
33
+ val hasInstance = packageInstance != null
34
+ require(hasImportPath == hasInstance) {
35
+ "Module '$name': Failed to validate config - packageImportPath and packageInstance " +
36
+ "must both be present or both be null. Check module's react-native.config.js."
37
+ }
38
+ }
39
+
40
+ /**
41
+ * True if module has Java TurboModule implementation.
42
+ * Identified by non-null packageImportPath and packageInstance.
43
+ */
44
+ val hasJavaImplementation: Boolean
45
+ get() = packageImportPath != null && packageInstance != null
46
+
47
+ /**
48
+ * True if module has C++ TurboModule implementation.
49
+ * Identified by non-null cxxModuleHeaderName.
50
+ */
51
+ val hasCxxImplementation: Boolean
52
+ get() = cxxModuleHeaderName != null
53
+
54
+ /**
55
+ * True if module has any Fabric components.
56
+ * Identified by non-empty componentDescriptors array.
57
+ */
58
+ val hasFabricComponents: Boolean
59
+ get() = componentDescriptors.isNotEmpty()
60
+
61
+ /**
62
+ * True if module has CMake configuration.
63
+ * Identified by non-null cmakeListsPath or cxxModuleCMakeListsPath.
64
+ */
65
+ val hasCMakeConfiguration: Boolean
66
+ get() = cmakeListsPath != null || cxxModuleCMakeListsPath != null
67
+
68
+ /**
69
+ * True if module needs to be included in C++ autolinking.
70
+ * Required for C++ TurboModules or Fabric components.
71
+ */
72
+ val needsCppAutolinking: Boolean
73
+ get() = hasCxxImplementation || hasFabricComponents
74
+
75
+ /**
76
+ * Returns C++ identifier-safe name by sanitizing module name.
77
+ * Replaces invalid characters (spaces, hyphens, special chars) with underscores.
78
+ */
79
+ fun sanitizedName(): String = name.replace(Regex("[^a-zA-Z0-9_]"), "_")
80
+
81
+ /**
82
+ * Returns list of CMake entries for this module.
83
+ * Handles both standard CMakeLists.txt and separate cxxModule CMakeLists.txt.
84
+ */
85
+ fun cmakeEntries(): List<CMakeEntry> {
86
+ val entries = mutableListOf<CMakeEntry>()
87
+
88
+ // Add standard CMakeLists.txt entry
89
+ cmakeListsPath?.let { path ->
90
+ entries.add(
91
+ CMakeEntry(
92
+ sourcePath = path,
93
+ buildDirName = "${libraryName}_autolinked_build",
94
+ libraryTargets = listOfNotNull(
95
+ libraryName?.let { "react_codegen_$it" },
96
+ ),
97
+ ),
98
+ )
99
+ }
100
+
101
+ // Add C++ TurboModule CMakeLists.txt entry
102
+ cxxModuleCMakeListsPath?.let { path ->
103
+ entries.add(
104
+ CMakeEntry(
105
+ sourcePath = path,
106
+ buildDirName = "${libraryName}_cxx_autolinked_build",
107
+ libraryTargets = listOfNotNull(cxxModuleCMakeListsModuleName),
108
+ ),
109
+ )
110
+ }
111
+
112
+ return entries
113
+ }
114
+
115
+ companion object {
116
+ /**
117
+ * Constructs NativeModule from AndroidDependencyConfig with validation.
118
+ * Treats null and missing optional fields identically.
119
+ *
120
+ * @param name Module name
121
+ * @param config Android dependency configuration
122
+ * @param moduleRootPath Root path of the module (for reading package.json)
123
+ */
124
+ fun from(
125
+ name: String,
126
+ config: AndroidDependencyConfig,
127
+ moduleRootPath: String? = null,
128
+ ): NativeModule {
129
+ val includesGeneratedCode = moduleRootPath?.let {
130
+ readIncludesGeneratedCodeFlag(it)
131
+ } ?: false
132
+
133
+ return NativeModule(
134
+ name = name,
135
+ packageImportPath = config.packageImportPath,
136
+ packageInstance = config.packageInstance,
137
+ dependencyConfiguration = config.dependencyConfiguration,
138
+ buildTypes = config.buildTypes.orEmpty(),
139
+ libraryName = config.libraryName,
140
+ componentDescriptors = config.componentDescriptors.orEmpty(),
141
+ cmakeListsPath = config.cmakeListsPath,
142
+ cxxModuleCMakeListsPath = config.cxxModuleCMakeListsPath,
143
+ cxxModuleCMakeListsModuleName = config.cxxModuleCMakeListsModuleName,
144
+ cxxModuleHeaderName = config.cxxModuleHeaderName,
145
+ isPureCxxDependency = config.isPureCxxDependency ?: false,
146
+ includesGeneratedCode = includesGeneratedCode,
147
+ )
148
+ }
149
+
150
+ /**
151
+ * Reads codegenConfig.includesGeneratedCode from module's package.json.
152
+ * Returns false if the file doesn't exist or the flag is not set.
153
+ */
154
+ private fun readIncludesGeneratedCodeFlag(moduleRootPath: String): Boolean {
155
+ return try {
156
+ val packageJsonFile = java.io.File(moduleRootPath, "package.json")
157
+ if (!packageJsonFile.exists()) return false
158
+
159
+ val content = packageJsonFile.readText()
160
+ val gson = com.google.gson.Gson()
161
+
162
+ @Suppress("UNCHECKED_CAST")
163
+ val json = gson.fromJson(content, Map::class.java) as? Map<String, Any>
164
+ ?: return false
165
+
166
+ @Suppress("UNCHECKED_CAST")
167
+ val codegenConfig = json["codegenConfig"] as? Map<String, Any>
168
+ ?: return false
169
+
170
+ codegenConfig["includesGeneratedCode"] as? Boolean ?: false
171
+ } catch (e: Exception) {
172
+ // Fail gracefully - treat as false if we can't read the file
173
+ false
174
+ }
175
+ }
176
+ }
177
+ }
@@ -0,0 +1,194 @@
1
+ package run.granite.gradle.tasks
2
+
3
+ import org.gradle.api.DefaultTask
4
+ import org.gradle.api.file.DirectoryProperty
5
+ import org.gradle.api.file.RegularFileProperty
6
+ import org.gradle.api.provider.Property
7
+ import org.gradle.api.tasks.*
8
+ import java.io.File
9
+ import java.util.zip.GZIPOutputStream
10
+
11
+ /**
12
+ * Gradle task for packaging React Native assets and bundles.
13
+ *
14
+ * This task:
15
+ * 1. Copies the JavaScript bundle to Android assets directory
16
+ * 2. Compresses the bundle with gzip (if enabled)
17
+ * 3. Copies React Native drawable assets
18
+ * 4. Organizes assets per build variant
19
+ *
20
+ * The packaged assets are included in the final AAR file.
21
+ *
22
+ * Inputs:
23
+ * - JavaScript bundle file
24
+ * - React Native assets directory
25
+ * - Compression settings
26
+ *
27
+ * Outputs:
28
+ * - Assets in src/{variant}/assets/
29
+ * - Drawable resources in src/{variant}/res/
30
+ */
31
+ @CacheableTask
32
+ abstract class AssetPackagingTask : DefaultTask() {
33
+
34
+ /**
35
+ * JavaScript bundle file to package.
36
+ */
37
+ @get:InputFile
38
+ @get:PathSensitive(PathSensitivity.RELATIVE)
39
+ abstract val bundleFile: RegularFileProperty
40
+
41
+ /**
42
+ * Directory containing React Native drawable assets.
43
+ */
44
+ @get:InputDirectory
45
+ @get:PathSensitive(PathSensitivity.RELATIVE)
46
+ @get:Optional
47
+ abstract val assetsDir: DirectoryProperty
48
+
49
+ /**
50
+ * Output assets directory (src/{variant}/assets/).
51
+ */
52
+ @get:OutputDirectory
53
+ abstract val outputAssetsDir: DirectoryProperty
54
+
55
+ /**
56
+ * Output resources directory (src/{variant}/res/).
57
+ */
58
+ @get:OutputDirectory
59
+ @get:Optional
60
+ abstract val outputResDir: DirectoryProperty
61
+
62
+ /**
63
+ * Bundle asset name (e.g., "index.android.bundle").
64
+ */
65
+ @get:Input
66
+ abstract val bundleAssetName: Property<String>
67
+
68
+ /**
69
+ * Whether to compress the bundle with gzip.
70
+ */
71
+ @get:Input
72
+ abstract val compressionEnabled: Property<Boolean>
73
+
74
+ /**
75
+ * Variant name for logging.
76
+ */
77
+ @get:Input
78
+ abstract val variantName: Property<String>
79
+
80
+ init {
81
+ group = "granite"
82
+ description = "Packages React Native assets and bundles"
83
+ }
84
+
85
+ @TaskAction
86
+ fun execute() {
87
+ val variant = variantName.get()
88
+ logger.lifecycle("Packaging assets for variant: $variant")
89
+
90
+ // Package JavaScript bundle
91
+ packageBundle()
92
+
93
+ // Package drawable assets
94
+ packageDrawableAssets()
95
+
96
+ logger.lifecycle("Asset packaging complete for variant: $variant")
97
+ }
98
+
99
+ /**
100
+ * Packages the JavaScript bundle into the assets directory.
101
+ *
102
+ * If compression is enabled, creates a gzipped bundle file.
103
+ */
104
+ private fun packageBundle() {
105
+ val bundleSource = bundleFile.get().asFile
106
+ val bundleName = bundleAssetName.get()
107
+
108
+ if (!bundleSource.exists()) {
109
+ error(
110
+ """
111
+ |Bundle file not found: ${bundleSource.absolutePath}
112
+ |
113
+ |Solution: Ensure the bundle task completed successfully before packaging.
114
+ """.trimMargin(),
115
+ )
116
+ }
117
+
118
+ val outputDir = outputAssetsDir.get().asFile
119
+ outputDir.mkdirs()
120
+
121
+ if (compressionEnabled.get()) {
122
+ // Create gzipped bundle
123
+ val compressedBundle = File(outputDir, "$bundleName.gz")
124
+ logger.lifecycle("Compressing bundle: ${compressedBundle.name}")
125
+
126
+ bundleSource.inputStream().use { input ->
127
+ GZIPOutputStream(compressedBundle.outputStream()).use { gzipOut ->
128
+ input.copyTo(gzipOut)
129
+ }
130
+ }
131
+
132
+ logger.lifecycle("Created compressed bundle: ${compressedBundle.absolutePath}")
133
+ } else {
134
+ // Copy bundle without compression
135
+ val outputBundle = File(outputDir, bundleName)
136
+ bundleSource.copyTo(outputBundle, overwrite = true)
137
+ logger.lifecycle("Copied bundle: ${outputBundle.absolutePath}")
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Packages React Native drawable assets into the res directory.
143
+ *
144
+ * Copies drawable-* directories from the assets directory to the Android
145
+ * resources directory for inclusion in the AAR.
146
+ */
147
+ private fun packageDrawableAssets() {
148
+ if (!assetsDir.isPresent) {
149
+ logger.debug("No assets directory specified, skipping drawable packaging")
150
+ return
151
+ }
152
+
153
+ val assetsSource = assetsDir.get().asFile
154
+ if (!assetsSource.exists()) {
155
+ logger.debug("Assets directory does not exist: ${assetsSource.absolutePath}")
156
+ return
157
+ }
158
+
159
+ if (!outputResDir.isPresent) {
160
+ logger.debug("No output res directory specified, skipping drawable packaging")
161
+ return
162
+ }
163
+
164
+ val outputRes = outputResDir.get().asFile
165
+ outputRes.mkdirs()
166
+
167
+ // Find all drawable-* directories
168
+ val drawableDirs = assetsSource.listFiles { file ->
169
+ file.isDirectory && file.name.startsWith("drawable-")
170
+ } ?: emptyArray()
171
+
172
+ if (drawableDirs.isEmpty()) {
173
+ logger.debug("No drawable directories found in assets")
174
+ return
175
+ }
176
+
177
+ // Copy each drawable directory
178
+ for (drawableDir in drawableDirs) {
179
+ val targetDir = File(outputRes, drawableDir.name)
180
+ targetDir.mkdirs()
181
+
182
+ drawableDir.walk()
183
+ .filter { it.isFile }
184
+ .forEach { sourceFile ->
185
+ val relativePath = sourceFile.relativeTo(drawableDir).path
186
+ val targetFile = File(targetDir, relativePath)
187
+ targetFile.parentFile.mkdirs()
188
+ sourceFile.copyTo(targetFile, overwrite = true)
189
+ }
190
+
191
+ logger.lifecycle("Copied drawable assets: ${drawableDir.name}")
192
+ }
193
+ }
194
+ }