vxrn 2.0.0-beta.58.1 → 2.0.0-beta.59.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.
@@ -0,0 +1,82 @@
1
+ const fs = require('node:fs')
2
+ const { createRequire } = require('node:module')
3
+ const path = require('node:path')
4
+ const nativeProjectPatches = require('./native-project-patches.cjs')
5
+
6
+ module.exports = function withVxrn(config) {
7
+ const projectRoot = config?._internal?.projectRoot
8
+ if (!projectRoot) {
9
+ throw new Error('[vxrn/expo-plugin] Expo config is missing _internal.projectRoot')
10
+ }
11
+
12
+ const projectRequire = createRequire(path.join(projectRoot, 'package.json'))
13
+ const {
14
+ withAppBuildGradle,
15
+ withDangerousMod,
16
+ withMainActivity,
17
+ withPlugins,
18
+ withXcodeProject,
19
+ } = projectRequire('@expo/config-plugins')
20
+
21
+ return withPlugins(config, [
22
+ [
23
+ withXcodeProject,
24
+ (nextConfig) => {
25
+ const phase = nextConfig.modResults.buildPhaseObject(
26
+ 'PBXShellScriptBuildPhase',
27
+ 'Bundle React Native code and images'
28
+ )
29
+ if (!phase) return nextConfig
30
+
31
+ let script = JSON.parse(phase.shellScript)
32
+ script =
33
+ nativeProjectPatches.removeExpoDefaultsFromBundleReactNativeShellScript(script)
34
+ script = nativeProjectPatches.addSetCliPathToBundleReactNativeShellScript(script)
35
+ script = nativeProjectPatches.addPodHermescToBundleReactNativeShellScript(script)
36
+ script = nativeProjectPatches.addDepsPatchToBundleReactNativeShellScript(script)
37
+ phase.shellScript = JSON.stringify(script)
38
+ return nextConfig
39
+ },
40
+ ],
41
+ [
42
+ withAppBuildGradle,
43
+ (nextConfig) => {
44
+ let contents = nativeProjectPatches.replaceAppBuildGradleReactBlock(
45
+ nextConfig.modResults.contents
46
+ )
47
+ contents = nativeProjectPatches.addDepsPatchToAppBuildGradle(contents)
48
+ nextConfig.modResults.contents = contents
49
+ return nextConfig
50
+ },
51
+ ],
52
+ [
53
+ withMainActivity,
54
+ (nextConfig) => {
55
+ nextConfig.modResults.contents = nativeProjectPatches.addReactNativeScreensFix(
56
+ nextConfig.modResults.contents
57
+ )
58
+ return nextConfig
59
+ },
60
+ ],
61
+ [
62
+ withDangerousMod,
63
+ [
64
+ 'ios',
65
+ (nextConfig) => {
66
+ const podfilePath = path.join(
67
+ nextConfig.modRequest.platformProjectRoot,
68
+ 'Podfile'
69
+ )
70
+ if (!fs.existsSync(podfilePath)) return nextConfig
71
+
72
+ let podfile = fs.readFileSync(podfilePath, 'utf8')
73
+ podfile = nativeProjectPatches.injectFmtCxx17FixIntoPodfile(podfile)
74
+ podfile = nativeProjectPatches.injectHermesMinificationPatchIntoPodfile(podfile)
75
+ podfile = nativeProjectPatches.injectReactNativeScreensGammaIntoPodfile(podfile)
76
+ fs.writeFileSync(podfilePath, podfile, 'utf8')
77
+ return nextConfig
78
+ },
79
+ ],
80
+ ],
81
+ ])
82
+ }
@@ -117,6 +117,17 @@ function injectReactNativeScreensGammaIntoPodfile(podfile) {
117
117
  return `${RNS_SCREENS_GAMMA_MARKER}\nENV['RNS_GAMMA_ENABLED'] ||= '1'\n${podfile}`
118
118
  }
119
119
 
120
+ /**
121
+ * Expo's bundle phase defaults to Expo CLI's export:embed command. One routes
122
+ * the React Native bundle command through react-native.config.cjs instead.
123
+ */
124
+ function removeExpoDefaultsFromBundleReactNativeShellScript(input) {
125
+ const cliPath = /if \[\[ -z "\$CLI_PATH" \]\]; then[\s\S]*?fi\n?/g
126
+ const bundleCommand = /if \[\[ -z "\$BUNDLE_COMMAND" \]\]; then[\s\S]*?fi\n?/g
127
+
128
+ return input.replace(cliPath, '').replace(bundleCommand, '')
129
+ }
130
+
120
131
  /**
121
132
  * replace android/app/build.gradle's react block with one's generated settings.
122
133
  */
@@ -130,9 +141,7 @@ function replaceAppBuildGradleReactBlock(appBuildGradleContents) {
130
141
  reactBlockStartIndex
131
142
 
132
143
  if (reactBlockStartIndex === -1 || reactBlockEndIndex === -1) {
133
- console.warn(
134
- '[vxrn] failed to patch Android app/build.gradle: react block not found'
135
- )
144
+ console.warn('[vxrn] failed to patch Android app/build.gradle: react block not found')
136
145
  return appBuildGradleContents
137
146
  }
138
147
 
@@ -514,4 +523,15 @@ function addReactNativeScreensFix(input) {
514
523
  return input
515
524
  }
516
525
 
517
- module.exports = { addSetCliPathToBundleReactNativeShellScript, addPodHermescToBundleReactNativeShellScript, addDepsPatchToBundleReactNativeShellScript, injectFmtCxx17FixIntoPodfile, injectHermesMinificationPatchIntoPodfile, injectReactNativeScreensGammaIntoPodfile, replaceAppBuildGradleReactBlock, addDepsPatchToAppBuildGradle, addReactNativeScreensFix }
526
+ module.exports = {
527
+ addSetCliPathToBundleReactNativeShellScript,
528
+ addPodHermescToBundleReactNativeShellScript,
529
+ addDepsPatchToBundleReactNativeShellScript,
530
+ injectFmtCxx17FixIntoPodfile,
531
+ injectHermesMinificationPatchIntoPodfile,
532
+ injectReactNativeScreensGammaIntoPodfile,
533
+ removeExpoDefaultsFromBundleReactNativeShellScript,
534
+ replaceAppBuildGradleReactBlock,
535
+ addDepsPatchToAppBuildGradle,
536
+ addReactNativeScreensFix,
537
+ }
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "vxrn",
3
- "version": "2.0.0-beta.58.1",
3
+ "version": "2.0.0-beta.59.1",
4
4
  "sideEffects": false,
5
5
  "type": "module",
6
6
  "exports": {
7
7
  "./package.json": "./package.json",
8
8
  "./react-native-commands": "./react-native-commands.cjs",
9
+ "./expo-plugin": "./expo-plugin.cjs",
9
10
  ".": {
10
11
  "types": "./types/index.d.ts",
11
12
  "default": "./dist/index.mjs"
@@ -43,6 +44,7 @@
43
44
  "patches",
44
45
  "dist",
45
46
  "run.mjs",
47
+ "expo-plugin.cjs",
46
48
  "native-project-patches.cjs",
47
49
  "react-native-commands.cjs"
48
50
  ],
@@ -62,16 +64,16 @@
62
64
  "@react-native/codegen": "^0.87.1",
63
65
  "@react-native-community/cli-config": "^20.2.0",
64
66
  "@react-native/dev-middleware": "^0.87.1",
65
- "@vxrn/compiler": "2.0.0-beta.58.1",
66
- "@vxrn/debug": "2.0.0-beta.58.1",
67
- "@vxrn/query-string": "2.0.0-beta.58.1",
68
- "@vxrn/resolve": "2.0.0-beta.58.1",
69
- "@vxrn/safe-area": "2.0.0-beta.58.1",
70
- "@vxrn/url-parse": "2.0.0-beta.58.1",
71
- "@vxrn/utils": "2.0.0-beta.58.1",
72
- "@vxrn/vendor": "2.0.0-beta.58.1",
73
- "@vxrn/vite-flow": "2.0.0-beta.58.1",
74
- "@vxrn/vite-plugin-metro": "2.0.0-beta.58.1",
67
+ "@vxrn/compiler": "2.0.0-beta.59.1",
68
+ "@vxrn/debug": "2.0.0-beta.59.1",
69
+ "@vxrn/query-string": "2.0.0-beta.59.1",
70
+ "@vxrn/resolve": "2.0.0-beta.59.1",
71
+ "@vxrn/safe-area": "2.0.0-beta.59.1",
72
+ "@vxrn/url-parse": "2.0.0-beta.59.1",
73
+ "@vxrn/utils": "2.0.0-beta.59.1",
74
+ "@vxrn/vendor": "2.0.0-beta.59.1",
75
+ "@vxrn/vite-flow": "2.0.0-beta.59.1",
76
+ "@vxrn/vite-plugin-metro": "2.0.0-beta.59.1",
75
77
  "citty": "^0.1.6",
76
78
  "dotenv": "^17.2.1",
77
79
  "dotenv-expand": "^12.0.2",
@@ -0,0 +1,135 @@
1
+ import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
2
+ import { createRequire } from 'node:module'
3
+ import { tmpdir } from 'node:os'
4
+ import { join } from 'node:path'
5
+ import { afterEach, describe, expect, it } from 'vitest'
6
+ import withVxrn from '../../expo-plugin.cjs'
7
+
8
+ const require = createRequire(import.meta.url)
9
+ const patches = require('../../native-project-patches.cjs')
10
+ const temporaryDirectories = []
11
+
12
+ afterEach(() => {
13
+ for (const directory of temporaryDirectories.splice(0)) {
14
+ rmSync(directory, { recursive: true, force: true })
15
+ }
16
+ })
17
+
18
+ const expoBundlePhase = [
19
+ 'if [[ -z "$CLI_PATH" ]]; then',
20
+ ' export CLI_PATH="expo-cli"',
21
+ 'fi',
22
+ 'if [[ -z "$BUNDLE_COMMAND" ]]; then',
23
+ ' export BUNDLE_COMMAND="export:embed"',
24
+ 'fi',
25
+ '"`"$NODE_BINARY" --print "require(\'path\').dirname(require.resolve(\'react-native/package.json\')) + \'/scripts/react-native-xcode.sh\'"`"',
26
+ ].join('\n')
27
+
28
+ const podfile = `
29
+ target 'TestApp' do
30
+ post_install do |installer|
31
+ react_native_post_install(installer)
32
+ end
33
+ end
34
+ `
35
+
36
+ describe('vxrn Expo project patches', () => {
37
+ it('replaces Expo bundle defaults with One bundle configuration', () => {
38
+ let result =
39
+ patches.removeExpoDefaultsFromBundleReactNativeShellScript(expoBundlePhase)
40
+ result = patches.addSetCliPathToBundleReactNativeShellScript(result)
41
+ result = patches.addPodHermescToBundleReactNativeShellScript(result)
42
+ result = patches.addDepsPatchToBundleReactNativeShellScript(result)
43
+
44
+ expect(result).not.toContain('export:embed')
45
+ expect(result).toContain('[vxrn/one] React Native now defaults CLI_PATH')
46
+ expect(result).toContain('[vxrn/one] use the hermes-engine pod')
47
+ expect(result).toContain('[vxrn/one] ensure patches are applied')
48
+ expect(result.indexOf('export CLI_PATH=')).toBeLessThan(
49
+ result.indexOf('react-native-xcode.sh')
50
+ )
51
+ })
52
+
53
+ it('replaces the Expo Android react block and applies build patches', () => {
54
+ const input = `react {\n entryFile = file("../index.js")\n}\n`
55
+ let result = patches.replaceAppBuildGradleReactBlock(input)
56
+ result = patches.addDepsPatchToAppBuildGradle(result)
57
+
58
+ expect(result).toContain('cliFile = new File(')
59
+ expect(result).toContain('autolinkLibrariesWithApp()')
60
+ expect(result).not.toContain('entryFile = file("../index.js")')
61
+ expect(result).toContain('[vxrn/one] ensure patches are applied')
62
+ })
63
+
64
+ it('applies the shared Podfile patches idempotently', () => {
65
+ let once = patches.injectFmtCxx17FixIntoPodfile(podfile)
66
+ once = patches.injectHermesMinificationPatchIntoPodfile(once)
67
+ once = patches.injectReactNativeScreensGammaIntoPodfile(once)
68
+ let twice = patches.injectFmtCxx17FixIntoPodfile(once)
69
+ twice = patches.injectHermesMinificationPatchIntoPodfile(twice)
70
+ twice = patches.injectReactNativeScreensGammaIntoPodfile(twice)
71
+
72
+ expect(twice).toBe(once)
73
+ expect(once).toContain('[vxrn/one] fmt c++17 fix')
74
+ expect(once).toContain('[vxrn/one] minify iOS Hermes Release bundle input')
75
+ expect(once).toContain("ENV['RNS_GAMMA_ENABLED'] ||= '1'")
76
+ })
77
+
78
+ it('applies the shared Android screens fix', () => {
79
+ const result = patches.addReactNativeScreensFix(`
80
+ package dev.one.test
81
+ import com.facebook.react.ReactActivity
82
+ class MainActivity : ReactActivity() {
83
+ }
84
+ `)
85
+
86
+ expect(result).toContain('RNScreensFragmentFactory')
87
+ expect(result).toContain('super.onCreate(null)')
88
+ })
89
+ })
90
+
91
+ describe('vxrn/expo-plugin', () => {
92
+ it('resolves Expo config plugins from the app and binds the dangerous mod', async () => {
93
+ const projectRoot = mkdtempSync(join(tmpdir(), 'vxrn-expo-plugin-'))
94
+ temporaryDirectories.push(projectRoot)
95
+ const iosRoot = join(projectRoot, 'ios')
96
+ mkdirSync(iosRoot, { recursive: true })
97
+ writeFileSync(join(iosRoot, 'Podfile'), podfile)
98
+ writeFileSync(join(projectRoot, 'package.json'), '{"private":true}')
99
+ const workspaceRequire = createRequire(import.meta.url)
100
+ const configPluginsRoot = workspaceRequire.resolve(
101
+ '@expo/config-plugins/package.json'
102
+ )
103
+ const scopedDirectory = join(projectRoot, 'node_modules', '@expo')
104
+ mkdirSync(scopedDirectory, { recursive: true })
105
+ const packageDirectory = configPluginsRoot.slice(0, -'/package.json'.length)
106
+ await import('node:fs/promises').then(({ symlink }) =>
107
+ symlink(packageDirectory, join(scopedDirectory, 'config-plugins'), 'dir')
108
+ )
109
+
110
+ const config = withVxrn({
111
+ name: 'TestApp',
112
+ slug: 'test-app',
113
+ ios: { bundleIdentifier: 'dev.one.test' },
114
+ android: { package: 'dev.one.test' },
115
+ _internal: { projectRoot },
116
+ })
117
+ await config.mods.ios.dangerous({
118
+ ...config,
119
+ modRequest: {
120
+ platformProjectRoot: iosRoot,
121
+ projectRoot,
122
+ platform: 'ios',
123
+ modName: 'dangerous',
124
+ projectName: 'TestApp',
125
+ introspect: false,
126
+ },
127
+ modResults: {},
128
+ })
129
+
130
+ const result = readFileSync(join(iosRoot, 'Podfile'), 'utf8')
131
+ expect(result).toContain('[vxrn/one] fmt c++17 fix')
132
+ expect(result).toContain('[vxrn/one] minify iOS Hermes Release bundle input')
133
+ expect(result).toContain("ENV['RNS_GAMMA_ENABLED'] ||= '1'")
134
+ })
135
+ })