vxrn 2.13.8 → 2.13.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 (118) hide show
  1. package/dist/cli.mjs +3 -2
  2. package/dist/cli.mjs.map +1 -1
  3. package/dist/cli.native.js +5 -2
  4. package/dist/cli.native.js.map +1 -1
  5. package/dist/config/getBaseViteConfigOnly.mjs +3 -3
  6. package/dist/config/getBaseViteConfigOnly.mjs.map +1 -1
  7. package/dist/config/getBaseViteConfigOnly.native.js +3 -3
  8. package/dist/config/getBaseViteConfigOnly.native.js.map +1 -1
  9. package/dist/config/getOptionsFilled.mjs +24 -2
  10. package/dist/config/getOptionsFilled.mjs.map +1 -1
  11. package/dist/config/getOptionsFilled.native.js +25 -2
  12. package/dist/config/getOptionsFilled.native.js.map +1 -1
  13. package/dist/config/getServerOptionsFilled.mjs +119 -1
  14. package/dist/config/getServerOptionsFilled.mjs.map +1 -1
  15. package/dist/config/getServerOptionsFilled.native.js +137 -1
  16. package/dist/config/getServerOptionsFilled.native.js.map +1 -1
  17. package/dist/exports/build.mjs +5 -5
  18. package/dist/exports/build.mjs.map +1 -1
  19. package/dist/exports/build.native.js +5 -5
  20. package/dist/exports/build.native.js.map +1 -1
  21. package/dist/exports/dev.mjs +4 -6
  22. package/dist/exports/dev.mjs.map +1 -1
  23. package/dist/exports/dev.native.js +6 -6
  24. package/dist/exports/dev.native.js.map +1 -1
  25. package/dist/exports/prebuild.mjs +4 -4
  26. package/dist/exports/prebuild.mjs.map +1 -1
  27. package/dist/exports/prebuild.native.js +4 -4
  28. package/dist/exports/prebuild.native.js.map +1 -1
  29. package/dist/exports/prebuildWithoutExpo.mjs +95 -88
  30. package/dist/exports/prebuildWithoutExpo.mjs.map +1 -1
  31. package/dist/exports/prebuildWithoutExpo.native.js +95 -88
  32. package/dist/exports/prebuildWithoutExpo.native.js.map +1 -1
  33. package/dist/plugins/autoDepOptimizePlugin.mjs +3 -3
  34. package/dist/plugins/autoDepOptimizePlugin.mjs.map +1 -1
  35. package/dist/plugins/autoDepOptimizePlugin.native.js +3 -3
  36. package/dist/plugins/autoDepOptimizePlugin.native.js.map +1 -1
  37. package/dist/plugins/reactNativeDevAssetPlugin.mjs +4 -4
  38. package/dist/plugins/reactNativeDevAssetPlugin.mjs.map +1 -1
  39. package/dist/plugins/reactNativeDevAssetPlugin.native.js +4 -4
  40. package/dist/plugins/reactNativeDevAssetPlugin.native.js.map +1 -1
  41. package/dist/rn-commands/bundle/buildBundle.mjs +18 -5
  42. package/dist/rn-commands/bundle/buildBundle.mjs.map +1 -1
  43. package/dist/rn-commands/bundle/buildBundle.native.js +18 -5
  44. package/dist/rn-commands/bundle/buildBundle.native.js.map +1 -1
  45. package/dist/user-interface/index.mjs +1 -1
  46. package/dist/user-interface/index.mjs.map +1 -1
  47. package/dist/user-interface/index.native.js +1 -1
  48. package/dist/user-interface/index.native.js.map +1 -1
  49. package/dist/utils/env-editor.mjs +198 -0
  50. package/dist/utils/env-editor.mjs.map +1 -0
  51. package/dist/utils/env-editor.native.js +266 -0
  52. package/dist/utils/env-editor.native.js.map +1 -0
  53. package/dist/utils/fs.mjs +48 -0
  54. package/dist/utils/fs.mjs.map +1 -0
  55. package/dist/utils/fs.native.js +48 -0
  56. package/dist/utils/fs.native.js.map +1 -0
  57. package/dist/utils/getVitePath.mjs +2 -2
  58. package/dist/utils/getVitePath.mjs.map +1 -1
  59. package/dist/utils/getVitePath.native.js +2 -2
  60. package/dist/utils/getVitePath.native.js.map +1 -1
  61. package/dist/utils/patches.mjs +38 -29
  62. package/dist/utils/patches.mjs.map +1 -1
  63. package/dist/utils/patches.native.js +38 -29
  64. package/dist/utils/patches.native.js.map +1 -1
  65. package/dist/utils/scanDepsToOptimize.mjs +12 -11
  66. package/dist/utils/scanDepsToOptimize.mjs.map +1 -1
  67. package/dist/utils/scanDepsToOptimize.native.js +12 -11
  68. package/dist/utils/scanDepsToOptimize.native.js.map +1 -1
  69. package/dist/utils/state.mjs +5 -5
  70. package/dist/utils/state.mjs.map +1 -1
  71. package/dist/utils/state.native.js +5 -5
  72. package/dist/utils/state.native.js.map +1 -1
  73. package/dist/utils/utils.mjs +4 -3
  74. package/dist/utils/utils.mjs.map +1 -1
  75. package/dist/utils/utils.native.js +4 -3
  76. package/dist/utils/utils.native.js.map +1 -1
  77. package/package.json +12 -18
  78. package/src/cli.ts +5 -4
  79. package/src/config/getBaseViteConfigOnly.ts +3 -3
  80. package/src/config/getOptionsFilled.ts +35 -2
  81. package/src/config/getServerOptionsFilled.test.ts +37 -0
  82. package/src/config/getServerOptionsFilled.ts +57 -1
  83. package/src/exports/build.ts +5 -5
  84. package/src/exports/clean.test.ts +1 -1
  85. package/src/exports/dev.ts +5 -6
  86. package/src/exports/prebuild.ts +4 -4
  87. package/src/exports/prebuildWithoutExpo.ts +102 -89
  88. package/src/plugins/autoDepOptimizePlugin.ts +3 -3
  89. package/src/plugins/reactNativeDevAssetPlugin.ts +4 -4
  90. package/src/rn-commands/bundle/buildBundle.ts +15 -5
  91. package/src/user-interface/index.ts +1 -1
  92. package/src/utils/env-editor.LICENSE +9 -0
  93. package/src/utils/env-editor.ts +214 -0
  94. package/src/utils/fs.ts +59 -0
  95. package/src/utils/getVitePath.ts +2 -2
  96. package/src/utils/patches.test.ts +36 -36
  97. package/src/utils/patches.ts +39 -27
  98. package/src/utils/scanDepsToOptimize.test.ts +2 -2
  99. package/src/utils/scanDepsToOptimize.ts +6 -5
  100. package/src/utils/state.ts +5 -7
  101. package/src/utils/utils.ts +5 -5
  102. package/types/config/getOptionsFilled.d.ts +9 -2
  103. package/types/config/getOptionsFilled.d.ts.map +1 -1
  104. package/types/config/getServerOptionsFilled.d.ts.map +1 -1
  105. package/types/exports/build.d.ts +1 -1
  106. package/types/exports/dev.d.ts.map +1 -1
  107. package/types/exports/prebuildWithoutExpo.d.ts.map +1 -1
  108. package/types/rn-commands/bundle/buildBundle.d.ts.map +1 -1
  109. package/types/user-interface/index.d.ts.map +1 -1
  110. package/types/utils/env-editor.d.ts +9 -0
  111. package/types/utils/env-editor.d.ts.map +1 -0
  112. package/types/utils/fs.d.ts +9 -0
  113. package/types/utils/fs.d.ts.map +1 -0
  114. package/types/utils/patches.d.ts.map +1 -1
  115. package/types/utils/scanDepsToOptimize.d.ts.map +1 -1
  116. package/types/utils/state.d.ts.map +1 -1
  117. package/types/utils/utils.d.ts +2 -2
  118. package/types/utils/utils.d.ts.map +1 -1
@@ -1,8 +1,9 @@
1
1
  import { createHash } from 'node:crypto'
2
+ import { existsSync } from 'node:fs'
3
+ import { readFile } from 'node:fs/promises'
2
4
  import { createRequire } from 'node:module'
3
5
  import { tmpdir } from 'node:os'
4
- import { join } from 'node:path'
5
- import { readPackageJSON } from 'pkg-types'
6
+ import { basename, dirname, join, resolve } from 'node:path'
6
7
  import type { Mode, VXRNOptions } from '../types'
7
8
  import { getServerOptionsFilled } from './getServerOptionsFilled'
8
9
  import { readState } from '../utils/state'
@@ -38,6 +39,14 @@ const require = createRequire(import.meta.url)
38
39
  * * Over time, we can progressively eliminate dependencies on
39
40
  * `VXRNOptionsFilled` across the codebase.
40
41
  */
42
+ export type PackageJSON = {
43
+ name?: string
44
+ version?: string
45
+ dependencies?: Record<string, string>
46
+ devDependencies?: Record<string, string>
47
+ [key: string]: any
48
+ }
49
+
41
50
  export type VXRNOptionsFilled = Awaited<ReturnType<typeof fillOptions>>
42
51
 
43
52
  let optionsFilled: VXRNOptionsFilled | null = null
@@ -115,6 +124,30 @@ export function getOptionsFilled() {
115
124
  return optionsFilled
116
125
  }
117
126
 
127
+ async function readPackageJSON(cwd = process.cwd()): Promise<PackageJSON> {
128
+ // nearest package.json walking up, stopping above the shallowest
129
+ // node_modules segment, matching the pkg-types lookup this replaces
130
+ let stopDir: string | null = null
131
+ for (let dir = resolve(cwd); ; ) {
132
+ if (basename(dir) === 'node_modules') stopDir = dir
133
+ const parent = dirname(dir)
134
+ if (parent === dir) break
135
+ dir = parent
136
+ }
137
+ let dir = resolve(cwd)
138
+ for (;;) {
139
+ const candidate = join(dir, 'package.json')
140
+ if (existsSync(candidate)) {
141
+ return JSON.parse(await readFile(candidate, 'utf8'))
142
+ }
143
+ if (stopDir && resolve(dir) === stopDir) break
144
+ const parent = dirname(dir)
145
+ if (parent === dir) break
146
+ dir = parent
147
+ }
148
+ throw new Error(`Cannot find matching package.json in ${cwd} or parent directories`)
149
+ }
150
+
118
151
  function hashString(str: string): string {
119
152
  const hash = createHash('sha256') // Create a hash object with the desired algorithm (e.g., 'sha256')
120
153
  hash.update(str) // Update the hash content with the input string
@@ -1,6 +1,22 @@
1
+ import { createServer } from 'node:net'
1
2
  import { describe, expect, test } from 'vitest'
2
3
  import { getServerOptionsFilled } from './getServerOptionsFilled'
3
4
 
5
+ async function occupyPort(): Promise<{ port: number; close: () => Promise<void> }> {
6
+ const server = createServer()
7
+ await new Promise<void>((resolve) => {
8
+ server.listen({ port: 0, host: '0.0.0.0' }, () => resolve())
9
+ })
10
+ const port = (server.address() as { port: number }).port
11
+ return {
12
+ port,
13
+ close: () =>
14
+ new Promise<void>((resolve, reject) => {
15
+ server.close((err) => (err ? reject(err) : resolve()))
16
+ }),
17
+ }
18
+ }
19
+
4
20
  describe('getServerOptionsFilled', () => {
5
21
  test('uses the configured TLS protocol in the public server URL', async () => {
6
22
  const server = await getServerOptionsFilled(
@@ -21,4 +37,25 @@ describe('getServerOptionsFilled', () => {
21
37
  key: 'private-key',
22
38
  })
23
39
  })
40
+
41
+ test('resolves an explicitly requested free port', async () => {
42
+ const occupied = await occupyPort()
43
+ const port = occupied.port
44
+ await occupied.close()
45
+
46
+ const server = await getServerOptionsFilled({ port }, 'dev')
47
+
48
+ expect(server.port).toBe(port)
49
+ })
50
+
51
+ test('rejects an explicitly requested port that is already in use', async () => {
52
+ const occupied = await occupyPort()
53
+ try {
54
+ await expect(getServerOptionsFilled({ port: occupied.port }, 'dev')).rejects.toThrow(
55
+ `Port ${occupied.port} is already in use`
56
+ )
57
+ } finally {
58
+ await occupied.close()
59
+ }
60
+ })
24
61
  })
@@ -1,6 +1,62 @@
1
- import { getPort } from 'get-port-please'
1
+ import { createServer } from 'node:net'
2
2
  import type { Mode, VXRNOptions, VXRNServeOptionsFilled } from '../types'
3
3
 
4
+ // ports browsers refuse to connect to, so binding one would serve nothing.
5
+ // same list get-port-please filters; vxrn's default ranges never hit it.
6
+ const UNSAFE_PORTS = new Set([
7
+ 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 69, 77, 79, 87,
8
+ 95, 101, 102, 103, 104, 109, 110, 111, 113, 115, 117, 119, 123, 135, 137, 139,
9
+ 143, 161, 179, 389, 427, 465, 512, 513, 514, 515, 526, 530, 531, 532, 540, 548,
10
+ 554, 556, 563, 587, 601, 636, 989, 990, 993, 995, 1719, 1720, 1723, 2049, 3659,
11
+ 4045, 5060, 5061, 6000, 6566, 6665, 6666, 6667, 6668, 6669, 6697, 10080,
12
+ ])
13
+
14
+ const HOSTNAME_RE = /^(?!-)[\d.:A-Za-z-]{1,63}(?<!-)$/
15
+
16
+ function tryPort(port: number, host: string | undefined): Promise<number | false> {
17
+ return new Promise((resolve) => {
18
+ const server = createServer()
19
+ server.unref()
20
+ server.on('error', () => {
21
+ resolve(false)
22
+ })
23
+ server.listen({ port, host }, () => {
24
+ const bound = (server.address() as { port: number }).port
25
+ server.close(() => {
26
+ resolve(!UNSAFE_PORTS.has(bound) && bound)
27
+ })
28
+ })
29
+ })
30
+ }
31
+
32
+ async function getPort({
33
+ port,
34
+ portRange,
35
+ host,
36
+ }: {
37
+ port: number
38
+ portRange: [number, number]
39
+ host: string | undefined
40
+ }): Promise<number> {
41
+ if (host && !HOSTNAME_RE.test(host)) {
42
+ host = '127.0.0.1'
43
+ }
44
+ const ports = [port]
45
+ for (let p = portRange[0]; p <= portRange[1]; p++) {
46
+ ports.push(p)
47
+ }
48
+ for (const p of ports) {
49
+ if (!p || UNSAFE_PORTS.has(p)) continue
50
+ const found = await tryPort(p, host)
51
+ if (found) return found
52
+ }
53
+ const random = await tryPort(0, host)
54
+ if (random) return random
55
+ throw new Error(
56
+ `Unable to find an available port ${host ? `on host ${JSON.stringify(host)}` : 'on any host'} (tried ${portRange.join('-')})`
57
+ )
58
+ }
59
+
4
60
  export async function getServerOptionsFilled(
5
61
  serverOptions: VXRNOptions['server'],
6
62
  mode: Mode
@@ -1,6 +1,6 @@
1
- import FSExtra from 'fs-extra'
2
- import { rm } from 'node:fs/promises'
1
+ import { readFile, rm, writeFile } from 'node:fs/promises'
3
2
  import { sep } from 'node:path'
3
+ import { readJSON } from '../utils/fs'
4
4
  import type { OutputAsset, OutputChunk, RolldownOutput } from 'rolldown'
5
5
  import {
6
6
  loadConfigFromFile,
@@ -355,11 +355,11 @@ export const build = async (optionsIn: VXRNOptions, buildArgs: BuildArgs = {}) =
355
355
  }
356
356
 
357
357
  if (serverOptions !== false) {
358
- clientManifest = await FSExtra.readJSON(`${outDir}/client/.vite/manifest.json`)
358
+ clientManifest = await readJSON(`${outDir}/client/.vite/manifest.json`)
359
359
 
360
360
  // temp fix - react native web is importing non-existent react 19 apis
361
- const old = await FSExtra.readFile(serverEntry, 'utf-8')
362
- await FSExtra.writeFile(
361
+ const old = await readFile(serverEntry, 'utf-8')
362
+ await writeFile(
363
363
  serverEntry,
364
364
  old
365
365
  .replace(
@@ -107,7 +107,7 @@ async function tryConnect(port: number): Promise<Socket | undefined> {
107
107
  }
108
108
 
109
109
  // outermost node_modules above the test, so a bundled worker's bare
110
- // imports (vite, fs-extra, ...) resolve the way the workspace lays out
110
+ // imports (vite, picocolors, ...) resolve the way the workspace lays out
111
111
  function outermostNodeModules(from: string): string {
112
112
  let dir = from
113
113
  let found: string | undefined
@@ -1,11 +1,11 @@
1
1
  import { execFileSync } from 'node:child_process'
2
- import FSExtra from 'fs-extra'
2
+ import { mkdir } from 'node:fs/promises'
3
3
  import colors from 'picocolors'
4
4
  import { debounce } from 'perfect-debounce'
5
5
  import { normalizePath, type PluginOption, type ViteDevServer } from 'vite'
6
6
  import type { VXRNOptions } from '../types'
7
7
 
8
- const { ensureDir } = FSExtra
8
+ const ensureDir = (dir: string) => mkdir(dir, { recursive: true })
9
9
 
10
10
  function readParentPid(pid: number): number | undefined {
11
11
  try {
@@ -31,19 +31,18 @@ function processExists(pid: number): boolean {
31
31
 
32
32
  // Exit if we become orphaned (parent dies). `bun run` stays between One and
33
33
  // its launcher, so monitor the launcher's PID too instead of only Bun's PID.
34
+ // PID 1 is a live container or machine init, not a dead parent.
34
35
  function setupOrphanDetection() {
35
36
  if (process.platform === 'win32') return
36
37
 
37
38
  const initialPpid = process.ppid
38
39
  const launcherPid = readParentPid(initialPpid)
39
- if (initialPpid === 1 || launcherPid === 1) {
40
- process.exit(0)
41
- }
40
+ const watchedLauncher = launcherPid !== undefined && launcherPid > 1 ? launcherPid : undefined
42
41
 
43
42
  const interval = setInterval(() => {
44
43
  if (
45
44
  process.ppid !== initialPpid ||
46
- (launcherPid !== undefined && !processExists(launcherPid))
45
+ (watchedLauncher !== undefined && !processExists(watchedLauncher))
47
46
  ) {
48
47
  process.exit(0)
49
48
  }
@@ -1,5 +1,5 @@
1
1
  import { detectPackageManager, type PackageManagerName } from '@vxrn/utils'
2
- import FSExtra from 'fs-extra'
2
+ import { readFile, writeFile } from 'node:fs/promises'
3
3
  import path from 'node:path'
4
4
  import { fillOptions } from '../config/getOptionsFilled'
5
5
  import { applyBuiltInPatches } from '../utils/patches'
@@ -38,7 +38,7 @@ export const prebuild = async ({
38
38
 
39
39
  try {
40
40
  const packageJsonPath = path.join(root, 'package.json')
41
- const packageJsonContents = await FSExtra.readFile(packageJsonPath, 'utf8')
41
+ const packageJsonContents = await readFile(packageJsonPath, 'utf8')
42
42
  const { devDependencies } = JSON.parse(packageJsonContents)
43
43
  if (!devDependencies?.['@react-native-community/template']) {
44
44
  const installCommand: `${PackageManagerName} ${'add' | 'install'} ${'-D'} ${'@react-native-community/template'}` =
@@ -126,9 +126,9 @@ export async function replaceInUTF8File(
126
126
  findThis: string,
127
127
  replaceWith: string
128
128
  ) {
129
- const fileContent = await FSExtra.readFile(filePath, 'utf8')
129
+ const fileContent = await readFile(filePath, 'utf8')
130
130
  const replacedFileContent = fileContent.replace(findThis, replaceWith)
131
131
  if (replacedFileContent !== fileContent) {
132
- await FSExtra.writeFile(filePath, replacedFileContent, 'utf8')
132
+ await writeFile(filePath, replacedFileContent, 'utf8')
133
133
  }
134
134
  }