diff --git a/.github/codemention.yml b/.github/codemention.yml
index 32e087bedcb9d1..26e71bb3dd7ee2 100644
--- a/.github/codemention.yml
+++ b/.github/codemention.yml
@@ -9,6 +9,8 @@ rules:
- patterns: ['docs/**']
mentions: ['amandeepmittal']
- patterns: ['packages/@expo/cli/**']
+ mentions: ['ubax']
+ - patterns: ['packages/@expo/codemod/**']
mentions: ['EvanBacon', 'bycedric', 'kitten']
- patterns: ['packages/@expo/config/**']
mentions: ['EvanBacon']
diff --git a/apps/bare-expo/ios/Podfile.lock b/apps/bare-expo/ios/Podfile.lock
index 22f22a8f3dcd16..77fcb953e75c78 100644
--- a/apps/bare-expo/ios/Podfile.lock
+++ b/apps/bare-expo/ios/Podfile.lock
@@ -3999,7 +3999,7 @@ SPEC CHECKSUMS:
EXUpdates: a0f980531cbcf45906b2489febd4e11a5895f332
EXUpdatesInterface: 5ab8c3e8018ef533a132b9327af5b2a1926dd299
FBLazyVector: 26fd21c75314e101f280d401e97f27d54f3f7064
- hermes-engine: bd2451c187da88af6812697281b68f5f94ec0d01
+ hermes-engine: 725fd85144e1348879039099a6be950c471a4f2c
libavif: 5f8e715bea24debec477006f21ef9e95432e254d
libdav1d: 23581a4d8ec811ff171ed5e2e05cd27bad64c39f
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
@@ -4017,7 +4017,7 @@ SPEC CHECKSUMS:
React: 13cf8451582adb1bb324306e1893b91d1cba28c6
React-callinvoker: 91e6a605826b684ad2e623811253b4d0c4196bef
React-Core: 46818de5f211b2a2759ac823b591af8a0a95c2c1
- React-Core-prebuilt: e4a674cb7708d81eabfeb2908618d510e1a14481
+ React-Core-prebuilt: 4016009b4cc1d669b1a2369a5d707cdb39fa12ef
React-CoreModules: a6a37afee48d4a31ab398640b0795462647d5c67
React-cxxreact: 2ec3e2f7a8ae9303460d4ba94cde183ea90d64cd
React-debug: 0d21117b897ce0359c9d2c9dfe952f237476a14a
@@ -4087,7 +4087,7 @@ SPEC CHECKSUMS:
ReactAppDependencyProvider: 22e2265d86a4e871e5e858f4e7ef1c8d01103680
ReactCodegen: f564776e1b15423920d439de1965d2000433dbd2
ReactCommon: a804bb8d1dcf3ecdec3a77eb8bba19b7863bbbdb
- ReactNativeDependencies: 166d44a54c7845b05d943b282be2f6fc683978b5
+ ReactNativeDependencies: a24dd0e4b4318c05556b4b7bb144738f83775e22
RNCAsyncStorage: 2ad919e88b8bc2cd80e8697ce66d04d006743283
RNCMaskedView: eb2b2e538afa907f05a5848a1a1ac26092e6fec9
RNCPicker: d74667bdfc08ed389a2a277d95b8faf2349290a9
diff --git a/apps/expo-go/ios/Podfile.lock b/apps/expo-go/ios/Podfile.lock
index d63ad5d5fa07c9..b6eb2ade8fb8e4 100644
--- a/apps/expo-go/ios/Podfile.lock
+++ b/apps/expo-go/ios/Podfile.lock
@@ -24,6 +24,7 @@ PODS:
- boost
- DoubleConversion
- ExpoModulesCore
+ - ExpoModulesJSI
- fast_float
- fmt
- glog
@@ -55,6 +56,7 @@ PODS:
- boost
- DoubleConversion
- ExpoModulesCore
+ - ExpoModulesJSI
- ExpoModulesTestCore
- fast_float
- fmt
@@ -83,10 +85,6 @@ PODS:
- ReactCommon/turbomodule/core
- SocketRocket
- Yoga
- - ExpoAgeRange (0.2.10):
- - ExpoModulesCore
- - ExpoAppleAuthentication (55.0.8):
- - ExpoModulesCore
- ExpoAsset (55.0.7):
- ExpoModulesCore
- ExpoAudio (55.0.8):
@@ -171,8 +169,6 @@ PODS:
- ExpoModulesCore
- ExpoLinking (55.0.7):
- ExpoModulesCore
- - ExpoLivePhoto (55.0.8):
- - ExpoModulesCore
- ExpoLocalAuthentication (55.0.8):
- ExpoModulesCore
- ExpoLocalization (55.0.8):
@@ -190,8 +186,6 @@ PODS:
- ExpoModulesCore
- ExpoModulesTestCore
- React-Core
- - ExpoMeshGradient (55.0.8):
- - ExpoModulesCore
- ExpoModulesCore (55.0.12):
- boost
- DoubleConversion
@@ -253,13 +247,11 @@ PODS:
- ReactCommon/turbomodule/core
- SocketRocket
- Yoga
- - ExpoModulesJSI (55.0.12):
- - hermes-engine
+ - ExpoModulesJSI (55.0.0):
- React-Core
- React-runtimescheduler
- ReactCommon
- - ExpoModulesJSI/Tests (55.0.12):
- - hermes-engine
+ - ExpoModulesJSI/Tests (55.0.0):
- React-Core
- React-runtimescheduler
- ReactCommon
@@ -4012,8 +4004,6 @@ DEPENDENCIES:
- EXManifests/Tests (from `../../../packages/expo-manifests/ios`)
- Expo (from `../../../packages/expo`)
- Expo/Tests (from `../../../packages/expo`)
- - ExpoAgeRange (from `../../../packages/expo-age-range/ios`)
- - ExpoAppleAuthentication (from `../../../packages/expo-apple-authentication/ios`)
- ExpoAsset (from `../../../packages/expo-asset/ios`)
- ExpoAudio (from `../../../packages/expo-audio/ios`)
- ExpoBackgroundFetch (from `../../../packages/expo-background-fetch/ios`)
@@ -4046,7 +4036,6 @@ DEPENDENCIES:
- ExpoKeepAwake (from `../../../packages/expo-keep-awake/ios`)
- ExpoLinearGradient (from `../../../packages/expo-linear-gradient/ios`)
- ExpoLinking (from `../../../packages/expo-linking/ios`)
- - ExpoLivePhoto (from `../../../packages/expo-live-photo/ios`)
- ExpoLocalAuthentication (from `../../../packages/expo-local-authentication/ios`)
- ExpoLocalization (from `../../../packages/expo-localization/ios`)
- ExpoLocation (from `../../../packages/expo-location/ios`)
@@ -4054,11 +4043,10 @@ DEPENDENCIES:
- ExpoMailComposer (from `../../../packages/expo-mail-composer/ios`)
- ExpoMediaLibrary (from `../../../packages/expo-media-library/ios`)
- ExpoMediaLibrary/Tests (from `../../../packages/expo-media-library/ios`)
- - ExpoMeshGradient (from `../../../packages/expo-mesh-gradient/ios`)
- ExpoModulesCore (from `../../../packages/expo-modules-core`)
- ExpoModulesCore/Tests (from `../../../packages/expo-modules-core`)
- - ExpoModulesJSI (from `../../../packages/expo-modules-core`)
- - ExpoModulesJSI/Tests (from `../../../packages/expo-modules-core`)
+ - ExpoModulesJSI (from `../../../packages/expo-modules-jsi/apple`)
+ - ExpoModulesJSI/Tests (from `../../../packages/expo-modules-jsi/apple`)
- ExpoModulesTestCore (from `../../../packages/expo-modules-test-core/ios`)
- ExpoModulesWorklets (from `../../../packages/expo-modules-core`)
- ExpoNetwork (from `../../../packages/expo-network/ios`)
@@ -4259,10 +4247,6 @@ EXTERNAL SOURCES:
:path: "../../../packages/expo-manifests/ios"
Expo:
:path: "../../../packages/expo"
- ExpoAgeRange:
- :path: "../../../packages/expo-age-range/ios"
- ExpoAppleAuthentication:
- :path: "../../../packages/expo-apple-authentication/ios"
ExpoAsset:
:path: "../../../packages/expo-asset/ios"
ExpoAudio:
@@ -4321,8 +4305,6 @@ EXTERNAL SOURCES:
:path: "../../../packages/expo-linear-gradient/ios"
ExpoLinking:
:path: "../../../packages/expo-linking/ios"
- ExpoLivePhoto:
- :path: "../../../packages/expo-live-photo/ios"
ExpoLocalAuthentication:
:path: "../../../packages/expo-local-authentication/ios"
ExpoLocalization:
@@ -4335,12 +4317,10 @@ EXTERNAL SOURCES:
:path: "../../../packages/expo-mail-composer/ios"
ExpoMediaLibrary:
:path: "../../../packages/expo-media-library/ios"
- ExpoMeshGradient:
- :path: "../../../packages/expo-mesh-gradient/ios"
ExpoModulesCore:
:path: "../../../packages/expo-modules-core"
ExpoModulesJSI:
- :path: "../../../packages/expo-modules-core"
+ :path: "../../../packages/expo-modules-jsi/apple"
ExpoModulesTestCore:
:path: "../../../packages/expo-modules-test-core/ios"
ExpoModulesWorklets:
@@ -4596,9 +4576,7 @@ SPEC CHECKSUMS:
EXConstants: ed3e09607493bb717a7932f0e6786846169692a2
EXJSONUtils: 475f1af3d20fbd99268694df81d096817092aa89
EXManifests: ad24b3a497444eee9e07cc8590b09974ba369050
- Expo: afa1e57c45ee9b2b4d2b048df78f1de8849eb507
- ExpoAgeRange: 87dd980cb26e47b1108e24c39bcfb88a9ab1030e
- ExpoAppleAuthentication: c8096cd35e4e4f88aec34fcbfcd9deae7cea04d3
+ Expo: be6381e9c1390a5be199a157939b52ef0a461dc0
ExpoAsset: 595cc0587b67afaaedddca79d4c1cf506400cb27
ExpoAudio: 40c883c60ed3bb17ecb3744a00732fb47029535b
ExpoBackgroundFetch: ee315c8d1a54a3f358fc0cedb68e808e95935c57
@@ -4628,16 +4606,14 @@ SPEC CHECKSUMS:
ExpoKeepAwake: 72475c0e8644ad4dae17742bde04d5a0fe4ad4f9
ExpoLinearGradient: 76405dc81ad27c300347829ed90ba26e7597e846
ExpoLinking: 8505f5b8fa023e82b9d3ac7be1f1241f35b415dd
- ExpoLivePhoto: cebdeea82761402e1bb2e0b61bd1622d889c729e
ExpoLocalAuthentication: efe64e98f2686d8bd0bd04300ba5ed360b3d0100
ExpoLocalization: 19a0300c9fe626c884ce65cbbc421f9980bd5636
ExpoLocation: 957da1f900af30208ee9245590dbd50c9d267709
ExpoLogBox: 4a892fbcf6e343ffc1a6b714dcdc04a754d7e42f
ExpoMailComposer: edd4f46a26ea55ff0e3a83ef0192e79f647911c8
ExpoMediaLibrary: 2fbddcb06042f43076cf5e495e8237ec2ab95726
- ExpoMeshGradient: 93cf09380e6d86cd7a525da26dfddab2620a8421
- ExpoModulesCore: 13bc5b9a2fefacfab477b20600dc57821aa4a200
- ExpoModulesJSI: 793687b04cbaa73ee8548bfdbcfba85954d1b2b5
+ ExpoModulesCore: d39abb48e55d828ac2abd4800e226244bcdb6447
+ ExpoModulesJSI: 16f789b94db843249981e5f550e050cc321fe554
ExpoModulesTestCore: 62ce59e8c8162b449e65467e0421240256ba6732
ExpoModulesWorklets: 3a4d6451e29822c01c397da92be1f962a0f870fe
ExpoNetwork: 15d026c5c28251e0810849c8c01ebc9bc73ad007
@@ -4661,7 +4637,7 @@ SPEC CHECKSUMS:
ExpoVideoThumbnails: 2340f0b7f599c9ce6ba49a885f783de919cf4dd3
ExpoWebBrowser: b65b3921741b51c5513e2a369f59c37076987d9b
EXStructuredHeaders: e25ac67c966d3795153dfdb40bfd3b999df18929
- EXUpdates: 56ce06b32bea90efb1e4c6b28d73e4cfb472b71b
+ EXUpdates: 33cea909186babc6befefeed3596a82b6c1d63de
EXUpdatesInterface: 5ab8c3e8018ef533a132b9327af5b2a1926dd299
fast_float: b32c788ed9c6a8c584d114d0047beda9664e7cc6
FBLazyVector: 4cd65993d9ef677523093deeb7d8710f39fb9ed7
diff --git a/apps/native-component-list/src/screens/Crypto/GeneralCryptoScreen.tsx b/apps/native-component-list/src/screens/Crypto/GeneralCryptoScreen.tsx
index c305a7362ba352..b750a7e8d720f2 100644
--- a/apps/native-component-list/src/screens/Crypto/GeneralCryptoScreen.tsx
+++ b/apps/native-component-list/src/screens/Crypto/GeneralCryptoScreen.tsx
@@ -1,6 +1,6 @@
import * as Crypto from 'expo-crypto';
import { CryptoDigestAlgorithm, CryptoEncoding } from 'expo-crypto';
-import { ScrollView, StyleSheet, Text } from 'react-native';
+import { Platform, ScrollView, StyleSheet, Text } from 'react-native';
import FunctionDemo, { FunctionDescription } from '../../components/FunctionDemo';
@@ -58,19 +58,25 @@ const RANDOM_UUID: FunctionDescription = {
actions: Crypto.randomUUID,
};
+const DIGEST_ALGORITHMS = [
+ CryptoDigestAlgorithm.MD5,
+ CryptoDigestAlgorithm.SHA1,
+ CryptoDigestAlgorithm.SHA256,
+ CryptoDigestAlgorithm.SHA384,
+ CryptoDigestAlgorithm.SHA512,
+];
+
+if (Platform.OS === 'ios') {
+ DIGEST_ALGORITHMS.push(CryptoDigestAlgorithm.MD2, CryptoDigestAlgorithm.MD4);
+}
+
const DIGEST_STRING: FunctionDescription = {
name: 'digestString',
parameters: [
{
name: 'algorithm',
type: 'string',
- values: [
- CryptoDigestAlgorithm.MD5,
- CryptoDigestAlgorithm.SHA1,
- CryptoDigestAlgorithm.SHA256,
- CryptoDigestAlgorithm.SHA384,
- CryptoDigestAlgorithm.SHA512,
- ],
+ values: DIGEST_ALGORITHMS,
},
{
name: 'data',
@@ -98,15 +104,7 @@ const DIGEST: FunctionDescription = {
{
name: 'algorithm',
type: 'string',
- values: [
- CryptoDigestAlgorithm.SHA1,
- CryptoDigestAlgorithm.SHA256,
- CryptoDigestAlgorithm.SHA384,
- CryptoDigestAlgorithm.SHA512,
- CryptoDigestAlgorithm.MD2,
- CryptoDigestAlgorithm.MD5,
- CryptoDigestAlgorithm.MD4,
- ],
+ values: DIGEST_ALGORITHMS,
},
{
name: 'data',
diff --git a/apps/native-component-list/src/screens/SensorScreen.tsx b/apps/native-component-list/src/screens/SensorScreen.tsx
index 27049f9b72d2e1..a9c19329321134 100644
--- a/apps/native-component-list/src/screens/SensorScreen.tsx
+++ b/apps/native-component-list/src/screens/SensorScreen.tsx
@@ -1,7 +1,7 @@
import { type EventSubscription } from 'expo-modules-core';
import * as Sensors from 'expo-sensors';
import React, { useEffect, useState } from 'react';
-import { ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
+import { Platform, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
const FAST_INTERVAL = 16;
const SLOW_INTERVAL = 1000;
@@ -21,7 +21,7 @@ export default class SensorScreen extends React.Component {
-
+ {Platform.OS === 'ios' && }
);
}
diff --git a/apps/native-component-list/src/screens/VideoThumbnailsScreen.tsx b/apps/native-component-list/src/screens/VideoThumbnailsScreen.tsx
index f879617a11e226..618b89f4240f20 100644
--- a/apps/native-component-list/src/screens/VideoThumbnailsScreen.tsx
+++ b/apps/native-component-list/src/screens/VideoThumbnailsScreen.tsx
@@ -21,11 +21,7 @@ export default function VideoThumbnailsScreen() {
{image && }
{image}
+
+---
+
+The `@expo/codemod` package is a CLI binary provided to help you upgrade between Expo SDK versions.
+
+```
+npx @expo/codemod
+```
+
+## Usage
+
+Run a transform against one or more paths or globs:
+
+```sh
+npx @expo/codemod
+```
+
+```
+Options:
+ (required) name of transform to apply to files
+ (see a list of transforms available below)
+ one or more paths or globs (e.g. src/**/*.tsx) of sources to transform
+ -h, --help print this help message
+ -v, --version print the CLI version
+```
+
+For example, to run a transform over everything under `src`:
+
+```sh
+npx @expo/codemod sdk-56-expo-router-react-navigation-replace src
+```
+
+Globs work too (wrap them in quotes so the shell doesn't expand them):
+
+```sh
+npx @expo/codemod sdk-56-expo-router-react-navigation-replace '**/*.{ts,tsx,js,jsx}'
+```
+
+## Transforms
+
+### `sdk-56-expo-router-react-navigation-replace`
+
+_Used to migrate a React Navigation app to Expo Router (SDK 56)._
+
+Replaces imports from `@react-navigation/*` with their `expo-router` equivalents.
+
+| From | To |
+| ----------------------------------- | -------------------------- |
+| `@react-navigation/native` | `expo-router` |
+| `@react-navigation/stack` | `expo-router/js-stack` |
+| `@react-navigation/bottom-tabs` | `expo-router/js-tabs` |
+| `@react-navigation/material-top-tabs` | `expo-router/js-top-tabs` |
+
+Default and namespace imports (`import X from ...` / `import * as X from ...`) from the mapped packages are not supported.
+
+#### Example
+
+Input:
+
+```ts
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import { useRouter } from 'expo-router';
+```
+
+Output:
+
+```ts
+import { NavigationContainer, useNavigation, useRouter } from "expo-router";
+```
+
+# Contributing
+
+## Adding a new transform
+
+1. Drop a new file under `src/transforms/` named `sdk--.ts` exporting a default jscodeshift `Transform` function. After the next build the runtime glob in [`src/transforms/index.ts`](./src/transforms/index.ts) picks it up automatically — no other wiring needed.
+2. Add a sibling test under `src/transforms/__tests__/-test.ts` using `applyTransform` from `jscodeshift/dist/testUtils`.
+3. Document the transform under [`## Transforms`](#transforms) above with a from/to table and a short example.
diff --git a/packages/@expo/codemod/bin/expo-codemod.js b/packages/@expo/codemod/bin/expo-codemod.js
new file mode 100755
index 00000000000000..3508e9786b7042
--- /dev/null
+++ b/packages/@expo/codemod/bin/expo-codemod.js
@@ -0,0 +1,3 @@
+#!/usr/bin/env node
+
+require('../build/index.js');
diff --git a/packages/@expo/codemod/build/index.d.ts b/packages/@expo/codemod/build/index.d.ts
new file mode 100644
index 00000000000000..b5e1e34a1f7399
--- /dev/null
+++ b/packages/@expo/codemod/build/index.d.ts
@@ -0,0 +1 @@
+export type Command = (argv?: string[]) => Promise;
diff --git a/packages/@expo/codemod/build/index.js b/packages/@expo/codemod/build/index.js
new file mode 100644
index 00000000000000..91e2d9007b5d7c
--- /dev/null
+++ b/packages/@expo/codemod/build/index.js
@@ -0,0 +1,70 @@
+"use strict";
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
+}) : (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+ o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || (function () {
+ var ownKeys = function(o) {
+ ownKeys = Object.getOwnPropertyNames || function (o) {
+ var ar = [];
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
+ return ar;
+ };
+ return ownKeys(o);
+ };
+ return function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+})();
+var __importDefault = (this && this.__importDefault) || function (mod) {
+ return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const path_1 = __importDefault(require("path"));
+const util_1 = require("util");
+const Log = __importStar(require("./log"));
+async function main() {
+ const argv = process.argv.slice(2);
+ // strict:false so unknown flags pass through to the inner runCommand parser,
+ // which owns the strict validation.
+ const { values } = (0, util_1.parseArgs)({
+ args: argv,
+ options: {
+ version: { type: 'boolean', short: 'v' },
+ },
+ allowPositionals: true,
+ strict: false,
+ });
+ if (values.version) {
+ // After build, this file is at build/index.js, so the package root is one level up.
+ const pkg = require(path_1.default.resolve(__dirname, '..', 'package.json'));
+ Log.log(pkg.version);
+ process.exit(0);
+ }
+ const { runCommand } = await import('./run/index.js');
+ await runCommand(argv);
+}
+process.on('SIGINT', () => process.exit(0));
+process.on('SIGTERM', () => process.exit(0));
+main().catch((error) => {
+ const message = error instanceof Error ? error.message : String(error);
+ Log.error(message);
+ process.exit(1);
+});
+//# sourceMappingURL=index.js.map
\ No newline at end of file
diff --git a/packages/@expo/codemod/build/index.js.map b/packages/@expo/codemod/build/index.js.map
new file mode 100644
index 00000000000000..aaf1bff479ddfc
--- /dev/null
+++ b/packages/@expo/codemod/build/index.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gDAAwB;AACxB,+BAAiC;AAEjC,2CAA6B;AAI7B,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAEnC,6EAA6E;IAC7E,oCAAoC;IACpC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,gBAAS,EAAC;QAC3B,IAAI,EAAE,IAAI;QACV,OAAO,EAAE;YACP,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE;SACzC;QACD,gBAAgB,EAAE,IAAI;QACtB,MAAM,EAAE,KAAK;KACd,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,oFAAoF;QACpF,MAAM,GAAG,GAAG,OAAO,CAAC,cAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC;QACnE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACtD,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;AACzB,CAAC;AAED,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5C,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAE7C,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC9B,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
\ No newline at end of file
diff --git a/packages/@expo/codemod/build/log.d.ts b/packages/@expo/codemod/build/log.d.ts
new file mode 100644
index 00000000000000..8dacb1c72c1f21
--- /dev/null
+++ b/packages/@expo/codemod/build/log.d.ts
@@ -0,0 +1,6 @@
+export declare function log(...message: string[]): void;
+export declare function error(...message: string[]): void;
+/** Print an error and provide additional info (the stack trace) in debug mode. */
+export declare function exception(e: Error): void;
+/** Log a message and exit the current process. If the `code` is non-zero then `console.error` will be used instead of `console.log`. */
+export declare function exit(message: string | Error, code?: number): never;
diff --git a/packages/@expo/codemod/build/log.js b/packages/@expo/codemod/build/log.js
new file mode 100644
index 00000000000000..041eeb04642023
--- /dev/null
+++ b/packages/@expo/codemod/build/log.js
@@ -0,0 +1,41 @@
+"use strict";
+var __importDefault = (this && this.__importDefault) || function (mod) {
+ return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.log = log;
+exports.error = error;
+exports.exception = exception;
+exports.exit = exit;
+/**
+ * These functions are copied from packages/@expo/cli/src/log.ts
+ */
+const chalk_1 = __importDefault(require("chalk"));
+function log(...message) {
+ console.log(...message);
+}
+function error(...message) {
+ console.error(...message);
+}
+/** Print an error and provide additional info (the stack trace) in debug mode. */
+function exception(e) {
+ const { env } = require('./utils/env');
+ error(chalk_1.default.red(e.toString()) + (env.EXPO_DEBUG ? '\n' + chalk_1.default.gray(e.stack) : ''));
+}
+/** Log a message and exit the current process. If the `code` is non-zero then `console.error` will be used instead of `console.log`. */
+function exit(message, code = 1) {
+ if (message instanceof Error) {
+ exception(message);
+ process.exit(code);
+ }
+ if (message) {
+ if (code === 0) {
+ log(message);
+ }
+ else {
+ error(message);
+ }
+ }
+ process.exit(code);
+}
+//# sourceMappingURL=log.js.map
\ No newline at end of file
diff --git a/packages/@expo/codemod/build/log.js.map b/packages/@expo/codemod/build/log.js.map
new file mode 100644
index 00000000000000..b1fdf6198fbf42
--- /dev/null
+++ b/packages/@expo/codemod/build/log.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":";;;;;AAKA,kBAEC;AAED,sBAEC;AAGD,8BAGC;AAGD,oBAeC;AAnCD;;GAEG;AACH,kDAA0B;AAE1B,SAAgB,GAAG,CAAC,GAAG,OAAiB;IACtC,OAAO,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC;AAC1B,CAAC;AAED,SAAgB,KAAK,CAAC,GAAG,OAAiB;IACxC,OAAO,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED,kFAAkF;AAClF,SAAgB,SAAS,CAAC,CAAQ;IAChC,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACvC,KAAK,CAAC,eAAK,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,GAAG,eAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACtF,CAAC;AAED,wIAAwI;AACxI,SAAgB,IAAI,CAAC,OAAuB,EAAE,OAAe,CAAC;IAC5D,IAAI,OAAO,YAAY,KAAK,EAAE,CAAC;QAC7B,SAAS,CAAC,OAAO,CAAC,CAAC;QACnB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC;IAED,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;YACf,GAAG,CAAC,OAAO,CAAC,CAAC;QACf,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,OAAO,CAAC,CAAC;QACjB,CAAC;IACH,CAAC;IAED,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC"}
\ No newline at end of file
diff --git a/packages/@expo/codemod/build/run/index.d.ts b/packages/@expo/codemod/build/run/index.d.ts
new file mode 100644
index 00000000000000..474b6240ef0ac2
--- /dev/null
+++ b/packages/@expo/codemod/build/run/index.d.ts
@@ -0,0 +1,17 @@
+import type { Command } from '../index';
+export type ParsedCommand = {
+ transform: string;
+ paths: string[];
+};
+/**
+ * Parse argv, validate it against the available transforms, and return the
+ * resolved command. Prints help and exits when --help is passed or required
+ * arguments are missing.
+ */
+export declare function parseAndValidateArgs(argv: string[] | undefined): Promise;
+/**
+ * Expand the given paths into a file list and dispatch them to the jscodeshift
+ * runner. Files are split by extension into the `tsx` and `jsx` parser buckets.
+ */
+export declare function resolveAndDispatch(command: ParsedCommand): Promise;
+export declare const runCommand: Command;
diff --git a/packages/@expo/codemod/build/run/index.js b/packages/@expo/codemod/build/run/index.js
new file mode 100644
index 00000000000000..17aaf4e15bf794
--- /dev/null
+++ b/packages/@expo/codemod/build/run/index.js
@@ -0,0 +1,136 @@
+"use strict";
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
+}) : (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+ o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || (function () {
+ var ownKeys = function(o) {
+ ownKeys = Object.getOwnPropertyNames || function (o) {
+ var ar = [];
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
+ return ar;
+ };
+ return ownKeys(o);
+ };
+ return function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+})();
+var __importDefault = (this && this.__importDefault) || function (mod) {
+ return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.runCommand = void 0;
+exports.parseAndValidateArgs = parseAndValidateArgs;
+exports.resolveAndDispatch = resolveAndDispatch;
+const chalk_1 = __importDefault(require("chalk"));
+const path_1 = __importDefault(require("path"));
+const tinyglobby_1 = require("tinyglobby");
+const Log = __importStar(require("../log"));
+const transforms_1 = require("../transforms");
+const args_1 = require("../utils/args");
+const runner_1 = require("../utils/runner");
+const transformsBlock = (transforms) => ['', ` ${chalk_1.default.bold('Transforms available')}`, ...transforms.map((t) => ` ${t}`), ''].join('\n');
+/**
+ * Parse argv, validate it against the available transforms, and return the
+ * resolved command. Prints help and exits when --help is passed or required
+ * arguments are missing.
+ */
+async function parseAndValidateArgs(argv) {
+ const { values, positionals } = (0, args_1.parseArgsOrExit)({
+ args: argv,
+ options: {
+ help: { type: 'boolean', short: 'h' },
+ },
+ allowPositionals: true,
+ strict: true,
+ });
+ const transforms = await (0, transforms_1.listTransformsAsync)();
+ const [transform, ...paths] = positionals;
+ if (values.help || !transform || paths.length === 0) {
+ (0, args_1.printHelp)('Run a codemod transform against the given paths.', 'npx @expo/codemod ', [
+ ' (required) name of transform to apply to files',
+ ' (see a list of transforms available below)',
+ ' one or more paths or globs (e.g. src/**/*.tsx)',
+ '-h, --help print this help message',
+ '-v, --version print the CLI version',
+ ].join('\n'), transformsBlock(transforms));
+ }
+ if (!transforms.includes(transform)) {
+ Log.exit(`Transform "${transform}" does not exist. Valid options: ${transforms.join(', ')}`);
+ }
+ return { transform, paths };
+}
+/**
+ * Expand the given paths into a file list and dispatch them to the jscodeshift
+ * runner. Files are split by extension into the `tsx` and `jsx` parser buckets.
+ */
+async function resolveAndDispatch(command) {
+ const { transform, paths } = command;
+ const allFiles = await (0, tinyglobby_1.glob)(paths, {
+ ignore: ['**/node_modules/**'],
+ });
+ const tsxFiles = [];
+ const tsFiles = [];
+ const jsxFiles = [];
+ for (const file of allFiles) {
+ const ext = path_1.default.extname(file);
+ if (ext === '.tsx')
+ tsxFiles.push(file);
+ else if (ext === '.ts')
+ tsFiles.push(file);
+ else if (ext === '.js' || ext === '.jsx')
+ jsxFiles.push(file);
+ }
+ const mappings = {
+ ts: tsFiles,
+ tsx: tsxFiles,
+ jsx: jsxFiles,
+ };
+ const stats = await Promise.all(Object.entries(mappings)
+ .filter(([_, files]) => files.length)
+ .map(async ([parser, files]) => {
+ Log.log(`Transforming ${files.length} ${parser.toUpperCase()} files...`);
+ return await (0, runner_1.runTransformAsync)({
+ files,
+ parser: parser,
+ transform,
+ });
+ }));
+ const combinedStats = stats.reduce((acc, { error, ok, nochange, skip, timeElapsed }) => ({
+ error: acc.error + error,
+ ok: acc.ok + ok,
+ nochange: acc.nochange + nochange,
+ skip: acc.skip + skip,
+ timeElapsed: Math.max(acc.timeElapsed, Number(timeElapsed)),
+ }), { error: 0, ok: 0, nochange: 0, skip: 0, timeElapsed: 0 });
+ Log.log('');
+ Log.log('Results:');
+ Log.log(chalk_1.default.red(` ${combinedStats.error} errors`));
+ // Log.log(chalk.yellow(` ${combinedStats.nochange} unmodified`));
+ Log.log(chalk_1.default.yellow(` ${combinedStats.skip} skipped`));
+ Log.log(chalk_1.default.green(` ${combinedStats.ok} ok`));
+ Log.log(` Time elapsed: ${combinedStats.timeElapsed.toFixed(2)}s`);
+}
+const runCommand = async (argv) => {
+ const command = await parseAndValidateArgs(argv);
+ await resolveAndDispatch(command);
+};
+exports.runCommand = runCommand;
+//# sourceMappingURL=index.js.map
\ No newline at end of file
diff --git a/packages/@expo/codemod/build/run/index.js.map b/packages/@expo/codemod/build/run/index.js.map
new file mode 100644
index 00000000000000..66bd2feb12285b
--- /dev/null
+++ b/packages/@expo/codemod/build/run/index.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/run/index.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/packages/@expo/codemod/build/transforms/index.d.ts b/packages/@expo/codemod/build/transforms/index.d.ts
new file mode 100644
index 00000000000000..2e827f6c14f80a
--- /dev/null
+++ b/packages/@expo/codemod/build/transforms/index.d.ts
@@ -0,0 +1,3 @@
+export declare const TRANSFORM_DIR: string;
+export declare function listTransformsAsync(): Promise;
+export declare function transformFilePath(transform: string): string;
diff --git a/packages/@expo/codemod/build/transforms/index.js b/packages/@expo/codemod/build/transforms/index.js
new file mode 100644
index 00000000000000..bd598d464a90fd
--- /dev/null
+++ b/packages/@expo/codemod/build/transforms/index.js
@@ -0,0 +1,23 @@
+"use strict";
+var __importDefault = (this && this.__importDefault) || function (mod) {
+ return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.TRANSFORM_DIR = void 0;
+exports.listTransformsAsync = listTransformsAsync;
+exports.transformFilePath = transformFilePath;
+const path_1 = __importDefault(require("path"));
+const tinyglobby_1 = require("tinyglobby");
+exports.TRANSFORM_DIR = __dirname;
+async function listTransformsAsync() {
+ // *.js, since this function will be called from within build folder
+ const modules = await (0, tinyglobby_1.glob)(['*.js'], { cwd: exports.TRANSFORM_DIR });
+ return modules
+ .map((filename) => path_1.default.basename(filename, '.js'))
+ .filter((name) => name !== 'index')
+ .sort();
+}
+function transformFilePath(transform) {
+ return path_1.default.join(exports.TRANSFORM_DIR, `${transform}.js`);
+}
+//# sourceMappingURL=index.js.map
\ No newline at end of file
diff --git a/packages/@expo/codemod/build/transforms/index.js.map b/packages/@expo/codemod/build/transforms/index.js.map
new file mode 100644
index 00000000000000..ad1918fa769de4
--- /dev/null
+++ b/packages/@expo/codemod/build/transforms/index.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/transforms/index.ts"],"names":[],"mappings":";;;;;;AAKA,kDAOC;AAED,8CAEC;AAhBD,gDAAwB;AACxB,2CAAkC;AAErB,QAAA,aAAa,GAAG,SAAS,CAAC;AAEhC,KAAK,UAAU,mBAAmB;IACvC,oEAAoE;IACpE,MAAM,OAAO,GAAG,MAAM,IAAA,iBAAI,EAAC,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,qBAAa,EAAE,CAAC,CAAC;IAC7D,OAAO,OAAO;SACX,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,cAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;SACjD,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,OAAO,CAAC;SAClC,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,SAAgB,iBAAiB,CAAC,SAAiB;IACjD,OAAO,cAAI,CAAC,IAAI,CAAC,qBAAa,EAAE,GAAG,SAAS,KAAK,CAAC,CAAC;AACrD,CAAC"}
\ No newline at end of file
diff --git a/packages/@expo/codemod/build/transforms/sdk-56-expo-router-react-navigation-replace.d.ts b/packages/@expo/codemod/build/transforms/sdk-56-expo-router-react-navigation-replace.d.ts
new file mode 100644
index 00000000000000..ea8cf0dd988cdf
--- /dev/null
+++ b/packages/@expo/codemod/build/transforms/sdk-56-expo-router-react-navigation-replace.d.ts
@@ -0,0 +1,14 @@
+/**
+ * Codemod: Replace @react-navigation/* imports with expo-router equivalents.
+ *
+ * Mapping:
+ * @react-navigation/native → expo-router
+ * @react-navigation/stack → expo-router/js-stack
+ * @react-navigation/bottom-tabs → expo-router/js-tabs
+ * @react-navigation/material-top-tabs → expo-router/js-top-tabs
+ *
+ * After replacement, duplicate `expo-router` imports are merged into one.
+ */
+import type { Transform } from 'jscodeshift';
+declare const transform: Transform;
+export default transform;
diff --git a/packages/@expo/codemod/build/transforms/sdk-56-expo-router-react-navigation-replace.js b/packages/@expo/codemod/build/transforms/sdk-56-expo-router-react-navigation-replace.js
new file mode 100644
index 00000000000000..a94965f993fa37
--- /dev/null
+++ b/packages/@expo/codemod/build/transforms/sdk-56-expo-router-react-navigation-replace.js
@@ -0,0 +1,106 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const IMPORT_MAP = {
+ '@react-navigation/native': 'expo-router',
+ '@react-navigation/elements': 'expo-router',
+ '@react-navigation/core': 'expo-router',
+ '@react-navigation/routers': 'expo-router',
+ '@react-navigation/stack': 'expo-router/js-stack',
+ '@react-navigation/bottom-tabs': 'expo-router/js-tabs',
+ '@react-navigation/material-top-tabs': 'expo-router/js-top-tabs',
+};
+const UNSUPPORTED_SPECIFIERS = {
+ ImportDefaultSpecifier: 'default import',
+ ImportNamespaceSpecifier: 'namespace import (import * as ...)',
+};
+const isTypeOnlyImport = (path) => path.node.importKind === 'type';
+/**
+ * Clone the specifier, changing its importKind to be type, for example:
+ * import { type A } from "b"
+ */
+const markAsInlineType = (spec) => {
+ const clone = { ...spec };
+ clone.importKind = 'type';
+ return clone;
+};
+/**
+ * Collects errors for unsupported import styles in the given declarations:
+ * default imports (`import A from "b"`) and namespace imports
+ * (`import * as A from "b"`). Only named imports (`import { A } from "b"`)
+ * can be safely rewritten.
+ */
+const collectUnsupportedImportStyleErrors = (filePath, paths) => {
+ const errors = [];
+ for (const declarationPath of paths) {
+ const specifiers = (declarationPath.node.specifiers ?? []);
+ for (const spec of specifiers) {
+ const label = UNSUPPORTED_SPECIFIERS[spec.type];
+ if (!label)
+ continue;
+ const line = declarationPath.node.loc?.start.line ?? '?';
+ const sourceModule = declarationPath.node.source.value;
+ errors.push(`${filePath}:${line} - ${label} from "${sourceModule}" is not supported. ` +
+ `Replace with named imports before running this codemod.`);
+ }
+ }
+ return errors;
+};
+const groupPathsBySource = (paths) => {
+ const groupsBySource = new Map();
+ for (const declarationPath of paths) {
+ const sourceModule = declarationPath.node.source.value;
+ const existing = groupsBySource.get(sourceModule);
+ if (existing) {
+ existing.push(declarationPath);
+ }
+ else {
+ groupsBySource.set(sourceModule, [declarationPath]);
+ }
+ }
+ return groupsBySource;
+};
+const mergeGroup = (j, groupPaths) => {
+ const hasAnyTypeOnlyImport = groupPaths.some(isTypeOnlyImport);
+ const allImportsAreTypeOnly = groupPaths.every(isTypeOnlyImport);
+ // Mixed: `import type { A }` + `import { B }` → `import { type A, B }`.
+ const mixesTypeAndValueImports = hasAnyTypeOnlyImport && !allImportsAreTypeOnly;
+ const [mergeTarget, ...declarationsToRemove] = groupPaths;
+ if (!mergeTarget)
+ return;
+ const specifiers = groupPaths.flatMap((declarationPath) => {
+ const specs = (declarationPath.node.specifiers ?? []);
+ return mixesTypeAndValueImports && isTypeOnlyImport(declarationPath)
+ ? specs.map(markAsInlineType)
+ : specs;
+ });
+ j(mergeTarget).replaceWith(j.importDeclaration(specifiers, mergeTarget.node.source, allImportsAreTypeOnly ? 'type' : 'value'));
+ for (const declarationPath of declarationsToRemove)
+ j(declarationPath).remove();
+};
+const transform = (fileInfo, api) => {
+ const j = api.jscodeshift;
+ const root = j(fileInfo.source);
+ const mappablePaths = root
+ .find(j.ImportDeclaration)
+ .filter((path) => path.node.source.value in IMPORT_MAP)
+ .paths();
+ if (mappablePaths.length === 0)
+ return undefined;
+ const errors = collectUnsupportedImportStyleErrors(fileInfo.path, mappablePaths);
+ if (errors.length > 0) {
+ throw new Error(`Unsupported import style(s) found:\n${errors.join('\n')}`);
+ }
+ for (const path of mappablePaths) {
+ const sourceModule = path.node.source.value;
+ path.node.source.value = IMPORT_MAP[sourceModule];
+ }
+ const groups = groupPathsBySource(root.find(j.ImportDeclaration).paths());
+ for (const [importPath, groupPaths] of groups.entries()) {
+ if (groupPaths.length > 1 && importPath.startsWith('expo-router')) {
+ mergeGroup(j, groupPaths);
+ }
+ }
+ return root.toSource();
+};
+exports.default = transform;
+//# sourceMappingURL=sdk-56-expo-router-react-navigation-replace.js.map
\ No newline at end of file
diff --git a/packages/@expo/codemod/build/transforms/sdk-56-expo-router-react-navigation-replace.js.map b/packages/@expo/codemod/build/transforms/sdk-56-expo-router-react-navigation-replace.js.map
new file mode 100644
index 00000000000000..99acd6eadff746
--- /dev/null
+++ b/packages/@expo/codemod/build/transforms/sdk-56-expo-router-react-navigation-replace.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"sdk-56-expo-router-react-navigation-replace.js","sourceRoot":"","sources":["../../src/transforms/sdk-56-expo-router-react-navigation-replace.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/packages/@expo/codemod/build/utils/args.d.ts b/packages/@expo/codemod/build/utils/args.d.ts
new file mode 100644
index 00000000000000..cf89eaab4d2336
--- /dev/null
+++ b/packages/@expo/codemod/build/utils/args.d.ts
@@ -0,0 +1,3 @@
+import { parseArgs, type ParseArgsConfig } from 'util';
+export declare function parseArgsOrExit(config: T): ReturnType>;
+export declare function printHelp(info: string, usage: string, options: string, extra?: string): never;
diff --git a/packages/@expo/codemod/build/utils/args.js b/packages/@expo/codemod/build/utils/args.js
new file mode 100644
index 00000000000000..3828b79725e756
--- /dev/null
+++ b/packages/@expo/codemod/build/utils/args.js
@@ -0,0 +1,73 @@
+"use strict";
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
+}) : (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+ o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || (function () {
+ var ownKeys = function(o) {
+ ownKeys = Object.getOwnPropertyNames || function (o) {
+ var ar = [];
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
+ return ar;
+ };
+ return ownKeys(o);
+ };
+ return function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+})();
+var __importDefault = (this && this.__importDefault) || function (mod) {
+ return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.parseArgsOrExit = parseArgsOrExit;
+exports.printHelp = printHelp;
+const chalk_1 = __importDefault(require("chalk"));
+const util_1 = require("util");
+const Log = __importStar(require("../log"));
+const isParseArgsError = (error) => {
+ if (!error || typeof error !== 'object' || !('code' in error))
+ return false;
+ const code = error.code;
+ return typeof code === 'string' && code.startsWith('ERR_PARSE_ARGS_');
+};
+function parseArgsOrExit(config) {
+ try {
+ return (0, util_1.parseArgs)(config);
+ }
+ catch (error) {
+ if (isParseArgsError(error)) {
+ Log.exit(error.message, 1);
+ }
+ throw error;
+ }
+}
+function printHelp(info, usage, options, extra = '') {
+ Log.exit((0, chalk_1.default) `
+ {bold Info}
+ ${info}
+
+ {bold Usage}
+ {dim $} ${usage}
+
+ {bold Options}
+ ${options.split('\n').join('\n ')}
+` + extra, 0);
+}
+//# sourceMappingURL=args.js.map
\ No newline at end of file
diff --git a/packages/@expo/codemod/build/utils/args.js.map b/packages/@expo/codemod/build/utils/args.js.map
new file mode 100644
index 00000000000000..524a6cb1987da4
--- /dev/null
+++ b/packages/@expo/codemod/build/utils/args.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/utils/args.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWA,0CAWC;AAED,8BAcC;AAtCD,kDAA0B;AAC1B,+BAAuD;AAEvD,4CAA8B;AAE9B,MAAM,gBAAgB,GAAG,CAAC,KAAc,EAAqC,EAAE;IAC7E,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5E,MAAM,IAAI,GAAI,KAA2B,CAAC,IAAI,CAAC;IAC/C,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;AACxE,CAAC,CAAC;AAEF,SAAgB,eAAe,CAC7B,MAAS;IAET,IAAI,CAAC;QACH,OAAO,IAAA,gBAAS,EAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,IAAI,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5B,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC7B,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAgB,SAAS,CAAC,IAAY,EAAE,KAAa,EAAE,OAAe,EAAE,QAAgB,EAAE;IACxF,GAAG,CAAC,IAAI,CACN,IAAA,eAAK,EAAA;;MAEH,IAAI;;;cAGI,KAAK;;;MAGb,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC;CACvC,GAAG,KAAK,EACL,CAAC,CACF,CAAC;AACJ,CAAC"}
\ No newline at end of file
diff --git a/packages/@expo/codemod/build/utils/runner.d.ts b/packages/@expo/codemod/build/utils/runner.d.ts
new file mode 100644
index 00000000000000..484f7fdacd4830
--- /dev/null
+++ b/packages/@expo/codemod/build/utils/runner.d.ts
@@ -0,0 +1,7 @@
+import Runner from 'jscodeshift/src/Runner';
+export type ParserKind = 'tsx' | 'jsx' | 'ts';
+export declare function runTransformAsync({ files, parser, transform, }: {
+ files: string[];
+ parser: ParserKind;
+ transform: string;
+}): ReturnType<(typeof Runner)['run']>;
diff --git a/packages/@expo/codemod/build/utils/runner.js b/packages/@expo/codemod/build/utils/runner.js
new file mode 100644
index 00000000000000..270e133cd073fa
--- /dev/null
+++ b/packages/@expo/codemod/build/utils/runner.js
@@ -0,0 +1,23 @@
+"use strict";
+var __importDefault = (this && this.__importDefault) || function (mod) {
+ return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.runTransformAsync = runTransformAsync;
+const Runner_1 = __importDefault(require("jscodeshift/src/Runner"));
+const transforms_1 = require("../transforms");
+const JSCODESHIFT_PARSER = {
+ tsx: 'tsx',
+ jsx: 'babel',
+ ts: 'ts',
+};
+async function runTransformAsync({ files, parser, transform, }) {
+ return Runner_1.default.run((0, transforms_1.transformFilePath)(transform), files, {
+ // Transforms are pre-compiled to JS by our build, so jscodeshift's @babel/register hook is unnecessary.
+ babel: false,
+ parser: JSCODESHIFT_PARSER[parser],
+ verbose: 0,
+ silent: true,
+ });
+}
+//# sourceMappingURL=runner.js.map
\ No newline at end of file
diff --git a/packages/@expo/codemod/build/utils/runner.js.map b/packages/@expo/codemod/build/utils/runner.js.map
new file mode 100644
index 00000000000000..616607413ba931
--- /dev/null
+++ b/packages/@expo/codemod/build/utils/runner.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/utils/runner.ts"],"names":[],"mappings":";;;;;AAYA,8CAgBC;AA5BD,oEAA4C;AAE5C,8CAAkD;AAIlD,MAAM,kBAAkB,GAA+C;IACrE,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,OAAO;IACZ,EAAE,EAAE,IAAI;CACT,CAAC;AAEK,KAAK,UAAU,iBAAiB,CAAC,EACtC,KAAK,EACL,MAAM,EACN,SAAS,GAKV;IACC,OAAO,gBAAM,CAAC,GAAG,CAAC,IAAA,8BAAiB,EAAC,SAAS,CAAC,EAAE,KAAK,EAAE;QACrD,wGAAwG;QACxG,KAAK,EAAE,KAAK;QACZ,MAAM,EAAE,kBAAkB,CAAC,MAAM,CAAC;QAClC,OAAO,EAAE,CAAC;QACV,MAAM,EAAE,IAAI;KACb,CAAC,CAAC;AACL,CAAC"}
\ No newline at end of file
diff --git a/packages/@expo/codemod/jest.config.js b/packages/@expo/codemod/jest.config.js
new file mode 100644
index 00000000000000..88ac58c2cae990
--- /dev/null
+++ b/packages/@expo/codemod/jest.config.js
@@ -0,0 +1,9 @@
+/** @type {import('jest').Config} */
+module.exports = {
+ ...require('expo-module-scripts/jest-preset-cli'),
+ preset: 'ts-jest',
+ clearMocks: true,
+ displayName: require('./package').name,
+ rootDir: __dirname,
+ roots: ['src'],
+};
diff --git a/packages/@expo/codemod/package.json b/packages/@expo/codemod/package.json
new file mode 100644
index 00000000000000..78222dd84f8b47
--- /dev/null
+++ b/packages/@expo/codemod/package.json
@@ -0,0 +1,52 @@
+{
+ "name": "@expo/codemod",
+ "private": true,
+ "version": "0.1.0",
+ "description": "Codemods for migrating Expo apps between SDK versions",
+ "license": "MIT",
+ "keywords": [
+ "codemod",
+ "expo",
+ "expo-router",
+ "react-navigation"
+ ],
+ "homepage": "https://github.com/expo/expo/tree/main/packages/@expo/codemod",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/expo/expo.git",
+ "directory": "packages/@expo/codemod"
+ },
+ "bugs": {
+ "url": "https://github.com/expo/expo/issues"
+ },
+ "bin": {
+ "expo-codemod": "./bin/expo-codemod.js"
+ },
+ "main": "build",
+ "files": [
+ "bin",
+ "build"
+ ],
+ "scripts": {
+ "build": "expo-module tsc",
+ "clean": "expo-module clean",
+ "lint": "expo-module lint",
+ "typecheck": "expo-module typecheck",
+ "test": "expo-module test",
+ "watch": "pnpm run build --watch --preserveWatchOutput",
+ "prepublishOnly": "pnpm run clean && pnpm run build"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "dependencies": {
+ "chalk": "^4.1.2",
+ "jscodeshift": "^17.1.2",
+ "tinyglobby": "^0.2.15"
+ },
+ "devDependencies": {
+ "@types/jscodeshift": "^17.0.0",
+ "@types/node": "^22.14.0",
+ "expo-module-scripts": "workspace:*"
+ }
+}
diff --git a/packages/@expo/codemod/src/index.ts b/packages/@expo/codemod/src/index.ts
new file mode 100644
index 00000000000000..7206e5e56da9e3
--- /dev/null
+++ b/packages/@expo/codemod/src/index.ts
@@ -0,0 +1,40 @@
+import path from 'path';
+import { parseArgs } from 'util';
+
+import * as Log from './log';
+
+export type Command = (argv?: string[]) => Promise;
+
+async function main(): Promise {
+ const argv = process.argv.slice(2);
+
+ // strict:false so unknown flags pass through to the inner runCommand parser,
+ // which owns the strict validation.
+ const { values } = parseArgs({
+ args: argv,
+ options: {
+ version: { type: 'boolean', short: 'v' },
+ },
+ allowPositionals: true,
+ strict: false,
+ });
+
+ if (values.version) {
+ // After build, this file is at build/index.js, so the package root is one level up.
+ const pkg = require(path.resolve(__dirname, '..', 'package.json'));
+ Log.log(pkg.version);
+ process.exit(0);
+ }
+
+ const { runCommand } = await import('./run/index.js');
+ await runCommand(argv);
+}
+
+process.on('SIGINT', () => process.exit(0));
+process.on('SIGTERM', () => process.exit(0));
+
+main().catch((error: unknown) => {
+ const message = error instanceof Error ? error.message : String(error);
+ Log.error(message);
+ process.exit(1);
+});
diff --git a/packages/@expo/codemod/src/log.ts b/packages/@expo/codemod/src/log.ts
new file mode 100644
index 00000000000000..4a80e58bc5f1a2
--- /dev/null
+++ b/packages/@expo/codemod/src/log.ts
@@ -0,0 +1,36 @@
+/**
+ * These functions are copied from packages/@expo/cli/src/log.ts
+ */
+import chalk from 'chalk';
+
+export function log(...message: string[]): void {
+ console.log(...message);
+}
+
+export function error(...message: string[]): void {
+ console.error(...message);
+}
+
+/** Print an error and provide additional info (the stack trace) in debug mode. */
+export function exception(e: Error): void {
+ const { env } = require('./utils/env');
+ error(chalk.red(e.toString()) + (env.EXPO_DEBUG ? '\n' + chalk.gray(e.stack) : ''));
+}
+
+/** Log a message and exit the current process. If the `code` is non-zero then `console.error` will be used instead of `console.log`. */
+export function exit(message: string | Error, code: number = 1): never {
+ if (message instanceof Error) {
+ exception(message);
+ process.exit(code);
+ }
+
+ if (message) {
+ if (code === 0) {
+ log(message);
+ } else {
+ error(message);
+ }
+ }
+
+ process.exit(code);
+}
diff --git a/packages/@expo/codemod/src/run/__tests__/index-test.ts b/packages/@expo/codemod/src/run/__tests__/index-test.ts
new file mode 100644
index 00000000000000..ef22b72e74ca6b
--- /dev/null
+++ b/packages/@expo/codemod/src/run/__tests__/index-test.ts
@@ -0,0 +1,141 @@
+import { parseAndValidateArgs, resolveAndDispatch } from '../index';
+
+jest.mock('../../transforms', () => ({
+ listTransformsAsync: jest.fn().mockResolvedValue(['sdk-56-expo-router-react-navigation-replace']),
+ transformFilePath: (name: string) => `/fake/${name}.js`,
+}));
+
+jest.mock('../../utils/runner', () => ({
+ runTransformAsync: jest.fn(),
+}));
+
+// `Log.exit` calls `process.exit` in production. The tests replace it with a
+// thrower so expectations can assert that exit was reached without terminating
+// the test runner.
+jest.mock('../../log', () => ({
+ log: jest.fn(),
+ error: jest.fn(),
+ exit: jest.fn((message: string | Error, code: number = 1): never => {
+ const text = message instanceof Error ? message.message : message;
+ const err = new Error(text) as Error & { exitCode?: number };
+ err.exitCode = code;
+ throw err;
+ }),
+}));
+
+jest.mock('tinyglobby', () => ({ glob: jest.fn() }));
+
+const Log = jest.requireMock>('../../log');
+const { runTransformAsync: runMock } =
+ jest.requireMock>('../../utils/runner');
+const { glob: globMock } = jest.requireMock>('tinyglobby');
+const exitMock = Log.exit;
+
+const TRANSFORM = 'sdk-56-expo-router-react-navigation-replace';
+
+beforeEach(() => {
+ runMock.mockReset();
+ runMock.mockResolvedValue({ error: 0, ok: 1, nochange: 1, skip: 2, timeElapsed: '0', stats: {} });
+ exitMock.mockClear();
+ globMock.mockReset();
+});
+
+describe('parseAndValidateArgs', () => {
+ test('returns parsed command for a valid transform + path', async () => {
+ const cmd = await parseAndValidateArgs([TRANSFORM, 'src']);
+ expect(cmd).toEqual({
+ transform: TRANSFORM,
+ paths: ['src'],
+ });
+ });
+
+ test('accepts multiple paths', async () => {
+ const cmd = await parseAndValidateArgs([TRANSFORM, 'src', 'app', 'components']);
+ expect(cmd.paths).toEqual(['src', 'app', 'components']);
+ });
+
+ test('prints help and exits with code 0 when --help is passed', async () => {
+ await expect(parseAndValidateArgs(['--help'])).rejects.toThrow();
+ expect(exitMock).toHaveBeenCalledWith(expect.stringContaining('Usage'), 0);
+ });
+
+ test('prints help and exits when no transform is provided', async () => {
+ await expect(parseAndValidateArgs([])).rejects.toThrow();
+ expect(exitMock).toHaveBeenCalledWith(expect.any(String), 0);
+ });
+
+ test('prints help and exits when transform has no paths', async () => {
+ await expect(parseAndValidateArgs([TRANSFORM])).rejects.toThrow();
+ expect(exitMock).toHaveBeenCalledWith(expect.any(String), 0);
+ });
+
+ test('exits with code 1 when an unknown flag is passed', async () => {
+ await expect(parseAndValidateArgs(['--bogus-flag', TRANSFORM, 'src'])).rejects.toThrow();
+ expect(exitMock).toHaveBeenCalledWith(
+ expect.stringContaining("Unknown option '--bogus-flag'"),
+ 1
+ );
+ });
+
+ test('exits with code 1 when transform is unknown', async () => {
+ await expect(parseAndValidateArgs(['does-not-exist', 'src'])).rejects.toThrow(
+ /Transform "does-not-exist" does not exist. Valid options:/
+ );
+ expect(exitMock).toHaveBeenCalledWith(
+ expect.stringContaining('Transform "does-not-exist" does not exist. Valid options:')
+ );
+ });
+});
+
+describe('resolveAndDispatch', () => {
+ test('splits files by extension into ts, tsx, and jsx parser buckets', async () => {
+ globMock.mockResolvedValue(['a.ts', 'b.tsx', 'c.js', 'd.jsx']);
+ await resolveAndDispatch({ transform: TRANSFORM, paths: ['src'] });
+ expect(runMock).toHaveBeenCalledTimes(3);
+
+ const tsCall = runMock.mock.calls.find((c) => c[0].parser === 'ts')![0];
+ const tsxCall = runMock.mock.calls.find((c) => c[0].parser === 'tsx')![0];
+ const jsxCall = runMock.mock.calls.find((c) => c[0].parser === 'jsx')![0];
+ expect(tsCall.files).toEqual(['a.ts']);
+ expect(tsxCall.files).toEqual(['b.tsx']);
+ expect(jsxCall.files).toEqual(['c.js', 'd.jsx']);
+ });
+
+ test('only dispatches tsx when only tsx files match', async () => {
+ globMock.mockResolvedValue(['a.tsx', 'b.tsx']);
+ await resolveAndDispatch({ transform: TRANSFORM, paths: ['src'] });
+ expect(runMock).toHaveBeenCalledTimes(1);
+ expect(runMock.mock.calls[0][0].parser).toBe('tsx');
+ });
+
+ test('only dispatches jsx when only jsx files match', async () => {
+ globMock.mockResolvedValue(['a.js', 'b.jsx']);
+ await resolveAndDispatch({ transform: TRANSFORM, paths: ['src'] });
+ expect(runMock).toHaveBeenCalledTimes(1);
+ expect(runMock.mock.calls[0][0].parser).toBe('jsx');
+ });
+
+ test('only dispatches ts when only ts files match', async () => {
+ globMock.mockResolvedValue(['a.ts', 'b.ts']);
+ await resolveAndDispatch({ transform: TRANSFORM, paths: ['src'] });
+ expect(runMock).toHaveBeenCalledTimes(1);
+ expect(runMock.mock.calls[0][0].parser).toBe('ts');
+ });
+
+ test('does not dispatch when no files match', async () => {
+ globMock.mockResolvedValue([]);
+ await resolveAndDispatch({ transform: TRANSFORM, paths: ['src'] });
+ expect(runMock).not.toHaveBeenCalled();
+ });
+
+ test('passes node_modules ignore option to glob', async () => {
+ globMock.mockResolvedValue([]);
+ await resolveAndDispatch({ transform: TRANSFORM, paths: ['src'] });
+ expect(globMock).toHaveBeenCalledWith(
+ ['src'],
+ expect.objectContaining({
+ ignore: ['**/node_modules/**'],
+ })
+ );
+ });
+});
diff --git a/packages/@expo/codemod/src/run/index.ts b/packages/@expo/codemod/src/run/index.ts
new file mode 100644
index 00000000000000..8d7bc89f6c4aa7
--- /dev/null
+++ b/packages/@expo/codemod/src/run/index.ts
@@ -0,0 +1,123 @@
+import chalk from 'chalk';
+import path from 'path';
+import { glob } from 'tinyglobby';
+
+import type { Command } from '../index';
+import * as Log from '../log';
+import { listTransformsAsync } from '../transforms';
+import { parseArgsOrExit, printHelp } from '../utils/args';
+import { runTransformAsync } from '../utils/runner';
+
+const transformsBlock = (transforms: string[]): string =>
+ ['', ` ${chalk.bold('Transforms available')}`, ...transforms.map((t) => ` ${t}`), ''].join(
+ '\n'
+ );
+
+export type ParsedCommand = {
+ transform: string;
+ paths: string[];
+};
+
+/**
+ * Parse argv, validate it against the available transforms, and return the
+ * resolved command. Prints help and exits when --help is passed or required
+ * arguments are missing.
+ */
+export async function parseAndValidateArgs(argv: string[] | undefined): Promise {
+ const { values, positionals } = parseArgsOrExit({
+ args: argv,
+ options: {
+ help: { type: 'boolean', short: 'h' },
+ },
+ allowPositionals: true,
+ strict: true,
+ });
+
+ const transforms = await listTransformsAsync();
+ const [transform, ...paths] = positionals;
+
+ if (values.help || !transform || paths.length === 0) {
+ printHelp(
+ 'Run a codemod transform against the given paths.',
+ 'npx @expo/codemod ',
+ [
+ ' (required) name of transform to apply to files',
+ ' (see a list of transforms available below)',
+ ' one or more paths or globs (e.g. src/**/*.tsx)',
+ '-h, --help print this help message',
+ '-v, --version print the CLI version',
+ ].join('\n'),
+ transformsBlock(transforms)
+ );
+ }
+
+ if (!transforms.includes(transform)) {
+ Log.exit(`Transform "${transform}" does not exist. Valid options: ${transforms.join(', ')}`);
+ }
+
+ return { transform, paths };
+}
+
+/**
+ * Expand the given paths into a file list and dispatch them to the jscodeshift
+ * runner. Files are split by extension into the `tsx` and `jsx` parser buckets.
+ */
+export async function resolveAndDispatch(command: ParsedCommand): Promise {
+ const { transform, paths } = command;
+ const allFiles = await glob(paths, {
+ ignore: ['**/node_modules/**'],
+ });
+
+ const tsxFiles: string[] = [];
+ const tsFiles: string[] = [];
+ const jsxFiles: string[] = [];
+ for (const file of allFiles) {
+ const ext = path.extname(file);
+ if (ext === '.tsx') tsxFiles.push(file);
+ else if (ext === '.ts') tsFiles.push(file);
+ else if (ext === '.js' || ext === '.jsx') jsxFiles.push(file);
+ }
+
+ const mappings = {
+ ts: tsFiles,
+ tsx: tsxFiles,
+ jsx: jsxFiles,
+ } as const;
+
+ const stats = await Promise.all(
+ Object.entries(mappings)
+ .filter(([_, files]) => files.length)
+ .map(async ([parser, files]) => {
+ Log.log(`Transforming ${files.length} ${parser.toUpperCase()} files...`);
+ return await runTransformAsync({
+ files,
+ parser: parser as keyof typeof mappings,
+ transform,
+ });
+ })
+ );
+
+ const combinedStats = stats.reduce(
+ (acc, { error, ok, nochange, skip, timeElapsed }) => ({
+ error: acc.error + error,
+ ok: acc.ok + ok,
+ nochange: acc.nochange + nochange,
+ skip: acc.skip + skip,
+ timeElapsed: Math.max(acc.timeElapsed, Number(timeElapsed)),
+ }),
+ { error: 0, ok: 0, nochange: 0, skip: 0, timeElapsed: 0 }
+ );
+
+ Log.log('');
+ Log.log('Results:');
+ Log.log(chalk.red(` ${combinedStats.error} errors`));
+ // Log.log(chalk.yellow(` ${combinedStats.nochange} unmodified`));
+ Log.log(chalk.yellow(` ${combinedStats.skip} skipped`));
+ Log.log(chalk.green(` ${combinedStats.ok} ok`));
+ Log.log(` Time elapsed: ${combinedStats.timeElapsed.toFixed(2)}s`);
+}
+
+export const runCommand: Command = async (argv) => {
+ const command = await parseAndValidateArgs(argv);
+ await resolveAndDispatch(command);
+};
diff --git a/packages/@expo/codemod/src/transforms/__tests__/sdk-56-expo-router-react-navigation-replace-test.ts b/packages/@expo/codemod/src/transforms/__tests__/sdk-56-expo-router-react-navigation-replace-test.ts
new file mode 100644
index 00000000000000..276c17d16c705c
--- /dev/null
+++ b/packages/@expo/codemod/src/transforms/__tests__/sdk-56-expo-router-react-navigation-replace-test.ts
@@ -0,0 +1,409 @@
+import { applyTransform } from 'jscodeshift/dist/testUtils';
+
+import transform from '../sdk-56-expo-router-react-navigation-replace';
+
+// Default parser (babel): covers plain JS and syntactically-unambiguous TS.
+const run = (source: string): string =>
+ applyTransform(transform, {}, { source, path: 'this/is/test.tsx' });
+
+// tsx parser: required for TypeScript-specific syntax like `import type`
+// declarations and inline `type` modifiers.
+const runTS = (source: string): string =>
+ applyTransform(transform, {}, { source }, { parser: 'tsx' });
+
+describe('basic replacements', () => {
+ test.each([
+ '@react-navigation/native',
+ '@react-navigation/core',
+ '@react-navigation/elements',
+ '@react-navigation/routers',
+ ])('replaces %s with expo-router', (rnImport) => {
+ const output = run(`import { NavigationContainer } from '${rnImport}';`);
+ expect(output).toBe(`import { NavigationContainer } from "expo-router";`);
+ });
+
+ test('replaces @react-navigation/stack with expo-router/js-stack', () => {
+ const output = run(`import { createStackNavigator } from '@react-navigation/stack';`);
+ expect(output).toBe(`import { createStackNavigator } from "expo-router/js-stack";`);
+ });
+
+ test('replaces @react-navigation/bottom-tabs with expo-router/js-tabs', () => {
+ const output = run(`import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';`);
+ expect(output).toBe(`import { createBottomTabNavigator } from "expo-router/js-tabs";`);
+ });
+
+ test('replaces @react-navigation/material-top-tabs with expo-router/js-top-tabs', () => {
+ const output = run(
+ `import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';`
+ );
+ expect(output).toBe(`import { createMaterialTopTabNavigator } from "expo-router/js-top-tabs";`);
+ });
+
+ test('handles multiple specifiers from the same react-navigation package', () => {
+ const output = run(
+ `import { useNavigation, useRoute, useFocusEffect } from '@react-navigation/native';`
+ );
+ expect(output).toBe(`import { useNavigation, useRoute, useFocusEffect } from "expo-router";`);
+ });
+
+ test('replaces all four react-navigation packages in one file', () => {
+ const input = [
+ `import { NavigationContainer } from '@react-navigation/native';`,
+ `import { createStackNavigator } from '@react-navigation/stack';`,
+ `import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';`,
+ `import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';`,
+ ].join('\n');
+ const output = run(input);
+ expect(output).toContain(`import { NavigationContainer } from "expo-router"`);
+ expect(output).toContain(`import { createStackNavigator } from "expo-router/js-stack"`);
+ expect(output).toContain(`import { createBottomTabNavigator } from "expo-router/js-tabs"`);
+ expect(output).toContain(
+ `import { createMaterialTopTabNavigator } from "expo-router/js-top-tabs"`
+ );
+ expect(output).not.toContain(`@react-navigation`);
+ });
+
+ test('leaves unrelated imports untouched', () => {
+ // applyTransform returns empty string when transform returns undefined (no changes)
+ const output = run(`import React from 'react';`);
+ expect(output).toBe(``);
+ });
+
+ test('handles mixed related and unrelated imports', () => {
+ const input = [
+ `import React from 'react';`,
+ `import { NavigationContainer } from '@react-navigation/native';`,
+ `import { createStackNavigator } from '@react-navigation/stack';`,
+ ].join('\n');
+ const output = run(input);
+ expect(output).toContain(`import { NavigationContainer } from "expo-router"`);
+ expect(output).toContain(`import { createStackNavigator } from "expo-router/js-stack"`);
+ expect(output).toContain(`import React from 'react'`);
+ expect(output).not.toContain(`@react-navigation`);
+ });
+});
+
+describe('merging duplicate imports', () => {
+ test('merges duplicate expo-router imports after replacement', () => {
+ const input = [
+ `import { NavigationContainer } from '@react-navigation/native';`,
+ `import { useNavigation } from '@react-navigation/native';`,
+ ].join('\n');
+ const output = run(input);
+ expect(output).toBe(`import { NavigationContainer, useNavigation } from "expo-router";`);
+ });
+
+ test('merges expo-router imports that come from different original packages', () => {
+ const input = [
+ `import { NavigationContainer } from '@react-navigation/native';`,
+ `import { useRoute } from "expo-router";`,
+ ].join('\n');
+ const output = run(input);
+ expect(output).toBe(`import { NavigationContainer, useRoute } from "expo-router";`);
+ });
+
+ test('merges @react-navigation/native with pre-existing expo-router import', () => {
+ const input = [
+ `import { Link, useRouter } from "expo-router";`,
+ `import { NavigationContainer, useNavigation } from '@react-navigation/native';`,
+ ].join('\n');
+ const output = run(input);
+ expect(output).toBe(
+ `import { Link, useRouter, NavigationContainer, useNavigation } from "expo-router";`
+ );
+ });
+
+ test('merges three separate @react-navigation/native imports into one expo-router import', () => {
+ const input = [
+ `import { NavigationContainer } from '@react-navigation/native';`,
+ `import { useNavigation } from '@react-navigation/native';`,
+ `import { useRoute } from '@react-navigation/native';`,
+ ].join('\n');
+ const output = run(input);
+ expect(output).toBe(
+ `import { NavigationContainer, useNavigation, useRoute } from "expo-router";`
+ );
+ });
+
+ test('preserves aliased imports when merging', () => {
+ const input = [
+ `import { useNavigation as useNav } from '@react-navigation/native';`,
+ `import { useRoute as useR } from '@react-navigation/native';`,
+ ].join('\n');
+ const output = run(input);
+ expect(output).toBe(`import { useNavigation as useNav, useRoute as useR } from "expo-router";`);
+ });
+
+ test('preserves identical aliased imports when merging', () => {
+ const input = [
+ `import { useNavigation as useNav } from '@react-navigation/native';`,
+ `import { useNavigation } from '@react-navigation/native';`,
+ ].join('\n');
+ const output = run(input);
+ expect(output).toBe(`import { useNavigation as useNav, useNavigation } from "expo-router";`);
+ });
+
+ test('complex real-world scenario with all package types and pre-existing expo-router', () => {
+ const input = [
+ `import React, { useEffect } from 'react';`,
+ `import { View, Text } from 'react-native';`,
+ `import { Link } from "expo-router";`,
+ `import { NavigationContainer } from '@react-navigation/native';`,
+ `import { useNavigation, useRoute } from '@react-navigation/native';`,
+ `import { createStackNavigator } from '@react-navigation/stack';`,
+ `import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';`,
+ `import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';`,
+ `import { SafeAreaView } from 'react-native-safe-area-context';`,
+ ].join('\n');
+ const output = run(input);
+
+ expect(output).toContain(`import React, { useEffect } from 'react'`);
+ expect(output).toContain(`import { View, Text } from 'react-native'`);
+ expect(output).toContain(`import { SafeAreaView } from 'react-native-safe-area-context'`);
+
+ expect(output).not.toContain(`@react-navigation`);
+
+ expect(output).toContain(`import { createStackNavigator } from "expo-router/js-stack"`);
+ expect(output).toContain(`import { createBottomTabNavigator } from "expo-router/js-tabs"`);
+ expect(output).toContain(
+ `import { createMaterialTopTabNavigator } from "expo-router/js-top-tabs"`
+ );
+
+ // Single merged expo-router import, preserving the pre-existing single-quote style
+ const expoRouterImports = output.match(/from ['"]expo-router['"]/g);
+ expect(expoRouterImports).toHaveLength(1);
+ const expoLine = output.split('\n').find((l) => /from ['"]expo-router['"]/.test(l));
+ expect(expoLine).toContain('Link');
+ expect(expoLine).toContain('NavigationContainer');
+ expect(expoLine).toContain('useNavigation');
+ expect(expoLine).toContain('useRoute');
+ });
+});
+
+describe('unsupported import styles', () => {
+ test('throws on default import from @react-navigation/native', () => {
+ const input = `import Navigation from '@react-navigation/native';`;
+ expect(() => run(input)).toThrow(
+ 'Unsupported import style(s) found:\n' +
+ 'this/is/test.tsx:1 - default import from "@react-navigation/native" is not supported. ' +
+ 'Replace with named imports before running this codemod.'
+ );
+ });
+
+ test('throws on namespace import from @react-navigation/native', () => {
+ const input = `import * as Nav from '@react-navigation/native';`;
+ expect(() => run(input)).toThrow(
+ 'Unsupported import style(s) found:\n' +
+ 'this/is/test.tsx:1 - namespace import (import * as ...) from "@react-navigation/native" is not supported. ' +
+ 'Replace with named imports before running this codemod.'
+ );
+ });
+
+ test('throws on default import from @react-navigation/stack', () => {
+ const input = `import Stack from '@react-navigation/stack';`;
+ expect(() => run(input)).toThrow(
+ 'Unsupported import style(s) found:\n' +
+ 'this/is/test.tsx:1 - default import from "@react-navigation/stack" is not supported. ' +
+ 'Replace with named imports before running this codemod.'
+ );
+ });
+
+ test('throws on namespace import from @react-navigation/bottom-tabs', () => {
+ const input = `import * as Tabs from '@react-navigation/bottom-tabs';`;
+ expect(() => run(input)).toThrow(
+ 'Unsupported import style(s) found:\n' +
+ 'this/is/test.tsx:1 - namespace import (import * as ...) from "@react-navigation/bottom-tabs" is not supported. ' +
+ 'Replace with named imports before running this codemod.'
+ );
+ });
+
+ test('throws on default import from @react-navigation/material-top-tabs', () => {
+ const input = `import TopTabs from '@react-navigation/material-top-tabs';`;
+ expect(() => run(input)).toThrow(
+ 'Unsupported import style(s) found:\n' +
+ 'this/is/test.tsx:1 - default import from "@react-navigation/material-top-tabs" is not supported. ' +
+ 'Replace with named imports before running this codemod.'
+ );
+ });
+
+ test('throws when default import is mixed with named imports', () => {
+ const input = `import Navigation, { useNavigation } from '@react-navigation/native';`;
+ expect(() => run(input)).toThrow(
+ 'Unsupported import style(s) found:\n' +
+ 'this/is/test.tsx:1 - default import from "@react-navigation/native" is not supported. ' +
+ 'Replace with named imports before running this codemod.'
+ );
+ });
+
+ test('collects all errors when multiple unsupported imports exist', () => {
+ const input = [
+ `import Navigation from '@react-navigation/native';`,
+ `import * as Stack from '@react-navigation/stack';`,
+ ].join('\n');
+ expect(() => run(input)).toThrow(
+ 'Unsupported import style(s) found:\n' +
+ 'this/is/test.tsx:1 - default import from "@react-navigation/native" is not supported. ' +
+ 'Replace with named imports before running this codemod.\n' +
+ 'this/is/test.tsx:2 - namespace import (import * as ...) from "@react-navigation/stack" is not supported. ' +
+ 'Replace with named imports before running this codemod.'
+ );
+ });
+
+ test('default imports from non-react-navigation packages are left alone', () => {
+ const input = [
+ `import React from 'react';`,
+ `import { useNavigation } from '@react-navigation/native';`,
+ ].join('\n');
+ const output = run(input);
+ expect(output).toContain(`import React from 'react'`);
+ expect(output).toContain(`from "expo-router"`);
+ });
+});
+
+describe('type imports', () => {
+ test('replaces import type from @react-navigation/native', () => {
+ const output = runTS(`import type { ScreenProps } from '@react-navigation/native';`);
+ expect(output).toBe(`import type { ScreenProps } from "expo-router";`);
+ });
+
+ test('replaces import type from @react-navigation/stack', () => {
+ const output = runTS(`import type { StackScreenProps } from '@react-navigation/stack';`);
+ expect(output).toBe(`import type { StackScreenProps } from "expo-router/js-stack";`);
+ });
+
+ test('replaces import { type ... } from @react-navigation/native', () => {
+ const output = runTS(`import { type ScreenProps } from '@react-navigation/native';`);
+ expect(output).toBe(`import { type ScreenProps } from "expo-router";`);
+ });
+
+ test('replaces import { type ... } from @react-navigation/bottom-tabs', () => {
+ const output = runTS(
+ `import { type BottomTabScreenProps } from '@react-navigation/bottom-tabs';`
+ );
+ expect(output).toBe(`import { type BottomTabScreenProps } from "expo-router/js-tabs";`);
+ });
+
+ test('handles mixed value and inline type specifiers from react-navigation', () => {
+ const output = runTS(
+ `import { useNavigation, type NavigationProp } from '@react-navigation/native';`
+ );
+ expect(output).toBe(`import { useNavigation, type NavigationProp } from "expo-router";`);
+ });
+
+ test('handles multiple type specifiers from react-navigation', () => {
+ const output = runTS(
+ `import type { NavigationProp, RouteProp, ParamListBase } from '@react-navigation/native';`
+ );
+ expect(output).toBe(
+ `import type { NavigationProp, RouteProp, ParamListBase } from "expo-router";`
+ );
+ });
+
+ test('handles mixed inline type and value specifiers across react-navigation packages', () => {
+ const input = [
+ `import { useNavigation, type NavigationProp } from '@react-navigation/native';`,
+ `import { createStackNavigator, type StackScreenProps } from '@react-navigation/stack';`,
+ ].join('\n');
+ const output = runTS(input);
+ expect(output).toBe(
+ [
+ `import { useNavigation, type NavigationProp } from "expo-router";`,
+ `import { createStackNavigator, type StackScreenProps } from "expo-router/js-stack";`,
+ ].join('\n')
+ );
+ });
+
+ test('leaves import type from unrelated packages untouched', () => {
+ const input = [
+ `import type { FC } from 'react';`,
+ `import { useNavigation } from '@react-navigation/native';`,
+ ].join('\n');
+ const output = runTS(input);
+ expect(output).toContain(`import type { FC } from 'react'`);
+ expect(output).toContain(`from "expo-router"`);
+ });
+});
+
+describe('merging type and value imports', () => {
+ test('handles import type from expo-router with value import from react-navigation', () => {
+ const input = [
+ `import type { ScreenProps } from "expo-router";`,
+ `import { useNavigation } from '@react-navigation/native';`,
+ ].join('\n');
+ const output = runTS(input);
+ // ScreenProps keeps its type modifier as an inline `type`, useNavigation stays as a value
+ expect(output).toBe(`import { type ScreenProps, useNavigation } from "expo-router";`);
+ });
+
+ test('handles value import from expo-router with import type from react-navigation', () => {
+ const input = [
+ `import { useRouter } from "expo-router";`,
+ `import type { NavigationProp } from '@react-navigation/native';`,
+ ].join('\n');
+ const output = runTS(input);
+ expect(output).toBe(`import { useRouter, type NavigationProp } from "expo-router";`);
+ });
+
+ test('handles import type from react-navigation with value import from react-navigation', () => {
+ const input = [
+ `import { useNavigation } from '@react-navigation/native';`,
+ `import type { NavigationProp } from '@react-navigation/native';`,
+ ].join('\n');
+ const output = runTS(input);
+ expect(output).toBe(`import { useNavigation, type NavigationProp } from "expo-router";`);
+ });
+
+ test('handles import type from react-navigation (first) with value import from react-navigation', () => {
+ const input = [
+ `import type { NavigationProp } from '@react-navigation/native';`,
+ `import { useNavigation } from '@react-navigation/native';`,
+ ].join('\n');
+ const output = runTS(input);
+ expect(output).toBe(`import { type NavigationProp, useNavigation } from "expo-router";`);
+ });
+
+ test('merges import { type ... } with value import from react-navigation', () => {
+ const input = [
+ `import { type NavigationProp } from '@react-navigation/native';`,
+ `import { useNavigation } from '@react-navigation/native';`,
+ ].join('\n');
+ const output = runTS(input);
+ expect(output).toBe(`import { type NavigationProp, useNavigation } from "expo-router";`);
+ });
+
+ test('merges value import with import { type ... } from react-navigation', () => {
+ const input = [
+ `import { useNavigation } from "expo-router";`,
+ `import { type NavigationProp } from '@react-navigation/native';`,
+ ].join('\n');
+ const output = runTS(input);
+ expect(output).toBe(`import { useNavigation, type NavigationProp } from "expo-router";`);
+ });
+
+ test('merges import { type ... } from expo-router with value import from react-navigation', () => {
+ const input = [
+ `import { type Href } from "expo-router";`,
+ `import { useNavigation } from '@react-navigation/native';`,
+ ].join('\n');
+ const output = runTS(input);
+ expect(output).toBe(`import { type Href, useNavigation } from "expo-router";`);
+ });
+
+ test('merges import type { ... } from expo-router with value import from react-navigation', () => {
+ const input = [
+ `import type { Href } from "expo-router";`,
+ `import { useNavigation } from '@react-navigation/native';`,
+ ].join('\n');
+ const output = runTS(input);
+ expect(output).toBe(`import { type Href, useNavigation } from "expo-router";`);
+ });
+
+ test('merges value import from expo-router with import { type ... } from react-navigation', () => {
+ const input = [
+ `import { useRouter } from "expo-router";`,
+ `import { type NavigationProp } from '@react-navigation/native';`,
+ ].join('\n');
+ const output = runTS(input);
+ expect(output).toBe(`import { useRouter, type NavigationProp } from "expo-router";`);
+ });
+});
diff --git a/packages/@expo/codemod/src/transforms/index.ts b/packages/@expo/codemod/src/transforms/index.ts
new file mode 100644
index 00000000000000..10899a7e62eaa4
--- /dev/null
+++ b/packages/@expo/codemod/src/transforms/index.ts
@@ -0,0 +1,17 @@
+import path from 'path';
+import { glob } from 'tinyglobby';
+
+export const TRANSFORM_DIR = __dirname;
+
+export async function listTransformsAsync(): Promise {
+ // *.js, since this function will be called from within build folder
+ const modules = await glob(['*.js'], { cwd: TRANSFORM_DIR });
+ return modules
+ .map((filename) => path.basename(filename, '.js'))
+ .filter((name) => name !== 'index')
+ .sort();
+}
+
+export function transformFilePath(transform: string): string {
+ return path.join(TRANSFORM_DIR, `${transform}.js`);
+}
diff --git a/packages/@expo/codemod/src/transforms/sdk-56-expo-router-react-navigation-replace.ts b/packages/@expo/codemod/src/transforms/sdk-56-expo-router-react-navigation-replace.ts
new file mode 100644
index 00000000000000..56d3b977c5859a
--- /dev/null
+++ b/packages/@expo/codemod/src/transforms/sdk-56-expo-router-react-navigation-replace.ts
@@ -0,0 +1,160 @@
+/**
+ * Codemod: Replace @react-navigation/* imports with expo-router equivalents.
+ *
+ * Mapping:
+ * @react-navigation/native → expo-router
+ * @react-navigation/stack → expo-router/js-stack
+ * @react-navigation/bottom-tabs → expo-router/js-tabs
+ * @react-navigation/material-top-tabs → expo-router/js-top-tabs
+ *
+ * After replacement, duplicate `expo-router` imports are merged into one.
+ */
+import type {
+ ASTPath,
+ ImportDeclaration,
+ ImportDefaultSpecifier,
+ ImportNamespaceSpecifier,
+ ImportSpecifier,
+ JSCodeshift,
+ Transform,
+} from 'jscodeshift';
+
+// Specifier types in `@types/jscodeshift` (via ast-types) don't expose the
+// inline `type` modifier (`import { type A }`), even though Babel emits it as
+// `importKind` on each specifier at runtime.
+type ImportSpecifierWithKind = (
+ | ImportSpecifier
+ | ImportDefaultSpecifier
+ | ImportNamespaceSpecifier
+) & { importKind?: 'type' | 'value' };
+
+const IMPORT_MAP: Record = {
+ '@react-navigation/native': 'expo-router',
+ '@react-navigation/elements': 'expo-router',
+ '@react-navigation/core': 'expo-router',
+ '@react-navigation/routers': 'expo-router',
+ '@react-navigation/stack': 'expo-router/js-stack',
+ '@react-navigation/bottom-tabs': 'expo-router/js-tabs',
+ '@react-navigation/material-top-tabs': 'expo-router/js-top-tabs',
+};
+
+const UNSUPPORTED_SPECIFIERS: Partial> = {
+ ImportDefaultSpecifier: 'default import',
+ ImportNamespaceSpecifier: 'namespace import (import * as ...)',
+};
+
+const isTypeOnlyImport = (path: ASTPath): boolean =>
+ path.node.importKind === 'type';
+
+/**
+ * Clone the specifier, changing its importKind to be type, for example:
+ * import { type A } from "b"
+ */
+const markAsInlineType = (spec: T): T => {
+ const clone = { ...spec };
+ clone.importKind = 'type';
+ return clone;
+};
+
+/**
+ * Collects errors for unsupported import styles in the given declarations:
+ * default imports (`import A from "b"`) and namespace imports
+ * (`import * as A from "b"`). Only named imports (`import { A } from "b"`)
+ * can be safely rewritten.
+ */
+const collectUnsupportedImportStyleErrors = (
+ filePath: string,
+ paths: ASTPath[]
+): string[] => {
+ const errors: string[] = [];
+ for (const declarationPath of paths) {
+ const specifiers = (declarationPath.node.specifiers ?? []) as ImportSpecifierWithKind[];
+ for (const spec of specifiers) {
+ const label = UNSUPPORTED_SPECIFIERS[spec.type];
+ if (!label) continue;
+ const line = declarationPath.node.loc?.start.line ?? '?';
+ const sourceModule = declarationPath.node.source.value as string;
+ errors.push(
+ `${filePath}:${line} - ${label} from "${sourceModule}" is not supported. ` +
+ `Replace with named imports before running this codemod.`
+ );
+ }
+ }
+ return errors;
+};
+
+const groupPathsBySource = (
+ paths: ASTPath[]
+): Map[]> => {
+ const groupsBySource = new Map[]>();
+ for (const declarationPath of paths) {
+ const sourceModule = declarationPath.node.source.value as string;
+ const existing = groupsBySource.get(sourceModule);
+ if (existing) {
+ existing.push(declarationPath);
+ } else {
+ groupsBySource.set(sourceModule, [declarationPath]);
+ }
+ }
+ return groupsBySource;
+};
+
+const mergeGroup = (j: JSCodeshift, groupPaths: ASTPath[]): void => {
+ const hasAnyTypeOnlyImport = groupPaths.some(isTypeOnlyImport);
+ const allImportsAreTypeOnly = groupPaths.every(isTypeOnlyImport);
+ // Mixed: `import type { A }` + `import { B }` → `import { type A, B }`.
+ const mixesTypeAndValueImports = hasAnyTypeOnlyImport && !allImportsAreTypeOnly;
+
+ const [mergeTarget, ...declarationsToRemove] = groupPaths;
+ if (!mergeTarget) return;
+
+ const specifiers = groupPaths.flatMap((declarationPath) => {
+ const specs = (declarationPath.node.specifiers ?? []) as ImportSpecifierWithKind[];
+ return mixesTypeAndValueImports && isTypeOnlyImport(declarationPath)
+ ? specs.map(markAsInlineType)
+ : specs;
+ });
+
+ j(mergeTarget).replaceWith(
+ j.importDeclaration(
+ specifiers,
+ mergeTarget.node.source,
+ allImportsAreTypeOnly ? 'type' : 'value'
+ )
+ );
+
+ for (const declarationPath of declarationsToRemove) j(declarationPath).remove();
+};
+
+const transform: Transform = (fileInfo, api) => {
+ const j = api.jscodeshift;
+ const root = j(fileInfo.source);
+
+ const mappablePaths = root
+ .find(j.ImportDeclaration)
+ .filter((path) => (path.node.source.value as string) in IMPORT_MAP)
+ .paths();
+
+ if (mappablePaths.length === 0) return undefined;
+
+ const errors = collectUnsupportedImportStyleErrors(fileInfo.path, mappablePaths);
+ if (errors.length > 0) {
+ throw new Error(`Unsupported import style(s) found:\n${errors.join('\n')}`);
+ }
+
+ for (const path of mappablePaths) {
+ const sourceModule = path.node.source.value as string;
+ path.node.source.value = IMPORT_MAP[sourceModule];
+ }
+
+ const groups = groupPathsBySource(root.find(j.ImportDeclaration).paths());
+ for (const [importPath, groupPaths] of groups.entries()) {
+ if (groupPaths.length > 1 && importPath.startsWith('expo-router')) {
+ mergeGroup(j, groupPaths);
+ }
+ }
+
+ return root.toSource();
+};
+
+export default transform;
diff --git a/packages/@expo/codemod/src/utils/args.ts b/packages/@expo/codemod/src/utils/args.ts
new file mode 100644
index 00000000000000..d6f0743d682184
--- /dev/null
+++ b/packages/@expo/codemod/src/utils/args.ts
@@ -0,0 +1,39 @@
+import chalk from 'chalk';
+import { parseArgs, type ParseArgsConfig } from 'util';
+
+import * as Log from '../log';
+
+const isParseArgsError = (error: unknown): error is Error & { code: string } => {
+ if (!error || typeof error !== 'object' || !('code' in error)) return false;
+ const code = (error as { code: unknown }).code;
+ return typeof code === 'string' && code.startsWith('ERR_PARSE_ARGS_');
+};
+
+export function parseArgsOrExit(
+ config: T
+): ReturnType> {
+ try {
+ return parseArgs(config);
+ } catch (error: unknown) {
+ if (isParseArgsError(error)) {
+ Log.exit(error.message, 1);
+ }
+ throw error;
+ }
+}
+
+export function printHelp(info: string, usage: string, options: string, extra: string = ''): never {
+ Log.exit(
+ chalk`
+ {bold Info}
+ ${info}
+
+ {bold Usage}
+ {dim $} ${usage}
+
+ {bold Options}
+ ${options.split('\n').join('\n ')}
+` + extra,
+ 0
+ );
+}
diff --git a/packages/@expo/codemod/src/utils/runner.ts b/packages/@expo/codemod/src/utils/runner.ts
new file mode 100644
index 00000000000000..232cc5206090a1
--- /dev/null
+++ b/packages/@expo/codemod/src/utils/runner.ts
@@ -0,0 +1,29 @@
+import Runner from 'jscodeshift/src/Runner';
+
+import { transformFilePath } from '../transforms';
+
+export type ParserKind = 'tsx' | 'jsx' | 'ts';
+
+const JSCODESHIFT_PARSER: Record = {
+ tsx: 'tsx',
+ jsx: 'babel',
+ ts: 'ts',
+};
+
+export async function runTransformAsync({
+ files,
+ parser,
+ transform,
+}: {
+ files: string[];
+ parser: ParserKind;
+ transform: string;
+}): ReturnType<(typeof Runner)['run']> {
+ return Runner.run(transformFilePath(transform), files, {
+ // Transforms are pre-compiled to JS by our build, so jscodeshift's @babel/register hook is unnecessary.
+ babel: false,
+ parser: JSCODESHIFT_PARSER[parser],
+ verbose: 0,
+ silent: true,
+ });
+}
diff --git a/packages/@expo/codemod/ts-declarations/jscodeshift-testUtils.d.ts b/packages/@expo/codemod/ts-declarations/jscodeshift-testUtils.d.ts
new file mode 100644
index 00000000000000..f8c893b332d3de
--- /dev/null
+++ b/packages/@expo/codemod/ts-declarations/jscodeshift-testUtils.d.ts
@@ -0,0 +1,12 @@
+// `@types/jscodeshift` only declares the package's main entrypoint, not the
+// `jscodeshift/dist/testUtils` submodule used by transform tests.
+declare module 'jscodeshift/dist/testUtils' {
+ import type { Parser, Transform } from 'jscodeshift';
+
+ export function applyTransform(
+ module: Transform | { default: Transform },
+ options: Record,
+ input: { source: string; path?: string },
+ testOptions?: { parser?: 'babel' | 'babylon' | 'flow' | 'ts' | 'tsx' | Parser }
+ ): string;
+}
diff --git a/packages/@expo/codemod/tsconfig.json b/packages/@expo/codemod/tsconfig.json
new file mode 100644
index 00000000000000..8278977342e83d
--- /dev/null
+++ b/packages/@expo/codemod/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "expo-module-scripts/tsconfig.node",
+ "include": ["./src", "./ts-declarations"],
+ "exclude": ["**/__mocks__/*", "**/__tests__/*"],
+ "compilerOptions": {
+ "rootDir": "./src",
+ "outDir": "./build",
+ "sourceMap": true
+ }
+}
diff --git a/packages/@expo/config-plugins/src/ios/__tests__/__snapshots__/Maps-test.ts.snap b/packages/@expo/config-plugins/src/ios/__tests__/__snapshots__/Maps-test.ts.snap
index f11b11e9c8e8ca..c7f1362a91d3c0 100644
--- a/packages/@expo/config-plugins/src/ios/__tests__/__snapshots__/Maps-test.ts.snap
+++ b/packages/@expo/config-plugins/src/ios/__tests__/__snapshots__/Maps-test.ts.snap
@@ -253,7 +253,7 @@ ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] ||= podfile_properties['EX_DEV_CLIENT_NET
ENV['RCT_USE_RN_DEP'] ||= '0' if podfile_properties['ios.buildReactNativeFromSource'] == 'true'
ENV['RCT_USE_PREBUILT_RNCORE'] ||= '0' if podfile_properties['ios.buildReactNativeFromSource'] == 'true'
ENV['RCT_HERMES_V1_ENABLED'] ||= '1' if podfile_properties['expo.useHermesV1'] == 'true'
-ENV['EXPO_USE_PRECOMPILED_MODULES'] ||= '1' if podfile_properties['EXPO_USE_PRECOMPILED_MODULES'] == '1'
+ENV['EXPO_USE_PRECOMPILED_MODULES'] ||= '1' if podfile_properties['EXPO_USE_PRECOMPILED_MODULES'] == 'true'
platform :ios, podfile_properties['ios.deploymentTarget'] || '16.4'
prepare_react_native_project!
diff --git a/packages/expo-calendar/CHANGELOG.md b/packages/expo-calendar/CHANGELOG.md
index c86c4c23d54d54..a630a30facc092 100644
--- a/packages/expo-calendar/CHANGELOG.md
+++ b/packages/expo-calendar/CHANGELOG.md
@@ -13,6 +13,7 @@
### 🐛 Bug fixes
- Fixed `NumberFormatException` crash on Android when calendar/event IDs exceed `Integer.MAX_VALUE`. ([#43344](https://github.com/expo/expo/pull/43344) by [@olivier-bouillet](https://github.com/olivier-bouillet))
+- [iOS] Fix `deleteEventAsync` deleting wrong recurring event instance when `instanceStartDate` is provided. ([#40172](https://github.com/expo/expo/pull/40172) by [@marcelogdeandrade](https://github.com/marcelogdeandrade))
### 💡 Others
diff --git a/packages/expo-calendar/ios/CalendarModule.swift b/packages/expo-calendar/ios/CalendarModule.swift
index 3a7e43f96a8878..b1e38521296343 100644
--- a/packages/expo-calendar/ios/CalendarModule.swift
+++ b/packages/expo-calendar/ios/CalendarModule.swift
@@ -584,7 +584,7 @@ public class CalendarModule: Module {
return firstEvent
}
- guard let firstEventStart = firstEvent.startDate, firstEventStart.compare(startDate) == .orderedSame else {
+ if let firstEventStart = firstEvent.startDate, firstEventStart.compare(startDate) == .orderedSame {
return firstEvent
}
@@ -594,10 +594,8 @@ public class CalendarModule: Module {
)
for event in events {
- if event.calendarItemIdentifier != id {
- break
- }
- if let eventStart = event.startDate, eventStart.compare(startDate) == .orderedSame {
+ if event.calendarItemIdentifier == firstEvent.calendarItemIdentifier,
+ let eventStart = event.startDate, eventStart.compare(startDate) == .orderedSame {
return event
}
}
diff --git a/packages/expo-dev-menu/CHANGELOG.md b/packages/expo-dev-menu/CHANGELOG.md
index 3667edacf4bbe3..1fc4f5014f467d 100644
--- a/packages/expo-dev-menu/CHANGELOG.md
+++ b/packages/expo-dev-menu/CHANGELOG.md
@@ -19,6 +19,7 @@
- [iOS] Fix deadlock in `DevMenuPackagerConnectionHandler.setup`. ([#44405](https://github.com/expo/expo/pull/44405) by [@alanjhughes](https://github.com/alanjhughes))
- [iOS] Remove `#Preview` SwiftUI blocks that cause build failures when consuming the package as a dependency. ([#44775](https://github.com/expo/expo/pull/44775) by [@fabriziocucci](https://github.com/fabriziocucci))
- [iOS] Fix dev menu auto-launch not triggering. The `updateAutoLaunchObserver()` now called from `setAppContext`. ([#45167](https://github.com/expo/expo/pull/45167) by [@alanjhughes](https://github.com/alanjhughes))
+- [iOS] Reconnect the packager to the bundle URL host so expo-cli commands reach the app on physical devices. ([#45195](https://github.com/expo/expo/pull/45195) by [@alanjhughes](https://github.com/alanjhughes))
### 💡 Others
diff --git a/packages/expo-dev-menu/ios/DevMenuPackagerConnectionHandler.swift b/packages/expo-dev-menu/ios/DevMenuPackagerConnectionHandler.swift
index ac38860da499bc..1804708a1f1ca7 100644
--- a/packages/expo-dev-menu/ios/DevMenuPackagerConnectionHandler.swift
+++ b/packages/expo-dev-menu/ios/DevMenuPackagerConnectionHandler.swift
@@ -39,6 +39,14 @@ class DevMenuPackagerConnectionHandler {
queue: DispatchQueue.main,
forMethod: "devMenu"
)
+
+ // RCTDevSettings starts the packager WS before its bundleManager has a host,
+ // leaving the socket on localhost so it can't be reached from a physical device.
+ // Reconnect to the bundle URL once AppContext is ready.
+ if let bundleURL = manager.currentAppContext?.bundleURL, let host = bundleURL.host {
+ let port = bundleURL.port ?? 8081
+ packagerConnection?.reconnect("\(host):\(port)")
+ }
}
#endif
}
diff --git a/packages/expo-file-system/CHANGELOG.md b/packages/expo-file-system/CHANGELOG.md
index 99f14514076c1a..37cf799fbc9927 100644
--- a/packages/expo-file-system/CHANGELOG.md
+++ b/packages/expo-file-system/CHANGELOG.md
@@ -16,6 +16,7 @@
- [Android] Add `mode` option when opening file handle. ([#42983](https://github.com/expo/expo/pull/42983) by [@barthap](https://github.com/barthap))
- Add `onProgress` callback and `AbortSignal` support to `File.downloadFileAsync()`. ([#43053](https://github.com/expo/expo/pull/43053) by [@aleqsio](https://github.com/aleqsio))
- Add `file.createUploadTask()` and `File.createDownloadTask()` APIs ([#44055](https://github.com/expo/expo/pull/44055) by [@barthap](https://github.com/barthap))
+- Add `File.upload()` with legacy-compatible upload semantics, progress callbacks, and `AbortSignal` support. ([#45033](https://github.com/expo/expo/pull/45033) by [@barthap](https://github.com/barthap))
### 🐛 Bug fixes
diff --git a/packages/expo-file-system/build/ExpoFileSystem.types.d.ts b/packages/expo-file-system/build/ExpoFileSystem.types.d.ts
index 26ebcb9027a952..f06805d57f739a 100644
--- a/packages/expo-file-system/build/ExpoFileSystem.types.d.ts
+++ b/packages/expo-file-system/build/ExpoFileSystem.types.d.ts
@@ -358,6 +358,17 @@ export declare class File {
* ```
*/
static downloadFileAsync(url: string, destination: Directory | File, options?: DownloadOptions): Promise;
+ /**
+ * Uploads this file to the network.
+ *
+ * The promise resolves with the HTTP response metadata and body for any completed response,
+ * including non-2xx status codes. It rejects only for local file errors, transport failures,
+ * or cancellation.
+ *
+ * @param url The URL to upload the file to.
+ * @param options Upload options.
+ */
+ upload(url: string, options?: UploadOptions): Promise;
/**
* An overload of the `pickFileAsync` method, which picks and returns a single `File`.
* This overload requires options to have `multipleFiles` flag be `undefined` or `false`.
diff --git a/packages/expo-file-system/build/ExpoFileSystem.types.d.ts.map b/packages/expo-file-system/build/ExpoFileSystem.types.d.ts.map
index 4895a2c95bfc6e..be2fa0f8e4c2fd 100644
--- a/packages/expo-file-system/build/ExpoFileSystem.types.d.ts.map
+++ b/packages/expo-file-system/build/ExpoFileSystem.types.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"ExpoFileSystem.types.d.ts","sourceRoot":"","sources":["../src/ExpoFileSystem.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,oBAAY,YAAY;IACtB;;OAEG;IACH,IAAI,SAAS;IACb;;OAEG;IACH,MAAM,WAAW;CAClB;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;;OAGG;IACH,QAAQ,CAAC,EAAE,YAAY,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC5C;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF;;GAEG;AACH,oBAAY,QAAQ;IAClB;;;;;OAKG;IACH,SAAS,OAAO;IAEhB;;;OAGG;IACH,QAAQ,MAAM;IAEd;;;OAGG;IACH,SAAS,MAAM;IAEf;;;;;;OAMG;IACH,MAAM,OAAO;IAEb;;OAEG;IACH,QAAQ,OAAO;CAChB;AAED,MAAM,CAAC,OAAO,OAAO,SAAS;IAC5B;;;;;;;OAOG;gBACS,GAAG,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE;IAElD;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,YAAY,IAAI,IAAI;IAEpB;;;;OAIG;IACH,MAAM,IAAI,IAAI;IAEd;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;;;OAIG;IACH,MAAM,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,IAAI;IAE9C,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAEvD,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS;IAExC;;OAEG;IACH,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAE/E;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,SAAS,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,IAAI;IAE1E;;OAEG;IACH,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAE/E;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,SAAS,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,IAAI;IAE1E;;OAEG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAE7B;;;;OAIG;IACH,aAAa,IAAI;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE;IAEvD;;OAEG;IACH,IAAI,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,EAAE;IAE5B;;;;;;OAMG;IACH,IAAI,IAAI,aAAa;IAErB;;OAEG;IACH,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAEpB;;;;;;;OAOG;IACH,MAAM,CAAC,kBAAkB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;CACnE;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B;;OAEG;IACH,OAAO,CAAC,EAAE;QACR,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;KACvB,CAAC;IACF;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC9C;;;OAGG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,IAAI;IACvB;;;;OAIG;gBACS,GAAG,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE;IAElD;;OAEG;IACH,IAAI,GAAG,IAAI,MAAM,CAAC;IAElB;;;OAGG;IACH,YAAY,IAAI,IAAI;IAEpB;;;OAGG;IACH,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAEvB;;;OAGG;IACH,QAAQ,IAAI,MAAM;IAElB;;;OAGG;IACH,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;IAEzB;;;OAGG;IACH,UAAU,IAAI,MAAM;IAEpB;;;OAGG;IACH,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;IAEzC;;;OAGG;IACH,SAAS,IAAI,UAAU;IAEvB;;;OAGG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,IAAI;IAErE;;;;OAIG;IACH,MAAM,IAAI,IAAI;IAEd;;;;OAIG;IACH,IAAI,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,QAAQ;IAErC;;;OAGG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;;;OAIG;IACH,MAAM,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,IAAI;IAEzC;;OAEG;IACH,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAE/E;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,SAAS,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,IAAI;IAE1E;;OAEG;IACH,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAE/E;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,SAAS,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,IAAI;IAE1E;;OAEG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAE7B;;;;;;;;;;OAUG;IACH,IAAI,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,UAAU;IAEjC;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,MAAM,CAAC,iBAAiB,CACtB,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,SAAS,GAAG,IAAI,EAC7B,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,IAAI,CAAC;IAEhB;;;;OAIG;IACH,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IACpF;;;;OAIG;IACH,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAC1F;;;;;;;;;OASG;IACH,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,EAAE,CAAC;IAEpF;;;;;;;;;;;;;;;;;;OAkBG;IACH,MAAM,CAAC,kBAAkB,CACvB,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,SAAS,GAAG,IAAI,EAC7B,OAAO,CAAC,EAAE,mBAAmB,GAC5B,YAAY;IAEf;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,UAAU;IAElE;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAEnB;;;OAGG;IACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhC;;OAEG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAE5B;;OAEG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAE5B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,CAAC,OAAO,OAAO,UAAU;IAI7B,KAAK,IAAI,IAAI;IAKb,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC;IAKlD,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAMnC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAItB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB;;;;OAIG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB;;OAEG;IACH,WAAW,EAAE,OAAO,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC9B;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,sBAAsB,GAAG;IAC3D,aAAa,CAAC,EAAE,KAAK,CAAC;CACvB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,sBAAsB,GAAG;IAC9D,aAAa,EAAE,IAAI,CAAC;CACrB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,qBAAqB,GAAG,wBAAwB,CAAC;AAE/E;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,2BAA2B,GAAG,sBAAsB,CAAC;AAExF;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG,8BAA8B,GAAG,sBAAsB,CAAC;AAE9F;;GAEG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,MAAM,EAAE,IAAI,CAAC;IACb,QAAQ,EAAE,KAAK,CAAC;CACjB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,8BAA8B,GAAG;IAC3C,MAAM,EAAE,IAAI,EAAE,CAAC;IACf,QAAQ,EAAE,KAAK,CAAC;CACjB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE,IAAI,CAAC;IACb,QAAQ,EAAE,IAAI,CAAC;CAChB,CAAC;AAEF;;GAEG;AACH,oBAAY,UAAU;IACpB;;OAEG;IACH,cAAc,IAAI;IAClB;;OAEG;IACH,SAAS,IAAI;CACd;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC;IACtC;;;OAGG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAC5C;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,EAAE,sBAAsB,CAAC;IACrC;;;OAGG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;;;;;;;;;;;OAYG;IACH,WAAW,CAAC,EAAE,sBAAsB,CAAC;IACrC;;OAEG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC9C;;OAEG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,YAAY,GAAG,YAAY,CAAC;AAEjE;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,WAAW,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;GAGG;AACH,KAAK,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,GAAG,OAAO,CAAC;AAEpF;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAE3D;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,SAAS,CAAC;AAE1C;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,UAAU;IAC7B;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAEhC;;;;;OAKG;gBACS,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa;IAE5D;;;;OAIG;IACH,WAAW,IAAI,OAAO,CAAC,YAAY,CAAC;IAEpC;;;OAGG;IACH,MAAM,IAAI,IAAI;IAEd;;OAEG;IACH,WAAW,CACT,SAAS,EAAE,UAAU,EACrB,QAAQ,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,GACvC;QAAE,MAAM,EAAE,MAAM,IAAI,CAAA;KAAE;CAC1B;AAED;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,YAAY;IAC/B;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;IAElC;;;;;OAKG;gBACS,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,GAAG,SAAS,EAAE,OAAO,CAAC,EAAE,mBAAmB;IAErF;;;;OAIG;IACH,aAAa,IAAI,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAErC;;OAEG;IACH,KAAK,IAAI,IAAI;IAEb;;;;OAIG;IACH,WAAW,IAAI,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAEnC;;;OAGG;IACH,MAAM,IAAI,IAAI;IAEd;;;OAGG;IACH,OAAO,IAAI,kBAAkB;IAE7B;;;;;OAKG;IACH,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,kBAAkB,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,YAAY;IAE1F;;OAEG;IACH,WAAW,CACT,SAAS,EAAE,UAAU,EACrB,QAAQ,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,GACzC;QAAE,MAAM,EAAE,MAAM,IAAI,CAAA;KAAE;CAC1B"}
\ No newline at end of file
+{"version":3,"file":"ExpoFileSystem.types.d.ts","sourceRoot":"","sources":["../src/ExpoFileSystem.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,oBAAY,YAAY;IACtB;;OAEG;IACH,IAAI,SAAS;IACb;;OAEG;IACH,MAAM,WAAW;CAClB;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;;OAGG;IACH,QAAQ,CAAC,EAAE,YAAY,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC5C;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF;;GAEG;AACH,oBAAY,QAAQ;IAClB;;;;;OAKG;IACH,SAAS,OAAO;IAEhB;;;OAGG;IACH,QAAQ,MAAM;IAEd;;;OAGG;IACH,SAAS,MAAM;IAEf;;;;;;OAMG;IACH,MAAM,OAAO;IAEb;;OAEG;IACH,QAAQ,OAAO;CAChB;AAED,MAAM,CAAC,OAAO,OAAO,SAAS;IAC5B;;;;;;;OAOG;gBACS,GAAG,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE;IAElD;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,YAAY,IAAI,IAAI;IAEpB;;;;OAIG;IACH,MAAM,IAAI,IAAI;IAEd;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;;;OAIG;IACH,MAAM,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,IAAI;IAE9C,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAEvD,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS;IAExC;;OAEG;IACH,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAE/E;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,SAAS,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,IAAI;IAE1E;;OAEG;IACH,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAE/E;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,SAAS,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,IAAI;IAE1E;;OAEG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAE7B;;;;OAIG;IACH,aAAa,IAAI;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE;IAEvD;;OAEG;IACH,IAAI,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,EAAE;IAE5B;;;;;;OAMG;IACH,IAAI,IAAI,aAAa;IAErB;;OAEG;IACH,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAEpB;;;;;;;OAOG;IACH,MAAM,CAAC,kBAAkB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;CACnE;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B;;OAEG;IACH,OAAO,CAAC,EAAE;QACR,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;KACvB,CAAC;IACF;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC9C;;;OAGG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,IAAI;IACvB;;;;OAIG;gBACS,GAAG,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE;IAElD;;OAEG;IACH,IAAI,GAAG,IAAI,MAAM,CAAC;IAElB;;;OAGG;IACH,YAAY,IAAI,IAAI;IAEpB;;;OAGG;IACH,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAEvB;;;OAGG;IACH,QAAQ,IAAI,MAAM;IAElB;;;OAGG;IACH,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;IAEzB;;;OAGG;IACH,UAAU,IAAI,MAAM;IAEpB;;;OAGG;IACH,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;IAEzC;;;OAGG;IACH,SAAS,IAAI,UAAU;IAEvB;;;OAGG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,IAAI;IAErE;;;;OAIG;IACH,MAAM,IAAI,IAAI;IAEd;;;;OAIG;IACH,IAAI,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,QAAQ;IAErC;;;OAGG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;;;OAIG;IACH,MAAM,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,IAAI;IAEzC;;OAEG;IACH,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAE/E;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,SAAS,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,IAAI;IAE1E;;OAEG;IACH,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAE/E;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,SAAS,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,IAAI;IAE1E;;OAEG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAE7B;;;;;;;;;;OAUG;IACH,IAAI,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,UAAU;IAEjC;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,MAAM,CAAC,iBAAiB,CACtB,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,SAAS,GAAG,IAAI,EAC7B,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,IAAI,CAAC;IAEhB;;;;;;;;;OASG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAEnE;;;;OAIG;IACH,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IACpF;;;;OAIG;IACH,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAC1F;;;;;;;;;OASG;IACH,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,EAAE,CAAC;IAEpF;;;;;;;;;;;;;;;;;;OAkBG;IACH,MAAM,CAAC,kBAAkB,CACvB,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,SAAS,GAAG,IAAI,EAC7B,OAAO,CAAC,EAAE,mBAAmB,GAC5B,YAAY;IAEf;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,UAAU;IAElE;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAEnB;;;OAGG;IACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhC;;OAEG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAE5B;;OAEG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAE5B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,CAAC,OAAO,OAAO,UAAU;IAI7B,KAAK,IAAI,IAAI;IAKb,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC;IAKlD,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAMnC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAItB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB;;;;OAIG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB;;OAEG;IACH,WAAW,EAAE,OAAO,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC9B;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,sBAAsB,GAAG;IAC3D,aAAa,CAAC,EAAE,KAAK,CAAC;CACvB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,sBAAsB,GAAG;IAC9D,aAAa,EAAE,IAAI,CAAC;CACrB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,qBAAqB,GAAG,wBAAwB,CAAC;AAE/E;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,2BAA2B,GAAG,sBAAsB,CAAC;AAExF;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG,8BAA8B,GAAG,sBAAsB,CAAC;AAE9F;;GAEG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,MAAM,EAAE,IAAI,CAAC;IACb,QAAQ,EAAE,KAAK,CAAC;CACjB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,8BAA8B,GAAG;IAC3C,MAAM,EAAE,IAAI,EAAE,CAAC;IACf,QAAQ,EAAE,KAAK,CAAC;CACjB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE,IAAI,CAAC;IACb,QAAQ,EAAE,IAAI,CAAC;CAChB,CAAC;AAEF;;GAEG;AACH,oBAAY,UAAU;IACpB;;OAEG;IACH,cAAc,IAAI;IAClB;;OAEG;IACH,SAAS,IAAI;CACd;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC;IACtC;;;OAGG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAC5C;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,EAAE,sBAAsB,CAAC;IACrC;;;OAGG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;;;;;;;;;;;OAYG;IACH,WAAW,CAAC,EAAE,sBAAsB,CAAC;IACrC;;OAEG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC9C;;OAEG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,YAAY,GAAG,YAAY,CAAC;AAEjE;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,WAAW,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;GAGG;AACH,KAAK,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,GAAG,OAAO,CAAC;AAEpF;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAE3D;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,SAAS,CAAC;AAE1C;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,UAAU;IAC7B;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAEhC;;;;;OAKG;gBACS,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa;IAE5D;;;;OAIG;IACH,WAAW,IAAI,OAAO,CAAC,YAAY,CAAC;IAEpC;;;OAGG;IACH,MAAM,IAAI,IAAI;IAEd;;OAEG;IACH,WAAW,CACT,SAAS,EAAE,UAAU,EACrB,QAAQ,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,GACvC;QAAE,MAAM,EAAE,MAAM,IAAI,CAAA;KAAE;CAC1B;AAED;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,YAAY;IAC/B;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;IAElC;;;;;OAKG;gBACS,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,GAAG,SAAS,EAAE,OAAO,CAAC,EAAE,mBAAmB;IAErF;;;;OAIG;IACH,aAAa,IAAI,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAErC;;OAEG;IACH,KAAK,IAAI,IAAI;IAEb;;;;OAIG;IACH,WAAW,IAAI,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAEnC;;;OAGG;IACH,MAAM,IAAI,IAAI;IAEd;;;OAGG;IACH,OAAO,IAAI,kBAAkB;IAE7B;;;;;OAKG;IACH,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,kBAAkB,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,YAAY;IAE1F;;OAEG;IACH,WAAW,CACT,SAAS,EAAE,UAAU,EACrB,QAAQ,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,GACzC;QAAE,MAAM,EAAE,MAAM,IAAI,CAAA;KAAE;CAC1B"}
\ No newline at end of file
diff --git a/packages/expo-file-system/build/FileSystem.d.ts b/packages/expo-file-system/build/FileSystem.d.ts
index 1a8a68081f022e..66110b8ee003f8 100644
--- a/packages/expo-file-system/build/FileSystem.d.ts
+++ b/packages/expo-file-system/build/FileSystem.d.ts
@@ -67,6 +67,9 @@ export declare class File extends ExpoFileSystem.FileSystemFile implements Blob
arrayBuffer(): Promise;
stream(): ReadableStream>;
slice(start?: number, end?: number, contentType?: string): Blob;
+ upload(url: string, options?: UploadOptions): Promise;
+ createUploadTask(url: string, options?: UploadOptions): UploadTask;
+ static createDownloadTask(url: string, destination: File | Directory, options?: DownloadTaskOptions): DownloadTask;
}
/**
* Represents a directory on the filesystem.
diff --git a/packages/expo-file-system/build/FileSystem.d.ts.map b/packages/expo-file-system/build/FileSystem.d.ts.map
index f500c029685382..7e92c7c51e6456 100644
--- a/packages/expo-file-system/build/FileSystem.d.ts.map
+++ b/packages/expo-file-system/build/FileSystem.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"FileSystem.d.ts","sourceRoot":"","sources":["../src/FileSystem.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"FileSystem.d.ts","sourceRoot":"","sources":["../src/FileSystem.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/packages/expo-file-system/src/ExpoFileSystem.types.ts b/packages/expo-file-system/src/ExpoFileSystem.types.ts
index 04e5c1eb9eb14a..e5508eb9e7d923 100644
--- a/packages/expo-file-system/src/ExpoFileSystem.types.ts
+++ b/packages/expo-file-system/src/ExpoFileSystem.types.ts
@@ -410,6 +410,18 @@ export declare class File {
options?: DownloadOptions
): Promise;
+ /**
+ * Uploads this file to the network.
+ *
+ * The promise resolves with the HTTP response metadata and body for any completed response,
+ * including non-2xx status codes. It rejects only for local file errors, transport failures,
+ * or cancellation.
+ *
+ * @param url The URL to upload the file to.
+ * @param options Upload options.
+ */
+ upload(url: string, options?: UploadOptions): Promise;
+
/**
* An overload of the `pickFileAsync` method, which picks and returns a single `File`.
* This overload requires options to have `multipleFiles` flag be `undefined` or `false`.
diff --git a/packages/expo-file-system/src/FileSystem.ts b/packages/expo-file-system/src/FileSystem.ts
index 1509fe6a095a71..35f197a85e4700 100644
--- a/packages/expo-file-system/src/FileSystem.ts
+++ b/packages/expo-file-system/src/FileSystem.ts
@@ -149,6 +149,22 @@ export class File extends ExpoFileSystem.FileSystemFile implements Blob {
slice(start?: number, end?: number, contentType?: string): Blob {
return new Blob([this.bytesSync().slice(start, end)], { type: contentType });
}
+
+ upload(url: string, options?: UploadOptions): Promise {
+ return new UploadTask(this, url, options).uploadAsync();
+ }
+
+ createUploadTask(url: string, options?: UploadOptions): UploadTask {
+ return new UploadTask(this, url, options);
+ }
+
+ static createDownloadTask(
+ url: string,
+ destination: File | Directory,
+ options?: DownloadTaskOptions
+ ): DownloadTask {
+ return new DownloadTask(url, destination, options);
+ }
}
function createAbortError(reason?: string): Error {
@@ -624,16 +640,3 @@ export class DownloadTask extends ExpoFileSystem.FileSystemDownloadTask {
}
}
}
-
-// Add factory methods to File
-File.prototype.createUploadTask = function (url: string, options?: UploadOptions): UploadTask {
- return new UploadTask(this, url, options);
-};
-
-File.createDownloadTask = function (
- url: string,
- destination: File | Directory,
- options?: DownloadTaskOptions
-): DownloadTask {
- return new DownloadTask(url, destination, options);
-};
diff --git a/packages/expo-file-system/src/__tests__/FSNetworkTasks-test.native.ts b/packages/expo-file-system/src/__tests__/FSNetworkTasks-test.native.ts
index 445bfae6def7f7..3c29957a526f61 100644
--- a/packages/expo-file-system/src/__tests__/FSNetworkTasks-test.native.ts
+++ b/packages/expo-file-system/src/__tests__/FSNetworkTasks-test.native.ts
@@ -5,9 +5,67 @@ import {
Paths,
UploadTask,
DownloadTask,
+ UploadType,
type DownloadPauseState,
} from '../index';
+describe('File.upload()', () => {
+ const url = 'https://example.com/upload';
+ const mockUploadResult = { body: '{"ok":true}', status: 200, headers: { 'x-req-id': '1' } };
+
+ afterEach(() => {
+ jest.restoreAllMocks();
+ });
+
+ it('starts an upload task and resolves with its result', async () => {
+ const file = new File(Paths.cache, 'photo.jpg');
+ const uploadAsyncSpy = jest
+ .spyOn(UploadTask.prototype, 'uploadAsync')
+ .mockResolvedValue(mockUploadResult);
+
+ const result = await file.upload(url);
+
+ expect(uploadAsyncSpy).toHaveBeenCalledTimes(1);
+ expect(result).toEqual(mockUploadResult);
+ });
+
+ it('forwards upload options through the underlying task', async () => {
+ const file = new File(Paths.cache, 'photo.jpg');
+ jest
+ .spyOn(ExpoFileSystem.FileSystemUploadTask.prototype, 'start')
+ .mockResolvedValue({ body: 'validation failed', status: 422, headers: { 'x-error': '1' } });
+
+ const result = await file.upload(url, {
+ httpMethod: 'PATCH',
+ uploadType: UploadType.MULTIPART,
+ headers: { Authorization: 'Bearer token' },
+ fieldName: 'asset',
+ mimeType: 'image/jpeg',
+ parameters: { albumId: '42' },
+ sessionType: 'foreground',
+ });
+
+ expect(ExpoFileSystem.FileSystemUploadTask.prototype.start).toHaveBeenCalledWith(
+ url,
+ file,
+ expect.objectContaining({
+ httpMethod: 'PATCH',
+ uploadType: UploadType.MULTIPART,
+ headers: { Authorization: 'Bearer token' },
+ fieldName: 'asset',
+ mimeType: 'image/jpeg',
+ parameters: { albumId: '42' },
+ sessionType: 'foreground',
+ })
+ );
+ expect(result).toEqual({
+ body: 'validation failed',
+ status: 422,
+ headers: { 'x-error': '1' },
+ });
+ });
+});
+
describe('UploadTask', () => {
let file: File;
const url = 'https://example.com/upload';
diff --git a/packages/expo-modules-core/CHANGELOG.md b/packages/expo-modules-core/CHANGELOG.md
index 05a1092c7ac833..d66ea8684680c7 100644
--- a/packages/expo-modules-core/CHANGELOG.md
+++ b/packages/expo-modules-core/CHANGELOG.md
@@ -66,6 +66,7 @@
- [Android] Added AsyncFunction support to the functional `ExpoUIView` DSL. ([#44081](https://github.com/expo/expo/pull/44081) by [@kudo](https://github.com/kudo))
- Fixed `ExpoModulesMacros` precompiling. ([#44863](https://github.com/expo/expo/pull/44863) by [@kudo](https://github.com/kudo))
- [Android] Improve performance of `PersistentFileLog`. ([#45058](https://github.com/expo/expo/pull/45058) by [@lukmccall](https://github.com/lukmccall))
+- [Android] Improve performance of `EnumTypeConverter`. ([#45204](https://github.com/expo/expo/pull/45204) by [@lukmccall](https://github.com/lukmccall))
## 55.0.12 — 2026-02-25
diff --git a/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/ReadableTypeExtensions.kt b/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/ReadableTypeExtensions.kt
index 39cd56b156aae4..3b55699f714178 100644
--- a/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/ReadableTypeExtensions.kt
+++ b/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/ReadableTypeExtensions.kt
@@ -3,15 +3,14 @@ package expo.modules.kotlin
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.bridge.ReadableType
-import kotlin.reflect.KClass
-fun ReadableType.toKClass(): KClass<*> {
+fun ReadableType.toClass(): Class<*> {
return when (this) {
- ReadableType.Null -> Any::class
- ReadableType.Boolean -> Boolean::class
- ReadableType.Number -> Number::class
- ReadableType.String -> String::class
- ReadableType.Map -> ReadableMap::class
- ReadableType.Array -> ReadableArray::class
+ ReadableType.Null -> Any::class.java
+ ReadableType.Boolean -> Boolean::class.java
+ ReadableType.Number -> Number::class.java
+ ReadableType.String -> String::class.java
+ ReadableType.Map -> ReadableMap::class.java
+ ReadableType.Array -> ReadableArray::class.java
}
}
diff --git a/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt b/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt
index 9e7d1c1b16b4fb..2d86feeb4cb36c 100644
--- a/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt
+++ b/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt
@@ -71,22 +71,39 @@ open class CodedException(
inline fun errorCodeOf(): String =
CodedException.inferCode(T::class.java)
-internal class IncompatibleArgTypeException(
- argumentType: KClass<*>,
- desiredType: KClass<*>,
- cause: Throwable? = null
-) : CodedException(
- message = "Argument type '$argumentType' is not compatible with expected type '$desiredType'.",
- cause = cause
-)
+internal class IncompatibleArgTypeException : CodedException {
+ constructor(
+ argumentType: KClass<*>,
+ desiredType: KClass<*>,
+ cause: Throwable? = null
+ ) : super("Argument type '$argumentType' is not compatible with expected type '$desiredType'.", cause)
-internal class EnumNoSuchValueException(
- enumType: KClass>,
- enumConstants: Array>,
- value: Any?
-) : CodedException(
- message = "'$value' is not present in ${enumType.simpleName} enum, it must be one of: ${enumConstants.joinToString(separator = ", ") { "'${it.name}'" }}"
-)
+ constructor(
+ argumentType: KClass<*>,
+ desiredType: Class<*>,
+ cause: Throwable? = null
+ ) : super("Argument type '$argumentType' is not compatible with expected type '${desiredType.simpleName}'.", cause)
+
+ constructor(
+ argumentType: Class<*>,
+ desiredType: Class<*>,
+ cause: Throwable? = null
+ ) : super("Argument type '${argumentType.simpleName}' is not compatible with expected type '${desiredType.simpleName}'.", cause)
+}
+
+internal class EnumNoSuchValueException : CodedException {
+ constructor(
+ enumType: KClass>,
+ enumConstants: Array>,
+ value: Any?
+ ) : super("'$value' is not present in ${enumType.simpleName} enum, it must be one of: ${enumConstants.joinToString(separator = ", ") { "'${it.name}'" }}")
+
+ constructor(
+ enumType: Class<*>,
+ enumConstants: Array>,
+ value: Any?
+ ) : super("'$value' is not present in ${enumType.simpleName} enum, it must be one of: ${enumConstants.joinToString(separator = ", ") { "'${it.name}'" }}")
+}
internal class MissingTypeConverter(
forType: TypeDescriptor
@@ -276,11 +293,10 @@ internal class CollectionElementCastException private constructor(
}
@DoNotStrip
-class DynamicCastException(
- type: KClass<*>
-) : CodedException(
- message = "Could not cast dynamic value to '${type.qualifiedName}'."
-)
+class DynamicCastException : CodedException {
+ constructor(type: KClass<*>) : super("Could not cast dynamic value to '${type.qualifiedName}'.")
+ constructor(type: Class<*>) : super("Could not cast dynamic value to '${type.canonicalName}'.")
+}
@DoNotStrip
class JavaScriptEvaluateException(
diff --git a/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt b/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt
index 78b4a12ec8466b..982a3af2ac396a 100644
--- a/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt
+++ b/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt
@@ -181,9 +181,12 @@ class IntrospectableRecordConversionStrategy(
.firstOrNull { annotation -> annotation.jClass == Field::class.java }
?: return@mapNotNull null
- val propertyName = fieldAnnotation
- .arguments
- .getOrDefault("key", property.name) as String
+ val propertyName = (
+ fieldAnnotation
+ .arguments
+ .getOrDefault("key", property.name) as String
+ )
+ .ifEmpty { property.name }
val isRequired = property
.annotations
diff --git a/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/types/EnumTypeConverter.kt b/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/types/EnumTypeConverter.kt
index b19341e3114255..9f9fa74523ddc1 100644
--- a/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/types/EnumTypeConverter.kt
+++ b/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/types/EnumTypeConverter.kt
@@ -5,16 +5,16 @@ import expo.modules.kotlin.AppContext
import expo.modules.kotlin.exception.DynamicCastException
import expo.modules.kotlin.exception.EnumNoSuchValueException
import expo.modules.kotlin.exception.IncompatibleArgTypeException
-import expo.modules.kotlin.fastPrimaryConstructor
import expo.modules.kotlin.jni.ExpectedType
import expo.modules.kotlin.logger
-import expo.modules.kotlin.toKClass
-import kotlin.reflect.KClass
+import expo.modules.kotlin.toClass
+import java.lang.reflect.Field
+import java.lang.reflect.Modifier
class EnumTypeConverter(
- private val enumClass: KClass>
+ private val enumClass: Class>
) : DynamicAwareTypeConverters>() {
- private val enumConstants = requireNotNull(enumClass.java.enumConstants) {
+ private val enumConstants = requireNotNull(enumClass.enumConstants) {
"Passed type is not an enum type"
}.also {
require(it.isNotEmpty()) {
@@ -22,15 +22,12 @@ class EnumTypeConverter(
}
}
- private val primaryConstructor = requireNotNull(
- enumClass.fastPrimaryConstructor
- ) {
- "Cannot convert js value to enum without the primary constructor"
- }
+ private val userFields: List =
+ enumClass.declaredFields.filter { !Modifier.isStatic(it.modifiers) && !it.isSynthetic }
init {
- if (!Enumerable::class.java.isAssignableFrom(enumClass.java)) {
- logger.error("Enum '$enumClass' should inherit from ${Enumerable::class}.")
+ if (!Enumerable::class.java.isAssignableFrom(enumClass)) {
+ logger.error("Enum '${enumClass.simpleName}' should inherit from ${Enumerable::class}.")
}
}
@@ -39,34 +36,34 @@ class EnumTypeConverter(
override fun isTrivial() = false
override fun convertFromDynamic(value: Dynamic, context: AppContext?, forceConversion: Boolean): Enum<*> {
- if (primaryConstructor.parameters.isEmpty()) {
+ if (userFields.isEmpty()) {
return convertEnumWithoutParameter(
- value.asString() ?: throw DynamicCastException(String::class),
+ value.asString() ?: throw DynamicCastException(String::class.java),
enumConstants
)
- } else if (primaryConstructor.parameters.size == 1) {
+ } else if (userFields.size == 1) {
return convertEnumWithParameter(
value,
enumConstants,
- primaryConstructor.parameters.first().name!!
+ userFields.first()
)
}
- throw IncompatibleArgTypeException(value.type.toKClass(), enumClass)
+ throw IncompatibleArgTypeException(value.type.toClass(), enumClass)
}
override fun convertFromAny(value: Any, context: AppContext?, forceConversion: Boolean): Enum<*> {
- if (primaryConstructor.parameters.isEmpty()) {
+ if (userFields.isEmpty()) {
return convertEnumWithoutParameter(value as String, enumConstants)
- } else if (primaryConstructor.parameters.size == 1) {
+ } else if (userFields.size == 1) {
return convertEnumWithParameter(
value,
enumConstants,
- primaryConstructor.parameters.first().name!!
+ userFields.first()
)
}
- throw IncompatibleArgTypeException(value::class, enumClass)
+ throw IncompatibleArgTypeException(value.javaClass, enumClass)
}
/**
@@ -82,17 +79,14 @@ class EnumTypeConverter(
}
/**
- * If the primary constructor take one parameter, we treat this parameter as a enum value.
- * In that case, we handles two different types: Int and String.
+ * If the primary constructor take one parameter, we treat this parameter as an enum value.
+ * In that case, we handle two different types: Int and String.
*/
private fun convertEnumWithParameter(
jsValue: Any,
enumConstants: Array>,
- parameterName: String
+ field: Field
): Enum<*> {
- val field = enumClass.java.getDeclaredField(parameterName)
- requireNotNull(field) { "Cannot find a property for $parameterName parameter" }
-
field.isAccessible = true
val parameterType = field.type
@@ -102,7 +96,7 @@ class EnumTypeConverter(
enumConstants.find {
field.get(it) == jsUnwrapValue
}
- ) { "Couldn't convert '$jsValue' to ${enumClass.simpleName} where $parameterName is the enum parameter" }
+ ) { "Couldn't convert '$jsValue' to ${enumClass.simpleName} where ${field.name} is the enum parameter" }
}
private fun Any.unwrapValue(parameterType: Class<*>): Any? {
diff --git a/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt b/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt
index 3ef76eb24350ee..9ca16b7d02c22c 100644
--- a/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt
+++ b/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt
@@ -52,7 +52,6 @@ import java.net.URI
import java.net.URL
import java.nio.file.Path
import java.time.LocalDate
-import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.time.Duration
@@ -135,7 +134,7 @@ object TypeConverterProviderImpl : TypeConverterProvider {
if (jClass.isEnum) {
@Suppress("UNCHECKED_CAST")
- return EnumTypeConverter(jClass.kotlin as KClass>)
+ return EnumTypeConverter(jClass as Class>)
}
val cachedConverter = cachedRecordConverters[jClass]
diff --git a/packages/expo-ui/android/src/main/java/expo/modules/ui/TextView.kt b/packages/expo-ui/android/src/main/java/expo/modules/ui/TextView.kt
index e3ef105bbfc615..66905c6331389a 100644
--- a/packages/expo-ui/android/src/main/java/expo/modules/ui/TextView.kt
+++ b/packages/expo-ui/android/src/main/java/expo/modules/ui/TextView.kt
@@ -222,8 +222,7 @@ interface TextSpanStyle {
val shadow: TextShadowRecord?
}
-// TODO(@lukmccall): Figure out why it's crashing with `Attempt to invoke virtual method 'io.github.lukmccall.pika.PIntrospectionData expo.modules.ui.TextSpanRecord$Companion.__PIntrospectionData()' on a null object reference`
-// @OptimizedRecord
+@OptimizedRecord
data class TextSpanRecord(
@Field override val text: String = "",
@Field val children: List? = null,
diff --git a/packages/expo-updates/android/src/main/java/expo/modules/updates/logging/UpdatesLogEntry.kt b/packages/expo-updates/android/src/main/java/expo/modules/updates/logging/UpdatesLogEntry.kt
index ff19ed869dd195..8dc78090e92ea2 100644
--- a/packages/expo-updates/android/src/main/java/expo/modules/updates/logging/UpdatesLogEntry.kt
+++ b/packages/expo-updates/android/src/main/java/expo/modules/updates/logging/UpdatesLogEntry.kt
@@ -63,5 +63,10 @@ data class UpdatesLogEntry(
null
}
}
+
+ fun getTimestamp(json: String): Long? {
+ return runCatching { JSONObject(json).getLong("timestamp") }
+ .getOrNull()
+ }
}
}
diff --git a/packages/expo-updates/android/src/main/java/expo/modules/updates/logging/UpdatesLogReader.kt b/packages/expo-updates/android/src/main/java/expo/modules/updates/logging/UpdatesLogReader.kt
index 255802680247a1..4a2236cc3d296c 100644
--- a/packages/expo-updates/android/src/main/java/expo/modules/updates/logging/UpdatesLogReader.kt
+++ b/packages/expo-updates/android/src/main/java/expo/modules/updates/logging/UpdatesLogReader.kt
@@ -40,8 +40,8 @@ class UpdatesLogReader(
private val persistentLog = PersistentFileLog(EXPO_UPDATES_LOGGING_TAG, filesDirectory)
private fun isEntryStringLaterThanTimestamp(entryString: String, timestamp: Long): Boolean {
- val entry = UpdatesLogEntry.create(entryString) ?: return false
- return entry.timestamp >= timestamp
+ val entryTimestamp = UpdatesLogEntry.getTimestamp(entryString) ?: return false
+ return entryTimestamp >= timestamp
}
private fun epochFromDateOrOneDayAgo(date: Date): Long {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7b07489d6acb66..e0e2368945045c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -2075,6 +2075,28 @@ importers:
specifier: ^1.2.2
version: 1.2.2
+ packages/@expo/codemod:
+ dependencies:
+ chalk:
+ specifier: ^4.1.2
+ version: 4.1.2
+ jscodeshift:
+ specifier: ^17.1.2
+ version: 17.3.0(@babel/preset-env@7.29.2(@babel/core@7.29.0))
+ tinyglobby:
+ specifier: ^0.2.15
+ version: 0.2.15
+ devDependencies:
+ '@types/jscodeshift':
+ specifier: ^17.0.0
+ version: 17.3.0
+ '@types/node':
+ specifier: ^22.14.0
+ version: 22.19.15
+ expo-module-scripts:
+ specifier: workspace:*
+ version: link:../../expo-module-scripts
+
packages/@expo/config:
dependencies:
'@expo/config-plugins':
@@ -3001,7 +3023,7 @@ importers:
version: link:../expo-splash-screen
jest:
specifier: ^29.2.1
- version: 29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2))
+ version: 29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@22.19.15)(typescript@6.0.2))
react-refresh:
specifier: ^0.14.2
version: 0.14.2
@@ -3259,7 +3281,7 @@ importers:
version: eslint@8.57.1
jest:
specifier: ^29.7.0
- version: 29.7.0(@types/node@25.5.0)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2))
+ version: 29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@22.19.15)(typescript@6.0.2))
prettier:
specifier: ^3.5.3
version: 3.5.3
@@ -3513,7 +3535,7 @@ importers:
devDependencies:
'@testing-library/react-native':
specifier: ^13.3.0
- version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2)))(react-native@0.85.2(@babel/core@7.29.0)(@react-native/jest-preset@0.85.2(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3)
+ version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@22.19.15)(typescript@6.0.2)))(react-native@0.85.2(@babel/core@7.29.0)(@react-native/jest-preset@0.85.2(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3)
'@types/node':
specifier: ^22.14.0
version: 22.19.15
@@ -4014,7 +4036,7 @@ importers:
version: 7.28.5(@babel/core@7.29.0)
'@testing-library/react-native':
specifier: ^13.3.0
- version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2)))(react-native@0.85.2(@babel/core@7.29.0)(@react-native/jest-preset@0.85.2(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3)
+ version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@22.19.15)(typescript@6.0.2)))(react-native@0.85.2(@babel/core@7.29.0)(@react-native/jest-preset@0.85.2(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3)
'@types/node':
specifier: ^22.14.0
version: 22.19.15
@@ -4196,7 +4218,7 @@ importers:
devDependencies:
'@testing-library/react-native':
specifier: ^13.3.0
- version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2)))(react-native@0.85.2(@babel/core@7.29.0)(@react-native/jest-preset@0.85.2(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3)
+ version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@22.19.15)(typescript@6.0.2)))(react-native@0.85.2(@babel/core@7.29.0)(@react-native/jest-preset@0.85.2(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3)
'@types/fontfaceobserver':
specifier: ^2.1.3
version: 2.1.3
@@ -5038,7 +5060,7 @@ importers:
version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@testing-library/react-native':
specifier: ^13.3.0
- version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2)))(react-native@0.85.2(@babel/core@7.29.0)(@react-native/jest-preset@0.85.2(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3)
+ version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@22.19.15)(typescript@6.0.2)))(react-native@0.85.2(@babel/core@7.29.0)(@react-native/jest-preset@0.85.2(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3)
'@tsd/typescript':
specifier: ^5.9.3
version: 5.9.3
@@ -5272,7 +5294,7 @@ importers:
devDependencies:
'@testing-library/react-native':
specifier: ^13.3.0
- version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2)))(react-native@0.85.2(@babel/core@7.29.0)(@react-native/jest-preset@0.85.2(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3)
+ version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@22.19.15)(typescript@6.0.2)))(react-native@0.85.2(@babel/core@7.29.0)(@react-native/jest-preset@0.85.2(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3)
'@types/better-sqlite3':
specifier: ^7.6.6
version: 7.6.13
@@ -5324,7 +5346,7 @@ importers:
version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@testing-library/react-native':
specifier: ^13.3.0
- version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2)))(react-native@0.85.2(@babel/core@7.29.0)(@react-native/jest-preset@0.85.2(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3)
+ version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@22.19.15)(typescript@6.0.2)))(react-native@0.85.2(@babel/core@7.29.0)(@react-native/jest-preset@0.85.2(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3)
'@types/node':
specifier: ^22.14.0
version: 22.19.15
@@ -6826,6 +6848,12 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
+ '@babel/preset-flow@7.27.1':
+ resolution: {integrity: sha512-ez3a2it5Fn6P54W8QkbfIyyIbxlXvcxyWHHvno1Wg0Ej5eiJY5hBb8ExttoIOJJk7V2dZE6prP7iby5q2aQ0Lg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
'@babel/preset-modules@0.1.6-no-external-plugins':
resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==}
peerDependencies:
@@ -6843,6 +6871,12 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
+ '@babel/register@7.28.6':
+ resolution: {integrity: sha512-pgcbbEl/dWQYb6L6Yew6F94rdwygfuv+vJ/tXfwIOYAfPB6TNWpXUMEtEq3YuTeHRdvMIhvz13bkT9CNaS+wqA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
'@babel/runtime@7.29.2':
resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==}
engines: {node: '>=6.9.0'}
@@ -9107,6 +9141,9 @@ packages:
'@types/jest@29.5.14':
resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==}
+ '@types/jscodeshift@17.3.0':
+ resolution: {integrity: sha512-ogvGG8VQQqAQQ096uRh+d6tBHrYuZjsumHirKtvBa5qEyTMN3IQJ7apo+sw9lxaB/iKWIhbbLlF3zmAWk9XQIg==}
+
'@types/jsdom@20.0.1':
resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==}
@@ -9804,6 +9841,10 @@ packages:
resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==}
engines: {node: '>=0.10.0'}
+ ast-types@0.16.1:
+ resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==}
+ engines: {node: '>=4'}
+
async-each@1.0.6:
resolution: {integrity: sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==}
@@ -10217,6 +10258,10 @@ packages:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'}
+ clone-deep@4.0.1:
+ resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==}
+ engines: {node: '>=6'}
+
clone-response@1.0.3:
resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==}
@@ -10293,6 +10338,9 @@ packages:
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
engines: {node: '>= 12'}
+ commondir@1.0.1:
+ resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
+
component-emitter@1.3.1:
resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==}
@@ -11355,6 +11403,10 @@ packages:
find-babel-config@2.1.2:
resolution: {integrity: sha512-ZfZp1rQyp4gyuxqt1ZqjFGVeVBvmpURMqdIWXbPRfB97Bf6BzdK/xSIbylEINzQ0kB5tlDQfn9HkNXXWsqTqLg==}
+ find-cache-dir@2.1.0:
+ resolution: {integrity: sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==}
+ engines: {node: '>=6'}
+
find-process@1.4.11:
resolution: {integrity: sha512-mAOh9gGk9WZ4ip5UjV0o6Vb4SrfnAmtsFNzkMRH9HQiFXVQnDyQFrSHTK5UoG6E+KV+s+cIznbtwpfN41l2nFA==}
hasBin: true
@@ -11388,6 +11440,10 @@ packages:
flow-enums-runtime@0.0.6:
resolution: {integrity: sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==}
+ flow-parser@0.311.0:
+ resolution: {integrity: sha512-3tbmV0VdoFXdNqJjNksVLIksu78BvUMK5eAR5ZF1TAsV+wVu4jXipRpHKquDGhPRKrLl2DCafjRTV99jVMlMwQ==}
+ engines: {node: '>=0.4.0'}
+
fmerge@1.2.0:
resolution: {integrity: sha512-ByFA9Pg++TFyB+MlYTEQ/FF/hpEVYjr56xWOpSzCxsjkwfWC7miNIgJAFj3cNbhtn/vusqyen55M8Bg7t3lYiA==}
@@ -12470,6 +12526,16 @@ packages:
jsc-safe-url@0.2.4:
resolution: {integrity: sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==}
+ jscodeshift@17.3.0:
+ resolution: {integrity: sha512-LjFrGOIORqXBU+jwfC9nbkjmQfFldtMIoS6d9z2LG/lkmyNXsJAySPT+2SWXJEoE68/bCWcxKpXH37npftgmow==}
+ engines: {node: '>=16'}
+ hasBin: true
+ peerDependencies:
+ '@babel/preset-env': ^7.1.6
+ peerDependenciesMeta:
+ '@babel/preset-env':
+ optional: true
+
jsdom@20.0.3:
resolution: {integrity: sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==}
engines: {node: '>=14'}
@@ -13669,6 +13735,10 @@ packages:
resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==}
engines: {node: '>=16.20.0'}
+ pkg-dir@3.0.0:
+ resolution: {integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==}
+ engines: {node: '>=6'}
+
pkg-dir@4.2.0:
resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
engines: {node: '>=8'}
@@ -14278,6 +14348,10 @@ packages:
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
engines: {node: '>= 14.18.0'}
+ recast@0.23.11:
+ resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==}
+ engines: {node: '>= 4'}
+
recursive-iterator@3.3.0:
resolution: {integrity: sha512-uc2aWu5ejeqFe4EqOD7eGqY2hn324FS9dEqRl6uOjG002X0SEilk6apBWckqjsL7NDEBjNKaqDOg+ejg30iDTA==}
engines: {node: '>=6'}
@@ -14603,6 +14677,10 @@ packages:
resolution: {integrity: sha512-TPbeg0b7ylrswdGCji8FRGFAKuqbpQlLbL8SOle3j1iHSs5Ob5mhvMAxWN2UItOjgALAB5Zp3fmMfj8mbWvXKw==}
engines: {node: '>=10'}
+ shallow-clone@3.0.1:
+ resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==}
+ engines: {node: '>=8'}
+
shallowequal@1.1.0:
resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==}
@@ -15063,6 +15141,9 @@ packages:
through@2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
+ tiny-invariant@1.3.3:
+ resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
+
tiny@0.0.10:
resolution: {integrity: sha512-BbTnGYnHGMf6/ctSctXyIGFQ3afpio2Xw2mlPONTL1pqeggpBAC80bZaoAJdsIqBFA8Ccp5ltJoz0YDWaxF3pg==}
engines: {node: '>= 0.8.0'}
@@ -15076,6 +15157,10 @@ packages:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'}
+ tmp@0.2.5:
+ resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==}
+ engines: {node: '>=14.14'}
+
tmpl@1.0.5:
resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}
@@ -15861,6 +15946,10 @@ packages:
resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+ write-file-atomic@5.0.1:
+ resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
ws@7.5.10:
resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==}
engines: {node: '>=8.3.0'}
@@ -16845,6 +16934,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/preset-flow@7.27.1(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+ '@babel/helper-validator-option': 7.27.1
+ '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.29.0)
+
'@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.29.0)':
dependencies:
'@babel/core': 7.29.0
@@ -16875,6 +16971,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/register@7.28.6(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ clone-deep: 4.0.1
+ find-cache-dir: 2.1.0
+ make-dir: 2.1.0
+ pirates: 4.0.7
+ source-map-support: 0.5.21
+
'@babel/runtime@7.29.2': {}
'@babel/template@7.28.6':
@@ -17935,7 +18040,6 @@ snapshots:
- babel-plugin-macros
- supports-color
- ts-node
- optional: true
'@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2))':
dependencies:
@@ -19114,18 +19218,6 @@ snapshots:
optionalDependencies:
jest: 29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@22.19.15)(typescript@6.0.2))
- '@testing-library/react-native@13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2)))(react-native@0.85.2(@babel/core@7.29.0)(@react-native/jest-preset@0.85.2(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3)':
- dependencies:
- jest-matcher-utils: 30.3.0
- picocolors: 1.1.1
- pretty-format: 30.3.0
- react: 19.2.3
- react-native: 0.85.2(@babel/core@7.29.0)(@react-native/jest-preset@0.85.2(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3)
- react-test-renderer: 19.2.3(react@19.2.3)
- redent: 3.0.0
- optionalDependencies:
- jest: 29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2))
-
'@testing-library/react-native@13.3.3(jest@29.7.0(@types/node@25.5.0)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2)))(react-native@0.85.2(@babel/core@7.29.0)(@react-native/jest-preset@0.85.2(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
jest-matcher-utils: 30.3.0
@@ -19389,6 +19481,11 @@ snapshots:
expect: 29.7.0
pretty-format: 29.7.0
+ '@types/jscodeshift@17.3.0':
+ dependencies:
+ ast-types: 0.16.1
+ recast: 0.23.11
+
'@types/jsdom@20.0.1':
dependencies:
'@types/node': 22.19.15
@@ -20204,6 +20301,10 @@ snapshots:
assign-symbols@1.0.0: {}
+ ast-types@0.16.1:
+ dependencies:
+ tslib: 2.8.1
+
async-each@1.0.6: {}
async-function@1.0.0: {}
@@ -20749,6 +20850,12 @@ snapshots:
strip-ansi: 6.0.1
wrap-ansi: 7.0.0
+ clone-deep@4.0.1:
+ dependencies:
+ is-plain-object: 2.0.4
+ kind-of: 6.0.3
+ shallow-clone: 3.0.1
+
clone-response@1.0.3:
dependencies:
mimic-response: 1.0.1
@@ -20811,6 +20918,8 @@ snapshots:
commander@8.3.0: {}
+ commondir@1.0.1: {}
+
component-emitter@1.3.1: {}
component-type@1.2.2: {}
@@ -20900,22 +21009,6 @@ snapshots:
- babel-plugin-macros
- supports-color
- ts-node
- optional: true
-
- create-jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2)):
- dependencies:
- '@jest/types': 29.6.3
- chalk: 4.1.2
- exit: 0.1.2
- graceful-fs: 4.2.11
- jest-config: 29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2))
- jest-util: 29.7.0
- prompts: 2.4.2
- transitivePeerDependencies:
- - '@types/node'
- - babel-plugin-macros
- - supports-color
- - ts-node
create-jest@29.7.0(@types/node@25.5.0)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2)):
dependencies:
@@ -21818,7 +21911,7 @@ snapshots:
eslint@8.57.1:
dependencies:
- '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1)
+ '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@1.21.7))
'@eslint-community/regexpp': 4.12.2
'@eslint/eslintrc': 2.1.4
'@eslint/js': 8.57.1
@@ -22313,6 +22406,12 @@ snapshots:
dependencies:
json5: 2.2.3
+ find-cache-dir@2.1.0:
+ dependencies:
+ commondir: 1.0.1
+ make-dir: 2.1.0
+ pkg-dir: 3.0.0
+
find-process@1.4.11:
dependencies:
chalk: 4.1.2
@@ -22379,6 +22478,8 @@ snapshots:
flow-enums-runtime@0.0.6: {}
+ flow-parser@0.311.0: {}
+
fmerge@1.2.0: {}
folder-hash@4.1.2:
@@ -23317,26 +23418,6 @@ snapshots:
- babel-plugin-macros
- supports-color
- ts-node
- optional: true
-
- jest-cli@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2)):
- dependencies:
- '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2))
- '@jest/test-result': 29.7.0
- '@jest/types': 29.6.3
- chalk: 4.1.2
- create-jest: 29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2))
- exit: 0.1.2
- import-local: 3.2.0
- jest-config: 29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2))
- jest-util: 29.7.0
- jest-validate: 29.7.0
- yargs: 17.7.2
- transitivePeerDependencies:
- - '@types/node'
- - babel-plugin-macros
- - supports-color
- - ts-node
jest-cli@29.7.0(@types/node@25.5.0)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2)):
dependencies:
@@ -23387,7 +23468,6 @@ snapshots:
transitivePeerDependencies:
- babel-plugin-macros
- supports-color
- optional: true
jest-config@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2)):
dependencies:
@@ -23740,19 +23820,6 @@ snapshots:
- babel-plugin-macros
- supports-color
- ts-node
- optional: true
-
- jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2)):
- dependencies:
- '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2))
- '@jest/types': 29.6.3
- import-local: 3.2.0
- jest-cli: 29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2))
- transitivePeerDependencies:
- - '@types/node'
- - babel-plugin-macros
- - supports-color
- - ts-node
jest@29.7.0(@types/node@25.5.0)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.2)):
dependencies:
@@ -23791,6 +23858,31 @@ snapshots:
jsc-safe-url@0.2.4: {}
+ jscodeshift@17.3.0(@babel/preset-env@7.29.2(@babel/core@7.29.0)):
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/parser': 7.29.2
+ '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0)
+ '@babel/preset-flow': 7.27.1(@babel/core@7.29.0)
+ '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0)
+ '@babel/register': 7.28.6(@babel/core@7.29.0)
+ flow-parser: 0.311.0
+ graceful-fs: 4.2.11
+ micromatch: 4.0.8
+ neo-async: 2.6.2
+ picocolors: 1.1.1
+ recast: 0.23.11
+ tmp: 0.2.5
+ write-file-atomic: 5.0.1
+ optionalDependencies:
+ '@babel/preset-env': 7.29.2(@babel/core@7.29.0)
+ transitivePeerDependencies:
+ - supports-color
+
jsdom@20.0.3:
dependencies:
abab: 2.0.6
@@ -25235,6 +25327,10 @@ snapshots:
pkce-challenge@5.0.1: {}
+ pkg-dir@3.0.0:
+ dependencies:
+ find-up: 3.0.0
+
pkg-dir@4.2.0:
dependencies:
find-up: 4.1.0
@@ -25924,6 +26020,14 @@ snapshots:
readdirp@4.1.2: {}
+ recast@0.23.11:
+ dependencies:
+ ast-types: 0.16.1
+ esprima: 4.0.1
+ source-map: 0.6.1
+ tiny-invariant: 1.3.3
+ tslib: 2.8.1
+
recursive-iterator@3.3.0: {}
recursive-omit-by@2.0.0:
@@ -26283,6 +26387,10 @@ snapshots:
sf-symbols-typescript@2.2.0: {}
+ shallow-clone@3.0.1:
+ dependencies:
+ kind-of: 6.0.3
+
shallowequal@1.1.0: {}
sharp-cli@5.2.0:
@@ -26832,6 +26940,8 @@ snapshots:
through@2.3.8: {}
+ tiny-invariant@1.3.3: {}
+
tiny@0.0.10:
dependencies:
lru-cache: 2.5.2
@@ -26843,6 +26953,8 @@ snapshots:
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
+ tmp@0.2.5: {}
+
tmpl@1.0.5: {}
to-object-path@0.3.0:
@@ -27804,6 +27916,11 @@ snapshots:
imurmurhash: 0.1.4
signal-exit: 3.0.7
+ write-file-atomic@5.0.1:
+ dependencies:
+ imurmurhash: 0.1.4
+ signal-exit: 4.1.0
+
ws@7.5.10: {}
ws@8.19.0: {}