vxrn 2.0.0-beta.115.1 → 2.0.0-beta.120.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.
@@ -5,6 +5,7 @@ import { pathToFileURL } from "node:url";
5
5
  import { validateNativeApp } from "@vxrn/utils/nativeAppManifest";
6
6
  import FSExtra from "fs-extra";
7
7
  import sharp from "sharp";
8
+ import { kotlinSourceId, renderKotlinSourceGlue, swiftPodManifest, writeNativeSourceDeclaration, writeSwiftPackageArtifacts } from "../utils/nativeSourceContract.mjs";
8
9
  import { swiftPackageDirectories } from "../utils/swiftPackageId.mjs";
9
10
 
10
11
  const nativeProjectPatches = module.createRequire(import.meta.url)("../../native-project-patches.cjs");
@@ -1294,19 +1295,41 @@ ${schemes.map((scheme) => ` <data android:scheme="${scheme}" />`).joi
1294
1295
  }
1295
1296
  }
1296
1297
  if (platform === "ios" && relativePath.endsWith("/Info.plist")) {
1297
- const usage = [];
1298
+ const usage = /* @__PURE__ */ new Map();
1298
1299
  if (app.imagePicker?.camera !== void 0) {
1299
- usage.push(["NSCameraUsageDescription", app.imagePicker.camera]);
1300
+ usage.set("NSCameraUsageDescription", app.imagePicker.camera);
1301
+ }
1302
+ if (app.photoLibrary !== void 0) {
1303
+ usage.set("NSPhotoLibraryAddUsageDescription", app.photoLibrary.addOnly);
1304
+ }
1305
+ if (app.location !== void 0) {
1306
+ usage.set("NSLocationWhenInUseUsageDescription", app.location.whenInUse);
1307
+ }
1308
+ if (app.ios?.faceIdUsageDescription !== void 0) {
1309
+ usage.set("NSFaceIDUsageDescription", app.ios.faceIdUsageDescription);
1300
1310
  }
1301
1311
  if (app.speech !== void 0) {
1302
- usage.push(["NSSpeechRecognitionUsageDescription", app.speech.recognition], ["NSMicrophoneUsageDescription", app.speech.microphone]);
1312
+ usage.set("NSSpeechRecognitionUsageDescription", app.speech.recognition);
1313
+ usage.set("NSMicrophoneUsageDescription", app.speech.microphone);
1314
+ }
1315
+ if (app.audio !== void 0) {
1316
+ usage.set("NSMicrophoneUsageDescription", app.audio.microphone);
1317
+ }
1318
+ const additions = [];
1319
+ for (const [key, text] of usage) {
1320
+ const existing = new RegExp(`(<key>${key}</key>\\s*<string>)[^<]*(</string>)`);
1321
+ if (existing.test(rendered)) {
1322
+ rendered = rendered.replace(existing, (_match, before, after) => `${before}${escapeXml(text)}${after}`);
1323
+ } else {
1324
+ additions.push([key, text]);
1325
+ }
1303
1326
  }
1304
- if (usage.length) {
1327
+ if (additions.length) {
1305
1328
  const anchor = " <key>LSRequiresIPhoneOS</key>";
1306
1329
  if (!rendered.includes(anchor)) {
1307
- throw new Error(`[vxrn] cannot stamp ${usage[0][0]}: expected LSRequiresIPhoneOS in Info.plist`);
1330
+ throw new Error(`[vxrn] cannot stamp ${additions[0][0]}: expected LSRequiresIPhoneOS in Info.plist`);
1308
1331
  }
1309
- rendered = rendered.replace(anchor, `${usage.map(([key, text]) => ` <key>${key}</key>
1332
+ rendered = rendered.replace(anchor, `${additions.map(([key, text]) => ` <key>${key}</key>
1310
1333
  <string>${escapeXml(text)}</string>
1311
1334
  `).join("")}${anchor}`);
1312
1335
  }
@@ -1458,7 +1481,42 @@ const generateForPlatform = async (root, platform, app, outDir = path.resolve(ro
1458
1481
  root,
1459
1482
  dest
1460
1483
  });
1484
+ if (platform === "android") generateKotlinSources({
1485
+ root,
1486
+ dest
1487
+ });
1461
1488
  };
1489
+ function generateKotlinSources({ root, dest }) {
1490
+ const skip = /* @__PURE__ */ new Set([
1491
+ "node_modules",
1492
+ "ios",
1493
+ "android",
1494
+ "dist",
1495
+ "types",
1496
+ "build",
1497
+ "tests",
1498
+ "__tests__",
1499
+ "scripts"
1500
+ ]);
1501
+ const collect = (dir) => {
1502
+ for (const entry of FSExtra.readdirSync(dir, { withFileTypes: true })) {
1503
+ if (entry.name.startsWith(".") || skip.has(entry.name)) continue;
1504
+ const source = path.join(dir, entry.name);
1505
+ if (entry.isDirectory()) collect(source);
1506
+ else if (entry.isFile() && entry.name.endsWith(".kt")) {
1507
+ const id = kotlinSourceId(root, source);
1508
+ const target = path.join(dest, "app/src/main/java/one/source", id);
1509
+ FSExtra.mkdirSync(target, { recursive: true });
1510
+ FSExtra.copyFileSync(source, path.join(target, entry.name));
1511
+ const contract = writeNativeSourceDeclaration(source);
1512
+ if (contract.modules.length > 0) {
1513
+ FSExtra.writeFileSync(path.join(target, `OneNativeSource_${id}.kt`), renderKotlinSourceGlue(id, contract).source);
1514
+ }
1515
+ }
1516
+ }
1517
+ };
1518
+ collect(root);
1519
+ }
1462
1520
  function generateSwiftPackages({ root, dest }) {
1463
1521
  const skip = /* @__PURE__ */ new Set([
1464
1522
  "node_modules",
@@ -1470,6 +1528,7 @@ function generateSwiftPackages({ root, dest }) {
1470
1528
  ]);
1471
1529
  for (const [id, packageDir] of swiftPackageDirectories(root)) {
1472
1530
  const podDir = path.join(dest, "OneSwiftPackages", id);
1531
+ const manifest = swiftPodManifest(path.join(packageDir, "Package.swift"), FSExtra.readFileSync(path.join(packageDir, "Package.swift"), "utf8"));
1473
1532
  const sources = [];
1474
1533
  const collect = (dir) => {
1475
1534
  for (const entry of FSExtra.readdirSync(dir, { withFileTypes: true })) {
@@ -1480,11 +1539,20 @@ function generateSwiftPackages({ root, dest }) {
1480
1539
  }
1481
1540
  };
1482
1541
  collect(packageDir);
1542
+ const artifacts = writeSwiftPackageArtifacts(packageDir);
1543
+ const contracts = artifacts.contracts;
1544
+ const hasView = contracts.some((contract) => contract.defaultView);
1545
+ if (contracts.filter((contract) => contract.defaultView).length > 1) {
1546
+ throw new Error(`[vxrn] swift package ${packageDir} has more than one @main type`);
1547
+ }
1483
1548
  for (const source of sources) {
1484
1549
  const link = path.join(podDir, "Sources", path.relative(packageDir, source));
1485
1550
  FSExtra.mkdirSync(path.dirname(link), { recursive: true });
1486
1551
  FSExtra.symlinkSync(FSExtra.realpathSync(source), link);
1487
1552
  }
1553
+ if (contracts.some((contract) => contract.modules.length > 0)) {
1554
+ FSExtra.writeFileSync(path.join(podDir, "Sources", "OneNativeSource.generated.swift"), artifacts.glue);
1555
+ }
1488
1556
  FSExtra.writeFileSync(path.join(podDir, `${id}.podspec`), `Pod::Spec.new do |s|
1489
1557
  s.name = '${id}'
1490
1558
  s.version = '0.0.0'
@@ -1493,15 +1561,15 @@ function generateSwiftPackages({ root, dest }) {
1493
1561
  s.license = 'MIT'
1494
1562
  s.author = 'one'
1495
1563
  s.source = { :path => '.' }
1496
- s.swift_version = '6.0'
1564
+ s.swift_version = '${manifest.languageMode}.0'
1497
1565
  s.source_files = 'Sources/**/*.swift', 'Register.m'
1498
1566
  s.dependency 'One'
1499
1567
  s.pod_target_xcconfig = {
1500
- 'OTHER_SWIFT_FLAGS' => '$(inherited) -cxx-interoperability-mode=default -Xcc -std=c++20 -Xfrontend -import-module -Xfrontend One -Xfrontend -entry-point-function-name -Xfrontend ${id}_main',
1568
+ 'OTHER_SWIFT_FLAGS' => '$(inherited) -cxx-interoperability-mode=default -Xcc -std=c++20 -Xfrontend -import-module -Xfrontend One${manifest.mainActorIsolation ? " -default-isolation MainActor" : ""}${hasView ? ` -Xfrontend -entry-point-function-name -Xfrontend ${id}_main` : ""}',
1501
1569
  }
1502
1570
  end
1503
1571
  `);
1504
- FSExtra.writeFileSync(path.join(podDir, "Register.m"), `#import <Foundation/Foundation.h>
1572
+ FSExtra.writeFileSync(path.join(podDir, "Register.m"), hasView ? `#import <Foundation/Foundation.h>
1505
1573
 
1506
1574
  extern int ${id}_main(int argc, char **argv);
1507
1575
  extern void OneSwiftRegisterPackage(const char *name, int (*entry)(int, char **));
@@ -1514,7 +1582,7 @@ extern void OneSwiftRegisterPackage(const char *name, int (*entry)(int, char **)
1514
1582
  OneSwiftRegisterPackage("${id}", ${id}_main);
1515
1583
  }
1516
1584
  @end
1517
- `);
1585
+ ` : "#import <Foundation/Foundation.h>\n");
1518
1586
  }
1519
1587
  }
1520
1588
  function applyAndroidDependencyPatches(args) {
@@ -1523,7 +1591,7 @@ function applyAndroidDependencyPatches(args) {
1523
1591
  return;
1524
1592
  }
1525
1593
  const activityPath = path.join(root, "android", "app", "src", "main", "java", ...app.android.applicationId.split("."), "MainActivity.kt");
1526
- const rendered = nativeProjectPatches.addReactNativeScreensFix(FSExtra.readFileSync(activityPath, "utf8"));
1594
+ const rendered = nativeProjectPatches.holdLaunchScreenInMainActivity(nativeProjectPatches.addReactNativeScreensFix(FSExtra.readFileSync(activityPath, "utf8")));
1527
1595
  if (!rendered.includes("RNScreensFragmentFactory")) {
1528
1596
  throw new Error("[vxrn] failed to apply the react-native-screens activity patch");
1529
1597
  }