brick-module 0.4.0 → 0.5.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.
@@ -102,7 +102,7 @@ ext.isBrickModule = { packageJson ->
102
102
  return packageJson?.brickModule != null
103
103
  }
104
104
 
105
- ext.runBrickCodegen = { workingDir ->
105
+ ext.runBrickCodegen = { workingDir, options = [:] ->
106
106
  def brickModuleDir = resolveModule(workingDir, "brick-module")
107
107
  if (brickModuleDir == null) {
108
108
  brickLog("brick-module not found, skipping code generation")
@@ -117,7 +117,18 @@ ext.runBrickCodegen = { workingDir ->
117
117
 
118
118
  try {
119
119
  def wdPath = (workingDir instanceof File) ? workingDir.absolutePath : workingDir
120
- def proc = ['node', codegenPath.absolutePath, '--platform', 'android', '--projectRoot', wdPath].execute(null, workingDir)
120
+ def codegenArgs = [
121
+ 'node',
122
+ codegenPath.absolutePath,
123
+ '--platform',
124
+ 'android',
125
+ '--projectRoot',
126
+ wdPath
127
+ ]
128
+ if (options.noClean == true) {
129
+ codegenArgs.add('--no-clean')
130
+ }
131
+ def proc = codegenArgs.execute(null, workingDir)
121
132
  proc.waitFor()
122
133
  return proc.exitValue() == 0
123
134
  } catch (Exception e) {
@@ -217,10 +228,12 @@ ext.applyBrickModules = { settings, args ->
217
228
  def androidBrickPath = getBrickAndroidPath(projectRoot)
218
229
  def brickDir = new File(androidBrickPath)
219
230
  def needsCodegen = !brickDir.exists() || !new File(brickDir, "src/main/kotlin/BrickModule.kt").exists()
220
-
231
+ def codegenAttempted = false
221
232
  if (needsCodegen) {
222
- runBrickCodegen(projectRoot)
233
+ codegenAttempted = true
234
+ runBrickCodegen(projectRoot, [noClean: true])
223
235
  }
236
+ settings.ext.brickCodegenRan = codegenAttempted
224
237
 
225
238
  // Include generated module (use configured path)
226
239
  if (brickDir.exists()) {
@@ -239,6 +252,8 @@ ext.applyBrickModules = { settings, args ->
239
252
  gradle.rootProject.ext.brickModulesList = foundModules
240
253
  gradle.rootProject.ext.brickProjectRoot = projectRoot
241
254
  gradle.rootProject.ext.brickAppProjectName = appProjectName
255
+ gradle.rootProject.ext.brickCodegenRanInBuild =
256
+ settings.ext.has("brickCodegenRan") ? settings.ext.brickCodegenRan : false
242
257
  configureAppProject(gradle)
243
258
  // Inline: ensure RN autolinking doesn't fail due to missing codegen/jni for brick-module
244
259
  gradle.rootProject.subprojects { subproj ->
@@ -418,24 +433,16 @@ ${moduleNames}
418
433
  project.preBuild.dependsOn('generateBrickModulesList')
419
434
  project.preBuild.doFirst {
420
435
  def workingDir = projectRoot
421
-
422
- // Skip codegen if already generated to prevent deleting Gradle build outputs
423
- // The codegen is already run during settings.gradle phase (applyBrickModules -> runBrickCodegen)
424
- // Re-running here with --clean would delete .brick_android/build/ directory
425
- // which contains package-aware-r.txt needed by dependent modules
426
- def brickOutputDir = getBrickAndroidPath(workingDir)
427
- def generatedMarker = new File(brickOutputDir, "src/main/kotlin/BrickModule.kt")
428
- def androidAppPath = getBrickAndroidAppPath(workingDir)
429
- def jniDir = new File(androidAppPath, "build/generated/autolinking/src/main/jni")
430
- def hasJniOutputs = new File(jniDir, "BrickModuleSpec-generated.cpp").exists() &&
431
- new File(jniDir, "BrickModuleSpec.h").exists() &&
432
- new File(jniDir, "CMakeLists.txt").exists()
433
- if (generatedMarker.exists() && hasJniOutputs) {
434
- // Already generated, skip to preserve build outputs
436
+
437
+ def alreadyRan = rootProject.ext.has("brickCodegenRanInBuild") ?
438
+ rootProject.ext.brickCodegenRanInBuild : false
439
+ if (alreadyRan) {
440
+ // Avoid double-running if settings already executed codegen.
435
441
  return
436
442
  }
443
+ rootProject.ext.brickCodegenRanInBuild = true
437
444
 
438
- // Run codegen only if not yet generated
445
+ // Run codegen once per build without cleaning outputs.
439
446
  try {
440
447
  def proc = ['node', '-p', "require.resolve('brick-module/package.json')"].execute(null, workingDir)
441
448
  proc.waitFor()
@@ -447,10 +454,15 @@ ${moduleNames}
447
454
  def codegenPath = new File(brickModuleDir, "bin/brick-codegen.js")
448
455
 
449
456
  if (codegenPath.exists()) {
450
- def codegenArgs = ['node', codegenPath.absolutePath, '--platform', 'android', '--projectRoot', workingDir.absolutePath]
451
- if (generatedMarker.exists()) {
452
- codegenArgs.add('--no-clean')
453
- }
457
+ def codegenArgs = [
458
+ 'node',
459
+ codegenPath.absolutePath,
460
+ '--platform',
461
+ 'android',
462
+ '--projectRoot',
463
+ workingDir.absolutePath,
464
+ '--no-clean'
465
+ ]
454
466
  def codegenProc = codegenArgs.execute(null, workingDir)
455
467
  codegenProc.waitFor()
456
468
  }
@@ -3,33 +3,45 @@ package com.brickmodule
3
3
  import com.facebook.react.bridge.*
4
4
 
5
5
  /**
6
- * Helper class for converting data to React Native compatible formats
7
- * Ensures type safety when sending events to JavaScript
6
+ * Helper class for converting data to React Native compatible formats Ensures type safety when
7
+ * sending events to JavaScript
8
8
  */
9
9
  object EventDataConverter {
10
-
10
+
11
+ private fun isNullSentinel(value: Any?): Boolean {
12
+ return value is Null ||
13
+ (value != null && value.javaClass.name == "com.brickmodule.Null") ||
14
+ (value is String && value.startsWith("com.brickmodule.Null@"))
15
+ }
16
+
11
17
  /**
12
18
  * Convert any data to React Native compatible format
13
- *
19
+ *
14
20
  * @param data The data to convert
15
21
  * @return React Native compatible data or null if conversion fails
16
22
  */
17
23
  fun convert(data: Any?): Any? {
18
24
  return try {
19
- when (data) {
20
- null -> null
21
- is String -> data
22
- is Boolean -> data
23
- is Int -> data
24
- is Double -> data
25
- is Float -> data.toDouble()
26
- is Long -> data.toDouble()
27
- is Map<*, *> -> convertMap(data)
28
- is List<*> -> convertList(data)
29
- is Array<*> -> convertList(data.toList())
30
- else -> {
31
- println("⚠️ EventDataConverter: Unsupported data type ${data.javaClass.simpleName}, converting to string")
32
- data.toString()
25
+ if (isNullSentinel(data)) {
26
+ null
27
+ } else {
28
+ when (data) {
29
+ null -> null
30
+ is String -> data
31
+ is Boolean -> data
32
+ is Int -> data
33
+ is Double -> data
34
+ is Float -> data.toDouble()
35
+ is Long -> data.toDouble()
36
+ is Map<*, *> -> convertMap(data)
37
+ is List<*> -> convertList(data)
38
+ is Array<*> -> convertList(data.toList())
39
+ else -> {
40
+ println(
41
+ "⚠️ EventDataConverter: Unsupported data type ${data.javaClass.simpleName}, converting to string"
42
+ )
43
+ data.toString()
44
+ }
33
45
  }
34
46
  }
35
47
  } catch (e: Exception) {
@@ -37,17 +49,17 @@ object EventDataConverter {
37
49
  null
38
50
  }
39
51
  }
40
-
52
+
41
53
  /**
42
54
  * Convert a map to WritableMap for React Native
43
- *
55
+ *
44
56
  * @param map The map to convert
45
57
  * @return WritableMap or null if conversion fails
46
58
  */
47
59
  private fun convertMap(map: Map<*, *>): WritableMap? {
48
60
  return try {
49
61
  val writableMap = Arguments.createMap()
50
-
62
+
51
63
  map.forEach { (key, value) ->
52
64
  val keyString = key?.toString()
53
65
  if (keyString != null) {
@@ -63,24 +75,24 @@ object EventDataConverter {
63
75
  }
64
76
  }
65
77
  }
66
-
78
+
67
79
  writableMap
68
80
  } catch (e: Exception) {
69
81
  println("❌ EventDataConverter: Failed to convert map: ${e.message}")
70
82
  null
71
83
  }
72
84
  }
73
-
85
+
74
86
  /**
75
87
  * Convert a list to WritableArray for React Native
76
- *
88
+ *
77
89
  * @param list The list to convert
78
90
  * @return WritableArray or null if conversion fails
79
91
  */
80
92
  private fun convertList(list: List<*>): WritableArray? {
81
93
  return try {
82
94
  val writableArray = Arguments.createArray()
83
-
95
+
84
96
  list.forEach { item ->
85
97
  when (val convertedValue = convert(item)) {
86
98
  null -> writableArray.pushNull()
@@ -93,30 +105,31 @@ object EventDataConverter {
93
105
  else -> writableArray.pushString(convertedValue.toString())
94
106
  }
95
107
  }
96
-
108
+
97
109
  writableArray
98
110
  } catch (e: Exception) {
99
111
  println("❌ EventDataConverter: Failed to convert list: ${e.message}")
100
112
  null
101
113
  }
102
114
  }
103
-
115
+
104
116
  /**
105
117
  * Validate if data can be safely converted to React Native format
106
- *
118
+ *
107
119
  * @param data The data to validate
108
120
  * @return true if data can be safely converted, false otherwise
109
121
  */
110
122
  fun isConvertible(data: Any?): Boolean {
123
+ if (isNullSentinel(data)) {
124
+ return true
125
+ }
111
126
  return when (data) {
112
127
  null -> true
113
128
  is String, is Boolean, is Number -> true
114
- is Map<*, *> -> data.all { (key, value) ->
115
- key is String && isConvertible(value)
116
- }
129
+ is Map<*, *> -> data.all { (key, value) -> key is String && isConvertible(value) }
117
130
  is List<*> -> data.all { isConvertible(it) }
118
131
  is Array<*> -> data.all { isConvertible(it) }
119
132
  else -> false
120
133
  }
121
134
  }
122
- }
135
+ }
@@ -0,0 +1,7 @@
1
+
2
+ package com.brickmodule
3
+
4
+ /**
5
+ * Sentinel value used to represent null for Any conversions.
6
+ */
7
+ object Null
package/dist/index.d.ts CHANGED
@@ -4,6 +4,16 @@ import { Any, AnyObject } from "./types.js";
4
4
 
5
5
  //#region src/index.d.ts
6
6
 
7
+ /**
8
+ * Error types for Brick modules
9
+ * @deprecated Use BrickError instead
10
+ */
11
+ declare class BrickModuleError extends Error {
12
+ code: string;
13
+ moduleName?: string | undefined;
14
+ methodName?: string | undefined;
15
+ constructor(message: string, code?: string, moduleName?: string | undefined, methodName?: string | undefined);
16
+ }
7
17
  /**
8
18
  * Configuration interface for brick-codegen
9
19
  */
@@ -21,4 +31,4 @@ interface BrickCodegenConfig {
21
31
  dev?: boolean;
22
32
  }
23
33
  //#endregion
24
- export { Any, AnyObject, BrickCodegenConfig, BrickError, BrickModule, type BrickModuleInterface, type BrickModuleSpec };
34
+ export { Any, AnyObject, BrickCodegenConfig, BrickError, BrickModule, BrickModuleError, type BrickModuleInterface, type BrickModuleSpec };
package/dist/index.js CHANGED
@@ -1,4 +1,20 @@
1
1
  import { BrickError } from "./BrickError.js";
2
2
  import BrickModule_default from "./BrickModule.js";
3
3
 
4
- export { BrickError, BrickModule_default as BrickModule };
4
+ //#region src/index.ts
5
+ /**
6
+ * Error types for Brick modules
7
+ * @deprecated Use BrickError instead
8
+ */
9
+ var BrickModuleError = class extends Error {
10
+ constructor(message, code = "BRICK_ERROR", moduleName, methodName) {
11
+ super(message);
12
+ this.code = code;
13
+ this.moduleName = moduleName;
14
+ this.methodName = methodName;
15
+ this.name = "BrickModuleError";
16
+ }
17
+ };
18
+
19
+ //#endregion
20
+ export { BrickError, BrickModule_default as BrickModule, BrickModuleError };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brick-module",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Better React Native native module development",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -58,7 +58,7 @@
58
58
  "brick-codegen": "./bin/brick-codegen.js"
59
59
  },
60
60
  "dependencies": {
61
- "brick-codegen": "0.4.0"
61
+ "brick-codegen": "0.5.0"
62
62
  },
63
63
  "peerDependencies": {
64
64
  "react": ">=18.2.0",
package/src/index.ts CHANGED
@@ -6,6 +6,22 @@ export type { BrickModuleInterface, BrickModuleSpec } from "./BrickModule";
6
6
  export { default as BrickModule } from "./BrickModule";
7
7
  export { BrickError } from "./BrickError";
8
8
 
9
+ /**
10
+ * Error types for Brick modules
11
+ * @deprecated Use BrickError instead
12
+ */
13
+ export class BrickModuleError extends Error {
14
+ constructor(
15
+ message: string,
16
+ public code: string = "BRICK_ERROR",
17
+ public moduleName?: string,
18
+ public methodName?: string
19
+ ) {
20
+ super(message);
21
+ this.name = "BrickModuleError";
22
+ }
23
+ }
24
+
9
25
  /**
10
26
  * Configuration interface for brick-codegen
11
27
  */