peachsim 0.1.1481 → 0.1.1619
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 +56 -13
- package/cli/app-catalog.ts +7 -5
- package/cli/app-config.ts +47 -8
- package/cli/app-fonts.ts +84 -49
- package/cli/app-stubs.ts +433 -0
- package/cli/bin.ts +15 -2
- package/cli/bridge-flow-runner.ts +383 -123
- package/cli/cloud-client.ts +26 -25
- package/cli/cloud-test-tunnel.ts +2 -0
- package/cli/command-registry.ts +2 -1
- package/cli/commands/agent-indicator-mode.ts +39 -0
- package/cli/commands/box/bundle-plane.ts +4 -0
- package/cli/commands/box.ts +16 -23
- package/cli/commands/control.ts +221 -126
- package/cli/commands/daemon.ts +99 -24
- package/cli/commands/debug.ts +31 -3
- package/cli/commands/detect.ts +1 -1
- package/cli/commands/device.ts +6 -8
- package/cli/commands/diagnose.ts +2 -0
- package/cli/commands/do-chain.ts +2 -2
- package/cli/commands/doctor.ts +1030 -0
- package/cli/commands/flow.ts +447 -406
- package/cli/commands/inspect/actions.ts +2 -0
- package/cli/commands/inspect/core.ts +138 -46
- package/cli/commands/inspect/env.ts +10 -0
- package/cli/commands/inspect/list.ts +25 -8
- package/cli/commands/inspect/memory.ts +9 -0
- package/cli/commands/inspect/settling.ts +219 -66
- package/cli/commands/inspect/shared.ts +53 -12
- package/cli/commands/inspect/wait-event.ts +0 -1
- package/cli/commands/inspect/wait.ts +2 -2
- package/cli/commands/inspect.ts +51 -16
- package/cli/commands/install-desktop.ts +3 -2
- package/cli/commands/kotlin.ts +81 -22
- package/cli/commands/login.ts +1 -1
- package/cli/commands/maestro-compare.ts +41 -5
- package/cli/commands/maestro-generate.ts +11 -7
- package/cli/commands/maestro.ts +43 -15
- package/cli/commands/mode.ts +13 -1
- package/cli/commands/no-bridge-hint.ts +26 -3
- package/cli/commands/perf-record.ts +296 -0
- package/cli/commands/perf.ts +6 -1
- package/cli/commands/platform.ts +15 -4
- package/cli/commands/preview.ts +1 -1
- package/cli/commands/record.ts +37 -6
- package/cli/commands/report-issue.ts +2 -1
- package/cli/commands/runtime.ts +9 -11
- package/cli/commands/screenshots.ts +4 -3
- package/cli/commands/serve.ts +30 -2
- package/cli/commands/setup.ts +32 -7
- package/cli/commands/skills.ts +16 -43
- package/cli/commands/swift.ts +22 -3
- package/cli/commands/upgrade.ts +7 -6
- package/cli/commands/upload.ts +118 -69
- package/cli/commands/window.ts +449 -0
- package/cli/current-sim.ts +1 -2
- package/cli/desktop-companion.ts +84 -10
- package/cli/device-option.ts +8 -0
- package/cli/drivers/desktop.ts +3 -1
- package/cli/drivers/playwright-sim-host.ts +30 -11
- package/cli/drivers/playwright.ts +1 -4
- package/cli/flow-export.ts +2 -2
- package/cli/flow-file.ts +19 -18
- package/cli/internal-child.ts +7 -6
- package/cli/maestro-compare.ts +28 -6
- package/cli/maestro-js.ts +2 -1
- package/cli/main.ts +25 -4
- package/cli/material-symbol-fonts.ts +171 -0
- package/cli/outbound-endpoints.ts +132 -54
- package/cli/parse-args.ts +7 -0
- package/cli/preview-capture.ts +12 -0
- package/cli/privacy.ts +1 -1
- package/cli/run-registry.ts +5 -2
- package/cli/runtime-compat.ts +16 -0
- package/cli/telemetry.ts +6 -3
- package/cli/ui.ts +5 -0
- package/cli/ws-bridge.ts +121 -62
- package/detox/attempt-events.cjs +762 -0
- package/detox/attempt-events.d.cts +106 -0
- package/detox/gestures.ts +115 -2
- package/detox/index.ts +762 -426
- package/detox/jest-environment.cjs +86 -0
- package/detox/jest-preset.cjs +3 -1
- package/detox/jest-setup-after-env.cjs +1 -0
- package/detox/matchers.ts +3 -6
- package/detox/motion-change.cjs +259 -0
- package/detox/motion-change.d.cts +73 -0
- package/detox/proof-frame.cjs +83 -9
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +15 -5
- 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 +3 -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 +5950 -1775
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cloud-contract.cjs +6 -1
- package/dist-lib/cloud-contract.mjs +5 -1
- package/dist-lib/cloud-test-args.cjs +1 -1
- package/dist-lib/cloud-test-args.mjs +1 -1
- package/dist-lib/cloud.cjs +1 -1
- package/dist-lib/cloud.mjs +1 -1
- package/dist-lib/config.cjs +34 -3
- package/dist-lib/config.mjs +33 -3
- package/dist-lib/detox/index.cjs +3443 -564
- package/dist-lib/detox/jest-environment.cjs +86 -0
- package/dist-lib/detox/jest-preset.cjs +3 -1
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/expo-swift-bundle.cjs +5518 -1446
- package/dist-lib/expo-swift-metadata.cjs +21 -13
- package/dist-lib/expo-swift-metadata.mjs +21 -13
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +21728 -16808
- package/dist-lib/host/fetch-proxy-handler.cjs +177 -102
- package/dist-lib/host/fetch-proxy-overrides.cjs +84 -4
- package/dist-lib/host/fetch-proxy-overrides.mjs +74 -3
- package/dist-lib/host/replacement-module-handler.cjs +16 -15
- package/dist-lib/host/websocket-proxy.cjs +243 -6
- package/dist-lib/index.cjs +6063 -4113
- package/dist-lib/jump-to-source-babel.cjs +73 -35
- package/dist-lib/jump-to-source-native.cjs +118 -10
- package/dist-lib/kotlin-host.cjs +264 -121
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.mjs +1 -1
- 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 +147 -99
- package/dist-lib/metro-production-bundle.mjs +142 -99
- package/dist-lib/metro.cjs +4249 -360
- package/dist-lib/native-app-identity-project.cjs +318 -131
- package/dist-lib/native-app-identity-project.mjs +317 -131
- package/dist-lib/native-app-identity.cjs +322 -33
- package/dist-lib/native-app-identity.mjs +319 -32
- package/dist-lib/native-firebase-metadata.cjs +1 -1
- package/dist-lib/native-firebase-metadata.mjs +1 -1
- 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 +1 -1
- package/dist-lib/react-native-host-modules.cjs +11 -1
- package/dist-lib/react-native-host-modules.mjs +11 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +127 -42
- package/dist-lib/sdk.cjs +311 -103
- package/dist-lib/sdk.mjs +311 -103
- package/dist-lib/skills.cjs +18344 -11121
- package/dist-lib/swift-compile.cjs +3291 -544
- package/dist-lib/swift-compile.mjs +3227 -478
- package/dist-lib/swift-package.cjs +25 -3
- package/dist-lib/swift-package.mjs +22 -2
- package/dist-lib/swift-submit.cjs +2 -2
- package/dist-lib/swift-submit.mjs +2 -2
- package/dist-lib/swift-toolchain.cjs +1 -1
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +5523 -1428
- package/dist-lib/vite.cjs +251 -60
- package/dist-lib/worklet-executable.cjs +18 -4
- package/dist-lib/worklet-executable.mjs +18 -4
- package/install.sh +8 -3
- package/kotlin/AndroidViewSources/android/graphics/Canvas.kt +2 -0
- package/kotlin/AndroidViewSources/android/graphics/Paint.kt +18 -0
- package/kotlin/AndroidViewSources/dev/peach/app/JavaFormat.kt +16 -8
- package/kotlin/AppSources/android/view/Window.kt +4 -1
- package/kotlin/AppSources/android/window/BackEvent.kt +24 -0
- package/kotlin/AppSources/android/window/OnBackAnimationCallback.kt +7 -0
- package/kotlin/AppSources/android/window/OnBackInvokedDispatcher.kt +37 -1
- package/kotlin/AppSources/androidx/activity/BackEventCompat.kt +5 -0
- package/kotlin/AppSources/androidx/activity/OnBackPressedDispatcher.kt +8 -1
- package/kotlin/AppSources/dev/peach/app/PeachApp.kt +14 -0
- package/kotlin/CompilerSources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar +1 -0
- package/kotlin/CompilerSources/PeachJniIncoming.kt +243 -0
- package/kotlin/CompilerSources/PeachJniPlugin.kt +116 -0
- package/kotlin/ComposeSources/PeachComposeHost.kt +58 -32
- package/kotlin/ComposeSources/PeachMagnifier.kt +185 -0
- package/kotlin/ExpoComposeSources/PeachAndroidView.kt +16 -0
- package/kotlin/JniPlatformSources/androidx/annotation/CallSuper.kt +5 -0
- package/kotlin/JniPlatformSources/com/facebook/proguard/annotations/DoNotStrip.kt +5 -0
- package/kotlin/JniPlatformSources/dalvik/annotation/optimization/FastNative.kt +5 -0
- package/kotlin/JniPlatformSources/kotlin/Synchronized.kt +6 -0
- package/kotlin/JniPlatformSources/kotlin/jvm/JvmField.kt +5 -0
- package/kotlin/JniPlatformSources/kotlin/jvm/JvmStatic.kt +5 -0
- package/kotlin/JniSources/com/facebook/jni/HybridData.kt +68 -0
- package/kotlin/JniSources/dev/peach/jni/IncomingBindings.kt +157 -0
- package/kotlin/JniSources/dev/peach/jni/NativeBindings.kt +79 -0
- package/kotlin/JniTransportSources/dev/peach/jni/integration/JniTransport.kt +91 -0
- package/kotlin/README.md +18 -0
- package/kotlin/Sources/java/util/IllegalFormatCodePointException.kt +10 -0
- package/kotlin/Sources/java/util/IllegalFormatException.kt +5 -0
- package/kotlin/fixtures/counter/CounterApp.native.tsx +30 -3
- package/kotlin/fixtures/counter/CounterScreen.d.kt.ts +1 -0
- package/kotlin/fixtures/counter/CounterScreen.kt +2 -1
- package/kotlin/fixtures/counter/package.json +15 -2
- package/kotlin/fixtures/counter/vite.config.ts +11 -1
- package/kotlin/fork/NOTICE +1 -0
- package/kotlin/fork/README.md +5 -4
- package/kotlin/fork/compose-worker-globals.patch +629 -0
- package/kotlin/fork/fork.json +1 -1
- package/kotlin/toolchain.json +3 -3
- package/package.json +1 -1
- package/scripts/dev-server-scanner.ts +166 -81
- package/skills/contrast/SKILL.md +4 -4
- package/skills/peach-debug/SKILL.md +1 -1
- package/skills/peach-perf/SKILL.md +25 -0
- package/skills/peach-setup/SKILL.md +47 -13
- package/skills/peach-test/SKILL.md +2 -3
- package/src/agent-identity.ts +18 -0
- package/src/assetCatalogContract.ts +31 -3
- package/src/auth/shared-session.ts +3 -2
- package/src/bridge-constants.ts +1 -1
- package/src/bridge-contract.ts +5 -0
- package/src/bundle-channel.mjs +38 -7
- package/src/bundleGlobalFooter.ts +70 -20
- package/src/cloud-contract.ts +11 -0
- package/src/config.ts +47 -1
- package/src/configured-bundle-producer.ts +4 -1
- package/src/desktop-host-delivery.ts +4 -3
- package/src/expo-kotlin-host.ts +7 -2
- package/src/guest-bundle.ts +15 -8
- package/src/host/agent-host.ts +2 -4
- package/src/host/bridge-host.ts +260 -119
- package/src/host/fetch-proxy-handler.ts +237 -146
- package/src/host/fetch-proxy-overrides.ts +118 -0
- package/src/host/fetch-proxy-transport.ts +77 -63
- package/src/host/lease-owner.test.ts +18 -1
- package/src/host/lease-owner.ts +4 -5
- package/src/host/loopback-ports.ts +34 -0
- package/src/host/replacement-module-handler.ts +24 -15
- package/src/host/server-scan-handler.ts +87 -0
- package/src/host/staged-host-files.ts +92 -0
- package/src/host/websocket-proxy.ts +271 -5
- package/src/installed-package-versions.ts +66 -0
- package/src/jump-to-source-babel.ts +97 -89
- package/src/jump-to-source-native.ts +109 -11
- package/src/kotlin-host.ts +144 -27
- package/src/kotlin-module.ts +52 -0
- package/src/kotlinDiscoveryContract.ts +27 -0
- package/src/metro-layout-parser.ts +78 -0
- package/src/metro-plugin.ts +129 -39
- package/src/metro-production-bundle.ts +140 -139
- package/src/native-app-identity-project.ts +103 -5
- package/src/native-app-identity.ts +173 -11
- package/src/native-firebase-config.ts +83 -80
- package/src/native-seam-manifest.ts +0 -44
- package/src/nativeAppIconContract.ts +137 -0
- package/src/nativeViewSourceContract.ts +49 -0
- package/src/nativeViewWebContract.ts +41 -0
- package/src/nitro-bundle.ts +43 -0
- package/src/nitro-compile.ts +107 -0
- package/src/nitro-source.ts +310 -0
- package/src/nitroContract.ts +51 -0
- package/src/one-widget-metadata.ts +23 -3
- package/src/oneWidgetContract.ts +36 -3
- package/src/packageVersionsContract.ts +52 -0
- package/src/process-group.ts +306 -0
- package/src/process-liveness.ts +24 -0
- package/src/process-output.ts +21 -2
- package/src/react-devtools-frontend-react.ts +45 -0
- package/src/react-native-host-modules.ts +10 -0
- package/src/serverDiscoveryContract.ts +41 -0
- package/src/skills/builtin/maestro-test.ts +2 -4
- package/src/swift-compile.ts +3195 -650
- package/src/swift-design-time.ts +552 -0
- package/src/swift-distribution-contract.ts +11 -0
- package/src/swift-distribution.lock.json +46 -11
- package/src/swift-host.ts +144 -30
- package/src/swift-icu.ts +10 -2
- package/src/swift-objc-sdk.ts +726 -0
- package/src/swift-objc-standin.ts +441 -0
- package/src/swift-package.ts +25 -0
- package/src/swift-packages.ts +25 -10
- package/src/swift-standin-config.ts +94 -0
- package/src/swift-standin-emit.ts +5 -3
- package/src/swift-standins.ts +225 -81
- package/src/swift-submit.ts +2 -1
- package/src/swiftContract.ts +4 -0
- package/src/target-matcher.ts +67 -0
- package/src/visit-parsed-program.ts +21 -0
- package/src/vite-plugin-one.ts +65 -17
- package/src/vite-plugin-swift.ts +1 -0
- package/src/vite-plugin.ts +168 -70
- package/src/worker-system-chunks.ts +89 -10
- package/src/worklet-executable.ts +21 -3
- package/src/worklets-babel.ts +85 -13
- package/src/xcode-project.ts +81 -134
- package/src/xcode-target-members.ts +107 -0
- package/cli/commands/compat.ts +0 -395
package/cli/app-stubs.ts
ADDED
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
// native stubs keyed by the package they answer (specs/peach-app-stubs.md):
|
|
2
|
+
// the app's peach.config.ts `packages`, installed `peach-stub-<package>`
|
|
3
|
+
// packages, and a library's own `peach` field, lowered into the engine's
|
|
4
|
+
// nativeModules / nativeViews wire slots.
|
|
5
|
+
|
|
6
|
+
import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from 'node:fs'
|
|
7
|
+
import { dirname, join, relative, resolve } from 'node:path'
|
|
8
|
+
import { resolveCompatVersionRange } from '@peach/compat/version-range'
|
|
9
|
+
import { parseSync, type Expression } from 'oxc-parser'
|
|
10
|
+
import { transform } from 'sucrase'
|
|
11
|
+
import {
|
|
12
|
+
isPackageStubs,
|
|
13
|
+
type NativeModuleResolution,
|
|
14
|
+
type NativeStub,
|
|
15
|
+
type NativeViewResolution,
|
|
16
|
+
type NativeViewSource,
|
|
17
|
+
type PackageStubs,
|
|
18
|
+
type PeachConfig,
|
|
19
|
+
} from '../src/config'
|
|
20
|
+
import { isRecord } from '../src/is-record'
|
|
21
|
+
import { visitParsedProgram } from '../src/visit-parsed-program'
|
|
22
|
+
import { readProjectManifest, scanRepository } from './commands/detect'
|
|
23
|
+
|
|
24
|
+
// who answers a package, in rising precedence
|
|
25
|
+
export type StubOrigin = 'library' | 'stub-package' | 'config'
|
|
26
|
+
|
|
27
|
+
export interface AppliedStubs {
|
|
28
|
+
target: string
|
|
29
|
+
origin: StubOrigin
|
|
30
|
+
// the package declaring the stubs; null for peach.config.ts
|
|
31
|
+
provider: string | null
|
|
32
|
+
providerVersion: string | null
|
|
33
|
+
modules: string[]
|
|
34
|
+
views: string[]
|
|
35
|
+
viewSources?: Record<string, NativeViewSource>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// a published stub whose `versions` range excludes the installed target
|
|
39
|
+
export interface SkippedStubs {
|
|
40
|
+
target: string
|
|
41
|
+
provider: string
|
|
42
|
+
providerVersion: string | null
|
|
43
|
+
versions: string
|
|
44
|
+
installed: string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface AppStubs {
|
|
48
|
+
applied: AppliedStubs[]
|
|
49
|
+
skipped: SkippedStubs[]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface StubSource {
|
|
53
|
+
target: string
|
|
54
|
+
origin: StubOrigin
|
|
55
|
+
provider: string | null
|
|
56
|
+
providerVersion: string | null
|
|
57
|
+
stubs: PackageStubs
|
|
58
|
+
// where relative stub paths resolve from
|
|
59
|
+
baseDir: string
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// `peach-stub-<package>` or `@scope/peach-stub-<package>`; a scoped target uses
|
|
63
|
+
// the DefinitelyTyped encoding, `@acme/scanner` as `peach-stub-acme__scanner`.
|
|
64
|
+
export function stubPackageTarget(name: string): string | null {
|
|
65
|
+
const match = /^(?:@[^/]+\/)?peach-stub-(.+)$/.exec(name)
|
|
66
|
+
if (!match?.[1]) return null
|
|
67
|
+
const split = match[1].indexOf('__')
|
|
68
|
+
return split > 0
|
|
69
|
+
? `@${match[1].slice(0, split)}/${match[1].slice(split + 2)}`
|
|
70
|
+
: match[1]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function resolveAppStubs(
|
|
74
|
+
appDir: string,
|
|
75
|
+
config: PeachConfig | undefined,
|
|
76
|
+
configDir: string,
|
|
77
|
+
): { config: PeachConfig | undefined; stubs: AppStubs } {
|
|
78
|
+
const sources: StubSource[] = []
|
|
79
|
+
const skipped: SkippedStubs[] = []
|
|
80
|
+
const manifest = readProjectManifest(appDir)
|
|
81
|
+
for (const name of Object.keys({
|
|
82
|
+
...manifest?.devDependencies,
|
|
83
|
+
...manifest?.dependencies,
|
|
84
|
+
})) {
|
|
85
|
+
const dir = installedPackageDir(appDir, name)
|
|
86
|
+
const installed = dir ? readPackageJson(dir) : null
|
|
87
|
+
if (!dir || !installed || installed.peach === undefined) continue
|
|
88
|
+
const stubTarget = stubPackageTarget(name)
|
|
89
|
+
const target = stubTarget ?? name
|
|
90
|
+
const field = installed.peach
|
|
91
|
+
const versions = isRecord(field) ? field.versions : undefined
|
|
92
|
+
const stubs = isRecord(field) ? { modules: field.modules, views: field.views } : field
|
|
93
|
+
if (
|
|
94
|
+
!isPackageStubs(stubs) ||
|
|
95
|
+
(versions !== undefined && typeof versions !== 'string')
|
|
96
|
+
)
|
|
97
|
+
throw new Error(
|
|
98
|
+
`${name}'s package.json "peach" field must be "noop" or { versions?, modules?, views? } whose values are "noop" or file paths`,
|
|
99
|
+
)
|
|
100
|
+
const providerVersion =
|
|
101
|
+
typeof installed.version === 'string' ? installed.version : null
|
|
102
|
+
const targetDir = stubTarget ? installedPackageDir(appDir, target) : dir
|
|
103
|
+
const targetVersion = targetDir ? readPackageJson(targetDir)?.version : undefined
|
|
104
|
+
// a stub for a package the app does not install answers nothing
|
|
105
|
+
if (typeof targetVersion !== 'string') continue
|
|
106
|
+
if (
|
|
107
|
+
versions !== undefined &&
|
|
108
|
+
resolveCompatVersionRange([versions], targetVersion).status !== 'matched'
|
|
109
|
+
) {
|
|
110
|
+
skipped.push({
|
|
111
|
+
target,
|
|
112
|
+
provider: name,
|
|
113
|
+
providerVersion,
|
|
114
|
+
versions,
|
|
115
|
+
installed: targetVersion,
|
|
116
|
+
})
|
|
117
|
+
continue
|
|
118
|
+
}
|
|
119
|
+
sources.push({
|
|
120
|
+
target,
|
|
121
|
+
origin: stubTarget ? 'stub-package' : 'library',
|
|
122
|
+
provider: name,
|
|
123
|
+
providerVersion,
|
|
124
|
+
stubs,
|
|
125
|
+
baseDir: dir,
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
for (const [target, stubs] of Object.entries(config?.packages ?? {})) {
|
|
129
|
+
sources.push({
|
|
130
|
+
target,
|
|
131
|
+
origin: 'config',
|
|
132
|
+
provider: null,
|
|
133
|
+
providerVersion: null,
|
|
134
|
+
stubs,
|
|
135
|
+
baseDir: configDir,
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const ORDER: Record<StubOrigin, number> = { library: 0, 'stub-package': 1, config: 2 }
|
|
140
|
+
sources.sort((a, b) => ORDER[a.origin] - ORDER[b.origin])
|
|
141
|
+
const nativeModules: Record<string, NativeModuleResolution> = {}
|
|
142
|
+
const nativeViews: Record<string, NativeViewResolution> = {}
|
|
143
|
+
const applied: AppliedStubs[] = []
|
|
144
|
+
for (const source of sources) {
|
|
145
|
+
const { modules, views } = expandStubs(appDir, source)
|
|
146
|
+
for (const [name, stub] of Object.entries(modules))
|
|
147
|
+
nativeModules[name] = lowerStub(stub, source.baseDir)
|
|
148
|
+
for (const [name, stub] of Object.entries(views))
|
|
149
|
+
nativeViews[name] =
|
|
150
|
+
typeof stub === 'string' ? lowerStub(stub, source.baseDir) : stub
|
|
151
|
+
applied.push({
|
|
152
|
+
target: source.target,
|
|
153
|
+
origin: source.origin,
|
|
154
|
+
provider: source.provider,
|
|
155
|
+
providerVersion: source.providerVersion,
|
|
156
|
+
modules: Object.keys(modules),
|
|
157
|
+
views: Object.keys(views),
|
|
158
|
+
...(Object.values(views).some((value) => typeof value !== 'string')
|
|
159
|
+
? {
|
|
160
|
+
viewSources: Object.fromEntries(
|
|
161
|
+
Object.entries(views).filter(
|
|
162
|
+
(entry): entry is [string, NativeViewSource] =>
|
|
163
|
+
typeof entry[1] !== 'string',
|
|
164
|
+
),
|
|
165
|
+
),
|
|
166
|
+
}
|
|
167
|
+
: {}),
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
const stubs = { applied, skipped }
|
|
171
|
+
if (!config && applied.length === 0) return { config, stubs }
|
|
172
|
+
const lowered: PeachConfig = { ...config, nativeModules, nativeViews }
|
|
173
|
+
delete lowered.packages
|
|
174
|
+
return { config: lowered, stubs }
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// a whole-package 'noop' answers every native name the installed package asks for
|
|
178
|
+
function expandStubs(
|
|
179
|
+
appDir: string,
|
|
180
|
+
source: StubSource,
|
|
181
|
+
): {
|
|
182
|
+
modules: Record<string, NativeStub>
|
|
183
|
+
views: Record<string, NativeStub | NativeViewSource>
|
|
184
|
+
} {
|
|
185
|
+
if (source.stubs !== 'noop')
|
|
186
|
+
return { modules: source.stubs.modules ?? {}, views: source.stubs.views ?? {} }
|
|
187
|
+
const dir = installedPackageDir(appDir, source.target)
|
|
188
|
+
if (!dir)
|
|
189
|
+
throw new Error(
|
|
190
|
+
`packages['${source.target}']: 'noop' answers the names an installed package asks for, and ${source.target} is not installed`,
|
|
191
|
+
)
|
|
192
|
+
const modules: Record<string, NativeStub> = {}
|
|
193
|
+
const views: Record<string, NativeStub> = {}
|
|
194
|
+
for (const request of packageNativeRequests(dir)) {
|
|
195
|
+
if (request.kind === 'module') modules[request.name] = 'noop'
|
|
196
|
+
else views[request.name] = 'noop'
|
|
197
|
+
}
|
|
198
|
+
return { modules, views }
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function lowerStub(stub: NativeStub, baseDir: string): 'noop' | { file: string } {
|
|
202
|
+
return stub === 'noop' ? stub : { file: resolve(baseDir, stub) }
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function readPackageJson(dir: string): Record<string, unknown> | null {
|
|
206
|
+
const text = readSmallFile(join(dir, 'package.json'))
|
|
207
|
+
if (!text) return null
|
|
208
|
+
try {
|
|
209
|
+
const parsed: unknown = JSON.parse(text)
|
|
210
|
+
return isRecord(parsed) ? parsed : null
|
|
211
|
+
} catch {
|
|
212
|
+
return null
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// the installed copy of a dependency, found the way node resolves it
|
|
217
|
+
export function installedPackageDir(appDir: string, name: string): string | null {
|
|
218
|
+
let current = appDir
|
|
219
|
+
while (true) {
|
|
220
|
+
const dir = join(current, 'node_modules', name)
|
|
221
|
+
if (existsSync(join(dir, 'package.json'))) return realpathSync(dir)
|
|
222
|
+
const parent = dirname(current)
|
|
223
|
+
if (parent === current) return null
|
|
224
|
+
current = parent
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export interface NativeRequest {
|
|
229
|
+
kind: 'module' | 'view'
|
|
230
|
+
name: string
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export const SOURCE_FILE = /\.[cm]?[jt]sx?$/
|
|
234
|
+
const NATIVE_LOOKUP =
|
|
235
|
+
/requireNative|requireOptionalNative|TurboModuleRegistry|codegenNativeComponent|NativeModules/
|
|
236
|
+
|
|
237
|
+
function nativeLookupName(node: Expression): string | null {
|
|
238
|
+
if (node.type === 'Identifier') return node.name
|
|
239
|
+
if (
|
|
240
|
+
node.type === 'MemberExpression' &&
|
|
241
|
+
!node.computed &&
|
|
242
|
+
node.property.type === 'Identifier'
|
|
243
|
+
)
|
|
244
|
+
return node.property.name
|
|
245
|
+
return null
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// sucrase removes flow and typescript types while retaining source lines. oxc
|
|
249
|
+
// then visits executable syntax, including expressions inside template literals,
|
|
250
|
+
// without interpreting comments, fixture strings or type queries as requests.
|
|
251
|
+
export function sourceNativeRequests(
|
|
252
|
+
file: string,
|
|
253
|
+
text: string,
|
|
254
|
+
includeLegacy = false,
|
|
255
|
+
): (NativeRequest & { line: number })[] {
|
|
256
|
+
if (!NATIVE_LOOKUP.test(text)) return []
|
|
257
|
+
let code: string
|
|
258
|
+
try {
|
|
259
|
+
code = transform(text, {
|
|
260
|
+
transforms: [/\.[cm]?tsx?$/.test(file) ? 'typescript' : 'flow', 'jsx'],
|
|
261
|
+
disableESTransforms: true,
|
|
262
|
+
}).code
|
|
263
|
+
} catch {
|
|
264
|
+
return []
|
|
265
|
+
}
|
|
266
|
+
const { program, errors } = parseSync(file, code, { lang: 'jsx' })
|
|
267
|
+
if (errors.length) return []
|
|
268
|
+
const found: (NativeRequest & { line: number })[] = []
|
|
269
|
+
const add = (kind: NativeRequest['kind'], name: string, at: number) => {
|
|
270
|
+
found.push({ kind, name, line: code.slice(0, at).split('\n').length })
|
|
271
|
+
}
|
|
272
|
+
visitParsedProgram(program, {
|
|
273
|
+
CallExpression(node) {
|
|
274
|
+
const first = node.arguments[0]
|
|
275
|
+
if (first?.type !== 'Literal' || typeof first.value !== 'string') return
|
|
276
|
+
const callee = node.callee
|
|
277
|
+
const lookup = nativeLookupName(callee)
|
|
278
|
+
if (
|
|
279
|
+
callee.type === 'MemberExpression' &&
|
|
280
|
+
!callee.computed &&
|
|
281
|
+
nativeLookupName(callee.object) === 'TurboModuleRegistry' &&
|
|
282
|
+
(lookup === 'get' || lookup === 'getEnforcing')
|
|
283
|
+
) {
|
|
284
|
+
add('module', first.value, node.start)
|
|
285
|
+
} else if (lookup) {
|
|
286
|
+
if (['requireNativeModule', 'requireOptionalNativeModule'].includes(lookup))
|
|
287
|
+
add('module', first.value, node.start)
|
|
288
|
+
else if (['requireNativeComponent', 'codegenNativeComponent'].includes(lookup))
|
|
289
|
+
add('view', first.value, node.start)
|
|
290
|
+
else if (['requireNativeView', 'requireNativeViewManager'].includes(lookup)) {
|
|
291
|
+
const second = node.arguments[1]
|
|
292
|
+
// expo registers a named view under the module_view key.
|
|
293
|
+
const name =
|
|
294
|
+
second?.type === 'Literal' && typeof second.value === 'string'
|
|
295
|
+
? `${first.value}_${second.value}`
|
|
296
|
+
: first.value
|
|
297
|
+
add('view', name, node.start)
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
MemberExpression(node) {
|
|
302
|
+
if (
|
|
303
|
+
includeLegacy &&
|
|
304
|
+
!node.computed &&
|
|
305
|
+
nativeLookupName(node.object) === 'NativeModules' &&
|
|
306
|
+
node.property.type === 'Identifier'
|
|
307
|
+
)
|
|
308
|
+
add('module', node.property.name, node.start)
|
|
309
|
+
},
|
|
310
|
+
})
|
|
311
|
+
return found
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// the native names a package's own JavaScript asks for, outside its native
|
|
315
|
+
// sources (top-level ios and android; src/ios is JavaScript), tests and examples
|
|
316
|
+
const NATIVE_SOURCE_DIRS = new Set(['ios', 'android'])
|
|
317
|
+
const PACKAGE_SKIP_DIRS = new Set([
|
|
318
|
+
'example',
|
|
319
|
+
'examples',
|
|
320
|
+
'__tests__',
|
|
321
|
+
'__mocks__',
|
|
322
|
+
'docs',
|
|
323
|
+
])
|
|
324
|
+
|
|
325
|
+
export function packageNativeRequests(dir: string): NativeRequest[] {
|
|
326
|
+
const found = new Map<string, NativeRequest>()
|
|
327
|
+
const directories = scanRepository(dir).directories.filter((sub) => {
|
|
328
|
+
const segments = relative(dir, sub).split('/')
|
|
329
|
+
return (
|
|
330
|
+
!NATIVE_SOURCE_DIRS.has(segments[0]) &&
|
|
331
|
+
!segments.some((segment) => PACKAGE_SKIP_DIRS.has(segment))
|
|
332
|
+
)
|
|
333
|
+
})
|
|
334
|
+
let files = 0
|
|
335
|
+
for (const sub of directories) {
|
|
336
|
+
for (const name of listDir(sub)) {
|
|
337
|
+
if (!SOURCE_FILE.test(name) || name.endsWith('.d.ts') || files >= 2_000) continue
|
|
338
|
+
files++
|
|
339
|
+
const file = join(sub, name)
|
|
340
|
+
const text = readSmallFile(file)
|
|
341
|
+
if (!text) continue
|
|
342
|
+
for (const { kind, name } of sourceNativeRequests(file, text, true))
|
|
343
|
+
found.set(`${kind}:${name}`, { kind, name })
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return [...found.values()].sort(
|
|
347
|
+
(a, b) => a.kind.localeCompare(b.kind) || a.name.localeCompare(b.name),
|
|
348
|
+
)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// the `packages` block that replaces a config's old top-level wire slots: each
|
|
352
|
+
// name keyed by the direct dependency whose JavaScript asks for it. `packages`
|
|
353
|
+
// takes only 'noop' or a file path, so any other old value is named for a hand
|
|
354
|
+
// move instead of guessed.
|
|
355
|
+
export function wireSlotMigration(
|
|
356
|
+
appDir: string,
|
|
357
|
+
wire: { modules: Record<string, unknown>; views: Record<string, unknown> },
|
|
358
|
+
): string {
|
|
359
|
+
const owners = new Map<string, string>()
|
|
360
|
+
const manifest = readProjectManifest(appDir)
|
|
361
|
+
for (const name of Object.keys({
|
|
362
|
+
...manifest?.devDependencies,
|
|
363
|
+
...manifest?.dependencies,
|
|
364
|
+
})) {
|
|
365
|
+
const dir = installedPackageDir(appDir, name)
|
|
366
|
+
if (!dir) continue
|
|
367
|
+
for (const request of packageNativeRequests(dir))
|
|
368
|
+
if (!owners.has(`${request.kind}:${request.name}`))
|
|
369
|
+
owners.set(`${request.kind}:${request.name}`, name)
|
|
370
|
+
}
|
|
371
|
+
// the app's own native code answers under its package name
|
|
372
|
+
if (typeof manifest?.name === 'string')
|
|
373
|
+
for (const request of packageNativeRequests(appDir))
|
|
374
|
+
if (!owners.has(`${request.kind}:${request.name}`))
|
|
375
|
+
owners.set(`${request.kind}:${request.name}`, manifest.name)
|
|
376
|
+
const unknownOwner = '<package that asks for it>'
|
|
377
|
+
const blocks = new Map<string, { modules: string[]; views: string[] }>()
|
|
378
|
+
const manual: string[] = []
|
|
379
|
+
for (const [kind, entries] of [
|
|
380
|
+
['module', wire.modules],
|
|
381
|
+
['view', wire.views],
|
|
382
|
+
] as const) {
|
|
383
|
+
for (const [name, value] of Object.entries(entries)) {
|
|
384
|
+
const stub =
|
|
385
|
+
value === 'noop'
|
|
386
|
+
? JSON.stringify(value)
|
|
387
|
+
: isRecord(value) &&
|
|
388
|
+
typeof value.file === 'string' &&
|
|
389
|
+
Object.keys(value).length === 1
|
|
390
|
+
? JSON.stringify(value.file)
|
|
391
|
+
: null
|
|
392
|
+
if (!stub) {
|
|
393
|
+
manual.push(
|
|
394
|
+
`${name}: packages takes 'noop' or a file path; move this ${kind} into a file that default-exports it and name that file`,
|
|
395
|
+
)
|
|
396
|
+
continue
|
|
397
|
+
}
|
|
398
|
+
const owner = owners.get(`${kind}:${name}`) ?? unknownOwner
|
|
399
|
+
const block = blocks.get(owner) ?? { modules: [], views: [] }
|
|
400
|
+
block[kind === 'module' ? 'modules' : 'views'].push(
|
|
401
|
+
`[${JSON.stringify(name)}]: ${stub}`,
|
|
402
|
+
)
|
|
403
|
+
blocks.set(owner, block)
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
const lines = [...blocks].map(([owner, { modules, views }]) => {
|
|
407
|
+
const parts = [
|
|
408
|
+
modules.length ? `modules: { ${modules.join(', ')} }` : '',
|
|
409
|
+
views.length ? `views: { ${views.join(', ')} }` : '',
|
|
410
|
+
].filter(Boolean)
|
|
411
|
+
return ` [${JSON.stringify(owner)}]: { ${parts.join(', ')} },`
|
|
412
|
+
})
|
|
413
|
+
return [
|
|
414
|
+
...(lines.length ? [' packages: {', ...lines, ' },'] : []),
|
|
415
|
+
...manual.map((line) => ` // ${line}`),
|
|
416
|
+
].join('\n')
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export function listDir(dir: string): string[] {
|
|
420
|
+
try {
|
|
421
|
+
return readdirSync(dir)
|
|
422
|
+
} catch {
|
|
423
|
+
return []
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export function readSmallFile(file: string): string | null {
|
|
428
|
+
try {
|
|
429
|
+
return statSync(file).size <= 1024 * 1024 ? readFileSync(file, 'utf8') : null
|
|
430
|
+
} catch {
|
|
431
|
+
return null
|
|
432
|
+
}
|
|
433
|
+
}
|
package/cli/bin.ts
CHANGED
|
@@ -13,6 +13,15 @@
|
|
|
13
13
|
import { exitWithFlushedOutput } from '../src/process-output'
|
|
14
14
|
import { RNX_FORKED_SCRIPT_ENV, RNX_INTERNAL_COMMAND } from './internal-child'
|
|
15
15
|
|
|
16
|
+
// a consumer such as head may close its pipe before the command finishes.
|
|
17
|
+
// exit directly: reporting this through stderr can fail on the same pipe.
|
|
18
|
+
for (const stream of [process.stdout, process.stderr]) {
|
|
19
|
+
stream.on('error', (error: NodeJS.ErrnoException) => {
|
|
20
|
+
if (error.code === 'EPIPE') process.exit(1)
|
|
21
|
+
throw error
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
16
25
|
if (process.argv[2] === '__shell-init') {
|
|
17
26
|
const { renderRnxShellInit } = await import('./shell-init')
|
|
18
27
|
try {
|
|
@@ -53,9 +62,13 @@ if (process.argv[2] === '__cloud-confirm') {
|
|
|
53
62
|
// work does. so this is the branch that must skip the CLI rather than the CLI
|
|
54
63
|
// that must skip it.
|
|
55
64
|
const forkedScript = process.argv[2]
|
|
56
|
-
if (
|
|
65
|
+
if (
|
|
66
|
+
process.env[RNX_FORKED_SCRIPT_ENV] === '1' &&
|
|
67
|
+
forkedScript &&
|
|
68
|
+
/\.(?:c|m)?js$/.test(forkedScript)
|
|
69
|
+
) {
|
|
57
70
|
const { runScript } = await import('./internal-child')
|
|
58
|
-
runScript(forkedScript, process.argv.slice(3))
|
|
71
|
+
await runScript(forkedScript, process.argv.slice(3))
|
|
59
72
|
} else if (process.argv[2] === RNX_INTERNAL_COMMAND) {
|
|
60
73
|
const { runInternalChild } = await import('./internal-child')
|
|
61
74
|
await runInternalChild(process.argv.slice(3))
|