@nx/expo 22.2.0-beta.2 → 22.2.0-beta.4

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 (35) hide show
  1. package/migrations.json +109 -0
  2. package/package.json +8 -7
  3. package/plugins/with-nx-metro.d.ts +1 -1
  4. package/plugins/with-nx-metro.d.ts.map +1 -1
  5. package/plugins/with-nx-metro.js +1 -1
  6. package/src/generators/application/application.js +1 -1
  7. package/src/generators/application/files/nx-welcome/claimed/src/app/App.tsx.template +7 -10
  8. package/src/generators/application/files/nx-welcome/not-configured/src/app/App.tsx.template +7 -10
  9. package/src/generators/application/files/nx-welcome/unclaimed/src/app/App.tsx.template +7 -10
  10. package/src/generators/init/init.d.ts +1 -1
  11. package/src/generators/init/init.d.ts.map +1 -1
  12. package/src/generators/init/init.js +11 -9
  13. package/src/generators/library/library.d.ts.map +1 -1
  14. package/src/generators/library/library.js +7 -6
  15. package/src/migrations/update-22-2-0/add-expo-system-ui.d.ts +8 -0
  16. package/src/migrations/update-22-2-0/add-expo-system-ui.d.ts.map +1 -0
  17. package/src/migrations/update-22-2-0/add-expo-system-ui.js +48 -0
  18. package/src/migrations/update-22-2-0/create-ai-instructions-for-expo-54.d.ts +3 -0
  19. package/src/migrations/update-22-2-0/create-ai-instructions-for-expo-54.d.ts.map +1 -0
  20. package/src/migrations/update-22-2-0/create-ai-instructions-for-expo-54.js +16 -0
  21. package/src/migrations/update-22-2-0/files/ai-instructions-for-expo-54.md +519 -0
  22. package/src/migrations/update-22-2-0/update-jest-for-expo-54.d.ts +12 -0
  23. package/src/migrations/update-22-2-0/update-jest-for-expo-54.d.ts.map +1 -0
  24. package/src/migrations/update-22-2-0/update-jest-for-expo-54.js +150 -0
  25. package/src/utils/ensure-dependencies.d.ts +1 -1
  26. package/src/utils/ensure-dependencies.d.ts.map +1 -1
  27. package/src/utils/ensure-dependencies.js +16 -15
  28. package/src/utils/jest/add-jest.d.ts.map +1 -1
  29. package/src/utils/jest/add-jest.js +35 -10
  30. package/src/utils/version-utils.d.ts +48 -0
  31. package/src/utils/version-utils.d.ts.map +1 -0
  32. package/src/utils/version-utils.js +130 -0
  33. package/src/utils/versions.d.ts +46 -16
  34. package/src/utils/versions.d.ts.map +1 -1
  35. package/src/utils/versions.js +51 -17
@@ -0,0 +1,519 @@
1
+ # Expo SDK 54 Migration Instructions for LLM
2
+
3
+ ## Overview
4
+
5
+ These instructions guide you through migrating an Nx workspace containing Expo projects from Expo SDK 53 to Expo SDK 54. Work systematically through each breaking change category.
6
+
7
+ ## Pre-Migration Checklist
8
+
9
+ 1. **Identify all Expo projects**:
10
+
11
+ ```bash
12
+ nx show projects --with-target start
13
+ ```
14
+
15
+ 2. **Locate all Expo configuration files**:
16
+
17
+ - Search for `app.json` or `app.config.{js,ts}`
18
+ - Search for `metro.config.{js,ts}`
19
+ - Check `project.json` files for Expo-related configuration
20
+
21
+ 3. **Identify affected code**:
22
+
23
+ - Files importing from `expo-av`
24
+ - Files importing from `expo-file-system`
25
+ - Files using `StatusBar` configuration
26
+ - Android-specific layout code
27
+ - Files importing from `@expo/vector-icons`
28
+
29
+ 4. **Check for Detox E2E testing projects**:
30
+ - Search for `detox` in package.json dependencies
31
+ - Look for `detox.config.js` or `.detoxrc.js` files
32
+
33
+ ## Important Warnings
34
+
35
+ ### Detox E2E Testing Not Supported
36
+
37
+ **Expo SDK 54 does not currently support Detox for end-to-end testing.** If your workspace includes Detox projects, be aware of the following:
38
+
39
+ 1. **Impact**: E2E tests using Detox will not work after migrating to Expo SDK 54
40
+ 2. **Detection**: Check for `detox` in package.json dependencies or projects with `detox.config.js`
41
+
42
+ **Action Required**: If Detox projects are detected, you MUST ask the user before proceeding:
43
+
44
+ > "Your workspace contains Detox E2E tests. Detox is not currently supported in Expo SDK 54. Do you want to proceed with the migration knowing that Detox tests will not work until Detox adds SDK 54 support?"
45
+
46
+ **Alternatives to consider**:
47
+
48
+ - Stay on Expo SDK 53 if Detox E2E testing is critical for your workflow
49
+ - Wait for Detox to add Expo SDK 54 support
50
+ - Consider migrating to [Maestro](https://maestro.mobile.dev/) for E2E testing (has Expo SDK 54 support)
51
+
52
+ ## Migration Steps by Category
53
+
54
+ ### 1. expo-av to expo-audio and expo-video Migration
55
+
56
+ The `expo-av` package has been deprecated and split into two separate packages: `expo-audio` and `expo-video`.
57
+
58
+ #### 1.1 Audio Migration
59
+
60
+ **Search Pattern**: `import.*from ['"]expo-av['"]` with `Audio` usage
61
+
62
+ **Changes Required**:
63
+
64
+ ```typescript
65
+ // BEFORE (Expo SDK 53)
66
+ import { Audio } from 'expo-av';
67
+
68
+ const sound = new Audio.Sound();
69
+ await sound.loadAsync(require('./audio.mp3'));
70
+ await sound.playAsync();
71
+
72
+ // AFTER (Expo SDK 54)
73
+ import { useAudioPlayer } from 'expo-audio';
74
+
75
+ function AudioComponent() {
76
+ const player = useAudioPlayer(require('./audio.mp3'));
77
+
78
+ const play = () => player.play();
79
+ const pause = () => player.pause();
80
+
81
+ return <Button onPress={play} title="Play" />;
82
+ }
83
+ ```
84
+
85
+ **Action Items**:
86
+
87
+ - [ ] Install `expo-audio`: `npx expo install expo-audio`
88
+ - [ ] Remove `expo-av` if only used for audio
89
+ - [ ] Replace `Audio.Sound` with `useAudioPlayer` hook
90
+ - [ ] Replace `Audio.Recording` with `useAudioRecorder` hook
91
+ - [ ] Update audio playback control methods (`playAsync` -> `play`, etc.)
92
+ - [ ] Convert class-based audio handling to hook-based approach
93
+
94
+ #### 1.2 Video Migration
95
+
96
+ **Search Pattern**: `import.*from ['"]expo-av['"]` with `Video` usage
97
+
98
+ **Changes Required**:
99
+
100
+ ```tsx
101
+ // BEFORE (Expo SDK 53)
102
+ import { Video } from 'expo-av';
103
+
104
+ function VideoPlayer() {
105
+ return (
106
+ <Video
107
+ source={{ uri: 'https://example.com/video.mp4' }}
108
+ style={{ width: 300, height: 200 }}
109
+ useNativeControls
110
+ resizeMode="contain"
111
+ />
112
+ );
113
+ }
114
+
115
+ // AFTER (Expo SDK 54)
116
+ import { VideoView, useVideoPlayer } from 'expo-video';
117
+
118
+ function VideoPlayer() {
119
+ const player = useVideoPlayer('https://example.com/video.mp4', (player) => {
120
+ player.loop = true;
121
+ player.play();
122
+ });
123
+
124
+ return (
125
+ <VideoView
126
+ player={player}
127
+ style={{ width: 300, height: 200 }}
128
+ nativeControls
129
+ contentFit="contain"
130
+ />
131
+ );
132
+ }
133
+ ```
134
+
135
+ **Action Items**:
136
+
137
+ - [ ] Install `expo-video`: `npx expo install expo-video`
138
+ - [ ] Remove `expo-av` if only used for video
139
+ - [ ] Replace `Video` component with `VideoView` and `useVideoPlayer`
140
+ - [ ] Replace `resizeMode` prop with `contentFit`
141
+ - [ ] Replace `useNativeControls` prop with `nativeControls`
142
+ - [ ] Update video control methods to use the player instance
143
+
144
+ ### 2. expo-file-system Import Path Changes
145
+
146
+ **Search Pattern**: `import.*from ['"]expo-file-system/next['"]`
147
+
148
+ **Changes Required**:
149
+
150
+ ```typescript
151
+ // BEFORE (Expo SDK 53)
152
+ import { File, Directory } from 'expo-file-system/next';
153
+
154
+ // AFTER (Expo SDK 54)
155
+ import { File, Directory } from 'expo-file-system';
156
+ ```
157
+
158
+ **Action Items**:
159
+
160
+ - [ ] Replace all `expo-file-system/next` imports with `expo-file-system`
161
+ - [ ] Verify API compatibility (the API is now stable)
162
+
163
+ ### 3. StatusBar Configuration Removal
164
+
165
+ In Expo SDK 54, the `expo-status-bar` configuration is now handled differently. The old `userInterfaceStyle` in `app.json` affects status bar theming.
166
+
167
+ **Search Pattern**: `userInterfaceStyle` in `app.json` or `app.config.*`
168
+
169
+ **Changes Required**:
170
+
171
+ ```json
172
+ // BEFORE (Expo SDK 53)
173
+ {
174
+ "expo": {
175
+ "userInterfaceStyle": "automatic",
176
+ "ios": {
177
+ "userInterfaceStyle": "light"
178
+ },
179
+ "android": {
180
+ "userInterfaceStyle": "dark"
181
+ }
182
+ }
183
+ }
184
+
185
+ // AFTER (Expo SDK 54)
186
+ // The userInterfaceStyle is now handled via expo-system-ui
187
+ // For programmatic control, use:
188
+ ```
189
+
190
+ ```typescript
191
+ // AFTER (Expo SDK 54) - Programmatic control
192
+ import * as SystemUI from 'expo-system-ui';
193
+
194
+ // Set root view background color
195
+ SystemUI.setBackgroundColorAsync('#ffffff');
196
+ ```
197
+
198
+ **Action Items**:
199
+
200
+ - [ ] Install `expo-system-ui`: `npx expo install expo-system-ui`
201
+ - [ ] Review `userInterfaceStyle` settings in app.json
202
+ - [ ] Move UI style handling to `expo-system-ui` if programmatic control is needed
203
+
204
+ ### 4. Android Edge-to-Edge UI
205
+
206
+ Expo SDK 54 enables edge-to-edge display by default on Android. This means content can extend behind system bars (status bar and navigation bar).
207
+
208
+ **Search Pattern**: Android-specific styles, safe area handling, padding/margin adjustments
209
+
210
+ **Changes Required**:
211
+
212
+ ```tsx
213
+ // BEFORE (Expo SDK 53) - Implicit safe area
214
+ function App() {
215
+ return (
216
+ <View style={{ flex: 1 }}>
217
+ <Text>Content</Text>
218
+ </View>
219
+ );
220
+ }
221
+
222
+ // AFTER (Expo SDK 54) - Explicit safe area handling
223
+ import { SafeAreaView } from 'react-native-safe-area-context';
224
+ // or
225
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
226
+
227
+ function App() {
228
+ return (
229
+ <SafeAreaView style={{ flex: 1 }}>
230
+ <Text>Content</Text>
231
+ </SafeAreaView>
232
+ );
233
+ }
234
+
235
+ // Or with hooks for more control
236
+ function App() {
237
+ const insets = useSafeAreaInsets();
238
+
239
+ return (
240
+ <View
241
+ style={{ flex: 1, paddingTop: insets.top, paddingBottom: insets.bottom }}
242
+ >
243
+ <Text>Content</Text>
244
+ </View>
245
+ );
246
+ }
247
+ ```
248
+
249
+ **Action Items**:
250
+
251
+ - [ ] Audit all screen components for safe area handling
252
+ - [ ] Install `react-native-safe-area-context` if not present
253
+ - [ ] Wrap root components with `SafeAreaProvider`
254
+ - [ ] Add `SafeAreaView` or use `useSafeAreaInsets` where content touches screen edges
255
+ - [ ] Test on Android devices/emulators to verify UI doesn't overlap system bars
256
+ - [ ] Pay special attention to:
257
+ - Header components
258
+ - Bottom navigation/tab bars
259
+ - Modal components
260
+ - Full-screen media players
261
+
262
+ ### 5. React Native Reanimated Version Decision
263
+
264
+ Expo SDK 54 supports both Reanimated v3 and v4. If you're using New Architecture, you should upgrade to v4.
265
+
266
+ **Search Pattern**: `react-native-reanimated` in package.json, worklet functions
267
+
268
+ **Reanimated v3 (stable)**:
269
+
270
+ ```bash
271
+ npx expo install react-native-reanimated@3
272
+ ```
273
+
274
+ **Reanimated v4 (recommended for New Architecture)**:
275
+
276
+ ```bash
277
+ npx expo install react-native-reanimated@4
278
+ ```
279
+
280
+ **Action Items**:
281
+
282
+ - [ ] Check if New Architecture is enabled in your project
283
+ - [ ] If using New Architecture, upgrade to Reanimated v4
284
+ - [ ] If staying on old architecture, Reanimated v3 is fine
285
+ - [ ] Test all animations after the upgrade
286
+
287
+ ### 6. @expo/vector-icons Validation
288
+
289
+ Some icons in `@expo/vector-icons` may have been renamed or removed in SDK 54.
290
+
291
+ **Search Pattern**: `import.*from ['"]@expo/vector-icons['"]`
292
+
293
+ **Action Items**:
294
+
295
+ - [ ] Run the app and check console for warnings about missing icons
296
+ - [ ] Search for icon names that may have changed
297
+ - [ ] Update icon names as needed
298
+ - [ ] Consider using the Expo Vector Icons directory to find replacements: https://icons.expo.fyi/
299
+
300
+ ### 7. React 19.1 Compatibility
301
+
302
+ Expo SDK 54 uses React 19.1 and React Native 0.81.
303
+
304
+ **Search Pattern**: `React.FC`, deprecated lifecycle methods, legacy context API
305
+
306
+ **Changes Required**:
307
+
308
+ ```typescript
309
+ // BEFORE - React.FC (discouraged in React 19)
310
+ const MyComponent: React.FC<Props> = ({ title }) => {
311
+ return <Text>{title}</Text>;
312
+ };
313
+
314
+ // AFTER - Direct function typing
315
+ function MyComponent({ title }: Props) {
316
+ return <Text>{title}</Text>;
317
+ }
318
+
319
+ // Or with explicit return type
320
+ const MyComponent = ({ title }: Props): React.ReactElement => {
321
+ return <Text>{title}</Text>;
322
+ };
323
+ ```
324
+
325
+ **Action Items**:
326
+
327
+ - [ ] Update TypeScript types for React 19.1 (`@types/react@~19.1.0`)
328
+ - [ ] Remove usage of `React.FC` pattern (optional but recommended)
329
+ - [ ] Check for deprecated lifecycle methods and update to hooks
330
+ - [ ] Verify third-party libraries are compatible with React 19.1
331
+
332
+ ### 8. Metro Configuration Updates
333
+
334
+ Expo SDK 54 uses Metro 0.83 with updated configuration.
335
+
336
+ **Search Pattern**: `metro.config.{js,ts}`
337
+
338
+ **Changes Required**:
339
+
340
+ ```javascript
341
+ // BEFORE (Expo SDK 53)
342
+ const { getDefaultConfig } = require('@expo/metro-config');
343
+
344
+ const config = getDefaultConfig(__dirname);
345
+
346
+ // AFTER (Expo SDK 54) - Same API, but verify compatibility
347
+ const { getDefaultConfig } = require('@expo/metro-config');
348
+
349
+ const config = getDefaultConfig(__dirname);
350
+
351
+ // Ensure any custom transformers are compatible with Metro 0.83
352
+ ```
353
+
354
+ **Action Items**:
355
+
356
+ - [ ] Update `@expo/metro-config` to `~0.22.0`
357
+ - [ ] Update `metro-config` to `~0.83.0`
358
+ - [ ] Update `metro-resolver` to `~0.83.0`
359
+ - [ ] Test custom Metro plugins/transformers for compatibility
360
+
361
+ ### 9. Babel Configuration Cleanup
362
+
363
+ Expo SDK 54 updates to `babel-preset-expo@~14.0.0`.
364
+
365
+ **Search Pattern**: `babel.config.js`, `.babelrc`
366
+
367
+ **Action Items**:
368
+
369
+ - [ ] Update `babel-preset-expo` to `~14.0.0`
370
+ - [ ] Remove any deprecated Babel plugins
371
+ - [ ] Clear Metro cache after Babel changes: `npx expo start --clear`
372
+
373
+ ## Post-Migration Validation
374
+
375
+ ### 1. Clear All Caches
376
+
377
+ ```bash
378
+ # Clear Expo cache
379
+ npx expo start --clear
380
+
381
+ # Clear Metro bundler cache
382
+ rm -rf node_modules/.cache/metro-*
383
+
384
+ # Clear Nx cache if needed
385
+ nx reset
386
+ ```
387
+
388
+ ### 2. Run Tests Per Project
389
+
390
+ ```bash
391
+ # Test each project individually
392
+ nx run-many -t test -p PROJECT_NAME
393
+ ```
394
+
395
+ ### 3. Run All Tests
396
+
397
+ ```bash
398
+ # Run tests across all affected projects
399
+ nx affected -t test
400
+ ```
401
+
402
+ ### 4. Test on Device/Simulator
403
+
404
+ ```bash
405
+ # iOS
406
+ nx run PROJECT_NAME:run-ios
407
+
408
+ # Android
409
+ nx run PROJECT_NAME:run-android
410
+ ```
411
+
412
+ ### 5. Review Migration Checklist
413
+
414
+ - [ ] All `expo-av` usages migrated to `expo-audio` or `expo-video`
415
+ - [ ] All `expo-file-system/next` imports updated
416
+ - [ ] Safe area handling verified on Android
417
+ - [ ] All icon references validated
418
+ - [ ] React 19.1 compatibility verified
419
+ - [ ] Metro and Babel configurations updated
420
+ - [ ] All tests pass
421
+ - [ ] App runs on iOS simulator/device
422
+ - [ ] App runs on Android emulator/device
423
+
424
+ ## Common Issues and Solutions
425
+
426
+ ### Issue: Audio playback stops when component unmounts
427
+
428
+ **Solution**: The new `useAudioPlayer` hook manages cleanup automatically. If you need persistent audio, consider using a context or state management solution.
429
+
430
+ ### Issue: Video player shows black screen
431
+
432
+ **Solution**: Ensure you're using `useVideoPlayer` with `VideoView`. Check that the video source URL is correct and accessible.
433
+
434
+ ### Issue: Content hidden behind Android navigation bar
435
+
436
+ **Solution**: Wrap your screen content with `SafeAreaView` from `react-native-safe-area-context` or use `useSafeAreaInsets` for custom padding.
437
+
438
+ ### Issue: Missing icons after upgrade
439
+
440
+ **Solution**: Check the Expo Vector Icons directory for renamed icons: https://icons.expo.fyi/
441
+
442
+ ### Issue: TypeScript errors with React 19.1
443
+
444
+ **Solution**: Update `@types/react` to `~19.1.0` and check for deprecated patterns.
445
+
446
+ ### Issue: Metro bundler fails to start
447
+
448
+ **Solution**: Clear all caches with `npx expo start --clear` and ensure Metro packages are at compatible versions.
449
+
450
+ ## Files to Review
451
+
452
+ Create a checklist of all files that need review:
453
+
454
+ ```bash
455
+ # Configuration files
456
+ find . -name "app.json" -o -name "app.config.*"
457
+ find . -name "metro.config.*"
458
+ find . -name "babel.config.*"
459
+
460
+ # Files with expo-av imports
461
+ rg "from ['\"]expo-av['\"]" --type ts --type tsx --type js
462
+
463
+ # Files with expo-file-system/next imports
464
+ rg "from ['\"]expo-file-system/next['\"]" --type ts --type tsx --type js
465
+
466
+ # Files with vector-icons imports
467
+ rg "from ['\"]@expo/vector-icons['\"]" --type ts --type tsx --type js
468
+
469
+ # Safe area related files
470
+ rg "SafeAreaView|useSafeAreaInsets" --type ts --type tsx --type js
471
+ ```
472
+
473
+ ## Migration Strategy for Large Workspaces
474
+
475
+ 1. **Migrate in phases**: Start with a small project, validate, then expand
476
+ 2. **Use feature branches**: Create separate branches for different migration aspects
477
+ 3. **Run tests frequently**: After each configuration change, run affected tests
478
+ 4. **Document issues**: Keep track of project-specific issues and solutions
479
+ 5. **Test on real devices**: Android edge-to-edge changes require device testing
480
+
481
+ ## Useful Commands During Migration
482
+
483
+ ```bash
484
+ # Find all Expo projects
485
+ nx show projects --with-target start
486
+
487
+ # Run specific project
488
+ nx start PROJECT_NAME
489
+
490
+ # Test specific project after changes
491
+ nx test PROJECT_NAME
492
+
493
+ # Test all affected
494
+ nx affected -t test
495
+
496
+ # View project details
497
+ nx show project PROJECT_NAME --web
498
+
499
+ # Clear Nx cache if needed
500
+ nx reset
501
+
502
+ # Clear Expo cache
503
+ npx expo start --clear
504
+ ```
505
+
506
+ ---
507
+
508
+ ## Notes for LLM Execution
509
+
510
+ When executing this migration:
511
+
512
+ 1. **Work systematically**: Complete one category before moving to the next
513
+ 2. **Test after each change**: Don't batch all changes without validation
514
+ 3. **Keep user informed**: Report progress through each section
515
+ 4. **Handle errors promptly**: If tests fail, fix immediately before proceeding
516
+ 5. **Update documentation**: Note any workspace-specific patterns or issues
517
+ 6. **Create meaningful commits**: Group related changes together with clear messages
518
+ 7. **Use TodoWrite tool**: Track migration progress for visibility
519
+ 8. **Test on both platforms**: Expo changes often affect iOS and Android differently
@@ -0,0 +1,12 @@
1
+ import { Tree } from '@nx/devkit';
2
+ /**
3
+ * Update Jest configuration for Expo SDK 54.
4
+ *
5
+ * Expo 54 requires a different approach for Jest configuration:
6
+ * - Remove the custom resolver from jest.config
7
+ * - Delete the jest.resolver.js file
8
+ * - Add ImportMetaRegistry mock and structuredClone polyfill to test-setup.ts
9
+ * - Update tsconfig files to remove jest.resolver.js references
10
+ */
11
+ export default function update(tree: Tree): Promise<void>;
12
+ //# sourceMappingURL=update-jest-for-expo-54.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-jest-for-expo-54.d.ts","sourceRoot":"","sources":["../../../../../../packages/expo/src/migrations/update-22-2-0/update-jest-for-expo-54.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EAOL,MAAM,YAAY,CAAC;AAIpB;;;;;;;;GAQG;AACH,wBAA8B,MAAM,CAAC,IAAI,EAAE,IAAI,iBA6C9C"}
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = update;
4
+ const devkit_1 = require("@nx/devkit");
5
+ const jest_1 = require("@nx/jest");
6
+ const path_1 = require("path");
7
+ /**
8
+ * Update Jest configuration for Expo SDK 54.
9
+ *
10
+ * Expo 54 requires a different approach for Jest configuration:
11
+ * - Remove the custom resolver from jest.config
12
+ * - Delete the jest.resolver.js file
13
+ * - Add ImportMetaRegistry mock and structuredClone polyfill to test-setup.ts
14
+ * - Update tsconfig files to remove jest.resolver.js references
15
+ */
16
+ async function update(tree) {
17
+ const projects = (0, devkit_1.getProjects)(tree);
18
+ for (const [projectName, config] of projects.entries()) {
19
+ if (!isExpoProject(tree, config.root)) {
20
+ continue;
21
+ }
22
+ // Check if this project has Jest configured
23
+ const jestConfigPath = findJestConfigPath(tree, config.root);
24
+ if (!jestConfigPath) {
25
+ continue;
26
+ }
27
+ const jestConfigContent = tree.read(jestConfigPath, 'utf-8');
28
+ // Only process if this is an Expo project with jest-expo preset and custom resolver
29
+ if (!jestConfigContent.includes('jest-expo') ||
30
+ !jestConfigContent.includes('jest.resolver.js')) {
31
+ continue;
32
+ }
33
+ // Remove resolver property from jest.config using AST
34
+ (0, jest_1.removePropertyFromJestConfig)(tree, jestConfigPath, 'resolver');
35
+ // Delete jest.resolver.js file
36
+ const resolverPath = (0, path_1.join)(config.root, 'jest.resolver.js');
37
+ if (tree.exists(resolverPath)) {
38
+ tree.delete(resolverPath);
39
+ }
40
+ // Update test-setup.ts with new mocks
41
+ updateTestSetup(tree, config.root);
42
+ // Update tsconfig files to remove jest.resolver.js references
43
+ updateTsConfigFilesForV54(tree, config.root);
44
+ devkit_1.logger.info(`Updated Jest configuration for ${projectName} to support Expo SDK 54`);
45
+ }
46
+ await (0, devkit_1.formatFiles)(tree);
47
+ }
48
+ function findJestConfigPath(tree, projectRoot) {
49
+ const possiblePaths = [
50
+ (0, path_1.join)(projectRoot, 'jest.config.ts'),
51
+ (0, path_1.join)(projectRoot, 'jest.config.cts'),
52
+ (0, path_1.join)(projectRoot, 'jest.config.js'),
53
+ ];
54
+ for (const configPath of possiblePaths) {
55
+ if (tree.exists(configPath)) {
56
+ return configPath;
57
+ }
58
+ }
59
+ return null;
60
+ }
61
+ function updateTestSetup(tree, projectRoot) {
62
+ const testSetupTsPath = (0, path_1.join)(projectRoot, 'src/test-setup.ts');
63
+ const testSetupJsPath = (0, path_1.join)(projectRoot, 'src/test-setup.js');
64
+ let testSetupPath = testSetupTsPath;
65
+ if (!tree.exists(testSetupTsPath)) {
66
+ if (tree.exists(testSetupJsPath)) {
67
+ testSetupPath = testSetupJsPath;
68
+ }
69
+ else {
70
+ // Create test-setup.ts if it doesn't exist
71
+ tree.write(testSetupPath, '');
72
+ }
73
+ }
74
+ let existingContent = tree.read(testSetupPath, 'utf-8') || '';
75
+ // Add ImportMetaRegistry mock if not already present
76
+ if (!existingContent.includes('ImportMetaRegistry')) {
77
+ const importMetaRegistryMock = `jest.mock('expo/src/winter/ImportMetaRegistry', () => ({
78
+ ImportMetaRegistry: {
79
+ get url() {
80
+ return null;
81
+ },
82
+ },
83
+ }));
84
+
85
+ `;
86
+ existingContent = importMetaRegistryMock + existingContent;
87
+ }
88
+ // Add structuredClone polyfill if not already present
89
+ if (!existingContent.includes('structuredClone')) {
90
+ const structuredClonePolyfill = `if (typeof global.structuredClone === 'undefined') {
91
+ global.structuredClone = (object) => JSON.parse(JSON.stringify(object));
92
+ }
93
+ `;
94
+ existingContent = existingContent + '\n' + structuredClonePolyfill;
95
+ }
96
+ tree.write(testSetupPath, existingContent);
97
+ }
98
+ function updateTsConfigFilesForV54(tree, projectRoot) {
99
+ // Update main tsconfig (app or lib) to remove jest.resolver.js from excludes
100
+ const mainTsConfigFiles = [
101
+ 'tsconfig.app.json',
102
+ 'tsconfig.lib.json',
103
+ 'tsconfig.json',
104
+ ];
105
+ for (const configFile of mainTsConfigFiles) {
106
+ const configPath = (0, path_1.join)(projectRoot, configFile);
107
+ if (tree.exists(configPath)) {
108
+ try {
109
+ (0, devkit_1.updateJson)(tree, configPath, (json) => {
110
+ if (json.exclude && Array.isArray(json.exclude)) {
111
+ json.exclude = json.exclude.filter((item) => item !== 'jest.resolver.js');
112
+ }
113
+ return json;
114
+ });
115
+ }
116
+ catch {
117
+ // Skip if JSON is invalid
118
+ }
119
+ break;
120
+ }
121
+ }
122
+ // Update tsconfig.spec.json to remove jest.resolver.js from includes
123
+ const specTsConfigPath = (0, path_1.join)(projectRoot, 'tsconfig.spec.json');
124
+ if (tree.exists(specTsConfigPath)) {
125
+ try {
126
+ (0, devkit_1.updateJson)(tree, specTsConfigPath, (json) => {
127
+ if (json.include && Array.isArray(json.include)) {
128
+ json.include = json.include.filter((item) => item !== 'jest.resolver.js');
129
+ }
130
+ return json;
131
+ });
132
+ }
133
+ catch {
134
+ // Skip if JSON is invalid
135
+ }
136
+ }
137
+ }
138
+ function isExpoProject(tree, projectRoot) {
139
+ const packageJsonPath = (0, devkit_1.joinPathFragments)(projectRoot, 'package.json');
140
+ if (!tree.exists(packageJsonPath)) {
141
+ return false;
142
+ }
143
+ try {
144
+ const packageJson = (0, devkit_1.readJson)(tree, packageJsonPath);
145
+ return Boolean(packageJson.dependencies?.expo || packageJson.devDependencies?.expo);
146
+ }
147
+ catch {
148
+ return false;
149
+ }
150
+ }
@@ -1,3 +1,3 @@
1
1
  import { type GeneratorCallback, type Tree } from '@nx/devkit';
2
- export declare function ensureDependencies(host: Tree, unitTestRunner: 'jest' | 'none'): GeneratorCallback;
2
+ export declare function ensureDependencies(host: Tree, unitTestRunner: 'jest' | 'none'): Promise<GeneratorCallback>;
3
3
  //# sourceMappingURL=ensure-dependencies.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ensure-dependencies.d.ts","sourceRoot":"","sources":["../../../../../packages/expo/src/utils/ensure-dependencies.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,IAAI,EACV,MAAM,YAAY,CAAC;AAiBpB,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,IAAI,EACV,cAAc,EAAE,MAAM,GAAG,MAAM,GAC9B,iBAAiB,CA+BnB"}
1
+ {"version":3,"file":"ensure-dependencies.d.ts","sourceRoot":"","sources":["../../../../../packages/expo/src/utils/ensure-dependencies.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,IAAI,EACV,MAAM,YAAY,CAAC;AAGpB,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,IAAI,EACV,cAAc,EAAE,MAAM,GAAG,MAAM,GAC9B,OAAO,CAAC,iBAAiB,CAAC,CAiC5B"}