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.
- package/expo-plugin.cjs +82 -0
- package/native-project-patches.cjs +24 -4
- package/package.json +13 -11
- package/src/exports/expoPlugin.test.mjs +135 -0
package/expo-plugin.cjs
ADDED
|
@@ -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 = {
|
|
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.
|
|
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.
|
|
66
|
-
"@vxrn/debug": "2.0.0-beta.
|
|
67
|
-
"@vxrn/query-string": "2.0.0-beta.
|
|
68
|
-
"@vxrn/resolve": "2.0.0-beta.
|
|
69
|
-
"@vxrn/safe-area": "2.0.0-beta.
|
|
70
|
-
"@vxrn/url-parse": "2.0.0-beta.
|
|
71
|
-
"@vxrn/utils": "2.0.0-beta.
|
|
72
|
-
"@vxrn/vendor": "2.0.0-beta.
|
|
73
|
-
"@vxrn/vite-flow": "2.0.0-beta.
|
|
74
|
-
"@vxrn/vite-plugin-metro": "2.0.0-beta.
|
|
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
|
+
})
|