Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions apps/minimal-tester/android/brownfield/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,6 @@ android {
properties["newArchEnabled"].toString(),
)
buildConfigField("boolean", "IS_HERMES_ENABLED", properties["hermesEnabled"].toString())
buildConfigField(
"boolean",
"IS_EDGE_TO_EDGE_ENABLED",
properties["edgeToEdgeEnabled"].toString(),
)
buildConfigField(
"String",
"REACT_NATIVE_RELEASE_LEVEL",
Expand Down
4 changes: 0 additions & 4 deletions apps/minimal-tester/android/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,3 @@ EX_DEV_CLIENT_NETWORK_INSPECTOR=true

# Use legacy packaging to compress native libraries in the resulting APK.
expo.useLegacyPackaging=false

# Specifies whether the app is configured to use edge-to-edge via the app config or plugin
# WARNING: This property has been deprecated and will be removed in Expo SDK 55. Use `edgeToEdgeEnabled` or `react.edgeToEdgeEnabled` to determine whether the project is using edge-to-edge.
expo.edgeToEdgeEnabled=true
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Host, Carousel, Box, background, size } from '@expo/ui/jetpack-compose';
import { Host, Carousel, Box } from '@expo/ui/jetpack-compose';
import { background, size } from '@expo/ui/jetpack-compose/modifiers';
import { Image } from 'expo-image';
import * as React from 'react';
import { View } from 'react-native';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Host, Column, Row, Box, Text } from '@expo/ui/jetpack-compose';
import {
background,
size,
Expand All @@ -8,12 +9,7 @@ import {
clickable,
animateContentSize,
paddingAll,
Host,
Column,
Row,
Box,
Text,
} from '@expo/ui/jetpack-compose';
} from '@expo/ui/jetpack-compose/modifiers';
import { useState } from 'react';
import { Button, View, StyleSheet, Text as RNText } from 'react-native';

Expand Down Expand Up @@ -131,7 +127,7 @@ export default function JetpackComposePrimitivesScreen() {
background('#ffdddd'),
clickable(() => setChecked((c) => !c)),
]}>
<Box modifiers={[matchParentSize(), background('#ddddff'), paddingAll(30)]} />
<Box modifiers={[matchParentSize(), paddingAll(30), background('#ddddff')]} />
</Box>
</Column>
</Host>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Host, rotate, Shape, size, Row } from '@expo/ui/jetpack-compose';
import { Host, Shape, Row } from '@expo/ui/jetpack-compose';
import { rotate, size } from '@expo/ui/jetpack-compose/modifiers';
import { useEffect } from 'react';
import { View } from 'react-native';
import Animated, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ export default function TextInputScreen() {
<Section title="Current value">
<Text>{JSON.stringify(value)}</Text>
</Section>
<Button
onPress={async () => {
textRef.current?.setText('Hello there!');
}}>
Set text
</Button>
<Host matchContents>
<Button
onPress={async () => {
textRef.current?.setText('Hello there!');
}}>
Set text
</Button>
</Host>
<Section title="Text Input">
<Host matchContents>
<TextInput
Expand Down
2 changes: 2 additions & 0 deletions packages/@expo/cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
- Fix RSC support in development ([#42617](https://github.com/expo/expo/pull/42617) by [@hassankhan](https://github.com/hassankhan))
- Fix loader URL resolution for nested `/index` paths ([#42629](https://github.com/expo/expo/pull/42629) by [@hassankhan](https://github.com/hassankhan))
- [web] Ensure `<Head>` component re-renders when focus changes ([#42681](https://github.com/expo/expo/pull/42681) by [@hassankhan](https://github.com/hassankhan))
- Mark `expo-router` as optional peer to prevent auto-installation ([#42728](https://github.com/expo/expo/pull/42728) by [@kitten](https://github.com/kitten))

### 💡 Others

- Bump `@expo/xcpretty` ([#42485](https://github.com/expo/expo/pull/42485) by [@kitten](https://github.com/kitten))
- Refactor to replace GraphQL fetch client ([#42556](https://github.com/expo/expo/pull/42556) by [@kitten](https://github.com/kitten))
- Replace tar dependency and `multipart/mixed` logic with `multitars` package ([#42472](https://github.com/expo/expo/pull/42472) by [@kitten](https://github.com/kitten))
- Improve global resolution for `ExternalModule` resolution ([#42513](https://github.com/expo/expo/pull/42513) by [@kitten](https://github.com/kitten))
- Replace `undici` dependency with `fetch-nodeshim` and Node built-ins ([#42720](https://github.com/expo/expo/pull/42720) by [@kitten](https://github.com/kitten))

## 55.0.4 — 2026-01-27

Expand Down
4 changes: 2 additions & 2 deletions packages/@expo/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,14 @@
"@expo/env": "~2.0.11",
"@expo/image-utils": "^0.8.11",
"@expo/json-file": "^10.0.11",
"@expo/log-box": "55.0.3",
"@expo/metro": "~54.2.0",
"@expo/metro-config": "~55.0.3",
"@expo/osascript": "^2.4.2",
"@expo/package-manager": "^1.10.2",
"@expo/plist": "^0.5.2",
"@expo/prebuild-config": "^55.0.3",
"@expo/router-server": "^55.0.3",
"@expo/log-box": "55.0.3",
"@expo/schema-utils": "^55.0.2",
"@expo/spawn-async": "^1.7.2",
"@expo/ws-tunnel": "^1.0.1",
Expand All @@ -73,6 +73,7 @@
"dnssd-advertise": "^1.1.1",
"env-editor": "^0.4.1",
"expo-server": "^55.0.2",
"fetch-nodeshim": "^0.4.2",
"getenv": "^2.0.0",
"glob": "^13.0.0",
"lan-network": "^0.1.6",
Expand All @@ -95,7 +96,6 @@
"stacktrace-parser": "^0.1.10",
"structured-headers": "^0.4.1",
"terminal-link": "^2.1.1",
"undici": "^6.18.2",
"wrap-ansi": "^7.0.0",
"ws": "^8.12.1",
"zod": "^3.25.76"
Expand Down
9 changes: 3 additions & 6 deletions packages/@expo/cli/src/api/graphql/client.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import type { Response } from 'undici';

import * as Log from '../../log';
import { fetch } from '../../utils/fetch';
import { fetch, type Response } from '../../utils/fetch';
import { getExpoApiBaseUrl } from '../endpoint';
import {
getResponseDataOrThrow,
Expand All @@ -10,7 +8,6 @@ import {
} from '../rest/client';
import { FetchLike } from '../rest/client.types';
import { wrapFetchWithOffline } from '../rest/wrapFetchWithOffline';
import { wrapFetchWithProxy } from '../rest/wrapFetchWithProxy';
import { wrapFetchWithUserAgent } from '../rest/wrapFetchWithUserAgent';
import { getAccessToken, getSession } from '../user/UserSettings';

Expand Down Expand Up @@ -39,7 +36,7 @@ export const query = (() => {
let _fetch: FetchLike | undefined;
const wrappedFetch: FetchLike = (...args) => {
if (!_fetch) {
_fetch = wrapFetchWithOffline(wrapFetchWithProxy(wrapFetchWithUserAgent(fetch)));
_fetch = wrapFetchWithOffline(wrapFetchWithUserAgent(fetch));
}
return _fetch(...args);
};
Expand Down Expand Up @@ -130,7 +127,7 @@ export const query = (() => {
// If we have a transient error, we retry immediately and discard the data
// Otherwise, we store the first available error and get the data
if ('errors' in json && Array.isArray(json.errors)) {
isTransient = json.errors.some((e) => e?.extensions?.isTransient);
isTransient = json.errors.some((e: any) => e?.extensions?.isTransient);
if (isTransient) {
data = undefined;
continue;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export class FileSystemResponseCache implements ResponseCache {
}

return {
body: responseBody,
body: responseBody as globalThis.ReadableStream,
info: cleanInfo,
};
} catch {
Expand Down Expand Up @@ -108,7 +108,7 @@ export class FileSystemResponseCache implements ResponseCache {
} else {
// Create write stream and pipe response body to file
const writeStream = fs.createWriteStream(paths.body);
const nodeStream = Readable.fromWeb(forWrite);
const nodeStream = Readable.fromWeb(forWrite as ReadableStream);
nodeStream.pipe(writeStream);

// Wait for the stream to finish
Expand Down
4 changes: 2 additions & 2 deletions packages/@expo/cli/src/api/rest/cache/ResponseCache.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import crypto from 'crypto';
import type { Response, RequestInfo, RequestInit } from 'fetch-nodeshim';
import { ReadStream } from 'fs';
import type { Response, RequestInfo, RequestInit } from 'undici';

const GLOBAL_CACHE_VERSION = 4;

export type ResponseCacheEntry = {
body: import('stream/web').ReadableStream;
body: ReadableStream;
info: ReturnType<typeof getResponseInfo>;
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FormData } from 'fetch-nodeshim';
import { vol } from 'memfs';
import nock from 'nock';
import { FormData } from 'undici';

import type { FetchLike } from '../../client.types';
import { FileSystemResponseCache } from '../FileSystemResponseCache';
Expand Down
4 changes: 2 additions & 2 deletions packages/@expo/cli/src/api/rest/cache/wrapFetchWithCache.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Response, type RequestInfo, type RequestInit } from 'undici';
import { Response, type RequestInfo, type RequestInit } from 'fetch-nodeshim';

import { getRequestCacheKey, getResponseInfo, type ResponseCache } from './ResponseCache';
import type { FetchLike } from '../client.types';

const debug = require('debug')('expo:undici-cache');
const debug = require('debug')('expo:fetch-cache');

export function wrapFetchWithCache(fetch: FetchLike, cache: ResponseCache): FetchLike {
return async function cachedFetch(url: RequestInfo, init?: RequestInit) {
Expand Down
12 changes: 3 additions & 9 deletions packages/@expo/cli/src/api/rest/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import type { FetchLike } from './client.types';
import { wrapFetchWithBaseUrl } from './wrapFetchWithBaseUrl';
import { wrapFetchWithOffline } from './wrapFetchWithOffline';
import { wrapFetchWithProgress } from './wrapFetchWithProgress';
import { wrapFetchWithProxy } from './wrapFetchWithProxy';
import { wrapFetchWithUserAgent } from './wrapFetchWithUserAgent';
import { env } from '../../utils/env';
import { CommandError } from '../../utils/errors';
Expand Down Expand Up @@ -120,10 +119,7 @@ export function wrapFetchWithCredentials(fetchFunction: FetchLike): FetchLike {
return response;
} catch (error: any) {
// When running `expo start`, but wifi or internet has issues
if (
isNetworkError(error) || // node-fetch error handling
('cause' in error && isNetworkError(error.cause)) // undici error handling
) {
if (isNetworkError(error) || ('cause' in error && isNetworkError(error.cause))) {
disableNetwork();

throw new CommandError(
Expand Down Expand Up @@ -159,9 +155,7 @@ const fetchWithOffline = wrapFetchWithOffline(wrapFetchWithUserAgent(fetch));

const fetchWithBaseUrl = wrapFetchWithBaseUrl(fetchWithOffline, getExpoApiBaseUrl() + '/v2/');

const fetchWithProxy = wrapFetchWithProxy(fetchWithBaseUrl);

const fetchWithCredentials = wrapFetchWithProgress(wrapFetchWithCredentials(fetchWithProxy));
const fetchWithCredentials = wrapFetchWithProgress(wrapFetchWithCredentials(fetchWithBaseUrl));

/**
* Create an instance of the fully qualified fetch command (auto authentication and api) but with caching in the '~/.expo' directory.
Expand Down Expand Up @@ -196,4 +190,4 @@ export function createCachedFetch({
}

/** Instance of fetch with automatic base URL pointing to the Expo API, user credential injection, and API error handling. Caching not included. */
export const fetchAsync = wrapFetchWithProgress(wrapFetchWithCredentials(fetchWithProxy));
export const fetchAsync = wrapFetchWithProgress(wrapFetchWithCredentials(fetchWithBaseUrl));
2 changes: 1 addition & 1 deletion packages/@expo/cli/src/api/rest/client.types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { RequestInfo, RequestInit, Response } from 'undici';
import { type RequestInfo, type RequestInit } from 'fetch-nodeshim';
import type { URLSearchParams } from 'url';

export type ProgressCallback = (props: {
Expand Down
2 changes: 0 additions & 2 deletions packages/@expo/cli/src/api/rest/wrapFetchWithProgress.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { Response } from 'undici';

import { FetchLike } from './client.types';
import * as Log from '../../log';

Expand Down
19 changes: 0 additions & 19 deletions packages/@expo/cli/src/api/rest/wrapFetchWithProxy.ts

This file was deleted.

5 changes: 2 additions & 3 deletions packages/@expo/cli/src/prebuild/resolveLocalTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import fs from 'fs';
import path from 'path';
import resolveFrom from 'resolve-from';

import { packNpmTarballAsync, extractNpmTarballAsync } from '../utils/npm';
import { packNpmTarballAsync, extractLocalNpmTarballAsync } from '../utils/npm';

const debug = require('debug')('expo:prebuild:resolveLocalTemplate') as typeof console.log;

Expand Down Expand Up @@ -50,8 +50,7 @@ export async function resolveLocalTemplateAsync({
debug('Using local template from Expo package:', templatePath);
}

const stream = fs.createReadStream(templatePath);
return await extractNpmTarballAsync(stream, templateDirectory, {
return await extractLocalNpmTarballAsync(templatePath, templateDirectory, {
expName: exp.name,
});
}
7 changes: 2 additions & 5 deletions packages/@expo/cli/src/utils/downloadAppAsync.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fs from 'fs';
import path from 'path';
import { Readable, Stream } from 'stream';
import { Agent } from 'undici';
import type { ReadableStream } from 'stream/web';
import { promisify } from 'util';

import { createTempFilePath } from './createTempPath';
Expand All @@ -13,8 +13,6 @@ import { FetchLike, ProgressCallback } from '../api/rest/client.types';

const debug = require('debug')('expo:utils:downloadAppAsync') as typeof console.log;

const TIMER_DURATION = 30000;

const pipeline = promisify(Stream.pipeline);

async function downloadAsync({
Expand Down Expand Up @@ -42,15 +40,14 @@ async function downloadAsync({
debug(`Downloading ${url} to ${outputPath}`);
const res = await fetchInstance(url, {
onProgress,
dispatcher: new Agent({ connectTimeout: TIMER_DURATION }),
});
if (!res.ok || !res.body) {
throw new CommandError(
'FILE_DOWNLOAD',
`Unexpected response: ${res.statusText}. From url: ${url}`
);
}
return pipeline(Readable.fromWeb(res.body), fs.createWriteStream(outputPath));
return pipeline(Readable.fromWeb(res.body as ReadableStream), fs.createWriteStream(outputPath));
}

export async function downloadAppAsync({
Expand Down
Loading
Loading