vxrn 1.16.12 → 1.17.0

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 (2) hide show
  1. package/expo-plugin.cjs +216 -0
  2. package/package.json +11 -11
package/expo-plugin.cjs CHANGED
@@ -8,6 +8,16 @@ const {
8
8
  const fs = require('fs')
9
9
  const path = require('path')
10
10
 
11
+ function getPodfilePropsOptOut(propsPath, key) {
12
+ if (!fs.existsSync(propsPath)) return false
13
+ try {
14
+ const props = JSON.parse(fs.readFileSync(propsPath, 'utf8'))
15
+ return props[key] === 'true'
16
+ } catch {
17
+ return false
18
+ }
19
+ }
20
+
11
21
  const plugin = (config, options = {}) => {
12
22
  return withPlugins(config, [
13
23
  // auto-inject swift 6 workaround for expo-modules-core into Podfile (version-gated, opt-out-able)
@@ -104,9 +114,137 @@ const plugin = (config, options = {}) => {
104
114
  return config
105
115
  },
106
116
  ],
117
+ // iOS Hermes Release: hermesc can crash on large unminified bundles.
118
+ // Force Metro to pre-minify so hermesc only does bytecode conversion.
119
+ [
120
+ withDangerousMod,
121
+ [
122
+ 'ios',
123
+ async (config) => {
124
+ const iosRoot = config.modRequest.platformProjectRoot
125
+ const podfilePath = path.join(iosRoot, 'Podfile')
126
+ if (!fs.existsSync(podfilePath)) return config
127
+
128
+ const propsPath = path.join(iosRoot, 'Podfile.properties.json')
129
+ if (getPodfilePropsOptOut(propsPath, 'one.disableHermesMinification')) {
130
+ return config
131
+ }
132
+
133
+ const podfile = fs.readFileSync(podfilePath, 'utf8')
134
+ const next = injectHermesMinificationPatchIntoPodfile(podfile)
135
+ if (next !== podfile) {
136
+ fs.writeFileSync(podfilePath, next, 'utf8')
137
+ console.info(
138
+ '[vxrn] patched Podfile to minify iOS Hermes Release bundle input\n' +
139
+ ' to disable: set "one.disableHermesMinification": "true" in ios/Podfile.properties.json'
140
+ )
141
+ }
142
+ return config
143
+ },
144
+ ],
145
+ ],
146
+ // iOS expo-updates: the EXUpdates pod's "Generate updates resources for
147
+ // expo-updates" build phase invokes Expo Metro directly to bundle the JS
148
+ // entry into app.manifest. Under One/VxRN, release bundling goes through
149
+ // the react-native CLI override (so One's router transforms are wired),
150
+ // and Expo Metro doesn't see those transforms — it fails to resolve the
151
+ // entry on monorepos or chokes on `one/metro-entry-ctx.js`. Replace that
152
+ // phase with one that writes a minimal embedded manifest and sets
153
+ // SKIP_BUNDLING=1 so the original script's bundle step is short-circuited
154
+ // while its fingerprint/resource side-effects still run.
155
+ [
156
+ withDangerousMod,
157
+ [
158
+ 'ios',
159
+ async (config) => {
160
+ const iosRoot = config.modRequest.platformProjectRoot
161
+ const podfilePath = path.join(iosRoot, 'Podfile')
162
+ if (!fs.existsSync(podfilePath)) return config
163
+
164
+ const propsPath = path.join(iosRoot, 'Podfile.properties.json')
165
+ if (
166
+ getPodfilePropsOptOut(propsPath, 'one.disableExpoUpdatesIosShellScriptPatch')
167
+ ) {
168
+ return config
169
+ }
170
+
171
+ const podfile = fs.readFileSync(podfilePath, 'utf8')
172
+ const next = injectExpoUpdatesIosResourcesPatchIntoPodfile(podfile)
173
+ if (next !== podfile) {
174
+ fs.writeFileSync(podfilePath, next, 'utf8')
175
+ console.info(
176
+ '[vxrn] patched Podfile to skip expo-updates Expo Metro invocation\n' +
177
+ ' to disable: set "one.disableExpoUpdatesIosShellScriptPatch": "true" in ios/Podfile.properties.json'
178
+ )
179
+ }
180
+ return config
181
+ },
182
+ ],
183
+ ],
107
184
  ])
108
185
  }
109
186
 
187
+
188
+ /**
189
+ * RN disables Metro minification for iOS Hermes Release, expecting
190
+ * hermesc -O to handle bytecode optimization. For large One/VxRN bundles
191
+ * hermesc can crash on EAS standard workers. Forcing --minify true makes
192
+ * Metro emit a minified bundle that hermesc consumes — final bytecode is
193
+ * identical, just smaller intermediate input.
194
+ *
195
+ * Skipped on non-Release configurations to keep debug build times sane.
196
+ */
197
+ const HERMES_MINIFY_PATCH_MARKER = '# [vxrn/one] minify iOS Hermes Release bundle input'
198
+
199
+ function injectHermesMinificationPatchIntoPodfile(podfile) {
200
+ if (podfile.includes(HERMES_MINIFY_PATCH_MARKER)) {
201
+ return podfile
202
+ }
203
+
204
+ const patch = `
205
+ ${HERMES_MINIFY_PATCH_MARKER}
206
+ # hermesc can OOM/crash on large unminified bundles on EAS standard
207
+ # workers. Pre-minify so hermesc only does bytecode conversion. Only
208
+ # applies in Release.
209
+ installer.aggregate_targets.each do |aggregate_target|
210
+ project = aggregate_target.user_project
211
+ next unless project
212
+
213
+ changed = false
214
+ project.targets.each do |target|
215
+ target.shell_script_build_phases.each do |phase|
216
+ next unless phase.name.to_s.include?('Bundle React Native code and images')
217
+ next if phase.shell_script.to_s.include?('${HERMES_MINIFY_PATCH_MARKER}')
218
+
219
+ original_script = phase.shell_script
220
+ phase.shell_script = <<~SCRIPT
221
+ ${HERMES_MINIFY_PATCH_MARKER}
222
+ if [ "$CONFIGURATION" = "Release" ]; then
223
+ export EXTRA_PACKAGER_ARGS="\${EXTRA_PACKAGER_ARGS:-} --minify true"
224
+ fi
225
+
226
+ #{original_script}
227
+ SCRIPT
228
+ changed = true
229
+ end
230
+ end
231
+
232
+ project.save if changed
233
+ end
234
+ `
235
+
236
+ const match = podfile.match(/post_install\s+do\s+\|installer\|/)
237
+ if (!match) {
238
+ console.warn(
239
+ '[vxrn] could not find post_install block in Podfile to inject Hermes minification patch'
240
+ )
241
+ return podfile
242
+ }
243
+
244
+ const insertAt = match.index + match[0].length
245
+ return podfile.slice(0, insertAt) + '\n' + patch + podfile.slice(insertAt)
246
+ }
247
+
110
248
  /**
111
249
  * The Expo templates defaults the `CLI_PATH` and `BUNDLE_COMMAND` environment variables to use Expo CLI with the `export:embed` command.
112
250
  *
@@ -514,6 +652,84 @@ function injectSwift6WorkaroundIntoPodfile(podfile) {
514
652
  return podfile
515
653
  }
516
654
 
655
+ /**
656
+ * Replace the EXUpdates pod's "Generate updates resources for expo-updates"
657
+ * Xcode build phase with one that writes a minimal embedded manifest and sets
658
+ * SKIP_BUNDLING=1.
659
+ *
660
+ * Why: expo-updates' shell script runs Expo Metro on the JS entry to produce
661
+ * `app.manifest`. Under One/VxRN, release bundling is routed through the
662
+ * react-native CLI override so One's router transforms are wired; Expo Metro
663
+ * doesn't see those transforms and (on monorepos) often can't even resolve the
664
+ * entry. The wrapped script still runs the original so fingerprint/resource
665
+ * side-effects fire — only the Metro bundle step is skipped.
666
+ *
667
+ * Disable: set "one.disableExpoUpdatesIosShellScriptPatch": "true" in
668
+ * ios/Podfile.properties.json.
669
+ */
670
+ const EXPO_UPDATES_METRO_SKIP_MARKER =
671
+ '# [vxrn/one] skip expo-updates Expo Metro for embedded manifest'
672
+
673
+ function injectExpoUpdatesIosResourcesPatchIntoPodfile(podfile) {
674
+ if (podfile.includes(EXPO_UPDATES_METRO_SKIP_MARKER)) {
675
+ return podfile
676
+ }
677
+
678
+ const patch = `
679
+ ${EXPO_UPDATES_METRO_SKIP_MARKER}
680
+ installer.pods_project.targets.each do |target|
681
+ next unless target.name == 'EXUpdates'
682
+
683
+ target.shell_script_build_phases.each do |phase|
684
+ next unless phase.name.to_s.include?('Generate updates resources for expo-updates')
685
+ next if phase.shell_script.to_s.include?('${EXPO_UPDATES_METRO_SKIP_MARKER}')
686
+
687
+ original_script = phase.shell_script
688
+ phase.shell_script = <<~SCRIPT
689
+ ${EXPO_UPDATES_METRO_SKIP_MARKER}
690
+ set -eo pipefail
691
+
692
+ RESOURCE_BUNDLE_NAME="EXUpdates.bundle"
693
+ DEST="$CONFIGURATION_BUILD_DIR"
694
+
695
+ if [ "$BUNDLE_FORMAT" = "shallow" ]; then
696
+ RESOURCE_DEST="$DEST/$RESOURCE_BUNDLE_NAME"
697
+ elif [ "$BUNDLE_FORMAT" = "deep" ]; then
698
+ RESOURCE_DEST="$DEST/$RESOURCE_BUNDLE_NAME/Contents/Resources"
699
+ else
700
+ echo "[vxrn/one] expo-updates patch: unsupported BUNDLE_FORMAT='$BUNDLE_FORMAT'" >&2
701
+ exit 1
702
+ fi
703
+
704
+ mkdir -p "$RESOURCE_DEST"
705
+ "\${NODE_BINARY:-node}" -e "const c=require('node:crypto');const f=require('node:fs');f.writeFileSync(process.argv[1],JSON.stringify({id:c.randomUUID(),commitTime:Date.now(),assets:[]}))" "$RESOURCE_DEST/app.manifest"
706
+
707
+ export SKIP_BUNDLING=1
708
+
709
+ #{original_script}
710
+ SCRIPT
711
+ end
712
+ end
713
+ `
714
+
715
+ const match = podfile.match(/post_install\s+do\s+\|installer\|/)
716
+ if (!match) {
717
+ console.warn(
718
+ '[vxrn] could not find post_install block in Podfile to inject expo-updates iOS resources patch'
719
+ )
720
+ return podfile
721
+ }
722
+
723
+ const insertAt = match.index + match[0].length
724
+ return podfile.slice(0, insertAt) + '\n' + patch + podfile.slice(insertAt)
725
+ }
726
+
517
727
  module.exports = plugin
518
728
  module.exports.addReactNativeScreensFix = addReactNativeScreensFix
519
729
  module.exports.injectSwift6WorkaroundIntoPodfile = injectSwift6WorkaroundIntoPodfile
730
+ module.exports.injectHermesMinificationPatchIntoPodfile =
731
+ injectHermesMinificationPatchIntoPodfile
732
+ module.exports.injectExpoUpdatesIosResourcesPatchIntoPodfile =
733
+ injectExpoUpdatesIosResourcesPatchIntoPodfile
734
+ module.exports.HERMES_MINIFY_PATCH_MARKER = HERMES_MINIFY_PATCH_MARKER
735
+ module.exports.EXPO_UPDATES_METRO_SKIP_MARKER = EXPO_UPDATES_METRO_SKIP_MARKER
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vxrn",
3
- "version": "1.16.12",
3
+ "version": "1.17.0",
4
4
  "sideEffects": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -59,16 +59,16 @@
59
59
  "@expo/config-plugins": "~55.0.6",
60
60
  "@hono/node-server": "^1.19.10",
61
61
  "@react-native/dev-middleware": "^0.83.4",
62
- "@vxrn/compiler": "1.16.12",
63
- "@vxrn/debug": "1.16.12",
64
- "@vxrn/query-string": "1.16.12",
65
- "@vxrn/resolve": "1.16.12",
66
- "@vxrn/safe-area": "1.16.12",
67
- "@vxrn/url-parse": "1.16.12",
68
- "@vxrn/utils": "1.16.12",
69
- "@vxrn/vendor": "1.16.12",
70
- "@vxrn/vite-flow": "1.16.12",
71
- "@vxrn/vite-plugin-metro": "1.16.12",
62
+ "@vxrn/compiler": "1.17.0",
63
+ "@vxrn/debug": "1.17.0",
64
+ "@vxrn/query-string": "1.17.0",
65
+ "@vxrn/resolve": "1.17.0",
66
+ "@vxrn/safe-area": "1.17.0",
67
+ "@vxrn/url-parse": "1.17.0",
68
+ "@vxrn/utils": "1.17.0",
69
+ "@vxrn/vendor": "1.17.0",
70
+ "@vxrn/vite-flow": "1.17.0",
71
+ "@vxrn/vite-plugin-metro": "1.17.0",
72
72
  "citty": "^0.1.6",
73
73
  "dotenv": "^17.2.1",
74
74
  "dotenv-expand": "^12.0.2",