@bm-fe/react-native-multi-bundle 1.0.0-beta.5 → 1.0.0-beta.6

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.
@@ -14,115 +14,249 @@ RCT_EXPORT_MODULE(ModuleLoader);
14
14
  // Bundle manifest file name
15
15
  static NSString *const BundleManifestFileName = @"bundle-manifest.json";
16
16
 
17
+ #pragma mark - Bundle Path Resolution
18
+
17
19
  /**
18
20
  * 获取 bundle 文件的完整路径
19
- * 查找顺序:
20
- * 1. CodePush 目录(如果可用)
21
- * 2. MainBundle 的 Bundles 目录
22
- * 3. MainBundle 根目录
23
- * 4. MainBundle 的 assets 目录
24
- * 5. Documents 目录
25
21
  */
26
22
  - (NSString *)getFullBundlePath:(NSString *)bundlePath {
27
- // bundlePath 格式可能是 "modules/home.jsbundle" 或 "home.jsbundle"
28
23
  NSString *fileName = [bundlePath lastPathComponent];
29
24
  NSString *relativePath = bundlePath;
30
-
31
- // 如果路径包含 "modules/",提取文件名
25
+
32
26
  if ([bundlePath containsString:@"modules/"]) {
33
27
  fileName = [bundlePath lastPathComponent];
34
28
  relativePath = [NSString stringWithFormat:@"modules/%@", fileName];
35
29
  }
36
-
37
- // 使用 NSMutableArray 来避免 nil 值问题
30
+
38
31
  NSMutableArray<NSString *> *searchPaths = [NSMutableArray array];
39
-
40
- // CodePush 路径(如果可用)
32
+
33
+ // CodePush 路径
41
34
  NSString *codePushPath = [self getCodePushBundlePath:bundlePath];
42
35
  if (codePushPath) {
43
36
  [searchPaths addObject:codePushPath];
44
37
  }
45
-
38
+
46
39
  // MainBundle/Bundles/modules/xxx.jsbundle
47
40
  NSString *path1 = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:[@"Bundles/" stringByAppendingString:relativePath]];
48
- if (path1) {
49
- [searchPaths addObject:path1];
50
- }
51
-
52
- // MainBundle/Bundles/xxx.jsbundle (直接文件名)
41
+ if (path1) [searchPaths addObject:path1];
42
+
43
+ // MainBundle/Bundles/xxx.jsbundle
53
44
  NSString *path2 = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:[@"Bundles/" stringByAppendingString:fileName]];
54
- if (path2) {
55
- [searchPaths addObject:path2];
56
- }
57
-
58
- // MainBundle 根目录下的完整路径
45
+ if (path2) [searchPaths addObject:path2];
46
+
47
+ // MainBundle 根目录
59
48
  NSString *path3 = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:bundlePath];
60
- if (path3) {
61
- [searchPaths addObject:path3];
62
- }
63
-
64
- // MainBundle 根目录下的文件名
49
+ if (path3) [searchPaths addObject:path3];
50
+
65
51
  NSString *path4 = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:fileName];
66
- if (path4) {
67
- [searchPaths addObject:path4];
68
- }
69
-
70
- // MainBundle/resourcePath 下的完整路径
52
+ if (path4) [searchPaths addObject:path4];
53
+
54
+ // resourcePath
71
55
  NSString *path5 = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:bundlePath];
72
- if (path5) {
73
- [searchPaths addObject:path5];
74
- }
75
-
76
- // MainBundle/resourcePath 下的文件名
56
+ if (path5) [searchPaths addObject:path5];
57
+
77
58
  NSString *path6 = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:fileName];
78
- if (path6) {
79
- [searchPaths addObject:path6];
80
- }
81
-
59
+ if (path6) [searchPaths addObject:path6];
60
+
82
61
  // Documents 目录
83
62
  NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
84
63
  if (documentsPath) {
85
64
  NSString *path7 = [documentsPath stringByAppendingPathComponent:bundlePath];
86
- if (path7) {
87
- [searchPaths addObject:path7];
88
- }
65
+ if (path7) [searchPaths addObject:path7];
89
66
  }
90
-
67
+
91
68
  for (NSString *fullPath in searchPaths) {
92
69
  if (fullPath && [[NSFileManager defaultManager] fileExistsAtPath:fullPath]) {
93
70
  RCTLogInfo(@"[ModuleLoader] Found bundle at: %@", fullPath);
94
71
  return fullPath;
95
72
  }
96
73
  }
97
-
98
- // 记录所有搜索路径用于调试
99
- RCTLogWarn(@"[ModuleLoader] Bundle not found in any location: %@", bundlePath);
100
- RCTLogWarn(@"[ModuleLoader] Searched locations:");
74
+
75
+ RCTLogWarn(@"[ModuleLoader] Bundle not found: %@", bundlePath);
101
76
  for (NSString *path in searchPaths) {
102
- if (path) {
103
- RCTLogWarn(@"[ModuleLoader] - %@", path);
104
- }
77
+ if (path) RCTLogWarn(@"[ModuleLoader] - %@", path);
105
78
  }
106
-
79
+
107
80
  return nil;
108
81
  }
109
82
 
83
+ - (NSString *)getCodePushBundlePath:(NSString *)bundlePath {
84
+ return nil;
85
+ }
86
+
87
+ #pragma mark - Bridge Access
88
+
110
89
  /**
111
- * 获取 CodePush bundle 路径(如果可用)
90
+ * 获取当前的 RCTBridge
112
91
  */
113
- - (NSString *)getCodePushBundlePath:(NSString *)bundlePath {
114
- // 这里可以集成 CodePush SDK 来获取路径
115
- // 暂时返回 nil,表示不使用 CodePush
92
+ - (RCTBridge *)getCurrentBridge {
93
+ // 方法 1: 使用模块自带的 bridge
94
+ if (self.bridge) {
95
+ return self.bridge;
96
+ }
97
+
98
+ // 方法 2: 从 AppDelegate 获取
99
+ UIApplication *app = [UIApplication sharedApplication];
100
+ id delegate = app.delegate;
101
+
102
+ if ([delegate respondsToSelector:@selector(bridge)]) {
103
+ RCTBridge *bridge = [delegate performSelector:@selector(bridge)];
104
+ if (bridge) return bridge;
105
+ }
106
+
107
+ // 方法 3: 通过 reactNativeHost
108
+ if ([delegate respondsToSelector:@selector(reactNativeHost)]) {
109
+ id reactNativeHost = [delegate performSelector:@selector(reactNativeHost)];
110
+ if (reactNativeHost && [reactNativeHost respondsToSelector:@selector(bridge)]) {
111
+ RCTBridge *bridge = [reactNativeHost performSelector:@selector(bridge)];
112
+ if (bridge) return bridge;
113
+ }
114
+ }
115
+
116
+ // 方法 4: 通过 rootViewFactory (新架构)
117
+ if ([delegate respondsToSelector:@selector(rootViewFactory)]) {
118
+ id factory = [delegate performSelector:@selector(rootViewFactory)];
119
+ if (factory && [factory respondsToSelector:@selector(bridge)]) {
120
+ RCTBridge *bridge = [factory performSelector:@selector(bridge)];
121
+ if (bridge) return bridge;
122
+ }
123
+ }
124
+
116
125
  return nil;
117
126
  }
118
127
 
128
+ #pragma mark - Bundle Loading Methods
129
+
130
+ /**
131
+ * 尝试使用各种方法加载 bundle
132
+ */
133
+ - (BOOL)tryLoadBundle:(NSString *)bundleContent
134
+ sourceURL:(NSURL *)sourceURL
135
+ bridge:(RCTBridge *)bridge
136
+ error:(NSError **)outError {
137
+
138
+ // 获取 batchedBridge(在新旧架构中都可能存在)
139
+ id targetBridge = bridge;
140
+ @try {
141
+ if ([bridge respondsToSelector:@selector(batchedBridge)]) {
142
+ id batchedBridge = [bridge performSelector:@selector(batchedBridge)];
143
+ if (batchedBridge) {
144
+ targetBridge = batchedBridge;
145
+ RCTLogInfo(@"[ModuleLoader] Using batchedBridge");
146
+ }
147
+ }
148
+ } @catch (NSException *e) {
149
+ RCTLogWarn(@"[ModuleLoader] Could not get batchedBridge: %@", e.reason);
150
+ }
151
+
152
+ // 方法 1: 尝试 executeSourceCode:withSourceURL:onComplete: (新架构可能支持)
153
+ SEL executeWithCompleteSel = NSSelectorFromString(@"executeSourceCode:withSourceURL:onComplete:");
154
+ if ([targetBridge respondsToSelector:executeWithCompleteSel]) {
155
+ @try {
156
+ RCTLogInfo(@"[ModuleLoader] Trying executeSourceCode:withSourceURL:onComplete:");
157
+ NSData *sourceData = [bundleContent dataUsingEncoding:NSUTF8StringEncoding];
158
+
159
+ __block BOOL completed = NO;
160
+ __block NSError *execError = nil;
161
+
162
+ void (^onComplete)(NSError *) = ^(NSError *error) {
163
+ execError = error;
164
+ completed = YES;
165
+ };
166
+
167
+ NSMethodSignature *sig = [targetBridge methodSignatureForSelector:executeWithCompleteSel];
168
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];
169
+ [invocation setTarget:targetBridge];
170
+ [invocation setSelector:executeWithCompleteSel];
171
+ [invocation setArgument:&sourceData atIndex:2];
172
+ [invocation setArgument:&sourceURL atIndex:3];
173
+ [invocation setArgument:&onComplete atIndex:4];
174
+ [invocation invoke];
175
+
176
+ RCTLogInfo(@"[ModuleLoader] Bundle loaded via executeSourceCode:withSourceURL:onComplete:");
177
+ return YES;
178
+ } @catch (NSException *e) {
179
+ RCTLogWarn(@"[ModuleLoader] executeSourceCode:withSourceURL:onComplete: failed: %@", e.reason);
180
+ }
181
+ }
182
+
183
+ // 方法 2: 尝试通过 CatalystInstance(旧架构)
184
+ id catalystInstance = nil;
185
+ @try {
186
+ catalystInstance = [targetBridge valueForKey:@"_catalystInstance"];
187
+ } @catch (NSException *e) {
188
+ // 继续尝试其他方法
189
+ }
190
+
191
+ if (catalystInstance) {
192
+ RCTLogInfo(@"[ModuleLoader] Found CatalystInstance, trying legacy methods");
193
+
194
+ // 方法 2a: loadScriptFromString:sourceURL:
195
+ SEL loadScriptSel = NSSelectorFromString(@"loadScriptFromString:sourceURL:");
196
+ if ([catalystInstance respondsToSelector:loadScriptSel]) {
197
+ @try {
198
+ #pragma clang diagnostic push
199
+ #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
200
+ [catalystInstance performSelector:loadScriptSel withObject:bundleContent withObject:sourceURL];
201
+ #pragma clang diagnostic pop
202
+ RCTLogInfo(@"[ModuleLoader] Bundle loaded via loadScriptFromString");
203
+ return YES;
204
+ } @catch (NSException *e) {
205
+ RCTLogWarn(@"[ModuleLoader] loadScriptFromString failed: %@", e.reason);
206
+ }
207
+ }
208
+
209
+ // 方法 2b: executeSourceCode:sync:
210
+ SEL executeSel = NSSelectorFromString(@"executeSourceCode:sync:");
211
+ if ([catalystInstance respondsToSelector:executeSel]) {
212
+ @try {
213
+ #pragma clang diagnostic push
214
+ #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
215
+ [catalystInstance performSelector:executeSel withObject:bundleContent withObject:@NO];
216
+ #pragma clang diagnostic pop
217
+ RCTLogInfo(@"[ModuleLoader] Bundle loaded via executeSourceCode:sync:");
218
+ return YES;
219
+ } @catch (NSException *e) {
220
+ RCTLogWarn(@"[ModuleLoader] executeSourceCode:sync: failed: %@", e.reason);
221
+ }
222
+ }
223
+ } else {
224
+ RCTLogInfo(@"[ModuleLoader] CatalystInstance not available (likely new architecture)");
225
+ }
226
+
227
+ // 方法 3: 尝试使用 RCTCxxBridge 的方法
228
+ if ([NSStringFromClass([targetBridge class]) containsString:@"Cxx"]) {
229
+ RCTLogInfo(@"[ModuleLoader] Detected RCTCxxBridge, trying Cxx-specific methods");
230
+
231
+ SEL runJSBundleSel = NSSelectorFromString(@"runJSBundle:");
232
+ if ([targetBridge respondsToSelector:runJSBundleSel]) {
233
+ @try {
234
+ #pragma clang diagnostic push
235
+ #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
236
+ [targetBridge performSelector:runJSBundleSel withObject:sourceURL];
237
+ #pragma clang diagnostic pop
238
+ RCTLogInfo(@"[ModuleLoader] Bundle loaded via runJSBundle:");
239
+ return YES;
240
+ } @catch (NSException *e) {
241
+ RCTLogWarn(@"[ModuleLoader] runJSBundle: failed: %@", e.reason);
242
+ }
243
+ }
244
+ }
245
+
246
+ // 所有方法都失败
247
+ if (outError) {
248
+ *outError = [NSError errorWithDomain:@"ModuleLoader"
249
+ code:1001
250
+ userInfo:@{NSLocalizedDescriptionKey: @"No suitable bundle loading method found"}];
251
+ }
252
+
253
+ return NO;
254
+ }
255
+
256
+ #pragma mark - Main Load Method
257
+
119
258
  /**
120
259
  * 加载子 bundle
121
- *
122
- * @param bundleId 模块 ID(如 "home", "details", "settings")
123
- * @param bundlePath bundle 文件路径(如 "modules/home.jsbundle")
124
- * @param resolve 成功回调
125
- * @param reject 失败回调
126
260
  */
127
261
  RCT_EXPORT_METHOD(loadBusinessBundle:(NSString *)bundleId
128
262
  bundlePath:(NSString *)bundlePath
@@ -130,191 +264,70 @@ RCT_EXPORT_METHOD(loadBusinessBundle:(NSString *)bundleId
130
264
  rejecter:(RCTPromiseRejectBlock)reject)
131
265
  {
132
266
  RCTLogInfo(@"[ModuleLoader] loadBusinessBundle: bundleId=%@, bundlePath=%@", bundleId, bundlePath);
133
-
134
- // 获取完整路径
267
+
135
268
  NSString *fullPath = [self getFullBundlePath:bundlePath];
136
269
  if (!fullPath) {
137
- resolve(@{
138
- @"success": @NO,
139
- @"errorMessage": @"BUNDLE_PATH_NOT_FOUND"
140
- });
270
+ resolve(@{@"success": @NO, @"errorMessage": @"BUNDLE_PATH_NOT_FOUND"});
141
271
  return;
142
272
  }
143
-
144
- // 确保在主线程执行
273
+
145
274
  dispatch_async(dispatch_get_main_queue(), ^{
146
- // 尝试多种方式获取 bridge
147
- RCTBridge *bridge = nil;
148
-
149
- // 方法 1: 从 AppDelegate 获取(最可靠的方式)
150
- UIApplication *app = [UIApplication sharedApplication];
151
- id delegate = app.delegate;
152
- if (delegate && [delegate isKindOfClass:NSClassFromString(@"RCTAppDelegate")]) {
153
- // 尝试获取 bridge 属性
154
- if ([delegate respondsToSelector:@selector(bridge)]) {
155
- bridge = [delegate performSelector:@selector(bridge)];
156
- }
157
- // 尝试获取 reactNativeHost,然后获取 bridge
158
- if (!bridge && [delegate respondsToSelector:@selector(reactNativeHost)]) {
159
- id reactNativeHost = [delegate performSelector:@selector(reactNativeHost)];
160
- if (reactNativeHost && [reactNativeHost respondsToSelector:@selector(bridge)]) {
161
- bridge = [reactNativeHost performSelector:@selector(bridge)];
162
- }
163
- }
164
- }
165
-
166
- // 方法 2: 使用 self.bridge(如果可用,通过协议方法)
167
- if (!bridge) {
168
- // RCTBridgeModule 协议应该提供 bridge 属性
169
- // 但需要通过 valueForKey 或直接访问
170
- @try {
171
- bridge = [self valueForKey:@"bridge"];
172
- } @catch (NSException *e) {
173
- RCTLogWarn(@"[ModuleLoader] Could not access bridge via valueForKey: %@", e.reason);
174
- }
175
- }
176
-
275
+ RCTBridge *bridge = [self getCurrentBridge];
276
+
177
277
  if (!bridge) {
178
- RCTLogError(@"[ModuleLoader] Bridge not available - cannot load bundle");
179
- resolve(@{
180
- @"success": @NO,
181
- @"errorMessage": @"BRIDGE_NOT_AVAILABLE"
182
- });
278
+ RCTLogError(@"[ModuleLoader] Bridge not available");
279
+ resolve(@{@"success": @NO, @"errorMessage": @"BRIDGE_NOT_AVAILABLE"});
183
280
  return;
184
281
  }
185
-
282
+
186
283
  // 读取 bundle 内容
187
- NSError *error = nil;
284
+ NSError *readError = nil;
188
285
  NSString *bundleContent = [NSString stringWithContentsOfFile:fullPath
189
286
  encoding:NSUTF8StringEncoding
190
- error:&error];
191
- if (error || !bundleContent) {
192
- RCTLogError(@"[ModuleLoader] Failed to read bundle file: %@", error.localizedDescription);
193
- resolve(@{
194
- @"success": @NO,
195
- @"errorMessage": [NSString stringWithFormat:@"READ_ERROR: %@", error.localizedDescription ?: @"Unknown error"]
196
- });
287
+ error:&readError];
288
+ if (readError || !bundleContent) {
289
+ RCTLogError(@"[ModuleLoader] Failed to read bundle: %@", readError.localizedDescription);
290
+ resolve(@{@"success": @NO, @"errorMessage": [NSString stringWithFormat:@"READ_ERROR: %@", readError.localizedDescription ?: @"Unknown"]});
197
291
  return;
198
292
  }
199
-
200
- // 使用 CatalystInstance 执行脚本
201
- // 注意:这是 React Native 的内部 API,但这是动态加载 bundle 的标准方式
202
- @try {
203
- // 获取 CatalystInstance
204
- id catalystInstance = nil;
205
-
206
- // 尝试从 bridge 获取 batchedBridge(旧架构)
207
- if ([bridge respondsToSelector:@selector(batchedBridge)]) {
208
- catalystInstance = [bridge performSelector:@selector(batchedBridge)];
209
- }
210
-
211
- // 如果失败,尝试直接获取 _catalystInstance(私有属性)
212
- if (!catalystInstance) {
213
- @try {
214
- catalystInstance = [bridge valueForKey:@"_catalystInstance"];
215
- } @catch (NSException *e) {
216
- RCTLogWarn(@"[ModuleLoader] Could not access _catalystInstance: %@", e.reason);
217
- }
218
- }
219
-
220
- if (catalystInstance) {
221
- // 方法 1: 尝试 loadScriptFromString:sourceURL:
222
- SEL loadScriptSelector = NSSelectorFromString(@"loadScriptFromString:sourceURL:");
223
- if ([catalystInstance respondsToSelector:loadScriptSelector]) {
224
- NSURL *sourceURL = [NSURL fileURLWithPath:fullPath];
225
- #pragma clang diagnostic push
226
- #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
227
- [catalystInstance performSelector:loadScriptSelector withObject:bundleContent withObject:sourceURL];
228
- #pragma clang diagnostic pop
229
-
230
- RCTLogInfo(@"[ModuleLoader] Bundle loaded successfully via loadScriptFromString: %@", bundleId);
231
- resolve(@{@"success": @YES});
232
- return;
233
- }
234
-
235
- // 方法 2: 尝试 executeSourceCode:sync:
236
- SEL executeSelector = NSSelectorFromString(@"executeSourceCode:sync:");
237
- if ([catalystInstance respondsToSelector:executeSelector]) {
238
- #pragma clang diagnostic push
239
- #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
240
- [catalystInstance performSelector:executeSelector withObject:bundleContent withObject:@NO];
241
- #pragma clang diagnostic pop
242
-
243
- RCTLogInfo(@"[ModuleLoader] Bundle loaded successfully via executeSourceCode: %@", bundleId);
244
- resolve(@{@"success": @YES});
245
- return;
246
- }
247
-
248
- // 方法 3: 尝试 loadScriptFromFile:withSourceURL:
249
- SEL loadFileSelector = NSSelectorFromString(@"loadScriptFromFile:withSourceURL:");
250
- if ([catalystInstance respondsToSelector:loadFileSelector]) {
251
- NSURL *sourceURL = [NSURL fileURLWithPath:fullPath];
252
- #pragma clang diagnostic push
253
- #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
254
- [catalystInstance performSelector:loadFileSelector withObject:fullPath withObject:sourceURL];
255
- #pragma clang diagnostic pop
256
-
257
- RCTLogInfo(@"[ModuleLoader] Bundle loaded successfully via loadScriptFromFile: %@", bundleId);
258
- resolve(@{@"success": @YES});
259
- return;
260
- }
261
-
262
- RCTLogError(@"[ModuleLoader] CatalystInstance found but no suitable method available");
263
- } else {
264
- RCTLogError(@"[ModuleLoader] CatalystInstance not found");
265
- }
266
-
267
- // 如果所有方法都失败,在开发环境下返回成功(用于测试)
268
- #if DEBUG
269
- RCTLogWarn(@"[ModuleLoader] Using debug fallback - bundle may not execute");
293
+
294
+ NSURL *sourceURL = [NSURL fileURLWithPath:fullPath];
295
+ NSError *loadError = nil;
296
+
297
+ BOOL success = [self tryLoadBundle:bundleContent sourceURL:sourceURL bridge:bridge error:&loadError];
298
+
299
+ if (success) {
300
+ RCTLogInfo(@"[ModuleLoader] Bundle loaded successfully: %@", bundleId);
270
301
  resolve(@{@"success": @YES});
302
+ } else {
303
+ // 在 DEBUG 模式下返回成功(因为模块可能已经在主 bundle 中)
304
+ #if DEBUG
305
+ RCTLogWarn(@"[ModuleLoader] Native loading failed, but continuing (DEBUG mode)");
306
+ RCTLogWarn(@"[ModuleLoader] Note: In new architecture, dynamic bundle loading has limitations");
307
+ RCTLogWarn(@"[ModuleLoader] The module may need to be included in the main bundle");
308
+ resolve(@{@"success": @YES, @"warning": @"NATIVE_LOADING_FAILED_DEBUG_FALLBACK"});
271
309
  #else
272
- resolve(@{
273
- @"success": @NO,
274
- @"errorMessage": @"CATALYST_INSTANCE_METHOD_NOT_FOUND"
275
- });
310
+ RCTLogError(@"[ModuleLoader] Failed to load bundle: %@", loadError.localizedDescription);
311
+ resolve(@{@"success": @NO, @"errorMessage": loadError.localizedDescription ?: @"UNKNOWN_ERROR"});
276
312
  #endif
277
-
278
- } @catch (NSException *exception) {
279
- RCTLogError(@"[ModuleLoader] Exception while loading bundle: %@", exception.reason);
280
- resolve(@{
281
- @"success": @NO,
282
- @"errorMessage": [NSString stringWithFormat:@"EXCEPTION: %@", exception.reason ?: @"Unknown exception"]
283
- });
284
313
  }
285
314
  });
286
315
  }
287
316
 
288
317
  #pragma mark - Bundle Manifest Methods
289
318
 
290
- /**
291
- * 获取 Application Support 目录路径
292
- */
293
- + (NSString *)getApplicationSupportDirectory
294
- {
295
- NSString *applicationSupportDirectory = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) objectAtIndex:0];
296
- return applicationSupportDirectory;
319
+ + (NSString *)getApplicationSupportDirectory {
320
+ return [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) objectAtIndex:0];
297
321
  }
298
322
 
299
- /**
300
- * 获取 bundle assets 路径
301
- */
302
- + (NSString *)bundleAssetsPath
303
- {
304
- NSString *resourcePath = [[NSBundle mainBundle] resourcePath];
305
- return [resourcePath stringByAppendingPathComponent:@"assets"];
323
+ + (NSString *)bundleAssetsPath {
324
+ return [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"assets"];
306
325
  }
307
326
 
308
- /**
309
- * 读取文件内容
310
- */
311
- - (NSString *)readFileContent:(NSString *)filePath
312
- {
327
+ - (NSString *)readFileContent:(NSString *)filePath {
313
328
  @try {
314
329
  NSError *error;
315
- NSString *content = [NSString stringWithContentsOfFile:filePath
316
- encoding:NSUTF8StringEncoding
317
- error:&error];
330
+ NSString *content = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];
318
331
  if (error) {
319
332
  RCTLogWarn(@"[ModuleLoader] Failed to read file: %@ - %@", filePath, error.localizedDescription);
320
333
  return nil;
@@ -326,78 +339,62 @@ RCT_EXPORT_METHOD(loadBusinessBundle:(NSString *)bundleId
326
339
  }
327
340
  }
328
341
 
329
- /**
330
- * 获取当前 bundle manifest 文件路径
331
- * 查找顺序:
332
- * 1. Bundles 目录
333
- * 2. Documents 目录
334
- * 3. MainBundle assets 目录
335
- */
336
342
  RCT_EXPORT_METHOD(getCurrentBundleManifest:(RCTPromiseResolveBlock)resolve
337
343
  rejecter:(RCTPromiseRejectBlock)reject)
338
344
  {
339
345
  @try {
340
346
  NSFileManager *fileManager = [NSFileManager defaultManager];
341
-
342
- // 1. 先尝试从 MainBundle/Bundles 目录获取
347
+
348
+ // 1. MainBundle/Bundles 目录
343
349
  NSString *bundlesPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"Bundles"];
344
350
  NSString *bundlesManifestPath = [bundlesPath stringByAppendingPathComponent:BundleManifestFileName];
345
351
  if ([fileManager fileExistsAtPath:bundlesManifestPath]) {
346
- RCTLogInfo(@"[ModuleLoader] Found bundle manifest at Bundles: %@", bundlesManifestPath);
352
+ RCTLogInfo(@"[ModuleLoader] Found manifest at Bundles: %@", bundlesManifestPath);
347
353
  resolve(bundlesManifestPath);
348
354
  return;
349
355
  }
350
-
351
- // 2. 尝试从 Documents 目录获取
356
+
357
+ // 2. Documents 目录
352
358
  NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
353
359
  if (documentsPath) {
354
360
  NSString *documentsManifestPath = [documentsPath stringByAppendingPathComponent:BundleManifestFileName];
355
361
  if ([fileManager fileExistsAtPath:documentsManifestPath]) {
356
- RCTLogInfo(@"[ModuleLoader] Found bundle manifest at Documents: %@", documentsManifestPath);
357
362
  resolve(documentsManifestPath);
358
363
  return;
359
364
  }
360
365
  }
361
-
362
- // 3. 尝试从 Application Support 目录获取
366
+
367
+ // 3. Application Support 目录
363
368
  NSString *appSupportPath = [[self class] getApplicationSupportDirectory];
364
369
  NSString *appSupportManifestPath = [appSupportPath stringByAppendingPathComponent:BundleManifestFileName];
365
370
  if ([fileManager fileExistsAtPath:appSupportManifestPath]) {
366
- RCTLogInfo(@"[ModuleLoader] Found bundle manifest at Application Support: %@", appSupportManifestPath);
367
371
  resolve(appSupportManifestPath);
368
372
  return;
369
373
  }
370
-
371
- // 4. 尝试从 MainBundle assets 目录获取
374
+
375
+ // 4. MainBundle assets 目录
372
376
  NSString *assetsPath = [[self class] bundleAssetsPath];
373
377
  NSString *assetsManifestPath = [assetsPath stringByAppendingPathComponent:BundleManifestFileName];
374
378
  if ([fileManager fileExistsAtPath:assetsManifestPath]) {
375
- RCTLogInfo(@"[ModuleLoader] Found bundle manifest at assets: %@", assetsManifestPath);
376
379
  resolve(assetsManifestPath);
377
380
  return;
378
381
  }
379
-
380
- // 5. 尝试从 MainBundle 根目录获取
382
+
383
+ // 5. MainBundle 根目录
381
384
  NSString *mainBundleManifestPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:BundleManifestFileName];
382
385
  if ([fileManager fileExistsAtPath:mainBundleManifestPath]) {
383
- RCTLogInfo(@"[ModuleLoader] Found bundle manifest at MainBundle: %@", mainBundleManifestPath);
384
386
  resolve(mainBundleManifestPath);
385
387
  return;
386
388
  }
387
-
388
- // 未找到 manifest 文件
389
- RCTLogWarn(@"[ModuleLoader] Bundle manifest not found in any location");
389
+
390
+ RCTLogWarn(@"[ModuleLoader] Bundle manifest not found");
390
391
  resolve(nil);
391
392
  } @catch (NSException *exception) {
392
- RCTLogError(@"[ModuleLoader] Failed to get current bundle manifest: %@", exception.reason);
393
+ RCTLogError(@"[ModuleLoader] Failed to get manifest: %@", exception.reason);
393
394
  reject(@"MANIFEST_ERROR", @"Failed to get manifest path", nil);
394
395
  }
395
396
  }
396
397
 
397
- /**
398
- * 获取当前 bundle manifest 文件内容
399
- * 查找顺序与 getCurrentBundleManifest 相同
400
- */
401
398
  RCT_EXPORT_METHOD(getCurrentBundleManifestContent:(RCTPromiseResolveBlock)resolve
402
399
  rejecter:(RCTPromiseRejectBlock)reject)
403
400
  {
@@ -405,85 +402,68 @@ RCT_EXPORT_METHOD(getCurrentBundleManifestContent:(RCTPromiseResolveBlock)resolv
405
402
  @try {
406
403
  NSFileManager *fileManager = [NSFileManager defaultManager];
407
404
  NSString *manifestContent = nil;
408
-
409
- // 1. 先尝试从 MainBundle/Bundles 目录获取
405
+
406
+ // 1. MainBundle/Bundles 目录
410
407
  NSString *bundlesPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"Bundles"];
411
408
  NSString *bundlesManifestPath = [bundlesPath stringByAppendingPathComponent:BundleManifestFileName];
412
409
  if ([fileManager fileExistsAtPath:bundlesManifestPath]) {
413
410
  manifestContent = [self readFileContent:bundlesManifestPath];
414
411
  if (manifestContent) {
415
- RCTLogInfo(@"[ModuleLoader] Read bundle manifest from Bundles");
416
- dispatch_async(dispatch_get_main_queue(), ^{
417
- resolve(manifestContent);
418
- });
412
+ RCTLogInfo(@"[ModuleLoader] Read manifest from Bundles");
413
+ dispatch_async(dispatch_get_main_queue(), ^{ resolve(manifestContent); });
419
414
  return;
420
415
  }
421
416
  }
422
-
423
- // 2. 尝试从 Documents 目录获取
417
+
418
+ // 2. Documents 目录
424
419
  NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
425
420
  if (documentsPath) {
426
421
  NSString *documentsManifestPath = [documentsPath stringByAppendingPathComponent:BundleManifestFileName];
427
422
  if ([fileManager fileExistsAtPath:documentsManifestPath]) {
428
423
  manifestContent = [self readFileContent:documentsManifestPath];
429
424
  if (manifestContent) {
430
- RCTLogInfo(@"[ModuleLoader] Read bundle manifest from Documents");
431
- dispatch_async(dispatch_get_main_queue(), ^{
432
- resolve(manifestContent);
433
- });
425
+ dispatch_async(dispatch_get_main_queue(), ^{ resolve(manifestContent); });
434
426
  return;
435
427
  }
436
428
  }
437
429
  }
438
-
439
- // 3. 尝试从 Application Support 目录获取
430
+
431
+ // 3. Application Support 目录
440
432
  NSString *appSupportPath = [[self class] getApplicationSupportDirectory];
441
433
  NSString *appSupportManifestPath = [appSupportPath stringByAppendingPathComponent:BundleManifestFileName];
442
434
  if ([fileManager fileExistsAtPath:appSupportManifestPath]) {
443
435
  manifestContent = [self readFileContent:appSupportManifestPath];
444
436
  if (manifestContent) {
445
- RCTLogInfo(@"[ModuleLoader] Read bundle manifest from Application Support");
446
- dispatch_async(dispatch_get_main_queue(), ^{
447
- resolve(manifestContent);
448
- });
437
+ dispatch_async(dispatch_get_main_queue(), ^{ resolve(manifestContent); });
449
438
  return;
450
439
  }
451
440
  }
452
-
453
- // 4. 尝试从 MainBundle assets 目录获取
441
+
442
+ // 4. MainBundle assets 目录
454
443
  NSString *assetsPath = [[self class] bundleAssetsPath];
455
444
  NSString *assetsManifestPath = [assetsPath stringByAppendingPathComponent:BundleManifestFileName];
456
445
  if ([fileManager fileExistsAtPath:assetsManifestPath]) {
457
446
  manifestContent = [self readFileContent:assetsManifestPath];
458
447
  if (manifestContent) {
459
- RCTLogInfo(@"[ModuleLoader] Read bundle manifest from assets");
460
- dispatch_async(dispatch_get_main_queue(), ^{
461
- resolve(manifestContent);
462
- });
448
+ dispatch_async(dispatch_get_main_queue(), ^{ resolve(manifestContent); });
463
449
  return;
464
450
  }
465
451
  }
466
-
467
- // 5. 尝试从 MainBundle 根目录获取
452
+
453
+ // 5. MainBundle 根目录
468
454
  NSString *mainBundleManifestPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:BundleManifestFileName];
469
455
  if ([fileManager fileExistsAtPath:mainBundleManifestPath]) {
470
456
  manifestContent = [self readFileContent:mainBundleManifestPath];
471
457
  if (manifestContent) {
472
- RCTLogInfo(@"[ModuleLoader] Read bundle manifest from MainBundle");
473
- dispatch_async(dispatch_get_main_queue(), ^{
474
- resolve(manifestContent);
475
- });
458
+ dispatch_async(dispatch_get_main_queue(), ^{ resolve(manifestContent); });
476
459
  return;
477
460
  }
478
461
  }
479
-
480
- // 未找到 manifest 文件
481
- RCTLogWarn(@"[ModuleLoader] Bundle manifest content not found in any location");
482
- dispatch_async(dispatch_get_main_queue(), ^{
483
- resolve(nil);
484
- });
462
+
463
+ RCTLogWarn(@"[ModuleLoader] Bundle manifest content not found");
464
+ dispatch_async(dispatch_get_main_queue(), ^{ resolve(nil); });
485
465
  } @catch (NSException *exception) {
486
- RCTLogError(@"[ModuleLoader] Failed to get bundle manifest content: %@", exception.reason);
466
+ RCTLogError(@"[ModuleLoader] Failed to get manifest content: %@", exception.reason);
487
467
  dispatch_async(dispatch_get_main_queue(), ^{
488
468
  reject(@"MANIFEST_CONTENT_ERROR", @"Failed to read manifest content", nil);
489
469
  });
@@ -492,4 +472,3 @@ RCT_EXPORT_METHOD(getCurrentBundleManifestContent:(RCTPromiseResolveBlock)resolv
492
472
  }
493
473
 
494
474
  @end
495
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bm-fe/react-native-multi-bundle",
3
- "version": "1.0.0-beta.5",
3
+ "version": "1.0.0-beta.6",
4
4
  "description": "React Native 多 Bundle 系统 - 支持模块按需加载和独立更新",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -342,3 +342,4 @@ function preloadModule(moduleId: string): Promise<void>
342
342
  - [多 Bundle 架构设计](../docs/React%20Native%20多%20bundle%20技术方案.md)
343
343
 
344
344
 
345
+