expo-router
Advanced tools
Comparing version 4.0.16 to 4.1.0-canary-20250114-804e26d
import type { RouteNode } from './Route'; | ||
export declare function loadStaticParamsAsync(route: RouteNode): Promise<RouteNode>; | ||
export declare function evalStaticParamsAsync(route: RouteNode, props: { | ||
parentParams: any; | ||
}, generateStaticParams?: (props: { | ||
params?: Record<string, string | string[]>; | ||
}) => Record<string, string | string[]>[]): Promise<Record<string, string | string[]>[] | null>; | ||
export declare function assertStaticParams(route: Pick<RouteNode, 'contextKey' | 'dynamic'>, params: Record<string, string | string[]>): void; | ||
//# sourceMappingURL=loadStaticParamsAsync.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.assertStaticParams = exports.loadStaticParamsAsync = void 0; | ||
exports.assertStaticParams = exports.evalStaticParamsAsync = exports.loadStaticParamsAsync = void 0; | ||
async function loadStaticParamsAsync(route) { | ||
@@ -10,10 +10,8 @@ const expandedChildren = await Promise.all(route.children.map((route) => loadStaticParamsRecursive(route, { parentParams: {} }))); | ||
exports.loadStaticParamsAsync = loadStaticParamsAsync; | ||
async function loadStaticParamsRecursive(route, props) { | ||
if (!route?.dynamic && !route?.children?.length) { | ||
return [route]; | ||
async function evalStaticParamsAsync(route, props, generateStaticParams) { | ||
if (!route.dynamic && generateStaticParams) { | ||
throw new Error('Cannot use generateStaticParams in a route without dynamic segments: ' + route.contextKey); | ||
} | ||
const loaded = await route.loadRoute(); | ||
let staticParams = []; | ||
if (loaded.generateStaticParams) { | ||
staticParams = await loaded.generateStaticParams({ | ||
if (generateStaticParams) { | ||
const staticParams = await generateStaticParams({ | ||
params: props.parentParams || {}, | ||
@@ -24,3 +22,13 @@ }); | ||
staticParams.forEach((params) => assertStaticParams(route, params)); | ||
return staticParams; | ||
} | ||
return null; | ||
} | ||
exports.evalStaticParamsAsync = evalStaticParamsAsync; | ||
async function loadStaticParamsRecursive(route, props) { | ||
if (!route?.dynamic && !route?.children?.length) { | ||
return [route]; | ||
} | ||
const loaded = await route.loadRoute(); | ||
const staticParams = (await evalStaticParamsAsync(route, props, loaded.generateStaticParams)) ?? []; | ||
const traverseForNode = async (nextParams) => { | ||
@@ -27,0 +35,0 @@ const nextChildren = []; |
@@ -115,3 +115,3 @@ "use strict"; | ||
else { | ||
throw new Error('Invalid page configuration'); | ||
throw new Error('Invalid page configuration: ' + page.path); | ||
} | ||
@@ -118,0 +118,0 @@ }; |
@@ -17,4 +17,8 @@ /** | ||
statusCode: number; | ||
/** Response headers from the server. */ | ||
headers: Headers; | ||
code: string; | ||
constructor(message: string, url: string, statusCode: number); | ||
constructor(message: string, url: string, statusCode: number, | ||
/** Response headers from the server. */ | ||
headers: Headers); | ||
} | ||
@@ -21,0 +25,0 @@ export declare class NetworkError extends Error { |
@@ -26,7 +26,11 @@ "use strict"; | ||
statusCode; | ||
headers; | ||
code = 'REACT_SERVER_ERROR'; | ||
constructor(message, url, statusCode) { | ||
constructor(message, url, statusCode, | ||
/** Response headers from the server. */ | ||
headers) { | ||
super(message); | ||
this.url = url; | ||
this.statusCode = statusCode; | ||
this.headers = headers; | ||
this.name = 'ReactServerError'; | ||
@@ -33,0 +37,0 @@ } |
@@ -6,4 +6,11 @@ "use strict"; | ||
const getRoutesSSR_1 = require("../../getRoutesSSR"); | ||
const loadStaticParamsAsync_1 = require("../../loadStaticParamsAsync"); | ||
const matchers_1 = require("../../matchers"); | ||
const sortRoutes_1 = require("../../sortRoutes"); | ||
const UNIMPLEMENTED_PARAMS = new Proxy({}, { | ||
// Assert that params is unimplemented when accessed. | ||
get() { | ||
throw new Error('generateStaticParams(): params is not implemented yet'); | ||
}, | ||
}); | ||
exports.default = (0, create_pages_1.createPages)(async ({ createPage, createLayout, unstable_setBuildData }) => { | ||
@@ -17,30 +24,90 @@ const routes = (0, getRoutesSSR_1.getRoutes)(_ctx_1.ctx, { | ||
return; | ||
function addLayout(route) { | ||
async function loadAndConvertStaticParamsAsync(route) { | ||
const loaded = route.loadRoute(); | ||
let staticPaths = undefined; | ||
if (route.dynamic) { | ||
const params = await (0, loadStaticParamsAsync_1.evalStaticParamsAsync)(route, { parentParams: UNIMPLEMENTED_PARAMS }, loaded.generateStaticParams); | ||
// Sort `params` like `[{a: 'x', b: 'y'}, { a: 'z', b: 'w' }]` for a route.dynamic like `[{name: 'a', deep: false}, {name: 'b', deep: false}]` to `[['a', 'y'], ['z', 'w]]` | ||
staticPaths = params?.map((p) => { | ||
const grouped = []; | ||
for (const dynamic of route.dynamic) { | ||
const defined = p[dynamic.name]; | ||
if (!defined) { | ||
throw new Error('generateStaticParams is missing param: ' + | ||
dynamic.name + | ||
'. In route: ' + | ||
route.contextKey); | ||
} | ||
if (Array.isArray(defined)) { | ||
if (defined.length > 1) { | ||
throw new Error('generateStaticParams does not support returning multiple static paths for deep dynamic routes in React Server Components yet. Update route: ' + | ||
route.contextKey); | ||
} | ||
} | ||
const first = Array.isArray(defined) ? defined[0] : defined; | ||
grouped.push(first); | ||
} | ||
return grouped; | ||
}); | ||
} | ||
else if (loaded.generateStaticParams) { | ||
throw new Error('Cannot use generateStaticParams without a dynamic route: ' + route.contextKey); | ||
} | ||
return staticPaths; | ||
} | ||
async function addLayout(route) { | ||
const normal = (0, matchers_1.getContextKey)(route.contextKey).replace(/\/index$/, ''); | ||
const loaded = route.loadRoute(); | ||
if (loaded.generateStaticParams) { | ||
throw new Error('generateStaticParams is not supported in _layout routes with React Server Components enabled yet.'); | ||
} | ||
createLayout({ | ||
// NOTE(EvanBacon): Support routes with top-level "use client" | ||
component: route.loadRoute().default, | ||
component: loaded.default, | ||
path: normal, | ||
// staticPaths | ||
render: 'static', | ||
...route.loadRoute().unstable_settings, | ||
...loaded.unstable_settings, | ||
}); | ||
route.children.sort(sortRoutes_1.sortRoutes).forEach((child) => { | ||
await Promise.all(route.children.sort(sortRoutes_1.sortRoutes).map(async (child) => { | ||
if (child.type === 'layout') { | ||
addLayout(child); | ||
await addLayout(child); | ||
} | ||
else { | ||
const normal = (0, matchers_1.getContextKey)(child.contextKey).replace(/\/index$/, ''); | ||
createPage({ | ||
// NOTE(EvanBacon): Support routes with top-level "use client" | ||
component: child.loadRoute().default, | ||
path: normal, | ||
render: 'dynamic', | ||
...child.loadRoute().unstable_settings, | ||
}); | ||
const loaded = child.loadRoute(); | ||
const settings = loaded.unstable_settings; | ||
// Support generateStaticParams for dynamic routes by defining the route twice. | ||
if (loaded.generateStaticParams) { | ||
createPage({ | ||
// NOTE(EvanBacon): Support routes with top-level "use client" | ||
component: loaded.default, | ||
path: normal, | ||
render: 'static', | ||
...loaded.unstable_settings, | ||
staticPaths: (await loadAndConvertStaticParamsAsync(child)), | ||
}); | ||
if (settings?.render !== 'static') { | ||
createPage({ | ||
// NOTE(EvanBacon): Support routes with top-level "use client" | ||
component: loaded.default, | ||
path: normal, | ||
render: 'dynamic', | ||
...settings, | ||
}); | ||
} | ||
} | ||
else { | ||
createPage({ | ||
// NOTE(EvanBacon): Support routes with top-level "use client" | ||
component: loaded.default, | ||
path: normal, | ||
render: 'dynamic', | ||
...settings, | ||
}); | ||
} | ||
} | ||
}); | ||
})); | ||
} | ||
addLayout(routes); | ||
await addLayout(routes); | ||
}); | ||
//# sourceMappingURL=expo-definedRouter.js.map |
@@ -73,3 +73,3 @@ /** | ||
// This was tested against using a Class component in a server component. | ||
if (response.status === 500) { | ||
if (__DEV__ && (response.status === 500 || response.status === 404)) { | ||
const errorText = await response.text(); | ||
@@ -81,5 +81,10 @@ let errorJson; | ||
catch { | ||
throw new errors_1.ReactServerError(errorText, response.url, response.status); | ||
// `Unable to resolve module` error should respond as JSON from the dev server and sent to the master red box, this can get corrupt when it's returned as the formatted string. | ||
if (errorText.startsWith('Unable to resolve module')) { | ||
console.error('Unexpected Metro error format from dev server'); | ||
// This is an unexpected state that occurs when the dev server renderer does not throw Metro errors in the expected JSON format. | ||
throw new Error(errorJson); | ||
} | ||
throw new errors_1.ReactServerError(errorText, response.url, response.status, response.headers); | ||
} | ||
// TODO: This should be a dev-only error. Add handling for production equivalent. | ||
throw new errors_1.MetroServerError(errorJson, response.url); | ||
@@ -92,5 +97,5 @@ } | ||
catch { | ||
throw new errors_1.ReactServerError(response.statusText, response.url, response.status); | ||
throw new errors_1.ReactServerError(response.statusText, response.url, response.status, response.headers); | ||
} | ||
throw new errors_1.ReactServerError(responseText, response.url, response.status); | ||
throw new errors_1.ReactServerError(responseText, response.url, response.status, response.headers); | ||
} | ||
@@ -97,0 +102,0 @@ return response; |
@@ -17,2 +17,7 @@ /** | ||
export declare function getBuildTimeServerManifestAsync(options?: Options): Promise<ExpoRouterServerManifestV1>; | ||
/** Get the linking manifest from a Node.js process. */ | ||
export declare function getManifest(options?: Options): Promise<{ | ||
initialRouteName: undefined; | ||
screens: Record<string, import("../getReactNavigationConfig").Screen>; | ||
}>; | ||
//# sourceMappingURL=getServerManifest.d.ts.map |
@@ -9,4 +9,5 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getBuildTimeServerManifestAsync = void 0; | ||
exports.getManifest = exports.getBuildTimeServerManifestAsync = void 0; | ||
const _ctx_1 = require("../../_ctx"); | ||
const getReactNavigationConfig_1 = require("../getReactNavigationConfig"); | ||
const getRoutes_1 = require("../getRoutes"); | ||
@@ -35,2 +36,17 @@ const getServerManifest_1 = require("../getServerManifest"); | ||
exports.getBuildTimeServerManifestAsync = getBuildTimeServerManifestAsync; | ||
/** Get the linking manifest from a Node.js process. */ | ||
async function getManifest(options = {}) { | ||
const routeTree = (0, getRoutes_1.getRoutes)(_ctx_1.ctx, { | ||
preserveApiRoutes: true, | ||
platform: 'web', | ||
...options, | ||
}); | ||
if (!routeTree) { | ||
throw new Error('No routes found'); | ||
} | ||
// Evaluate all static params | ||
await (0, loadStaticParamsAsync_1.loadStaticParamsAsync)(routeTree); | ||
return (0, getReactNavigationConfig_1.getReactNavigationConfig)(routeTree, false); | ||
} | ||
exports.getManifest = getManifest; | ||
//# sourceMappingURL=getServerManifest.js.map |
@@ -8,11 +8,4 @@ /** | ||
import '@expo/metro-runtime'; | ||
import { Options } from '../getRoutes'; | ||
/** Get the linking manifest from a Node.js process. */ | ||
declare function getManifest(options?: Options): Promise<{ | ||
initialRouteName: undefined; | ||
screens: Record<string, import("../getReactNavigationConfig").Screen>; | ||
}>; | ||
export declare function getStaticContent(location: URL): Promise<string>; | ||
export { getManifest }; | ||
export { getBuildTimeServerManifestAsync } from './getServerManifest'; | ||
export { getBuildTimeServerManifestAsync, getManifest } from './getServerManifest'; | ||
//# sourceMappingURL=renderStaticContent.d.ts.map |
@@ -29,3 +29,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getBuildTimeServerManifestAsync = exports.getManifest = exports.getStaticContent = void 0; | ||
exports.getManifest = exports.getBuildTimeServerManifestAsync = exports.getStaticContent = void 0; | ||
/** | ||
@@ -46,23 +46,5 @@ * Copyright © 2023 650 Industries. | ||
const ExpoRoot_1 = require("../ExpoRoot"); | ||
const getReactNavigationConfig_1 = require("../getReactNavigationConfig"); | ||
const getRoutes_1 = require("../getRoutes"); | ||
const head_1 = require("../head"); | ||
const loadStaticParamsAsync_1 = require("../loadStaticParamsAsync"); | ||
const debug = require('debug')('expo:router:renderStaticContent'); | ||
react_native_web_1.AppRegistry.registerComponent('App', () => ExpoRoot_1.ExpoRoot); | ||
/** Get the linking manifest from a Node.js process. */ | ||
async function getManifest(options = {}) { | ||
const routeTree = (0, getRoutes_1.getRoutes)(_ctx_1.ctx, { | ||
preserveApiRoutes: true, | ||
platform: 'web', | ||
...options, | ||
}); | ||
if (!routeTree) { | ||
throw new Error('No routes found'); | ||
} | ||
// Evaluate all static params | ||
await (0, loadStaticParamsAsync_1.loadStaticParamsAsync)(routeTree); | ||
return (0, getReactNavigationConfig_1.getReactNavigationConfig)(routeTree, false); | ||
} | ||
exports.getManifest = getManifest; | ||
function resetReactNavigationContexts() { | ||
@@ -126,4 +108,6 @@ // https://github.com/expo/router/discussions/588 | ||
} | ||
// Re-export for use in server | ||
var getServerManifest_1 = require("./getServerManifest"); | ||
Object.defineProperty(exports, "getBuildTimeServerManifestAsync", { enumerable: true, get: function () { return getServerManifest_1.getBuildTimeServerManifestAsync; } }); | ||
Object.defineProperty(exports, "getManifest", { enumerable: true, get: function () { return getServerManifest_1.getManifest; } }); | ||
//# sourceMappingURL=renderStaticContent.js.map |
@@ -11,2 +11,3 @@ 'use client'; | ||
const Link_1 = require("../link/Link"); | ||
const errors_1 = require("../rsc/router/errors"); | ||
let useMetroSymbolication; | ||
@@ -72,2 +73,16 @@ if (process.env.NODE_ENV === 'development') { | ||
} | ||
function StandardErrorView({ error }) { | ||
return (<react_native_1.View style={{ | ||
marginBottom: 12, | ||
gap: 4, | ||
flexWrap: process.env.EXPO_OS === 'web' ? 'wrap' : 'nowrap', | ||
}}> | ||
<react_native_1.Text role="heading" aria-level={1} style={styles.title}> | ||
Something went wrong | ||
</react_native_1.Text> | ||
<react_native_1.Text testID="router_error_message" role="heading" aria-level={2} style={styles.errorMessage}> | ||
Error: {error.message} | ||
</react_native_1.Text> | ||
</react_native_1.View>); | ||
} | ||
function ErrorBoundary({ error, retry }) { | ||
@@ -77,22 +92,17 @@ const logBoxLog = useMetroSymbolication(error); | ||
const Wrapper = inTabBar ? react_native_1.View : react_native_safe_area_context_1.SafeAreaView; | ||
const isServerError = error instanceof errors_1.ReactServerError; | ||
return (<react_native_1.View style={styles.container}> | ||
<Wrapper style={{ flex: 1, gap: 8, maxWidth: 720, marginHorizontal: 'auto' }}> | ||
<react_native_1.View style={{ | ||
marginBottom: 12, | ||
gap: 4, | ||
flexWrap: process.env.EXPO_OS === 'web' ? 'wrap' : 'nowrap', | ||
}}> | ||
<react_native_1.Text role="heading" aria-level={1} style={styles.title}> | ||
Something went wrong | ||
</react_native_1.Text> | ||
<react_native_1.Text role="heading" aria-level={2} style={styles.errorMessage}> | ||
Error: {error.message} | ||
</react_native_1.Text> | ||
</react_native_1.View> | ||
{isServerError ? (<> | ||
<ReactServerErrorView error={error}/> | ||
<react_native_1.View style={{ flex: 1 }}/> | ||
</>) : (<> | ||
<StandardErrorView error={error}/> | ||
<StackTrace logData={logBoxLog}/> | ||
</>)} | ||
<StackTrace logData={logBoxLog}/> | ||
{process.env.NODE_ENV === 'development' && (<Link_1.Link href="/_sitemap" style={styles.link}> | ||
{process.env.NODE_ENV === 'development' && (<Link_1.Link testID="router_error_sitemap" href="/_sitemap" style={styles.link}> | ||
Sitemap | ||
</Link_1.Link>)} | ||
<Pressable_1.Pressable onPress={retry}> | ||
<Pressable_1.Pressable testID="router_error_retry" onPress={retry}> | ||
{({ hovered, pressed }) => (<react_native_1.View style={[styles.buttonInner, (hovered || pressed) && { backgroundColor: 'white' }]}> | ||
@@ -113,2 +123,70 @@ <react_native_1.Text style={[ | ||
exports.ErrorBoundary = ErrorBoundary; | ||
const COMMON_ERROR_STATUS = { | ||
404: 'NOT_FOUND', | ||
500: 'INTERNAL_SERVER_ERROR', | ||
503: 'SERVICE_UNAVAILABLE', | ||
504: 'GATEWAY_TIMEOUT', | ||
}; | ||
// TODO: This should probably be replaced by a DOM component that loads server errors in the future. | ||
function ReactServerErrorView({ error }) { | ||
let title = String(error.statusCode); | ||
title += ': ' + (COMMON_ERROR_STATUS[error.statusCode] ?? 'Server Error'); | ||
const errorId = error.headers.get('cf-ray'); | ||
const date = error.headers.get('Date'); | ||
return (<react_native_1.View style={{ | ||
padding: 12, | ||
gap: 8, | ||
}}> | ||
<react_native_1.Text selectable allowFontScaling style={{ | ||
fontSize: react_native_1.Platform.select({ web: 24, default: 16 }), | ||
fontWeight: 'bold', | ||
marginBottom: 4, | ||
color: 'white', | ||
}}> | ||
{title} | ||
</react_native_1.Text> | ||
{process.env.EXPO_OS === 'web' ? (<react_native_1.ScrollView style={{ | ||
borderColor: 'rgba(255,255,255,0.5)', | ||
borderTopWidth: react_native_1.StyleSheet.hairlineWidth, | ||
borderBottomWidth: react_native_1.StyleSheet.hairlineWidth, | ||
maxHeight: 150, | ||
}} contentContainerStyle={{ paddingVertical: 4 }}> | ||
<react_native_1.Text testID="router_error_message" selectable allowFontScaling style={{ | ||
color: 'white', | ||
}}> | ||
{error.message} | ||
</react_native_1.Text> | ||
</react_native_1.ScrollView>) : (<react_native_1.TextInput testID="router_error_message" scrollEnabled multiline editable={false} allowFontScaling value={error.message} style={{ | ||
borderColor: 'rgba(255,255,255,0.5)', | ||
borderTopWidth: react_native_1.StyleSheet.hairlineWidth, | ||
borderBottomWidth: react_native_1.StyleSheet.hairlineWidth, | ||
paddingVertical: 4, | ||
maxHeight: 150, | ||
color: 'white', | ||
}}/>)} | ||
<InfoRow title="Code" right={error.statusCode}/> | ||
{errorId && <InfoRow title="ID" right={errorId}/>} | ||
{date && <InfoRow title="Date" right={date}/>} | ||
{error.url && (<react_native_1.Text selectable allowFontScaling style={{ fontSize: 14, opacity: 0.5, color: 'white' }}> | ||
{error.url} | ||
</react_native_1.Text>)} | ||
</react_native_1.View>); | ||
} | ||
function InfoRow({ title, right }) { | ||
const style = { | ||
fontSize: 16, | ||
color: 'white', | ||
}; | ||
return (<react_native_1.View style={{ flexDirection: 'row', justifyContent: 'space-between' }}> | ||
<react_native_1.Text selectable allowFontScaling style={style}> | ||
{title} | ||
</react_native_1.Text> | ||
{right && (<react_native_1.Text selectable allowFontScaling style={[style, styles.code]}> | ||
{right} | ||
</react_native_1.Text>)} | ||
</react_native_1.View>); | ||
} | ||
const styles = react_native_1.StyleSheet.create({ | ||
@@ -115,0 +193,0 @@ container: { |
{ | ||
"name": "expo-router", | ||
"version": "4.0.16", | ||
"version": "4.1.0-canary-20250114-804e26d", | ||
"description": "Expo Router is a file-based router for React Native and web applications.", | ||
@@ -78,5 +78,5 @@ "author": "650 Industries, Inc.", | ||
"@react-navigation/drawer": "^7.1.1", | ||
"expo": "*", | ||
"expo-constants": "*", | ||
"expo-linking": "*", | ||
"expo": "53.0.0-canary-20250114-804e26d", | ||
"expo-constants": "17.0.5-canary-20250114-804e26d", | ||
"expo-linking": "7.0.5-canary-20250114-804e26d", | ||
"react-native-reanimated": "*", | ||
@@ -107,4 +107,4 @@ "react-native-safe-area-context": "*", | ||
"dependencies": { | ||
"@expo/metro-runtime": "4.0.0", | ||
"@expo/server": "^0.5.0", | ||
"@expo/metro-runtime": "4.0.1-canary-20250114-804e26d", | ||
"@expo/server": "0.5.1-canary-20250114-804e26d", | ||
"@radix-ui/react-slot": "1.0.1", | ||
@@ -122,3 +122,3 @@ "@react-navigation/bottom-tabs": "^7.2.0", | ||
}, | ||
"gitHead": "1955c7d38e69c0eb62df89d516a392ea64db6405" | ||
"gitHead": "804e26d8545f2b890dfa80eeb8122036f38d4d0c" | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
1661884
16538
1
85
+ Added@expo/cli@0.23.0-canary-20250114-804e26d(transitive)
+ Added@expo/config@10.0.9-canary-20250114-804e26d(transitive)
+ Added@expo/config-plugins@9.0.15-canary-20250114-804e26d(transitive)
+ Added@expo/config-types@53.0.0-canary-20250114-804e26d(transitive)
+ Added@expo/env@0.4.2-canary-20250114-804e26d(transitive)
+ Added@expo/fingerprint@0.11.8-canary-20250114-804e26d(transitive)
+ Added@expo/image-utils@0.6.5-canary-20250114-804e26d(transitive)
+ Added@expo/json-file@9.0.2-canary-20250114-804e26d(transitive)
+ Added@expo/metro-config@0.19.10-canary-20250114-804e26d(transitive)
+ Added@expo/metro-runtime@4.0.1-canary-20250114-804e26d(transitive)
+ Added@expo/osascript@2.1.6-canary-20250114-804e26d(transitive)
+ Added@expo/package-manager@1.7.2-canary-20250114-804e26d(transitive)
+ Added@expo/plist@0.2.2-canary-20250114-804e26d(transitive)
+ Added@expo/prebuild-config@8.0.26-canary-20250114-804e26d(transitive)
+ Added@expo/server@0.5.1-canary-20250114-804e26d(transitive)
+ Addedbabel-preset-expo@12.0.7-canary-20250114-804e26d(transitive)
+ Addedexpo@53.0.0-canary-20250114-804e26d(transitive)
+ Addedexpo-asset@11.0.3-canary-20250114-804e26d(transitive)
+ Addedexpo-constants@17.0.5-canary-20250114-804e26d(transitive)
+ Addedexpo-file-system@18.0.8-canary-20250114-804e26d(transitive)
+ Addedexpo-font@13.0.4-canary-20250114-804e26d(transitive)
+ Addedexpo-keep-awake@14.0.3-canary-20250114-804e26d(transitive)
+ Addedexpo-linking@7.0.5-canary-20250114-804e26d(transitive)
+ Addedexpo-modules-autolinking@2.0.6-canary-20250114-804e26d(transitive)
+ Addedexpo-modules-core@2.2.0-canary-20250114-804e26d(transitive)
- Removed@expo/cli@0.22.9(transitive)
- Removed@expo/config@10.0.8(transitive)
- Removed@expo/config-plugins@9.0.14(transitive)
- Removed@expo/config-types@52.0.3(transitive)
- Removed@expo/env@0.4.1(transitive)
- Removed@expo/fingerprint@0.11.7(transitive)
- Removed@expo/image-utils@0.6.4(transitive)
- Removed@expo/json-file@9.0.1(transitive)
- Removed@expo/metro-config@0.19.9(transitive)
- Removed@expo/metro-runtime@4.0.0(transitive)
- Removed@expo/osascript@2.1.5(transitive)
- Removed@expo/package-manager@1.7.1(transitive)
- Removed@expo/plist@0.2.1(transitive)
- Removed@expo/prebuild-config@8.0.25(transitive)
- Removed@expo/server@0.5.0(transitive)
- Removedbabel-preset-expo@12.0.6(transitive)
- Removedexpo@52.0.25(transitive)
- Removedexpo-asset@11.0.2(transitive)
- Removedexpo-constants@17.0.4(transitive)
- Removedexpo-file-system@18.0.7(transitive)
- Removedexpo-font@13.0.3(transitive)
- Removedexpo-keep-awake@14.0.2(transitive)
- Removedexpo-linking@7.0.4(transitive)
- Removedexpo-modules-autolinking@2.0.5(transitive)
- Removedexpo-modules-core@2.1.3(transitive)