brick-module 0.1.7 → 0.1.9

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.
@@ -43,87 +43,29 @@ ext.readPackageJson = { File packageJsonFile ->
43
43
  }
44
44
  }
45
45
 
46
- ext.findBrickJson = { startDir ->
47
- // Walk up the directory tree looking for brick.json
48
- if (!startDir) {
49
- return null
50
- }
51
-
52
- try {
53
- // startDir can be either a File or String, handle both
54
- def current = startDir instanceof File ? startDir.canonicalFile : new File(startDir).canonicalFile
55
- def root = new File('/').canonicalFile
56
-
57
- while (current != root && current != null) {
58
- def brickJsonFile = new File(current, 'brick.json')
59
- if (brickJsonFile.exists()) {
60
- return brickJsonFile
61
- }
62
- def parent = current.parentFile
63
- if (parent == current || parent == null) break // Reached filesystem root
64
- current = parent
65
- }
66
- } catch (Exception e) {
67
- // Failed to process directory, return null
68
- }
69
-
70
- return null
71
- }
46
+ // Removed discovery helpers; explicit appPath is required
72
47
 
73
- ext.resolveProjectRoot = { projectRootPath, configDir ->
74
- // Handle both absolute and relative paths
75
- if (!projectRootPath || !configDir) {
76
- return null
77
- }
78
-
48
+ ext.getBrickAndroidPath = { projectRoot ->
49
+ // Read brick.json from explicit project root
79
50
  try {
80
- def projectRootFile = new File(projectRootPath)
81
- if (projectRootFile.isAbsolute()) {
82
- return projectRootFile.exists() ? projectRootFile : null
83
- } else {
84
- // Relative to brick.json location
85
- def resolvedFile = new File(configDir, projectRootPath).canonicalFile
86
- return resolvedFile.exists() ? resolvedFile : null
87
- }
88
- } catch (Exception e) {
89
- return null
90
- }
91
- }
92
-
93
- ext.getBrickAndroidPath = { searchRoot ->
94
- // Walk up directory tree to find brick.json
95
- def brickConfigFile = findBrickJson(searchRoot)
96
-
97
- if (brickConfigFile && brickConfigFile.exists()) {
98
- try {
51
+ def brickConfigFile = new File(projectRoot, 'brick.json')
52
+ if (brickConfigFile.exists()) {
99
53
  def config = new JsonSlurper().parse(brickConfigFile)
100
-
101
- // Get the effective project root
102
- def effectiveRoot = brickConfigFile.parentFile
103
- if (config.projectRoot) {
104
- def resolvedRoot = resolveProjectRoot(config.projectRoot, brickConfigFile.parentFile)
105
- if (resolvedRoot) {
106
- effectiveRoot = resolvedRoot
107
- }
108
- }
109
-
110
54
  def androidPath = config?.output?.android
111
55
  if (androidPath && !androidPath.isEmpty()) {
112
- // Handle absolute paths (though not recommended)
113
56
  if (new File(androidPath).isAbsolute()) {
114
57
  println("[Brick] Warning: Using absolute path for Android output is not recommended: ${androidPath}")
115
58
  return androidPath
116
59
  }
117
- // Otherwise, treat as relative to effective project root
118
- return new File(effectiveRoot, androidPath).canonicalPath
60
+ return new File(projectRoot, androidPath).canonicalPath
119
61
  }
120
- } catch (Exception e) {
121
- println("[Brick] Warning: Failed to parse brick.json: ${e.message}")
122
62
  }
63
+ } catch (Exception e) {
64
+ println("[Brick] Warning: Failed to read brick.json from ${projectRoot}: ${e.message}")
123
65
  }
124
66
 
125
- // Default: .brick directory in the Android project root
126
- return new File(searchRoot, '.brick').canonicalPath
67
+ // Default: android/.brick in project root
68
+ return new File(projectRoot, 'android/.brick').canonicalPath
127
69
  }
128
70
 
129
71
  ext.getAllDependencies = { packageJson ->
@@ -137,44 +79,6 @@ ext.isBrickModule = { packageJson ->
137
79
  return packageJson?.brickModule != null
138
80
  }
139
81
 
140
- ext.findProjectRoot = { androidDir ->
141
- // 1. Walk up directory tree to find brick.json
142
- def brickConfigFile = findBrickJson(androidDir)
143
- if (brickConfigFile) {
144
- brickLog("Found brick.json at: ${brickConfigFile.canonicalPath}")
145
- try {
146
- def config = new JsonSlurper().parse(brickConfigFile)
147
- if (config.projectRoot) {
148
- def projectRoot = resolveProjectRoot(config.projectRoot, brickConfigFile.parentFile)
149
- if (projectRoot) {
150
- brickLog("Using projectRoot from brick.json: ${projectRoot.canonicalPath}")
151
- return projectRoot
152
- }
153
- }
154
- // If brick.json exists but has no projectRoot, use its directory as project root
155
- return brickConfigFile.parentFile
156
- } catch (Exception e) {
157
- println("[Brick] Warning: Failed to parse brick.json: ${e.message}")
158
- }
159
- }
160
-
161
- // 2. Walk up to find package.json with node_modules
162
- def current = androidDir
163
- while (current != null && current != new File('/')) {
164
- if (new File(current, 'package.json').exists() && new File(current, 'node_modules').exists()) {
165
- brickLog("Found project root via package.json: ${current.canonicalPath}")
166
- return current
167
- }
168
- def parent = current.parentFile
169
- if (parent == current) break
170
- current = parent
171
- }
172
-
173
- // If no project root found, use default fallback
174
- brickLog("Warning: Failed to find project root via brick.json or package.json. Using default: ${androidDir.parentFile.canonicalPath}")
175
- return androidDir.parentFile
176
- }
177
-
178
82
  ext.runBrickCodegen = { workingDir ->
179
83
  def brickModuleDir = resolveModule(workingDir, "brick-module")
180
84
  if (brickModuleDir == null) {
@@ -189,7 +93,8 @@ ext.runBrickCodegen = { workingDir ->
189
93
  }
190
94
 
191
95
  try {
192
- def proc = ['node', codegenPath.absolutePath, '--platform', 'android'].execute(null, workingDir)
96
+ def wdPath = (workingDir instanceof File) ? workingDir.absolutePath : workingDir
97
+ def proc = ['node', codegenPath.absolutePath, '--platform', 'android', '--projectRoot', wdPath].execute(null, workingDir)
193
98
  proc.waitFor()
194
99
  return proc.exitValue() == 0
195
100
  } catch (Exception e) {
@@ -202,13 +107,26 @@ ext.runBrickCodegen = { workingDir ->
202
107
  // Main Configuration
203
108
  // =============================================================================
204
109
 
205
- ext.applyBrickModules = { settings ->
206
- def projectRoot = findProjectRoot(settings.rootDir)
110
+ ext.applyBrickModules = { settings, args = [:] ->
111
+ def appPath = (args instanceof Map) ? args.appPath : args
112
+ if (!appPath) {
113
+ throw new GradleException("Brick: appPath is required. Call applyBrickModules(settings, [appPath: '/absolute/path/to/app'])")
114
+ }
115
+ // Support relative appPath (relative to settings.rootDir)
116
+ def appPathFile = new File(appPath.toString())
117
+ def projectRoot = (appPathFile.isAbsolute() ? appPathFile : new File(settings.rootDir, appPathFile.path)).canonicalFile
118
+
119
+ // Validate package.json exists in appPath
120
+ def packageJsonFile = new File(projectRoot, 'package.json')
121
+ if (!packageJsonFile.exists()) {
122
+ throw new GradleException("Brick: package.json not found at ${projectRoot}. Please pass a valid appPath.")
123
+ }
124
+
207
125
  def foundModules = []
208
126
  def brickModulesData = [:]
209
127
 
210
128
  // Read app's package.json
211
- def appPackageJson = readPackageJson(new File(projectRoot, "package.json"))
129
+ def appPackageJson = readPackageJson(packageJsonFile)
212
130
  if (appPackageJson == null) {
213
131
  throw new GradleException("Could not find or parse package.json at ${projectRoot}")
214
132
  }
@@ -266,7 +184,7 @@ ext.applyBrickModules = { settings ->
266
184
  }
267
185
 
268
186
  // Check if codegen is needed (use configured path)
269
- def androidBrickPath = getBrickAndroidPath(settings.rootDir)
187
+ def androidBrickPath = getBrickAndroidPath(projectRoot)
270
188
  def brickDir = new File(androidBrickPath)
271
189
  def needsCodegen = !brickDir.exists() || !new File(brickDir, "src/main/kotlin/BrickModule.kt").exists()
272
190
 
@@ -309,7 +227,7 @@ def configureAppProject(gradle) {
309
227
  def configureBrickModules(project) {
310
228
  def rootProject = project.rootProject
311
229
  def brickModulesData = rootProject.ext.brickModulesData
312
- def projectRoot = project.rootDir.parentFile
230
+ def projectRoot = rootProject.ext.brickProjectRoot
313
231
 
314
232
  // Add source directory for generated code
315
233
  project.android.sourceSets.main.java.srcDir "${project.buildDir}/generated/source/brick-provider"
@@ -341,7 +259,7 @@ def configureBrickModules(project) {
341
259
  def codegenPath = new File(brickModuleDir, "bin/brick-codegen.js")
342
260
 
343
261
  if (codegenPath.exists()) {
344
- def codegenProc = ['node', codegenPath.absolutePath, '--platform', 'android'].execute(null, workingDir)
262
+ def codegenProc = ['node', codegenPath.absolutePath, '--platform', 'android', '--projectRoot', workingDir.absolutePath].execute(null, workingDir)
345
263
  codegenProc.waitFor()
346
264
  }
347
265
  }
@@ -14,7 +14,7 @@ class BrickModuleRegistry {
14
14
  private var isRegistered: Boolean = false
15
15
 
16
16
  /**
17
- * Registers an array of Brick modules Each module must implement their respective TypeModule
17
+ * Registers an array of Brick modules Each module must implement their respective Spec
18
18
  * interface
19
19
  *
20
20
  * @param modules List of module instances to register
@@ -50,7 +50,7 @@ class BrickModuleRegistry {
50
50
  // Register or update the module
51
51
  val isUpdate = modules.containsKey(moduleName)
52
52
  modules[moduleName] = module
53
-
53
+
54
54
  if (isUpdate) {
55
55
  println("🔄 BrickModuleRegistry: Module '$moduleName' updated (replaced existing)")
56
56
  } else {
@@ -21,7 +21,7 @@ import React
21
21
 
22
22
  /**
23
23
  * Registers an array of Brick modules (Swift API)
24
- * Each module must implement BrickModuleBase and its respective TypeModule protocol
24
+ * Each module must implement BrickModuleBase and its respective Spec protocol
25
25
  * This should be called once during app initialization
26
26
  */
27
27
  public func register(_ modules: [BrickModuleBase]) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brick-module",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Better React Native native module development",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -62,7 +62,7 @@
62
62
  "brick-codegen": "./bin/brick-codegen.js"
63
63
  },
64
64
  "dependencies": {
65
- "brick-codegen": "0.1.7"
65
+ "brick-codegen": "0.1.9"
66
66
  },
67
67
  "peerDependencies": {
68
68
  "react": ">=18.2.0",
@@ -71,7 +71,7 @@
71
71
  "devDependencies": {
72
72
  "@types/react": "^18.2.0",
73
73
  "@types/react-native": "^0.72.0",
74
- "tsdown": "^0.14.0",
74
+ "tsdown": "^0.14.2",
75
75
  "typescript": "^5.9.2"
76
76
  },
77
77
  "codegenConfig": {
package/podfile_helper.rb CHANGED
@@ -6,89 +6,24 @@
6
6
  require 'json'
7
7
  require 'pathname'
8
8
 
9
- def find_brick_json(start_dir)
10
- # Walk up the directory tree looking for brick.json
11
- current = File.expand_path(start_dir)
12
- root = File.expand_path('/')
13
-
14
- while current != root
15
- brick_json_path = File.join(current, 'brick.json')
16
- if File.exist?(brick_json_path)
17
- return brick_json_path
18
- end
19
- parent = File.dirname(current)
20
- break if parent == current # Reached filesystem root
21
- current = parent
22
- end
23
-
24
- return nil
25
- end
26
9
 
27
- def find_project_root
28
- ios_root = Pod::Config.instance.installation_root
29
-
30
- # 1. Walk up directory tree to find brick.json
31
- brick_json_path = find_brick_json(ios_root)
32
- if brick_json_path
33
- Pod::UI.puts "[Brick] Found brick.json at: #{brick_json_path}"
34
- begin
35
- config = JSON.parse(File.read(brick_json_path))
36
- if config['projectRoot']
37
- project_root = resolve_project_root(config['projectRoot'], File.dirname(brick_json_path))
38
- if project_root
39
- Pod::UI.puts "[Brick] Using projectRoot from brick.json: #{project_root}"
40
- return project_root
41
- end
42
- end
43
- # If brick.json exists but has no projectRoot, use its directory as project root
44
- return File.dirname(brick_json_path)
45
- rescue => e
46
- Pod::UI.warn "[Brick] Failed to parse brick.json: #{e.message}"
47
- end
48
- end
49
-
50
- # 2. Walk up to find package.json with node_modules
51
- current = ios_root
52
- while current != '/'
53
- if File.exist?(File.join(current, 'package.json')) && File.exist?(File.join(current, 'node_modules'))
54
- Pod::UI.puts "[Brick] Found project root via package.json: #{current}"
55
- return current
56
- end
57
- parent = File.dirname(current)
58
- break if parent == current
59
- current = parent
10
+ def use_brick_modules!(app_path: nil)
11
+ if app_path.nil? || app_path.empty?
12
+ raise "[Brick] app_path is required. Call use_brick_modules!(app_path: '<path to your app root>')"
60
13
  end
61
-
62
- # If no project root found, use default fallback
63
- default_root = File.expand_path(File.join(ios_root, '..'))
64
- Pod::UI.warn "[Brick] Warning: Failed to find project root via brick.json or package.json. Using default: #{default_root}"
65
- return default_root
66
- end
14
+ brick_root = app_path
67
15
 
68
- def resolve_project_root(project_root_path, config_dir)
69
- # Handle both absolute and relative paths
70
- if Pathname.new(project_root_path).absolute?
71
- return project_root_path if File.exist?(project_root_path)
72
- else
73
- # Relative to brick.json location
74
- resolved_path = File.expand_path(File.join(config_dir, project_root_path))
75
- return resolved_path if File.exist?(resolved_path)
16
+ # Validate package.json exists in app_path
17
+ package_json_path = File.join(brick_root, 'package.json')
18
+ unless File.exist?(package_json_path)
19
+ raise "[Brick] package.json not found at #{brick_root}. Please pass a valid app_path."
76
20
  end
77
- return nil
78
- end
79
-
80
- def use_brick_modules!
81
- brick_root = find_project_root
82
- discovered_modules = []
83
21
 
84
22
  begin
85
23
  # Run brick-codegen with real-time output and colors (iOS only)
86
- exit_status = system("cd #{brick_root} && FORCE_COLOR=1 npx brick-codegen --platform ios")
24
+ exit_status = system("cd #{brick_root} && FORCE_COLOR=1 npx brick-codegen --platform ios --projectRoot \"#{brick_root}\"")
87
25
 
88
26
  if exit_status
89
- # Generate BrickModuleProvider.swift for auto-registration
90
- generate_brick_module_provider(brick_root, discovered_modules)
91
-
92
27
  # Get iOS output path from brick.json if it exists
93
28
  ios_brick_path = get_brick_ios_path(brick_root)
94
29
 
@@ -117,81 +52,21 @@ def use_brick_modules!
117
52
  end
118
53
  end
119
54
 
120
- def discover_brick_modules(project_root)
121
- modules = []
122
- node_modules_path = File.join(project_root, 'node_modules')
123
-
124
- return modules unless File.exist?(node_modules_path)
125
-
126
- # Read the main package.json to get dependencies
127
- package_json_path = File.join(project_root, 'package.json')
128
- return modules unless File.exist?(package_json_path)
129
-
130
- begin
131
- package_json = JSON.parse(File.read(package_json_path))
132
- dependencies = (package_json['dependencies'] || {}).merge(package_json['devDependencies'] || {})
133
-
134
- dependencies.each do |dep_name, _version|
135
- dep_path = File.join(node_modules_path, dep_name)
136
- dep_package_json_path = File.join(dep_path, 'package.json')
137
-
138
- next unless File.exist?(dep_package_json_path)
139
-
140
- begin
141
- dep_package_json = JSON.parse(File.read(dep_package_json_path))
142
-
143
- # Check if this is a Brick module
144
- if dep_package_json['brickModule']
145
- brick_config = dep_package_json['brickModule']
146
- ios_config = brick_config['ios'] || {}
147
-
148
- # Check if autoRegister is enabled (default: true)
149
- auto_register = brick_config['autoRegister'] != false
150
-
151
- if auto_register
152
- # Extract the iOS module name and class name
153
- module_name = ios_config['moduleName']
154
- class_name = ios_config['className']
155
-
156
- modules << {
157
- 'name' => dep_name,
158
- 'moduleName' => module_name,
159
- 'className' => class_name,
160
- }
161
-
162
- Pod::UI.puts "[Brick] Found module: #{dep_name} (#{module_name})"
163
- else
164
- Pod::UI.puts "[Brick] Skipping module (autoRegister=false): #{dep_name}"
165
- end
166
- end
167
- rescue => e
168
- Pod::UI.warn "[Brick] Failed to parse #{dep_package_json_path}: #{e.message}"
169
- end
170
- end
171
- rescue => e
172
- Pod::UI.warn "[Brick] Failed to discover modules: #{e.message}"
173
- end
174
-
175
- modules
176
- end
177
55
 
178
56
  def get_brick_ios_path(project_root)
179
- # Walk up from project_root to find brick.json
180
- brick_config_path = find_brick_json(project_root)
57
+ # Read brick.json from the explicit project root
58
+ brick_config_path = File.join(project_root, 'brick.json')
181
59
 
182
- if brick_config_path && File.exist?(brick_config_path)
60
+ if File.exist?(brick_config_path)
183
61
  begin
184
62
  config = JSON.parse(File.read(brick_config_path))
185
-
186
63
  ios_path = config.dig('output', 'ios')
187
64
  if ios_path && !ios_path.empty?
188
- # Handle absolute paths (though not recommended)
189
65
  if Pathname.new(ios_path).absolute?
190
66
  Pod::UI.warn "[Brick] Using absolute path for iOS output is not recommended: #{ios_path}"
191
67
  return ios_path
192
68
  else
193
- # Relative to brick.json location, not project root
194
- return File.expand_path(File.join(File.dirname(brick_config_path), ios_path))
69
+ return File.expand_path(File.join(project_root, ios_path))
195
70
  end
196
71
  end
197
72
  rescue => e
@@ -199,99 +74,5 @@ def get_brick_ios_path(project_root)
199
74
  end
200
75
  end
201
76
 
202
- # Return default path - just the relative path, caller will resolve it
203
- return 'ios/.brick'
204
- end
205
-
206
- def generate_brick_module_provider(project_root, modules)
207
- return if modules.empty?
208
-
209
- # Get iOS output path from brick.json
210
- ios_brick_path = get_brick_ios_path(project_root)
211
-
212
- # Handle relative vs absolute paths
213
- if Pathname.new(ios_brick_path).absolute?
214
- brick_dir = ios_brick_path
215
- else
216
- brick_dir = File.expand_path(File.join(project_root, ios_brick_path))
217
- end
218
-
219
- # Create the brick directory if it doesn't exist
220
- FileUtils.mkdir_p(brick_dir) unless File.exist?(brick_dir)
221
-
222
- # Generate imports and module instances
223
- imports = modules.map { |m| "import #{m['moduleName']}" }.join("\n")
224
- module_instances = modules.map { |m| " #{m['className']}()" }.join(",\n")
225
- module_names = modules.map { |m| " \"#{m['moduleName']}\"" }.join(",\n")
226
-
227
- # Generate the Swift file content
228
- swift_content = <<-SWIFT
229
- // Auto-generated by brick-module podfile_helper.rb
230
- // Do not edit manually - this file is regenerated on each pod install
231
-
232
- import Foundation
233
- import BrickModule
234
- #{imports}
235
-
236
- @objcMembers
237
- public class BrickModuleProvider: NSObject {
238
- @objc public static func registerAll() {
239
- let modules: [BrickModuleBase] = [
240
- #{module_instances}
241
- ]
242
-
243
- if !modules.isEmpty {
244
- BrickModuleRegistry.shared.register(modules)
245
- print("🧱 BrickModuleProvider: Registered \\(modules.count) modules")
246
- }
247
- }
248
-
249
- @objc public static func getAvailableModules() -> [String] {
250
- return [
251
- #{module_names}
252
- ]
253
- }
254
- }
255
- SWIFT
256
-
257
- # Write the Swift file
258
- provider_path = File.join(brick_dir, 'BrickModuleProvider.swift')
259
- File.write(provider_path, swift_content)
260
-
261
- # Generate simple Objective-C wrapper with clean name
262
- objc_header_content = <<-OBJC
263
- // Auto-generated by brick-module podfile_helper.rb
264
- // Simple Objective-C wrapper for auto-linking
265
-
266
- #import <Foundation/Foundation.h>
267
-
268
- @interface BrickModuleProviderObjC : NSObject
269
- + (void)registerAll;
270
- @end
271
- OBJC
272
-
273
- objc_impl_content = <<-OBJC
274
- // Auto-generated by brick-module podfile_helper.rb
275
- // Simple Objective-C wrapper implementation
276
-
277
- #import "BrickModuleProviderObjC.h"
278
- #import "BrickCodegen-Swift.h"
279
-
280
- @implementation BrickModuleProviderObjC
281
-
282
- + (void)registerAll {
283
- [BrickModuleProvider registerAll];
284
- }
285
-
286
- @end
287
- OBJC
288
-
289
- # Write Objective-C wrapper files
290
- objc_header_path = File.join(brick_dir, 'BrickModuleProviderObjC.h')
291
- objc_impl_path = File.join(brick_dir, 'BrickModuleProviderObjC.m')
292
- File.write(objc_header_path, objc_header_content)
293
- File.write(objc_impl_path, objc_impl_content)
294
-
295
- Pod::UI.puts "[Brick] Generated BrickModuleProvider.swift with #{modules.size} modules"
296
- Pod::UI.puts "[Brick] Generated BrickModuleProviderObjC wrapper for clean Objective-C integration"
77
+ return File.expand_path(File.join(project_root, 'ios/.brick'))
297
78
  end