vxrn 1.25.8 → 1.25.9

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.
Files changed (45) hide show
  1. package/dist/config/getViteServerConfig.mjs +6 -0
  2. package/dist/config/getViteServerConfig.mjs.map +1 -1
  3. package/dist/config/getViteServerConfig.native.js +6 -0
  4. package/dist/config/getViteServerConfig.native.js.map +1 -1
  5. package/dist/config/getViteServerConfig.test.mjs +26 -0
  6. package/dist/config/getViteServerConfig.test.mjs.map +1 -0
  7. package/dist/config/getViteServerConfig.test.native.js +26 -0
  8. package/dist/config/getViteServerConfig.test.native.js.map +1 -0
  9. package/dist/exports/dev.mjs +26 -1
  10. package/dist/exports/dev.mjs.map +1 -1
  11. package/dist/exports/dev.native.js +26 -1
  12. package/dist/exports/dev.native.js.map +1 -1
  13. package/dist/plugins/reactNativeDevServer.mjs +80 -7
  14. package/dist/plugins/reactNativeDevServer.mjs.map +1 -1
  15. package/dist/plugins/reactNativeDevServer.native.js +80 -7
  16. package/dist/plugins/reactNativeDevServer.native.js.map +1 -1
  17. package/dist/runtime/native-prelude.mjs +7 -0
  18. package/dist/runtime/native-prelude.mjs.map +1 -1
  19. package/dist/runtime/native-prelude.native.js +7 -0
  20. package/dist/runtime/native-prelude.native.js.map +1 -1
  21. package/dist/utils/createNativeDevEngine.mjs +302 -197
  22. package/dist/utils/createNativeDevEngine.mjs.map +1 -1
  23. package/dist/utils/createNativeDevEngine.native.js +410 -221
  24. package/dist/utils/createNativeDevEngine.native.js.map +1 -1
  25. package/dist/utils/createNativeDevEngine.test.mjs +536 -6
  26. package/dist/utils/createNativeDevEngine.test.mjs.map +1 -1
  27. package/dist/utils/createNativeDevEngine.test.native.js +559 -6
  28. package/dist/utils/createNativeDevEngine.test.native.js.map +1 -1
  29. package/package.json +11 -11
  30. package/src/config/getViteServerConfig.test.ts +29 -0
  31. package/src/config/getViteServerConfig.ts +11 -0
  32. package/src/exports/dev.ts +34 -3
  33. package/src/plugins/reactNativeDevServer.ts +89 -12
  34. package/src/runtime/native-prelude.ts +7 -0
  35. package/src/utils/createNativeDevEngine.test.ts +682 -5
  36. package/src/utils/createNativeDevEngine.ts +516 -229
  37. package/types/config/getViteServerConfig.d.ts.map +1 -1
  38. package/types/config/getViteServerConfig.test.d.ts +2 -0
  39. package/types/config/getViteServerConfig.test.d.ts.map +1 -0
  40. package/types/exports/dev.d.ts.map +1 -1
  41. package/types/plugins/reactNativeDevServer.d.ts +1 -0
  42. package/types/plugins/reactNativeDevServer.d.ts.map +1 -1
  43. package/types/runtime/native-prelude.d.ts.map +1 -1
  44. package/types/utils/createNativeDevEngine.d.ts +40 -2
  45. package/types/utils/createNativeDevEngine.d.ts.map +1 -1
@@ -1,14 +1,15 @@
1
+ import { createHash } from "node:crypto";
1
2
  import { basename, dirname, extname, join, relative, resolve } from "node:path";
2
- import { copyFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
3
+ import { copyFileSync, mkdirSync, readFileSync, readdirSync, realpathSync } from "node:fs";
3
4
  import { pathToFileURL } from "node:url";
4
- import { normalizePath } from "vite";
5
+ import { loadEnv, normalizePath } from "vite";
5
6
  import { DEFAULT_ASSET_EXTS } from "../constants/defaults.mjs";
6
7
  import { getNativePrelude } from "../runtime/native-prelude.mjs";
7
8
  const FLOW_FILE_PATTERN = /node_modules[\\/](?:react-native|@react-native)[\\/].*\.js$/;
8
- const HERMES_CLASS_TRANSFORMS = ["transform-classes", "transform-parameters", "transform-class-properties", "transform-class-static-block", "transform-private-methods", "transform-private-property-in-object"];
9
- const HERMES_PROD_TRANSFORMS = ["transform-async-to-generator"];
9
+ const HERMES_CLASS_TRANSFORMS = ["transform-classes", "transform-parameters", "transform-block-scoping", "transform-class-properties", "transform-class-static-block", "transform-private-methods", "transform-private-property-in-object"];
10
+ const HERMES_ASYNC_TRANSFORMS = ["transform-async-to-generator"];
10
11
  function getHermesSWCIncludes(dev) {
11
- return [...HERMES_CLASS_TRANSFORMS, ...(dev ? [] : HERMES_PROD_TRANSFORMS)];
12
+ return [...HERMES_CLASS_TRANSFORMS, ...HERMES_ASYNC_TRANSFORMS];
12
13
  }
13
14
  function getResolveExtensions(platform) {
14
15
  const platformExts = platform === "ios" ? [".ios.tsx", ".ios.ts", ".ios.jsx", ".ios.js"] : [".android.tsx", ".android.ts", ".android.jsx", ".android.js"];
@@ -19,7 +20,7 @@ function getResolveExtensions(platform) {
19
20
  function getNativeResolveConfig(platform) {
20
21
  return {
21
22
  extensions: getResolveExtensions(platform),
22
- conditionNames: ["react-native", "import", "require", "default"],
23
+ conditionNames: ["react-native"],
23
24
  mainFields: ["react-native", "module", "main"]
24
25
  };
25
26
  }
@@ -54,28 +55,13 @@ function getNativeTransformConfig(platform, dev, root) {
54
55
  })
55
56
  };
56
57
  })();
57
- const envDefines = (() => {
58
- const defines = {};
59
- try {
60
- const mode2 = dev ? "development" : "production";
61
- for (const envFile of [".env", ".env.local", `.env.${mode2}`, `.env.${mode2}.local`]) {
62
- const envPath = join(root, envFile);
63
- if (!existsSync(envPath)) continue;
64
- const content = readFileSync(envPath, "utf8");
65
- for (const line of content.split("\n")) {
66
- const match = line.match(/^\s*(VITE_\w+)\s*=\s*(.*)$/);
67
- if (match) {
68
- const [, key, rawVal] = match;
69
- const val = rawVal.replace(/^['"]|['"]$/g, "").trim();
70
- defines[`import.meta.env.${key}`] = JSON.stringify(val);
71
- defines[`process.env.${key}`] = JSON.stringify(val);
72
- }
73
- }
74
- }
75
- } catch {}
76
- return defines;
77
- })();
78
58
  const mode = dev ? "development" : "production";
59
+ const publicEnv = loadEnv(mode, root, ["VITE_", "EXPO_PUBLIC_"]);
60
+ const envDefines = {};
61
+ for (const [key, value] of Object.entries(publicEnv)) {
62
+ envDefines[`import.meta.env.${key}`] = JSON.stringify(value);
63
+ envDefines[`process.env.${key}`] = JSON.stringify(value);
64
+ }
79
65
  const envObject = {
80
66
  MODE: mode,
81
67
  DEV: dev,
@@ -126,14 +112,15 @@ function getNativeTransformConfig(platform, dev, root) {
126
112
  }
127
113
  };
128
114
  }
129
- function getNativePlugins(root, platform, viteImportGlobPlugin, dev, assetsDest) {
115
+ function getNativePlugins(root, platform, viteImportGlobPlugin, dev, assetsDest, onAsset, sourceMaps = false) {
130
116
  return [...(globalThis.__vxrnAddNativePlugins || []), serverFileExclusionPlugin(), environmentGuardPlugin(), hmrClientNoopPlugin(), cssStubPlugin(), viteImportGlobPlugin({
131
117
  root
132
- }), vxrnCompilerPlugin(platform, dev, root), flowStripPlugin(), nativeAnimatedGuardPlugin(), assetPlugin({
118
+ }), vxrnCompilerPlugin(platform, dev, root, sourceMaps), flowStripPlugin(), nativeAnimatedGuardPlugin(), assetPlugin({
133
119
  root,
134
120
  platform,
135
- assetsDest
136
- }), hermesCompatSWCPlugin(dev)];
121
+ assetsDest,
122
+ onAsset
123
+ }), hermesCompatSWCPlugin(dev, sourceMaps)];
137
124
  }
138
125
  function getNativeOutputOptions(prelude, sourcemap) {
139
126
  return {
@@ -144,7 +131,12 @@ function getNativeOutputOptions(prelude, sourcemap) {
144
131
  strictExecutionOrder: true
145
132
  };
146
133
  }
134
+ function normalizeNativeCommonJSInterop(code) {
135
+ return code.replace(/(\b__toESM(?:\$\d+)?\(\s*require[\w$]*\(\)\s*),\s*1(\s*\))/g, "$1$2");
136
+ }
147
137
  function postProcessNativeBundle(code) {
138
+ code = normalizeNativeCommonJSInterop(code);
139
+ code = code.replace(/\btypeof\s+import\.meta\b/g, "\"object\"");
148
140
  code = code.replace(/^\s*export\s*\{[^}]*\}\s*;?\s*$/gm, "");
149
141
  code = code.replace(/^\s*export\s+default\s+([^;\n]+);?\s*$/gm, "$1;");
150
142
  code = code.replace(/^if \(import\.meta\.hot\).*$/gm, "");
@@ -181,41 +173,43 @@ async function downlevelClassFieldsInBundle(code) {
181
173
  if (endIdx === -1) return code;
182
174
  const runtimeEnd = endIdx + 12;
183
175
  const runtimeSection = code.slice(startIdx, runtimeEnd);
184
- try {
185
- const result = await (await import("@swc/core")).transform(runtimeSection, {
186
- filename: "rolldown-runtime.js",
187
- configFile: false,
188
- swcrc: false,
189
- sourceMaps: false,
190
- inputSourceMap: false,
191
- isModule: false,
192
- env: {
193
- targets: {
194
- node: 9999
195
- },
196
- include: [...HERMES_CLASS_TRANSFORMS]
176
+ const originalNewlines = runtimeSection.match(/\n/g)?.length ?? 0;
177
+ const transformedCode = (await (await import("@swc/core")).transform(runtimeSection, {
178
+ filename: "rolldown-runtime.js",
179
+ configFile: false,
180
+ swcrc: false,
181
+ sourceMaps: false,
182
+ inputSourceMap: false,
183
+ isModule: false,
184
+ minify: true,
185
+ env: {
186
+ targets: {
187
+ node: 9999
197
188
  },
198
- jsc: {
199
- parser: {
200
- syntax: "ecmascript"
201
- },
202
- transform: {
203
- react: {
204
- runtime: "preserve"
205
- }
206
- },
207
- externalHelpers: false,
208
- assumptions: {
209
- setPublicClassFields: true,
210
- privateFieldsAsProperties: true
189
+ include: [...HERMES_CLASS_TRANSFORMS]
190
+ },
191
+ jsc: {
192
+ parser: {
193
+ syntax: "ecmascript"
194
+ },
195
+ transform: {
196
+ react: {
197
+ runtime: "preserve"
211
198
  }
199
+ },
200
+ externalHelpers: false,
201
+ assumptions: {
202
+ setPublicClassFields: true,
203
+ privateFieldsAsProperties: true
212
204
  }
213
- });
214
- return code.slice(0, startIdx) + result.code + code.slice(runtimeEnd);
215
- } catch (err) {
216
- console.warn("[vxrn] downlevelClassFieldsInBundle failed, returning original:", err);
217
- return code;
218
- }
205
+ }
206
+ })).code.trimEnd();
207
+ const transformed = transformedCode.includes(startMarker) ? transformedCode : `${startMarker}
208
+ ${transformedCode}`;
209
+ const transformedNewlines = transformed.match(/\n/g)?.length ?? 0;
210
+ if (transformedNewlines > originalNewlines) throw new Error("[vxrn] Hermes runtime transform added lines and would invalidate the production source map");
211
+ const linePreservingRuntime = transformed + "\n".repeat(originalNewlines - transformedNewlines);
212
+ return code.slice(0, startIdx) + linePreservingRuntime + code.slice(runtimeEnd);
219
213
  }
220
214
  async function createNativeDevEngine(options) {
221
215
  const {
@@ -237,6 +231,7 @@ async function createNativeDevEngine(options) {
237
231
  platform,
238
232
  serverUrl: serverUrl || `http://${host}:${port}`
239
233
  });
234
+ const assetRegistry = createNativeDevAssetRegistry();
240
235
  let currentBundle = null;
241
236
  let firstBuildError = null;
242
237
  let bundleResolve = null;
@@ -265,7 +260,7 @@ async function createNativeDevEngine(options) {
265
260
  },
266
261
  plugins: [nativeVirtualEntryPlugin(root, {
267
262
  dev: true
268
- }), ...getNativePlugins(root, platform, viteImportGlobPlugin, true), ...userPlugins]
263
+ }), ...getNativePlugins(root, platform, viteImportGlobPlugin, true, void 0, assetRegistry.register, false), ...userPlugins]
269
264
  }, {
270
265
  ...getNativeOutputOptions(prelude, false),
271
266
  outro: `
@@ -384,6 +379,9 @@ try {
384
379
  }
385
380
  return bundlePromise;
386
381
  },
382
+ getAsset(pathname, hash) {
383
+ return assetRegistry.resolve(pathname, hash);
384
+ },
387
385
  async close() {
388
386
  await engine.close();
389
387
  }
@@ -427,7 +425,7 @@ async function buildNativeBundle(options) {
427
425
  },
428
426
  plugins: [...(entryFile ? [] : [nativeVirtualEntryPlugin(root, {
429
427
  dev
430
- })]), ...getNativePlugins(root, platform, viteImportGlobPlugin, dev, assetsDest), ...userPlugins],
428
+ })]), ...getNativePlugins(root, platform, viteImportGlobPlugin, dev, assetsDest, void 0, sourcemap), ...userPlugins],
431
429
  output: getNativeOutputOptions(prelude, sourcemap)
432
430
  })).output.find(o => o.type === "chunk" && o.isEntry);
433
431
  if (!chunk || !("code" in chunk)) throw new Error("[vxrn] production build produced no output");
@@ -507,8 +505,14 @@ function nativeAnimatedGuardPlugin() {
507
505
  const target = "const method = nullthrows(NativeAnimatedModule)[methodName];";
508
506
  if (!code.includes(target)) return;
509
507
  return {
510
- code: code.replace(target, `const method = nullthrows(NativeAnimatedModule)[methodName];
511
- if (typeof method !== 'function') return;`)
508
+ code: code.replace(target, `${target} if (typeof method !== 'function') return;`),
509
+ map: {
510
+ version: 3,
511
+ sources: [id],
512
+ sourcesContent: [code],
513
+ names: [],
514
+ mappings: code.split("\n").map((_, index) => index === 0 ? "AAAA" : "AACA").join(";")
515
+ }
512
516
  };
513
517
  }
514
518
  };
@@ -587,7 +591,7 @@ function cssStubPlugin() {
587
591
  }
588
592
  };
589
593
  }
590
- function vxrnCompilerPlugin(platform, dev, projectRoot = process.cwd()) {
594
+ function vxrnCompilerPlugin(platform, dev, projectRoot = process.cwd(), sourceMaps = false) {
591
595
  let compiler = null;
592
596
  const isRefreshCandidate = id => dev && !id.includes("node_modules") && !id.includes("__virtual-native-entry") && /\.[tj]sx?$/.test(id);
593
597
  return {
@@ -596,33 +600,37 @@ function vxrnCompilerPlugin(platform, dev, projectRoot = process.cwd()) {
596
600
  if (!/\.[cm]?[jt]sx?$/.test(id)) return;
597
601
  if (id.includes("\0") || id.includes("virtual:")) return;
598
602
  const needsRefresh = isRefreshCandidate(id);
599
- try {
600
- if (!compiler) compiler = await import("@vxrn/compiler");
601
- const props = {
602
- id,
603
- code,
604
- projectRoot,
605
- development: dev,
606
- environment: platform,
607
- reactForRNVersion: "19"
603
+ if (!compiler) compiler = await import("@vxrn/compiler");
604
+ const props = {
605
+ id,
606
+ code,
607
+ projectRoot,
608
+ development: dev,
609
+ environment: platform,
610
+ reactForRNVersion: "19"
611
+ };
612
+ let babelOptions = compiler.getBabelOptions(props);
613
+ if (needsRefresh) {
614
+ const existingPlugins = babelOptions?.plugins || [];
615
+ babelOptions = {
616
+ ...babelOptions,
617
+ plugins: [...existingPlugins, ["react-refresh/babel", {
618
+ skipEnvCheck: true,
619
+ refreshReg: "__vxrnRefreshReg",
620
+ refreshSig: "__vxrnRefreshSig"
621
+ }]]
608
622
  };
609
- let babelOptions = compiler.getBabelOptions(props);
610
- if (needsRefresh) {
611
- const existingPlugins = babelOptions?.plugins || [];
612
- babelOptions = {
613
- ...babelOptions,
614
- plugins: [...existingPlugins, ["react-refresh/babel", {
615
- skipEnvCheck: true,
616
- refreshReg: "__vxrnRefreshReg",
617
- refreshSig: "__vxrnRefreshSig"
618
- }]]
619
- };
620
- }
621
- if (!babelOptions) return;
622
- const result = await compiler.transformBabel(id, code, babelOptions);
623
- if (result?.code) {
624
- let out = result.code;
625
- if (needsRefresh) out = `
623
+ }
624
+ if (!babelOptions) return;
625
+ if (sourceMaps) babelOptions = {
626
+ ...babelOptions,
627
+ sourceMaps: true,
628
+ sourceFileName: id
629
+ };
630
+ const result = await compiler.transformBabel(id, code, babelOptions);
631
+ if (result?.code) {
632
+ let out = result.code;
633
+ if (needsRefresh) out = `
626
634
  var __prevRefreshReg = globalThis.$RefreshReg$;
627
635
  var __prevRefreshSig = globalThis.$RefreshSig$;
628
636
  if (globalThis.__ReactRefresh) {
@@ -640,23 +648,16 @@ ${out}
640
648
  globalThis.$RefreshReg$ = __prevRefreshReg;
641
649
  globalThis.$RefreshSig$ = __prevRefreshSig;
642
650
  if (import.meta.hot) {
643
- import.meta.hot.accept(function() {
651
+ import.meta.hot.acceptReactRefresh(function() {
644
652
  if (globalThis.__ReactRefresh) {
645
653
  setTimeout(function() { globalThis.__ReactRefresh.performReactRefresh(); }, 30);
646
654
  }
647
655
  });
648
656
  }
649
657
  `;
650
- return {
651
- code: out
652
- };
653
- }
654
- } catch (err) {
655
- if (dev) console.warn(`[vxrn:compiler] ${id}: ${err.message || err}`);
656
- if (needsRefresh) return {
657
- code: code + `
658
- if (import.meta.hot) { import.meta.hot.accept(); }
659
- `
658
+ return {
659
+ code: out,
660
+ map: sourceMaps ? result.map : void 0
660
661
  };
661
662
  }
662
663
  }
@@ -668,21 +669,17 @@ function flowStripPlugin() {
668
669
  transform: {
669
670
  async handler(code, id) {
670
671
  if (!FLOW_FILE_PATTERN.test(id)) return;
671
- try {
672
- const result = await (await import("fast-flow-transform")).default({
673
- filename: id,
674
- source: code,
675
- sourcemap: true,
676
- dialect: "flow",
677
- format: "pretty"
678
- });
679
- return {
680
- code: result.code,
681
- map: result.map
682
- };
683
- } catch (err) {
684
- console.warn(`[vxrn:flow-strip] ${id}: ${err.message}`);
685
- }
672
+ const result = await (await import("fast-flow-transform")).default({
673
+ filename: id,
674
+ source: code,
675
+ sourcemap: true,
676
+ dialect: "flow",
677
+ format: "pretty"
678
+ });
679
+ return {
680
+ code: result.code,
681
+ map: result.map
682
+ };
686
683
  }
687
684
  }
688
685
  };
@@ -694,37 +691,9 @@ function assetPlugin(opts) {
694
691
  load: {
695
692
  async handler(id) {
696
693
  if (!assetRegex.test(id)) return;
697
- const ext = extname(id).slice(1);
698
- const name = basename(id, `.${ext}`);
699
- const dir = dirname(id);
700
- const relativePath = relative(opts.root, id);
701
- const assetData = {
702
- __packager_asset: true,
703
- name,
704
- type: ext,
705
- scales: [1],
706
- httpServerLocation: "/assets/" + dirname(relativePath).replace(/\\/g, "/"),
707
- fileSystemLocation: dir,
708
- hash: "",
709
- width: void 0,
710
- height: void 0
711
- };
712
- if (["png", "jpg", "jpeg", "gif", "webp", "bmp"].includes(ext)) try {
713
- const {
714
- imageSize
715
- } = await import("image-size");
716
- const dims = imageSize(id);
717
- assetData.width = dims.width;
718
- assetData.height = dims.height;
719
- } catch {}
720
- if (opts.assetsDest) {
721
- const relativeAssetDir = dirname(relativePath).replace(/\\/g, "/");
722
- const assetDestDir = join(opts.assetsDest, "assets", relativeAssetDir);
723
- mkdirSync(assetDestDir, {
724
- recursive: true
725
- });
726
- copyFileSync(id, join(assetDestDir, `${name}.${ext}`));
727
- }
694
+ const assetData = await getNativeAssetData(id, opts.root, opts.platform);
695
+ opts.onAsset?.(assetData);
696
+ if (opts.assetsDest) copyNativeAssetFiles(assetData, opts.assetsDest, opts.platform);
728
697
  return {
729
698
  code: `module.exports = require('react-native/Libraries/Image/AssetRegistry').registerAsset(${JSON.stringify(assetData)});`,
730
699
  moduleType: "js"
@@ -733,7 +702,116 @@ function assetPlugin(opts) {
733
702
  }
734
703
  };
735
704
  }
736
- function hermesCompatSWCPlugin(dev) {
705
+ function createNativeDevAssetRegistry() {
706
+ const assets = /* @__PURE__ */new Map();
707
+ return {
708
+ register(asset) {
709
+ for (const [index, scale] of asset.scales.entries()) {
710
+ const filePath = asset.files[index];
711
+ if (!filePath) continue;
712
+ const fileName = `${asset.name}${scale === 1 ? "" : `@${scale}x`}.${asset.type}`;
713
+ assets.set(`${asset.httpServerLocation}/${fileName}`, {
714
+ filePath,
715
+ hash: asset.hash,
716
+ type: asset.type
717
+ });
718
+ }
719
+ },
720
+ resolve(pathname, hash) {
721
+ const asset = assets.get(pathname);
722
+ if (!asset || hash !== void 0 && hash !== asset.hash) return;
723
+ return asset;
724
+ }
725
+ };
726
+ }
727
+ const NATIVE_IMAGE_EXTS = /* @__PURE__ */new Set(["png", "jpg", "jpeg", "gif", "webp", "bmp", "psd", "svg", "tiff", "ktx"]);
728
+ function parseNativeAssetName(filePath) {
729
+ const type = extname(filePath).slice(1);
730
+ if (!type) return;
731
+ const stem = basename(filePath, `.${type}`);
732
+ const platformMatch = stem.match(/^(.*)\.(ios|android)$/);
733
+ const platform = platformMatch?.[2];
734
+ const scaleMatch = (platformMatch?.[1] ?? stem).match(/^(.+?)(?:@([\d.]+)x)?$/);
735
+ if (!scaleMatch) return;
736
+ const scale = scaleMatch[2] === void 0 ? 1 : Number.parseFloat(scaleMatch[2]);
737
+ if (!Number.isFinite(scale) || scale <= 0) return;
738
+ return {
739
+ name: scaleMatch[1],
740
+ scale,
741
+ platform,
742
+ type
743
+ };
744
+ }
745
+ function getNativeAssetUrlDirectory(root, assetDirectory) {
746
+ const safeDirectory = relative(realpathSync(root), realpathSync(assetDirectory)).replace(/\\/g, "/").split("/").filter(segment => segment && segment !== ".").map(segment => segment === ".." ? "_" : segment).join("/");
747
+ return safeDirectory ? `/assets/${safeDirectory}` : "/assets";
748
+ }
749
+ async function getNativeAssetData(id, root, platform) {
750
+ const requested = parseNativeAssetName(id);
751
+ if (!requested) throw new Error(`[vxrn] invalid native asset path: ${id}`);
752
+ const assetDirectory = dirname(id);
753
+ const filesByScale = /* @__PURE__ */new Map();
754
+ for (const fileName of readdirSync(assetDirectory)) {
755
+ const candidate = parseNativeAssetName(fileName);
756
+ if (!candidate || candidate.name !== requested.name || candidate.type !== requested.type || candidate.platform !== void 0 && candidate.platform !== platform) continue;
757
+ const platformSpecific = candidate.platform === platform;
758
+ const existing = filesByScale.get(candidate.scale);
759
+ if (!existing || platformSpecific && !existing.platformSpecific) filesByScale.set(candidate.scale, {
760
+ file: join(assetDirectory, fileName),
761
+ platformSpecific
762
+ });
763
+ }
764
+ const scales = [...filesByScale.keys()].sort((a, b) => a - b);
765
+ const files = scales.map(scale => filesByScale.get(scale).file);
766
+ if (files.length === 0) throw new Error(`[vxrn] native asset has no files for ${platform}: ${id}`);
767
+ const hash = createHash("md5");
768
+ for (const file of files) hash.update(readFileSync(file));
769
+ const assetData = {
770
+ __packager_asset: true,
771
+ name: requested.name,
772
+ type: requested.type,
773
+ scales,
774
+ files,
775
+ httpServerLocation: getNativeAssetUrlDirectory(root, assetDirectory),
776
+ fileSystemLocation: assetDirectory,
777
+ hash: hash.digest("hex")
778
+ };
779
+ if (NATIVE_IMAGE_EXTS.has(requested.type)) try {
780
+ const {
781
+ imageSize
782
+ } = await import("image-size");
783
+ const dims = imageSize(files[0]);
784
+ assetData.width = dims.width === void 0 ? void 0 : dims.width / scales[0];
785
+ assetData.height = dims.height === void 0 ? void 0 : dims.height / scales[0];
786
+ } catch {}
787
+ return assetData;
788
+ }
789
+ function getIOSAssetScales(scales) {
790
+ const supported = scales.filter(scale => scale === 1 || scale === 2 || scale === 3);
791
+ if (supported.length > 0 || scales.length === 0) return supported;
792
+ return [scales.find(scale => scale > 3) ?? scales[scales.length - 1]];
793
+ }
794
+ function getAndroidAssetDirectory(type, scale) {
795
+ if (!(/* @__PURE__ */new Set(["gif", "heic", "heif", "jpeg", "jpg", "ktx", "png", "webp", "xml"])).has(type)) return "raw";
796
+ const density = (/* @__PURE__ */new Map([[.75, "ldpi"], [1, "mdpi"], [1.5, "hdpi"], [2, "xhdpi"], [3, "xxhdpi"], [4, "xxxhdpi"]])).get(scale);
797
+ if (density) return `drawable-${density}`;
798
+ return `drawable-${Math.round(scale * 160)}dpi`;
799
+ }
800
+ function getAndroidAssetName(asset) {
801
+ return `${asset.httpServerLocation}/${asset.name}`.toLowerCase().replace(/^\//, "").replace(/\//g, "_").replace(/([^a-z0-9_])/g, "").replace(/^assets_/, "");
802
+ }
803
+ function copyNativeAssetFiles(asset, assetsDest, platform) {
804
+ const validIOSScales = new Set(getIOSAssetScales(asset.scales));
805
+ for (const [index, scale] of asset.scales.entries()) {
806
+ if (platform === "ios" && !validIOSScales.has(scale)) continue;
807
+ const destination = platform === "android" ? join(assetsDest, getAndroidAssetDirectory(asset.type, scale), `${getAndroidAssetName(asset)}.${asset.type}`) : join(assetsDest, asset.httpServerLocation.slice(1), `${asset.name}${scale === 1 ? "" : `@${scale}x`}.${asset.type}`);
808
+ mkdirSync(dirname(destination), {
809
+ recursive: true
810
+ });
811
+ copyFileSync(asset.files[index], destination);
812
+ }
813
+ }
814
+ function hermesCompatSWCPlugin(dev, sourceMaps = false) {
737
815
  let swc = null;
738
816
  return {
739
817
  name: "vxrn:hermes-compat",
@@ -741,45 +819,47 @@ function hermesCompatSWCPlugin(dev) {
741
819
  if (!/\.[cm]?[jt]sx?$/.test(id)) return;
742
820
  if (id.includes("\0") || id.includes("virtual:")) return;
743
821
  const hasClass = code.includes("class ") || code.includes("class{");
744
- const hasAsync = !dev && code.includes("async ");
745
- if (!hasClass && !hasAsync) return;
822
+ const hasAsync = code.includes("async ");
823
+ const hasBlockScopedLoop = /\bfor\s*\(\s*(?:const|let)\b/.test(code);
824
+ if (!hasClass && !hasAsync && !hasBlockScopedLoop) return;
746
825
  if (code.length > 5e5) return;
747
- try {
748
- if (!swc) swc = await import("@swc/core");
749
- const envIncludes = getHermesSWCIncludes(dev);
750
- return {
751
- code: (await swc.transform(code, {
752
- filename: id,
753
- configFile: false,
754
- swcrc: false,
755
- sourceMaps: false,
756
- inputSourceMap: false,
757
- env: {
758
- targets: {
759
- node: 9999
760
- },
761
- include: envIncludes
762
- },
763
- jsc: {
764
- parser: {
765
- syntax: "typescript",
766
- tsx: true
767
- },
768
- transform: {
769
- react: {
770
- runtime: "preserve"
771
- }
772
- },
773
- externalHelpers: false,
774
- assumptions: {
775
- setPublicClassFields: true,
776
- privateFieldsAsProperties: true
777
- }
778
- },
779
- isModule: !id.endsWith(".cjs")
780
- })).code
781
- };
782
- } catch (err) {}
826
+ if (!swc) swc = await import("@swc/core");
827
+ const envIncludes = getHermesSWCIncludes(dev);
828
+ const result = await swc.transform(code, {
829
+ filename: id,
830
+ configFile: false,
831
+ swcrc: false,
832
+ sourceMaps,
833
+ sourceFileName: sourceMaps ? id : void 0,
834
+ inputSourceMap: false,
835
+ env: {
836
+ targets: {
837
+ node: 9999
838
+ },
839
+ include: envIncludes
840
+ },
841
+ jsc: {
842
+ parser: {
843
+ syntax: "typescript",
844
+ tsx: true
845
+ },
846
+ transform: {
847
+ react: {
848
+ runtime: "preserve"
849
+ }
850
+ },
851
+ externalHelpers: false,
852
+ assumptions: {
853
+ setPublicClassFields: true,
854
+ privateFieldsAsProperties: true
855
+ }
856
+ },
857
+ isModule: !id.endsWith(".cjs")
858
+ });
859
+ return {
860
+ code: result.code,
861
+ map: sourceMaps ? result.map : void 0
862
+ };
783
863
  }
784
864
  };
785
865
  }
@@ -801,6 +881,7 @@ class ReactNativeDevRuntime extends BaseDevRuntime {
801
881
  var runtime = this;
802
882
  var ctx = {
803
883
  acceptCallbacks: [],
884
+ reactRefreshAcceptCallback: null,
804
885
  accept: function(deps, callback) {
805
886
  if (typeof deps === 'function' || !deps) {
806
887
  ctx.acceptCallbacks.push({
@@ -816,6 +897,9 @@ class ReactNativeDevRuntime extends BaseDevRuntime {
816
897
  ctx.acceptCallbacks.push({ deps: deps, fn: callback || function() {} });
817
898
  }
818
899
  },
900
+ acceptReactRefresh: function(callback) {
901
+ ctx.reactRefreshAcceptCallback = callback;
902
+ },
819
903
  invalidate: function() { runtime.requestReload('module invalidated: ' + moduleId); },
820
904
  on: function() {},
821
905
  off: function() {},
@@ -849,9 +933,18 @@ class ReactNativeDevRuntime extends BaseDevRuntime {
849
933
 
850
934
  isSelfAccepted(moduleId) {
851
935
  var ctx = this.moduleHotContexts[moduleId];
852
- return !!(ctx && ctx.acceptCallbacks.some(function(callback) {
936
+ var explicitlyAccepted = !!(ctx && ctx.acceptCallbacks.some(function(callback) {
853
937
  return callback.deps.indexOf(moduleId) !== -1;
854
938
  }));
939
+ if (explicitlyAccepted) return { accepted: true, reactRefresh: false };
940
+ if (!ctx || !ctx.reactRefreshAcceptCallback || !globalThis.__ReactRefresh) {
941
+ return { accepted: false, reactRefresh: false };
942
+ }
943
+ var exports = this.loadExports(moduleId);
944
+ return {
945
+ accepted: ctx.refreshUtils.isReactRefreshBoundary(exports),
946
+ reactRefresh: true
947
+ };
855
948
  }
856
949
 
857
950
  acceptsDependency(parentId, moduleId) {
@@ -866,8 +959,13 @@ class ReactNativeDevRuntime extends BaseDevRuntime {
866
959
  traversed.add(moduleId);
867
960
  updateSet.add(moduleId);
868
961
 
869
- if (this.isSelfAccepted(moduleId)) {
870
- boundaries.push({ boundary: moduleId, acceptedVia: moduleId });
962
+ var selfAcceptance = this.isSelfAccepted(moduleId);
963
+ if (selfAcceptance.accepted) {
964
+ boundaries.push({
965
+ boundary: moduleId,
966
+ acceptedVia: moduleId,
967
+ reactRefresh: selfAcceptance.reactRefresh
968
+ });
871
969
  return true;
872
970
  }
873
971
 
@@ -903,12 +1001,19 @@ class ReactNativeDevRuntime extends BaseDevRuntime {
903
1001
 
904
1002
  var callbacks = boundaries.map(function(item) {
905
1003
  var ctx = this.moduleHotContexts[item.boundary];
1004
+ var selectedCallbacks = ctx ? ctx.acceptCallbacks.filter(function(callback) {
1005
+ return callback.deps.indexOf(item.acceptedVia) !== -1;
1006
+ }) : [];
1007
+ if (item.reactRefresh && ctx && ctx.reactRefreshAcceptCallback) {
1008
+ selectedCallbacks.push({
1009
+ deps: [item.acceptedVia],
1010
+ fn: function() { ctx.reactRefreshAcceptCallback(); }
1011
+ });
1012
+ }
906
1013
  return {
907
1014
  boundary: item.boundary,
908
1015
  acceptedVia: item.acceptedVia,
909
- callbacks: ctx ? ctx.acceptCallbacks.filter(function(callback) {
910
- return callback.deps.indexOf(item.acceptedVia) !== -1;
911
- }) : []
1016
+ callbacks: selectedCallbacks
912
1017
  };
913
1018
  }, this);
914
1019
 
@@ -959,5 +1064,5 @@ class ReactNativeDevRuntime extends BaseDevRuntime {
959
1064
  globalThis.__rolldown_runtime__ = new ReactNativeDevRuntime();
960
1065
  `;
961
1066
  }
962
- export { buildNativeBundle, createNativeDevEngine, getHermesSWCIncludes, getHmrRuntimeSource, getNativeTransformConfig, hermesCompatSWCPlugin, hmrClientNoopPlugin, vxrnCompilerPlugin, wrapNativeBundleModuleScope };
1067
+ export { buildNativeBundle, createNativeDevAssetRegistry, createNativeDevEngine, getHermesSWCIncludes, getHmrRuntimeSource, getNativeAssetData, getNativeTransformConfig, hermesCompatSWCPlugin, hmrClientNoopPlugin, nativeAnimatedGuardPlugin, normalizeNativeCommonJSInterop, vxrnCompilerPlugin, wrapNativeBundleModuleScope };
963
1068
  //# sourceMappingURL=createNativeDevEngine.mjs.map