brick-module 0.1.1
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/BrickModule.podspec +56 -0
- package/android/brick_modules.gradle +280 -0
- package/android/build/generated/source/codegen/jni/CMakeLists.txt +14 -0
- package/android/build.gradle +144 -0
- package/android/gradle.properties +5 -0
- package/android/react-native-helpers.gradle +42 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/com/brickmodule/AndroidManifestNew.xml +2 -0
- package/android/src/main/java/com/brickmodule/BrickModuleBase.kt +23 -0
- package/android/src/main/java/com/brickmodule/BrickModulePackage.kt +87 -0
- package/android/src/main/java/com/brickmodule/BrickModuleRegistry.kt +224 -0
- package/android/src/main/resources/META-INF/gradle-plugins/com.brickmodule.properties +1 -0
- package/bin/brick-codegen.js +8 -0
- package/dist/BrickModule.d.ts +64 -0
- package/dist/BrickModule.js +91 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +18 -0
- package/ios/BrickModule/Internal/BrickCoreModule.swift +51 -0
- package/ios/BrickModule/Internal/BrickModuleRegistry.swift +138 -0
- package/ios/BrickModule/Public/BrickModule-Swift.h +339 -0
- package/package.json +75 -0
- package/podfile_helper.rb +176 -0
- package/src/BrickModule.ts +175 -0
- package/src/index.ts +37 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
+
folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
|
|
5
|
+
|
|
6
|
+
Pod::Spec.new do |s|
|
|
7
|
+
s.name = "BrickModule"
|
|
8
|
+
s.version = package["version"]
|
|
9
|
+
s.summary = package["description"]
|
|
10
|
+
s.homepage = package["homepage"]
|
|
11
|
+
s.license = package["license"]
|
|
12
|
+
s.authors = package["author"]
|
|
13
|
+
|
|
14
|
+
s.platforms = { :ios => '13.0' }
|
|
15
|
+
s.source = { :git => "https://github.com/toss/brick.git", :tag => "#{s.version}" }
|
|
16
|
+
s.source_files = "ios/**/*.{h,m,mm,swift}"
|
|
17
|
+
s.public_header_files = "ios/BrickModule/Public/*.h"
|
|
18
|
+
s.private_header_files = "ios/BrickModule/Internal/*.h"
|
|
19
|
+
|
|
20
|
+
# Swift 설정
|
|
21
|
+
s.swift_version = "5.0"
|
|
22
|
+
s.requires_arc = true
|
|
23
|
+
|
|
24
|
+
# Base configuration
|
|
25
|
+
base_config = {
|
|
26
|
+
"DEFINES_MODULE" => "YES",
|
|
27
|
+
"SWIFT_VERSION" => "5.0",
|
|
28
|
+
"OTHER_SWIFT_FLAGS" => "-enable-experimental-feature AccessLevelOnImport"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
|
|
32
|
+
# See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
|
|
33
|
+
if respond_to?(:install_modules_dependencies, true)
|
|
34
|
+
install_modules_dependencies(s)
|
|
35
|
+
s.pod_target_xcconfig = base_config
|
|
36
|
+
else
|
|
37
|
+
s.dependency "React-Core"
|
|
38
|
+
|
|
39
|
+
# Don't install the dependencies when we run `pod install` in the old architecture.
|
|
40
|
+
if ENV["RCT_NEW_ARCH_ENABLED"] == "1" then
|
|
41
|
+
s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
|
|
42
|
+
s.pod_target_xcconfig = base_config.merge({
|
|
43
|
+
"HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
|
|
44
|
+
"OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
|
|
45
|
+
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
|
|
46
|
+
})
|
|
47
|
+
s.dependency "React-Codegen"
|
|
48
|
+
s.dependency "RCT-Folly"
|
|
49
|
+
s.dependency "RCTRequired"
|
|
50
|
+
s.dependency "RCTTypeSafety"
|
|
51
|
+
s.dependency "ReactCommon/turbomodule/core"
|
|
52
|
+
else
|
|
53
|
+
s.pod_target_xcconfig = base_config
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Brick Modules Gradle Configuration
|
|
3
|
+
* This file provides automatic configuration for brick modules in React Native projects.
|
|
4
|
+
*
|
|
5
|
+
* Usage: Add to settings.gradle only:
|
|
6
|
+
* apply from: file("../node_modules/brick-module/android/brick_modules.gradle")
|
|
7
|
+
* applyBrickModules(settings)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import groovy.json.JsonSlurper
|
|
11
|
+
|
|
12
|
+
// =============================================================================
|
|
13
|
+
// Utility Functions - Store as ext properties for global access
|
|
14
|
+
// =============================================================================
|
|
15
|
+
|
|
16
|
+
ext.brickLog = { message ->
|
|
17
|
+
println("🧱 BrickModules: $message")
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
ext.resolveModule = { workingDir, moduleName ->
|
|
21
|
+
try {
|
|
22
|
+
def proc = ['node', '-p', "require.resolve('${moduleName}/package.json')"].execute(null, workingDir)
|
|
23
|
+
proc.waitFor()
|
|
24
|
+
|
|
25
|
+
if (proc.exitValue() == 0) {
|
|
26
|
+
def packageJsonPath = proc.text.trim()
|
|
27
|
+
if (packageJsonPath && !packageJsonPath.isEmpty()) {
|
|
28
|
+
return new File(packageJsonPath).parentFile
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
} catch (Exception e) {
|
|
32
|
+
// Silent fail - module not found
|
|
33
|
+
}
|
|
34
|
+
return null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
ext.readPackageJson = { File packageJsonFile ->
|
|
38
|
+
if (!packageJsonFile.exists()) return null
|
|
39
|
+
try {
|
|
40
|
+
return new JsonSlurper().parse(packageJsonFile)
|
|
41
|
+
} catch (Exception e) {
|
|
42
|
+
return null
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
ext.getAllDependencies = { packageJson ->
|
|
47
|
+
def deps = []
|
|
48
|
+
deps.addAll(packageJson.dependencies?.keySet() ?: [])
|
|
49
|
+
deps.addAll(packageJson.devDependencies?.keySet() ?: [])
|
|
50
|
+
return deps
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
ext.isBrickModule = { packageJson ->
|
|
54
|
+
return packageJson?.brickModule != null
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
ext.runBrickCodegen = { workingDir ->
|
|
58
|
+
def brickModuleDir = resolveModule(workingDir, "brick-module")
|
|
59
|
+
if (brickModuleDir == null) {
|
|
60
|
+
brickLog("brick-module not found, skipping code generation")
|
|
61
|
+
return false
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
def codegenPath = new File(brickModuleDir, "bin/brick-codegen.js")
|
|
65
|
+
if (!codegenPath.exists()) {
|
|
66
|
+
brickLog("brick-codegen not found at ${codegenPath}")
|
|
67
|
+
return false
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
def proc = ['node', codegenPath.absolutePath, '--platform', 'android'].execute(null, workingDir)
|
|
72
|
+
proc.waitFor()
|
|
73
|
+
return proc.exitValue() == 0
|
|
74
|
+
} catch (Exception e) {
|
|
75
|
+
brickLog("Failed to run brick-codegen: ${e.message}")
|
|
76
|
+
return false
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// =============================================================================
|
|
81
|
+
// Main Configuration
|
|
82
|
+
// =============================================================================
|
|
83
|
+
|
|
84
|
+
ext.applyBrickModules = { settings ->
|
|
85
|
+
def projectRoot = settings.rootDir.parentFile
|
|
86
|
+
def foundModules = []
|
|
87
|
+
def brickModulesData = [:]
|
|
88
|
+
|
|
89
|
+
// Read app's package.json
|
|
90
|
+
def appPackageJson = readPackageJson(new File(projectRoot, "package.json"))
|
|
91
|
+
if (appPackageJson == null) {
|
|
92
|
+
throw new GradleException("Could not find or parse package.json at ${projectRoot}")
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Store app package.json for later use
|
|
96
|
+
settings.ext.appPackageJson = appPackageJson
|
|
97
|
+
|
|
98
|
+
// Get all dependencies
|
|
99
|
+
def allDeps = getAllDependencies(appPackageJson)
|
|
100
|
+
|
|
101
|
+
// Always include brick-module if it exists
|
|
102
|
+
def brickModuleDir = resolveModule(projectRoot, "brick-module")
|
|
103
|
+
if (brickModuleDir != null) {
|
|
104
|
+
def androidDir = new File(brickModuleDir, "android")
|
|
105
|
+
if (androidDir.exists()) {
|
|
106
|
+
settings.include(":brick-module")
|
|
107
|
+
settings.project(":brick-module").projectDir = androidDir
|
|
108
|
+
foundModules.add("brick-module")
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Scan and include other brick modules
|
|
113
|
+
allDeps.each { depName ->
|
|
114
|
+
if (depName == "brick-module") return // Already handled
|
|
115
|
+
|
|
116
|
+
def moduleDir = resolveModule(projectRoot, depName)
|
|
117
|
+
if (moduleDir == null) return
|
|
118
|
+
|
|
119
|
+
def packageJson = readPackageJson(new File(moduleDir, "package.json"))
|
|
120
|
+
if (!isBrickModule(packageJson)) return
|
|
121
|
+
|
|
122
|
+
def androidDir = new File(moduleDir, "android")
|
|
123
|
+
if (androidDir.exists()) {
|
|
124
|
+
settings.include(":${depName}")
|
|
125
|
+
settings.project(":${depName}").projectDir = androidDir
|
|
126
|
+
foundModules.add(depName)
|
|
127
|
+
|
|
128
|
+
// Store module data for project configuration
|
|
129
|
+
if (packageJson.brickModule.android?.package) {
|
|
130
|
+
brickModulesData[depName] = [
|
|
131
|
+
moduleName: packageJson.brickModule.android.moduleName ?: depName,
|
|
132
|
+
packageName: packageJson.brickModule.android.package
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Check if codegen is needed
|
|
139
|
+
def brickDir = new File(settings.rootDir, ".brick")
|
|
140
|
+
def needsCodegen = !brickDir.exists() || !new File(brickDir, "src/main/kotlin/BrickModule.kt").exists()
|
|
141
|
+
|
|
142
|
+
if (needsCodegen) {
|
|
143
|
+
runBrickCodegen(projectRoot)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Include generated module
|
|
147
|
+
if (brickDir.exists()) {
|
|
148
|
+
settings.include(":brick-generated")
|
|
149
|
+
settings.project(":brick-generated").projectDir = brickDir
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!foundModules.isEmpty()) {
|
|
153
|
+
brickLog("Found modules: ${foundModules.join(', ')}")
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Configure app project after all projects are loaded
|
|
157
|
+
settings.gradle.projectsLoaded {
|
|
158
|
+
// Transfer data to root project
|
|
159
|
+
gradle.rootProject.ext.brickModulesData = brickModulesData
|
|
160
|
+
gradle.rootProject.ext.brickModulesList = foundModules
|
|
161
|
+
gradle.rootProject.ext.brickProjectRoot = projectRoot
|
|
162
|
+
configureAppProject(gradle)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Configure the app project
|
|
167
|
+
def configureAppProject(gradle) {
|
|
168
|
+
gradle.rootProject.subprojects { project ->
|
|
169
|
+
if (project.name == 'app') {
|
|
170
|
+
project.afterEvaluate {
|
|
171
|
+
configureBrickModules(project)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Configure brick modules for the app project
|
|
178
|
+
def configureBrickModules(project) {
|
|
179
|
+
def rootProject = project.rootProject
|
|
180
|
+
def brickModulesData = rootProject.ext.brickModulesData
|
|
181
|
+
def projectRoot = project.rootDir.parentFile
|
|
182
|
+
|
|
183
|
+
// Add source directory for generated code
|
|
184
|
+
project.android.sourceSets.main.java.srcDir "${project.buildDir}/generated/source/brick-provider"
|
|
185
|
+
|
|
186
|
+
// Create provider generation task
|
|
187
|
+
project.tasks.create('generateBrickModuleProvider') {
|
|
188
|
+
description = 'Generates BrickModuleProvider class for automatic module registration'
|
|
189
|
+
group = 'brick-module'
|
|
190
|
+
|
|
191
|
+
doLast {
|
|
192
|
+
generateProvider(project, brickModulesData)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Hook into build process
|
|
197
|
+
project.preBuild.dependsOn('generateBrickModuleProvider')
|
|
198
|
+
project.preBuild.doFirst {
|
|
199
|
+
def workingDir = projectRoot
|
|
200
|
+
|
|
201
|
+
// Run codegen inline
|
|
202
|
+
try {
|
|
203
|
+
def proc = ['node', '-p', "require.resolve('brick-module/package.json')"].execute(null, workingDir)
|
|
204
|
+
proc.waitFor()
|
|
205
|
+
|
|
206
|
+
if (proc.exitValue() == 0) {
|
|
207
|
+
def packageJsonPath = proc.text.trim()
|
|
208
|
+
if (packageJsonPath && !packageJsonPath.isEmpty()) {
|
|
209
|
+
def brickModuleDir = new File(packageJsonPath).parentFile
|
|
210
|
+
def codegenPath = new File(brickModuleDir, "bin/brick-codegen.js")
|
|
211
|
+
|
|
212
|
+
if (codegenPath.exists()) {
|
|
213
|
+
def codegenProc = ['node', codegenPath.absolutePath, '--platform', 'android'].execute(null, workingDir)
|
|
214
|
+
codegenProc.waitFor()
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
} catch (Exception e) {
|
|
219
|
+
println("🧱 BrickModules: Failed to run brick-codegen: ${e.message}")
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Add dependencies
|
|
224
|
+
project.dependencies {
|
|
225
|
+
// Add brick module dependencies
|
|
226
|
+
rootProject.subprojects.each { subproject ->
|
|
227
|
+
if (subproject.name.startsWith('brick-') || brickModulesData.containsKey(subproject.name)) {
|
|
228
|
+
add('implementation', subproject)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Add required Kotlin dependencies
|
|
233
|
+
add('implementation', "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
|
|
234
|
+
add('implementation', "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")
|
|
235
|
+
add('implementation', "com.google.code.gson:gson:2.8.9")
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Generate BrickModuleProvider.kt
|
|
240
|
+
def generateProvider(project, brickModulesData) {
|
|
241
|
+
def modules = brickModulesData.collect { name, data ->
|
|
242
|
+
[
|
|
243
|
+
name: data.moduleName,
|
|
244
|
+
className: data.moduleName + "Module",
|
|
245
|
+
packageName: data.packageName
|
|
246
|
+
]
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
def outputDir = project.file("${project.buildDir}/generated/source/brick-provider/com/brickmodule")
|
|
250
|
+
outputDir.mkdirs()
|
|
251
|
+
|
|
252
|
+
def imports = modules.collect { "import ${it.packageName}.${it.className}" }.join("\n")
|
|
253
|
+
def moduleInstances = modules.collect { " ${it.name}Module()" }.join(",\n")
|
|
254
|
+
def moduleNames = modules.collect { " \"${it.name}\"" }.join(",\n")
|
|
255
|
+
|
|
256
|
+
new File(outputDir, "BrickModuleProvider.kt").text = """// Auto-generated by brick_modules.gradle
|
|
257
|
+
package com.brickmodule
|
|
258
|
+
|
|
259
|
+
import com.brickmodule.BrickModuleRegistry
|
|
260
|
+
${imports}
|
|
261
|
+
|
|
262
|
+
object BrickModuleProvider {
|
|
263
|
+
fun registerAll() {
|
|
264
|
+
val modules = listOf<Any>(
|
|
265
|
+
${moduleInstances}
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
if (modules.isNotEmpty()) {
|
|
269
|
+
BrickModuleRegistry.register(modules)
|
|
270
|
+
println("🧱 BrickModuleProvider: Registered \${modules.size} modules")
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
fun getAvailableModules(): List<String> = listOf(
|
|
275
|
+
${moduleNames}
|
|
276
|
+
)
|
|
277
|
+
}
|
|
278
|
+
"""
|
|
279
|
+
}
|
|
280
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Dummy CMakeLists.txt for React Native autolinking
|
|
2
|
+
# This file is required by React Native's autolinking system
|
|
3
|
+
# The actual TurboModule implementation is handled by brick-codegen in user projects
|
|
4
|
+
cmake_minimum_required(VERSION 3.13)
|
|
5
|
+
set(CMAKE_VERBOSE_MAKEFILE on)
|
|
6
|
+
|
|
7
|
+
# Create a dummy source file
|
|
8
|
+
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/dummy.cpp "// Dummy file for BrickModuleSpec\n")
|
|
9
|
+
|
|
10
|
+
# Create an empty static library to satisfy React Native's build system
|
|
11
|
+
add_library(react_codegen_BrickModuleSpec STATIC ${CMAKE_CURRENT_BINARY_DIR}/dummy.cpp)
|
|
12
|
+
|
|
13
|
+
# Set necessary properties
|
|
14
|
+
target_include_directories(react_codegen_BrickModuleSpec PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
// Buildscript is evaluated before everything else so we can't use getExtOrDefault
|
|
3
|
+
def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["HotUpdater_kotlinVersion"]
|
|
4
|
+
|
|
5
|
+
repositories {
|
|
6
|
+
google()
|
|
7
|
+
mavenCentral()
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
dependencies {
|
|
11
|
+
classpath "com.android.tools.build:gradle:7.2.1"
|
|
12
|
+
// noinspection DifferentKotlinGradleVersion
|
|
13
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
14
|
+
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
def reactNativeArchitectures() {
|
|
19
|
+
def value = rootProject.getProperties().get("reactNativeArchitectures")
|
|
20
|
+
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
def isNewArchitectureEnabled() {
|
|
24
|
+
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
apply plugin: "com.android.library"
|
|
28
|
+
apply plugin: "kotlin-android"
|
|
29
|
+
apply from: "$projectDir/react-native-helpers.gradle"
|
|
30
|
+
|
|
31
|
+
if (isNewArchitectureEnabled()) {
|
|
32
|
+
apply plugin: "com.facebook.react"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
def getExtOrDefault(name) {
|
|
36
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["BrickModule_" + name]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
def getExtOrIntegerDefault(name) {
|
|
40
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["BrickModule_" + name]).toInteger()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
def supportsNamespace() {
|
|
44
|
+
def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
|
|
45
|
+
def major = parsed[0].toInteger()
|
|
46
|
+
def minor = parsed[1].toInteger()
|
|
47
|
+
|
|
48
|
+
// Namespace support was added in 7.3.0
|
|
49
|
+
return (major == 7 && minor >= 3) || major >= 8
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
android {
|
|
53
|
+
if (supportsNamespace()) {
|
|
54
|
+
namespace "com.brickmodule"
|
|
55
|
+
|
|
56
|
+
sourceSets {
|
|
57
|
+
main {
|
|
58
|
+
manifest.srcFile "src/main/AndroidManifest.xml"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
|
|
64
|
+
|
|
65
|
+
defaultConfig {
|
|
66
|
+
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
|
|
67
|
+
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
|
68
|
+
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
|
69
|
+
consumerProguardFiles 'proguard-rules.pro'
|
|
70
|
+
buildConfigField "long", "BUILD_TIMESTAMP", "${System.currentTimeMillis()}L"
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
buildFeatures {
|
|
74
|
+
buildConfig true
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
buildTypes {
|
|
78
|
+
release {
|
|
79
|
+
minifyEnabled false
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
lintOptions {
|
|
84
|
+
disable "GradleCompatible"
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
compileOptions {
|
|
88
|
+
sourceCompatibility JavaVersion.VERSION_1_8
|
|
89
|
+
targetCompatibility JavaVersion.VERSION_1_8
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
repositories {
|
|
94
|
+
mavenCentral()
|
|
95
|
+
google()
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
def kotlin_version = getExtOrDefault("kotlinVersion")
|
|
99
|
+
|
|
100
|
+
dependencies {
|
|
101
|
+
// For < 0.71, this will be from the local maven repo
|
|
102
|
+
// For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
|
|
103
|
+
//noinspection GradleDynamicVersion
|
|
104
|
+
if (project.ext.shouldConsumeReactNativeFromMavenCentral()) {
|
|
105
|
+
// noinspection GradleDynamicVersion
|
|
106
|
+
implementation 'com.facebook.react:react-android:+'
|
|
107
|
+
} else {
|
|
108
|
+
// noinspection GradleDynamicVersion
|
|
109
|
+
implementation 'com.facebook.react:react-native:+'
|
|
110
|
+
}
|
|
111
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
112
|
+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4"
|
|
113
|
+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Custom clean task that preserves CMakeLists.txt
|
|
117
|
+
task cleanButPreserveCMakeLists {
|
|
118
|
+
doFirst {
|
|
119
|
+
def cmakeFile = file("$buildDir/generated/source/codegen/jni/CMakeLists.txt")
|
|
120
|
+
def cmakeContent = ""
|
|
121
|
+
|
|
122
|
+
// Save CMakeLists.txt content if it exists
|
|
123
|
+
if (cmakeFile.exists()) {
|
|
124
|
+
cmakeContent = cmakeFile.text
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Store in project property for restoration
|
|
128
|
+
project.ext.savedCMakeContent = cmakeContent
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Override the clean task to preserve CMakeLists.txt
|
|
133
|
+
clean {
|
|
134
|
+
dependsOn cleanButPreserveCMakeLists
|
|
135
|
+
|
|
136
|
+
doLast {
|
|
137
|
+
// Restore CMakeLists.txt after clean
|
|
138
|
+
if (project.hasProperty('savedCMakeContent') && project.savedCMakeContent) {
|
|
139
|
+
def cmakeFile = file("$buildDir/generated/source/codegen/jni/CMakeLists.txt")
|
|
140
|
+
cmakeFile.parentFile.mkdirs()
|
|
141
|
+
cmakeFile.text = project.savedCMakeContent
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
def safeAppExtGet(prop, fallback) {
|
|
2
|
+
def appProject = rootProject.allprojects.find { it.plugins.hasPlugin('com.android.application') }
|
|
3
|
+
appProject?.ext?.has(prop) ? appProject.ext.get(prop) : fallback
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// Let's detect react-native's directory, it will be used to determine RN's version
|
|
7
|
+
// https://github.com/software-mansion/react-native-reanimated/blob/36c291a15880c78a94dd125c51484630546ceb7c/packages/react-native-reanimated/android/build.gradle#L73
|
|
8
|
+
def resolveReactNativeDirectory() {
|
|
9
|
+
def reactNativeLocation = safeAppExtGet("REACT_NATIVE_NODE_MODULES_DIR", null)
|
|
10
|
+
if (reactNativeLocation != null) {
|
|
11
|
+
return file(reactNativeLocation)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Fallback to node resolver for custom directory structures like monorepos.
|
|
15
|
+
def reactNativePackage = file(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim())
|
|
16
|
+
if (reactNativePackage.exists()) {
|
|
17
|
+
return reactNativePackage.parentFile
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
throw new GradleException(
|
|
21
|
+
"${project.name}: unable to resolve react-native location in " +
|
|
22
|
+
"node_modules. You should project extension property (in app/build.gradle) " +
|
|
23
|
+
"`REACT_NATIVE_NODE_MODULES_DIR` with path to react-native."
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// https://github.com/software-mansion/react-native-reanimated/blob/cda4627c3337c33674f05f755b7485165c6caca9/android/build.gradle#L199#L205
|
|
28
|
+
def reactNativeRootDir = resolveReactNativeDirectory()
|
|
29
|
+
|
|
30
|
+
def reactProperties = new Properties()
|
|
31
|
+
file("$reactNativeRootDir/ReactAndroid/gradle.properties").withInputStream { reactProperties.load(it) }
|
|
32
|
+
|
|
33
|
+
def REACT_NATIVE_VERSION = reactProperties.getProperty("VERSION_NAME")
|
|
34
|
+
def REACT_NATIVE_MINOR_VERSION = REACT_NATIVE_VERSION.startsWith("0.0.0-") ? 1000 : REACT_NATIVE_VERSION.split("\\.")[1].toInteger()
|
|
35
|
+
|
|
36
|
+
project.ext.resolveReactNativeDirectory = { ->
|
|
37
|
+
return resolveReactNativeDirectory()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
project.ext.shouldConsumeReactNativeFromMavenCentral = { ->
|
|
41
|
+
return REACT_NATIVE_MINOR_VERSION >= 71
|
|
42
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
package com.brickmodule
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Minimal interface that all Brick modules must implement
|
|
5
|
+
* Contains only essential properties for module identification
|
|
6
|
+
*/
|
|
7
|
+
interface BrickModuleBase {
|
|
8
|
+
/**
|
|
9
|
+
* The name of the module (required for registration)
|
|
10
|
+
*/
|
|
11
|
+
val moduleName: String
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Error types for Brick modules
|
|
16
|
+
*/
|
|
17
|
+
open class BrickModuleError(message: String, val errorCode: String) : Exception(message) {
|
|
18
|
+
class TypeMismatch(message: String) : BrickModuleError("Type mismatch: $message", "TYPE_ERROR")
|
|
19
|
+
class ExecutionError(message: String) : BrickModuleError("Execution error: $message", "EXECUTION_ERROR")
|
|
20
|
+
class InvalidDefinition(message: String) : BrickModuleError("Invalid definition: $message", "DEFINITION_ERROR")
|
|
21
|
+
class MethodNotFound(message: String) : BrickModuleError("Method not found: $message", "METHOD_NOT_FOUND")
|
|
22
|
+
class ModuleNotFound(message: String) : BrickModuleError("Module not found: $message", "MODULE_NOT_FOUND")
|
|
23
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
@file:Suppress("DEPRECATION")
|
|
2
|
+
|
|
3
|
+
package com.brickmodule
|
|
4
|
+
|
|
5
|
+
import com.facebook.react.TurboReactPackage
|
|
6
|
+
import com.facebook.react.bridge.NativeModule
|
|
7
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
8
|
+
import com.facebook.react.module.model.ReactModuleInfo
|
|
9
|
+
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
10
|
+
import java.util.HashMap
|
|
11
|
+
|
|
12
|
+
class BrickModulePackage : TurboReactPackage() {
|
|
13
|
+
|
|
14
|
+
init {
|
|
15
|
+
println("🔍 BrickModulePackage initialized")
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
override fun getModule(
|
|
19
|
+
name: String,
|
|
20
|
+
reactContext: ReactApplicationContext,
|
|
21
|
+
): NativeModule? {
|
|
22
|
+
println("🔍 BrickModulePackage.getModule called with name: $name")
|
|
23
|
+
return if (name == "BrickModule") {
|
|
24
|
+
// Try to find the generated BrickModule class
|
|
25
|
+
try {
|
|
26
|
+
// Try with app's class loader first
|
|
27
|
+
val classLoader = reactContext.applicationContext.classLoader
|
|
28
|
+
val generatedClass =
|
|
29
|
+
Class.forName("com.brickmodule.codegen.BrickModule", true, classLoader)
|
|
30
|
+
val constructor = generatedClass.getConstructor(ReactApplicationContext::class.java)
|
|
31
|
+
val instance = constructor.newInstance(reactContext) as NativeModule
|
|
32
|
+
println("✅ BrickModule successfully created: ${instance.javaClass.name}")
|
|
33
|
+
println("BrickModule ${instance.javaClass.methods.map { it.name }}")
|
|
34
|
+
instance
|
|
35
|
+
} catch (e: ClassNotFoundException) {
|
|
36
|
+
// Try with current thread class loader as fallback
|
|
37
|
+
try {
|
|
38
|
+
val currentClassLoader = Thread.currentThread().contextClassLoader
|
|
39
|
+
val generatedClass =
|
|
40
|
+
Class.forName("com.brickmodule.BrickModule", true, currentClassLoader)
|
|
41
|
+
val constructor =
|
|
42
|
+
generatedClass.getConstructor(ReactApplicationContext::class.java)
|
|
43
|
+
val instance = constructor.newInstance(reactContext) as NativeModule
|
|
44
|
+
println(
|
|
45
|
+
"✅ BrickModule successfully created with thread classloader: ${instance.javaClass.name}"
|
|
46
|
+
)
|
|
47
|
+
instance
|
|
48
|
+
} catch (e2: Exception) {
|
|
49
|
+
e.printStackTrace()
|
|
50
|
+
e2.printStackTrace()
|
|
51
|
+
throw RuntimeException(
|
|
52
|
+
"BrickModule not found in any classloader. Please run 'brick-codegen' to generate the bridge code. " +
|
|
53
|
+
"The generated BrickModule.kt should be in your app's source directory. " +
|
|
54
|
+
"Original error: ${e.message}, Fallback error: ${e2.message}"
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
} catch (e: Exception) {
|
|
58
|
+
e.printStackTrace()
|
|
59
|
+
throw RuntimeException("Failed to create BrickModule instance: ${e.message}", e)
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
null
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = ReactModuleInfoProvider {
|
|
67
|
+
println("🔍 BrickModulePackage.getReactModuleInfoProvider called")
|
|
68
|
+
val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
|
|
69
|
+
// Force TurboModule to true since we're using NativeBrickModuleSpec
|
|
70
|
+
val isTurboModule: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
|
|
71
|
+
moduleInfos["BrickModule"] =
|
|
72
|
+
ReactModuleInfo(
|
|
73
|
+
"BrickModule",
|
|
74
|
+
"com.brickmodule.codegen.BrickModule",
|
|
75
|
+
false, // canOverrideExistingModule
|
|
76
|
+
false, // needsEagerInit
|
|
77
|
+
true, // hasConstants
|
|
78
|
+
false, // isCxxModule
|
|
79
|
+
isTurboModule, // isTurboModule
|
|
80
|
+
)
|
|
81
|
+
moduleInfos
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
companion object {
|
|
85
|
+
const val NAME = "BrickModulePackage"
|
|
86
|
+
}
|
|
87
|
+
}
|