rnxsim 0.1.402 → 0.1.404
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 +31 -9
- package/cli/commands/box.ts +10 -3
- package/cli/outbound-endpoints.ts +2 -2
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.cjs +1 -1
- package/dist-lib/agent-sessions.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- 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 +5 -2
- package/dist-lib/capture-contract.mjs +5 -2
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/cloud-contract.cjs +3 -2
- package/dist-lib/cloud-contract.mjs +3 -2
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +1 -1
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +1 -1
- 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 +63 -14
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.mjs +1 -1
- package/dist-lib/metro-fingerprint-registry.cjs +186 -0
- package/dist-lib/metro-fingerprint-registry.mjs +156 -0
- package/dist-lib/metro-production-bundle.cjs +131 -17
- package/dist-lib/metro-production-bundle.mjs +125 -16
- package/dist-lib/metro.cjs +67 -12
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +1 -1
- 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 +1 -1
- package/dist-lib/sdk.cjs +1 -1
- package/dist-lib/sdk.mjs +1 -1
- package/dist-lib/skills.cjs +58 -25
- package/dist-lib/vite.cjs +6 -1
- package/package.json +8 -1
- package/src/bridge-contract.ts +20 -0
- package/src/capture-contract.ts +8 -0
- package/src/cloud-contract.ts +1 -0
- package/src/metro-fingerprint-registry.ts +302 -0
- package/src/metro-plugin.ts +73 -24
- package/src/metro-production-bundle.ts +213 -19
- package/src/runtime-assets.ts +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rnxsim",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.404",
|
|
4
4
|
"description": "Vite and Metro plugins, testing drivers, and SDK exports for rnx.",
|
|
5
5
|
"author": "Tamagui LLC",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -45,6 +45,13 @@
|
|
|
45
45
|
"import": "./dist-lib/metro-production-bundle.mjs",
|
|
46
46
|
"default": "./dist-lib/metro-production-bundle.cjs"
|
|
47
47
|
},
|
|
48
|
+
"./metro-fingerprint-registry": {
|
|
49
|
+
"types": "./src/metro-fingerprint-registry.ts",
|
|
50
|
+
"source": "./src/metro-fingerprint-registry.ts",
|
|
51
|
+
"bun": "./src/metro-fingerprint-registry.ts",
|
|
52
|
+
"import": "./dist-lib/metro-fingerprint-registry.mjs",
|
|
53
|
+
"default": "./dist-lib/metro-fingerprint-registry.cjs"
|
|
54
|
+
},
|
|
48
55
|
"./react-native-host-modules": {
|
|
49
56
|
"types": "./src/react-native-host-modules.ts",
|
|
50
57
|
"source": "./src/react-native-host-modules.ts",
|
package/src/bridge-contract.ts
CHANGED
|
@@ -37,6 +37,7 @@ export type BridgeCommandType =
|
|
|
37
37
|
| 'resolve'
|
|
38
38
|
| 'reset'
|
|
39
39
|
| 'camera'
|
|
40
|
+
| 'diagnostics'
|
|
40
41
|
|
|
41
42
|
export interface WsCommand {
|
|
42
43
|
id: string | number
|
|
@@ -86,6 +87,9 @@ export interface WsCommand {
|
|
|
86
87
|
resetOptions?: ResetOptions
|
|
87
88
|
// camera — fixture injection into the host camera pipeline
|
|
88
89
|
camera?: CameraCommand
|
|
90
|
+
// diagnostics — read-only engine debug state: channel recording, the sheet /
|
|
91
|
+
// portal / boundary finders, and the recent event ring.
|
|
92
|
+
diagnostics?: DiagnosticsOptions
|
|
89
93
|
}
|
|
90
94
|
|
|
91
95
|
// ---------------------------------------------------------------------------
|
|
@@ -258,6 +262,22 @@ export interface SemanticWaitResult {
|
|
|
258
262
|
// state — one round-trip situational awareness
|
|
259
263
|
// ---------------------------------------------------------------------------
|
|
260
264
|
|
|
265
|
+
export interface DiagnosticsOptions {
|
|
266
|
+
// debug channels to record from this point on. omitted leaves recording as
|
|
267
|
+
// it is; an empty list stops recording.
|
|
268
|
+
channels?: readonly string[]
|
|
269
|
+
find?: 'sheets' | 'portals' | 'boundaries'
|
|
270
|
+
recentChannel?: string
|
|
271
|
+
recentLimit?: number
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export interface DiagnosticsResult {
|
|
275
|
+
channels: string[]
|
|
276
|
+
runtime: unknown
|
|
277
|
+
found: unknown[] | null
|
|
278
|
+
events: unknown[]
|
|
279
|
+
}
|
|
280
|
+
|
|
261
281
|
export interface SimStateOptions {
|
|
262
282
|
// omit a section an agent does not need this turn
|
|
263
283
|
screenshot?: boolean
|
package/src/capture-contract.ts
CHANGED
|
@@ -162,6 +162,7 @@ export interface RnxRasterRegionTree {
|
|
|
162
162
|
nodeId: number
|
|
163
163
|
parentNodeId: number | null
|
|
164
164
|
type: string
|
|
165
|
+
text?: string | null
|
|
165
166
|
style: Record<string, unknown>
|
|
166
167
|
layout: RnxCaptureRect
|
|
167
168
|
frame: RnxCaptureRect | null
|
|
@@ -178,6 +179,12 @@ export function selectRnxRasterNodeIds(tree: RnxRasterRegionTree): Set<number> {
|
|
|
178
179
|
if (!visibleFrame || visibleFrame.width <= 0 || visibleFrame.height <= 0) continue
|
|
179
180
|
if (
|
|
180
181
|
node.type === 'masked-view' ||
|
|
182
|
+
(node.type === 'text' &&
|
|
183
|
+
typeof node.text === 'string' &&
|
|
184
|
+
(/\p{Emoji_Presentation}/u.test(node.text) ||
|
|
185
|
+
/\uFE0F|\u200D|\u20E3|\p{Emoji_Modifier}|\p{Regional_Indicator}/u.test(
|
|
186
|
+
node.text,
|
|
187
|
+
))) ||
|
|
181
188
|
node.style._liquidGlass !== undefined ||
|
|
182
189
|
node.style._liquidGlassEdge !== undefined
|
|
183
190
|
) {
|
|
@@ -293,6 +300,7 @@ export function rnxCaptureRasterRegions(
|
|
|
293
300
|
nodeId: node.nodeId,
|
|
294
301
|
parentNodeId: node.parentNodeId,
|
|
295
302
|
type: node.type,
|
|
303
|
+
text: node.text,
|
|
296
304
|
style: node.style.resolved,
|
|
297
305
|
layout: node.layout.local,
|
|
298
306
|
frame: node.layout.frame,
|
package/src/cloud-contract.ts
CHANGED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
export const RNX_METRO_FINGERPRINT_SCHEMA_VERSION = 2
|
|
2
|
+
export const RNX_METRO_FINGERPRINT_POINTER_PATH = '/compile/metro-fingerprints.json'
|
|
3
|
+
export const RNX_METRO_FINGERPRINT_MAX_POINTER_BYTES = 16 * 1024
|
|
4
|
+
export const RNX_METRO_FINGERPRINT_MAX_PAYLOAD_BYTES = 8 * 1024 * 1024
|
|
5
|
+
|
|
6
|
+
export type RNXMetroFingerprintPlatform = 'ios' | 'android'
|
|
7
|
+
|
|
8
|
+
export interface RNXMetroFingerprintAtom {
|
|
9
|
+
key: string
|
|
10
|
+
exports: string[]
|
|
11
|
+
literals: string[]
|
|
12
|
+
dependencyArity: number
|
|
13
|
+
factoryDigest: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface RNXMetroFingerprintNode {
|
|
17
|
+
atom: number
|
|
18
|
+
internalDependencies: Array<{ position: number; node: number }>
|
|
19
|
+
landmarkPath?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface RNXMetroFingerprintRoot {
|
|
23
|
+
node: number
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface RNXMetroFingerprintTemplate {
|
|
27
|
+
packageName: string
|
|
28
|
+
packageVersion: string
|
|
29
|
+
sourceIntegrity: string
|
|
30
|
+
profile: string
|
|
31
|
+
platform: RNXMetroFingerprintPlatform
|
|
32
|
+
nodes: RNXMetroFingerprintNode[]
|
|
33
|
+
roots: RNXMetroFingerprintRoot[]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface RNXMetroFingerprintCandidate {
|
|
37
|
+
template: number
|
|
38
|
+
root: number
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface RNXMetroFingerprintRegistry {
|
|
42
|
+
schemaVersion: typeof RNX_METRO_FINGERPRINT_SCHEMA_VERSION
|
|
43
|
+
revision: string
|
|
44
|
+
atoms: RNXMetroFingerprintAtom[]
|
|
45
|
+
templates: RNXMetroFingerprintTemplate[]
|
|
46
|
+
candidatesByFactoryDigest: Record<string, RNXMetroFingerprintCandidate[]>
|
|
47
|
+
candidatesByDiscriminator: Record<string, RNXMetroFingerprintCandidate[]>
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface RNXMetroFingerprintPointer {
|
|
51
|
+
schemaVersion: typeof RNX_METRO_FINGERPRINT_SCHEMA_VERSION
|
|
52
|
+
revision: string
|
|
53
|
+
integrity: string
|
|
54
|
+
bytes: number
|
|
55
|
+
payloadUrl: string
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
59
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function isSha256(value: unknown): value is string {
|
|
63
|
+
return typeof value === 'string' && /^[a-f0-9]{64}$/.test(value)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function readStringArray(value: unknown): string[] | null {
|
|
67
|
+
if (!Array.isArray(value) || !value.every((entry) => typeof entry === 'string')) {
|
|
68
|
+
return null
|
|
69
|
+
}
|
|
70
|
+
const entries = [...value]
|
|
71
|
+
if (
|
|
72
|
+
new Set(entries).size !== entries.length ||
|
|
73
|
+
entries.some((entry, index) => index > 0 && entries[index - 1] >= entry)
|
|
74
|
+
) {
|
|
75
|
+
return null
|
|
76
|
+
}
|
|
77
|
+
return entries
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function parseRNXMetroFingerprintPointer(
|
|
81
|
+
value: unknown,
|
|
82
|
+
): RNXMetroFingerprintPointer | null {
|
|
83
|
+
if (!isRecord(value)) return null
|
|
84
|
+
const { schemaVersion, revision, integrity, bytes, payloadUrl } = value
|
|
85
|
+
if (
|
|
86
|
+
schemaVersion !== RNX_METRO_FINGERPRINT_SCHEMA_VERSION ||
|
|
87
|
+
!isSha256(revision) ||
|
|
88
|
+
!isSha256(integrity) ||
|
|
89
|
+
typeof bytes !== 'number' ||
|
|
90
|
+
!Number.isSafeInteger(bytes) ||
|
|
91
|
+
bytes < 1 ||
|
|
92
|
+
typeof payloadUrl !== 'string' ||
|
|
93
|
+
payloadUrl.length === 0
|
|
94
|
+
) {
|
|
95
|
+
return null
|
|
96
|
+
}
|
|
97
|
+
return { schemaVersion, revision, integrity, bytes, payloadUrl }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function parseRNXMetroFingerprintRegistry(
|
|
101
|
+
value: unknown,
|
|
102
|
+
): RNXMetroFingerprintRegistry | null {
|
|
103
|
+
if (!isRecord(value)) return null
|
|
104
|
+
const { schemaVersion, revision } = value
|
|
105
|
+
if (
|
|
106
|
+
schemaVersion !== RNX_METRO_FINGERPRINT_SCHEMA_VERSION ||
|
|
107
|
+
!isSha256(revision) ||
|
|
108
|
+
!Array.isArray(value.atoms) ||
|
|
109
|
+
!Array.isArray(value.templates) ||
|
|
110
|
+
!isRecord(value.candidatesByFactoryDigest) ||
|
|
111
|
+
!isRecord(value.candidatesByDiscriminator)
|
|
112
|
+
) {
|
|
113
|
+
return null
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const atoms: RNXMetroFingerprintAtom[] = []
|
|
117
|
+
for (const atomValue of value.atoms) {
|
|
118
|
+
if (!isRecord(atomValue)) return null
|
|
119
|
+
const exports = readStringArray(atomValue.exports)
|
|
120
|
+
const literals = readStringArray(atomValue.literals)
|
|
121
|
+
if (
|
|
122
|
+
typeof atomValue.key !== 'string' ||
|
|
123
|
+
atomValue.key.length === 0 ||
|
|
124
|
+
!exports ||
|
|
125
|
+
!literals ||
|
|
126
|
+
typeof atomValue.dependencyArity !== 'number' ||
|
|
127
|
+
!Number.isSafeInteger(atomValue.dependencyArity) ||
|
|
128
|
+
atomValue.dependencyArity < 0 ||
|
|
129
|
+
!isSha256(atomValue.factoryDigest)
|
|
130
|
+
) {
|
|
131
|
+
return null
|
|
132
|
+
}
|
|
133
|
+
atoms.push({
|
|
134
|
+
key: atomValue.key,
|
|
135
|
+
exports,
|
|
136
|
+
literals,
|
|
137
|
+
dependencyArity: atomValue.dependencyArity,
|
|
138
|
+
factoryDigest: atomValue.factoryDigest,
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const templates: RNXMetroFingerprintTemplate[] = []
|
|
143
|
+
for (const templateValue of value.templates) {
|
|
144
|
+
if (
|
|
145
|
+
!isRecord(templateValue) ||
|
|
146
|
+
typeof templateValue.packageName !== 'string' ||
|
|
147
|
+
templateValue.packageName.length === 0 ||
|
|
148
|
+
typeof templateValue.packageVersion !== 'string' ||
|
|
149
|
+
templateValue.packageVersion.length === 0 ||
|
|
150
|
+
!isSha256(templateValue.sourceIntegrity) ||
|
|
151
|
+
typeof templateValue.profile !== 'string' ||
|
|
152
|
+
templateValue.profile.length === 0 ||
|
|
153
|
+
(templateValue.platform !== 'ios' && templateValue.platform !== 'android') ||
|
|
154
|
+
!Array.isArray(templateValue.nodes) ||
|
|
155
|
+
templateValue.nodes.length === 0 ||
|
|
156
|
+
!Array.isArray(templateValue.roots) ||
|
|
157
|
+
templateValue.roots.length === 0
|
|
158
|
+
) {
|
|
159
|
+
return null
|
|
160
|
+
}
|
|
161
|
+
const nodes: RNXMetroFingerprintNode[] = []
|
|
162
|
+
for (const nodeValue of templateValue.nodes) {
|
|
163
|
+
if (
|
|
164
|
+
!isRecord(nodeValue) ||
|
|
165
|
+
typeof nodeValue.atom !== 'number' ||
|
|
166
|
+
!Number.isSafeInteger(nodeValue.atom) ||
|
|
167
|
+
nodeValue.atom < 0 ||
|
|
168
|
+
nodeValue.atom >= atoms.length ||
|
|
169
|
+
!Array.isArray(nodeValue.internalDependencies) ||
|
|
170
|
+
(nodeValue.landmarkPath !== undefined &&
|
|
171
|
+
(typeof nodeValue.landmarkPath !== 'string' ||
|
|
172
|
+
nodeValue.landmarkPath.length === 0))
|
|
173
|
+
) {
|
|
174
|
+
return null
|
|
175
|
+
}
|
|
176
|
+
const internalDependencies: Array<{ position: number; node: number }> = []
|
|
177
|
+
const positions = new Set<number>()
|
|
178
|
+
for (const edgeValue of nodeValue.internalDependencies) {
|
|
179
|
+
if (
|
|
180
|
+
!isRecord(edgeValue) ||
|
|
181
|
+
typeof edgeValue.position !== 'number' ||
|
|
182
|
+
!Number.isSafeInteger(edgeValue.position) ||
|
|
183
|
+
edgeValue.position < 0 ||
|
|
184
|
+
positions.has(edgeValue.position) ||
|
|
185
|
+
typeof edgeValue.node !== 'number' ||
|
|
186
|
+
!Number.isSafeInteger(edgeValue.node) ||
|
|
187
|
+
edgeValue.node < 0 ||
|
|
188
|
+
edgeValue.node >= templateValue.nodes.length
|
|
189
|
+
) {
|
|
190
|
+
return null
|
|
191
|
+
}
|
|
192
|
+
positions.add(edgeValue.position)
|
|
193
|
+
internalDependencies.push({
|
|
194
|
+
position: edgeValue.position,
|
|
195
|
+
node: edgeValue.node,
|
|
196
|
+
})
|
|
197
|
+
}
|
|
198
|
+
internalDependencies.sort((left, right) => left.position - right.position)
|
|
199
|
+
nodes.push({
|
|
200
|
+
atom: nodeValue.atom,
|
|
201
|
+
internalDependencies,
|
|
202
|
+
...(typeof nodeValue.landmarkPath === 'string'
|
|
203
|
+
? { landmarkPath: nodeValue.landmarkPath }
|
|
204
|
+
: {}),
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
const roots: RNXMetroFingerprintRoot[] = []
|
|
208
|
+
for (const rootValue of templateValue.roots) {
|
|
209
|
+
if (
|
|
210
|
+
!isRecord(rootValue) ||
|
|
211
|
+
typeof rootValue.node !== 'number' ||
|
|
212
|
+
!Number.isSafeInteger(rootValue.node) ||
|
|
213
|
+
rootValue.node < 0 ||
|
|
214
|
+
rootValue.node >= nodes.length ||
|
|
215
|
+
!nodes[rootValue.node].landmarkPath
|
|
216
|
+
) {
|
|
217
|
+
return null
|
|
218
|
+
}
|
|
219
|
+
roots.push({ node: rootValue.node })
|
|
220
|
+
}
|
|
221
|
+
templates.push({
|
|
222
|
+
packageName: templateValue.packageName,
|
|
223
|
+
packageVersion: templateValue.packageVersion,
|
|
224
|
+
sourceIntegrity: templateValue.sourceIntegrity,
|
|
225
|
+
profile: templateValue.profile,
|
|
226
|
+
platform: templateValue.platform,
|
|
227
|
+
nodes,
|
|
228
|
+
roots,
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const candidatesByFactoryDigest: Record<string, RNXMetroFingerprintCandidate[]> = {}
|
|
233
|
+
for (const [factoryDigest, candidateValues] of Object.entries(
|
|
234
|
+
value.candidatesByFactoryDigest,
|
|
235
|
+
)) {
|
|
236
|
+
if (!isSha256(factoryDigest) || !Array.isArray(candidateValues)) return null
|
|
237
|
+
const candidates: RNXMetroFingerprintCandidate[] = []
|
|
238
|
+
for (const candidateValue of candidateValues) {
|
|
239
|
+
if (
|
|
240
|
+
!isRecord(candidateValue) ||
|
|
241
|
+
typeof candidateValue.template !== 'number' ||
|
|
242
|
+
!Number.isSafeInteger(candidateValue.template) ||
|
|
243
|
+
candidateValue.template < 0 ||
|
|
244
|
+
candidateValue.template >= templates.length ||
|
|
245
|
+
typeof candidateValue.root !== 'number' ||
|
|
246
|
+
!Number.isSafeInteger(candidateValue.root) ||
|
|
247
|
+
candidateValue.root < 0 ||
|
|
248
|
+
candidateValue.root >= templates[candidateValue.template].roots.length
|
|
249
|
+
) {
|
|
250
|
+
return null
|
|
251
|
+
}
|
|
252
|
+
const template = templates[candidateValue.template]
|
|
253
|
+
const root = template.roots[candidateValue.root]
|
|
254
|
+
if (atoms[template.nodes[root.node].atom].factoryDigest !== factoryDigest) {
|
|
255
|
+
return null
|
|
256
|
+
}
|
|
257
|
+
candidates.push({
|
|
258
|
+
template: candidateValue.template,
|
|
259
|
+
root: candidateValue.root,
|
|
260
|
+
})
|
|
261
|
+
}
|
|
262
|
+
candidatesByFactoryDigest[factoryDigest] = candidates
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const candidatesByDiscriminator: Record<string, RNXMetroFingerprintCandidate[]> = {}
|
|
266
|
+
for (const [discriminator, candidateValues] of Object.entries(
|
|
267
|
+
value.candidatesByDiscriminator,
|
|
268
|
+
)) {
|
|
269
|
+
if (!/^(?:e|l|ea|la|el|ela):[a-f0-9]{16}$/.test(discriminator)) return null
|
|
270
|
+
if (!Array.isArray(candidateValues) || candidateValues.length === 0) return null
|
|
271
|
+
const candidates: RNXMetroFingerprintCandidate[] = []
|
|
272
|
+
for (const candidateValue of candidateValues) {
|
|
273
|
+
if (
|
|
274
|
+
!isRecord(candidateValue) ||
|
|
275
|
+
typeof candidateValue.template !== 'number' ||
|
|
276
|
+
!Number.isSafeInteger(candidateValue.template) ||
|
|
277
|
+
candidateValue.template < 0 ||
|
|
278
|
+
candidateValue.template >= templates.length ||
|
|
279
|
+
typeof candidateValue.root !== 'number' ||
|
|
280
|
+
!Number.isSafeInteger(candidateValue.root) ||
|
|
281
|
+
candidateValue.root < 0 ||
|
|
282
|
+
candidateValue.root >= templates[candidateValue.template].roots.length
|
|
283
|
+
) {
|
|
284
|
+
return null
|
|
285
|
+
}
|
|
286
|
+
candidates.push({
|
|
287
|
+
template: candidateValue.template,
|
|
288
|
+
root: candidateValue.root,
|
|
289
|
+
})
|
|
290
|
+
}
|
|
291
|
+
candidatesByDiscriminator[discriminator] = candidates
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return {
|
|
295
|
+
schemaVersion,
|
|
296
|
+
revision,
|
|
297
|
+
atoms,
|
|
298
|
+
templates,
|
|
299
|
+
candidatesByFactoryDigest,
|
|
300
|
+
candidatesByDiscriminator,
|
|
301
|
+
}
|
|
302
|
+
}
|
package/src/metro-plugin.ts
CHANGED
|
@@ -24,8 +24,11 @@ import {
|
|
|
24
24
|
isReplacementModuleRequestUrl,
|
|
25
25
|
} from './host/replacement-module-handler'
|
|
26
26
|
import {
|
|
27
|
-
|
|
27
|
+
appendRNXMetroModuleIdentityFooter,
|
|
28
|
+
RNX_METRO_MODULE_IDENTITY_VERSION,
|
|
29
|
+
toRNXDevelopmentBundleUrl,
|
|
28
30
|
toRNXProductionBundleUrl,
|
|
31
|
+
type RNXMetroModuleIdentityMetadata,
|
|
29
32
|
} from './metro-production-bundle'
|
|
30
33
|
import {
|
|
31
34
|
isRootRuntimeAssetPath,
|
|
@@ -36,13 +39,21 @@ import {
|
|
|
36
39
|
} from './runtime-assets'
|
|
37
40
|
|
|
38
41
|
const prefix = '/__soot'
|
|
39
|
-
export { toRNXProductionBundleUrl }
|
|
42
|
+
export { toRNXDevelopmentBundleUrl, toRNXProductionBundleUrl }
|
|
43
|
+
|
|
44
|
+
type MetroSerializerModule = {
|
|
45
|
+
path: string
|
|
46
|
+
dependencies?: ReadonlyMap<
|
|
47
|
+
string,
|
|
48
|
+
{
|
|
49
|
+
absolutePath?: string
|
|
50
|
+
data?: { name?: string }
|
|
51
|
+
}
|
|
52
|
+
>
|
|
53
|
+
}
|
|
40
54
|
|
|
41
55
|
type MetroSerializerGraph = {
|
|
42
|
-
dependencies?: ReadonlyMap<unknown,
|
|
43
|
-
transformOptions?: {
|
|
44
|
-
dev?: boolean
|
|
45
|
-
}
|
|
56
|
+
dependencies?: ReadonlyMap<unknown, MetroSerializerModule>
|
|
46
57
|
}
|
|
47
58
|
|
|
48
59
|
type MetroSerializerOptions = {
|
|
@@ -70,6 +81,12 @@ export interface RNXMetroOptions {
|
|
|
70
81
|
* bundle the app ships and rnx can load it directly.
|
|
71
82
|
*/
|
|
72
83
|
productionBundles?: boolean
|
|
84
|
+
/**
|
|
85
|
+
* append the `moduleId -> sourcePath` identity footer to every development
|
|
86
|
+
* bundle. resolution and transforms stay ordinary, while Metro keeps verbose
|
|
87
|
+
* module names and its source map.
|
|
88
|
+
*/
|
|
89
|
+
developmentBundles?: boolean
|
|
73
90
|
}
|
|
74
91
|
|
|
75
92
|
// the slice of metro's config we touch — keeps `T` open for whatever
|
|
@@ -130,11 +147,10 @@ function createMetroDefaultSerializer(projectRoot: string): MetroSerializer {
|
|
|
130
147
|
}
|
|
131
148
|
}
|
|
132
149
|
|
|
133
|
-
// the only thing rnx adds to
|
|
134
|
-
// argument when `dev` is false
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
// is inert everywhere else — on a device it assigns one unused global.
|
|
150
|
+
// the only thing rnx adds to an ordinary Metro bundle. production needs the
|
|
151
|
+
// footer because Metro drops the fourth `__d` argument when `dev` is false;
|
|
152
|
+
// development keeps that verbose name and receives the same identity without
|
|
153
|
+
// changing resolution, transforms, or the trailing source map.
|
|
138
154
|
function appendRNXModulePaths(
|
|
139
155
|
result: MetroSerializerResult,
|
|
140
156
|
graph: MetroSerializerGraph,
|
|
@@ -144,9 +160,7 @@ function appendRNXModulePaths(
|
|
|
144
160
|
const dependencies = graph.dependencies
|
|
145
161
|
const createModuleId = options.createModuleId
|
|
146
162
|
if (!dependencies || !createModuleId) {
|
|
147
|
-
throw new Error(
|
|
148
|
-
'rnxsim: Metro did not provide the production module graph and createModuleId',
|
|
149
|
-
)
|
|
163
|
+
throw new Error('rnxsim: Metro did not provide the module graph and createModuleId')
|
|
150
164
|
}
|
|
151
165
|
|
|
152
166
|
const modulePaths: Record<string, string> = {}
|
|
@@ -156,20 +170,56 @@ function appendRNXModulePaths(
|
|
|
156
170
|
.replaceAll(path.sep, '/')
|
|
157
171
|
}
|
|
158
172
|
if (Object.keys(modulePaths).length !== dependencies.size) {
|
|
159
|
-
throw new Error('rnxsim:
|
|
173
|
+
throw new Error('rnxsim: bundle module IDs were not unique')
|
|
160
174
|
}
|
|
161
175
|
|
|
162
|
-
const
|
|
176
|
+
const specifiersByModuleId = new Map<string, Set<string>>()
|
|
177
|
+
for (const module of dependencies.values()) {
|
|
178
|
+
for (const [dependencyKey, dependency] of module.dependencies ?? []) {
|
|
179
|
+
const specifier = dependency.data?.name ?? dependencyKey
|
|
180
|
+
if (
|
|
181
|
+
specifier.length === 0 ||
|
|
182
|
+
specifier.startsWith('.') ||
|
|
183
|
+
specifier.startsWith('/') ||
|
|
184
|
+
typeof dependency.absolutePath !== 'string'
|
|
185
|
+
) {
|
|
186
|
+
continue
|
|
187
|
+
}
|
|
188
|
+
const moduleId = String(createModuleId(dependency.absolutePath))
|
|
189
|
+
if (!Object.prototype.hasOwnProperty.call(modulePaths, moduleId)) continue
|
|
190
|
+
const found = specifiersByModuleId.get(moduleId)
|
|
191
|
+
if (found) found.add(specifier)
|
|
192
|
+
else specifiersByModuleId.set(moduleId, new Set([specifier]))
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const logicalSpecifiers = Object.fromEntries(
|
|
196
|
+
[...specifiersByModuleId]
|
|
197
|
+
.sort(([left], [right]) => Number(left) - Number(right))
|
|
198
|
+
.map(([moduleId, specifiers]) => [moduleId, [...specifiers].sort()]),
|
|
199
|
+
)
|
|
200
|
+
const identity: RNXMetroModuleIdentityMetadata = {
|
|
201
|
+
version: RNX_METRO_MODULE_IDENTITY_VERSION,
|
|
202
|
+
identitySource: 'rnx-plugin',
|
|
203
|
+
modulePaths,
|
|
204
|
+
logicalSpecifiers,
|
|
205
|
+
}
|
|
163
206
|
return typeof result === 'string'
|
|
164
|
-
? result
|
|
165
|
-
: { ...result, code: result.code
|
|
207
|
+
? appendRNXMetroModuleIdentityFooter(result, identity)
|
|
208
|
+
: { ...result, code: appendRNXMetroModuleIdentityFooter(result.code, identity) }
|
|
166
209
|
}
|
|
167
210
|
|
|
168
211
|
export function withRNX<T extends Record<string, unknown>>(
|
|
169
212
|
config: T,
|
|
170
213
|
options: RNXMetroOptions = {},
|
|
171
214
|
): T {
|
|
172
|
-
if (
|
|
215
|
+
if (
|
|
216
|
+
!options.devServer &&
|
|
217
|
+
!options.open &&
|
|
218
|
+
!options.productionBundles &&
|
|
219
|
+
!options.developmentBundles
|
|
220
|
+
) {
|
|
221
|
+
return config
|
|
222
|
+
}
|
|
173
223
|
|
|
174
224
|
const cfgProjectRoot = (config as MetroConfigWithServer).projectRoot || process.cwd()
|
|
175
225
|
const appName = resolveRnxDesktopAppName(options.open, cfgProjectRoot)
|
|
@@ -191,7 +241,7 @@ export function withRNX<T extends Record<string, unknown>>(
|
|
|
191
241
|
const existingSerializer = cfgSerializer?.customSerializer
|
|
192
242
|
let productionConfig: Partial<MetroConfigWithServer> = {}
|
|
193
243
|
|
|
194
|
-
if (options.productionBundles) {
|
|
244
|
+
if (options.productionBundles || options.developmentBundles) {
|
|
195
245
|
let defaultSerializer: MetroSerializer | undefined
|
|
196
246
|
|
|
197
247
|
productionConfig = {
|
|
@@ -212,10 +262,9 @@ export function withRNX<T extends Record<string, unknown>>(
|
|
|
212
262
|
graph,
|
|
213
263
|
serializerOptions,
|
|
214
264
|
)
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
return graph.transformOptions?.dev === false
|
|
265
|
+
const dev = serializerOptions.dev
|
|
266
|
+
return (dev === false && options.productionBundles === true) ||
|
|
267
|
+
(dev === true && options.developmentBundles === true)
|
|
219
268
|
? appendRNXModulePaths(result, graph, serializerOptions, cfgProjectRoot)
|
|
220
269
|
: result
|
|
221
270
|
},
|