peachsim 0.1.1619 → 0.1.1625
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.
- package/README.md +35 -5
- package/cli/app-fonts.ts +2 -1
- package/cli/bridge-flow-runner.ts +222 -175
- package/cli/cloud-client.ts +81 -48
- package/cli/commands/box/search-command.ts +3 -2
- package/cli/commands/box.ts +10 -29
- package/cli/commands/control.ts +108 -43
- package/cli/commands/inspect/settling.ts +57 -20
- package/cli/commands/maestro-compare.ts +11 -32
- package/cli/commands/platform.ts +74 -17
- package/cli/commands/record.ts +20 -15
- package/cli/commands/upload.ts +5 -1
- package/cli/dev-build-freshness.ts +37 -2
- package/cli/drivers/playwright-sim-host.ts +18 -6
- package/cli/maestro-accessibility.ts +53 -0
- package/cli/maestro-compare.ts +24 -0
- package/cli/outbound-endpoints.ts +27 -8
- package/cli/parent-pid.ts +0 -9
- package/cli/remote-simulators.ts +112 -0
- package/cli/remoteSimulatorContract.ts +21 -0
- package/detox/expectations.ts +5 -1
- package/detox/index.ts +133 -98
- package/detox/motion-change.cjs +37 -0
- package/detox/motion-change.d.cts +16 -0
- package/detox/navigation-lifecycle.ts +22 -4
- package/dist-lib/agent-daemon-client.cjs +3 -2
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +3 -2
- package/dist-lib/backend-origin.cjs +1 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -1
- package/dist-lib/bridge-contract-input.cjs +1 -1
- package/dist-lib/bridge-contract-input.mjs +1 -1
- package/dist-lib/bridge-contract.cjs +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/bundle-channel.cjs +1528 -937
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cloud-contract.cjs +1 -1
- package/dist-lib/cloud-contract.mjs +1 -1
- package/dist-lib/cloud-test-args.cjs +1 -1
- package/dist-lib/cloud-test-args.mjs +1 -1
- package/dist-lib/cloud.cjs +4 -3
- package/dist-lib/cloud.mjs +4 -3
- package/dist-lib/config.cjs +5 -4
- package/dist-lib/config.mjs +5 -4
- package/dist-lib/detox/index.cjs +1021 -364
- package/dist-lib/dev-bundle-resolution.cjs +51 -23
- package/dist-lib/expo-swift-bundle.cjs +1351 -804
- package/dist-lib/expo-swift-metadata.cjs +5 -4
- package/dist-lib/expo-swift-metadata.mjs +5 -4
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +2475 -1343
- package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
- package/dist-lib/host/replacement-module-handler.cjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +1269 -300
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/kotlin-host.cjs +1 -1
- package/dist-lib/menu.cjs +3 -2
- package/dist-lib/menu.mjs +3 -2
- package/dist-lib/metro-fingerprint-registry.cjs +1 -1
- package/dist-lib/metro-fingerprint-registry.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +5 -4
- package/dist-lib/metro-production-bundle.mjs +5 -4
- package/dist-lib/metro.cjs +806 -398
- package/dist-lib/native-app-identity-project.cjs +729 -21
- package/dist-lib/native-app-identity-project.mjs +728 -21
- package/dist-lib/native-app-identity.cjs +232 -9
- package/dist-lib/native-app-identity.mjs +230 -8
- package/dist-lib/native-firebase-metadata.cjs +5 -4
- package/dist-lib/native-firebase-metadata.mjs +5 -4
- package/dist-lib/plist.cjs +1 -1
- package/dist-lib/plist.mjs +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +3 -2
- package/dist-lib/react-native-host-modules.cjs +4 -1
- package/dist-lib/react-native-host-modules.mjs +4 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +94 -28
- package/dist-lib/sdk.cjs +89 -24
- package/dist-lib/sdk.mjs +89 -24
- package/dist-lib/skills.cjs +4471 -2394
- package/dist-lib/swift-compile.cjs +336 -145
- package/dist-lib/swift-compile.mjs +350 -150
- package/dist-lib/swift-package.cjs +1 -1
- package/dist-lib/swift-package.mjs +1 -1
- package/dist-lib/swift-submit.cjs +1 -1
- package/dist-lib/swift-submit.mjs +1 -1
- package/dist-lib/swift-toolchain.cjs +1 -1
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +891 -557
- package/dist-lib/vite.cjs +68 -14
- package/dist-lib/worklet-executable.cjs +25 -2
- package/dist-lib/worklet-executable.mjs +25 -2
- package/package.json +2 -2
- package/scripts/dev-server-scanner.ts +33 -13
- package/src/admitted-mac-build.ts +34 -8
- package/src/android-app-identity.ts +146 -0
- package/src/android-autolinking.ts +75 -0
- package/src/android-string-value.ts +36 -0
- package/src/android-strings.ts +5 -36
- package/src/androidAutolinkingContract.ts +40 -0
- package/src/bundleGlobalFooter.ts +26 -7
- package/src/cloud.ts +2 -2
- package/src/codex-client.ts +3 -2
- package/src/dev-bundle-resolution.ts +58 -25
- package/src/expo-kotlin-host.ts +6 -36
- package/src/expo-swift-bundle.ts +17 -12
- package/src/expo-swift-host.ts +477 -173
- package/src/expoSwiftContract.ts +6 -0
- package/src/host/bridge-host.ts +7 -55
- package/src/host/staged-host-files.ts +29 -2
- package/src/installed-package-versions.ts +54 -14
- package/src/launchMetadataContract.ts +24 -0
- package/src/metro-plugin.ts +37 -42
- package/src/native-app-identity-project.ts +118 -3
- package/src/native-app-identity.ts +103 -11
- package/src/nitro-compile.ts +2 -5
- package/src/nitro-source.ts +147 -63
- package/src/process-group.ts +90 -60
- package/src/process-output.ts +0 -9
- package/src/public-brand.ts +1 -0
- package/src/react-native-host-modules.ts +3 -0
- package/src/sim-client.ts +6 -4
- package/src/swift-compile.ts +185 -31
- package/src/swift-distribution.lock.json +37 -11
- package/src/swift-distribution.ts +61 -9
- package/src/swift-host.ts +58 -27
- package/src/swift-objc-standin.ts +13 -0
- package/src/swift-standin-emit.ts +1 -1
- package/src/swift-standins.ts +17 -5
- package/src/vite-plugin.ts +8 -19
- package/src/worklet-executable.ts +30 -3
- package/src/worklets-babel.ts +35 -14
- package/src/xcode-project.ts +10 -0
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
// generic native app identity: the values an installed iOS/Android binary
|
|
2
2
|
// carries, read from the app's own native project (`Info.plist` resolved
|
|
3
|
-
// against
|
|
4
|
-
// a simulator tenant reports the
|
|
3
|
+
// against Xcode Release build settings, or a completed Android app manifest
|
|
4
|
+
// merger output), so a simulator tenant reports the selected native build.
|
|
5
5
|
//
|
|
6
6
|
// identity travels in a bundle footer or response header. the one updates
|
|
7
7
|
// receipt travels only in the response header so publisher JavaScript stays
|
|
8
8
|
// unchanged. tenant boot validates both before evaluation and clears the realm
|
|
9
9
|
// store when neither carries identity.
|
|
10
10
|
|
|
11
|
+
import { nativeAppIdentityFromAndroidManifest } from './android-app-identity'
|
|
11
12
|
import { findBundleFooter } from './bundleGlobalFooter'
|
|
12
13
|
import {
|
|
13
14
|
resolveNativeAppIconArtwork,
|
|
@@ -27,8 +28,14 @@ export interface NativeOneUpdatesReceipt {
|
|
|
27
28
|
embeddedManifestJson: string | null
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
export interface AndroidManifestPermission {
|
|
32
|
+
name: string
|
|
33
|
+
minSdkVersion: number
|
|
34
|
+
maxSdkVersion?: number
|
|
35
|
+
}
|
|
36
|
+
|
|
30
37
|
export interface NativeAppIdentity {
|
|
31
|
-
// the user-facing name (
|
|
38
|
+
// the user-facing name (iOS bundle name or a resolved Android app label).
|
|
32
39
|
applicationName: string | null
|
|
33
40
|
// the literal CFBundleDisplayName, without a bundle-name fallback.
|
|
34
41
|
applicationDisplayName?: string
|
|
@@ -51,6 +58,9 @@ export interface NativeAppIdentity {
|
|
|
51
58
|
buildVersion: string
|
|
52
59
|
bundleIdentifier: string
|
|
53
60
|
platform: NativeAppIdentityPlatform
|
|
61
|
+
// declarations from the selected app manifest merger output; install-time
|
|
62
|
+
// SDK filtering belongs to the evaluation's device, not the bundle builder.
|
|
63
|
+
androidManifestPermissions?: readonly AndroidManifestPermission[]
|
|
54
64
|
permissionUsageDescriptions?: Readonly<Record<string, unknown>>
|
|
55
65
|
urlQuerySchemes?: readonly unknown[]
|
|
56
66
|
appIntentIdentifiers?: readonly string[]
|
|
@@ -60,6 +70,8 @@ export interface NativeAppIdentity {
|
|
|
60
70
|
ipadSupportedInterfaceOrientations?: readonly string[]
|
|
61
71
|
defaultScreenOrientationMask?: string
|
|
62
72
|
shareIntoAppGroupId?: string
|
|
73
|
+
rtcAppGroupIdentifier?: string
|
|
74
|
+
screenShareExtensionBundleId?: string
|
|
63
75
|
alternateIconNames?: readonly string[]
|
|
64
76
|
appIconArtwork?: NativeAppIconArtwork
|
|
65
77
|
// resolved Reanimated compile flags from the native build inputs.
|
|
@@ -113,6 +125,24 @@ export function isNativeAppIdentity(value: unknown): value is NativeAppIdentity
|
|
|
113
125
|
typeof value.dtPlatformVersion !== 'string'
|
|
114
126
|
)
|
|
115
127
|
return false
|
|
128
|
+
if (
|
|
129
|
+
value.androidManifestPermissions !== undefined &&
|
|
130
|
+
(!Array.isArray(value.androidManifestPermissions) ||
|
|
131
|
+
!value.androidManifestPermissions.every(
|
|
132
|
+
(permission: unknown) =>
|
|
133
|
+
isRecord(permission) &&
|
|
134
|
+
typeof permission.name === 'string' &&
|
|
135
|
+
permission.name.length > 0 &&
|
|
136
|
+
Number.isInteger(permission.minSdkVersion) &&
|
|
137
|
+
typeof permission.minSdkVersion === 'number' &&
|
|
138
|
+
permission.minSdkVersion > 0 &&
|
|
139
|
+
(permission.maxSdkVersion === undefined ||
|
|
140
|
+
(typeof permission.maxSdkVersion === 'number' &&
|
|
141
|
+
Number.isInteger(permission.maxSdkVersion) &&
|
|
142
|
+
permission.maxSdkVersion >= 0)),
|
|
143
|
+
))
|
|
144
|
+
)
|
|
145
|
+
return false
|
|
116
146
|
const descriptions = value.permissionUsageDescriptions
|
|
117
147
|
if (
|
|
118
148
|
value.reanimatedStaticFeatureFlags !== undefined &&
|
|
@@ -150,6 +180,16 @@ export function isNativeAppIdentity(value: unknown): value is NativeAppIdentity
|
|
|
150
180
|
typeof value.shareIntoAppGroupId !== 'string'
|
|
151
181
|
)
|
|
152
182
|
return false
|
|
183
|
+
if (
|
|
184
|
+
value.rtcAppGroupIdentifier !== undefined &&
|
|
185
|
+
typeof value.rtcAppGroupIdentifier !== 'string'
|
|
186
|
+
)
|
|
187
|
+
return false
|
|
188
|
+
if (
|
|
189
|
+
value.screenShareExtensionBundleId !== undefined &&
|
|
190
|
+
typeof value.screenShareExtensionBundleId !== 'string'
|
|
191
|
+
)
|
|
192
|
+
return false
|
|
153
193
|
if (
|
|
154
194
|
value.applicationDisplayName !== undefined &&
|
|
155
195
|
typeof value.applicationDisplayName !== 'string'
|
|
@@ -244,6 +284,13 @@ export function readNativeAppIdentity(value: unknown): NativeAppIdentity | null
|
|
|
244
284
|
...(value.alternateIconNames === undefined
|
|
245
285
|
? {}
|
|
246
286
|
: { alternateIconNames: [...value.alternateIconNames] }),
|
|
287
|
+
...(value.androidManifestPermissions === undefined
|
|
288
|
+
? {}
|
|
289
|
+
: {
|
|
290
|
+
androidManifestPermissions: value.androidManifestPermissions.map(
|
|
291
|
+
(permission) => ({ ...permission }),
|
|
292
|
+
),
|
|
293
|
+
}),
|
|
247
294
|
...(value.permissionUsageDescriptions === undefined
|
|
248
295
|
? {}
|
|
249
296
|
: {
|
|
@@ -276,6 +323,12 @@ export function readNativeAppIdentity(value: unknown): NativeAppIdentity | null
|
|
|
276
323
|
...(value.shareIntoAppGroupId === undefined
|
|
277
324
|
? {}
|
|
278
325
|
: { shareIntoAppGroupId: value.shareIntoAppGroupId }),
|
|
326
|
+
...(value.rtcAppGroupIdentifier === undefined
|
|
327
|
+
? {}
|
|
328
|
+
: { rtcAppGroupIdentifier: value.rtcAppGroupIdentifier }),
|
|
329
|
+
...(value.screenShareExtensionBundleId === undefined
|
|
330
|
+
? {}
|
|
331
|
+
: { screenShareExtensionBundleId: value.screenShareExtensionBundleId }),
|
|
279
332
|
...(value.urlQuerySchemes === undefined
|
|
280
333
|
? {}
|
|
281
334
|
: { urlQuerySchemes: [...value.urlQuerySchemes] }),
|
|
@@ -857,6 +910,20 @@ function nativeAppIdentityFromIos(
|
|
|
857
910
|
: undefined
|
|
858
911
|
if (shareIntoAppGroupId === null)
|
|
859
912
|
throw new Error('native app Info.plist ExpoShareIntoAppGroupId is unresolved')
|
|
913
|
+
const declaredRtcGroup = dictionary.RTCAppGroupIdentifier
|
|
914
|
+
const rtcAppGroupIdentifier =
|
|
915
|
+
typeof declaredRtcGroup === 'string'
|
|
916
|
+
? resolveBuildSetting(declaredRtcGroup, settings)
|
|
917
|
+
: undefined
|
|
918
|
+
if (rtcAppGroupIdentifier === null)
|
|
919
|
+
throw new Error('native app Info.plist RTCAppGroupIdentifier is unresolved')
|
|
920
|
+
const declaredScreenShareExtension = dictionary.ScreenShareExtensionBundleId
|
|
921
|
+
const screenShareExtensionBundleId =
|
|
922
|
+
typeof declaredScreenShareExtension === 'string'
|
|
923
|
+
? resolveBuildSetting(declaredScreenShareExtension, settings)
|
|
924
|
+
: undefined
|
|
925
|
+
if (screenShareExtensionBundleId === null)
|
|
926
|
+
throw new Error('native app Info.plist ScreenShareExtensionBundleId is unresolved')
|
|
860
927
|
const querySchemes = dictionary.LSApplicationQueriesSchemes
|
|
861
928
|
if (querySchemes !== undefined && !Array.isArray(querySchemes)) {
|
|
862
929
|
throw new Error('native app Info.plist LSApplicationQueriesSchemes is not an array')
|
|
@@ -991,6 +1058,10 @@ function nativeAppIdentityFromIos(
|
|
|
991
1058
|
permissionUsageDescriptions,
|
|
992
1059
|
urlQuerySchemes,
|
|
993
1060
|
...(shareIntoAppGroupId === undefined ? {} : { shareIntoAppGroupId }),
|
|
1061
|
+
...(rtcAppGroupIdentifier === undefined ? {} : { rtcAppGroupIdentifier }),
|
|
1062
|
+
...(screenShareExtensionBundleId === undefined
|
|
1063
|
+
? {}
|
|
1064
|
+
: { screenShareExtensionBundleId }),
|
|
994
1065
|
backgroundTaskIdentifiers: backgroundTaskIdentifiers.every((value) => value !== null)
|
|
995
1066
|
? backgroundTaskIdentifiers
|
|
996
1067
|
: [],
|
|
@@ -1003,11 +1074,6 @@ function nativeAppIdentityFromIos(
|
|
|
1003
1074
|
}
|
|
1004
1075
|
}
|
|
1005
1076
|
|
|
1006
|
-
// android's shipped identity requires resolving the selected flavor and
|
|
1007
|
-
// buildType overrides (`productFlavors.prod`, `buildTypes.release`), which a
|
|
1008
|
-
// text reader of `defaultConfig` cannot distinguish from the defaults it
|
|
1009
|
-
// overrides. an Android project therefore ships no footer until that
|
|
1010
|
-
// resolution exists. unsupported or absent bundle platforms ship no footer.
|
|
1011
1077
|
// the module an app's own sources start react native with: the template
|
|
1012
1078
|
// `startReactNative(withModuleName: "X")`, RCTAppDelegate's
|
|
1013
1079
|
// `self.moduleName = @"X"` and RCTRootView's `moduleName:@"X"`. a target that
|
|
@@ -1022,8 +1088,19 @@ export function readRootModuleName(sources: readonly string[]): string | undefin
|
|
|
1022
1088
|
return names.size === 1 ? [...names][0] : undefined
|
|
1023
1089
|
}
|
|
1024
1090
|
|
|
1025
|
-
export function canExtractNativeAppIdentity(
|
|
1026
|
-
|
|
1091
|
+
export function canExtractNativeAppIdentity(
|
|
1092
|
+
platform: unknown,
|
|
1093
|
+
): platform is NativeAppIdentityPlatform {
|
|
1094
|
+
return platform === 'ios' || platform === 'android'
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
// the bundle platform's identity reader. selecting it here keeps every native
|
|
1098
|
+
// project reader free of platform branches.
|
|
1099
|
+
export function selectNativeAppIdentityReader<Reader>(
|
|
1100
|
+
platform: unknown,
|
|
1101
|
+
readers: Readonly<Record<NativeAppIdentityPlatform, Reader>>,
|
|
1102
|
+
): Reader | null {
|
|
1103
|
+
return canExtractNativeAppIdentity(platform) ? readers[platform] : null
|
|
1027
1104
|
}
|
|
1028
1105
|
|
|
1029
1106
|
// read the identity a bundle builder transports. files are project-relative
|
|
@@ -1036,8 +1113,23 @@ export function extractNativeAppIdentityFromFiles(
|
|
|
1036
1113
|
parseInfoPlist: (source: string) => unknown,
|
|
1037
1114
|
iosTarget?: string,
|
|
1038
1115
|
resourceUri?: (file: string) => string | undefined,
|
|
1116
|
+
androidMergedManifest?: string,
|
|
1039
1117
|
): NativeAppIdentity | null {
|
|
1040
|
-
|
|
1118
|
+
if (platform === 'android') {
|
|
1119
|
+
const candidates = androidMergedManifest
|
|
1120
|
+
? [androidMergedManifest]
|
|
1121
|
+
: Object.keys(files).filter((file) =>
|
|
1122
|
+
/^android\/[^/]+\/build\/intermediates\/merged_manifests\/.+\/AndroidManifest\.xml$/.test(
|
|
1123
|
+
file,
|
|
1124
|
+
),
|
|
1125
|
+
)
|
|
1126
|
+
if (candidates.length !== 1) return null
|
|
1127
|
+
const source = files[candidates[0]!]
|
|
1128
|
+
return source === undefined
|
|
1129
|
+
? null
|
|
1130
|
+
: nativeAppIdentityFromAndroidManifest(source, candidates[0]!, files)
|
|
1131
|
+
}
|
|
1132
|
+
return platform === 'ios'
|
|
1041
1133
|
? nativeAppIdentityFromIos(files, parseInfoPlist, iosTarget, resourceUri)
|
|
1042
1134
|
: null
|
|
1043
1135
|
}
|
package/src/nitro-compile.ts
CHANGED
|
@@ -20,10 +20,7 @@ export function collectNitroCompileInput(
|
|
|
20
20
|
): { input: SwiftCompileInput; collection: NitroSourceCollection } {
|
|
21
21
|
const collection = collectNitroSources(options)
|
|
22
22
|
const files = { ...collection.files }
|
|
23
|
-
const platform = path.join(
|
|
24
|
-
options.moduleRoot,
|
|
25
|
-
'platform/react-native-nitro-modules',
|
|
26
|
-
)
|
|
23
|
+
const platform = path.join(options.moduleRoot, 'platform/react-native-nitro-modules')
|
|
27
24
|
const read = (file: string) =>
|
|
28
25
|
readPackageFile(
|
|
29
26
|
platform,
|
|
@@ -71,7 +68,7 @@ export function collectNitroCompileInput(
|
|
|
71
68
|
'let package = Package(name: "PeachNativeModule", targets: [',
|
|
72
69
|
...collection.targets.map(
|
|
73
70
|
(target) =>
|
|
74
|
-
`.target(name: ${JSON.stringify(target.name)}, dependencies: ${JSON.stringify(target.dependencies?.map((dependency) => dependency.name) ?? [])}, path: ${JSON.stringify(target.path)}, cxxSettings: [.define("DEBUG")]),`,
|
|
71
|
+
`.target(name: ${JSON.stringify(target.name)}, dependencies: ${JSON.stringify(target.dependencies?.map((dependency) => dependency.name) ?? [])}, path: ${JSON.stringify(target.path)}, ${target.cxxInterop ? 'swiftSettings: [.interoperabilityMode(.Cxx)]' : 'cxxSettings: [.define("DEBUG")]'}),`,
|
|
75
72
|
),
|
|
76
73
|
`.target(name: "${runtime}", dependencies: ["JsiBridge", "NitroCore"], cxxSettings: [.define("DEBUG")]),`,
|
|
77
74
|
`.executableTarget(name: "PeachNitroApp", dependencies: ["${runtime}", "${implementation.name}"], swiftSettings: [.interoperabilityMode(.Cxx)]),`,
|
package/src/nitro-source.ts
CHANGED
|
@@ -55,61 +55,72 @@ function packageInfo(root: string): NitroSourceCollection['packages'][number] {
|
|
|
55
55
|
return { name: value.name, version: value.version, root }
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
function registrations(root: string)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
58
|
+
function registrations(root: string) {
|
|
59
|
+
// cocoapods compiles this generated initializer through +autolinking.rb.
|
|
60
|
+
// nitro.json is a generator input and need not ship in the package.
|
|
61
|
+
const directory = path.join(root, 'nitrogen/generated/ios')
|
|
62
|
+
const initializers = fs
|
|
63
|
+
.readdirSync(directory)
|
|
64
|
+
.filter((file) => file.endsWith('Autolinking.mm'))
|
|
65
|
+
if (initializers.length !== 1)
|
|
66
|
+
throw new Error(`${root}: expected one generated Nitro iOS initializer`)
|
|
67
|
+
const source = readPackageFile(
|
|
68
|
+
root,
|
|
69
|
+
path.join(directory, initializers[0]!),
|
|
70
|
+
MAX_SWIFT_COMPILE_SOURCE_BYTES,
|
|
71
|
+
).toString('utf8')
|
|
72
|
+
const namespaces = [
|
|
73
|
+
...source.matchAll(/\busing namespace (margelo::nitro(?:::[A-Za-z_]\w*)*)\s*;/g),
|
|
74
|
+
]
|
|
75
|
+
const namespace = namespaces[1]?.[1]
|
|
76
|
+
if (namespaces.length !== 2 || namespaces[0]?.[1] !== 'margelo::nitro' || !namespace)
|
|
77
|
+
throw new Error(`${root}: invalid generated Nitro C++ namespace`)
|
|
78
|
+
|
|
79
|
+
const classes: (
|
|
80
|
+
| { name: string; implementation: string; language: 'c++' }
|
|
81
|
+
| { name: string; language: 'swift' }
|
|
82
|
+
)[] = []
|
|
83
|
+
let swiftModule: string | null = null
|
|
84
|
+
for (const registration of source.matchAll(
|
|
85
|
+
/\bHybridObjectRegistry::registerHybridObjectConstructor\(\s*("(?:[^"\\]|\\.)*")\s*,\s*\[\]\(\)\s*->\s*std::shared_ptr<HybridObject>\s*\{([\s\S]*?)\}\s*\);/g,
|
|
76
86
|
)) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const languages = Object.entries(entry).map(
|
|
86
|
-
([platform, value]) =>
|
|
87
|
-
`${platform}:${object(value) ? String(value.language) : 'invalid'}`,
|
|
88
|
-
)
|
|
89
|
-
throw new Error(
|
|
90
|
-
`${root}: unsupported Nitro module ${name} (${languages.join(', ')}); ` +
|
|
91
|
-
'this collector supports shared all.language=c++ only, not Swift, Kotlin, Java or JNI',
|
|
87
|
+
const name: unknown = JSON.parse(registration[1]!)
|
|
88
|
+
const body = registration[2]!
|
|
89
|
+
if (typeof name !== 'string' || name.length === 0)
|
|
90
|
+
throw new Error(`${root}: invalid generated Nitro registration name`)
|
|
91
|
+
const cpp = /return\s+std::make_shared<([A-Za-z_]\w*)>\(\);/.exec(body)
|
|
92
|
+
const swift =
|
|
93
|
+
/=\s*([A-Za-z_]\w*)::([A-Za-z_]\w*)::create([A-Za-z_]\w*)\(\);\s*return hybridObject;/.exec(
|
|
94
|
+
body,
|
|
92
95
|
)
|
|
96
|
+
if (cpp && !swift) {
|
|
97
|
+
classes.push({ name, implementation: cpp[1]!, language: 'c++' })
|
|
98
|
+
} else if (
|
|
99
|
+
swift &&
|
|
100
|
+
!cpp &&
|
|
101
|
+
swift[2] === `${swift[1]}Autolinking` &&
|
|
102
|
+
swift[3] === name &&
|
|
103
|
+
(swiftModule === null || swiftModule === swift[1])
|
|
104
|
+
) {
|
|
105
|
+
swiftModule = swift[1]!
|
|
106
|
+
classes.push({ name, language: 'swift' })
|
|
107
|
+
} else {
|
|
108
|
+
throw new Error(`${root}: unsupported generated Nitro iOS registration ${name}`)
|
|
93
109
|
}
|
|
94
|
-
const implementation = entry.all.implementationClassName
|
|
95
|
-
if (
|
|
96
|
-
name.length === 0 ||
|
|
97
|
-
typeof implementation !== 'string' ||
|
|
98
|
-
!/^[A-Za-z_][A-Za-z0-9_]*$/.test(implementation)
|
|
99
|
-
)
|
|
100
|
-
throw new Error(`${root}: invalid Nitro C++ implementation for ${name}`)
|
|
101
|
-
classes.push({ name, implementation })
|
|
102
|
-
}
|
|
103
|
-
if (classes.length === 0) throw new Error(`${root}: no Nitro C++ registrations`)
|
|
104
|
-
return {
|
|
105
|
-
namespace: ['margelo', 'nitro', ...config.cxxNamespace].join('::'),
|
|
106
|
-
classes,
|
|
107
110
|
}
|
|
111
|
+
const count = [
|
|
112
|
+
...source.matchAll(/\bHybridObjectRegistry::registerHybridObjectConstructor\s*\(/g),
|
|
113
|
+
].length
|
|
114
|
+
if (classes.length === 0 || classes.length !== count)
|
|
115
|
+
throw new Error(`${root}: incomplete generated Nitro iOS registrations`)
|
|
116
|
+
if (new Set(classes.map(({ name }) => name)).size !== classes.length)
|
|
117
|
+
throw new Error(`${root}: duplicate generated Nitro iOS registration`)
|
|
118
|
+
classes.sort((a, b) => a.name.localeCompare(b.name))
|
|
119
|
+
return { namespace, swiftModule, classes }
|
|
108
120
|
}
|
|
109
121
|
|
|
110
|
-
/** collect installed
|
|
111
|
-
* rewriting package JavaScript.
|
|
112
|
-
* this C++ module collector; the caller must supply real platform bindings. */
|
|
122
|
+
/** collect installed native bodies, generated bridges, Nitro core and RN's
|
|
123
|
+
* JSI without reading or rewriting package JavaScript. */
|
|
113
124
|
export function collectNitroSources(options: NitroSourceOptions): NitroSourceCollection {
|
|
114
125
|
const module = packageInfo(options.packageRoot)
|
|
115
126
|
const config = registrations(module.root)
|
|
@@ -189,6 +200,7 @@ export function collectNitroSources(options: NitroSourceOptions): NitroSourceCol
|
|
|
189
200
|
includeNamespace: string | null,
|
|
190
201
|
skip: ReadonlySet<string> = new Set(),
|
|
191
202
|
select: (file: string) => boolean = () => true,
|
|
203
|
+
extensions: RegExp = /\.(?:c|cc|cpp|cxx|h|hpp|hxx|inl)$/,
|
|
192
204
|
) => {
|
|
193
205
|
const headers = new Map<string, string>()
|
|
194
206
|
const walk = (relative: string) => {
|
|
@@ -204,7 +216,7 @@ export function collectNitroSources(options: NitroSourceOptions): NitroSourceCol
|
|
|
204
216
|
walk(sourceRelative)
|
|
205
217
|
continue
|
|
206
218
|
}
|
|
207
|
-
if (
|
|
219
|
+
if (!extensions.test(entry.name)) continue
|
|
208
220
|
if (!select(entry.name)) continue
|
|
209
221
|
if (/[\p{Cc}"\\]/u.test(sourceRelative))
|
|
210
222
|
throw new Error(`invalid Nitro source path: ${sourceRelative}`)
|
|
@@ -259,13 +271,72 @@ export function collectNitroSources(options: NitroSourceOptions): NitroSourceCol
|
|
|
259
271
|
(file) => (file.endsWith('.h') && file !== 'JSIDynamic.h') || file === 'jsi.cpp',
|
|
260
272
|
)
|
|
261
273
|
collect(nitro.root, 'cpp', coreTarget, 'NitroModules', new Set(['views']))
|
|
262
|
-
const implementationHeaders =
|
|
274
|
+
const implementationHeaders = fs.existsSync(path.join(module.root, 'cpp'))
|
|
275
|
+
? collect(module.root, 'cpp', targetName, null)
|
|
276
|
+
: new Map<string, string>()
|
|
263
277
|
collect(module.root, 'nitrogen/generated/shared/c++', targetName, null)
|
|
264
|
-
for (const
|
|
265
|
-
if (
|
|
278
|
+
for (const entry of config.classes)
|
|
279
|
+
if (
|
|
280
|
+
entry.language === 'c++' &&
|
|
281
|
+
!implementationHeaders.has(`${entry.implementation}.hpp`)
|
|
282
|
+
)
|
|
266
283
|
throw new Error(
|
|
267
|
-
`${module.root}: missing C++ implementation header ${implementation}.hpp`,
|
|
284
|
+
`${module.root}: missing C++ implementation header ${entry.implementation}.hpp`,
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
if (config.swiftModule) {
|
|
288
|
+
for (const directory of ['ios/core', 'ios/utils'])
|
|
289
|
+
collect(
|
|
290
|
+
nitro.root,
|
|
291
|
+
directory,
|
|
292
|
+
coreTarget,
|
|
293
|
+
'NitroModules',
|
|
294
|
+
new Set(),
|
|
295
|
+
(file) => !file.endsWith('.swift'),
|
|
268
296
|
)
|
|
297
|
+
const nitroSwift = target('NitroModules', [coreTarget, jsiTarget])
|
|
298
|
+
nitroSwift.cxxInterop = true
|
|
299
|
+
const packageSwift = target(config.swiftModule, [targetName, 'NitroModules'])
|
|
300
|
+
packageSwift.cxxInterop = true
|
|
301
|
+
result.targets.push(nitroSwift, packageSwift)
|
|
302
|
+
for (const directory of ['ios/core', 'ios/utils', 'ios/views'])
|
|
303
|
+
collect(
|
|
304
|
+
nitro.root,
|
|
305
|
+
directory,
|
|
306
|
+
'NitroModules',
|
|
307
|
+
null,
|
|
308
|
+
new Set(['HybridView.swift']),
|
|
309
|
+
() => true,
|
|
310
|
+
/\.swift$/,
|
|
311
|
+
)
|
|
312
|
+
add(
|
|
313
|
+
`${targetPath('NitroModules')}/PeachNitroExports.swift`,
|
|
314
|
+
`@_exported import ${coreTarget}\n`,
|
|
315
|
+
)
|
|
316
|
+
collect(
|
|
317
|
+
module.root,
|
|
318
|
+
'ios',
|
|
319
|
+
config.swiftModule,
|
|
320
|
+
null,
|
|
321
|
+
new Set(['Tests']),
|
|
322
|
+
() => true,
|
|
323
|
+
/\.swift$/,
|
|
324
|
+
)
|
|
325
|
+
collect(
|
|
326
|
+
module.root,
|
|
327
|
+
'nitrogen/generated/ios',
|
|
328
|
+
config.swiftModule,
|
|
329
|
+
null,
|
|
330
|
+
new Set(),
|
|
331
|
+
() => true,
|
|
332
|
+
/\.swift$/,
|
|
333
|
+
)
|
|
334
|
+
collect(module.root, 'nitrogen/generated/ios', targetName, null)
|
|
335
|
+
add(
|
|
336
|
+
`${targetPath(config.swiftModule)}/PeachNitroExports.swift`,
|
|
337
|
+
`@_exported import ${targetName}\n`,
|
|
338
|
+
)
|
|
339
|
+
}
|
|
269
340
|
|
|
270
341
|
// explicit module maps avoid importing every RN view or private JSI header.
|
|
271
342
|
add(
|
|
@@ -274,7 +345,7 @@ export function collectNitroSources(options: NitroSourceOptions): NitroSourceCol
|
|
|
274
345
|
)
|
|
275
346
|
add(
|
|
276
347
|
`${targetPath(coreTarget)}/include/module.modulemap`,
|
|
277
|
-
`module ${coreTarget} { header "NitroModules/InstallNitro.hpp" export * }\n`,
|
|
348
|
+
`module ${coreTarget} { header "NitroModules/InstallNitro.hpp"${config.swiftModule ? ['ArrayBufferHolder.hpp', 'AnyMapUtils.hpp', 'PromiseHolder.hpp', 'Result.hpp', 'RuntimeError.hpp', 'DateToChronoDate.hpp'].map((header) => ` header "NitroModules/${header}"`).join('') : ''} export * }\n`,
|
|
278
349
|
)
|
|
279
350
|
add(
|
|
280
351
|
result.registration.header,
|
|
@@ -282,7 +353,7 @@ export function collectNitroSources(options: NitroSourceOptions): NitroSourceCol
|
|
|
282
353
|
)
|
|
283
354
|
add(
|
|
284
355
|
`${targetPath(targetName)}/include/module.modulemap`,
|
|
285
|
-
`module ${targetName} { header "PeachNitroRegistration.hpp" export * }\n`,
|
|
356
|
+
`module ${targetName} { header "PeachNitroRegistration.hpp"${config.swiftModule ? ` header "${config.swiftModule}-Swift-Cxx-Bridge.hpp"` : ''} export * }\n`,
|
|
286
357
|
)
|
|
287
358
|
add(
|
|
288
359
|
`${targetPath(targetName)}/PeachNitroRegistration.cpp`,
|
|
@@ -291,15 +362,28 @@ export function collectNitroSources(options: NitroSourceOptions): NitroSourceCol
|
|
|
291
362
|
'#include <NitroModules/HybridObjectRegistry.hpp>',
|
|
292
363
|
'#include <memory>',
|
|
293
364
|
'#include <type_traits>',
|
|
294
|
-
...
|
|
295
|
-
|
|
296
|
-
|
|
365
|
+
...(config.swiftModule
|
|
366
|
+
? [`#include "${config.swiftModule}-Swift-Cxx-Umbrella.hpp"`]
|
|
367
|
+
: []),
|
|
368
|
+
...[
|
|
369
|
+
...new Set(
|
|
370
|
+
config.classes
|
|
371
|
+
.filter((entry) => entry.language === 'c++')
|
|
372
|
+
.map(({ implementation }) => implementation),
|
|
373
|
+
),
|
|
374
|
+
].map((implementation) => `#include "${implementation}.hpp"`),
|
|
297
375
|
`extern "C" void ${result.registration.symbol}() {`,
|
|
298
|
-
...config.classes.flatMap((
|
|
299
|
-
|
|
376
|
+
...config.classes.flatMap((entry) => [
|
|
377
|
+
...(entry.language === 'c++'
|
|
378
|
+
? [
|
|
379
|
+
` static_assert(std::is_default_constructible_v<${config.namespace}::${entry.implementation}>);`,
|
|
380
|
+
]
|
|
381
|
+
: []),
|
|
300
382
|
' margelo::nitro::HybridObjectRegistry::registerHybridObjectConstructor(',
|
|
301
|
-
` ${JSON.stringify(name)}, []() -> std::shared_ptr<margelo::nitro::HybridObject> {`,
|
|
302
|
-
|
|
383
|
+
` ${JSON.stringify(entry.name)}, []() -> std::shared_ptr<margelo::nitro::HybridObject> {`,
|
|
384
|
+
entry.language === 'swift'
|
|
385
|
+
? ` return ${config.swiftModule}::${config.swiftModule}Autolinking::create${entry.name}();`
|
|
386
|
+
: ` return std::make_shared<${config.namespace}::${entry.implementation}>();`,
|
|
303
387
|
' });',
|
|
304
388
|
]),
|
|
305
389
|
'}',
|