rnxsim 0.1.402 → 0.1.403
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 +16 -7
- 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 +37 -12
- 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-production-bundle.cjs +72 -7
- package/dist-lib/metro-production-bundle.mjs +69 -7
- package/dist-lib/metro.cjs +41 -10
- 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 +3 -2
- package/dist-lib/vite.cjs +6 -1
- package/package.json +1 -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-plugin.ts +29 -23
- package/src/metro-production-bundle.ts +124 -8
- package/src/runtime-assets.ts +5 -0
package/src/metro-plugin.ts
CHANGED
|
@@ -24,7 +24,8 @@ import {
|
|
|
24
24
|
isReplacementModuleRequestUrl,
|
|
25
25
|
} from './host/replacement-module-handler'
|
|
26
26
|
import {
|
|
27
|
-
|
|
27
|
+
appendRNXMetroModulePathsFooter,
|
|
28
|
+
toRNXDevelopmentBundleUrl,
|
|
28
29
|
toRNXProductionBundleUrl,
|
|
29
30
|
} from './metro-production-bundle'
|
|
30
31
|
import {
|
|
@@ -36,13 +37,10 @@ import {
|
|
|
36
37
|
} from './runtime-assets'
|
|
37
38
|
|
|
38
39
|
const prefix = '/__soot'
|
|
39
|
-
export { toRNXProductionBundleUrl }
|
|
40
|
+
export { toRNXDevelopmentBundleUrl, toRNXProductionBundleUrl }
|
|
40
41
|
|
|
41
42
|
type MetroSerializerGraph = {
|
|
42
43
|
dependencies?: ReadonlyMap<unknown, { path: string }>
|
|
43
|
-
transformOptions?: {
|
|
44
|
-
dev?: boolean
|
|
45
|
-
}
|
|
46
44
|
}
|
|
47
45
|
|
|
48
46
|
type MetroSerializerOptions = {
|
|
@@ -70,6 +68,12 @@ export interface RNXMetroOptions {
|
|
|
70
68
|
* bundle the app ships and rnx can load it directly.
|
|
71
69
|
*/
|
|
72
70
|
productionBundles?: boolean
|
|
71
|
+
/**
|
|
72
|
+
* append the `moduleId -> sourcePath` identity footer to every development
|
|
73
|
+
* bundle. resolution and transforms stay ordinary, while Metro keeps verbose
|
|
74
|
+
* module names and its source map.
|
|
75
|
+
*/
|
|
76
|
+
developmentBundles?: boolean
|
|
73
77
|
}
|
|
74
78
|
|
|
75
79
|
// the slice of metro's config we touch — keeps `T` open for whatever
|
|
@@ -130,11 +134,10 @@ function createMetroDefaultSerializer(projectRoot: string): MetroSerializer {
|
|
|
130
134
|
}
|
|
131
135
|
}
|
|
132
136
|
|
|
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.
|
|
137
|
+
// the only thing rnx adds to an ordinary Metro bundle. production needs the
|
|
138
|
+
// footer because Metro drops the fourth `__d` argument when `dev` is false;
|
|
139
|
+
// development keeps that verbose name and receives the same identity without
|
|
140
|
+
// changing resolution, transforms, or the trailing source map.
|
|
138
141
|
function appendRNXModulePaths(
|
|
139
142
|
result: MetroSerializerResult,
|
|
140
143
|
graph: MetroSerializerGraph,
|
|
@@ -144,9 +147,7 @@ function appendRNXModulePaths(
|
|
|
144
147
|
const dependencies = graph.dependencies
|
|
145
148
|
const createModuleId = options.createModuleId
|
|
146
149
|
if (!dependencies || !createModuleId) {
|
|
147
|
-
throw new Error(
|
|
148
|
-
'rnxsim: Metro did not provide the production module graph and createModuleId',
|
|
149
|
-
)
|
|
150
|
+
throw new Error('rnxsim: Metro did not provide the module graph and createModuleId')
|
|
150
151
|
}
|
|
151
152
|
|
|
152
153
|
const modulePaths: Record<string, string> = {}
|
|
@@ -156,20 +157,26 @@ function appendRNXModulePaths(
|
|
|
156
157
|
.replaceAll(path.sep, '/')
|
|
157
158
|
}
|
|
158
159
|
if (Object.keys(modulePaths).length !== dependencies.size) {
|
|
159
|
-
throw new Error('rnxsim:
|
|
160
|
+
throw new Error('rnxsim: bundle module IDs were not unique')
|
|
160
161
|
}
|
|
161
162
|
|
|
162
|
-
const footer = createRNXMetroModulePathsFooter(modulePaths)
|
|
163
163
|
return typeof result === 'string'
|
|
164
|
-
? result
|
|
165
|
-
: { ...result, code: result.code
|
|
164
|
+
? appendRNXMetroModulePathsFooter(result, modulePaths)
|
|
165
|
+
: { ...result, code: appendRNXMetroModulePathsFooter(result.code, modulePaths) }
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
export function withRNX<T extends Record<string, unknown>>(
|
|
169
169
|
config: T,
|
|
170
170
|
options: RNXMetroOptions = {},
|
|
171
171
|
): T {
|
|
172
|
-
if (
|
|
172
|
+
if (
|
|
173
|
+
!options.devServer &&
|
|
174
|
+
!options.open &&
|
|
175
|
+
!options.productionBundles &&
|
|
176
|
+
!options.developmentBundles
|
|
177
|
+
) {
|
|
178
|
+
return config
|
|
179
|
+
}
|
|
173
180
|
|
|
174
181
|
const cfgProjectRoot = (config as MetroConfigWithServer).projectRoot || process.cwd()
|
|
175
182
|
const appName = resolveRnxDesktopAppName(options.open, cfgProjectRoot)
|
|
@@ -191,7 +198,7 @@ export function withRNX<T extends Record<string, unknown>>(
|
|
|
191
198
|
const existingSerializer = cfgSerializer?.customSerializer
|
|
192
199
|
let productionConfig: Partial<MetroConfigWithServer> = {}
|
|
193
200
|
|
|
194
|
-
if (options.productionBundles) {
|
|
201
|
+
if (options.productionBundles || options.developmentBundles) {
|
|
195
202
|
let defaultSerializer: MetroSerializer | undefined
|
|
196
203
|
|
|
197
204
|
productionConfig = {
|
|
@@ -212,10 +219,9 @@ export function withRNX<T extends Record<string, unknown>>(
|
|
|
212
219
|
graph,
|
|
213
220
|
serializerOptions,
|
|
214
221
|
)
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
return graph.transformOptions?.dev === false
|
|
222
|
+
const dev = serializerOptions.dev
|
|
223
|
+
return (dev === false && options.productionBundles === true) ||
|
|
224
|
+
(dev === true && options.developmentBundles === true)
|
|
219
225
|
? appendRNXModulePaths(result, graph, serializerOptions, cfgProjectRoot)
|
|
220
226
|
: result
|
|
221
227
|
},
|
|
@@ -4,6 +4,11 @@ export type RNXMetroModulePathMap = Record<string, string>
|
|
|
4
4
|
export interface RNXMetroModuleIdentity {
|
|
5
5
|
source: string
|
|
6
6
|
modulePaths: RNXMetroModulePathMap
|
|
7
|
+
/**
|
|
8
|
+
* Metro's trailing `//# sourceMappingURL=` comment when the graph carried
|
|
9
|
+
* one, already split off `source`. A production graph has none.
|
|
10
|
+
*/
|
|
11
|
+
sourceMappingUrl: string | null
|
|
7
12
|
}
|
|
8
13
|
|
|
9
14
|
// the production bundle rnx loads is the app's ORDINARY production bundle.
|
|
@@ -19,6 +24,49 @@ export function toRNXProductionBundleUrl(bundleUrl: string): string {
|
|
|
19
24
|
return relative ? `${url.pathname}${url.search}${url.hash}` : url.toString()
|
|
20
25
|
}
|
|
21
26
|
|
|
27
|
+
/**
|
|
28
|
+
* the ordinary development counterpart. Metro keeps `__DEV__` true, verbose
|
|
29
|
+
* module names, and its source map. `hot` stays false because the artifact is
|
|
30
|
+
* immutable once produced.
|
|
31
|
+
*/
|
|
32
|
+
export function toRNXDevelopmentBundleUrl(bundleUrl: string): string {
|
|
33
|
+
const relative = bundleUrl.startsWith('/')
|
|
34
|
+
const url = new URL(bundleUrl, 'http://rnxsim.local')
|
|
35
|
+
url.searchParams.set('dev', 'true')
|
|
36
|
+
url.searchParams.set('hot', 'false')
|
|
37
|
+
url.searchParams.set('minify', 'false')
|
|
38
|
+
return relative ? `${url.pathname}${url.search}${url.hash}` : url.toString()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const RNXSIM_METRO_SOURCE_MAP_COMMENT = '//# sourceMappingURL='
|
|
42
|
+
|
|
43
|
+
export interface RNXMetroDetachedSourceMapUrl {
|
|
44
|
+
source: string
|
|
45
|
+
/** the comment's URL, which is a whole inline map when Metro inlined it. */
|
|
46
|
+
sourceMappingUrl: string | null
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* splits Metro's trailing `//# sourceMappingURL=` comment off the bundle.
|
|
51
|
+
*
|
|
52
|
+
* a development bundle ends with that comment, and Metro inlines the entire
|
|
53
|
+
* map into it as a data URL whenever the caller did not ask for an external
|
|
54
|
+
* map file. leaving it attached makes the last `__r(` statement carry the map
|
|
55
|
+
* as source text: the layout reader would hand megabytes of base64 to the
|
|
56
|
+
* minifier, and the artifact the runner loads would keep the map resident in
|
|
57
|
+
* child memory for the life of the simulator.
|
|
58
|
+
*/
|
|
59
|
+
export function detachRNXMetroSourceMapUrl(
|
|
60
|
+
bundleText: string,
|
|
61
|
+
): RNXMetroDetachedSourceMapUrl {
|
|
62
|
+
const trimmedEnd = bundleText.replace(/\s+$/, '')
|
|
63
|
+
const commentStart = trimmedEnd.lastIndexOf(`\n${RNXSIM_METRO_SOURCE_MAP_COMMENT}`)
|
|
64
|
+
if (commentStart < 0) return { source: bundleText, sourceMappingUrl: null }
|
|
65
|
+
const url = trimmedEnd.slice(commentStart + 1 + RNXSIM_METRO_SOURCE_MAP_COMMENT.length)
|
|
66
|
+
if (url.includes('\n')) return { source: bundleText, sourceMappingUrl: null }
|
|
67
|
+
return { source: trimmedEnd.slice(0, commentStart), sourceMappingUrl: url }
|
|
68
|
+
}
|
|
69
|
+
|
|
22
70
|
export function createRNXMetroModulePathsFooter(
|
|
23
71
|
modulePaths: RNXMetroModulePathMap,
|
|
24
72
|
): string {
|
|
@@ -29,15 +77,34 @@ export function createRNXMetroModulePathsFooter(
|
|
|
29
77
|
return `\n${RNXSIM_METRO_MODULE_PATHS_PREFIX}${payload};`
|
|
30
78
|
}
|
|
31
79
|
|
|
80
|
+
/**
|
|
81
|
+
* writes the identity footer into a serialized bundle, keeping Metro's
|
|
82
|
+
* `//# sourceMappingURL=` comment on the last line when the graph has one. a
|
|
83
|
+
* production graph has no such comment and gets the footer appended, exactly
|
|
84
|
+
* as before.
|
|
85
|
+
*/
|
|
86
|
+
export function appendRNXMetroModulePathsFooter(
|
|
87
|
+
bundleText: string,
|
|
88
|
+
modulePaths: RNXMetroModulePathMap,
|
|
89
|
+
): string {
|
|
90
|
+
const footer = createRNXMetroModulePathsFooter(modulePaths)
|
|
91
|
+
const detached = detachRNXMetroSourceMapUrl(bundleText)
|
|
92
|
+
return detached.sourceMappingUrl === null
|
|
93
|
+
? bundleText + footer
|
|
94
|
+
: `${detached.source}${footer}\n${RNXSIM_METRO_SOURCE_MAP_COMMENT}${detached.sourceMappingUrl}`
|
|
95
|
+
}
|
|
96
|
+
|
|
32
97
|
export function readRNXMetroModuleIdentity(
|
|
33
98
|
bundleText: string,
|
|
34
99
|
): RNXMetroModuleIdentity | null {
|
|
100
|
+
const detached = detachRNXMetroSourceMapUrl(bundleText)
|
|
101
|
+
const withoutMap = detached.source
|
|
35
102
|
const footerPrefix = `\n${RNXSIM_METRO_MODULE_PATHS_PREFIX}`
|
|
36
|
-
const footerStart =
|
|
37
|
-
if (footerStart < 0 || !
|
|
103
|
+
const footerStart = withoutMap.lastIndexOf(footerPrefix)
|
|
104
|
+
if (footerStart < 0 || !withoutMap.endsWith(';')) return null
|
|
38
105
|
|
|
39
106
|
const parsed: unknown = JSON.parse(
|
|
40
|
-
|
|
107
|
+
withoutMap.slice(footerStart + footerPrefix.length, -1),
|
|
41
108
|
)
|
|
42
109
|
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
43
110
|
return null
|
|
@@ -48,7 +115,11 @@ export function readRNXMetroModuleIdentity(
|
|
|
48
115
|
if (typeof modulePath !== 'string') return null
|
|
49
116
|
modulePaths[moduleId] = modulePath
|
|
50
117
|
}
|
|
51
|
-
return {
|
|
118
|
+
return {
|
|
119
|
+
source: withoutMap.slice(0, footerStart),
|
|
120
|
+
modulePaths,
|
|
121
|
+
sourceMappingUrl: detached.sourceMappingUrl,
|
|
122
|
+
}
|
|
52
123
|
}
|
|
53
124
|
|
|
54
125
|
// the exact layout Metro's baseJSBundle emits and the rnx runtime contract
|
|
@@ -67,6 +138,12 @@ export interface RNXMetroDefine {
|
|
|
67
138
|
factory: { start: number; end: number }
|
|
68
139
|
/** the dependency array, brackets included. */
|
|
69
140
|
dependencies: { start: number; end: number }
|
|
141
|
+
/**
|
|
142
|
+
* the module's verbose name, quotes included, when the graph carries one.
|
|
143
|
+
* Metro emits it as a fourth `__d` argument on a development graph and omits
|
|
144
|
+
* it entirely on a production one.
|
|
145
|
+
*/
|
|
146
|
+
verboseName?: { start: number; end: number }
|
|
70
147
|
}
|
|
71
148
|
|
|
72
149
|
export interface RNXMetroRun {
|
|
@@ -92,12 +169,33 @@ interface DefineTail {
|
|
|
92
169
|
factoryEnd: number
|
|
93
170
|
dependenciesStart: number
|
|
94
171
|
dependenciesEnd: number
|
|
172
|
+
verboseNameStart?: number
|
|
173
|
+
verboseNameEnd?: number
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// walks back over a single-quoted or double-quoted string literal ending at
|
|
177
|
+
// `end`, returning the index of its opening quote. an odd run of backslashes
|
|
178
|
+
// before a quote escapes it, so count them before accepting a candidate.
|
|
179
|
+
function readStringLiteralStart(source: string, start: number, end: number): number {
|
|
180
|
+
const quote = source[end]
|
|
181
|
+
if (quote !== '"' && quote !== "'") return -1
|
|
182
|
+
for (let index = end - 1; index > start; index--) {
|
|
183
|
+
if (source[index] !== quote) continue
|
|
184
|
+
let backslashes = 0
|
|
185
|
+
while (index - 1 - backslashes > start && source[index - 1 - backslashes] === '\\') {
|
|
186
|
+
backslashes++
|
|
187
|
+
}
|
|
188
|
+
if (backslashes % 2 === 0) return index
|
|
189
|
+
}
|
|
190
|
+
return -1
|
|
95
191
|
}
|
|
96
192
|
|
|
97
|
-
// each `__d(...)` ends with `),<id>,[dep,dep,...])
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
193
|
+
// each `__d(...)` ends with `),<id>,[dep,dep,...]);?`, and a development graph
|
|
194
|
+
// adds a fourth verbose-name string after the dependency array. the dependency
|
|
195
|
+
// array can be hundreds of entries long, so walk back from `end` instead of
|
|
196
|
+
// searching a fixed tail window: skip trailing `;` and whitespace, consume the
|
|
197
|
+
// closing `)`, take the verbose name when one is there, balance `[...]` to find
|
|
198
|
+
// its opening bracket, then read the integer before it.
|
|
101
199
|
function readDefineTail(source: string, start: number, end: number): DefineTail | null {
|
|
102
200
|
let cursor = end
|
|
103
201
|
while (
|
|
@@ -112,6 +210,19 @@ function readDefineTail(source: string, start: number, end: number): DefineTail
|
|
|
112
210
|
if (source[cursor] !== ')') return null
|
|
113
211
|
cursor--
|
|
114
212
|
while (cursor > start && /\s/.test(source[cursor])) cursor--
|
|
213
|
+
let verboseNameStart: number | undefined
|
|
214
|
+
let verboseNameEnd: number | undefined
|
|
215
|
+
if (source[cursor] === '"' || source[cursor] === "'") {
|
|
216
|
+
const literalStart = readStringLiteralStart(source, start, cursor)
|
|
217
|
+
if (literalStart < 0) return null
|
|
218
|
+
verboseNameStart = literalStart
|
|
219
|
+
verboseNameEnd = cursor + 1
|
|
220
|
+
cursor = literalStart - 1
|
|
221
|
+
while (cursor > start && /\s/.test(source[cursor])) cursor--
|
|
222
|
+
if (source[cursor] !== ',') return null
|
|
223
|
+
cursor--
|
|
224
|
+
while (cursor > start && /\s/.test(source[cursor])) cursor--
|
|
225
|
+
}
|
|
115
226
|
if (source[cursor] !== ']') return null
|
|
116
227
|
const dependenciesEnd = cursor + 1
|
|
117
228
|
let depth = 1
|
|
@@ -147,6 +258,8 @@ function readDefineTail(source: string, start: number, end: number): DefineTail
|
|
|
147
258
|
factoryEnd,
|
|
148
259
|
dependenciesStart,
|
|
149
260
|
dependenciesEnd,
|
|
261
|
+
verboseNameStart,
|
|
262
|
+
verboseNameEnd,
|
|
150
263
|
}
|
|
151
264
|
}
|
|
152
265
|
|
|
@@ -192,6 +305,9 @@ export function readRNXMetroBundleLayout(source: string): RNXMetroBundleLayout {
|
|
|
192
305
|
endLineIndex,
|
|
193
306
|
factory: { start: start + '__d('.length, end: tail.factoryEnd },
|
|
194
307
|
dependencies: { start: tail.dependenciesStart, end: tail.dependenciesEnd },
|
|
308
|
+
...(tail.verboseNameStart !== undefined && tail.verboseNameEnd !== undefined
|
|
309
|
+
? { verboseName: { start: tail.verboseNameStart, end: tail.verboseNameEnd } }
|
|
310
|
+
: {}),
|
|
195
311
|
})
|
|
196
312
|
}
|
|
197
313
|
|
package/src/runtime-assets.ts
CHANGED
|
@@ -211,6 +211,11 @@ export function serveRuntimeFile(req: any, res: any, fullPath: string) {
|
|
|
211
211
|
const stat = fs.statSync(fullPath)
|
|
212
212
|
const lastModified = stat.mtime.toUTCString()
|
|
213
213
|
res.setHeader('content-type', MIME_TYPES[ext] || 'application/octet-stream')
|
|
214
|
+
if (/^canvaskit-graphite-pipelines-[0-9a-f]{8}\.bin$/.test(path.basename(fullPath))) {
|
|
215
|
+
res.setHeader('cache-control', 'public, max-age=31536000, immutable')
|
|
216
|
+
fs.createReadStream(fullPath).pipe(res)
|
|
217
|
+
return
|
|
218
|
+
}
|
|
214
219
|
// these urls are STABLE across dev rebuilds and runtime version switches
|
|
215
220
|
// (worker.js, canvaskit-graphite.wasm, the html shells are unhashed), so the
|
|
216
221
|
// browser must revalidate every load — the old year-long immutable here made every
|