vxrn 1.26.0 → 2.0.0-beta.33.1
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/dist/patches/builtInDepPatches.mjs +23 -42
- package/dist/patches/builtInDepPatches.mjs.map +1 -1
- package/dist/patches/builtInDepPatches.native.js +23 -42
- package/dist/patches/builtInDepPatches.native.js.map +1 -1
- package/dist/plugins/reactNativeDevServer.mjs +88 -42
- package/dist/plugins/reactNativeDevServer.mjs.map +1 -1
- package/dist/plugins/reactNativeDevServer.native.js +113 -44
- package/dist/plugins/reactNativeDevServer.native.js.map +1 -1
- package/dist/rn-commands/bundle/buildBundle.mjs +29 -10
- package/dist/rn-commands/bundle/buildBundle.mjs.map +1 -1
- package/dist/rn-commands/bundle/buildBundle.native.js +29 -10
- package/dist/rn-commands/bundle/buildBundle.native.js.map +1 -1
- package/dist/utils/createNativeDevEngine.mjs +288 -95
- package/dist/utils/createNativeDevEngine.mjs.map +1 -1
- package/dist/utils/createNativeDevEngine.native.js +329 -95
- package/dist/utils/createNativeDevEngine.native.js.map +1 -1
- package/dist/utils/createNativeDevEngine.test.mjs +285 -23
- package/dist/utils/createNativeDevEngine.test.mjs.map +1 -1
- package/dist/utils/createNativeDevEngine.test.native.js +319 -23
- package/dist/utils/createNativeDevEngine.test.native.js.map +1 -1
- package/dist/utils/patches.test.mjs.map +1 -1
- package/dist/utils/patches.test.native.js.map +1 -1
- package/dist/utils/scanDepsToOptimize.mjs +1 -3
- package/dist/utils/scanDepsToOptimize.mjs.map +1 -1
- package/dist/utils/scanDepsToOptimize.native.js +1 -3
- package/dist/utils/scanDepsToOptimize.native.js.map +1 -1
- package/dist/utils/symbolicateNativeStack.mjs +66 -0
- package/dist/utils/symbolicateNativeStack.mjs.map +1 -0
- package/dist/utils/symbolicateNativeStack.native.js +70 -0
- package/dist/utils/symbolicateNativeStack.native.js.map +1 -0
- package/dist/utils/symbolicateNativeStack.test.mjs +96 -0
- package/dist/utils/symbolicateNativeStack.test.mjs.map +1 -0
- package/dist/utils/symbolicateNativeStack.test.native.js +96 -0
- package/dist/utils/symbolicateNativeStack.test.native.js.map +1 -0
- package/expo-plugin.cjs +12 -6
- package/package.json +13 -12
- package/src/patches/builtInDepPatches.ts +24 -48
- package/src/plugins/reactNativeDevServer.ts +124 -52
- package/src/rn-commands/bundle/buildBundle.ts +28 -9
- package/src/utils/createNativeDevEngine.test.ts +581 -193
- package/src/utils/createNativeDevEngine.ts +506 -123
- package/src/utils/patches.test.ts +1 -4
- package/src/utils/scanDepsToOptimize.ts +0 -3
- package/src/utils/symbolicateNativeStack.test.ts +78 -0
- package/src/utils/symbolicateNativeStack.ts +129 -0
- package/types/patches/builtInDepPatches.d.ts.map +1 -1
- package/types/plugins/reactNativeDevServer.d.ts.map +1 -1
- package/types/rn-commands/bundle/buildBundle.d.ts.map +1 -1
- package/types/utils/createNativeDevEngine.d.ts +22 -4
- package/types/utils/createNativeDevEngine.d.ts.map +1 -1
- package/types/utils/scanDepsToOptimize.d.ts.map +1 -1
- package/types/utils/symbolicateNativeStack.d.ts +46 -0
- package/types/utils/symbolicateNativeStack.d.ts.map +1 -0
- package/types/utils/symbolicateNativeStack.test.d.ts +2 -0
- package/types/utils/symbolicateNativeStack.test.d.ts.map +1 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { basename, dirname, extname, join, relative, resolve } from "node:path";
|
|
3
|
-
import { copyFileSync, mkdirSync, readFileSync, readdirSync, realpathSync } from "node:fs";
|
|
3
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, statSync, watch, writeFileSync } from "node:fs";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
|
+
import micromatch from "micromatch";
|
|
5
6
|
import { loadEnv, normalizePath } from "vite";
|
|
6
7
|
import { shouldStripFlow, transformHermesAsync } from "@vxrn/compiler";
|
|
7
8
|
import { DEFAULT_ASSET_EXTS } from "../constants/defaults.mjs";
|
|
@@ -109,7 +110,12 @@ function getNativeTransformConfig(platform, dev, root) {
|
|
|
109
110
|
}
|
|
110
111
|
};
|
|
111
112
|
}
|
|
112
|
-
function getNativePlugins(root, platform, viteImportGlobPlugin, dev, assetsDest, onAsset, sourceMaps = false) {
|
|
113
|
+
function getNativePlugins(root, platform, viteImportGlobPlugin, dev, assetsDest, onAsset, sourceMaps = false, userPlugins = []) {
|
|
114
|
+
const context = {
|
|
115
|
+
root,
|
|
116
|
+
platform,
|
|
117
|
+
dev
|
|
118
|
+
};
|
|
113
119
|
return [...(globalThis.__vxrnAddNativePlugins || []), serverFileExclusionPlugin(), environmentGuardPlugin(), hmrClientNoopPlugin(), cssStubPlugin(), viteImportGlobPlugin({
|
|
114
120
|
root
|
|
115
121
|
}), vxrnCompilerPlugin(platform, dev, root, sourceMaps), rnCodegenPlugin({
|
|
@@ -119,25 +125,38 @@ function getNativePlugins(root, platform, viteImportGlobPlugin, dev, assetsDest,
|
|
|
119
125
|
platform,
|
|
120
126
|
assetsDest,
|
|
121
127
|
onAsset
|
|
122
|
-
}), hermesCompatSWCPlugin(dev, sourceMaps), hermesLoopsPlugin(sourceMaps)];
|
|
128
|
+
}), hermesCompatSWCPlugin(dev, sourceMaps), hermesLoopsPlugin(sourceMaps), ...userPlugins].map(plugin => plugin.api?.vxrnNative ? plugin.api.vxrnNative(context) : plugin);
|
|
123
129
|
}
|
|
124
|
-
function getNativeOutputOptions(prelude, sourcemap) {
|
|
130
|
+
function getNativeOutputOptions(prelude, sourcemap, minify) {
|
|
125
131
|
return {
|
|
126
132
|
format: "esm",
|
|
127
133
|
sourcemap,
|
|
134
|
+
sourcemapPathTransform: (relativeSourcePath, sourcemapPath) => normalizePath(resolve(dirname(sourcemapPath), relativeSourcePath)),
|
|
128
135
|
intro: prelude,
|
|
129
136
|
codeSplitting: false,
|
|
130
|
-
strictExecutionOrder: true
|
|
137
|
+
strictExecutionOrder: true,
|
|
138
|
+
minify: minify || "dce-only"
|
|
131
139
|
};
|
|
132
140
|
}
|
|
133
141
|
function normalizeNativeCommonJSInterop(code) {
|
|
134
142
|
return code.replace(/(\b__toESM(?:\$\d+)?\(\s*(?:require[\w$]*\(\)|__rolldown_runtime__\.loadExports\("(?:\\.|[^"\\])*"\))\s*),\s*1(\s*\))/g, "$1$2");
|
|
135
143
|
}
|
|
144
|
+
function blankPreservingLines(match) {
|
|
145
|
+
return "\n".repeat(match.match(/\n/g)?.length ?? 0);
|
|
146
|
+
}
|
|
147
|
+
function countLines(code) {
|
|
148
|
+
return (code.match(/\n/g)?.length ?? 0) + 1;
|
|
149
|
+
}
|
|
150
|
+
function assertBundleLinesPreserved(before, after, stage) {
|
|
151
|
+
const beforeLines = countLines(before);
|
|
152
|
+
const afterLines = countLines(after);
|
|
153
|
+
if (beforeLines !== afterLines) throw new Error(`[vxrn] native bundle post-processing (${stage}) changed the line count (${beforeLines} -> ${afterLines}), which invalidates the source map. Every pass after Rolldown emits the map must preserve lines.`);
|
|
154
|
+
}
|
|
136
155
|
function postProcessNativeBundle(code) {
|
|
137
156
|
code = normalizeNativeCommonJSInterop(code);
|
|
138
157
|
code = code.replace(/\btypeof\s+import\.meta\b/g, "\"object\"");
|
|
139
|
-
code = code.replace(
|
|
140
|
-
code = code.replace(
|
|
158
|
+
code = code.replace(/^[^\S\n]*export[^\S\n]*\{[^}]*\}[^\S\n]*;?[^\S\n]*$/gm, blankPreservingLines);
|
|
159
|
+
code = code.replace(/^([^\S\n]*)export[^\S\n]+default[^\S\n]+([^;\n]+);?[^\S\n]*$/gm, "$1$2;");
|
|
141
160
|
code = code.replace(/^if \(import\.meta\.hot\).*$/gm, "");
|
|
142
161
|
{
|
|
143
162
|
const idx = code.indexOf("NativeAnimatedModule_default ?? NativeAnimatedTurboModule_default;");
|
|
@@ -150,7 +169,8 @@ function postProcessNativeBundle(code) {
|
|
|
150
169
|
const afterMarker = code.indexOf("})();", idx);
|
|
151
170
|
if (afterMarker !== -1) {
|
|
152
171
|
const end = afterMarker + 5;
|
|
153
|
-
|
|
172
|
+
const removed = code.slice(beforeMarker, end);
|
|
173
|
+
code = code.slice(0, beforeMarker) + "NativeAnimatedModule = NativeAnimatedModule_default ?? NativeAnimatedTurboModule_default;" + blankPreservingLines(removed) + code.slice(end);
|
|
154
174
|
}
|
|
155
175
|
}
|
|
156
176
|
}
|
|
@@ -161,7 +181,7 @@ function postProcessNativeBundle(code) {
|
|
|
161
181
|
function wrapNativeBundleModuleScope(code) {
|
|
162
182
|
const idx = code.indexOf("//#region \\0rolldown/runtime.js");
|
|
163
183
|
if (idx === -1) return code;
|
|
164
|
-
return code.slice(0, idx) + ";(function() {
|
|
184
|
+
return code.slice(0, idx) + ";(function() {" + code.slice(idx) + "\n})();\n";
|
|
165
185
|
}
|
|
166
186
|
async function downlevelClassFieldsInBundle(code) {
|
|
167
187
|
const startMarker = "//#region \\0rolldown/runtime.js";
|
|
@@ -191,6 +211,19 @@ ${transformedCode}`;
|
|
|
191
211
|
const linePreservingRuntime = transformed + "\n".repeat(originalNewlines - transformedNewlines);
|
|
192
212
|
return code.slice(0, startIdx) + linePreservingRuntime + code.slice(runtimeEnd);
|
|
193
213
|
}
|
|
214
|
+
function nativeBundlePostProcessPlugin() {
|
|
215
|
+
return {
|
|
216
|
+
name: "vxrn:native-post-process",
|
|
217
|
+
async renderChunk(code) {
|
|
218
|
+
const processed = await downlevelClassFieldsInBundle(postProcessNativeBundle(code));
|
|
219
|
+
assertBundleLinesPreserved(code, processed, "production");
|
|
220
|
+
return {
|
|
221
|
+
code: processed,
|
|
222
|
+
map: null
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
194
227
|
async function createNativeDevEngine(options) {
|
|
195
228
|
const {
|
|
196
229
|
root,
|
|
@@ -218,7 +251,18 @@ async function createNativeDevEngine(options) {
|
|
|
218
251
|
let bundleReject = null;
|
|
219
252
|
let bundlePromise = null;
|
|
220
253
|
const resolvedHost = host === "0.0.0.0" ? "localhost" : host;
|
|
221
|
-
const
|
|
254
|
+
const virtualEntry = nativeVirtualEntryPlugin(root, {
|
|
255
|
+
dev: true,
|
|
256
|
+
platform,
|
|
257
|
+
dynamicRoutes: true
|
|
258
|
+
});
|
|
259
|
+
let engineWork = Promise.resolve();
|
|
260
|
+
const queueEngineWork = work => {
|
|
261
|
+
const result = engineWork.then(work, work);
|
|
262
|
+
engineWork = result.catch(() => {});
|
|
263
|
+
return result;
|
|
264
|
+
};
|
|
265
|
+
const inputOptions = {
|
|
222
266
|
input: VIRTUAL_NATIVE_ENTRY,
|
|
223
267
|
cwd: root,
|
|
224
268
|
platform: "neutral",
|
|
@@ -238,72 +282,46 @@ async function createNativeDevEngine(options) {
|
|
|
238
282
|
moduleTypes: {
|
|
239
283
|
".js": "jsx"
|
|
240
284
|
},
|
|
241
|
-
plugins: [
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
...getNativeOutputOptions(prelude, false),
|
|
285
|
+
plugins: [virtualEntry.plugin, ...getNativePlugins(root, platform, viteImportGlobPlugin, true, void 0, assetRegistry.register, true, userPlugins)]
|
|
286
|
+
};
|
|
287
|
+
const outputOptions = {
|
|
288
|
+
...getNativeOutputOptions(prelude, true, false),
|
|
246
289
|
outro: `
|
|
247
290
|
try {
|
|
248
291
|
var __WS = (init_WebSocket(), __toCommonJS(WebSocket_exports)).default;
|
|
249
292
|
var __hmrUrl = 'ws://${resolvedHost}:${port}/hot?platform=${platform}&clientId=' + encodeURIComponent(__rolldown_runtime__.clientId);
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
var applied = __rolldown_runtime__.applyHmrUpdate(msg.code, msg.changedIds, msg.seq);
|
|
257
|
-
if (!applied) {
|
|
258
|
-
var updateSettings = g.__turboModuleProxy ? g.__turboModuleProxy('DevSettings') : null;
|
|
259
|
-
if (updateSettings && updateSettings.reload) updateSettings.reload();
|
|
260
|
-
}
|
|
261
|
-
} else if (msg.type === 'hmr:reload') {
|
|
262
|
-
var ds = g.__turboModuleProxy ? g.__turboModuleProxy('DevSettings') : null;
|
|
263
|
-
if (ds && ds.reload) ds.reload();
|
|
264
|
-
}
|
|
265
|
-
} catch(e) { console.error('[vxrn] HMR eval error:', e); }
|
|
266
|
-
};
|
|
267
|
-
__hmrWS.onopen = function() {
|
|
268
|
-
if (typeof __rolldown_runtime__ !== 'undefined' && __rolldown_runtime__.setup) {
|
|
269
|
-
__rolldown_runtime__.setup(__hmrWS);
|
|
270
|
-
}
|
|
271
|
-
};
|
|
272
|
-
__hmrWS.onerror = function(e) { console.warn('[vxrn] HMR connection error:', e.message || e); };
|
|
273
|
-
} catch(e) {}
|
|
293
|
+
__rolldown_runtime__.setup(new __WS(__hmrUrl));
|
|
294
|
+
} catch(e) {
|
|
295
|
+
// a swallowed failure here leaves the app permanently without Fast Refresh and
|
|
296
|
+
// nothing on screen or in the terminal says so.
|
|
297
|
+
console.error('[vxrn] HMR client failed to start:', (e && e.message) || e);
|
|
298
|
+
}
|
|
274
299
|
`
|
|
275
|
-
}
|
|
300
|
+
};
|
|
301
|
+
let outputProcessed = Promise.resolve();
|
|
302
|
+
let engine;
|
|
303
|
+
let routeWatcher;
|
|
304
|
+
const rebuildIfRouteGraphChanged = () => queueEngineWork(async () => {
|
|
305
|
+
const {
|
|
306
|
+
files: knownRoutes,
|
|
307
|
+
scanFiles,
|
|
308
|
+
updateFiles
|
|
309
|
+
} = virtualEntry.routes;
|
|
310
|
+
const currentRoutes = scanFiles();
|
|
311
|
+
if (!(currentRoutes.size !== knownRoutes.size || [...currentRoutes].some(route => !knownRoutes.has(route)))) return false;
|
|
312
|
+
updateFiles(currentRoutes);
|
|
313
|
+
return true;
|
|
314
|
+
});
|
|
315
|
+
engine = await dev(inputOptions, outputOptions, {
|
|
276
316
|
onOutput: async result => {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
bundlePromise = null;
|
|
286
|
-
reject(firstBuildError);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
const chunk = result.output.find(o => o.type === "chunk" && o.isEntry);
|
|
292
|
-
if (chunk && "code" in chunk) {
|
|
293
|
-
firstBuildError = null;
|
|
294
|
-
let code = postProcessNativeBundle(chunk.code);
|
|
295
|
-
code = await downlevelClassFieldsInBundle(code);
|
|
296
|
-
code = wrapNativeBundleModuleScope(code);
|
|
297
|
-
currentBundle = {
|
|
298
|
-
code
|
|
299
|
-
};
|
|
300
|
-
console.info(`[vxrn] native bundle ready (${Math.round(code.length / 1024)}KB)`);
|
|
301
|
-
if (bundleResolve) {
|
|
302
|
-
bundleResolve(currentBundle);
|
|
303
|
-
bundleResolve = null;
|
|
304
|
-
bundleReject = null;
|
|
305
|
-
bundlePromise = null;
|
|
306
|
-
}
|
|
317
|
+
let finishOutput = () => {};
|
|
318
|
+
outputProcessed = new Promise(resolve2 => {
|
|
319
|
+
finishOutput = resolve2;
|
|
320
|
+
});
|
|
321
|
+
try {
|
|
322
|
+
await handleOutput(result);
|
|
323
|
+
} finally {
|
|
324
|
+
finishOutput();
|
|
307
325
|
}
|
|
308
326
|
},
|
|
309
327
|
onHmrUpdates: async result => {
|
|
@@ -314,6 +332,7 @@ try {
|
|
|
314
332
|
});
|
|
315
333
|
return;
|
|
316
334
|
}
|
|
335
|
+
if (await rebuildIfRouteGraphChanged()) return;
|
|
317
336
|
for (const {
|
|
318
337
|
clientId,
|
|
319
338
|
update
|
|
@@ -331,11 +350,74 @@ try {
|
|
|
331
350
|
rebuildStrategy: "never",
|
|
332
351
|
watch: {}
|
|
333
352
|
});
|
|
353
|
+
async function handleOutput(result) {
|
|
354
|
+
if (result instanceof Error) {
|
|
355
|
+
console.error("[vxrn] native bundle error:", result.message);
|
|
356
|
+
if (/panic/i.test(result.message)) console.error("[vxrn] rolldown itself panicked. Fast Refresh will full-reload until you restart the dev server.");
|
|
357
|
+
if (!currentBundle) {
|
|
358
|
+
firstBuildError ||= result;
|
|
359
|
+
if (bundleReject) {
|
|
360
|
+
const reject = bundleReject;
|
|
361
|
+
bundleResolve = null;
|
|
362
|
+
bundleReject = null;
|
|
363
|
+
bundlePromise = null;
|
|
364
|
+
reject(firstBuildError);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
const chunk = result.output.find(o => o.type === "chunk" && o.isEntry);
|
|
370
|
+
if (chunk && "code" in chunk) {
|
|
371
|
+
firstBuildError = null;
|
|
372
|
+
let code = postProcessNativeBundle(chunk.code);
|
|
373
|
+
code = await downlevelClassFieldsInBundle(code);
|
|
374
|
+
assertBundleLinesPreserved(chunk.code, code, "dev");
|
|
375
|
+
code = wrapNativeBundleModuleScope(code);
|
|
376
|
+
if (!chunk.map) throw new Error("[vxrn] rolldown produced no source map for the dev bundle, so /symbolicate cannot resolve a frame");
|
|
377
|
+
currentBundle = {
|
|
378
|
+
code,
|
|
379
|
+
map: chunk.map.toString()
|
|
380
|
+
};
|
|
381
|
+
console.info(`[vxrn] native bundle ready (${Math.round(code.length / 1024)}KB)`);
|
|
382
|
+
if (bundleResolve) {
|
|
383
|
+
bundleResolve(currentBundle);
|
|
384
|
+
bundleResolve = null;
|
|
385
|
+
bundleReject = null;
|
|
386
|
+
bundlePromise = null;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
334
390
|
await engine.run();
|
|
391
|
+
let routeWatchTarget = virtualEntry.routes.routeRoot;
|
|
392
|
+
while (!statSync(routeWatchTarget, {
|
|
393
|
+
throwIfNoEntry: false
|
|
394
|
+
})?.isDirectory()) {
|
|
395
|
+
const parent = dirname(routeWatchTarget);
|
|
396
|
+
if (parent === routeWatchTarget) break;
|
|
397
|
+
routeWatchTarget = parent;
|
|
398
|
+
}
|
|
399
|
+
const normalizedRouteRoot = normalizePath(virtualEntry.routes.routeRoot);
|
|
400
|
+
routeWatcher = watch(routeWatchTarget, {
|
|
401
|
+
recursive: true
|
|
402
|
+
}, (_eventType, file) => {
|
|
403
|
+
if (file === null) return;
|
|
404
|
+
const changed = normalizePath(resolve(routeWatchTarget, file.toString()));
|
|
405
|
+
if (changed !== normalizedRouteRoot && !changed.startsWith(`${normalizedRouteRoot}/`)) return;
|
|
406
|
+
rebuildIfRouteGraphChanged().catch(error => {
|
|
407
|
+
console.error(`[vxrn] rebuilding the native route map failed`, error);
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
routeWatcher.on("error", error => {
|
|
411
|
+
console.error(`[vxrn] watching the native route map failed`, error);
|
|
412
|
+
});
|
|
413
|
+
await rebuildIfRouteGraphChanged();
|
|
335
414
|
return {
|
|
336
415
|
engine,
|
|
337
416
|
async getBundle() {
|
|
338
|
-
await
|
|
417
|
+
await queueEngineWork(async () => {
|
|
418
|
+
await engine.ensureLatestBuildOutput();
|
|
419
|
+
await outputProcessed;
|
|
420
|
+
});
|
|
339
421
|
if (currentBundle) return currentBundle;
|
|
340
422
|
if (firstBuildError) throw firstBuildError;
|
|
341
423
|
if (!bundlePromise) {
|
|
@@ -363,6 +445,7 @@ try {
|
|
|
363
445
|
return assetRegistry.resolve(pathname, hash);
|
|
364
446
|
},
|
|
365
447
|
async close() {
|
|
448
|
+
routeWatcher?.close();
|
|
366
449
|
await engine.close();
|
|
367
450
|
}
|
|
368
451
|
};
|
|
@@ -376,7 +459,8 @@ async function buildNativeBundle(options) {
|
|
|
376
459
|
entryFile,
|
|
377
460
|
assetsDest,
|
|
378
461
|
plugins: userPlugins = [],
|
|
379
|
-
sourcemap = false
|
|
462
|
+
sourcemap = false,
|
|
463
|
+
minify = !dev
|
|
380
464
|
} = options;
|
|
381
465
|
const {
|
|
382
466
|
build
|
|
@@ -404,21 +488,22 @@ async function buildNativeBundle(options) {
|
|
|
404
488
|
".js": "jsx"
|
|
405
489
|
},
|
|
406
490
|
plugins: [...(entryFile ? [] : [nativeVirtualEntryPlugin(root, {
|
|
407
|
-
dev
|
|
408
|
-
|
|
409
|
-
|
|
491
|
+
dev,
|
|
492
|
+
platform
|
|
493
|
+
}).plugin]), ...getNativePlugins(root, platform, viteImportGlobPlugin, dev, assetsDest, void 0, sourcemap, userPlugins), nativeBundlePostProcessPlugin()],
|
|
494
|
+
output: getNativeOutputOptions(prelude, sourcemap, minify)
|
|
410
495
|
})).output.find(o => o.type === "chunk" && o.isEntry);
|
|
411
496
|
if (!chunk || !("code" in chunk)) throw new Error("[vxrn] production build produced no output");
|
|
412
|
-
|
|
413
|
-
code = await downlevelClassFieldsInBundle(code);
|
|
497
|
+
if (sourcemap && !chunk.map) throw new Error("[vxrn] a source map was requested but rolldown produced none for the production bundle");
|
|
414
498
|
return {
|
|
415
|
-
code,
|
|
499
|
+
code: chunk.code,
|
|
416
500
|
map: sourcemap ? chunk.map?.toString() : void 0
|
|
417
501
|
};
|
|
418
502
|
}
|
|
419
503
|
const VIRTUAL_NATIVE_ENTRY = "virtual:native-entry";
|
|
420
504
|
function nativeVirtualEntryPlugin(root, opts) {
|
|
421
505
|
const isDev = opts?.dev !== false;
|
|
506
|
+
const useDynamicRouteRegistry = opts.dynamicRoutes === true;
|
|
422
507
|
const resolvedId = normalizePath(resolve(root, "__virtual-native-entry.tsx"));
|
|
423
508
|
const entryConfig = globalThis.__vxrnNativeEntryConfig || {};
|
|
424
509
|
const routerRoot = entryConfig.routerRoot || "app";
|
|
@@ -433,8 +518,7 @@ function nativeVirtualEntryPlugin(root, opts) {
|
|
|
433
518
|
return `import ${JSON.stringify(resolved)};`;
|
|
434
519
|
})();
|
|
435
520
|
const routeGlobs = [`./${routerRoot}/**/*.tsx`, `./${routerRoot}/**/*.ts`, `!./${routerRoot}/**/*+api.*`, `!./${routerRoot}/**/*.test.*`, `!./${routerRoot}/**/*.d.ts`, `!./${routerRoot}/**/*.server.*`, `!./${routerRoot}/**/_middleware.*`, `!./${routerRoot}/**/*.web.*`, ...(entryConfig.ignoredRouteFiles || []).map(pattern => `!./${routerRoot}/${pattern}`)];
|
|
436
|
-
const
|
|
437
|
-
${isDev ? `
|
|
521
|
+
const refreshSetup = isDev ? `
|
|
438
522
|
// react-refresh/runtime MUST initialize before React loads
|
|
439
523
|
import RefreshRuntime from 'react-refresh/runtime';
|
|
440
524
|
RefreshRuntime.injectIntoGlobalHook(globalThis);
|
|
@@ -443,22 +527,25 @@ globalThis.$RefreshReg$ = function(type, id) {
|
|
|
443
527
|
RefreshRuntime.register(type, id);
|
|
444
528
|
};
|
|
445
529
|
globalThis.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;
|
|
446
|
-
` : ""
|
|
530
|
+
` : "";
|
|
531
|
+
const entryCode = () => `
|
|
532
|
+
${refreshSetup}
|
|
447
533
|
import * as ReactNativeInitializeCore from 'react-native/Libraries/Core/InitializeCore';
|
|
448
534
|
import NativeWebSocket from 'react-native/Libraries/WebSocket/WebSocket';
|
|
535
|
+
import DevSettings from 'react-native/Libraries/Utilities/DevSettings';
|
|
449
536
|
${setupFileImport}
|
|
537
|
+
${routeImports}
|
|
450
538
|
import { createApp } from 'one';
|
|
451
539
|
|
|
452
540
|
void ReactNativeInitializeCore;
|
|
453
541
|
globalThis.WebSocket = NativeWebSocket;
|
|
542
|
+
globalThis.__VXRN_RELOAD_NATIVE_DEV_BUNDLE__ = function(reason) {
|
|
543
|
+
DevSettings.reload(reason);
|
|
544
|
+
};
|
|
454
545
|
|
|
455
|
-
|
|
456
|
-
//
|
|
457
|
-
|
|
458
|
-
Object.keys(_routes).forEach(function(key) {
|
|
459
|
-
var normalizedKey = key.replace(/^\\.\\//, '');
|
|
460
|
-
routes['/' + normalizedKey] = _routes[key];
|
|
461
|
-
});
|
|
546
|
+
// React Native reloads a dev bundle in the existing JS runtime. Advance One's
|
|
547
|
+
// route-cache version so a full reload cannot reuse the previous context.
|
|
548
|
+
globalThis.__vxrnVersion = (globalThis.__vxrnVersion || 0) + 1;
|
|
462
549
|
|
|
463
550
|
createApp({
|
|
464
551
|
routes: routes,
|
|
@@ -466,14 +553,83 @@ createApp({
|
|
|
466
553
|
flags: ${JSON.stringify(flags)},
|
|
467
554
|
linking: ${JSON.stringify(linking)},
|
|
468
555
|
});
|
|
556
|
+
|
|
557
|
+
${routeHmr}
|
|
469
558
|
`;
|
|
559
|
+
const routeRoot = resolve(root, routerRoot);
|
|
560
|
+
const stripDot = pattern => pattern.replace(/^\.\//, "");
|
|
561
|
+
const includeGlobs = routeGlobs.filter(p => !p.startsWith("!")).map(stripDot);
|
|
562
|
+
const excludeGlobs = routeGlobs.filter(p => p.startsWith("!")).map(p => stripDot(p.slice(1)));
|
|
563
|
+
const isRouteFile = file => {
|
|
564
|
+
const relativeToRoot = normalizePath(relative(root, file));
|
|
565
|
+
return micromatch.isMatch(relativeToRoot, includeGlobs) && !micromatch.isMatch(relativeToRoot, excludeGlobs);
|
|
566
|
+
};
|
|
567
|
+
const scanFiles = () => {
|
|
568
|
+
const files = /* @__PURE__ */new Set();
|
|
569
|
+
const walkRouteRoot = dir => {
|
|
570
|
+
for (const entry of readdirSync(dir, {
|
|
571
|
+
withFileTypes: true
|
|
572
|
+
})) {
|
|
573
|
+
const child = resolve(dir, entry.name);
|
|
574
|
+
if (entry.isDirectory()) walkRouteRoot(child);else if (isRouteFile(child)) files.add(normalizePath(child));
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
if (statSync(routeRoot, {
|
|
578
|
+
throwIfNoEntry: false
|
|
579
|
+
})?.isDirectory()) walkRouteRoot(routeRoot);
|
|
580
|
+
return files;
|
|
581
|
+
};
|
|
582
|
+
const registrySpecifier = `./node_modules/.vxrn/native-routes-${opts.platform}.mjs`;
|
|
583
|
+
const registryPath = resolve(root, registrySpecifier);
|
|
584
|
+
const routes = {
|
|
585
|
+
routeRoot,
|
|
586
|
+
files: /* @__PURE__ */new Set(),
|
|
587
|
+
isRouteFile,
|
|
588
|
+
scanFiles,
|
|
589
|
+
updateFiles(files) {
|
|
590
|
+
routes.files = files;
|
|
591
|
+
if (!useDynamicRouteRegistry) return;
|
|
592
|
+
const source = `export const routes = {
|
|
593
|
+
${[...files].sort().map(file => {
|
|
594
|
+
const routeKey = `/${normalizePath(relative(root, file))}`;
|
|
595
|
+
let importPath = normalizePath(relative(dirname(registryPath), file));
|
|
596
|
+
if (!importPath.startsWith(".")) importPath = `./${importPath}`;
|
|
597
|
+
return ` ${JSON.stringify(routeKey)}: () => import(${JSON.stringify(importPath)})`;
|
|
598
|
+
}).join(",\n")}
|
|
599
|
+
}
|
|
600
|
+
`;
|
|
601
|
+
mkdirSync(dirname(registryPath), {
|
|
602
|
+
recursive: true
|
|
603
|
+
});
|
|
604
|
+
if (!existsSync(registryPath) || readFileSync(registryPath, "utf8") !== source) writeFileSync(registryPath, source);
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
routes.updateFiles(routes.scanFiles());
|
|
608
|
+
const routeImports = useDynamicRouteRegistry ? `import { routes } from ${JSON.stringify(registrySpecifier)};` : `var _routes = import.meta.glob(${JSON.stringify(routeGlobs)}, { exhaustive: true });
|
|
609
|
+
// fix route keys: One expects '/${routerRoot}/...' prefix but import.meta.glob returns './${routerRoot}/...'
|
|
610
|
+
var routes = {};
|
|
611
|
+
Object.keys(_routes).forEach(function(key) {
|
|
612
|
+
var normalizedKey = key.replace(/^\\.\\//, '');
|
|
613
|
+
routes['/' + normalizedKey] = _routes[key];
|
|
614
|
+
});`;
|
|
615
|
+
const routeHmr = useDynamicRouteRegistry ? `if (import.meta.hot) {
|
|
616
|
+
import.meta.hot.accept(${JSON.stringify(registrySpecifier)}, function(next) {
|
|
617
|
+
if (globalThis.__VXRN_UPDATE_NATIVE_ROUTES__) {
|
|
618
|
+
globalThis.__VXRN_UPDATE_NATIVE_ROUTES__(next.routes);
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
}` : "";
|
|
470
622
|
return {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
623
|
+
routes,
|
|
624
|
+
plugin: {
|
|
625
|
+
name: "vxrn:native-virtual-entry",
|
|
626
|
+
resolveId(id) {
|
|
627
|
+
if (id === VIRTUAL_NATIVE_ENTRY) return resolvedId;
|
|
628
|
+
},
|
|
629
|
+
load(id) {
|
|
630
|
+
if (id !== resolvedId) return;
|
|
631
|
+
return entryCode();
|
|
632
|
+
}
|
|
477
633
|
}
|
|
478
634
|
};
|
|
479
635
|
}
|
|
@@ -1154,14 +1310,51 @@ class ReactNativeDevRuntime extends BaseDevRuntime {
|
|
|
1154
1310
|
}
|
|
1155
1311
|
}
|
|
1156
1312
|
|
|
1313
|
+
reload(reason) {
|
|
1314
|
+
var reloadNativeBundle = globalThis.__VXRN_RELOAD_NATIVE_DEV_BUNDLE__;
|
|
1315
|
+
if (typeof reloadNativeBundle === 'function') {
|
|
1316
|
+
reloadNativeBundle(reason);
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
console.error('[vxrn HMR]: DevSettings.reload is unavailable');
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1157
1322
|
setup(socket) {
|
|
1158
1323
|
if (this._socket) return;
|
|
1159
1324
|
this._socket = socket;
|
|
1325
|
+
var runtime = this;
|
|
1326
|
+
// addEventListener rather than socket.onmessage: the bundle's WebSocket is
|
|
1327
|
+
// whatever the host platform provides, and a host that does not honor on*
|
|
1328
|
+
// property assignment would otherwise leave the app permanently without
|
|
1329
|
+
// Fast Refresh and say nothing.
|
|
1330
|
+
socket.addEventListener('message', function(event) {
|
|
1331
|
+
var message;
|
|
1332
|
+
try {
|
|
1333
|
+
message = JSON.parse(event.data);
|
|
1334
|
+
} catch (error) {
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
try {
|
|
1338
|
+
if (message.type === 'hmr:update') {
|
|
1339
|
+
// a patch that cannot be applied in place is the reload signal
|
|
1340
|
+
if (!runtime.applyHmrUpdate(message.code, message.changedIds, message.seq)) {
|
|
1341
|
+
runtime.reload('native HMR update could not be applied');
|
|
1342
|
+
}
|
|
1343
|
+
} else if (message.type === 'hmr:reload') {
|
|
1344
|
+
runtime.reload('native route map changed');
|
|
1345
|
+
}
|
|
1346
|
+
} catch (error) {
|
|
1347
|
+
console.error('[vxrn HMR]: failed to apply update', error);
|
|
1348
|
+
}
|
|
1349
|
+
});
|
|
1350
|
+
socket.addEventListener('error', function(event) {
|
|
1351
|
+
console.warn('[vxrn HMR]: connection error', (event && event.message) || event);
|
|
1352
|
+
});
|
|
1160
1353
|
}
|
|
1161
1354
|
}
|
|
1162
1355
|
|
|
1163
1356
|
globalThis.__rolldown_runtime__ = new ReactNativeDevRuntime();
|
|
1164
1357
|
`;
|
|
1165
1358
|
}
|
|
1166
|
-
export { buildNativeBundle, createNativeDevAssetRegistry, createNativeDevEngine, getHermesSWCIncludes, getHmrRuntimeSource, getNativeAssetData, getNativeTransformConfig, hermesCompatPlugin, hermesCompatSWCPlugin, hermesLoopsPlugin, hmrClientNoopPlugin, nativeAnimatedGuardPlugin, normalizeNativeCommonJSInterop, vxrnCompilerPlugin, wrapNativeBundleModuleScope };
|
|
1359
|
+
export { buildNativeBundle, createNativeDevAssetRegistry, createNativeDevEngine, getHermesSWCIncludes, getHmrRuntimeSource, getNativeAssetData, getNativeTransformConfig, hermesCompatPlugin, hermesCompatSWCPlugin, hermesLoopsPlugin, hmrClientNoopPlugin, nativeAnimatedGuardPlugin, normalizeNativeCommonJSInterop, postProcessNativeBundle, vxrnCompilerPlugin, wrapNativeBundleModuleScope };
|
|
1167
1360
|
//# sourceMappingURL=createNativeDevEngine.mjs.map
|