peachsim 0.1.527 → 0.1.571
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 +28 -28
- package/cli/app-fonts.ts +1 -1
- package/cli/app-splash.ts +1 -1
- package/cli/bin.ts +1 -1
- package/cli/bridge-diagnostics.ts +1 -1
- package/cli/bridge-flow-runner.ts +72 -12
- package/cli/cloud-client.ts +273 -6
- package/cli/cloud-dispatch.ts +23 -2
- package/cli/cloud-session.ts +60 -4
- package/cli/cloud-test-result-files.ts +97 -0
- package/cli/cloud-test-tunnel.ts +354 -0
- package/cli/commands/box/bundle-plane.ts +2 -2
- package/cli/commands/box/checkout-plane.ts +1 -1
- package/cli/commands/box/rnx-command.ts +2 -2
- package/cli/commands/box/search-command.ts +2 -2
- package/cli/commands/box.ts +8 -8
- package/cli/commands/compat.ts +1 -1
- package/cli/commands/control.ts +22 -3
- package/cli/commands/detox.ts +1 -1
- package/cli/commands/device.ts +1 -1
- package/cli/commands/diagnose.ts +1 -1
- package/cli/commands/film.ts +1 -1
- package/cli/commands/flow.ts +60 -3
- package/cli/commands/inspect/actions.ts +1 -1
- package/cli/commands/inspect/core.ts +6 -7
- package/cli/commands/inspect/find.ts +2 -1
- package/cli/commands/inspect/resolve-target.ts +5 -3
- package/cli/commands/inspect/shared.ts +1 -1
- package/cli/commands/inspect/wait-event.ts +1 -1
- package/cli/commands/inspect/wait-selector.ts +15 -4
- package/cli/commands/inspect.ts +19 -14
- package/cli/commands/maestro.ts +582 -69
- package/cli/commands/permissions.ts +1 -1
- package/cli/commands/platform.ts +205 -8
- package/cli/commands/preview.ts +2 -2
- package/cli/commands/record.ts +2 -2
- package/cli/commands/report-issue.ts +2 -2
- package/cli/commands/screenshot-layers.ts +1 -1
- package/cli/commands/screenshot.ts +1 -1
- package/cli/commands/setup.ts +2 -2
- package/cli/commands/test.ts +600 -117
- package/cli/commands/three-mode.ts +1 -1
- package/cli/commands/upload.ts +5 -5
- package/cli/commands/what-happened.ts +1 -1
- package/cli/desktop-companion.ts +3 -3
- package/cli/drivers/playwright-sim-host.ts +2 -2
- package/cli/flow-file.ts +7 -0
- package/cli/flow-live-status.ts +65 -8
- package/cli/help-core.ts +3 -3
- package/cli/hidden-runtime-alias.ts +1 -1
- package/cli/maestro-report.ts +180 -0
- package/cli/main.ts +1 -1
- package/cli/outbound-endpoints.ts +187 -136
- package/cli/parse-args.ts +1 -1
- package/cli/registry.ts +1 -1
- package/cli/resolve-assets.ts +1 -1
- package/cli/run-registry.ts +10 -1
- package/cli/send-to-box.ts +4 -4
- package/cli/ws-bridge.ts +3 -5
- package/detox/expectations.ts +1 -1
- package/detox/gestures.ts +2 -2
- package/detox/index.ts +71 -29
- package/detox/jest-preset.cjs +5 -5
- package/detox/jest-setup-after-env.cjs +1 -1
- package/dist-lib/agent-daemon-client.cjs +2 -2
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.cjs +1 -1
- package/dist-lib/agent-sessions.cjs +2 -2
- package/dist-lib/attached-projects.cjs +2 -2
- package/dist-lib/auth/shared-session.cjs +1 -1
- 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/capture-contract.cjs +94 -3
- package/dist-lib/capture-contract.mjs +93 -3
- package/dist-lib/cli-constants.cjs +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 +116 -0
- package/dist-lib/cloud-test-args.mjs +86 -0
- package/dist-lib/cloud.cjs +2 -2
- package/dist-lib/cloud.mjs +2 -2
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +106 -25
- package/dist-lib/detox/jest-preset.cjs +5 -5
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +150 -65
- package/dist-lib/host/fetch-proxy-handler.cjs +28 -2
- package/dist-lib/host/fetch-proxy-overrides.cjs +33 -3
- package/dist-lib/host/fetch-proxy-overrides.mjs +30 -2
- package/dist-lib/host/replacement-module-handler.cjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +224 -41
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/menu.cjs +2 -2
- package/dist-lib/menu.mjs +2 -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 +3 -3
- package/dist-lib/metro-production-bundle.mjs +3 -3
- package/dist-lib/metro.cjs +8 -8
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +2 -2
- package/dist-lib/react-native-host-modules.cjs +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +31 -15
- package/dist-lib/sdk.cjs +3 -2
- package/dist-lib/sdk.mjs +3 -2
- package/dist-lib/skills.cjs +478 -266
- package/dist-lib/swift-compile.cjs +372 -0
- package/dist-lib/swift-compile.mjs +339 -0
- package/dist-lib/swift-package.cjs +78 -0
- package/dist-lib/swift-package.mjs +52 -0
- package/dist-lib/swift-submit.cjs +1 -1
- package/dist-lib/swift-submit.mjs +1 -1
- package/dist-lib/swift.cjs +583 -16
- package/dist-lib/vite.cjs +483 -150
- package/package.json +30 -9
- package/scripts/dev-server-scanner.ts +41 -14
- package/scripts/optional-demo-registry.ts +3 -3
- package/skills/contrast/SKILL.md +9 -9
- package/skills/rnx-debug/SKILL.md +14 -4
- package/skills/rnx-perf/SKILL.md +5 -5
- package/skills/rnx-setup/SKILL.md +11 -11
- package/skills/rnx-test/SKILL.md +14 -14
- package/skills/rnx-visual/SKILL.md +6 -6
- package/src/agent-sessions.ts +3 -3
- package/src/attached-projects.ts +1 -1
- package/src/bridge-contract-input.ts +1 -1
- package/src/bridge-contract.ts +23 -5
- package/src/capture-contract.ts +145 -3
- package/src/cloud-test-args.ts +100 -0
- package/src/config.ts +2 -2
- package/src/connect.ts +2 -2
- package/src/home-paths.ts +3 -3
- package/src/host/bridge-host.ts +2 -2
- package/src/host/fetch-proxy-handler.ts +4 -3
- package/src/host/fetch-proxy-overrides.ts +41 -0
- package/src/host/plane-host.ts +2 -2
- package/src/index.ts +3 -0
- package/src/metro-plugin.ts +4 -4
- package/src/metro-production-bundle.ts +2 -2
- package/src/native-seam-manifest.ts +6 -6
- package/src/public-brand.ts +1 -1
- package/src/rolldown-plugin-swift.ts +16 -12
- package/src/runtime-assets.ts +8 -6
- package/src/screenshots/frame-compose.ts +24 -56
- package/src/screenshots/org-deck.ts +1 -1
- package/src/screenshots/registry.ts +4 -4
- package/src/screenshots/schema.ts +1 -1
- package/src/sdk.ts +2 -2
- package/src/settings-bundle.ts +1 -1
- package/src/sim-client.ts +2 -4
- package/src/skills/builtin/compat-check.ts +1 -1
- package/src/swift-compile.ts +359 -0
- package/src/swift-package.ts +69 -0
- package/src/swift-submit.ts +3 -2
- package/src/vite-plugin-one.ts +1 -1
- package/src/vite-plugin-swift.ts +114 -113
- package/src/vite-plugin.ts +30 -17
- package/src/worklets-babel.ts +2 -2
package/src/vite-plugin-swift.ts
CHANGED
|
@@ -1,41 +1,30 @@
|
|
|
1
|
-
// `.swift` handling for a vite dev server:
|
|
2
|
-
//
|
|
3
|
-
// and answer every `.swift` import with the
|
|
1
|
+
// `.swift` handling for a vite dev server: send the project source set through
|
|
2
|
+
// the same project-unit compiler as `/api/swift-compile`, serve the resulting
|
|
3
|
+
// artifact at a content-hashed url, and answer every `.swift` import with the
|
|
4
|
+
// module that mounts it.
|
|
4
5
|
//
|
|
5
6
|
// two hosts use this. the bundler that builds the app's native bundle runs
|
|
6
7
|
// resolveId/load, and the dev server runs the middleware that serves the
|
|
7
8
|
// artifact. both share the build below, so the artifact is compiled once.
|
|
8
9
|
//
|
|
9
|
-
// the hash
|
|
10
|
-
//
|
|
11
|
-
// remounting.
|
|
10
|
+
// the hash covers the same source/module/toolchain inputs as the remote
|
|
11
|
+
// service, so a new hash means a genuinely different app: createSwiftEntry
|
|
12
|
+
// moves the @State store across it instead of remounting.
|
|
12
13
|
|
|
13
|
-
import { execFile } from 'node:child_process'
|
|
14
14
|
import { createHash } from 'node:crypto'
|
|
15
15
|
import fs from 'node:fs'
|
|
16
16
|
import os from 'node:os'
|
|
17
17
|
import path from 'node:path'
|
|
18
|
-
import { promisify } from 'node:util'
|
|
19
18
|
import {
|
|
20
19
|
enumerateBundleImages,
|
|
21
20
|
enumerateBundleJson,
|
|
22
21
|
} from '../../sootsim-swift/src/bundleAssets'
|
|
23
|
-
import
|
|
22
|
+
import { swiftEntryModuleEsm } from '../../sootsim-swift/src/entryModule'
|
|
23
|
+
import { compileSwiftProjectUnit, swiftModuleInputFiles } from './swift-compile'
|
|
24
|
+
import type { SwiftCompileInput } from './swift-compile'
|
|
25
|
+
import type { SwiftImageUpload } from './swift-submit'
|
|
24
26
|
import type { Plugin, ViteDevServer } from 'vite'
|
|
25
27
|
|
|
26
|
-
const run = promisify(execFile)
|
|
27
|
-
|
|
28
|
-
// the swift.org release scripts/build.sh builds with, from the one place it is
|
|
29
|
-
// named: the toolchain is <release>.xctoolchain and its matching wasm sdk is
|
|
30
|
-
// <release>_wasm. xcode's swift has no wasm backend, so this toolchain is the
|
|
31
|
-
// only one that can build the target.
|
|
32
|
-
const TOOLCHAIN_BIN = path.join(
|
|
33
|
-
os.homedir(),
|
|
34
|
-
`Library/Developer/Toolchains/${toolchain.release}.xctoolchain/usr/bin`,
|
|
35
|
-
)
|
|
36
|
-
const WASM_SDK = `${toolchain.release}_wasm`
|
|
37
|
-
const WASM_TRIPLE = 'wasm32-unknown-wasip1'
|
|
38
|
-
|
|
39
28
|
const ARTIFACT_PATH = '/__rnx/swift/'
|
|
40
29
|
const RESOURCE_PATH = '/__rnx/swift-res/'
|
|
41
30
|
const VIRTUAL_PREFIX = '\0rnx-swift:'
|
|
@@ -140,6 +129,8 @@ const RESOURCE_CONTENT_TYPES: Record<string, string> = {
|
|
|
140
129
|
export interface SwiftResourceTable {
|
|
141
130
|
/** bundle asset name to absolute serving url. */
|
|
142
131
|
table: Record<string, string>
|
|
132
|
+
/** the same rasters as compile-address inputs. */
|
|
133
|
+
images: Record<string, SwiftImageUpload>
|
|
143
134
|
/** project-relative image files this load's table covers (for watching). */
|
|
144
135
|
watched: string[]
|
|
145
136
|
}
|
|
@@ -167,6 +158,7 @@ export function buildSwiftResourceTable(
|
|
|
167
158
|
}
|
|
168
159
|
})
|
|
169
160
|
const table: Record<string, string> = {}
|
|
161
|
+
const images: Record<string, SwiftImageUpload> = {}
|
|
170
162
|
const watched: string[] = []
|
|
171
163
|
const registered = new Map<string, string>()
|
|
172
164
|
for (const { name, file } of found) {
|
|
@@ -181,6 +173,10 @@ export function buildSwiftResourceTable(
|
|
|
181
173
|
const hash = createHash('sha256').update(bytes).digest('hex').slice(0, 16)
|
|
182
174
|
const ext = path.posix.extname(file).toLowerCase()
|
|
183
175
|
table[name] = `${baseUrl}${RESOURCE_PATH}${hash}${ext}`
|
|
176
|
+
images[name] = {
|
|
177
|
+
file: path.posix.basename(file),
|
|
178
|
+
data: bytes.toString('base64'),
|
|
179
|
+
}
|
|
184
180
|
watched.push(file)
|
|
185
181
|
// a Contents.json edit (scale swap, file rename) re-resolves the same
|
|
186
182
|
// way a raster edit does, so the catalog rides the watcher too.
|
|
@@ -189,7 +185,7 @@ export function buildSwiftResourceTable(
|
|
|
189
185
|
registered.set(`${hash}${ext}`, disk)
|
|
190
186
|
}
|
|
191
187
|
project.resourceFiles = registered
|
|
192
|
-
return { table, watched }
|
|
188
|
+
return { table, images, watched }
|
|
193
189
|
}
|
|
194
190
|
|
|
195
191
|
// the /res pack: every Resources/ json in the package, basename-keyed and
|
|
@@ -292,22 +288,41 @@ export function linkedModuleRoots(packagePath: string): string[] {
|
|
|
292
288
|
return [...roots].sort()
|
|
293
289
|
}
|
|
294
290
|
|
|
295
|
-
// the stamp covers
|
|
296
|
-
//
|
|
297
|
-
//
|
|
298
|
-
//
|
|
299
|
-
//
|
|
300
|
-
//
|
|
301
|
-
//
|
|
302
|
-
// stable when a checkout restores byte-identical files. a restore rebuilds
|
|
303
|
-
// here and reuses the stored artifact there, by design.
|
|
291
|
+
// the stamp covers every file that contributes to compileInput plus the
|
|
292
|
+
// linked module and toolchain release that the shared artifact hash names. a
|
|
293
|
+
// running dev server rebuilds only when this moves, so its input closure must
|
|
294
|
+
// stay identical to the artifact address's even though this cheap watcher
|
|
295
|
+
// signal hashes mtimes while that stable address hashes content. a checkout
|
|
296
|
+
// restoring byte-identical files rebuilds here and reuses the same address
|
|
297
|
+
// there, by design.
|
|
304
298
|
export function sourceStamp(packagePath: string): string {
|
|
305
299
|
const hash = createHash('sha256')
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
300
|
+
hash.update(packagePath)
|
|
301
|
+
const projectInputs = new Set([
|
|
302
|
+
path.join(packagePath, 'Package.swift'),
|
|
303
|
+
...sourceFiles(packagePath),
|
|
304
|
+
...buildSwiftBundleFiles(packagePath).watched.map((file) =>
|
|
305
|
+
path.join(packagePath, ...file.split(path.posix.sep)),
|
|
306
|
+
),
|
|
307
|
+
...buildSwiftResourceTable(projectFor(packagePath), '').watched.map((file) =>
|
|
308
|
+
path.join(packagePath, ...file.split(path.posix.sep)),
|
|
309
|
+
),
|
|
310
|
+
])
|
|
311
|
+
for (const file of [...projectInputs].sort()) {
|
|
312
|
+
hash.update(path.relative(packagePath, file))
|
|
313
|
+
hash.update(String(fs.statSync(file).mtimeMs))
|
|
314
|
+
}
|
|
315
|
+
const moduleRoot = linkedModuleRoots(packagePath).find((root) =>
|
|
316
|
+
fs.existsSync(path.join(root, 'toolchain.json')),
|
|
317
|
+
)
|
|
318
|
+
if (moduleRoot) {
|
|
319
|
+
hash.update(moduleRoot)
|
|
320
|
+
for (const relative of [
|
|
321
|
+
...swiftModuleInputFiles(moduleRoot),
|
|
322
|
+
'toolchain.json',
|
|
323
|
+
].sort()) {
|
|
324
|
+
hash.update(relative)
|
|
325
|
+
hash.update(String(fs.statSync(path.join(moduleRoot, relative)).mtimeMs))
|
|
311
326
|
}
|
|
312
327
|
}
|
|
313
328
|
return hash.digest('hex').slice(0, 16)
|
|
@@ -338,82 +353,62 @@ function artifactUrl(project: SwiftProject, artifact: SwiftArtifact): string {
|
|
|
338
353
|
return `${serverOrigin()}${ARTIFACT_PATH}${artifact.hash}.wasm`
|
|
339
354
|
}
|
|
340
355
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
356
|
+
function compileInput(packagePath: string, entry: string): SwiftCompileInput {
|
|
357
|
+
const files: Record<string, string> = {}
|
|
358
|
+
for (const file of sourceFiles(packagePath)) {
|
|
359
|
+
const relative = path.relative(packagePath, file).split(path.sep).join(path.posix.sep)
|
|
360
|
+
files[relative] = fs.readFileSync(file, 'utf8')
|
|
361
|
+
}
|
|
362
|
+
const manifest = path.join(packagePath, 'Package.swift')
|
|
363
|
+
if (fs.existsSync(manifest)) files['Package.swift'] = fs.readFileSync(manifest, 'utf8')
|
|
364
|
+
const resources = buildSwiftBundleFiles(packagePath).files
|
|
365
|
+
const images = buildSwiftResourceTable(projectFor(packagePath), '').images
|
|
366
|
+
return {
|
|
367
|
+
files,
|
|
368
|
+
resources,
|
|
369
|
+
images,
|
|
370
|
+
entry,
|
|
355
371
|
}
|
|
356
|
-
return path.join(binDir, artifacts[0])
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
function streamText(value: unknown): string {
|
|
360
|
-
if (typeof value === 'string') return value.trim()
|
|
361
|
-
if (Buffer.isBuffer(value)) return value.toString('utf8').trim()
|
|
362
|
-
return ''
|
|
363
372
|
}
|
|
364
373
|
|
|
365
|
-
async function compile(
|
|
366
|
-
|
|
367
|
-
|
|
374
|
+
async function compile(
|
|
375
|
+
packagePath: string,
|
|
376
|
+
entry: string,
|
|
377
|
+
stamp: string,
|
|
378
|
+
): Promise<SwiftArtifact> {
|
|
379
|
+
const moduleRoot = linkedModuleRoots(packagePath).find((root) =>
|
|
380
|
+
fs.existsSync(path.join(root, 'toolchain.json')),
|
|
381
|
+
)
|
|
382
|
+
if (!moduleRoot) {
|
|
368
383
|
throw new Error(
|
|
369
|
-
`
|
|
370
|
-
)
|
|
371
|
-
}
|
|
372
|
-
try {
|
|
373
|
-
await run(
|
|
374
|
-
swift,
|
|
375
|
-
[
|
|
376
|
-
'build',
|
|
377
|
-
'--package-path',
|
|
378
|
-
packagePath,
|
|
379
|
-
'--swift-sdk',
|
|
380
|
-
WASM_SDK,
|
|
381
|
-
'-c',
|
|
382
|
-
'release',
|
|
383
|
-
'-Xswiftc',
|
|
384
|
-
'-Osize',
|
|
385
|
-
],
|
|
386
|
-
{ maxBuffer: 64 * 1024 * 1024 },
|
|
384
|
+
`swift project ${packagePath} has no linked rnx SwiftUI package with toolchain.json`,
|
|
387
385
|
)
|
|
388
|
-
} catch (error) {
|
|
389
|
-
// `swift build` prints the compiler's diagnostics on stdout and swiftpm's
|
|
390
|
-
// own on stderr; the failure's message holds only the command line, so
|
|
391
|
-
// both streams are what names the line that broke. stdout leads because
|
|
392
|
-
// compiler diagnostics are what the editor needs to show.
|
|
393
|
-
const streams =
|
|
394
|
-
error && typeof error === 'object'
|
|
395
|
-
? [
|
|
396
|
-
streamText(Reflect.get(error, 'stdout')),
|
|
397
|
-
streamText(Reflect.get(error, 'stderr')),
|
|
398
|
-
].filter(Boolean)
|
|
399
|
-
: []
|
|
400
|
-
const detail =
|
|
401
|
-
streams.length > 0
|
|
402
|
-
? streams.join('\n')
|
|
403
|
-
: error instanceof Error
|
|
404
|
-
? error.message
|
|
405
|
-
: String(error)
|
|
406
|
-
throw new Error(`swift build failed for ${packagePath}\n${detail}`)
|
|
407
386
|
}
|
|
408
|
-
const
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
387
|
+
const toolchain: unknown = JSON.parse(
|
|
388
|
+
fs.readFileSync(path.join(moduleRoot, 'toolchain.json'), 'utf8'),
|
|
389
|
+
)
|
|
390
|
+
const release =
|
|
391
|
+
toolchain && typeof toolchain === 'object'
|
|
392
|
+
? Reflect.get(toolchain, 'release')
|
|
393
|
+
: undefined
|
|
394
|
+
if (typeof release !== 'string') {
|
|
395
|
+
throw new Error(`swift toolchain manifest at ${moduleRoot} has no release`)
|
|
413
396
|
}
|
|
397
|
+
const artifact = await compileSwiftProjectUnit(compileInput(packagePath, entry), {
|
|
398
|
+
buildRoot:
|
|
399
|
+
process.env.CONTRAST_SWIFT_BUILD_ROOT ??
|
|
400
|
+
path.join(os.tmpdir(), 'contrast-swift-build'),
|
|
401
|
+
moduleRoot,
|
|
402
|
+
release,
|
|
403
|
+
toolchainBin: path.join(
|
|
404
|
+
os.homedir(),
|
|
405
|
+
`Library/Developer/Toolchains/${release}.xctoolchain/usr/bin`,
|
|
406
|
+
),
|
|
407
|
+
})
|
|
408
|
+
return { ...artifact, stamp }
|
|
414
409
|
}
|
|
415
410
|
|
|
416
|
-
function artifactFor(project: SwiftProject): Promise<SwiftArtifact> {
|
|
411
|
+
function artifactFor(project: SwiftProject, entry: string): Promise<SwiftArtifact> {
|
|
417
412
|
const stamp = sourceStamp(project.packagePath)
|
|
418
413
|
if (project.artifact?.stamp === stamp) return Promise.resolve(project.artifact)
|
|
419
414
|
if (project.building) {
|
|
@@ -423,10 +418,10 @@ function artifactFor(project: SwiftProject): Promise<SwiftArtifact> {
|
|
|
423
418
|
// would emit the older artifact and nothing would trigger another load.
|
|
424
419
|
const running = project.building
|
|
425
420
|
if (running.stamp === stamp) return running.promise
|
|
426
|
-
const again = () => artifactFor(project)
|
|
421
|
+
const again = () => artifactFor(project, entry)
|
|
427
422
|
return running.promise.then(again, again)
|
|
428
423
|
}
|
|
429
|
-
const promise = compile(project.packagePath, stamp).then(
|
|
424
|
+
const promise = compile(project.packagePath, entry, stamp).then(
|
|
430
425
|
(artifact) => {
|
|
431
426
|
project.artifact = artifact
|
|
432
427
|
project.building = null
|
|
@@ -498,11 +493,13 @@ export function swiftPlugin(options: SwiftPluginOptions = {}): Plugin {
|
|
|
498
493
|
if (!fs.existsSync(file)) throw new Error(`swift source ${file} no longer exists`)
|
|
499
494
|
const packagePath = options.packagePath ?? packageFor(file)
|
|
500
495
|
const project = projectFor(packagePath)
|
|
496
|
+
const entry = path.relative(packagePath, file).split(path.sep).join(path.posix.sep)
|
|
501
497
|
// every source in the package is a dependency of this module, so a
|
|
502
498
|
// bundler watching this module learns about a `.swift` edit and re-runs
|
|
503
499
|
// load. that is the whole hot reload: the new hash in the emitted module
|
|
504
500
|
// is what tells the mount it is looking at a different app.
|
|
505
501
|
for (const source of sourceFiles(packagePath)) this.addWatchFile(source)
|
|
502
|
+
this.addWatchFile(path.join(packagePath, 'Package.swift'))
|
|
506
503
|
// an edit to the linked SwiftUI module rebuilds the artifact without
|
|
507
504
|
// touching the project's own sources, so those files gate the reload
|
|
508
505
|
// too: the new hash in the emitted module is what tells the mount it
|
|
@@ -512,7 +509,7 @@ export function swiftPlugin(options: SwiftPluginOptions = {}): Plugin {
|
|
|
512
509
|
}
|
|
513
510
|
let artifact: SwiftArtifact
|
|
514
511
|
try {
|
|
515
|
-
artifact = await artifactFor(project)
|
|
512
|
+
artifact = await artifactFor(project, entry)
|
|
516
513
|
} catch (error) {
|
|
517
514
|
// a compile error must not fail this module. a module whose load threw
|
|
518
515
|
// leaves the graph unwatched, so the edit that fixes the error would
|
|
@@ -530,7 +527,9 @@ export function swiftPlugin(options: SwiftPluginOptions = {}): Plugin {
|
|
|
530
527
|
try {
|
|
531
528
|
const built = buildSwiftResourceTable(project, serverOrigin())
|
|
532
529
|
resources = built.table
|
|
533
|
-
for (const watched of built.watched)
|
|
530
|
+
for (const watched of built.watched) {
|
|
531
|
+
this.addWatchFile(path.join(packagePath, ...watched.split(path.posix.sep)))
|
|
532
|
+
}
|
|
534
533
|
} catch (error) {
|
|
535
534
|
this.warn(
|
|
536
535
|
`swift resources for ${packagePath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -560,11 +559,13 @@ export function swiftPlugin(options: SwiftPluginOptions = {}): Plugin {
|
|
|
560
559
|
// whether it is a view or an app root at runtime, so the transform
|
|
561
560
|
// never re-derives the conformance from source. the entry is rooted by
|
|
562
561
|
// its package and source path so a hot reload finds the same instance.
|
|
563
|
-
return
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
562
|
+
return swiftEntryModuleEsm({
|
|
563
|
+
url: artifactUrl(project, artifact),
|
|
564
|
+
hash: artifact.hash,
|
|
565
|
+
rootId: entryRootId(packagePath, file),
|
|
566
|
+
resources,
|
|
567
|
+
bundleFiles,
|
|
568
|
+
})
|
|
568
569
|
},
|
|
569
570
|
|
|
570
571
|
configureServer(server) {
|
package/src/vite-plugin.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { createRequire } from 'module'
|
|
|
13
13
|
import path from 'path'
|
|
14
14
|
import { fileURLToPath } from 'url'
|
|
15
15
|
import { type Plugin, transformWithOxc } from 'vite'
|
|
16
|
-
import { resolveExportsHiddenSubpath } from '../../compat/src/resolve-hidden-subpath.ts'
|
|
16
|
+
import { resolveExportsHiddenSubpath } from '../../peach-compat/src/resolve-hidden-subpath.ts'
|
|
17
17
|
import {
|
|
18
18
|
SOOTSIM_COMPAT_PUBLISHED_BROWSER_ENTRIES,
|
|
19
19
|
SOOTSIM_COMPAT_WRAPPER_REAL_PACKAGES,
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
SOOTSIM_HAPTIC_FEEDBACK_TOUCHABLE_SPECIFIER,
|
|
22
22
|
compatStubsForBuildResolver,
|
|
23
23
|
reactNativeDeepStubsForBuildResolver,
|
|
24
|
-
} from '../../compat/src/stub-manifest.ts'
|
|
24
|
+
} from '../../peach-compat/src/stub-manifest.ts'
|
|
25
25
|
import { resolveSootsimBridgePort } from './bridge-constants.ts'
|
|
26
26
|
import {
|
|
27
27
|
SHELL_BRIDGE_IDENTITY_PATH,
|
|
@@ -50,19 +50,32 @@ const sootsimRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '
|
|
|
50
50
|
const workspaceRoot = path.resolve(sootsimRoot, '..', '..')
|
|
51
51
|
const workspaceNodeModules = path.resolve(workspaceRoot, 'node_modules')
|
|
52
52
|
const workspaceTamaguiDir = path.resolve(workspaceNodeModules, '@tamagui')
|
|
53
|
-
// stubs and the mutable React bridge live in @
|
|
54
|
-
const compatRoot = path.resolve(sootsimRoot, '..', 'compat')
|
|
53
|
+
// stubs and the mutable React bridge live in @peach/compat.
|
|
54
|
+
const compatRoot = path.resolve(sootsimRoot, '..', 'peach-compat')
|
|
55
55
|
|
|
56
56
|
// the rendering engine and its shims live in the private sootsim-engine
|
|
57
57
|
// workspace package. resolve via workspace root rather than relative hops so
|
|
58
58
|
// renames of either package don't break things.
|
|
59
|
-
const engineRoot = path.resolve(workspaceRoot, 'packages/
|
|
59
|
+
const engineRoot = path.resolve(workspaceRoot, 'packages/peach-engine')
|
|
60
60
|
const rnShimPath = path.resolve(engineRoot, 'src/react-native/index.ts')
|
|
61
61
|
const reactBridgePath = path.resolve(
|
|
62
62
|
workspaceRoot,
|
|
63
63
|
'packages/contrast-kit/src/react-bridge.ts',
|
|
64
64
|
)
|
|
65
65
|
const sootsimBrowserSourceAliases = [
|
|
66
|
+
{
|
|
67
|
+
find: 'peachsim/backend-origin',
|
|
68
|
+
replacement: path.resolve(sootsimRoot, 'src/backend-origin.ts'),
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
find: 'peachsim/bridge-constants',
|
|
72
|
+
replacement: path.resolve(sootsimRoot, 'src/bridge-constants.ts'),
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
find: 'peachsim/dev-bundle-resolution',
|
|
76
|
+
replacement: path.resolve(sootsimRoot, 'src/dev-bundle-resolution.ts'),
|
|
77
|
+
},
|
|
78
|
+
// old specifiers for user code still on rnxsim; remove with packages/sootsim.
|
|
66
79
|
{
|
|
67
80
|
find: 'rnxsim/backend-origin',
|
|
68
81
|
replacement: path.resolve(sootsimRoot, 'src/backend-origin.ts'),
|
|
@@ -475,13 +488,13 @@ function rnghUpstreamInternalResolvePlugin(): Plugin {
|
|
|
475
488
|
export function recordingPluginWorkerRedirect(): Plugin {
|
|
476
489
|
const workerTarget = path.resolve(
|
|
477
490
|
workspaceRoot,
|
|
478
|
-
'packages/
|
|
491
|
+
'packages/peach-plugin-recording/src/worker.ts',
|
|
479
492
|
)
|
|
480
493
|
return {
|
|
481
494
|
name: 'sootsim-recording-plugin-worker-redirect',
|
|
482
495
|
enforce: 'pre',
|
|
483
496
|
resolveId(source) {
|
|
484
|
-
if (source === '@
|
|
497
|
+
if (source === '@peach/plugin-recording') {
|
|
485
498
|
return workerTarget
|
|
486
499
|
}
|
|
487
500
|
return null
|
|
@@ -542,7 +555,7 @@ const ENGINE_WORKER_BOOT_CLOSURES: WorkerBootClosure[] = [
|
|
|
542
555
|
// statically instead of require()ing the whole react-native namespace into
|
|
543
556
|
// the boot file.
|
|
544
557
|
{ entry: 'src/render-worker/shell-worker-entry.ts', maxBytes: 3_451_000 },
|
|
545
|
-
{ entry: 'src/render-worker/compositor-worker-entry.ts', maxBytes:
|
|
558
|
+
{ entry: 'src/render-worker/compositor-worker-entry.ts', maxBytes: 1_085_000 },
|
|
546
559
|
{ entry: 'src/render-worker/compositor-worker-ganesh-entry.ts', maxBytes: 1_820_000 },
|
|
547
560
|
]
|
|
548
561
|
|
|
@@ -670,7 +683,7 @@ export function keyboardControllerBindingsRedirect(): Plugin {
|
|
|
670
683
|
//
|
|
671
684
|
// per the project policy of not shimming pure-JS libs, we let upstream resolve
|
|
672
685
|
// from node_modules and redirect ONLY the native seam to our compat stub
|
|
673
|
-
// (one file at packages/compat/src/stubs/react-native-reanimated.ts that
|
|
686
|
+
// (one file at packages/peach-compat/src/stubs/react-native-reanimated.ts that
|
|
674
687
|
// exports the right shape — default = turbomodule, named = platform fns).
|
|
675
688
|
//
|
|
676
689
|
// upstream's `assertWorkletsVersion` (DEV-only) imports a build-time script
|
|
@@ -1274,11 +1287,11 @@ function reactBridgePlugin(): Plugin {
|
|
|
1274
1287
|
// skip during dep scanning / SSR — only active in browser serve mode
|
|
1275
1288
|
if ((options as { scan?: boolean })?.scan || options?.ssr) return null
|
|
1276
1289
|
|
|
1277
|
-
// only redirect imports from
|
|
1290
|
+
// only redirect imports from peachsim, peach-engine, peach-compat, and react-reconciler
|
|
1278
1291
|
const shouldRedirect =
|
|
1279
|
-
importer.includes('/
|
|
1280
|
-
importer.includes('/
|
|
1281
|
-
importer.includes('/compat/src/') ||
|
|
1292
|
+
importer.includes('/peachsim/src/') ||
|
|
1293
|
+
importer.includes('/peach-engine/src/') ||
|
|
1294
|
+
importer.includes('/peach-compat/src/') ||
|
|
1282
1295
|
importer.includes('/contrast-kit/src/') ||
|
|
1283
1296
|
importer.includes('react-reconciler')
|
|
1284
1297
|
|
|
@@ -1326,16 +1339,16 @@ export function workerReactBridgePlugin(): Plugin {
|
|
|
1326
1339
|
// stub-registry needs the real React (with __CLIENT_INTERNALS) for bundle stubs
|
|
1327
1340
|
if (importer.includes('stub-registry')) return null
|
|
1328
1341
|
|
|
1329
|
-
// redirect react imports from
|
|
1342
|
+
// redirect react imports from peachsim, peach-engine, peach-compat, react-reconciler,
|
|
1330
1343
|
// and any pure-JS RN-ecosystem package the bundle pulls in. without this
|
|
1331
1344
|
// redirect, packages like react-native-drawer-layout / use-latest-callback
|
|
1332
1345
|
// call into a separate React instance than the renderer/bridge does, so
|
|
1333
1346
|
// their useRef/useEffect refs collide with the engine's hooks state and
|
|
1334
1347
|
// we hit "Maximum update depth exceeded" infinite-loop renders.
|
|
1335
1348
|
const shouldRedirect =
|
|
1336
|
-
importer.includes('/
|
|
1337
|
-
importer.includes('/
|
|
1338
|
-
importer.includes('/compat/src/') ||
|
|
1349
|
+
importer.includes('/peachsim/src/') ||
|
|
1350
|
+
importer.includes('/peach-engine/src/') ||
|
|
1351
|
+
importer.includes('/peach-compat/src/') ||
|
|
1339
1352
|
importer.includes('/contrast-kit/src/') ||
|
|
1340
1353
|
importer.includes('react-reconciler') ||
|
|
1341
1354
|
importer.includes('/node_modules/')
|
package/src/worklets-babel.ts
CHANGED
|
@@ -41,8 +41,8 @@ export const REANIMATED_WORKLETIZATION_REGEX = new RegExp(
|
|
|
41
41
|
// content-gated so most of node_modules skips for free.
|
|
42
42
|
const REANIMATED_IGNORED_PATHS_REGEX =
|
|
43
43
|
/node_modules\/(react|react-dom|react-native|react-native-web)\//
|
|
44
|
-
const SOOTSIM_ENGINE_SRC_PATH = '/packages/
|
|
45
|
-
const SOOTSIM_ENGINE_TEST_FIXTURE_PATH = '/packages/
|
|
44
|
+
const SOOTSIM_ENGINE_SRC_PATH = '/packages/peach-engine/src/'
|
|
45
|
+
const SOOTSIM_ENGINE_TEST_FIXTURE_PATH = '/packages/peach-engine/src/test-fixtures/'
|
|
46
46
|
const REANIMATED_IMPORT_REGEX =
|
|
47
47
|
/(?:from\s+['"]react-native-reanimated(?:\/[^'"]*)?['"]|require\(\s*['"]react-native-reanimated(?:\/[^'"]*)?['"]\s*\))/
|
|
48
48
|
|