react-native-3rddigital-appupdate 1.0.19 → 1.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/scripts/bundle.js +164 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-3rddigital-appupdate",
3
- "version": "1.0.19",
3
+ "version": "1.0.20",
4
4
  "description": "A React Native library for seamless over-the-air (OTA) updates with version checks, automatic bundle download, and customizable user prompts for iOS and Android.",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
package/scripts/bundle.js CHANGED
@@ -169,6 +169,54 @@ function findFirstXcodeProj(dir) {
169
169
  return null;
170
170
  }
171
171
 
172
+ function findFirstXcworkspace(dir) {
173
+ const files = fs.readdirSync(dir);
174
+
175
+ for (const file of files) {
176
+ const fullPath = path.join(dir, file);
177
+ const stat = fs.statSync(fullPath);
178
+
179
+ if (stat.isDirectory()) {
180
+ if (file.endsWith('.xcworkspace') && file !== 'Pods.xcworkspace') {
181
+ return fullPath;
182
+ }
183
+ const nested = findFirstXcworkspace(fullPath);
184
+ if (nested) return nested;
185
+ }
186
+ }
187
+
188
+ return null;
189
+ }
190
+
191
+ function quoteShellArg(value) {
192
+ return `'${String(value).replace(/'/g, `'\\''`)}'`;
193
+ }
194
+
195
+ function runAndCapture(command, cwd = process.cwd()) {
196
+ try {
197
+ return execSync(command, {
198
+ cwd,
199
+ stdio: ['ignore', 'pipe', 'pipe'],
200
+ })
201
+ .toString()
202
+ .trim();
203
+ } catch (_error) {
204
+ return null;
205
+ }
206
+ }
207
+
208
+ function extractJsonObject(text) {
209
+ if (!text) return null;
210
+
211
+ const start = text.indexOf('{');
212
+ const end = text.lastIndexOf('}');
213
+ if (start === -1 || end === -1 || end <= start) {
214
+ return null;
215
+ }
216
+
217
+ return text.slice(start, end + 1);
218
+ }
219
+
172
220
  function extractBracedBlock(content, startIndex) {
173
221
  const openIndex = content.indexOf('{', startIndex);
174
222
  if (openIndex === -1) return null;
@@ -472,19 +520,134 @@ function getIosProjectFiles() {
472
520
  return null;
473
521
  }
474
522
 
523
+ const xcworkspacePath = findFirstXcworkspace(iosDir);
524
+
475
525
  const pbxprojPath = path.join(xcodeProjPath, 'project.pbxproj');
476
526
  if (!fs.existsSync(pbxprojPath)) {
477
527
  console.warn('⚠️ project.pbxproj not found.');
478
528
  return null;
479
529
  }
480
530
 
481
- return { iosDir, xcodeProjPath, pbxprojPath };
531
+ return { iosDir, xcodeProjPath, xcworkspacePath, pbxprojPath };
532
+ }
533
+
534
+ function getIosBuildContainerArgs(projectFiles) {
535
+ if (projectFiles.xcworkspacePath) {
536
+ return `-workspace ${quoteShellArg(projectFiles.xcworkspacePath)}`;
537
+ }
538
+
539
+ return `-project ${quoteShellArg(projectFiles.xcodeProjPath)}`;
540
+ }
541
+
542
+ function parseXcodebuildSettings(output) {
543
+ const settings = {};
544
+
545
+ for (const line of output.split('\n')) {
546
+ const match = line.match(/^\s*([A-Za-z0-9_]+)\s*=\s*(.+)$/);
547
+ if (!match) continue;
548
+ settings[match[1]] = cleanPbxString(match[2]);
549
+ }
550
+
551
+ return settings;
552
+ }
553
+
554
+ function isResolvedBuildSetting(value) {
555
+ if (!value) return false;
556
+
557
+ return !/[()$]/.test(value);
558
+ }
559
+
560
+ function getIosSchemeMetadataFromXcodebuild(projectFiles) {
561
+ const projectRoot = getProjectRoot();
562
+ const buildContainerArgs = getIosBuildContainerArgs(projectFiles);
563
+ const listOutput = runAndCapture(
564
+ `xcodebuild -list -json ${buildContainerArgs}`,
565
+ projectRoot
566
+ );
567
+
568
+ if (!listOutput) return null;
569
+
570
+ try {
571
+ const jsonOutput = extractJsonObject(listOutput);
572
+ if (!jsonOutput) return null;
573
+
574
+ const parsed = JSON.parse(jsonOutput);
575
+ const schemes =
576
+ parsed.project?.schemes ??
577
+ parsed.workspace?.schemes ??
578
+ parsed.project?.targets ??
579
+ [];
580
+
581
+ const uniqueSchemes = [...new Set(schemes)].filter(Boolean);
582
+ if (!uniqueSchemes.length) return null;
583
+
584
+ const entries = uniqueSchemes
585
+ .map((schemeName) => {
586
+ const buildConfigurations = ['Release', 'Profile', 'Debug'];
587
+ const buildSettingsOutput = buildConfigurations
588
+ .map((configuration) => ({
589
+ configuration,
590
+ output: runAndCapture(
591
+ `xcodebuild -showBuildSettings ${buildContainerArgs} -scheme ${quoteShellArg(
592
+ schemeName
593
+ )} -configuration ${quoteShellArg(configuration)}`,
594
+ projectRoot
595
+ ),
596
+ }))
597
+ .find((item) => item.output)?.output;
598
+
599
+ if (!buildSettingsOutput) return null;
600
+
601
+ const settings = parseXcodebuildSettings(buildSettingsOutput);
602
+ const appId = isResolvedBuildSetting(settings.PRODUCT_BUNDLE_IDENTIFIER)
603
+ ? settings.PRODUCT_BUNDLE_IDENTIFIER
604
+ : null;
605
+ const version = isResolvedBuildSetting(settings.MARKETING_VERSION)
606
+ ? settings.MARKETING_VERSION
607
+ : null;
608
+ const targetName =
609
+ settings.TARGET_NAME || settings.PRODUCT_NAME || schemeName;
610
+
611
+ return {
612
+ name: schemeName,
613
+ targetName,
614
+ label: schemeName,
615
+ appId,
616
+ version,
617
+ productName: settings.PRODUCT_NAME || targetName,
618
+ buildConfiguration: settings.CONFIGURATION || 'Release',
619
+ };
620
+ })
621
+ .filter(Boolean);
622
+
623
+ if (!entries.length) return null;
624
+
625
+ const dedupedEntries = entries.filter(
626
+ (entry, index, allEntries) =>
627
+ allEntries.findIndex(
628
+ (candidate) =>
629
+ candidate.name === entry.name && candidate.appId === entry.appId
630
+ ) === index
631
+ );
632
+
633
+ return {
634
+ defaultConfig: dedupedEntries[0] ?? null,
635
+ targets: dedupedEntries,
636
+ };
637
+ } catch (_error) {
638
+ return null;
639
+ }
482
640
  }
483
641
 
484
642
  function getIosTargetMetadata() {
485
643
  const projectFiles = getIosProjectFiles();
486
644
  if (!projectFiles) return null;
487
645
 
646
+ const xcodebuildMetadata = getIosSchemeMetadataFromXcodebuild(projectFiles);
647
+ if (xcodebuildMetadata?.targets?.length) {
648
+ return xcodebuildMetadata;
649
+ }
650
+
488
651
  const pbxprojContent = fs.readFileSync(projectFiles.pbxprojPath, 'utf8');
489
652
  const configObjects = parsePbxprojObjectsByIsa(
490
653
  pbxprojContent,